From 818f33cc324c20e6b686a9c948cc6db7d74a8c27 Mon Sep 17 00:00:00 2001 From: NotCoded <66999075+not-coded@users.noreply.github.com> Date: Sat, 25 Mar 2023 21:59:19 +0100 Subject: [PATCH] Add from other repository --- .github/workflows/buíld.yml | 39 ++ .gitignore | 29 ++ LICENSE | 121 ++++++ README.md | 7 +- build.gradle | 114 +++++ gradle.properties | 17 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 ++++++++ gradlew.bat | 103 +++++ settings.gradle | 10 + src/main/java/com/nexia/core/Main.java | 33 ++ .../core/commands/player/DiscordCommand.java | 36 ++ .../core/commands/player/HelpCommand.java | 47 +++ .../core/commands/player/LeaveCommand.java | 30 ++ .../core/commands/player/MessageCommand.java | 86 ++++ .../core/commands/player/PingCommand.java | 54 +++ .../core/commands/player/PlayCommand.java | 62 +++ .../core/commands/player/PrefixCommand.java | 21 + .../core/commands/player/ReportCommand.java | 68 +++ .../core/commands/player/RulesCommand.java | 36 ++ .../core/commands/player/StatsCommand.java | 132 ++++++ .../commands/player/duels/DuelCommand.java | 64 +++ .../commands/player/duels/QueueCommand.java | 39 ++ .../commands/player/oitc/OitcCommand.java | 30 ++ .../nexia/core/commands/staff/BanCommand.java | 61 +++ .../commands/staff/BwReloadShopCommand.java | 37 ++ .../staff/BwReloadTeamColorsCommand.java | 27 ++ .../commands/staff/BwSkipQueueCommand.java | 35 ++ .../staff/DefaultGameRulesCommand.java | 53 +++ .../core/commands/staff/HealCommand.java | 42 ++ .../nexia/core/commands/staff/MapCommand.java | 95 +++++ .../core/commands/staff/MuteCommand.java | 75 ++++ .../commands/staff/ProtectionMapCommand.java | 28 ++ .../core/commands/staff/RandomCommand.java | 25 ++ .../core/commands/staff/RankCommand.java | 47 +++ .../core/commands/staff/RoundPosCommand.java | 32 ++ .../core/commands/staff/UnBanCommand.java | 55 +++ .../core/commands/staff/UnMuteCommand.java | 34 ++ .../java/com/nexia/core/config/ModConfig.java | 56 +++ .../com/nexia/core/games/util/LobbyUtil.java | 177 ++++++++ .../nexia/core/games/util/PlayerGameMode.java | 25 ++ src/main/java/com/nexia/core/gui/PlayGUI.java | 169 ++++++++ src/main/java/com/nexia/core/gui/RankGUI.java | 104 +++++ .../com/nexia/core/loader/CommandLoader.java | 63 +++ .../core/mixin/block/AnvilBlockMixin.java | 32 ++ .../nexia/core/mixin/block/BlockMixin.java | 35 ++ .../block/StructureBlockEntityMixin.java | 80 ++++ .../core/mixin/entity/AbstractArrowMixin.java | 39 ++ .../AbstractHurtingProjectileMixin.java | 30 ++ .../entity/AbstractLivingEntityMixin.java | 56 +++ .../core/mixin/entity/ArmorStandMixin.java | 34 ++ .../nexia/core/mixin/entity/EntityMixin.java | 41 ++ .../mixin/entity/FishingBobberEntity.java | 47 +++ .../core/mixin/entity/ItemEntityMixin.java | 24 ++ .../core/mixin/entity/ItemFrameMixin.java | 33 ++ .../core/mixin/entity/LargeFireballMixin.java | 39 ++ .../core/mixin/entity/LivingEntityMixin.java | 46 +++ .../core/mixin/entity/ThrownEggMixin.java | 24 ++ .../mixin/entity/ThrownEnderpearlMixin.java | 34 ++ .../core/mixin/entity/ThrownTridentMixin.java | 44 ++ .../nexia/core/mixin/item/AxeItemMixin.java | 32 ++ .../nexia/core/mixin/item/BlockItemMixin.java | 32 ++ .../core/mixin/item/CompassItemMixin.java | 33 ++ .../nexia/core/mixin/item/CraftingMixin.java | 55 +++ .../nexia/core/mixin/item/EggItemMixin.java | 44 ++ .../core/mixin/item/PotionItemMixin.java | 52 +++ .../core/mixin/item/TridentItemMixin.java | 44 ++ .../nexia/core/mixin/item/UseItemMixin.java | 32 ++ .../misc/AbstractContainerMenuMixin.java | 28 ++ .../core/mixin/misc/BanCommandMixin.java | 18 + .../mixin/misc/CommandSourceStackMixin.java | 33 ++ .../nexia/core/mixin/misc/ExplosionMixin.java | 69 ++++ .../core/mixin/misc/MinecraftServerMixin.java | 33 ++ .../core/mixin/misc/MsgCommandMixin.java | 20 + .../core/mixin/misc/PardonCommandMixin.java | 19 + .../core/mixin/player/FoodDataMixin.java | 54 +++ .../core/mixin/player/PlayerListMixin.java | 191 +++++++++ .../nexia/core/mixin/player/PlayerMixin.java | 152 +++++++ .../player/ServerGamePacketListenerMixin.java | 133 ++++++ .../ServerLoginPacketListenerMixin.java | 33 ++ .../player/ServerPlayerGameModeMixin.java | 45 ++ .../core/mixin/player/ServerPlayerMixin.java | 82 ++++ .../nexia/core/utilities/chat/ChatFormat.java | 152 +++++++ .../core/utilities/chat/PlayerMutes.java | 76 ++++ .../core/utilities/chat/RomanNumbers.java | 33 ++ .../core/utilities/http/DiscordWebhook.java | 391 ++++++++++++++++++ .../nexia/core/utilities/item/BlockUtil.java | 61 +++ .../core/utilities/item/InventoryUtil.java | 158 +++++++ .../core/utilities/item/ItemDisplayUtil.java | 59 +++ .../core/utilities/item/ItemStackUtil.java | 137 ++++++ .../nexia/core/utilities/misc/EventUtil.java | 17 + .../nexia/core/utilities/misc/NxCmdUtil.java | 44 ++ .../nexia/core/utilities/misc/NxFileUtil.java | 20 + .../nexia/core/utilities/misc/RandomUtil.java | 13 + .../core/utilities/player/PlayerData.java | 41 ++ .../utilities/player/PlayerDataManager.java | 76 ++++ .../core/utilities/player/PlayerUtil.java | 197 +++++++++ .../utilities/player/SavedPlayerData.java | 13 + .../nexia/core/utilities/pos/BlockVec3.java | 91 ++++ .../nexia/core/utilities/pos/EntityPos.java | 98 +++++ .../core/utilities/pos/PositionUtil.java | 15 + .../core/utilities/pos/ProtectionBlock.java | 15 + .../core/utilities/pos/ProtectionMap.java | 134 ++++++ .../nexia/core/utilities/time/ServerTime.java | 77 ++++ .../nexia/core/utilities/time/TickUtil.java | 27 ++ src/main/java/com/nexia/ffa/Main.java | 17 + .../java/com/nexia/ffa/config/ModConfig.java | 15 + .../nexia/ffa/mixin/ServerPlayerMixin.java | 145 +++++++ .../com/nexia/ffa/utilities/FfaAreas.java | 43 ++ .../java/com/nexia/ffa/utilities/FfaUtil.java | 189 +++++++++ .../ffa/utilities/player/PlayerData.java | 16 + .../utilities/player/PlayerDataManager.java | 80 ++++ .../ffa/utilities/player/SavedPlayerData.java | 16 + src/main/java/com/nexia/minigames/Main.java | 17 + .../com/nexia/minigames/config/ModConfig.java | 13 + .../nexia/minigames/games/bedwars/BwGame.java | 330 +++++++++++++++ .../games/bedwars/areas/BwAreas.java | 168 ++++++++ .../games/bedwars/areas/BwDimension.java | 26 ++ .../games/bedwars/custom/BwBridgeEgg.java | 68 +++ .../bedwars/custom/BwExplosiveSlime.java | 75 ++++ .../games/bedwars/custom/BwTrident.java | 58 +++ .../games/bedwars/players/BwPlayerEvents.java | 304 ++++++++++++++ .../games/bedwars/players/BwPlayers.java | 175 ++++++++ .../games/bedwars/players/BwTeam.java | 287 +++++++++++++ .../games/bedwars/shop/BwLoadShop.java | 173 ++++++++ .../minigames/games/bedwars/shop/BwShop.java | 159 +++++++ .../games/bedwars/shop/BwShopCost.java | 45 ++ .../bedwars/shop/BwShopUpgradeables.java | 143 +++++++ .../games/bedwars/shop/BwShopUtil.java | 167 ++++++++ .../games/bedwars/upgrades/BwApplyTraps.java | 74 ++++ .../bedwars/upgrades/BwApplyUpgrades.java | 84 ++++ .../games/bedwars/upgrades/BwTrap.java | 64 +++ .../games/bedwars/upgrades/BwUpgrade.java | 81 ++++ .../games/bedwars/upgrades/BwUpgradeShop.java | 216 ++++++++++ .../minigames/games/bedwars/util/BwGen.java | 224 ++++++++++ .../games/bedwars/util/BwPlayerTracker.java | 97 +++++ .../games/bedwars/util/BwScoreboard.java | 134 ++++++ .../minigames/games/bedwars/util/BwUtil.java | 378 +++++++++++++++++ .../minigames/games/duels/DuelGameMode.java | 40 ++ .../minigames/games/duels/DuelsGame.java | 184 +++++++++ .../minigames/games/duels/DuelsSpawn.java | 32 ++ .../duels/gamemodes/GamemodeHandler.java | 353 ++++++++++++++++ .../games/duels/util/player/PlayerData.java | 41 ++ .../duels/util/player/PlayerDataManager.java | 76 ++++ .../duels/util/player/SavedPlayerData.java | 13 + .../nexia/minigames/games/oitc/OitcGame.java | 235 +++++++++++ .../minigames/games/oitc/OitcGameMode.java | 14 + .../nexia/minigames/games/oitc/OitcSpawn.java | 32 ++ .../games/oitc/util/OitcScoreboard.java | 138 +++++++ .../games/oitc/util/player/PlayerData.java | 31 ++ .../oitc/util/player/PlayerDataManager.java | 76 ++++ .../oitc/util/player/SavedPlayerData.java | 16 + src/main/resources/assets/nexia/icon.png | Bin 0 -> 20884 bytes src/main/resources/core.mixins.json | 48 +++ .../resources/data/ffa/dimension/map.json | 61 +++ .../data/minigames/dimension/bedwars.json | 13 + .../dimension_type/bedwars_type.json | 14 + src/main/resources/fabric.mod.json | 40 ++ src/main/resources/ffa.mixins.json | 12 + 160 files changed, 11972 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/buíld.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/com/nexia/core/Main.java create mode 100644 src/main/java/com/nexia/core/commands/player/DiscordCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/HelpCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/LeaveCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/MessageCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/PingCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/PlayCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/PrefixCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/ReportCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/RulesCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/StatsCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/duels/DuelCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/duels/QueueCommand.java create mode 100644 src/main/java/com/nexia/core/commands/player/oitc/OitcCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/BanCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/BwReloadShopCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/BwReloadTeamColorsCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/BwSkipQueueCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/DefaultGameRulesCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/HealCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/MapCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/MuteCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/ProtectionMapCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/RandomCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/RankCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/RoundPosCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/UnBanCommand.java create mode 100644 src/main/java/com/nexia/core/commands/staff/UnMuteCommand.java create mode 100644 src/main/java/com/nexia/core/config/ModConfig.java create mode 100644 src/main/java/com/nexia/core/games/util/LobbyUtil.java create mode 100644 src/main/java/com/nexia/core/games/util/PlayerGameMode.java create mode 100644 src/main/java/com/nexia/core/gui/PlayGUI.java create mode 100644 src/main/java/com/nexia/core/gui/RankGUI.java create mode 100644 src/main/java/com/nexia/core/loader/CommandLoader.java create mode 100644 src/main/java/com/nexia/core/mixin/block/AnvilBlockMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/block/BlockMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/block/StructureBlockEntityMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/AbstractArrowMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/AbstractHurtingProjectileMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/AbstractLivingEntityMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ArmorStandMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/EntityMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/FishingBobberEntity.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ItemEntityMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ItemFrameMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/LargeFireballMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/LivingEntityMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ThrownEggMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ThrownEnderpearlMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/entity/ThrownTridentMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/AxeItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/BlockItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/CompassItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/CraftingMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/EggItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/PotionItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/TridentItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/item/UseItemMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/AbstractContainerMenuMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/BanCommandMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/CommandSourceStackMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/ExplosionMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/MinecraftServerMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/MsgCommandMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/misc/PardonCommandMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/FoodDataMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/PlayerListMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/PlayerMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/ServerGamePacketListenerMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/ServerLoginPacketListenerMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/ServerPlayerGameModeMixin.java create mode 100644 src/main/java/com/nexia/core/mixin/player/ServerPlayerMixin.java create mode 100644 src/main/java/com/nexia/core/utilities/chat/ChatFormat.java create mode 100644 src/main/java/com/nexia/core/utilities/chat/PlayerMutes.java create mode 100644 src/main/java/com/nexia/core/utilities/chat/RomanNumbers.java create mode 100644 src/main/java/com/nexia/core/utilities/http/DiscordWebhook.java create mode 100644 src/main/java/com/nexia/core/utilities/item/BlockUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/item/InventoryUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/item/ItemDisplayUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/item/ItemStackUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/misc/EventUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/misc/NxCmdUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/misc/NxFileUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/misc/RandomUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/player/PlayerData.java create mode 100644 src/main/java/com/nexia/core/utilities/player/PlayerDataManager.java create mode 100644 src/main/java/com/nexia/core/utilities/player/PlayerUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/player/SavedPlayerData.java create mode 100644 src/main/java/com/nexia/core/utilities/pos/BlockVec3.java create mode 100644 src/main/java/com/nexia/core/utilities/pos/EntityPos.java create mode 100644 src/main/java/com/nexia/core/utilities/pos/PositionUtil.java create mode 100644 src/main/java/com/nexia/core/utilities/pos/ProtectionBlock.java create mode 100644 src/main/java/com/nexia/core/utilities/pos/ProtectionMap.java create mode 100644 src/main/java/com/nexia/core/utilities/time/ServerTime.java create mode 100644 src/main/java/com/nexia/core/utilities/time/TickUtil.java create mode 100644 src/main/java/com/nexia/ffa/Main.java create mode 100644 src/main/java/com/nexia/ffa/config/ModConfig.java create mode 100644 src/main/java/com/nexia/ffa/mixin/ServerPlayerMixin.java create mode 100644 src/main/java/com/nexia/ffa/utilities/FfaAreas.java create mode 100644 src/main/java/com/nexia/ffa/utilities/FfaUtil.java create mode 100644 src/main/java/com/nexia/ffa/utilities/player/PlayerData.java create mode 100644 src/main/java/com/nexia/ffa/utilities/player/PlayerDataManager.java create mode 100644 src/main/java/com/nexia/ffa/utilities/player/SavedPlayerData.java create mode 100644 src/main/java/com/nexia/minigames/Main.java create mode 100644 src/main/java/com/nexia/minigames/config/ModConfig.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/BwGame.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/areas/BwAreas.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/areas/BwDimension.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/custom/BwBridgeEgg.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/custom/BwExplosiveSlime.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/custom/BwTrident.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayerEvents.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayers.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/players/BwTeam.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/shop/BwLoadShop.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/shop/BwShop.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopCost.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUpgradeables.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUtil.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyTraps.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyUpgrades.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwTrap.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgrade.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgradeShop.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/util/BwGen.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/util/BwPlayerTracker.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/util/BwScoreboard.java create mode 100644 src/main/java/com/nexia/minigames/games/bedwars/util/BwUtil.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/DuelGameMode.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/DuelsGame.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/DuelsSpawn.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/gamemodes/GamemodeHandler.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/util/player/PlayerData.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/util/player/PlayerDataManager.java create mode 100644 src/main/java/com/nexia/minigames/games/duels/util/player/SavedPlayerData.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/OitcGame.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/OitcGameMode.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/OitcSpawn.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/util/OitcScoreboard.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerData.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerDataManager.java create mode 100644 src/main/java/com/nexia/minigames/games/oitc/util/player/SavedPlayerData.java create mode 100644 src/main/resources/assets/nexia/icon.png create mode 100644 src/main/resources/core.mixins.json create mode 100644 src/main/resources/data/ffa/dimension/map.json create mode 100644 src/main/resources/data/minigames/dimension/bedwars.json create mode 100644 src/main/resources/data/minigames/dimension_type/bedwars_type.json create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/ffa.mixins.json diff --git a/.github/workflows/buíld.yml b/.github/workflows/buíld.yml new file mode 100644 index 00000000..3e6ca6f5 --- /dev/null +++ b/.github/workflows/buíld.yml @@ -0,0 +1,39 @@ +# Automatically build the project and run any configured tests for every push +# and submitted pull request. This can help catch issues that only occur on +# certain platforms or Java versions, and provides a first line of defence +# against bad commits. + +name: build +on: [pull_request, push] + +jobs: + build: + strategy: + matrix: + # Use these Java versions + java: [ + 17 # Minimum supported by Minecraft + ] + # and run on both Linux and Windows + os: [ubuntu-20.04, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: checkout repository + uses: actions/checkout@v2 + - name: validate gradle wrapper + uses: gradle/wrapper-validation-action@v1 + - name: setup jdk ${{ matrix.java }} + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: make gradle wrapper executable + if: ${{ runner.os != 'Windows' }} + run: chmod +x ./gradlew + - name: build + run: ./gradlew build + - name: capture build artifacts + if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS + uses: actions/upload-artifact@v2 + with: + name: Artifacts + path: build/libs/ \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..550b373c --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# gradle + +.gradle/ +build/ +out/ +classes/ + +# eclipse + +*.launch + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# fabric + +run/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md index 1a6fd18b..bc0cf326 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -# Nexia Mod +# Nexia + +### Requirements +- Java 17 +- Gradle 7.3 +- Fabric API (cts version) diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..5a83f90b --- /dev/null +++ b/build.gradle @@ -0,0 +1,114 @@ +plugins { + id 'fabric-loom' version '0.13-SNAPSHOT' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +archivesBaseName = project.archives_base_name +version = project.mod_version +group = project.maven_group + +// Repo for CT intermediary mappings +repositories { + maven { url 'https://maven.nucleoid.xyz' } + maven { url "https://maven.rizecookey.net/" } + maven { url "https://maven.shedaniel.me/" } + maven { url "https://maven.terraformersmc.com/releases/" } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://github.com/ricksouth/serilum-forge-maven/raw/maven/' } +} + +dependencies { + //to change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings loom.officialMojangMappings() + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + //modApiMapped('com.combatreforged.factory:factory-api:0.1.13-full') + //modApiMapped('com.combatreforged.factory:factory-builder:0.1.13-1.16_combat-6') + + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + //include(modApi("eu.pb4:hologram-api:0.1.0") { exclude(group: "net.fabricmc.fabric-api") }) + //include(modApi("eu.pb4:sidebar-api:0.0.2") { exclude(group: "net.fabricmc.fabric-api") }) + + modApi('com.natamus.collective:collective-fabric:1.16.5-5.50') + include(modApi('xyz.nucleoid:fantasy:0.4.0') {exclude(group: "net.fabricmc.fabric-api") }) + include(modApi('eu.pb4:sgui:0.5.0') { exclude(group: "net.fabricmc.fabric-api") }) + include(modApi('me.sargunvohra.mcmods:autoconfig1u:3.3.1') { exclude(group: "net.fabricmc.fabric-api") }) + modApi('me.lucko:fabric-permissions-api:0.2-SNAPSHOT') + + // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. + // You may need to force-disable transitiveness on them. +} + +// Custom Manifest and Intermediary mappings declaration +loom { + customMinecraftManifest = "https://gist.githubusercontent.com/rizecookey/4c6142baaccc3875f9b227fe22f2ace5/raw/c8ed74b19f7a5315813c9d4b199798b692a8f359/1.16_combat-6.json" + intermediaryUrl = "https://maven.rizecookey.net/net/fabricmc/intermediary/%1\$s/intermediary-%1\$s-v2.jar" +} + +processResources { + inputs.property "version", project.version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +// ensure that the encoding is set to UTF-8, no matter what the system default is +// this fixes some edge cases with special characters not displaying correctly +// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html +def targetJavaVersion = 17 +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release = targetJavaVersion + } +} + +// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task +// if it is present. +// If you remove this task, sources will not be generated. +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = "sources" + from sourceSets.main.allSource +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + archivesBaseName = project.archives_base_name +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}" } + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + builtBy remapJar + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // select the repositories you want to publish to + repositories { + // uncomment to publish to the local maven + // mavenLocal() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..c9dd0601 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G + +# Fabric Properties + # check these on https://fabricmc.net/use + minecraft_version=1.16_combat-6 + yarn_mappings=1.16_combat-6+build.2 + loader_version=0.9.3+build.207 + +# Mod Properties + mod_version = 1.0.0 + maven_group = com.nexia + archives_base_name = nexia + +# Dependencies + fabric_version=0.42.0+1.16 + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 zcma&OV~}Oh(k5J8>Mq;1ZQHhO+v>7y+qO>Gc6Hgdjp>5?}0s%q%y~>Cv3(!c&iqe4q$^V<9O+7CU z|6d2bzlQvOI?4#hN{EUmDbvb`-pfo*NK4Vs&cR60P)<+IG%C_BGVL7RP11}?Ovy}9 zNl^cQJPR>SIVjSkXhS0@IVhqGLL)&%E<(L^ymkEXU!M5)A^-c;K>yy`Ihy@nZ}orr zK>gFl%+bKu+T{P~iuCWUZjJ`__9l-1*OFwCg_8CkKtLEEKtOc=d5NH%owJkk-}N#E z7Pd;x29C}qj>HVKM%D&SPSJ`JwhR2oJPU0u3?)GiA|6TndJ+~^eXL<%D)IcZ)QT?t zE7BJP>Ejq;`w$<dd^@|esR(;1Z@9EVR%7cZG`%Xr%6 zLHXY#GmPV!HIO3@j5yf7D{PN5E6tHni4mC;qIq0Fj_fE~F1XBdnzZIRlk<~?V{-Uc zt9ldgjf)@8NoAK$6OR|2is_g&pSrDGlQS);>YwV7C!=#zDSwF}{_1#LA*~RGwALm) zC^N1ir5_}+4!)@;uj92irB5_Ugihk&Uh|VHd924V{MiY7NySDh z|6TZCb1g`c)w{MWlMFM5NK@xF)M33F$ZElj@}kMu$icMyba8UlNQ86~I$sau*1pzZ z4P)NF@3(jN(thO5jwkx(M5HOe)%P1~F!hXMr%Rp$&OY0X{l_froFdbi(jCNHbHj#! z(G`_tuGxu#h@C9HlIQ8BV4>%8eN=MApyiPE0B3dR`bsa1=MM$lp+38RN4~`m>PkE? zARywuzZ#nV|0wt;22|ITkkrt>ahz7`sKXd2!vpFCC4i9VnpNvmqseE%XnxofI*-Mr6tjm7-3$I-v}hr6B($ALZ=#Q4|_2l#i5JyVQCE{hJAnFhZF>vfSZgnw`Vgn zIi{y#1e7`}xydrUAdXQ%e?_V6K(DK89yBJ;6Sf{Viv*GzER9C3Mns=nTFt6`Eu?yu<*Fb}WpP$iO#-y+^H>OQ< zw%DSM@I=@a)183hx!sz(#&cg-6HVfK(UMgo8l2jynx5RWEo8`?+^3x0sEoj9H8%m1 z87?l+w;0=@Dx_J86rA6vesuDQ^nY(n?SUdaY}V)$Tvr%>m9XV>G>6qxKxkH zN6|PyTD(7+fjtb}cgW1rctvZQR!3wX2S|ils!b%(=jj6lLdx#rjQ6XuJE1JhNqzXO zKqFyP8Y1tN91g;ahYsvdGsfyUQz6$HMat!7N1mHzYtN3AcB>par(Q>mP7^`@7@Ox14gD12*4RISSYw-L>xO#HTRgM)eLaOOFuN}_UZymIhu%J?D|k>Y`@ zYxTvA;=QLhu@;%L6;Ir_$g+v3;LSm8e3sB;>pI5QG z{Vl6P-+69G-P$YH-yr^3cFga;`e4NUYzdQy6vd|9${^b#WDUtxoNe;FCcl5J7k*KC z7JS{rQ1%=7o8to#i-`FD3C?X3!60lDq4CqOJ8%iRrg=&2(}Q95QpU_q ziM346!4()C$dHU@LtBmfKr!gZGrZzO{`dm%w_L1DtKvh8UY zTP3-|50~Xjdu9c%Cm!BN^&9r?*Wgd(L@E!}M!#`C&rh&c2fsGJ_f)XcFg~$#3S&Qe z_%R=Gd`59Qicu`W5YXk>vz5!qmn`G>OCg>ZfGGuI5;yQW9Kg*exE+tdArtUQfZ&kO ze{h37fsXuQA2Z(QW|un!G2Xj&Qwsk6FBRWh;mfDsZ-$-!YefG!(+bY#l3gFuj)OHV830Xl*NKp1-L&NPA3a8jx#yEn3>wea~ z9zp8G6apWn$0s)Pa!TJo(?lHBT1U4L>82jifhXlkv^a+p%a{Og8D?k6izWyhv`6prd7Yq5{AqtzA8n{?H|LeQFqn(+fiIbDG zg_E<1t%>753QV!erV^G4^7p1SE7SzIqBwa{%kLHzP{|6_rlM*ae{*y4WO?{%&eQ`| z>&}ZkQ;<)rw;d(Dw*om?J@3<~UrXsvW2*0YOq_-Lfq45PQGUVu?Ws3&6g$q+q{mx4 z$2s@!*|A+74>QNlK!D%R(u22>Jeu}`5dsv9q~VD!>?V86x;Fg4W<^I;;ZEq5z4W5c z#xMX=!iYaaW~O<(q>kvxdjNk15H#p0CSmMaZB$+%v90@w(}o$T7;(B+Zv%msQvjnW z`k7=uf(h=gkivBw?57m%k^SPxZnYu@^F% zKd`b)S#no`JLULZCFuP^y5ViChc;^3Wz#c|ehD+2MHbUuB3IH5+bJ_FChTdARM6Q2 zdyuu9eX{WwRasK!aRXE+0j zbTS8wg@ue{fvJ*=KtlWbrXl8YP88;GXto?_h2t@dY3F?=gX9Frwb8f1n!^xdOFDL7 zbddq6he>%k+5?s}sy?~Ya!=BnwSDWloNT;~UF4|1>rUY!SSl^*F6NRs_DT-rn=t-p z_Ga0p)`@!^cxW_DhPA=0O;88pCT*G9YL29_4fJ(b{| zuR~VCZZCR97e%B(_F5^5Eifes$8!7DCO_4(x)XZDGO%dY9Pkm~-b1-jF#2H4kfl<3 zsBes0sP@Zyon~Q&#<7%gxK{o+vAsIR>gOm$w+{VY8ul7OsSQ>07{|7jB6zyyeu+WU zME>m2s|$xvdsY^K%~nZ^%Y`D7^PCO(&)eV-Qw|2_PnL=Nd=}#4kY)PS=Y62Dzz1e2 z&*)`$OEBuC&M5f`I}A-pEzy^lyEEcd$n1mEgLj}u_b^d!5pg{v+>_FexoDxYj%X_F z5?4eHVXurS%&n2ISv2&Eik?@3ry}0qCwS9}N)`Zc_Q8}^SOViB_AB&o6Eh#bG;NnL zAhP2ZF_la`=dZv6Hs@78DfMjy*KMSExRZfccK=-DPGkqtCK%U1cUXxbTX-I0m~x$3 z&Oc&aIGWtcf|i~=mPvR^u6^&kCj|>axShGlPG}r{DyFp(Fu;SAYJ}9JfF*x0k zA@C(i5ZM*(STcccXkpV$=TznZKQVtec!A24VWu*oS0L(^tkEm2ZIaE4~~?#y9Z4 zlU!AB6?yc(jiB`3+{FC zl|IdP1Fdt#e5DI{W{d8^$EijTU(8FA@8V&_A*tO?!9rI zhoRk`Q*riCozP>F%4pDPmA>R#Zm>_mAHB~Y5$sE4!+|=qK0dhMi4~`<6sFHb=x8Naml}1*8}K_Es3#oh3-7@0W}BJDREnwWmw<{wY9p)3+Mq2CLcX?uAvItguqhk*Po!RoP`kR)!OQy3Ayi zL@ozJ!I_F2!pTC?OBAaOrJmpGX^O(dSR-yu5Wh)f+o5O262f6JOWuXiJS_Jxgl@lS z6A9c*FSHGP4HuwS)6j3~b}t{+B(dqG&)Y}C;wnb!j#S0)CEpARwcF4Q-5J1NVizx7 z(bMG>ipLI1lCq?UH~V#i3HV9|bw%XdZ3Q#c3)GB+{2$zoMAev~Y~(|6Ae z^QU~3v#*S>oV*SKvA0QBA#xmq9=IVdwSO=m=4Krrlw>6t;Szk}sJ+#7=ZtX(gMbrz zNgv}8GoZ&$=ZYiI2d?HnNNGmr)3I);U4ha+6uY%DpeufsPbrea>v!D50Q)k2vM=aF-zUsW*aGLS`^2&YbchmKO=~eX@k9B!r;d{G% zrJU~03(->>utR^5;q!i>dAt)DdR!;<9f{o@y2f}(z(e)jj^*pcd%MN{5{J=K<@T!z zseP#j^E2G31piu$O@3kGQ{9>Qd;$6rr1>t!{2CuT_XWWDRfp7KykI?kXz^{u_T2AZ z-@;kGj8Iy>lOcUyjQqK!1OHkY?0Kz+_`V8$Q-V|8$9jR|%Ng;@c%kF_!rE3w>@FtX zX1w7WkFl%Vg<mE0aAHX==DLjyxlfA}H|LVh;}qcWPd8pSE!_IUJLeGAW#ZJ?W}V7P zpVeo|`)a<#+gd}dH%l)YUA-n_Vq3*FjG1}6mE;@A5ailjH*lJaEJl*51J0)Xecn6X zz zDr~lx5`!ZJ`=>>Xb$}p-!3w;ZHtu zX@xB4PbX!J(Jl((<8K%)inh!-3o2S2sbI4%wu9-4ksI2%e=uS?Wf^Tp%(Xc&wD6lV z*DV()$lAR&##AVg__A=Zlu(o$3KE|N7ZN{X8oJhG+FYyF!(%&R@5lpCP%A|{Q1cdr>x0<+;T`^onat<6tlGfEwRR?ZgMTD-H zjWY?{Fd8=Fa6&d@0+pW9nBt-!muY@I9R>eD5nEDcU~uHUT04gH-zYB>Re+h4EX|IH zp`Ls>YJkwWD3+}DE4rC3kT-xE89^K@HsCt6-d;w*o8xIHua~||4orJ<7@4w_#C6>W z2X$&H38OoW8Y-*i=@j*yn49#_C3?@G2CLiJUDzl(6P&v`lW|=gQ&)DVrrx8Bi8I|$ z7(7`p=^Lvkz`=Cwd<0%_jn&6k_a(+@)G^D04}UylQax*l(bhJ~;SkAR2q*4>ND5nc zq*k9(R}Ijc1J8ab>%Tv{kb-4TouWfA?-r(ns#ghDW^izG3{ts{C7vHc5Mv?G;)|uX zk&Fo*xoN`OG9ZXc>9(`lpHWj~9!hI;2aa_n!Ms1i;BFHx6DS23u^D^e(Esh~H@&f}y z(=+*7I@cUGi`U{tbSUcSLK`S)VzusqEY)E$ZOokTEf2RGchpmTva?Fj! z<7{9Gt=LM|*h&PWv6Q$Td!|H`q-aMIgR&X*;kUHfv^D|AE4OcSZUQ|1imQ!A$W)pJtk z56G;0w?&iaNV@U9;X5?ZW>qP-{h@HJMt;+=PbU7_w`{R_fX>X%vnR&Zy1Q-A=7**t zTve2IO>eEKt(CHjSI7HQ(>L5B5{~lPm91fnR^dEyxsVI-wF@82$~FD@aMT%$`usqNI=ZzH0)u>@_9{U!3CDDC#xA$pYqK4r~9cc_T@$nF1yODjb{=(x^({EuO?djG1Hjb{u zm*mDO(e-o|v2tgXdy87*&xVpO-z_q)f0~-cf!)nb@t_uCict?p-L%v$_mzG`FafIV zPTvXK4l3T8wAde%otZhyiEVVU^5vF zQSR{4him-GCc-(U;tIi;qz1|Az0<4+yh6xFtqB-2%0@ z&=d_5y>5s^NQKAWu@U#IY_*&G73!iPmFkWxxEU7f9<9wnOVvSuOeQ3&&HR<>$!b%J z#8i?CuHx%la$}8}7F5-*m)iU{a7!}-m@#O}ntat&#d4eSrT1%7>Z?A-i^Y!Wi|(we z$PBfV#FtNZG8N-Ot#Y>IW@GtOfzNuAxd1%=it zDRV-dU|LP#v70b5w~fm_gPT6THi zNnEw&|Yc9u5lzTVMAL} zgj|!L&v}W(2*U^u^+-e?Tw#UiCZc2omzhOf{tJX*;i2=i=9!kS&zQN_hKQ|u7_3vo6MU0{U+h~` zckXGO+XK9{1w3Z$U%%Fw`lr7kK8PzU=8%0O8ZkW`aQLFlR4OCb^aQgGCBqu6AymXk zX!p(JDJtR`xB$j48h}&I2FJ*^LFJzJQJ0T>=z{*> zWesZ#%W?fm`?f^B^%o~Jzm|Km5$LP#d7j9a{NCv!j14axHvO<2CpidW=|o4^a|l+- zSQunLj;${`o%xrlcaXzOKp>nU)`m{LuUW!CXzbyvn;MeK#-D{Z4)+>xSC)km=&K%R zsXs3uRkta6-rggb8TyRPnquv1>wDd)C^9iN(5&CEaV9yAt zM+V+%KXhGDc1+N$UNlgofj8+aM*(F7U3=?grj%;Pd+p)U9}P3ZN`}g3`{N`bm;B(n z12q1D7}$``YQC7EOed!n5Dyj4yl~s0lptb+#IEj|!RMbC!khpBx!H-Kul(_&-Z^OS zQTSJA@LK!h^~LG@`D}sMr2VU#6K5Q?wqb7-`ct2(IirhhvXj?(?WhcNjJiPSrwL0} z8LY~0+&7<~&)J!`T>YQgy-rcn_nf+LjKGy+w+`C*L97KMD%0FWRl`y*piJz2=w=pj zxAHHdkk9d1!t#bh8Joi1hTQr#iOmt8v`N--j%JaO`oqV^tdSlzr#3 zw70~p)P8lk<4pH{_x$^i#=~E_ApdX6JpR`h{@<Y;PC#{0uBTe z1Puhl^q=DuaW}Gdak6kV5w);35im0PJ0F)Zur)CI*LXZxZQTh=4dWX}V}7mD#oMAn zbxKB7lai}G8C){LS`hn>?4eZFaEw-JoHI@K3RbP_kR{5eyuwBL_dpWR>#bo!n~DvoXvX`ZK5r|$dBp6%z$H@WZ6Pdp&(zFKGQ z2s6#ReU0WxOLti@WW7auSuyOHvVqjaD?kX;l)J8tj7XM}lmLxLvp5V|CPQrt6ep+t z>7uK|fFYALj>J%ou!I+LR-l9`z3-3+92j2G`ZQPf18rst;qXuDk-J!kLB?0_=O}*XQ5wZMn+?ZaL5MKlZie- z0aZ$*5~FFU*qGs|-}v-t5c_o-ReR@faw^*mjbMK$lzHSheO*VJY)tBVymS^5ol=ea z)W#2z8xCoh1{FGtJA+01Hwg-bx`M$L9Ex-xpy?w-lF8e*xJXS4(I^=k1zFy|V)=ll z#&yez3hRC5?@rPywJo2eOHWezUxZphm#wo`oyA-sP@|^+LV0^nzq|UJEZZM9wqa z5Y}M0Lu@0Qd%+Q=3kCSb6q4J60t_s(V|qRw^LC>UL7I`=EZ zvIO;P2n27=QJ1u;C+X)Si-P#WB#phpY3XOzK(3nEUF7ie$>sBEM3=hq+x<=giJjgS zo;Cr5uINL%4k@)X%+3xvx$Y09(?<6*BFId+399%SC)d# zk;Qp$I}Yiytxm^3rOxjmRZ@ws;VRY?6Bo&oWewe2i9Kqr1zE9AM@6+=Y|L_N^HrlT zAtfnP-P8>AF{f>iYuKV%qL81zOkq3nc!_?K7R3p$fqJ?};QPz6@V8wnGX>3%U%$m2 zdZv|X+%cD<`OLtC<>=ty&o{n-xfXae2~M-euITZY#X@O}bkw#~FMKb5vG?`!j4R_X%$ZSdwW zUA0Gy&Q_mL5zkhAadfCo(yAw1T@}MNo>`3Dwou#CMu#xQKY6Z+9H+P|!nLI;4r9@k zn~I*^*4aA(4y^5tLD+8eX;UJW;>L%RZZUBo(bc{)BDM!>l%t?jm~}eCH?OOF%ak8# z*t$YllfyBeT(9=OcEH(SHw88EOH0L1Ad%-Q`N?nqM)<`&nNrp>iEY_T%M6&U>EAv3 zMsvg1E#a__!V1E|ZuY!oIS2BOo=CCwK1oaCp#1ED_}FGP(~Xp*P5Gu(Pry_U zm{t$qF^G^0JBYrbFzPZkQ;#A63o%iwe;VR?*J^GgWxhdj|tj`^@i@R+vqQWt~^ z-dLl-Ip4D{U<;YiFjr5OUU8X^=i35CYi#j7R! zI*9do!LQrEr^g;nF`us=oR2n9ei?Gf5HRr&(G380EO+L6zJD)+aTh_<9)I^{LjLZ} z{5Jw5vHzucQ*knJ6t}Z6k+!q5a{DB-(bcN*)y?Sfete7Y}R9Lo2M|#nIDsYc({XfB!7_Db0Z99yE8PO6EzLcJGBlHe(7Q{uv zlBy7LR||NEx|QyM9N>>7{Btifb9TAq5pHQpw?LRe+n2FV<(8`=R}8{6YnASBj8x}i zYx*enFXBG6t+tmqHv!u~OC2nNWGK0K3{9zRJ(umqvwQ~VvD;nj;ihior5N$Hf@y0G z$7zrb=CbhyXSy`!vcXK-T}kisTgI$8vjbuCSe7Ev*jOqI&Pt@bOEf>WoQ!A?`UlO5 zSLDKE(-mN4a{PUu$QdGbfiC)pA}phS|A1DE(f<{Dp4kIB_1mKQ5!0fdA-K0h#_ z{qMsj@t^!n0Lq%)h3rJizin0wT_+9K>&u0%?LWm<{e4V8W$zZ1w&-v}y zY<6F2$6Xk>9v{0@K&s(jkU9B=OgZI(LyZSF)*KtvI~a5BKr_FXctaVNLD0NIIokM}S}-mCB^^Sgqo%e{4!Hp)$^S%q@ zU%d&|hkGHUKO2R6V??lfWCWOdWk74WI`xmM5fDh+hy6>+e)rG_w>_P^^G!$hSnRFy z5fMJx^0LAAgO5*2-rsN)qx$MYzi<_A=|xez#rsT9&K*RCblT2FLJvb?Uv3q^@Dg+J zQX_NaZza4dAajS!khuvt_^1dZzOZ@eLg~t02)m2+CSD=}YAaS^Y9S`iR@UcHE%+L0 zOMR~6r?0Xv#X8)cU0tpbe+kQ;ls=ZUIe2NsxqZFJQj87#g@YO%a1*^ zJZ+`ah#*3dVYZdeNNnm8=XOOc<_l-b*uh zJR8{yQJ#-FyZ!7yNxY|?GlLse1ePK!VVPytKmBwlJdG-bgTYW$3T5KinRY#^Cyu@& zd7+|b@-AC67VEHufv=r5(%_#WwEIKjZ<$JD%4!oi1XH65r$LH#nHHab{9}kwrjtf= zD}rEC65~TXt=5bg*UFLw34&*pE_(Cw2EL5Zl2i^!+*Vx+kbkT_&WhOSRB#8RInsh4 z#1MLczJE+GAHR^>8hf#zC{pJfZ>6^uGn6@eIxmZ6g_nHEjMUUfXbTH1ZgT7?La;~e zs3(&$@4FmUVw3n033!1+c9dvs&5g#a;ehO(-Z}aF{HqygqtHf=>raoWK9h7z)|DUJ zlE0#|EkzOcrAqUZF+Wd@4$y>^0eh!m{y@qv6=C zD(){00vE=5FU@Fs_KEpaAU1#$zpPJGyi0!aXI8jWaDeTW=B?*No-vfv=>`L`LDp$C zr4*vgJ5D2Scl{+M;M(#9w_7ep3HY#do?!r0{nHPd3x=;3j^*PQpXv<~Ozd9iWWlY_ zVtFYzhA<4@zzoWV-~in%6$}Hn$N;>o1-pMK+w$LaN1wA95mMI&Q6ayQO9 zTq&j)LJm4xXjRCse?rMnbm%7E#%zk!EQiZwt6gMD=U6A0&qXp%yMa(+C~^(OtJ8dH z%G1mS)K9xV9dlK>%`(o6dKK>DV07o46tBJfVxkIz#%VIv{;|)?#_}Qq(&| zd&;iIJt$|`te=bIHMpF1DJMzXKZp#7Fw5Q0MQe@;_@g$+ELRfh-UWeYy%L*A@SO^J zLlE}MRZt(zOi6yo!);4@-`i~q5OUAsac^;RpULJD(^bTLt9H{0a6nh0<)D6NS7jfB ze{x#X2FLD2deI8!#U@5$i}Wf}MzK&6lSkFy1m2c~J?s=!m}7%3UPXH_+2MnKNY)cI z(bLGQD4ju@^<+%T5O`#77fmRYxbs(7bTrFr=T@hEUIz1t#*ntFLGOz)B`J&3WQa&N zPEYQ;fDRC-nY4KN`8gp*uO@rMqDG6=_hHIX#u{TNpjYRJ9ALCl!f%ew7HeprH_I2L z6;f}G90}1x9QfwY*hxe&*o-^J#qQ6Ry%2rn=9G3*B@86`$Pk1`4Rb~}`P-8^V-x+s zB}Ne8)A3Ex29IIF2G8dGEkK^+^0PK36l3ImaSv1$@e=qklBmy~7>5IxwCD9{RFp%q ziejFT(-C>MdzgQK9#gC?iFYy~bjDcFA^%dwfTyVCk zuralB)EkA)*^8ZQd8T!ofh-tRQ#&mWFo|Y3taDm8(0=KK>xke#KPn8yLCXwq zc*)>?gGKvSK(}m0p4uL8oQ~!xRqzDRo(?wvwk^#Khr&lf9YEPLGwiZjwbu*p+mkWPmhoh0Fb(mhJEKXl+d68b6%U{E994D z3$NC=-avSg7s{si#CmtfGxsijK_oO7^V`s{?x=BsJkUR4=?e@9# z-u?V8GyQp-ANr%JpYO;3gxWS?0}zLmnTgC66NOqtf*p_09~M-|Xk6ss7$w#kdP8`n zH%UdedsMuEeS8Fq0RfN}Wz(IW%D%Tp)9owlGyx#i8YZYsxWimQ>^4ikb-?S+G;HDT zN4q1{0@|^k_h_VFRCBtku@wMa*bIQc%sKe0{X@5LceE`Uqqu7E9i9z-r}N2ypvdX1{P$*-pa$A8*~d0e5AYkh_aF|LHt7qOX>#d3QOp-iEO7Kq;+}w zb)Le}C#pfmSYYGnq$Qi4!R&T{OREvbk_;7 zHP<*B$~Qij1!9Me!@^GJE-icH=set0fF-#u5Z{JmNLny=S*9dbnU@H?OCXAr7nHQH zw?$mVH^W-Y89?MZo5&q{C2*lq}sj&-3@*&EZaAtpxiLU==S@m_PJ6boIC9+8fKz@hUDw==nNm9? z`#!-+AtyCOSDPZA)zYeB|EQ)nBq6!QI66xq*PBI~_;`fHEOor}>5jj^BQ;|-qS5}1 zRezNBpWm1bXrPw3VC_VHd z$B06#uyUhx)%6RkK2r8*_LZ3>-t5tG8Q?LU0Yy+>76dD(m|zCJ>)}9AB>y{*ftDP3 z(u8DDZd(m;TcxW-w$(vq7bL&s#U_bsIm67w{1n|y{k9Ei8Q9*8E^W0Jr@M?kBFJE< zR7Pu}#3rND;*ulO8X%sX>8ei7$^z&ZH45(C#SbEXrr3T~e`uhVobV2-@p5g9Of%!f z6?{|Pt*jW^oV0IV7V76Pd>Pcw5%?;s&<7xelwDKHz(KgGL7GL?IZO%upB+GMgBd3ReR9BS zL_FPE2>LuGcN#%&=eWWe;P=ylS9oIWY)Xu2dhNe6piyHMI#X4BFtk}C9v?B3V+zty zLFqiPB1!E%%mzSFV+n<(Rc*VbvZr)iJHu(HabSA_YxGNzh zN~O(jLq9bX41v{5C8%l%1BRh%NDH7Vx~8nuy;uCeXKo2Do{MzWQyblZsWdk>k0F~t z`~8{PWc86VJ)FDpj!nu))QgHjl7a%ArDrm#3heEHn|;W>xYCocNAqX{J(tD!)~rWu zlRPZ3i5sW;k^^%0SkgV4lypb zqKU2~tqa+!Z<)!?;*50pT&!3xJ7=7^xOO0_FGFw8ZSWlE!BYS2|hqhQT8#x zm2a$OL>CiGV&3;5-sXp>3+g+|p2NdJO>bCRs-qR(EiT&g4v@yhz(N5cU9UibBQ8wM z0gwd4VHEs(Mm@RP(Zi4$LNsH1IhR}R7c9Wd$?_+)r5@aj+!=1-`fU(vr5 z1c+GqAUKulljmu#ig5^SF#{ag10PEzO>6fMjOFM_Le>aUbw>xES_Ow|#~N%FoD{5!xir^;`L1kSb+I^f z?rJ0FZugo~sm)@2rP_8p$_*&{GcA4YyWT=!uriu+ZJ%~_OD4N%!DEtk9SCh+A!w=< z3af%$60rM%vdi%^X2mSb)ae>sk&DI_&+guIC88_Gq|I1_7q#}`9b8X zGj%idjshYiq&AuXp%CXk>zQ3d2Ce9%-?0jr%6-sX3J{*Rgrnj=nJ2`#m`TaW-13kl zS2>w8ehkYEx@ml2JPivxp zIa2l^?)!?Y*=-+jk_t;IMABQ5Uynh&LM^(QB{&VrD7^=pXNowzD9wtMkH_;`H|d0V z*rohM)wDg^EH_&~=1j1*?@~WvMG3lH=m#Btz?6d9$E*V5t~weSf4L%|H?z-^g>Fg` zI_Q+vgHOuz31?mB{v#4(aIP}^+RYU}^%XN}vX_KN=fc{lHc5;0^F2$2A+%}D=gk-) zi1qBh!1%xw*uL=ZzYWm-#W4PV(?-=hNF%1cXpWQ_m=ck1vUdTUs5d@2Jm zV8cXsVsu~*f6=_7@=1 zaV0n2`FeQ{62GMaozYS)v~i10wGoOs+Z8=g$F-6HH1qBbasAkkcZj-}MVz{%xf8`2 z1XJU;&QUY4Hf-I(AG8bX zhu~KqL}TXS6{)DhW=GFkCzMFMSf`Y00e{Gzu2wiS4zB|PczU^tjLhOJUv=i2KuFZHf-&`wi>CU0h_HUxCdaZ`s9J8|7F}9fZXg`UUL}ws7G=*n zImEd-k@tEXU?iKG#2I13*%OX#dXKTUuv1X3{*WEJS41ci+uy=>30LWCv*YfX_A2(M z9lnNAjLIzX=z;g;-=ARa<`z$x)$PYig1|#G;lnOs8-&rB2lT0#e;`EH8qZ_xNvwy7 zo_9>P@SHK(YPu*8r86f==eshYjM3yAPOHDn- zmuW04o02AGMz!S|S32(h560d(IP$;S7LIM(PC7Owwr$&XCbsQNY))+3HYS+ZcHTVq zJm;QsfA`#~_m8fwuI~DFb$@pE-h1t}*HZB7hc-CUM~x6aZ<4v9_Jr-))=El>(rphK z(@wMC$e>^o+cQ(9S+>&JfP;&KM6nff2{RNu;MqE9>L9t^lvzo^*B5>@$TG!gZlh0Z z%us8ys$1~v&&N-gPBvXl5b<#>-@lhAkg_4Ev6#R&r{ObIn=Qki&`wxR_OWj%kU_RW&w#Mxv%x zW|-sJ^jss+;xmxi8?gphNW{^HZ!xF?poe%mgZ>nwlqgvH@TrZ zad5)yJx3T|&$Afl$pkh=7bZAwBdv+tQEP=d3vE#o<&r6h+sTU$64ZZQ0e^Fu9FrnL zN-?**4ta&!+{cP=jt`w)5|dD&CP@-&*BsN#mlbUn!V*(E_gskcQ*%F#Nw#aTkp%x| z8^&g)1d!%Y+`L!Se2s_XzKfonT_BWbn}LQo#YUAx%f7L__h4Xi680GIk)s z8GHm59EYn(@4c&eAO)}0US@((t#0+rNZ680SS<=I^|Y=Yv)b<@n%L20qu7N%V1-k1 z*oxpOj$ZAc>L6T)SZX?Pyr#}Q?B`7ZlBrE1fHHx_Au{q9@ zLxwPOf>*Gtfv6-GYOcT^ZJ7RGEJTVXN=5(;{;{xAV3n`q1Z-USkK626;atcu%dTHU zBewQwrpcZkKoR(iF;fVev&D;m9q)URqvKP*eF9J=A?~0=jn3=_&80vhfBp?6@KUpgyS`kBk(S0@X5Xf%a~?#4Ct5nMB9q~)LP<`G#T-eA z+)6cl1H-2uMP=u<=saDj*;pOggb2(NJO^pW8O<6u^?*eiqn7h)w9{D`TrE1~k?Xuo z(r%NIhw3kcTHS%9nbff>-jK1k^~zr8kypQJ6W+?dkY7YS`Nm z5i;Q23ZpJw(F7|e?)Tm~1bL9IUKx6GC*JpUa_Y00Xs5nyxGmS~b{ zR!(TzwMuC%bB8&O->J82?@C|9V)#i3Aziv7?3Z5}d|0eTTLj*W3?I32?02>Eg=#{> zpAO;KQmA}fx?}j`@@DX-pp6{-YkYY81dkYQ(_B88^-J#rKVh8Wys-;z)LlPu{B)0m zeZr=9{@6=7mrjShh~-=rU}n&B%a7qs1JL_nBa>kJFQ8elV=2!WY1B5t2M5GD5lt|f zSAvTgLUv#8^>CX}cM(i(>(-)dxz;iDvWw5O!)c5)TBoWp3$>3rUI=pH9D1ffeIOUW zDbYx}+)$*+`hT}j226{;=*3(uc*ge(HQpTHM4iD&r<=JVc1(gCy}hK%<(6)^`uY4>Tj6rIHYB zqW5UAzpdS!34#jL;{)Fw{QUgJ~=w`e>PHMsnS1TcIXXHZ&3M~eK5l>Xu zKsoFCd%;X@qk#m-fefH;((&?Y9grF{Al#55A3~L5YF0plJ;G=;Tr^+W-7|6IO;Q+8 z(jAXq$ayf;ZkMZ4(*w?Oh@p8LhC6=8??!%@V(e}%*>fW^Gdn|qZVyvHhcn;7nP7e; z13!D$^-?^#x*6d1)88ft06hVZh%m4w`xR?!cnzuoOj(g9mdE2vbKT@RghJ)XOPj{9 z@)8!#=HRJvG=jDJ77XND;cYsC=CszC!<6GUC=XLuTJ&-QRa~EvJ1rk2+G!*oQJ-rv zDyHVZ{iQN$*5is?dNbqV8|qhc*O15)HGG)f2t9s^Qf|=^iI?0K-Y1iTdr3g=GJp?V z$xZiigo(pndUv;n1xV1r5+5qPf#vQQWw3m&pRT>G&vF( zUfKIQg9%G;R`*OdO#O;nP4o+BElMgmKt<>DmKO1)S$&&!q6#4HnU4||lxfMa-543{ zkyJ+ohEfq{OG3{kZszURE;Rw$%Q;egRKJ%zsVcXx!KIO0*3MFBx83sD=dDVsvc17i zIOZuEaaI~q`@!AR{gEL#Iw}zQpS$K6i&omY2n94@a^sD@tQSO(dA(npgkPs7kGm>;j?$Ia@Q-Xnzz?(tgpkA6VBPNX zE?K%$+e~B{@o>S+P?h6K=XP;caQ=3)I{@ZMNDz)9J2T#5m#h9nXd*33TEH^v7|~i) zeYctF*06eX)*0e{xXaPT!my1$Xq>KPJakJto3xnuT&z zSaL8NwRUFm?&xIMwA~gt4hc3=hAde#vDjQ!I)@;V<9h2YOvi-XzleP!g4blZm|$iV zF%c3G8Cs;FH8|zEczqGSY%F54h`$P_VsmJ6TaXRLc8lSf`Sv%s%6<4+;Wbs-3lya( z=9I>I%97Y~G945O48YaAq6ENPUs%EJvyC! zM4jMgJj}r~@D;cdaQ-j#`5zCRku}42aI<>CgraXuKDr19db~#|@UyM;f-uc!(KDsu z5EA@CsN>^t@oH+0!SALi;ud>`P5mQta+Lh*-#RHJ)Gin%>EaFLSoU`(TG7c|yeFvl zk|Yll%)h-*%WoI6M*j+4xw`OqiDVX{k-^V2{rzCIM9mzNHGP^D={!*P7T)%yDSI5- zkGA4}r3`)#Vl6JFJ3xG)8K;FTtII9o7jNHof_Z_Zc<%@-H4RPpyXudpf)ky zmTH$LFGxaIUGQ;l=>R>?+>ZSCU|@&+Gt@5Bj3w{L{KPpgQ<~)jqx0oNZSv9R&^A42 zzqJr?C#D-n>=9FjM=D=7h_$QO$KQ8*%0%)rI(Npai_JjE9_lBk75BQMI zkk4X5PATWgrub!fb5Hxi8{(Y<(GOO8^HECOA)eanyS{u%leQOkp;1W}_8eH?nPQxW zd#Z+uJfTK>g-TR3WPu~2Ru9A+NkuIICM@PyPmJn(GBZt;xFZNDMbw8`xzl2`(?UC- z#<*=*fo{UOvycb|b&4y0Nm!sHhFMI*Y$Olgh;BG#xBU+yxav82Ejj(ZvQ|64Wwy7I zN=DXx7(V^NTH3YRB4HOu6T5=DW86P`L#Ng!SuT{%&>Cq8>|o8lF^^U%MRU41TT?h& z!uJ$YdbM*2y?#`LJ2)XPoKq`hm$I3R{V5-;@u7!E9tH4sR(`Ab-Qh!|UN-a5fZ?P@2LWRvSv!hOk08;Yy!h&uEI-X}j+&v`X` zkqY%*F@{}DHL*Jgjg2}a54hwEV`63bK4>mL%D^YT|>m1-kX{876BRm&`Y#{$&oz($qWJL}T*tj42k+yu8fa=4b7VUPq()Wb~=L?DU0U-4*Iu^KMZBRByWn-@=_f(4){Or#| zpw}~Ajs6a=z!8_H59lqYlfnS77QY0pHpIz0#)}!EGhypupZeZe@%cv z6Dngnl*SsUy^a`v?>lARi6Yps@%32JpGQvrcd*A8LPLEInBEU2vriGvMqG!jh^=Gj zXvu5zpikqnt*e4&Un_e$2FAB?(yOS0JAzxh@nN?Blqc-)Pv`U}&E5|# z)97-9utpqi*`hR+$;eS)A+KK)CO)V`b?*}z&*+28mDfWI31)sF)tBg6LVlxS z225poL+O|x)5;skkj{rew<}TsDVqFMMLSgd;UK7^clMcObM~IgSq6!eJ($JP!KHPr zBJ&SHi{wLsgMzn1^#kV#_!NO@RG@B5lxBO7WfIAi@o`{_XQg(*{R=@Z(0ij+*i7sK zW5D%_fRN7l6qpytW2K1lUqP&W5jDT!AA9@q<;M!T=CKv*^MP)Er_uLL+Y53>**w7Y zQ!2?^4$wC;Soc!+#~d?Yec;NLdR z{~*hrSQS>UOMBe)1pHe0EsyO@d(IrU4ZiS&jL`wqv6Oqv=HbI^70qu9kn~wGkNL^> z!Pd2)i--+&zp^`#4@*Myg;3r(jt*h@RWgRt70byZr;0Na8n4!bmpuX1&gK=QK!@j< zH2fF7@2s0H0!9%VC-BIp(99@e@<%Ko?BB9uv*xPnZ5dQr z8r7~9cZXv(AZPY^<(X@}GARv&_}mfYA7`vdl=)g2GIyN(<}(b_S_N2--NKp$SgO<3 zRx|EabcjUSB44GaH3Kxmx3SW;E;Eia2Zs5SkbkQ8E%VQqr0J?tQjF~p;nbIXn+D;? zg;t3Jg7A@9U**@aaqs}9;%??Scm{zBIY2ceYAQd*W-hB-!+H&4#yrm*GtT*&#`FXx zGIVm}G<;Pj+h*KQ68S4rcIIGw-mkl039s@O4p9F%TC&&&xRL=N49v2PdBb$MxJoMo zQk8+Sv+F5m{xP1prZvn1=x-Q z&Yox|y&arZrLTm~<%o}VfPV#z+i&{)W5emXhx^g~8>eUe)|Vvwp8-x8d-MOj%@mSk zZ9i{-Hu8m-rfO##y(_Rv;Y@?6%h4Id#6%`7ah+IaQ13o7o>bG&ScMj&KO~QoCmNT6()+oo%B zugV3Da)t>unQq=tbD)FP{JmB~S5QCmb)lq9Fp(*|(UGeXr3kR?k35sKFs{{a*y+h0anA_K@iCi;BR6nFmKHC=@)rMmu=XWS1nVqD*=#${cFJ6<{e=U7!Rbg>Y0b~d#&viX+5m9aNAv=RAMt8=n6a&@t^|2LsKMR7xF z;Cmw>t0<=W2II;doX`p#bcjPV9z&3dhAObzcB9xXMslqr(y!P6+2kG>Eh!rx&ZKmW)Wk~_xh`?neJqVhJk~1eTvRF#ehRwpS>s1{vUx*qf&Jm z$)Wh|lmwYatW@U@*$<14>^|yYwmwFs)C5ke9hG42{gilSU#^ulO`M}`wJ_4*-3 zGb?hfQj_AGQBI?4ghGijqfu>uAYkLK#!^uGUXuctdn8Ae5I7}o+j{9MJiM|sf9Nc{ zuP&Ls@?rMe=IfJo!=iX?9&*4!Yjs5d?0Yx4cIFXrkSHRk17Fc@yM__fyFLLl6O9nT zQqaDXunH;!PpQ7+-&#wJVtJXl8LjIkh)5qmcqhErYrP31w5~#!tS{LYTWGKEtbpE%(hH>qV(!2KMfs#a z?ZzzbDB}(7+NWIiSBQ<_{3>;H;z}uZI;n2PKWJNxM=l;5-^zpu-}+1x|38lS-}6GX z6F=M~bUtHg98X@of>mgCH-&5g6UpXGAla<+g`b&MQANW6D^;zfSzq0mQ)*J%;&tPOYin?J*G7GqmQ=>jvWvOn6E?! z{$(CU7}zChEnl$(>xf`ZdeF2E9Bv=eH&T4HWAOQ!9gBs z{gl^|(78q-ioBS^rR2PEGZLe_4Rl**H(bB?84RHquCEKi8N#29u=Eoh(DV`ZX{+8< z3BIX<`sOFNBziFWS#-X%(e`0C_|Q8;Pw9izjNOF8h|kvmWCmDHM&pANC9MV<wEJ;W{-jXqm!zC+Y@Q1y_lLL zfV^(1{A;L%TWmyI)RPknVUB<4r+d42S(W=%bXd@YB(~d>ABq-E;t)ie6%ouy(Fg`p zuj<=I7^PDs5H+UsG}+GH}zoGt*{yKF&n23C7aW@ z4ydrRtFW-uuAUu@RWe&0c!N4!H;`!n@@t#u zxlGQB4rx(F7#&MKHPy}EI;d+l(G{1KG!ZBE)7)@P!AsUCCCb0IH!P5TW=GoNFcif`NB4en16Cp<7=fhz7^uQAjbJBH>@naf2ueMktmtZ|U|)ICDMN2r`mgMSl=qDwHL;}L-d~El>pf8UJRts_03eTj*hVy6H z5o!>?AcffORZq9!NJNa`-W4wMfe6I{3*rYUhIMA>y|T}KZ56HR5XEs{(|x#SDtP@N z5?12L0W7qfvWl8T-V+u=fkBH8!$}g)7hRs34m7~)^S&Ar zd`Kz7$S2Mz(|5H(Dwn$V7n8K2pqhHQ8!i{G4C~Y6_Ex&Y%EyXdw#Nj}VdG`XCN_1n zFg4;3DGjjUo$%=m@ui%z$JU66QK^qywvLKZpD6ZQ2Ve2VBps8rcvJ6^Cf^#H4?UQ5PW$4;b)55yIY9}@k@48RLtJa>7bofX{EUE7 z?0Cx0PeYbbLAelC-BfqHf_08;{lzC1kwr|a>5{O6*g<~wt6KYPfP5uW0w?VTO!M~Q z6H@n{cONp`{>hVjEIkOV6m^ZP^l;mGz=T&*5&`m84astyZ#XZ6CpH384tt%vSJ zsvYDC5u`D&U_u)1OJ&D2=F*ie-7!%N+V6*qoM6m-zj|}hDZ+@?`mJ10OX3K-`+R0m zNk$^+zBJK7%It=_&sIc}&DT>!LYU{|WPNrp-Nfly8u5&3@(l{!pcPxek3^{L`<9*! zE-0KukkD^^+<&3BNJM$e0=~B$=VQEp@V`L+PsUEL-_%+E_kyR-_mUjr|D1Z2J->y2 zZNHTrzP$=uEKQvy4DG&+4*o5^8Kd?eI>5S#b;NXlSrGVnj3~e^OLe4*Qe7%U#4WiX z)k7h@VHRERR_j{wp8ALHdD6bj&+Dl^?2(MuL9*oTRUI3SQ2jJ4x#!GR~b8F(H6|clt%g_O=v(@*;;5eW{e)CsR{UNDIE{C-1@qe z7NY&S7DeI4?z7tR9LJ$e6za%qLsF(>%M?m1nQQ4htpl?P)yj7_C#Ds5k5F z1h@YlI%a#k9x6}=hs(mkRr-fSrmikEk)Iv6D`S==)-dDVbNK;4F@J7iC(M!K6l<^lm@iXKpYbd7b{_0BDjc9ju~tFH7Qfcgu>A9~3tzmbFnXbS(pWES9955Vbu=iI zX>GH$kbD_?_fRojp{~Mz+%=%RHG!3l(wxQb{zQlW&MTlbr2*9|peUBo#YZ8u!UMPz zJo9lmW3isPrkErmxp&SA4Z4vpe~LLL-w6JUW}f*bf#w6lVyDvUhdK9fX!p#TT3fL+ z7im|;28gcWM)UdfRI;603BWd`d%7#sP0t)qNW*R*WmrD?hg37Zngmu{P;Lm`rlK_> zITGMQH~V(}6l6}TeG5nPEHYI3EHiY}TD%AAQ@%&*Q@w}lLp!VC>E;PCjzgVyNqNmA zYd0t~-pn55?#)1Tc-(xbL07m;Md14bPJOLyoRpLhRx-BtH{Z%<78P>0$olxWy4d9! zncKIDHrWFnBRUUqc`qiz@xrz52u-?2kq~5n$h}&*K?MxJ?xV?vVXvLErROVl7L9s; zedsv`#k1PCWY;`{${N?=R9%uy1P+jKf$&__RLHP zWVH#4;U{}bB4D^B*hm%nhRpQF{4?xW$&|oNp2CUE?Coyj1QI%P|w91%+*lty%ecgZ$I1|mJWq9_c?+4{KElHR%TIU zf+^4^hXY?f0&(|Q5=NG~AhiIVR+(a1gF)Q;L&vH%zPO{yydKt*(f#LehU3CVRIS&* zA1khb+xXe{29|Ggayz;nqv9M8n$JYj?Z!w0Sb}^lq#XQlg~=nkBhYxmlB{huZcL}F zA6sNZgJpJ|laA>P$V#ZhT+&$nvNM2sudEEeUaohc#ab+sC zrj7G)E-#;G-w=I1hTjN@b;lAjX40pR+<>)=n`V_!(JFk*yE zP3nDEs^C9DCSbs8`TV~U17Bmq%9I^$2xWK;N>;W~^^HOu)jQt*LH(-WD@UyR?lk$o z+mZhVgYn<1!ov1;W|rozPKN*0V#Xxdelr-6M$Gf?*Y~BQbHRK-&@B;ni(p_#pe0mg z(1pQKcH#lqe^P^eZVUta>(kWOPSnhH^E-oKtcJzCI^FSuJ zze(PI3_%VP4Fp7k#GyT8c6l?vndL`$$s5Z05+P==upnazJ>&{eIc?MW6fVO34pXfm zmmilQmRYtQ*e*BV>J{aqI%F$j*;=Tdx{msYgM{2Gd`D^TU>~NLKrbqtQDh6KPGcB& zYEY{fj~P1Q zY_vIx8j+W?nOTo{k7|A!vvlK?qYKZnTkm@qV7lWQf#;J@)(qh~m07vHwdQ@701t>}N2> zYt=Q^?p;5oP%enrkvLCarS2rlJ;zjT@1)Ha_28t7T(IMcZi3U?D_dTzMKnR%{b7 zXeWL6f-xfJvhsVNF_?I2^3gmv=2|f7azO~wc+o|=2cR+N_<9sF;vio2z;vtlV7U6o z%q9XNPhjS1Fv)QuRq|0#HVGw&HG!!t0wQo=W>hP)uYZ7o;_qdM=-*`k-Z%4+>VGZ; z{vGL`lv&#q*NFJmy`%{yAIPrAB%*freDk*5cHaNPB~B86YH zIw9gNDz9H+n0&}J-c0V{E(`My-2Nkt0NBY-PjL5r*s48D&j)h7pIpJUb+0ol1F*~` zp1!}vw0*&IA^z*SXZ}pIG9;ySrW01 zpU6d%LB2t@(;)LD!*G(DXK-!R!}Bp1mKS>Uu`^#p z>~WR%dn&;>iuz9Pv3W7EPX~GtnCg$63a-#A$1B7q;ZqH{xws^Pf-V1eO|D zHXE9qC~c)%CS>n>jc?m)ux2hN2UpKIU2hP(X}`Ljjc|CDFH%asVJH&6j5&Rb6aaVeQvSt z6VIX1X(pXAmxL>}wO&QIImzI9LcFhECJ|Mzi1FWhCgS$=^!!D3^vyEEY0HM0>?fsv zz1W(i8*H{v9APY$IW@J9NQ06Y@g$&STTrPC$I1{t0ptDZ=rHjEZnN2BSw{(Pn+6KD zRZ-hjn-KgzRa=ZoUs=W0cAc-}66Rmi)kZgub$G6zPQn>fM&}9X6!J^UsbVFdewj#M zt5erf{g$1$WV`h=0<2Y%iDK|HwH6hSu-8LDPknW`jl$UfmI_z9=GkC(@A$oVsRFl` zMYdksp797E2vzaH-N_%;t@q4}Z;FxZ(y&6&(#;_uzaGV+M%CB= zVNRMN3tj1#%##v%wdYNDfy0)|Q$>JYJ8-6o*K4hcC(;5F=_Mn-l)y@UX$ zt$YU7Q%o3cqwRC6;{vbL1No%d&)=)2$$;SD9a-=PfFh$6P1;*I*d z?C_52JLp$(UF}SCxJXTY+9?uE`@f35}k=i`#4Rk6e@*KDc^(tnQcw(jY^fcG z2hqo(q%7)o0YkX;lCq$o6hgCi3n%i#6vZ7x&_k#aW{QnPk2CWm8yVytzz-Xd_05x& zK3Vo>SFs-R)cf&`{&tL=xJVe`-HvE7&mAL^uj`W z%$d@~HtC6RV)R6}b6PqR$Pa7R8c3d_D4Hqq2NfG(>kTi!rOp%>Lc~n3!5mddW>>pR zt8tmTCxnr(Xk6g2^MqN08AmxcFLP;APA}^V80R_+K#agUx(RR48L2ZQej@XRm?OF3 z&jyIH+L2f<&wdR}X$XB~;2tBIf^AThY(zLA4*i6@9FdbT!Xy~7Ywt-zdi=wCIRuOL z73^T>|0wMU6&500dh%`EqjoMKS;Z+_5iFfnaLNy+B-@vyNWRdcmRaaBUdtQvT_Q17 zTG$aE4SA0iRA}+d@r;k~BwsTn@=r*;LgW8Q~>>Y9oke1Rm(xx!gv){TQFv|25IK_jjLj z_mxH%0-WoyI`)361H|?QVmz7;GfF~EKrTLxMMI`-GF&@Hdq@W!)mBLYniN*qL^iti)BMVHlCJ}6zkOoinJYolUHu!*(WoxKrxmw=1b&YHkFD)8! zM;5~XMl=~kcaLx%$51-XsJ|ZRi6_Vf{D(Kj(u!%R1@wR#`p!%eut#IkZ5eam1QVDF zeNm0!33OmxQ-rjGle>qhyZSvRfes@dC-*e=DD1-j%<$^~4@~AX+5w^Fr{RWL>EbUCcyC%19 z80kOZqZF0@@NNNxjXGN=X>Rfr=1-1OqLD8_LYcQ)$D0 zV4WKz{1eB#jUTU&+IVkxw9Vyx)#iM-{jY_uPY4CEH31MFZZ~+5I%9#6yIyZ(4^4b7 zd{2DvP>-bt9Zlo!MXFM`^@N?@*lM^n=7fmew%Uyz9numNyV{-J;~}``lz9~V9iX8` z1DJAS$ejyK(rPP!r43N(R`R%ay*Te2|MStOXlu&Na7^P-<-+VzRB!bKslVU1OQf;{WQ`}Nd5KDyDEr#7tB zKtpT2-pRh5N~}mdm+@1$<>dYcykdY94tDg4K3xZc?hfwps&VU*3x3>0ejY84MrKTz zQ{<&^lPi{*BCN1_IJ9e@#jCL4n*C;8Tt?+Z>1o$dPh;zywNm4zZ1UtJ&GccwZJcU+H_f@wLdeXfw(8tbE1{K>*X1 ze|9e`K}`)B-$3R$3=j~{{~fvi8H)b}WB$K`vRX}B{oC8@Q;vD8m+>zOv_w97-C}Uj zptN+8q@q-LOlVX|;3^J}OeiCg+1@1BuKe?*R`;8het}DM`|J7FjbK{KPdR!d6w7gD zO|GN!pO4!|Ja2BdXFKwKz}M{Eij2`urapNFP7&kZ!q)E5`811 z_Xf}teCb0lglZkv5g>#=E`*vPgFJd8W}fRPjC0QX=#7PkG2!}>Ei<<9g7{H%jpH%S zJNstSm;lCYoh_D}h>cSujzZYlE0NZj#!l_S$(^EB6S*%@gGHuW z<5$tex}v$HdO|{DmAY=PLn(L+V+MbIN)>nEdB)ISqMDSL{2W?aqO72SCCq${V`~Ze z#PFWr7?X~=08GVa5;MFqMPt$8e*-l$h* zw=_VR1PeIc$LXTeIf3X3_-JoIXLftZMg?JDcnctMTH0aJ`DvU{k}B1JrU(TEqa_F zPLhu~YI`*APCk%*IhBESX!*CLEKTI9vSD9IXLof$a4mLTe?Vowa0cRAGP!J;D)JC( z@n)MB^41Iari`eok4q+2rg;mKqmb)1b@CJ3gf$t{z;o0q4BPVPz_N!Zk0p~iR_&9f ztG4r5U0Fq~2siVlw3h6YEBh_KpiMbas0wAX_B{@z&V@{(7jze4fqf#OP(qSuE|aca zaMu)GD18I+Lq0`_7yC7Vbd44}0`E=pyfUq3poQ-ajw^kZ+BT=gnh{h>him533v+o7 zuI18YU5ZPG>90kTxI(#aFOh~_37&3NK|h?(K7M8_22UIYl$5*-E7X9K++N?J5X3@O z2ym8Yrt5Zekk;S{f3llyqQi)F-ZAq;PkePNF=?`k(ibbbYq)OsFBkC7^H7nb6&bhDx~F#muc#-a(ymv|)2@4)NQw!cgZ|NLJ@N6o#y!T* zi0kdtK#GC8e7m#SA9pSuiE5bOKs^ox%=l6KBL?8Rl;8R~V>7UCaz+Y_hEOZ^fT}$m{$;GJt9$l$m3ax6_ro{OH@r z8LmGIt2C9tM6fNUD<(Y1Q8w(aN2t@VPrjc;dLp9756VNLt9&>pX!L*6kyU=uui9e7 zrQ^&h7Nuk|fa1WH?@{DNg}C&i2BPX$%)+AMi%-ImT2Q_QnRV)3UbO2JW7T-JYoYnU!(}tii1LAN|D(%7cL@IEI0mCT0!t|kd)1KahVC2K z|9L76JA1F#-=|{!eJcN|r2bI={kK#3M*^rokSGIa zWe@gc$gT&!Q!WYqGHNy3PlhBvcjf&X0o_R>a?DGQ`e|uWa)>YuWk(ibM6r_Xpiaq4 zWtcFh6k&ih==f(%+T$`L1EYJ^CeevsviNKGK3iUF&1QI!EZOR4y2d?z{kh!@hfoR4 zR$n!oTq-{w^eSf-ckrX)rp`@DG4(8%e{AtoKlwoHjNIX8hY>P;3y*y_O8XZ8ien=J zQR{%EX3|XA79>Al$+8(rw$Y~9ydiaH!@*{;*H_Weng(B+tJe^@Hh~lm^J?rL_`0$g z%o51AI)M5AP4)R##rWU8U-|zQ>N#rK?x?C*TS+B3tQmUYjh6X32PBq4xJ`|D)tg%M zLwd8z7?Ds5CNhvE8H^bY$XD*~ke$yZo!3P40jio4f0GcqUohXX>C;+gOt>>PizdRd z?{b{G8+tZA!Aj6GmXFD*thAzMDL!h{90}jI=PdjS093DQi3v@l|5~^hKrwR6 zeUbcTjhPDLUg*ao;c>8JN}wB>MOIE^vN22t5147OVW>!BTDvz4xeP$B({i(Po~_BL z9*#5s@;l~%7S3?WkF0}E8>iN+UQZh{-D}3F##`x$+YG@H0vyyD%vY!zsJHcnGrN|& z;j<&E%0i6kwaMT{tjp$m5^V4*+9;13^DDjgaFvvOe3=j2hWU3(PY)kFXvfx#EJF(V zM!l@%;xJuF3pERftbWw~WnR$A&ok4UQ0dISRjNi-j7>!WdGm0^FUmns_uy2DYX1!< zihag3z-a%BI*WE?er9_UTY_Eui-R>cvS1;=N#Bv{mPKKIv5O9iXS- z3|WAAOhFjGB1il&5F9vj6Vm!t99VnZ6v)$mKW$!I)_=41msTtDQ`CAV`azZw#(aSt z5XK052F(2mTOy|hb~KaAM@(Gg9l3=rqXB79Zp!Q>)*)Hhm(8O3s53@BCx_ltYRV=o ztb3!SE4UlbZadeiDcr2NZnT1}MNd0Au}VRHKQ!`nW(2!sPW5ulYI zosR$tFs@ul-q2)^z}}Y;3$Jj4J#kik5ou3xxf)_JL$5C!E%MDFH5fza9unrHXXw5F zHY#AcZSU73&;sy;y;fM_*p0Txd{DmQVYSyT(8Bu@vSLZAPKlVDd&6%bHj%HaV1{=L z91uK99)#H)!*Q6S`Dv))pyUoDkMa0Sllw7Fvb!iKKjbR3>q-@zp>$lcNLt4(&F9yk z!g!~88ulk{z2xgG-3{{il~#8wah-S$PDsv)h$4v?e@iEW{%JRU21>lL%fw8~(DT#^ zywKIPee|O;<3lWQL$hEWAUeA2)~-xA7yV(I(Pe55DMTFD&6fP6bS3JXHE& ze2nS2pMh>pdB%}#XYcS*N|SMQmQ2J&7WZu72OP zj&wXEJHG2^_XZLJUco>yC|q(0L~1fPN+}|}7%$xcp-i$$kXV=D`~$(T`2Y)+8U2yu zvr%Mzd~RzcUfF#X_+uh&RV1fO9P&C;yFTuW5sb%e_xPYEB%AgtaOJ(ztnLEW_Hao2 zZHV-;f-^2epH zxn#@~NOA z11ZBV6tw5T5>Iz^Jb)0%OIlra;qJl^ufG156Ui{A2$qpZ_{^c1^R`+fbi*WT%;He@ zyieltZ{6ivdgz6i=@iEldc;jVS!5E5$rymBrD?v#K?Mr`?ocG-n&lL`@;sMYaM2m6 z)Tt641KSaR_(MIZi0J-0r(53x)8LPvfBwp-{yFxkKiTU)pdB)FGjC~7AfTS_$=v_Y z*Z#MJ`R|V^X!eb+h*>&0yC}OF{rl;vioX)<^+YRtY&IVpwZx%m(G%kbE0AM%G$dMnxO@9U~x`$qY-b?f@fkQ`9pNJeiFRud6ZB~-h_kWX>mCgONAn%y8FDS z1jJ5f3AGpr111cNW(=njoJxN_XIF;t1dO^e0km*ZO?76yVM(*B>Ix?cT=nC+o2XP$ zo!&hK$H9sd8H07(XoY2&7QG(*iL;qrs4U*82`MFg4P0Dzw%rEFXuGLBslk;D|Cf}sL{Bdj9TpChAGEEN*DvCLV(j_N-e zcLNc98=ZJ>3?UluoPSL2QwygpEHOrNp?KEVT77e1i3zzY%Y9lStpis{$m zm(cz{%HDxH)4xj^O$Qy@?AW%`NjkP|cWgVkW81cE+qP}nZ)X0p&N}nVoOeCvGhF+3 z?b@|#SADRMCTILsR4>rrHy4AU0PJ{|)~M^(@q-e3hLdj7_}OdzCb7?6jvhyQy!)3Gv3ELg)6!VjwA<}NC@GK%{NI0 zJT}T#aRk{>TXHs_T?t5eRw>v2ntXC6^p*jkWo`a)WZ0?8&JFWArnx^e@#->FsW0`H zaG;x(iE*;8ugY6Nhw%)c!hpKUyX3jhGA*i6J6@(fUBPL$z{4dz!^d6OL#hN?41I+g z!KjR5!+yZ+z+Y#U0p;s{fV{jmnQyy>%`Eu5GUWo&fsZL97=D~-b_O#00NQ+zO>XS` z6cn1v6jGixMb@=ItgwK*pbiAms3``uBok32wSnIF!(VPSH!Aca2(cTt_k_R zo!iTIMT0nvu%dfM`Tm^UEy_oqiKOy5hANU5*kqB?bbwBoz>e&)X{#5b+bFeY#FB}p zj#JFe|1ix8(itqE%U8Oe9{8p+lmPB#ITX?HhA~WU^`aMeLagZ?{J#$k1(<*Ga=!-# z(r?kozXS&T@4ut}e53yWT>JmB5K8z*I`ZXC(_u$bUyRSI0_sa;;}c3a_~)8{7*#4- z*hR0l-h`v$GUX!Y8S$OAGx`t7Oh5c~5aXowl-+DBh(YT4|& zz2Q~Iz2(b(#FdLc$(X>h-N-=%K&sS{-j3KfIshl~vZ(yd@zZNg`=RANO&IW5GfVZE zs6mU)V!n_RSxggdO;6lhUb4T6hUvzQ$bXz{bZkC4QCxql0E>+~jH^F@J~OC%bQSnw z!dVcM*I_fSE>Yp7Ty9TQ8VjoGh>2rpcziKFwP#ZBOnF7Eb+fb#57*n=S;keHfwc zH49H*3q*cDponQrD`v$M1l5b=n=zY6HiA!3d-3ZhDZ+LzKN9kDW#xrc^yy*`$5>{c zL~=_5`{q}NdlgOp5;!td)>hv&2umQuUJip0G-qJ0O^3tqXGdqmn}Z9DTz4j33Oh6* zRs?8e!2wbIsGfGP{9#WZD|RF{E86KJLEy$vz9KuntCBzNS(>A~j5a$SlK;1USU4_S zB~S;>^=U+8Kqh5?r+Nbfvr>prvVolf25hJ>p9%wx5ew2uyC4l%vXv}jkoT5T@NOml z^@+(g=Fks#f9@XKR3CWI`oEWac$gIO`*&M%ga!iQ{=d%2|J9ZRjEt@AzT>j~_r7Ge zrikzvS+U<-JIh%phK;}dvq;P%#NIq@*-Ro zG795&jLHtK3kt@gsFnVb^geyY&Q#0!O5NK<5l`92U6zg)2z^ixqqM;dD69k{pn5na zjzCXM7%i#qTM&x#D|7;Cs8qI%RB+HS5}ROsznNr@l{c2b$1$=!oSc;%3db4qHN!gG z%>$rEZM~8pIiTEB<|bT*mBLb{tT1uWu6OFJ)KF7(hj^P2rs5QyMx#q_*|BJuoXwJv zyh%!-X{q#YM`heA8Hj!57>5|U9qR_sVak1r z2ZH_d(s!DNqIuDZc5gkw(w^h@n7~LZ82aCz6|aG^n5bXeTCFdW z7m@2Ej5B%8MSD2HAr*BPh~b^9^;NJ~HXJJX7VeGl(#=!DS?r0mNIH^}d}=~&Ui+B^ z_wm)B4@6oIZ9FP|3#qxxW6-_;>b*pN_iexjXi=h}e`(krgGC?N9fbTnyYPYIO6K}B zFA_P-suUrOEb6b`R1i9SkQ*s2Jb7^Y-tOTodB9(}j@~WUg#QJE`jW#~0+;?p-Oyv- zf|?tPS8>)50*6Qh^}EqVu&_nQ+F^C-IvX6tCg-UDYg3UXsv^pjsXxyJD>pVkh$z=?hWh9Cyd8bJRGUUU{A@XK zEFVF%XrUA0yYJ(VcELR{+rh(`Av6SI^lRD?z)AQ$gLvakWpQF`_zp{aqZKUt@U1H2uD*qV*seS(QQ2Dy-oc-O8X zMKUd~h#|T^-6H}`fk?iJx;2kI2$Jj;QIf6%C{vhRVjqTvaHy7Wq*g(r%|c-3w(n|C zr9N;Rs9JfUDeCWJFL}uP;Y0FDf(Wy};!IZ2zFjeU(d+_6MEJlaX*p=3D!D0b>op*k zuYr23N1W0wly8w74c#W1LpXP|?)nWr(3eXs$E(c&PiERe!JWE^z0mm5cg@7F`_!@X za8nQpF$jOM+JDY~nb?BoW=-xIQ22c3TFS?M{R<~rPg$le_1#FXz85*d|IS}UP|x1z z+ey;M%HGW3JB?4_`{vKeW ztvEN4bJui=CcnsQr$FVybke#RDpaIHY{GaczId-A9x@ zD;Gi-lJ9Iau-2o;`eV1*3ztzN3!P`Jxrc)3ocRRAct^jD5E<^lS-Z2}IFL)oUQ<%h z4?B_#BP>07`M}`7ywGkk}UQpFIOvRZx*v_~StXIsHv% zk|F{D@%%dlD`92rZ1oTF`=>D~IOsVT{euA~R8PKHPL!_>)`|SN9}+Q?LbiX7V;y|` zxRlL>%Ik$H(5Pr(Mxx>JnH-I0{je|Ff^ zz-BM|Nl%;W&QA{{-tTu0O+e~5f#GiJBzZraC7MNqDOlr?|LhqN(b;MvwI7GKiU~0K z{eT373oTRU0c$+Rhw4@XlTr&~#ma@bzsx0Wj}{NwfD$q4FH;&|U+$&78LfwdW8CyW z;OP%PLaqA+xw`)8&GY!c(BaeeC9Brzjgx$h5BNTOB+6D5tkg^CsI*KLgPcM%ya0vp zbV@C>a?WQSn!)u=q#cuPB(|i9nbp{($Sdf>!kHiclcaabX4aUu7DhI!LxJ!}0zu6Q zTOuR4jCzAp4HQB~$lx0-I*OxW?+7`C+)yPz2LhTJcEWDtrjrKPGYcx7JOz5>Fq1BbCwdcc~)V(_dWb^W^Cg+d`E znHou4u_BxEZ#{w1)X2Kp1f&31bB$h<4(gDTg@SKrHdbYIH!LCpjoWx$m6H?^Rn_?n zQtIMb-Te>usVOR~oBNm|$%EuM-Al$LI7T(caHlUC_)EwIwb_}nTuQcJOCTkj73b`fRMv9KQcH|un^M#jXkC}A*2{;)>XL4t%9j;TE~jj=;kQxkt|4?2+jG$ zO>MA4Ihwb3fs%0QJ?(xri>|+HFKQwe~VKVDLRp+kcn%p&_N|cAcOg@pMI36hxJ}`pdX&g37 z;cjX3*$bO0ZP)WGjS+*#9BPg-k|%%ld(u(z6#Rs)CdDq3v`;~(3yzuCIThvMSR?)N8k)5*zG&`Z5~4mo5!kDs8X%#wWG=BAOu>f;BBx)i={ZF2%pg&8u9OHu$RwHWi(Zrnb_F!S4}H4Pemup{B?g&x zU#uE<^xzLw!p;7LfV$qJaB~})?F?0goeb3_q^thbL^rZUwm(m}&9u{(G_k#^JTnZ# z?ls#Ol&@v+(`?BLI#?e_JDXMXZ{(A&w5)*9@rU$xbIzoJK{+Kq$9~gGf?d^9H95ge z9~bmk_TQ;pQR=n`mb-!up;6q>rJg5h&~DXGOL10ZCpZElV9+NXAe{ z(U{+>WGl-7n9_cB;esbv`zQd5PGDmtwrS6_?5O|j?f&4!=Swn)P&{DTRm#Q z?lZCaTsQRukADw>9hvymR@=x9j+`A^;gGe7opW<)l3(+nJ@lsz+RXHLf8DN7;}xZk z?qsC(lwIfrLNr`%cX`j&a39Sp*W&E5ABI{ZAa5xsdUx~eii8JeRZF~w%iTbC#CrAF z-f(##d2g%O_TH()d(?*AHm2=rhVJdR;EgIyP9gikuT_JX+bTqZK_f(F?2|1`kjc^R zBzDQ!BZWG%cOfa7HvQaL{Ub@Sf-hnaA$2DxLI5WNxlEM_Y{{$4dSJMYh7u9pnQdxV z4jn2yc%eOWUGmF0IvlC|>3K7RbP86le>*$oQf1o9Hu$U5W?FiyW4x15Ke~2{<~fNTN9&{nZ5ltn)|0&e(%8lU!5}Jn=P4>{Wc_V#@<*& z#iR_5lKis*QVSbHPz*U4gh7_7OW&h{zBrzGiDu1}dlO-OKldzv6xfgM1;iJBv)(xV zL*nOH>}C4e_pM>gMOIgr7fA9zY$T{1XY4SU7$v!*x(F28!b*5-sBQdSve9%p&6M3A zoF)u_&hxDVt(HQi+d30wc#%MI?O*#P7A-(aDiQVoVBc|#+G2bKX3W9;9o8 zD4HbHZV4&TIV&gj0z6v7AXq7b^MENIMn!!BR-tnjn>8c7k|S+hdv8|W%?0CbQ$7B2 z*nZ5BW(Fd9tQJwZVVWzfGE-5!b%f6Gtb7t<-@dIT#=TMz3ERX_;%e*+5i3(E=Fe|ao}{&(4(W{aQ4Aoc)ELdd z5xg&)DFQ19QdauMEM#(&`Aef|XP5yeP7=4gf8P)3_V6z`))+>cj3Zt1W8V+5k z6@?Vs07*I%!{dvD{3k3PvAAMT~6`Iim@M4XaO_%YOCvyx_aZ#OE zEoQCTV=MOnIy3QCDFvy%ko~6YBp3`2U{rdbr*BHVsIz1!_!-at!VxNhO7NC`mw*3v z`Ttu;@xSWcS?XvTO7%Eu&JIN?8S!yGelAjipZZjjL?kL>E`1=KPegVn$cd#Q3 zmrT=BIxi`@g_jH)Xa+_?g2hpyNK%m(2OB8!%k?+{0(O|w)+-aJ*9?afapdUc!Kzrs z{bs76WLj({R!@J8BMHvCo3*s0;2pzhzGX)r8;v!#bHTvh^<3+|+&~E$E|kdCik&Q* zvXm9N43@#(!o=hFvr%fQ&OT-!rqBw$jx?HZJdVPlcdD=K;SDr6uCWgM^>3>bYYyzD zw(m$e)>4rAZ2TKb((Vb1@C$)B zlGwcqUCU-rWbV8uqUIsl`VCcnOj-itFqI_2Vd=!Iq?jNi9x#_YHyx#bWu>p$(+<#3 zm8~w;gB*jg_f08pzm}{qhFqd*D)ma%t4`7=-7rq(#5?lpDE3t^qTn!nJd{~h0E~E- zRQR>Q81&d@rddwej@!YvrbA+RoMKfi;I-d?R$U8^y^k3xwU)Hbm+Y+5OD;`JOia_@ z@eFpvBey;1Twd9l*KHO!*;QK5)5hjZ6$t;DMfiE(0a6m5?s6M|m_vXC)Q4Fs9sn_y zI!or%?trl8Gt;p&}Jf;`yVHP@rsXhgAkueW}cmxLXHXddup{SVk z>^B@F*hxOnbBoJ8BbZ4}yNfh{NlUbMcb;7pL3x^mNLtFPzQXori=YGCNI{)ZAZ2Ki zs3qvR(7N>3nl%-R(nxn9g25ba>ww@!Zk2n&Ba}d16bhv_#ER1_5xYp4v>EZSD=SiN zawHYv%hwEpP%wK16R};MR@m~tu!hMb+v9EDkD&DX5wQI`eh`K1)O`&W>qHzi z!b-DJ&}vPMc~072@*LfJeLTEC`v}F87}68vWOcpLQ|U|l0V(wYixZ*=QHzP%b48F5 zDzkei^(!En6E0%9u}ZGpvth=98Ab7vbAkWtt0*l8ho~bKg&k)N)D{X)Sw;9K%Rymb9ZkXRbICW~F^rHlD@gHfrM)$z@z z$hD#^b4Oa|U>c*}O;;{gCD0tASCj@XM=^K~@*b&A(W9HhBW7}y*>zs`L6&b(Numk+ z?}W2dTTY-k=m`2Mn)4HUL~E6!TYM-44baeHe*R4+@g^O;S2E_999y!?b&i{oCw2p8XKj8~?@*s%WZ!JnBS*(vHBdP{u*jZ;&mPhgW- z$TymUXpLsqmETA3RIEm7PvM~#n2jc{hcz=P?u0)H3}EOmNcTzyZTDabzVJS};Lw~R z^_n%#OhfmE{M47|-{~Pe!$80aEMfivs=~;(cxH+gPUI*ZYK)Fs^CUuPfB%5wwKIf`Er>NFR$wv_^&lqkC2)JPA$tSp%^o25 zAg&XPxP;|y!~aPnY+-Z{-RB5sI)^EdId1W3Ryen*fIbqnZ*#ViWDj((OR4xJM)(;? z@Cf4i$TZxF!ziNG;)MR>mr=gWYsSqO1fHC|%#CXi%S_NF)#i?IVU?g9jGmIR0)3Bq z;tln(pGsuhYpC|QPZ-M*8&b?$?(Qip*nJ?akUU7FF0*UvGnI!R3f3ehEjPhPEH4?iI+hc$O*6CpeI~ z4Sg%6ZtDeiGX3M@Xb0VgXkGxN8nJgs*k=MrN#I7+%!m&e>Y)R!$GXr{Ox1#dMkdI= zlKCh%&BnMT;qlKbqHxO{`^lO_0%GE1Wrg?yydI<3s6he$-Lq$K9S~S3G^v4nX^Z) zB1xZCP}vgY{yApKcg{ysSWd~`b){kFXX{Ue7MRxdIp*Pn%tWiA;G zK}!DfOQSN$&ZWcr5-u-l7x|fv7&wHK*XJt#+uRJnB2FM~@^XCA<8EU7^5gaHgUsjK zVOWSyGNZpfk~vg>rhqFct7@kb;0^O2Xsel9!;mh_$I zaKvjBu*O_)8H>OOS4ydd6g-9Aa_$Ws${Ws6Fz0|USEkulnyRswYM|urnEWUey-5v< zK|YioRQPd{ip*!92N>e3y5>A+Nv3n4toNold<;@)Cpa-}o{A3jKdb?O!_ZABIy-wA ztzaL_l_MAt9Aem+gcuy}HD3IYtK{aB*hzTjXq&0A@uXRXv^;8|0?@Am=!pbiG=C5N zM)McoW~TRnVW3NZq1KJj+xK2C;;K|}6aa~;Hr(bM#K7Rt=}86*!4%lv7!SYq>1?b! zoj=E)44db=!=F?h3B5g#AL`+B*zeH*a^T`<+KZ^BuwjR)kT#^@EDMz<=4WrL{?JQL z(Midu5k`G6nx|MAl2Y&qGSM%%J)+Yw(FWm|z4fu4I z{{3wjNT2C$ql;!i*H5F{3gKU*q?bZrK0;+SlBwYIPElp%gqUQ} zu~PZr#qYvYE(y1#z$@vrcmgY2xRG0o>lUpzY=8Rxlo4QAjRJzT;NnCL<(mUbSdA4= ztVE89jFFMl`L#!Zg%3PXupV$V{iK<4bVwi2|NAg#!f#s}|6Tho-?jh$0}cQ0{CR|dmG3a^sq@LvxXZ)+3$dF}+2P(mIEWS<*7dvo6~{*oVgRl! zQj7D|**X2unoU|<->1K~fm%Nsb}uww1XK5 zPTkQf9B`IX6+xXBtW=vbHP=GNFEGLjjx=4n!T8k>P0Dxgg)8?1odzkeL#&YQ#Ot0b z=PB19V^dl>CF9vFxxuNE`{qHrf083@(u~2?E+QAb|ND4Ak^;V`^p(&%y!)wtA0#DI~1sjPy=Gl=Jk_LKV+s!Y^j?t@%~H!tX2)H zm{hZ!i~RL`v`e690}D)}3FD}V(vmxXyhY%K5Guq{_Mv9?v2lT{bOWg4Zu^7y1ar8n zmAHd)JADf~14}K&Kd>r_R}_x(PBD?%GkD@IDUklYfy|?y1BVdi#9312{)remsr!-H zjW0tu#v*ygyWbLt^s5_5MkpYWOUgiCwk>cCafD`_APTvKBz%WJjzlS-G2A*dS)qkQzz504s~eJE&!(*U_>0mr$HykbwGNoNWwCEjL=c7M*D!Nb`PH zx2NPxryn>XZ%|N7#-LQKLHw1-kG_2=QJ2=JLW=C*nydd_?z&Q5N}%86-u%7SV*Gb- z@Bf(i5)`(qXJx-{k|yJdb?lP{@*FHb*?$CWe>MafB>S6?GqJ~&cUG(*a1pK4j zcf{!2#D*VPQ_jByclkm!s~C_7tTThdil^s=WdwIgp0IA$=lH>9hCTx z5Xr)>@*R|x(DjaQ$DHV74NS`Whn+KWt~fSy84>OBxriMf6kUU4Q-kS1l88`oJ;U37 zBQ0WgFx`l;cSai&{i2YGMjA#*3na}+e^znG8aHDsy4bZf z{#LURLOT3~vp8(Iz0R{4 z(_8XLA)?)amfcWVTsCQ-sSBOwSm)13fLBY`sl!Db%2|ifT=q zA}^pepW;deI;)PQ&|m^3N#3nC$*tDKC&*TfWst8|sxfW&I?b{?nN`JNk9Ca(mhRwR z;e*YDD(uF0O__g-j`;qano_bd|GzAsI+Vubzr}$(&aq;>^uHkxZUTeJ#UKKb;6ZDm zXJ;v)Dg@N3+lUox9T)|rNJr_O>1gvqMG~O-x)ZQ{39k$k* zrcOGGtVyrDyF9^lp_*9wqZg(DHLU6pbt5$?+x}t^@`ZWLSOY9S8qUS0f_DMG--u2U zVVx5|fL}q@Sl3A;632wqbUjvV!&-8wpc7-pG>olAC=&9uR9P+aLa{6Tryv9JHBdyU z`QqpdCu5x$noe5^wes^G-+w6U9@E!NDHQLKi5hO!OIh=Gi{cttNKdQZov`>`$0}qW zwz3-)$gk3`583rGJ_}20tDDcVxc&m|+f<1AbLy?n*OZa;*e5mRaNf1g%?~}~d-9qg z)YnEg7G_l=&u9@fFIBKaalRbC<3=@@*feY>lRsNADQ15TvdRTJZ<)eCYVPqzdL=Ef zN5(>Vd%-(d`|e!KyLWUEG);_E!J-fhAOl=zUcrgVX1&hj`Zz+wvF9Oz%X4gGuONcH z%h?(;os*+5gzz&rd5$4ULvA`P^W&(9fPMjG4QPG?KhaXi@O6O|U0j#gaaIq8)g2TV zw^p{f?V!a@N*#6eiN&o9wm34rAKw#f?N|a+zzc!gN;w?_aaFF$hD3`u9UipKy2=a?eobQF_M*REf$ zj;+{$jx7^GXy!mmwnHMf3B}G*11Dl+ur+U$HV>=|*rWme??d4H)D^+~34-e<&T4fK z9ektGZMEA`+wEVx>}pcQ8=?b3U&4M_&cEw^b7&G~t`IahA*>38X=Dd9PK+d+v5AchxFfgIsaho z3^g-d&4HLt@zfMHx9?onm0BKMiye@&M25!d0|j0nObOP+ni%+TRkv7Sys6+6#71_3 z=3c}|gh*XvU|-!JP`?&KXx|m7=3b=XOQhwATD=v29v@f&3!tGPuaC{Nnek)Hkat;U z8D}L&CC7!O1(_;b_eTUDwOd6z&YPOQpDHX}OEqX&rqBLxbi6Y+6raWRuS~FCMLRMt z&#=5pIeXB!uFvv)dfz7vM;+QgV~i`G1D= z-T1{F=Svc>DCY7thwMnMEmQWBpxlHg7sL~EN*8FEl-J$-QY%K%J<1cYy3$KV zG+EM%8p|KXJPMwGyQmer(9LR9MVP?GkZ=w}PhCJq%Z)LsM&!Gw6`W|6YLt|VXVknn zG+d8xv`&o*XpcrIyO?E>GlQ59W6fo)hgdm&!us+gk&~Z(xzd@ocd|b&VXN{1iqTsr*tppm%|xZev}kgETo?Ip)PrPEKQ`fJY27Z?+iQ zPb+`K9I8RYFXR$~Ml+_RwfhqjPI$G<^2eQukio^mMUAfca=8^`P$}-3av))0#reBX zJO?KRoQN}PfKy6EWE<${E5oA4psTIXI5R3P!`afUEO#@F#cW6?SdJ)pjcBxn{HXms zby#DnxcBA!a)&`0rbZD2SYTN$P0#hKE_J>aS6t>Fk>J=OkHFT(x{~rHi3m`WL<=kn zYqLhsunHC_IFkJ)nD=}RTK!-#DyN3zk?9q}WQ|y1rKvmlPWbjHi7UlXup~E2|PJyPAGVueL7){V%z~!0G zXAH|iVbtT<`S2``Tz}5WNHpQkL-$|7{gJQRQ z{~K-@lS>`6>%9heUPf-y_RL%GwF=+XQ~OK*X5E^AVS9Hz$Yi?j*y$}A5lRJRSrKl( z3QcA!z)W=;sR?}0Mz~&?X z!oKp_GaPNka5j@l=_W8i_Ofa*C=4c}Wn{Tg&f#Kv>KXE-R$KfXiUCcU6VXc% z=8i?pTr4YAqN+|9NHN6(T6PSGByZO+A&`CaMYXfh0S?fVLF)`1*NWI$0?QTU>kd1; zGzWn5_-2B({Gn)x14cpGBq|78lCZr3xPjhMM!`-370O&|EV~3vDVO@igfR9m|9LnF``CmprMnO!UW=7QAFV7bZS z&97u9G63r&&SVh|)l9V;7LLGCY8;X~D^VDNon%jj$@1u7VD2c4OvIF-u>sc%Ihq#3{;M1c1{1p*hfy2MCQDBv0zVR>fl{I|lfOf;-g+=$^M zq0Rs#+yN#^6GhBtw92LZA^WH9cMTdqHT|aKv9`5>skD<(_o8oU-&XLEN{BSkLfhlzuyX9QH{N}qaK6~?EU{Kz zFf*F$WS+nvgybofAOzsSJB2OZAEG_m7vlWn+^D;_jaN7gg(HGtYw~px zw}w`idAI|sf^=i2^*GKT7v~wW-*+2JZJYOB6^uJwuw86RE7aIFD9F(*S)1|L=(x*R zBloIwb9(ht1|YF%8f9femH5?zGAQAwWo zyqo4TV2R=B`U<5m8wAeMHEHpWnOW5wp)I$xr(kkl)R;Oi0isun=y}c-l7LZ7m;lm$ z$q4Iy6Sc&$7dUfcx*n3=`*`*UR zN1JtLOUYS-=7UaFQks;9^B@e^CN+Pz{Jd$gh_F`j>;ZkK-Md1}-@#73aDFjIwBy*d zTlwKK`nqGu3$(>F?Ap8A?q4y9mka`bxGNnAlZNNKWA&(V)8YwF5nmp7j%ul`_QG%4 zaeXBNd7~ytMg3#Xf>6W<>tYbEa%-$6=;P^Sh>aUHZ+e~0RG)Xi3%`rEs8MS8uYqwNdw4SWVkOjZaf` zG5VfUUiPoOG}N6 z<{qp@h!mly6=>7I?*}czyF3Y!CUIt=0}iD^XE&VrDA?Dp@(yuX{qsEJgb&Q}SNvXl zg?HrA?!MH-r4JN!Af3G9!#Qn(6l%OCA`)Ef2g8*M)Z!C4?WMK9NKh2jRTsnTgfut9 zpcZ7xAHd%`iq|80efZ31m3pN9wwBIl#Hqv=X)1r?($L>(#BR+)^)pSgbo+7#q<^S1nr$1&0=q$@M&POX?y?3L&3X z!%^Atu025LgEZ~|-)Cd0=o8K9A{$sT;SHj3M?l{!Er;st5w=T=K2^hJ<$(>&P!j2m zy3~(Qm?r5vh*EGKNLnP31{fhbiIU~c2GX_wqmM}ik7)NF$bEYKH^bK?MD+uJ24Qa=6~Fg-o!gSX*ZYoo{fzTLs$371<;7oLD|PiS3s zz;aIW1HVCV2r*#r`V-0hw_!s4!G4R|L@`u_;)KA?o(p8@$&bkWXV*taO%NC3k? zok=*KA5vswZe|5QOQd*4kD7Db^c|__5C;&|S5MvKdkPtu)vo}DGqDpc097%52V*z( zXp%Esq4?Rzj53SE6hKu;Xc!&LMZPPIj;O-Gnpq&!&u5db7Xi z64ox137#@4w5it68EPn<8RO48KG_2>?+Aa}Qo7fR%&wXJNf2J;Kwm6Opddsyx$gY# zU+b%y*{cBju|sw!wOcY_sMFWX9(C02d(;_YQh1*sH9?j$%`tKJyd(j0PtK#D+KLHI zL;b*n{CZ7IBb}MUGdG3l2vFGJn3TOYJD$Hz2OOy*%!5a{!!0mvok+e+N zaP?Ndm;SO(8-v%yvu#Rr;qFSgZrKJxV^uEnX@L(r4)dZeyh@yRqoi@3M|#Hz`hHN6 zA|8#&oFv8+1F8t(#j1%Ywdn%N2uREt;@bFAF}2zeI2KE&uZr$?-SIwKu<5ThXn_}f z`@RRcJ!3;pKi>mQe)VU5;c)zA@b#dd(J?}$sg0K5L^fIm8%TV4|>Q?qdfMwAh4AM8l8J|tiSF32B4q`!TYj_z!4Lowq99lipY?vlC zJssf0Vy+@In|fg`2sUl$wDGr$XY+4g*%PhDjM^G!Z{H44gwY-ymOqXka)G3ulfWdY ztNvx4oW*}=5^&NGhiS)Vzwb4;K`^*tjj8h$esujKb7&}?V_cU5kQElGgCL<358O^% zcT-EwP>hqb1%_8C_5R4e#7RH zp@tA$bVGG}q@TDR#-_^YT6}Zo5~p_5P%C_pRxwhgkor!;FtNFF#cncoEHm=#?xtY0 z1dHK{(;)5CQJ`0upxdRV?(5PH{JISW%d+@v8FmbTh9n5TXGnM`Cs}{(AbDxaIg&O2 zg<~{fKtj#r91u9PujPqhkFt7tid?IZ={dML<$3sh;A*Hw=VP++12;lVguAyio!na#kaYeX{|8h3_;g*K=UEf zU*{ZR($$Bw*(h;CSO4{alBraU^)52&nxLKUxg=1N5MCBUJ+3a^`9#f?7=4#`&oz?k zoz-#s4C)f8Uk@S*VF!Uc>X}9M`_*gkn0&GI2R*j zUlHUy5b;rLro3?bBLIt%dRd~2lT@kjcfY~OL5ZmTl)ExZyt!)^K#1p>U~rdclk``e z>=zHu6Qp^z%nX2U*RE14f{$U0*Cf)LfBz-c)t%iD%3wxsgHpRPvieqZgEC0IX_Vkd zxh27*KXpXxYD=^PP&EtX{NlX zC%v9)Wz6De((qH}Jqg-g`mwJ!IZ^L?eE2PE9@#9U0T>jD%e^K8-Phz7cZ-bP zU%h91CvGtNYmE{gk=tex+96fK^!I7P7YI3Ma}h)ty%NEN zn}d&kVV1DM4tPht`B!poikUOE396Uy+VE|E*eQuq zoT8M0M&bcREYOX7Q)F5+d!xec;2;H!WO+!r;v#uo402OEt*q%vj)mC@8wg}HO02G( zYG=<5*Vgl3R(5)N@{y+rvBY9CgUHeN`qQLm*3;$@Ez|2z2j3@V_m6j4Kc{5MTf}GG zMS_qp%5n(5$y|Ke#!!7w$4KKAJmhA@sJLcoS}Mv+l^X$2DS9H)ezLP0LfVpNMIPwL2U@Y%%7Q7jPXmGSPlRwa7*y~EkqObIDtyFm)q z-D~m~?At^+db`FvO2uEi2FuK@`RaSN*`T%G!}yA5f-hG1SYtty+Q}}`O^In~cgi>l z=zXVDDNVH?QHtgup3*d46+OEicA^)pIn2`}B}8}{g`msSbzzvq5zHCIjU>OrtmbrG zU26iOxr*A6%_LC(|3nH@ef$16q%glnTl}ob+(w=A9Uk48Pe(F^%ktv(oHC2Ve4|TE zc6J5le1ZqXdLP~+(UY@`Y?r~{B6_Alh8Q{OmhufQSf94*GFtAi(lV<=!6wqxL;jck zOnpR+=HK3Nh}Vv}%LXPzn;0b#^5Afk3y&G)X}NEkE`~TM%tU-P1@^=msCxOyP!IRO zBegW5wZ@10CM!9*_|kF~ZSxrk>r^zyCL|dy9$~*`OX?>1)fL1l(|lW|G!``CEq!N$ zMM)W~G2zDb6wA#)D5OmIMu_&UH_5B%DJ#NKl#R!?QVz>y5jLrK(-JpI6LIGVyD%W9 zg+7;cE40;Rcv9 zkCrUgZ-H}IaC=aY8~7*9+Ny?O=Ep;yso*#-SesEGSa3T&e&DQ`k!p#Zgb<6@KRjgn zG+Z?LoNstww}#+R`Y(?d>>GG^ncorkoKX@REYSTD zQTYHMwNiE~9MM(>u%!3KVR=O=by_thqeFR&Bm;D|lW@>^unOrb^k9yd-=S2LH0S7} z>ae^bwruKEB*7m=)u$5MIo(`)Y+RR5o>9(DDDV623UMVck1##|b`7H%yjK9unoDGkVIKrG*dvN;2S3P_9>ckR6c?7n{s5v!i;dE&<_aDaPA_ zi>Z&SHW^bWYJr-2sb7{WC|0k-a}7>k3)*YgZora(7dVnK7b6?Y7U|>t*u=-aLgC3` zvnz>+QQ_%r^ePEJA5X6^`Ey@^#{dDW(QZr*A_L9Y+QI4?xFXAQ-JDe?&YmeAVN{2b zK0DO+&S-fQWDg`ab0$mQodAEemrA3p{cHbqx{yVqz5Ns6)Rixse^k(i5spvs@22QF zAhsD~>)rC%n(#M+D1!s?DFCBTRfNF~`N7kC8by+1samiHH9dbid%Masz0;p`l^GuF z)taCc0FD9!#^qP3B`G>vZA2db%ma*@6WNWW{*kPq^|f^R%Ee|F-FM69H)u|#Qt{qt zoi{%@b&~<}!vBf99Ef=ih~RNSh2LT6zvdLf+KCi=hu6#d5v7kpppM&Z;F3;`{0FxW z@#nY=LnIjx1?~XD?48~y)>Y&odjWF%6G64~A_3<{rx6>R zqF2ozPyJzzmcF+3AQwJQ@C?KEo|5k3xP%;^ZN*zpQBm5ho(*e)*zn8NzzzG6V?5V0 z2<7tkys|TInay6or7^K(y0ZdwJz|6$blXL}SX7s2es~5{gYwS3d>6k|3V9vz-#G3! zh@|-B?^JP~seJrS$&XAfp`RknZ!pFw@e!a9WgKijDz3K#6@`ifTCWHTa}Tr}n!~;0 zh0~X4_sEKGZZ^}8+X9!T7NazNv{%@nJgpJ8M;Oa zaYo_2Qbk6_j7W15!`+XKC!`+_)IGZ>r6X=buKUkQ*5wXs5}A2D@eYvF0{q(=wm znxEYB{>rdO75{|gy2>`^UB!(y+9acVVRieAMG@Lhf)g>yr+Ccgf8oy1qUO@L$n8@A z;nKV>muW=<*rD@Su=A?nhxTpx>?1>jYOk(ytb|TNwq8q1{;WERaWZi0ov0xFjiIm} z)PkKhn`#2CSuR?p?4)9Vk#`#oL)#q8!B*j3s+x*6kQ~2Pog{K^{k(=xfv{IP9MecW zCB_bMVE;HQS12k5L;tHHjhJ8m%07IN<1N(vQCG+8IilmMo{g$Y5nrPhSx`OH03*55 z;^!ZP!KR|h3~K&8O?uAqKie(}FOYVMt}S-M;FF6%#pX@C<8P!jbk&G&a^_Oj+^2Ys z*1tnnx4eOpd*hgE$xD+(iTw1TaGNs=4*;Pf#P`fd%_%)Jk|eeooma)pR9ka)Ek(PX zq2N$R8sio=D*TQ0BaO+M*8wF-0cR8Bq6vZjr?NAFhjQ!V_)x?Yxmhd9T8#bPWJ^p2 zVbs{=P2C~;GV>Zlkw%u3?OM9&TE|2xMT@t3uSiNEt`MOO*Q>52Wh>pfXJR}YW6XQ{ zJfCN%^ZlJU=RD7Ip3^zMKT-4Q8#0faYOd#r>yK58)sH5XCS>Yj%p1^_p%gSNX4Iai z%;dio52O@`qrWD0>K#6CJvdGFcB%`pA47@W5qIzGe`HRY=O5CK4bZvl6IkJj{#%r? z|A5O4Uo8)Ng;t9f!sRAIsl1a8=TST_Vn(m0i`>XCa0r`>YP-LwxB%^wu8;8+GdQv( zG^usXB?ocI0_)y0MR`T!?Us5ehia8>M~+$sXlUCRovE--QR@;Ys?Ozq9P(Q7ZQ43> zpIo}_{z39UhS{5f8wKSDu+TKfi+#n{O-~4Uk zh*EmSxYYrfwOxCYV}}!zL%2uIc%Oe$XRV@rFeWeka?;Z(XI{}`X?HJGyIgFm@ZX;w zsc2~^A%MTLdqhpoV!jr)}36>dv>Px$jJImpFCzVcs)1b7l%&=qcE;^ zEoSbtk#6sYkpC=iQX(3 z5EUP%LDh0p49U2=$~DIZhi;dDRKwLN8`|PiC-Echa#PXZ|6)S}wWEA@3f!rX>G_!A zphhlmxu@3JVRr3xOWD}*UYv04{*WHt*vT;0@pVLmuu52Mb_Vg9Wg9EUuA2 zl8?Jv5GSU+*{PO$tBpirns`>?!VL-cX@gZO&q)OL%2_8U)8r*4jrGrH`p2zV!T-&| zaf{j)uCI!{A{R9~aJ?$SZ?kk?jfE7FM%1sOCd&S0B(^ckufHtAOetsuspYrqyZ)x8Z8=dG=GG1lcFtKmoxl{>m zAakHGc|f5ZKh>>}F8qu)Y29d2Op+uf?qK|dKPwE!pPkfGl#Sa#?TmJfv}jA5;1`#= zQqplM=!3^!2QZeCx7wu8uWl9!IN85^zrmqGDxsj;TVs=EU)ubiDaD<*@ss- zm%Y-l)9@TN+_0W7Ml5XnEz>_ep>fFIL{5V-n#cCKFhy#0p;!@D!D-=e{(8;*$#2G- z-~F3cHNv>%;D819xg3-F_yHg8bD1W}{1-kQ-da2kMRP?r=@>BD^b5H6=`Lf3y6VPn$`%)-GW}O^kSon7EBP;q9?=n_7O67v9pc>!pQb z)auPuaqG5v3l(E)_GSI_vFY2BtlPgw{(hIMip%d;>9vWnej@q%qMva4iRPI|N7n7w z(!_tL^K*((d428fyiU(eFYzyaICWGnFx_T^a$3(A4p<5kwVtGjOSNa=ey z3;wiIDZDmghb8BsMcSVyT9^W#{YkoGJ9As)0ccff5 zB`U1^TKO@jql!utGX7_6ceT=$mJTWcQ+7_Fk7=jIE7Lu2Ja%~~6K=X$o@5Q7)=`Ao z%Vptz#p~F$l82kO>0*a`LQ8HomkN}$Q0{w8GzfUMX3_$LbiUMT6?eJhshLtmT2m`2 zrK@zuUt8C6$2Zb?u5HM~2xm~H)s1rOJ^3v#{cdG~?xM<+6Lrd(chPMthvmtIcgJoV z-(H!YsUD=t^F)QFU+e|WYBXo`#ht!`&flPI?tga}(nLX13WI~;V?XO(57wx&_pbkw zBgcA$g+wx2w|Xvakrlw=n~x7nWeO7*SwR2(p1`8M*~Ae34SZ&}#$zt|Z%!C%XpOXbpLFv5`sjlu|+#!Pgo9FXG>J~QZn(O%YH zBWQs46dZC)E;!SviJp zefD-koJ?SaKCq_$3t)wALZM_9CQK zGw9iXX^iWLHTQFmME^y==>muB0FYBWAg>aJ#z};63aHSV~ z^&BI1Xx6m%m3k8-P|$7QUIaSpT%uDW?OD?BB+n%~l7+?9t%+Q~hX?=}`?8pcPE~ed z2_t~uEm#W0-QN{N#+ApD+=zZSaBm3ob`3@h+u^Gh4ttNN2s$sX!nzuwp?JOsGoHwj z2@l5>ME8YD3`fUA=$RfY>9hSG4D8@onJ^lTK8T>xz1g7`#v+8NaNr$;IubZHjA0js z2L>_#pi_KLjIjbU(W!eWi-1dyWY}RDad&1C;~9SzVCP+CjBSB%W;hBDGdrDHyErp5 z5X#cSZWs?oRzdJKA&bh!#B=h>1`ELv5fGsjM;8grEB_Ml5nw!Q?T_Fy!`b1Xw-Oi& zJK7`IPZ8{}^QU`YChTvFFb$*GF~83#Ejd(!t%MOOCWZs*(#FDY@nJtyM5ys3r$RH; zGwY5D3&8G^h`_zm90;)SqJ))TM><4FJcR=#j{NChP1sZn(R`H3fhIePF<1&VWkIAq zW^y3K#-asQg8eTLr4LygD9v;SEK4^GSPFI-K%^#fIhF$V7sl;-&O{IvfwyiWBC85G z7MZzT=Na3;D)1g*L}lf9j#XxMO|l*@z#B0U0n~;6Q((CogEzq;QX^ml3_auK-QH(! zYRlFYydetV8<%jvXTLoPZWwqE2_hCzy1W?cwt!a;Ak6maMa=Kjv3M;3Tu%5uArNL? z-SSL!&nS5679sOBE+%t6kqdtVcsdc$>26x21CM6sb)#h-?QyJ literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e750102e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..2fe81a7d --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..62bd9b9c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..5b60df3d --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + jcenter() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/com/nexia/core/Main.java b/src/main/java/com/nexia/core/Main.java new file mode 100644 index 00000000..c0152ec9 --- /dev/null +++ b/src/main/java/com/nexia/core/Main.java @@ -0,0 +1,33 @@ +package com.nexia.core; + +import com.nexia.core.config.ModConfig; +import com.nexia.core.loader.CommandLoader; +import com.nexia.core.utilities.misc.NxFileUtil; +import com.nexia.minigames.games.bedwars.areas.BwDimension; +import com.nexia.minigames.games.bedwars.shop.BwLoadShop; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.sargunvohra.mcmods.autoconfig1u.serializer.GsonConfigSerializer; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; + +public class Main implements ModInitializer { + + public static MinecraftServer server; + + public static ModConfig config; + + public static final String MOD_NAME = "Nexia"; + public static final String MOD_NAME_SHORT = "Nx"; + public static final String modConfigDir = NxFileUtil.makeDir(FabricLoader.getInstance().getConfigDir().toString() + "/nexia"); + @Override + public void onInitialize() { + AutoConfig.register(ModConfig.class, GsonConfigSerializer::new); + config = AutoConfig.getConfigHolder(ModConfig.class).getConfig(); + + CommandLoader.registerCommands(); + BwLoadShop.loadBedWarsShop(true); + BwDimension.register(); + } +} diff --git a/src/main/java/com/nexia/core/commands/player/DiscordCommand.java b/src/main/java/com/nexia/core/commands/player/DiscordCommand.java new file mode 100644 index 00000000..e61151c2 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/DiscordCommand.java @@ -0,0 +1,36 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.TextComponent; + +public class DiscordCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("discord").executes(DiscordCommand::run)); + } + + public static int run(CommandContext context) { + + + TextComponent message = new TextComponent(ChatFormat.brandColor1 + "Link to discord: "); + + TextComponent discordLink = new TextComponent(ChatFormat.brandColor2 + "\247n" + Main.config.discordLink); + discordLink.withStyle(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, Main.config.discordLink))); + + message.append(discordLink); + + CommandSourceStack player = context.getSource(); + player.sendSuccess(message, false); + + + + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/player/HelpCommand.java b/src/main/java/com/nexia/core/commands/player/HelpCommand.java new file mode 100644 index 00000000..443e9c55 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/HelpCommand.java @@ -0,0 +1,47 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.nexia.core.utilities.chat.ChatFormat; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.TextComponent; + +public class HelpCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("help").executes(HelpCommand::run)); + } + + static String commandSeparator = ": "; + + static String[] commands = { + "join" + commandSeparator + "join a game", + "leave" + commandSeparator + "leave a game", + "rules" + commandSeparator + "rules of the server", + "discord" + commandSeparator + "leads you to our discord", + "ping" + commandSeparator + "shows a player's ping", + "prefix" + commandSeparator + "allows you to select your prefix", + "report" + commandSeparator + "allows you to report other players", + "msg" + commandSeparator + "allows you to message other players", + "stats" + commandSeparator + "shows you your stats" + }; + + public static int run(CommandContext context) { + + String message = ChatFormat.separatorLine("Commands"); + + for (int i = 0; i < commands.length; i++) { + String[] commandInfo = commands[i].split(commandSeparator); + if (commandInfo.length < 2) continue; + message += "\n" + ChatFormat.brandColor1 + "/" + commandInfo[0] + ChatFormat.lineColor + " | " + ChatFormat.brandColor2 + commandInfo[1]; + } + + message += "\n" + ChatFormat.separatorLine(null); + + CommandSourceStack player = context.getSource(); + player.sendSuccess(new TextComponent(message), false); + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/LeaveCommand.java b/src/main/java/com/nexia/core/commands/player/LeaveCommand.java new file mode 100644 index 00000000..aee197eb --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/LeaveCommand.java @@ -0,0 +1,30 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.games.util.LobbyUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class LeaveCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + register(dispatcher, "leave"); + register(dispatcher, "lobby"); + register(dispatcher, "l"); + register(dispatcher, "hub"); + } + + private static void register(CommandDispatcher dispatcher, String string) { + dispatcher.register(Commands.literal(string).executes(LeaveCommand::run)); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + LobbyUtil.leaveAllGames(player, true); + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/player/MessageCommand.java b/src/main/java/com/nexia/core/commands/player/MessageCommand.java new file mode 100644 index 00000000..043f154f --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/MessageCommand.java @@ -0,0 +1,86 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.chat.PlayerMutes; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +public class MessageCommand { + + public static void registerMsg(CommandDispatcher commandDispatcher, boolean bl) { + commandDispatcher.register(Commands.literal("msg") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("message", StringArgumentType.greedyString()) + .executes(MessageCommand::msgCommand) + ) + )); + commandDispatcher.register(Commands.literal("mesaage") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("message", StringArgumentType.greedyString()) + .executes(MessageCommand::msgCommand) + ) + ) + ); + } + + public static void registerReply(CommandDispatcher commandDispatcher, boolean bl) { + commandDispatcher.register(Commands.literal("r") + .then(Commands.argument("message", StringArgumentType.greedyString()) + .executes(MessageCommand::replyCommand) + )); + commandDispatcher.register(Commands.literal("reply") + .then(Commands.argument("message", StringArgumentType.greedyString()) + .executes(MessageCommand::replyCommand) + ) + ); + } + + private static int msgCommand(CommandContext context) throws CommandSyntaxException { + ServerPlayer sender = context.getSource().getPlayerOrException(); + ServerPlayer receiver = EntityArgument.getPlayer(context, "player"); + String message = StringArgumentType.getString(context, "message"); + + sendMessage(sender, receiver, message); + + return 1; + } + + private static int replyCommand(CommandContext context) throws CommandSyntaxException { + ServerPlayer sender = context.getSource().getPlayerOrException(); + PlayerData senderData = PlayerDataManager.get(sender); + ServerPlayer receiver = PlayerUtil.getFixedPlayer(senderData.lastMessageSender); + + if (receiver == null) { + sender.sendMessage(ChatFormat.format("{s}Nobody to reply to"), Util.NIL_UUID); + return 1; + } + + String message = StringArgumentType.getString(context, "message"); + + sendMessage(sender, receiver, message); + return 1; + } + + private static void sendMessage(ServerPlayer sender, ServerPlayer receiver, String message) { + if (PlayerMutes.muted(sender) || sender == receiver) return; + + sender.sendMessage(ChatFormat.format("{b1}To {} {s}» {b1}{}", receiver.getScoreboardName(), message), + Util.NIL_UUID); + PlayerDataManager.get(sender).lastMessageSender = receiver; + + receiver.sendMessage(ChatFormat.format("{b1}From {} {s}» {b1}{}", sender.getScoreboardName(), message), + Util.NIL_UUID); + PlayerDataManager.get(receiver).lastMessageSender = sender; + } + +} diff --git a/src/main/java/com/nexia/core/commands/player/PingCommand.java b/src/main/java/com/nexia/core/commands/player/PingCommand.java new file mode 100644 index 00000000..f5a9d172 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/PingCommand.java @@ -0,0 +1,54 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.ChatFormat; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +public class PingCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("ping").executes(PingCommand::run) + .then(Commands.argument("player", EntityArgument.player()) + .executes(context -> PingCommand.ping(context, EntityArgument.getPlayer(context, "player"))) + ) + ); + } + + public static int calculate(int ping){ + if(ping < 4){ + return -1; + } + return ping; + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer executer = context.getSource().getPlayerOrException(); + int ping = executer.latency; + + if(calculate(ping) == -1){ + executer.sendMessage(ChatFormat.format("{b1}Your ping is {b2}INVALID{b1}." ), Util.NIL_UUID); + } else { + executer.sendMessage(ChatFormat.format("{b1}Your ping is {b2}{}ms{b1}.", ping), Util.NIL_UUID); + } + + return ping; + } + + public static int ping(CommandContext context, ServerPlayer player) { + CommandSourceStack executer = context.getSource(); + int ping = player.latency; + + if(calculate(ping) == -1){ + executer.sendSuccess(ChatFormat.format("{b1}The ping of {} is {b2}INVALID{b1}.", player.getScoreboardName()), false); + } else { + executer.sendSuccess(ChatFormat.format("{b1}The ping of {} is {b2}{}ms{b1}.", player.getScoreboardName(), ping), false); + } + + return ping; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/PlayCommand.java b/src/main/java/com/nexia/core/commands/player/PlayCommand.java new file mode 100644 index 00000000..e79324e1 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/PlayCommand.java @@ -0,0 +1,62 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.gui.PlayGUI; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class PlayCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("play").executes(PlayCommand::openGUI) + .then(Commands.literal("ffa").executes(PlayCommand::openGUI) + .then(Commands.literal("classic").executes(PlayCommand::playNormalFFA))) + .then(Commands.literal("bedwars").executes(PlayCommand::playBedWars)) + .then(Commands.literal("duels").executes(PlayCommand::playDuels)) + .then(Commands.literal("oitc").executes(PlayCommand::playOITC)) + .then(Commands.literal("bw").executes(PlayCommand::playBedWars))); + dispatcher.register(Commands.literal("join").executes(PlayCommand::openGUI) + .then(Commands.literal("ffa").executes(PlayCommand::openGUI) + .then(Commands.literal("classic")).executes(PlayCommand::playNormalFFA)) + .then(Commands.literal("bedwars").executes(PlayCommand::playBedWars)) + .then(Commands.literal("duels").executes(PlayCommand::playDuels)) + .then(Commands.literal("bw").executes(PlayCommand::playBedWars)) + .then(Commands.literal("oitc").executes(PlayCommand::playOITC)) + ); + dispatcher.register(Commands.literal("ffa").executes(PlayCommand::playNormalFFA)); + } + + private static int openGUI(CommandContext context) throws CommandSyntaxException{ + PlayGUI.openMainGUI(context.getSource().getPlayerOrException()); + return 1; + } + + private static int playNormalFFA(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + LobbyUtil.sendGame(player, "normal ffa", true); + return 1; + } + + private static int playBedWars(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + LobbyUtil.sendGame(player, "bedwars", true); + return 1; + } + + private static int playDuels(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + LobbyUtil.sendGame(player, "duels", true); + return 1; + } + + private static int playOITC(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + LobbyUtil.sendGame(player, "oitc", true); + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/player/PrefixCommand.java b/src/main/java/com/nexia/core/commands/player/PrefixCommand.java new file mode 100644 index 00000000..9303a362 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/PrefixCommand.java @@ -0,0 +1,21 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.gui.RankGUI; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class PrefixCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("prefix").executes(PrefixCommand::run)); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer executer = context.getSource().getPlayerOrException(); + RankGUI.openRankGUI(executer); + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/ReportCommand.java b/src/main/java/com/nexia/core/commands/player/ReportCommand.java new file mode 100644 index 00000000..4c1aa00e --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/ReportCommand.java @@ -0,0 +1,68 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.http.DiscordWebhook; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +import java.awt.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class ReportCommand { + + private static void sendWebhook(String reporter, String victim, String reason){ + DiscordWebhook webhook = new DiscordWebhook("https://discord.com/api/webhooks/1076143579585593404/WAvzxt2x1hPOX2UHrklkiCumwVU7xBvUjZ91InBnHKCuuL0nnKPERaEiQxQvgkUxHnZ0"); + webhook.addEmbed(new DiscordWebhook.EmbedObject() + .setAuthor((reporter + " reported " + victim), null, null) + .setColor(Color.RED) + .addField("Reporter:", reporter, true) + .addField("Reported Player:", victim, true) + .setThumbnail("https://mc-heads.net/avatar/" + victim + "/64") + .addField("Reason:", reason, false) + .setFooter("Nexia • " + DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(LocalDateTime.now()), "https://notcoded.needs.rest/r/nexia.png")); + try { + webhook.execute(); + } catch (Exception ignored) { } + } + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("report") + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("reason", StringArgumentType.greedyString()) + .executes(context -> ReportCommand.report(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "reason"))) + ) + ) + ); + } + + + public static int report(CommandContext context, ServerPlayer player, String reason) throws CommandSyntaxException { + ServerPlayer executor = context.getSource().getPlayerOrException(); + if(executor == player){ + player.sendMessage(ChatFormat.formatFail("You cannot report yourself!"), Util.NIL_UUID); + return 1; + } + + executor.sendMessage(ChatFormat.format("{b1}You have reported {b2}{} {b1}for {b2}{}.", player.getScoreboardName(), reason), Util.NIL_UUID); + ServerPlayer staffPlayer; + sendWebhook(executor.getScoreboardName(), player.getScoreboardName(), reason); + for (int i = 0; i != Main.server.getPlayerCount(); i++){ + staffPlayer = PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]); + CommandSourceStack staffStack = staffPlayer.createCommandSourceStack(); + if(PlayerUtil.hasPermission(staffStack, "nexia.staff.report", 1)) { + staffStack.sendSuccess(ChatFormat.format("{b2}{} {b1}has reported {b2}{} {b1}for {b2}{}.", executor.getScoreboardName(), player.getScoreboardName(), reason), false); + } + } + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/RulesCommand.java b/src/main/java/com/nexia/core/commands/player/RulesCommand.java new file mode 100644 index 00000000..a8b05d65 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/RulesCommand.java @@ -0,0 +1,36 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.nexia.core.utilities.chat.ChatFormat; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.TextComponent; + +public class RulesCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("rules").executes(RulesCommand::run)); + } + + private static final String[] rules = { + "No cheats or other unfair advantages", + "No racism, excessive spamming or hate speech", + "Don't abuse any bugs and glitches", + "Keep it G, PG and PG-13", + "No interrupting", + }; + + public static int run(CommandContext context) { + + String message = ChatFormat.separatorLine("Rules"); + for (int i = 0; i < rules.length; i++) { + message += "\n" + "\247d" + ChatFormat.bold + (i+1) + ". §8» " + ChatFormat.normalColor + rules[i]; + } + message += "\n" + ChatFormat.separatorLine(null); + + context.getSource().sendSuccess(new TextComponent(message), false); + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/StatsCommand.java b/src/main/java/com/nexia/core/commands/player/StatsCommand.java new file mode 100644 index 00000000..51af3945 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/StatsCommand.java @@ -0,0 +1,132 @@ +package com.nexia.core.commands.player; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.ffa.utilities.player.PlayerDataManager; +import com.nexia.ffa.utilities.player.SavedPlayerData; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; + +import java.text.DecimalFormat; + +public class StatsCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("stats").executes(StatsCommand::run) + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("gamemode", StringArgumentType.greedyString()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((LobbyUtil.statsGameModes), builder))) + .executes(context -> StatsCommand.other(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "gamemode"))))) + ); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + + ServerPlayer player = context.getSource().getPlayerOrException(); + PlayerData executerData = com.nexia.core.utilities.player.PlayerDataManager.get(player); + if(executerData.gameMode == PlayerGameMode.FFA){ + String message = ChatFormat.separatorLine("FFA Stats"); + SavedPlayerData data = PlayerDataManager.get(player).savedData; + player.sendMessage(new TextComponent(message), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Kills: §a{}", data.kills), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Deaths: §c{}", data.deaths), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}KDR: §a{}", Float.parseFloat(new DecimalFormat("#.##").format(data.kills / data.deaths))), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Killstreak: §6{}§8/§6{}", data.killstreak, data.bestKillstreak), Util.NIL_UUID); + player.sendMessage(new TextComponent(ChatFormat.separatorLine(null)), Util.NIL_UUID); + } + + if(executerData.gameMode == PlayerGameMode.DUELS){ + String message = ChatFormat.separatorLine("Duels Stats"); + com.nexia.minigames.games.duels.util.player.SavedPlayerData data = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).savedData; + player.sendMessage(new TextComponent(message), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), Util.NIL_UUID); + player.sendMessage(new TextComponent(ChatFormat.separatorLine(null)), Util.NIL_UUID); + } + + + if(executerData.gameMode == PlayerGameMode.BEDWARS){ + String message = ChatFormat.separatorLine("Bedwars Stats"); + com.nexia.minigames.games.duels.util.player.SavedPlayerData data = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).savedData; + player.sendMessage(new TextComponent(message), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), Util.NIL_UUID); + player.sendMessage(new TextComponent(ChatFormat.separatorLine(null)), Util.NIL_UUID); + } + + if(executerData.gameMode == PlayerGameMode.OITC){ + String message = ChatFormat.separatorLine("OITC Stats"); + com.nexia.minigames.games.oitc.util.player.SavedPlayerData data = com.nexia.minigames.games.oitc.util.player.PlayerDataManager.get(player).savedData; + player.sendMessage(new TextComponent(message), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Kills: §c{}", data.kills), Util.NIL_UUID); + player.sendMessage(new TextComponent(ChatFormat.separatorLine(null)), Util.NIL_UUID); + } + return 1; + } + + public static int other(CommandContext context, ServerPlayer player, String gamemode) { + CommandSourceStack executor = context.getSource(); + if(gamemode.equalsIgnoreCase("ffa")){ + String message = ChatFormat.separatorLine("FFA Stats"); + SavedPlayerData data = PlayerDataManager.get(player).savedData; + executor.sendSuccess(new TextComponent(message), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Kills: §a{}", data.kills), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Deaths: §c{}", data.deaths), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}KDR: §a{}", Float.parseFloat(new DecimalFormat("#.##").format(data.kills / data.deaths))), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Killstreak: §6{}§8/§6{}", data.killstreak, data.bestKillstreak), false); + executor.sendSuccess(new TextComponent(ChatFormat.separatorLine(null)), false); + } + + if(gamemode.equalsIgnoreCase("duels")){ + String message = ChatFormat.separatorLine("Duels Stats"); + com.nexia.minigames.games.duels.util.player.SavedPlayerData data = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).savedData; + executor.sendSuccess(new TextComponent(message), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), false); + executor.sendSuccess(new TextComponent(ChatFormat.separatorLine(null)), false); + } + + + if(gamemode.equalsIgnoreCase("bedwars")){ + String message = ChatFormat.separatorLine("Bedwars Stats"); + com.nexia.minigames.games.duels.util.player.SavedPlayerData data = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).savedData; + executor.sendSuccess(new TextComponent(message), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), false); + executor.sendSuccess(new TextComponent(ChatFormat.separatorLine(null)), false); + } + + if(gamemode.equalsIgnoreCase("oitc")){ + String message = ChatFormat.separatorLine("OITC Stats"); + com.nexia.minigames.games.oitc.util.player.SavedPlayerData data = com.nexia.minigames.games.oitc.util.player.PlayerDataManager.get(player).savedData; + executor.sendSuccess(new TextComponent(message), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}User: {n}{}", player.getScoreboardName()), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Wins: §a{}", data.wins), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Losses: §c{}", data.loss), false); + executor.sendSuccess(ChatFormat.format(" §8» {b1}Kills: §c{}", data.kills), false); + executor.sendSuccess(new TextComponent(ChatFormat.separatorLine(null)), false); + } + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/duels/DuelCommand.java b/src/main/java/com/nexia/core/commands/player/duels/DuelCommand.java new file mode 100644 index 00000000..754a9330 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/duels/DuelCommand.java @@ -0,0 +1,64 @@ +package com.nexia.core.commands.player.duels; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.minigames.Main; +import com.nexia.minigames.games.duels.DuelGameMode; +import com.nexia.minigames.games.duels.gamemodes.GamemodeHandler; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +public class DuelCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("duel") + .requires(commandSourceStack -> { + try { + com.nexia.minigames.games.duels.util.player.PlayerData playerData = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + PlayerData playerData1 = PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + return playerData.gameMode == DuelGameMode.LOBBY && playerData1.gameMode == PlayerGameMode.DUELS; + } catch (Exception ignored) {} + return false; + }) + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("gamemode", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((DuelGameMode.duels), builder))) + .executes(context -> DuelCommand.challenge(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "gamemode"), null)) + .then(Commands.argument("map", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((Main.config.duelsMaps), builder))) + .executes(context -> DuelCommand.challenge(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "gamemode"), StringArgumentType.getString(context, "map"))) + )))); + dispatcher.register(Commands.literal("challenge") + .requires(commandSourceStack -> { + try { + com.nexia.minigames.games.duels.util.player.PlayerData playerData = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + PlayerData playerData1 = PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + return playerData.gameMode == DuelGameMode.LOBBY && playerData1.gameMode == PlayerGameMode.DUELS; + } catch (Exception ignored) {} + return false; + }) + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("gamemode", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((DuelGameMode.duels), builder))) + .executes(context -> DuelCommand.challenge(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "gamemode"), null)) + .then(Commands.argument("map", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((Main.config.duelsMaps), builder))) + .executes(context -> DuelCommand.challenge(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "gamemode"), StringArgumentType.getString(context, "map"))) + )))); + } + + public static int challenge(CommandContext context, ServerPlayer player, String gameMode, @Nullable String map) throws CommandSyntaxException { + ServerPlayer executer = context.getSource().getPlayerOrException(); + GamemodeHandler.challengePlayer(executer, player, gameMode, map); + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/duels/QueueCommand.java b/src/main/java/com/nexia/core/commands/player/duels/QueueCommand.java new file mode 100644 index 00000000..d2fc78ed --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/duels/QueueCommand.java @@ -0,0 +1,39 @@ +package com.nexia.core.commands.player.duels; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.minigames.games.duels.DuelGameMode; +import com.nexia.minigames.games.duels.gamemodes.GamemodeHandler; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.server.level.ServerPlayer; + +public class QueueCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("queue") + .requires(commandSourceStack -> { + try { + com.nexia.minigames.games.duels.util.player.PlayerData playerData = com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + PlayerData playerData1 = PlayerDataManager.get(commandSourceStack.getPlayerOrException()); + return playerData.gameMode == DuelGameMode.LOBBY && playerData1.gameMode == PlayerGameMode.DUELS; + } catch (Exception ignored) {} + return false; + }) + .then(Commands.argument("gamemode", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((DuelGameMode.duels), builder))) + .executes(context -> QueueCommand.queue(context, StringArgumentType.getString(context, "gamemode")))) + ); + } + + public static int queue(CommandContext context, String gameMode) throws CommandSyntaxException { + ServerPlayer executer = context.getSource().getPlayerOrException(); + GamemodeHandler.joinQueue(executer, gameMode, false); + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/player/oitc/OitcCommand.java b/src/main/java/com/nexia/core/commands/player/oitc/OitcCommand.java new file mode 100644 index 00000000..ede589b9 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/player/oitc/OitcCommand.java @@ -0,0 +1,30 @@ +package com.nexia.core.commands.player.oitc; + +import com.mojang.brigadier.CommandDispatcher; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.minigames.games.oitc.OitcGame; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +public class OitcCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("oitc") + .requires(commandSourceStack -> { + try { + return PlayerDataManager.get(commandSourceStack.getPlayerOrException()).gameMode == PlayerGameMode.OITC; + } catch (Exception ignored) {} + return false; + }) + .then(Commands.literal("join").executes(context -> { + OitcGame.joinQueue(context.getSource().getPlayerOrException()); + return 1; + })) + .then(Commands.literal("leave").executes(context -> { + LobbyUtil.sendGame(context.getSource().getPlayerOrException(), "oitc", false); + return 1; + })) + ); + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/BanCommand.java b/src/main/java/com/nexia/core/commands/staff/BanCommand.java new file mode 100644 index 00000000..c0ad6835 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/BanCommand.java @@ -0,0 +1,61 @@ +package com.nexia.core.commands.staff; + +import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.UserBanList; +import net.minecraft.server.players.UserBanListEntry; + +import java.util.Collection; +import java.util.Date; + +public class BanCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("ban") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.ban", 3)) + .then(Commands.argument("player", GameProfileArgument.gameProfile()) + .executes(context -> BanCommand.ban(context.getSource(), GameProfileArgument.getGameProfiles(context, "player"), "No reason specified.")) + .then(Commands.argument("reason", StringArgumentType.greedyString()) + .executes(context -> BanCommand.ban(context.getSource(), GameProfileArgument.getGameProfiles(context, "player"), StringArgumentType.getString(context, "reason")))) + ) + ); + } + + public static int ban(CommandSourceStack context, Collection collection, String reason) throws CommandSyntaxException { + UserBanList userBanList = Main.server.getPlayerList().getBans(); + int i = 0; + + for (GameProfile gameProfile : collection) { + if (!userBanList.isBanned(gameProfile)) { + ServerPlayer serverPlayer = Main.server.getPlayerList().getPlayer(gameProfile.getId()); + + UserBanListEntry userBanListEntry = new UserBanListEntry(gameProfile, (Date) null, context.getTextName(), (Date) null, reason); + userBanList.add(userBanListEntry); + ++i; + context.sendSuccess(ChatFormat.format("{b1}You have banned {b2}{} {b1}for {b2}{}{b1}.", ComponentUtils.getDisplayName(gameProfile).getString(), reason), true); + if (serverPlayer != null) { + serverPlayer.connection.disconnect(new TextComponent("§c§lYou have been banned.\n§7Reason: §d" + reason + "\n§7You can appeal your ban at §d" + Main.config.discordLink)); + } + } + } + + if (i == 0) { + context.sendFailure(ChatFormat.formatFail("That player is already banned.")); + } else { + return i; + } + + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/BwReloadShopCommand.java b/src/main/java/com/nexia/core/commands/staff/BwReloadShopCommand.java new file mode 100644 index 00000000..82188853 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/BwReloadShopCommand.java @@ -0,0 +1,37 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.shop.BwLoadShop; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.TextComponent; + +public class BwReloadShopCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("bwreloadshop").executes(BwReloadShopCommand::run) + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.dev.bwreloadshop", 3))); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + + boolean success = BwLoadShop.loadBedWarsShop(false) && + BwLoadShop.loadBedWarsShop(false); + + if (success) { + String message = ChatFormat.brandColor1 + "Reloaded Bedwars shop successfully!"; + context.getSource().sendSuccess(new TextComponent(message), false); + + } else { + String message = ChatFormat.brandColor2 + "Failed to reload Bedwars shop."; + context.getSource().sendFailure(new TextComponent(message)); + } + + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/BwReloadTeamColorsCommand.java b/src/main/java/com/nexia/core/commands/staff/BwReloadTeamColorsCommand.java new file mode 100644 index 00000000..6e714d8a --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/BwReloadTeamColorsCommand.java @@ -0,0 +1,27 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class BwReloadTeamColorsCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("bwreloadteamcolors").executes(BwReloadTeamColorsCommand::run) + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.dev.bwreloadteamcolors", 3))); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + + BwTeam.reloadPlayerTeamColors(player); + + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/BwSkipQueueCommand.java b/src/main/java/com/nexia/core/commands/staff/BwSkipQueueCommand.java new file mode 100644 index 00000000..4cbc65cc --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/BwSkipQueueCommand.java @@ -0,0 +1,35 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; + +public class BwSkipQueueCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register((Commands.literal("bwskipqueue") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.bwskipqueue", 1))) + .executes(BwSkipQueueCommand::run) + ); + + } + + private static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + + if (BwUtil.skipQueue()) { + player.sendMessage(new TextComponent("Skipped queue"), Util.NIL_UUID); + } else { + player.sendMessage(new TextComponent("Epic queue skip failure"), Util.NIL_UUID); + } + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/DefaultGameRulesCommand.java b/src/main/java/com/nexia/core/commands/staff/DefaultGameRulesCommand.java new file mode 100644 index 00000000..fe47cb81 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/DefaultGameRulesCommand.java @@ -0,0 +1,53 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +import java.util.HashMap; + +public class DefaultGameRulesCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("defaultgamerules").executes(DefaultGameRulesCommand::run) + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.dev.defaultgamerules", 3))); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + + for (String gamerule : gamerules.keySet()) { + String value = gamerules.get(gamerule); + Main.server.getCommands().performCommand(player.createCommandSourceStack(), + "gamerule " + gamerule + " " + value); + } + + return 1; + } + + private static final HashMap gamerules = getGameRules(); + + private static HashMap getGameRules() { + HashMap gamerules = new HashMap<>(); + gamerules.put("announceAdvancements", "false"); + gamerules.put("commandBlockOutput", "false"); + gamerules.put("doDaylightCycle", "false"); + gamerules.put("doFireTick", "false"); + gamerules.put("doImmediateRespawn", "true"); + gamerules.put("doMobSpawning", "false"); + gamerules.put("doPatrolSpawning", "false"); + gamerules.put("doTraderSpawning", "false"); + gamerules.put("doWeatherCycle", "false"); + gamerules.put("keepInventory", "true"); + gamerules.put("mobGriefing", "false"); + gamerules.put("randomTickSpeed", "0"); + gamerules.put("showDeathMessages", "false"); + return gamerules; + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/HealCommand.java b/src/main/java/com/nexia/core/commands/staff/HealCommand.java new file mode 100644 index 00000000..22d040ca --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/HealCommand.java @@ -0,0 +1,42 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +public class HealCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("heal").executes(HealCommand::run) + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.heal", 1)) + .then(Commands.argument("player", EntityArgument.player()) + .executes(context -> HealCommand.heal(context, EntityArgument.getPlayer(context, "player"))) + ) + ); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer executer = context.getSource().getPlayerOrException(); + executer.heal(executer.getMaxHealth()); + + executer.sendMessage(ChatFormat.format("{b1}You have been healed."), Util.NIL_UUID); + + return 1; + } + + public static int heal(CommandContext context, ServerPlayer player) throws CommandSyntaxException { + CommandSourceStack executer = context.getSource(); + player.heal(player.getMaxHealth()); + + executer.sendSuccess(ChatFormat.format("{b1}You have healed {b2}{}{b1}.", player.getScoreboardName()), false); + player.sendMessage(ChatFormat.format("{b1}You have been healed."), Util.NIL_UUID); + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/MapCommand.java b/src/main/java/com/nexia/core/commands/staff/MapCommand.java new file mode 100644 index 00000000..1f630040 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/MapCommand.java @@ -0,0 +1,95 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.duels.DuelsSpawn; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.GameRules; +import xyz.nucleoid.fantasy.RuntimeWorldConfig; + +public class MapCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register((Commands.literal("map") + .requires(commandSourceStack -> { + try { + return PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.map", 2); + } catch (Exception ignored) { + return false; + } + }) + .then(Commands.argument("type", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((new String[]{"delete", "create", "tp"}), builder))) + .then(Commands.argument("map", StringArgumentType.greedyString()) + .executes(MapCommand::run) + ) + ) + )); + } + + private static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + + String type = StringArgumentType.getString(context, "type"); + String map = StringArgumentType.getString(context, "map"); + + if((ChatFormat.hasWhiteSpacesOrSpaces(null, map) || ChatFormat.hasWhiteSpacesOrSpaces(null, type)) || (type == null || map == null)) { + player.sendMessage(ChatFormat.formatFail("Invalid name!"), Util.NIL_UUID); + return 1; + } + + String[] mapname = map.split(":"); + + if(type.equalsIgnoreCase("create")){ + + ServerLevel level = ServerTime.fantasy.getOrOpenPersistentWorld(ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(mapname[0], mapname[1])).location(), ( + new RuntimeWorldConfig() + .setDimensionType(DuelsSpawn.duelWorld.dimensionType()) + .setGenerator(DuelsSpawn.duelWorld.getChunkSource().getGenerator()) + .setDifficulty(Difficulty.HARD) + .setGameRule(GameRules.RULE_KEEPINVENTORY, false) + .setGameRule(GameRules.RULE_MOBGRIEFING, false) + .setGameRule(GameRules.RULE_WEATHER_CYCLE, false) + .setGameRule(GameRules.RULE_DAYLIGHT, false) + .setGameRule(GameRules.RULE_DO_IMMEDIATE_RESPAWN, false) + .setGameRule(GameRules.RULE_DOMOBSPAWNING, false) + .setGameRule(GameRules.RULE_SHOWDEATHMESSAGES, false) + .setGameRule(GameRules.RULE_SPAWN_RADIUS, 0))).asWorld(); + + player.teleportTo(level, 0, 80, 0, 0, 0); + + player.sendMessage(ChatFormat.format("{b1}Created map called: {b2}{}{b1}.", mapname[1]), Util.NIL_UUID); + return 1; + } + + if (type.equalsIgnoreCase("delete")) { + ServerTime.fantasy.getOrOpenPersistentWorld(ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(mapname[0], mapname[1])).location(), null).delete(); + player.sendMessage(ChatFormat.format("{b1}Deleted map called: {b2}{}{b1}.", mapname[1]), Util.NIL_UUID); + return 1; + } + + if(type.equalsIgnoreCase("tp")) { + ServerLevel level = ServerTime.fantasy.getOrOpenPersistentWorld(ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(mapname[0], mapname[1])).location(), null).asWorld(); + player.teleportTo(level, 0, 80, 0, 0, 0); + + player.sendMessage(ChatFormat.format("{b1}Teleported to map called: {b2}{}{b1}.", mapname[1]), Util.NIL_UUID); + return 1; + } + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/MuteCommand.java b/src/main/java/com/nexia/core/commands/staff/MuteCommand.java new file mode 100644 index 00000000..8f24d7a9 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/MuteCommand.java @@ -0,0 +1,75 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.chat.PlayerMutes; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +import java.util.HashMap; + +public class MuteCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("mute") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.mute", 1)) + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("duration", StringArgumentType.word()) + .executes(context -> MuteCommand.mute(context, StringArgumentType.getString(context, "duration"), "No reason specified.")) + .then(Commands.argument("reason", StringArgumentType.greedyString()) + .executes(context -> MuteCommand.mute(context, + StringArgumentType.getString(context, "duration"), + StringArgumentType.getString(context, "reason")) + ) + ) + ) + ) + ); + } + + public static int mute(CommandContext context, String durationArg, String reason) throws CommandSyntaxException { + + CommandSourceStack sender = context.getSource(); + ServerPlayer muted = EntityArgument.getPlayer(context, "player"); + + int durationInSeconds; + try { + durationInSeconds = parseTimeArg(durationArg); + } catch (Exception e) { + sender.sendFailure(ChatFormat.format("{f}Invalid duration. Examples: 1s / 2m / 3h")); + return 1; + } + + PlayerMutes.mute(sender, muted, durationInSeconds * 1000, reason); + + return 1; + } + + private static int parseTimeArg(String durationArg) throws Exception { + StringReader stringReader = new StringReader(durationArg); + float number = stringReader.readFloat(); + String unit = stringReader.readUnquotedString(); + + int unitValue = units.getOrDefault(unit, 0); + int time = Math.round(number * unitValue); + if (time <= 0) { + throw new Exception(); + } + return time; + } + + static HashMap units = new HashMap<>(); + static { + units.put("s", 1); + units.put("m", 60); + units.put("h", 3600); + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/ProtectionMapCommand.java b/src/main/java/com/nexia/core/commands/staff/ProtectionMapCommand.java new file mode 100644 index 00000000..3725fcce --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/ProtectionMapCommand.java @@ -0,0 +1,28 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class ProtectionMapCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("protectionmap") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.dev.protectionmap", 3)) + + .then(Commands.literal("bedwars").executes(ProtectionMapCommand::bedwars)) + ); + } + + public static int bedwars(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + BwAreas.createProtectionMap(player); + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/RandomCommand.java b/src/main/java/com/nexia/core/commands/staff/RandomCommand.java new file mode 100644 index 00000000..efef899b --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/RandomCommand.java @@ -0,0 +1,25 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.nexia.core.utilities.misc.RandomUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.Random; +public class RandomCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("random") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.random", 1)) + .then(Commands.argument("minimum", IntegerArgumentType.integer()) + .then(Commands.argument("maximum", IntegerArgumentType.integer()) + .executes(context -> RandomCommand.calculate(context, IntegerArgumentType.getInteger(context, "minimum"), IntegerArgumentType.getInteger(context, "maximum"))))) + ); + } + + public static int calculate(CommandContext context, int minimum, int maximum) { + return RandomUtil.randomInt(minimum, maximum); + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/RankCommand.java b/src/main/java/com/nexia/core/commands/staff/RankCommand.java new file mode 100644 index 00000000..0ccb5170 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/RankCommand.java @@ -0,0 +1,47 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +public class RankCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("rank") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.rank", 4)) + .then(Commands.argument("player", EntityArgument.player()) + .then(Commands.argument("rank", StringArgumentType.string()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((Main.config.ranks), builder))) + .executes(context -> RankCommand.give(context, EntityArgument.getPlayer(context, "player"), StringArgumentType.getString(context, "rank"))))) + ); + } + + public static int give(CommandContext context, ServerPlayer player, String rank) throws CommandSyntaxException { + CommandSourceStack executer = context.getSource(); + + for(int i = 0; i < 8; i++){ + if(rank.equalsIgnoreCase(Main.config.ranks[i])){ + executer.sendSuccess(ChatFormat.format("{b1}You have set the rank of {b2}{} {b1}to: {b2}{b}{}{b1}.", player.getScoreboardName(), Main.config.ranks[i]), false); + player.sendMessage(ChatFormat.format("{b1}Your rank has been set to: {b2}{b}{}{b1}.", Main.config.ranks[i]), Util.NIL_UUID); + + for (int i2 = 0; i2 < 8; i2++) { + player.removeTag(Main.config.ranks[i2]); + } + + PlayerUtil.executeServerCommand("/lp user %player% parent set " + Main.config.ranks[i], player); + player.addTag(Main.config.ranks[i]); + } + } + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/RoundPosCommand.java b/src/main/java/com/nexia/core/commands/staff/RoundPosCommand.java new file mode 100644 index 00000000..bd74d253 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/RoundPosCommand.java @@ -0,0 +1,32 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.level.ServerPlayer; + +public class RoundPosCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("roundpos").executes(RoundPosCommand::run) + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.dev.roundpos", 3))); + } + + public static int run(CommandContext context) throws CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + + EntityPos pos = new EntityPos(player); + pos.x = Math.floor(pos.x) + 0.5; + pos.z = Math.floor(pos.z) + 0.5; + pos.yaw = Math.round(pos.yaw / 45) * 45; + pos.pitch = 0; + + pos.teleportPlayer(player.getLevel(), player); + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/commands/staff/UnBanCommand.java b/src/main/java/com/nexia/core/commands/staff/UnBanCommand.java new file mode 100644 index 00000000..ab4521d8 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/UnBanCommand.java @@ -0,0 +1,55 @@ +package com.nexia.core.commands.staff; + +import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.server.players.UserBanList; + +import java.util.Collection; + +public class UnBanCommand { + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("unban") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.ban", 3)) + .then(Commands.argument("player", GameProfileArgument.gameProfile()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((context.getSource()).getServer().getPlayerList().getBans().getUserList(), builder))) + .executes(context -> UnBanCommand.unban(context.getSource(), GameProfileArgument.getGameProfiles(context, "player"))) + ) + ); + dispatcher.register(Commands.literal("pardon") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.ban", 3)) + .then(Commands.argument("player", GameProfileArgument.gameProfile()) + .suggests(((context, builder) -> SharedSuggestionProvider.suggest((context.getSource()).getServer().getPlayerList().getBans().getUserList(), builder))) + .executes(context -> UnBanCommand.unban(context.getSource(), GameProfileArgument.getGameProfiles(context, "player"))) + ) + ); + } + + public static int unban(CommandSourceStack context, Collection collection) throws CommandSyntaxException { + UserBanList userBanList = Main.server.getPlayerList().getBans(); + int i = 0; + + for (GameProfile gameProfile : collection) { + if (userBanList.isBanned(gameProfile)) { + userBanList.remove(gameProfile); + ++i; + context.sendSuccess(ChatFormat.format("{b1}You have unbanned {b2}{}{b1}.", gameProfile.getName()), true); + } + } + + if (i == 0) { + context.sendSuccess(ChatFormat.formatFail("That player is not banned."), false); + } else { + return i; + } + + return 1; + } +} diff --git a/src/main/java/com/nexia/core/commands/staff/UnMuteCommand.java b/src/main/java/com/nexia/core/commands/staff/UnMuteCommand.java new file mode 100644 index 00000000..f48a4035 --- /dev/null +++ b/src/main/java/com/nexia/core/commands/staff/UnMuteCommand.java @@ -0,0 +1,34 @@ +package com.nexia.core.commands.staff; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.nexia.core.utilities.chat.PlayerMutes; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.server.level.ServerPlayer; + +public class UnMuteCommand { + + public static void register(CommandDispatcher dispatcher, boolean bl) { + dispatcher.register(Commands.literal("unmute") + .requires(commandSourceStack -> PlayerUtil.hasPermission(commandSourceStack, "nexia.staff.mute", 1)) + + .then(Commands.argument("player", EntityArgument.player()) + .executes(UnMuteCommand::unMute) + ) + ); + } + + public static int unMute(CommandContext context) throws CommandSyntaxException { + CommandSourceStack sender = context.getSource(); + ServerPlayer muted = EntityArgument.getPlayer(context, "player"); + + PlayerMutes.unMute(sender, muted); + + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/config/ModConfig.java b/src/main/java/com/nexia/core/config/ModConfig.java new file mode 100644 index 00000000..eb046e22 --- /dev/null +++ b/src/main/java/com/nexia/core/config/ModConfig.java @@ -0,0 +1,56 @@ +package com.nexia.core.config; + +import me.sargunvohra.mcmods.autoconfig1u.ConfigData; +import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; +import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.Comment; + +@Config(name = "nexia-core") +public class ModConfig implements ConfigData { + @Comment("The server commands that get run whenever a player leaves the server.") + public String[] serverLeaveCommands = {""}; + @Comment("The player commands that get run whenever a player leaves the server.") + public String[] playerLeaveCommands = {""}; + + @Comment("The server commands that get run whenever a player joins the server.") + public String[] serverJoinCommands = {""}; + @Comment("The player commands that get run whenever a player joins the server.") + public String[] playerJoinCommands = {""}; + + @Comment("The server commands that get run whenever a player joins the server for the first time.") + public String[] serverFirstJoinCommands = {""}; + @Comment("The player commands that get run whenever a player joins the server for the first time.") + public String[] playerFirstJoinCommands = {""}; + + @Comment("What the discord link should be.") + public String discordLink = ""; + + @Comment("List of ranks.") + public String[] ranks = {""}; + + public double[] lobbyPos = { + 0.5, + 65.0, + 0.5, + }; + + @Comment("Toggles if the modified knockback is enabled.") + public boolean modifiedKnockback = false; + + @Comment("Toggles if the modified fishing rods are enabled.") + public boolean modifiedRods = false; + + @Comment("Toggles if the better shields are enabled.") + public boolean betterShields = false; + + @Comment("Toggles if the join and leave messages show.") + public boolean statusMessages = true; + + @Comment("The message that gets announced when the player joins the server for the first time.") + public String firstJoinMessage = "WELCOME %player%"; + @Comment("The message that gets announced when the player joins the server.") + public String joinMessage = "JOIN %player%"; + + @Comment("The message that gets announced when the player leaves the server.") + public String leaveMessage = "LEAVE %player%"; + +} diff --git a/src/main/java/com/nexia/core/games/util/LobbyUtil.java b/src/main/java/com/nexia/core/games/util/LobbyUtil.java new file mode 100644 index 00000000..40b04073 --- /dev/null +++ b/src/main/java/com/nexia/core/games/util/LobbyUtil.java @@ -0,0 +1,177 @@ +package com.nexia.core.games.util; + +import com.nexia.core.Main; +import com.nexia.core.utilities.item.ItemDisplayUtil; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.ffa.utilities.FfaAreas; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwScoreboard; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import com.nexia.minigames.games.duels.DuelGameMode; +import com.nexia.minigames.games.duels.DuelsGame; +import com.nexia.minigames.games.duels.DuelsSpawn; +import com.nexia.minigames.games.duels.gamemodes.GamemodeHandler; +import com.nexia.minigames.games.oitc.OitcGame; +import com.nexia.minigames.games.oitc.OitcGameMode; +import com.nexia.minigames.games.oitc.OitcSpawn; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundStopSoundPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.Level; + +public class LobbyUtil { + + public static String[] statsGameModes = {"FFA", "BEDWARS", "OITC", "DUELS"}; + + public static ServerLevel lobbyWorld = null; + public static EntityPos lobbySpawn = new EntityPos(Main.config.lobbyPos[0], Main.config.lobbyPos[1], Main.config.lobbyPos[2], 0, 0); + + public static boolean isLobbyWorld(Level level) { + return level.dimension() == Level.OVERWORLD; + } + + public static void setLobbyWorld(MinecraftServer server) { + for (ServerLevel level : server.getAllLevels()) { + if (isLobbyWorld(level)) { + lobbyWorld = level; + } + } + } + + // Players with the tag will not be affected by server rank teams given by server datapack + public static final String NO_RANK_DISPLAY_TAG = "no_rank_display"; + + public static final String NO_FALL_DAMAGE_TAG = "no_fall_damage"; + + public static final String NO_DAMAGE_TAG = "no_damage"; + + public static String[] removedTags = { + "in_bedwars", + "ffa", + "duels", + "oitc", + "in_oitc_game" + }; + + public static void leaveAllGames(ServerPlayer player, boolean tp) { + if (BwUtil.isInBedWars(player)) BwPlayerEvents.leaveInBedWars(player); + else if (FfaUtil.isFfaPlayer(player)) FfaUtil.leaveOrDie(player, player.getLastDamageSource()); + else if (PlayerDataManager.get(player).gameMode == PlayerGameMode.DUELS) DuelsGame.leave(player); + else if (PlayerDataManager.get(player).gameMode == PlayerGameMode.OITC) OitcGame.leave(player); + + BwScoreboard.removeScoreboardFor(player); + + for(int i = 0; i < LobbyUtil.removedTags.length; i++){ + if(PlayerUtil.hasTag(player, LobbyUtil.removedTags[i])){ + player.removeTag(LobbyUtil.removedTags[i]); + } + } + + returnToLobby(player, tp); + } + + public static void returnToLobby(ServerPlayer player, boolean tp) { + for(int i = 0; i < LobbyUtil.removedTags.length; i++){ + if(PlayerUtil.hasTag(player, LobbyUtil.removedTags[i])){ + player.removeTag(LobbyUtil.removedTags[i]); + } + } + + PlayerUtil.resetHealthStatus(player); + player.setGameMode(GameType.ADVENTURE); + + player.inventory.clearContent(); + player.inventory.setCarried(ItemStack.EMPTY); + player.getEnderChestInventory().clearContent(); + + if (tp) { + player.setRespawnPosition(lobbyWorld.dimension(), lobbySpawn.toBlockPos(), lobbySpawn.yaw, true, false); + lobbySpawn.teleportPlayer(lobbyWorld, player); + + ItemStack compass = new ItemStack(Items.COMPASS); + compass.setHoverName(new TextComponent("§eGamemode Selector")); + ItemDisplayUtil.addGlint(compass); + ItemDisplayUtil.addLore(compass, "§7Right click to open the menu.", 0); + + ItemStack nametag = new ItemStack(Items.NAME_TAG); + nametag.setHoverName(new TextComponent("§ePrefix Selector")); + ItemDisplayUtil.addGlint(nametag); + ItemDisplayUtil.addLore(nametag, "§7Right click to open the menu.", 0); + + player.setSlot(4, compass); + player.setSlot(3, nametag); + ItemStackUtil.sendInventoryRefreshPacket(player); + } + + player.connection.send(new ClientboundStopSoundPacket()); + player.server.getPlayerList().sendPlayerPermissionLevel(player); + + PlayerDataManager.get(player).gameMode = PlayerGameMode.LOBBY; + player.removeTag(LobbyUtil.NO_RANK_DISPLAY_TAG); + player.removeTag(LobbyUtil.NO_FALL_DAMAGE_TAG); + player.removeTag(LobbyUtil.NO_DAMAGE_TAG); + } + + public static void sendGame(ServerPlayer player, String game, boolean message){ + if (!LobbyUtil.isLobbyWorld(player.level) || PlayerDataManager.get(player).gameMode != PlayerGameMode.LOBBY) { + LobbyUtil.leaveAllGames(player, false); + } else{ + PlayerUtil.resetHealthStatus(player); + player.setGameMode(GameType.ADVENTURE); + + player.inventory.clearContent(); + player.inventory.setCarried(ItemStack.EMPTY); + player.getEnderChestInventory().clearContent(); + + player.connection.send(new ClientboundStopSoundPacket()); + + player.removeTag(LobbyUtil.NO_FALL_DAMAGE_TAG); + player.removeTag(LobbyUtil.NO_DAMAGE_TAG); + } + if(game.equalsIgnoreCase("normal ffa")){ + player.addTag("ffa"); + FfaUtil.wasInSpawn.add(player.getUUID()); + PlayerDataManager.get(player).gameMode = PlayerGameMode.FFA; + player.teleportTo(FfaAreas.ffaWorld, FfaAreas.spawn.x, FfaAreas.spawn.y, FfaAreas.spawn.z, FfaAreas.spawn.yaw, FfaAreas.spawn.pitch); + player.setRespawnPosition(FfaAreas.ffaWorld.dimension(), FfaAreas.spawn.toBlockPos(), FfaAreas.spawn.yaw, true, false); + FfaUtil.clearThrownTridents(player); + if(message){PlayerUtil.sendActionbar(player, "You have joined §8🗡 §7§lFFA §b🔱");} + FfaUtil.setInventory(player); + } + if(game.equalsIgnoreCase("bedwars")){ + if(message){PlayerUtil.sendActionbar(player, "You have joined §b\uD83E\uDE93 §c§lBedwars §e⚡");} + BwPlayerEvents.tryToJoin(player, false); + } + + if(game.equalsIgnoreCase("duels")){ + player.addTag("duels"); + PlayerDataManager.get(player).gameMode = PlayerGameMode.DUELS; + GamemodeHandler.removeQueue(player, com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).gameMode.toString(), true); + DuelsGame.death(player, player.getLastDamageSource()); + com.nexia.minigames.games.duels.util.player.PlayerDataManager.get(player).gameMode = DuelGameMode.LOBBY; + player.teleportTo(DuelsSpawn.duelWorld, DuelsSpawn.spawn.x, DuelsSpawn.spawn.y, DuelsSpawn.spawn.z, DuelsSpawn.spawn.yaw, DuelsSpawn.spawn.pitch); + player.setRespawnPosition(DuelsSpawn.duelWorld.dimension(), DuelsSpawn.spawn.toBlockPos(), DuelsSpawn.spawn.yaw, true, false); + if(message){PlayerUtil.sendActionbar(player, "You have joined §f☯ §c§lDuels §7\uD83E\uDE93");} + } + + if(game.equalsIgnoreCase("oitc")){ + player.addTag("oitc"); + PlayerDataManager.get(player).gameMode = PlayerGameMode.OITC; + OitcGame.death(player, player.getLastDamageSource()); + com.nexia.minigames.games.oitc.util.player.PlayerDataManager.get(player).gameMode = OitcGameMode.LOBBY; + player.teleportTo(OitcSpawn.oitcWorld, OitcSpawn.spawn.x, OitcSpawn.spawn.y, OitcSpawn.spawn.z, OitcSpawn.spawn.yaw, OitcSpawn.spawn.pitch); + player.setRespawnPosition(OitcSpawn.oitcWorld.dimension(), OitcSpawn.spawn.toBlockPos(), OitcSpawn.spawn.yaw, true, false); + if(message){PlayerUtil.sendActionbar(player, "You have joined §7\uD83D\uDDE1 §f§lOITC §7\uD83C\uDFF9");} + } + } + +} diff --git a/src/main/java/com/nexia/core/games/util/PlayerGameMode.java b/src/main/java/com/nexia/core/games/util/PlayerGameMode.java new file mode 100644 index 00000000..6f3affe0 --- /dev/null +++ b/src/main/java/com/nexia/core/games/util/PlayerGameMode.java @@ -0,0 +1,25 @@ +package com.nexia.core.games.util; + +import net.minecraft.world.level.Level; + +public class PlayerGameMode { + + String id; + + public static final PlayerGameMode LOBBY = new PlayerGameMode("lobby"); + public static final PlayerGameMode BEDWARS = new PlayerGameMode("bedwars"); + + public static final PlayerGameMode OITC = new PlayerGameMode("oitc"); + public static final PlayerGameMode FFA = new PlayerGameMode("ffa"); + + public static final PlayerGameMode DUELS = new PlayerGameMode("duels"); + + PlayerGameMode(String id) { + this.id = id; + } + + public static boolean isLobbyWorld(Level level) { + return level.dimension().toString().contains("minecraft:overworld"); + } + +} diff --git a/src/main/java/com/nexia/core/gui/PlayGUI.java b/src/main/java/com/nexia/core/gui/PlayGUI.java new file mode 100644 index 00000000..658588db --- /dev/null +++ b/src/main/java/com/nexia/core/gui/PlayGUI.java @@ -0,0 +1,169 @@ +package com.nexia.core.gui; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import eu.pb4.sgui.api.ClickType; +import eu.pb4.sgui.api.elements.GuiElementInterface; +import eu.pb4.sgui.api.gui.SimpleGui; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; + +public class PlayGUI extends SimpleGui { + + static final TextComponent title = new TextComponent("Game Menu"); + static final TextComponent ffaTitle = new TextComponent("FFA Menu"); + + static final TextComponent minigamesTitle = new TextComponent("Minigames Menu"); + public PlayGUI(MenuType type, ServerPlayer player, boolean includePlayer) { + super(type, player, includePlayer); + } + + private void fillEmptySlots(ItemStack itemStack, int slots){ + for(int i = 0; i < slots; i++){ + this.setSlot(i, itemStack); + /* + GuiElementInterface element = this.getSlot(i); + if(element != null && element.getItemStack().getItem() == null || element.getItemStack().getItem() == Items.AIR){ + this.setSlot(i, itemStack); + } + */ + } + } + private void setMainLayout(){ + ItemStack enchanted_sword = new ItemStack(Items.NETHERITE_SWORD, 1); + enchanted_sword.setHoverName(new TextComponent("§7§lFFA")); + enchanted_sword.enchant(Enchantments.SHARPNESS, 1); + enchanted_sword.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + enchanted_sword.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack enchanted_barrier = new ItemStack(Items.BARRIER, 1); + enchanted_barrier.setHoverName(new TextComponent("§c§lHub")); + enchanted_barrier.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack enchanted_compass = new ItemStack(Items.COMPASS, 1); + enchanted_compass.setHoverName(new TextComponent("§f§lMinigames")); + enchanted_compass.enchant(Enchantments.SHARPNESS, 1); + enchanted_compass.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + enchanted_compass.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack emptySlot = new ItemStack(Items.BLACK_STAINED_GLASS_PANE, 1); + emptySlot.setHoverName(new TextComponent("")); + + fillEmptySlots(emptySlot, 27); + this.setSlot(11, enchanted_sword); + this.setSlot(13, enchanted_barrier); + this.setSlot(15, enchanted_compass); + } + + private void setFFALayout(){ + this.setTitle(ffaTitle); + ItemStack enchanted_sword = new ItemStack(Items.NETHERITE_SWORD, 1); + enchanted_sword.setHoverName(new TextComponent("§7§lClassic FFA")); + enchanted_sword.enchant(Enchantments.SHARPNESS, 1); + enchanted_sword.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + enchanted_sword.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack unknown = new ItemStack(Items.BARRIER, 1); + unknown.setHoverName(new TextComponent("§7§l???")); + unknown.enchant(Enchantments.SHARPNESS, 1); + unknown.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + unknown.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + + ItemStack emptySlot = new ItemStack(Items.BLACK_STAINED_GLASS_PANE, 1); + emptySlot.setHoverName(new TextComponent("")); + + fillEmptySlots(emptySlot, 27); + this.setSlot(11, unknown); + this.setSlot(13, enchanted_sword); + this.setSlot(15, unknown); + } + + private void setMinigamesLayout(){ + this.setTitle(minigamesTitle); + ItemStack unknown = new ItemStack(Items.BARRIER, 1); + unknown.setHoverName(new TextComponent("§7§l???")); + unknown.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack bedwars = new ItemStack(Items.RED_BED, 1); + bedwars.setHoverName(new TextComponent("§c§lBedwars")); + bedwars.enchant(Enchantments.SHARPNESS, 1); + bedwars.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + bedwars.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + ItemStack oitc = new ItemStack(Items.BOW, 1); + oitc.setHoverName(new TextComponent("§f§lOITC")); + oitc.enchant(Enchantments.SHARPNESS, 1); + oitc.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + oitc.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + + ItemStack duels = new ItemStack(Items.DIAMOND_SWORD, 1); + duels.setHoverName(new TextComponent("§b§lDuels")); + duels.enchant(Enchantments.SHARPNESS, 1); + duels.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + duels.hideTooltipPart(ItemStack.TooltipPart.MODIFIERS); + + + + ItemStack emptySlot = new ItemStack(Items.BLACK_STAINED_GLASS_PANE, 1); + emptySlot.setHoverName(new TextComponent("")); + + fillEmptySlots(emptySlot, 27); + this.setSlot(11, duels); + this.setSlot(13, bedwars); + this.setSlot(15, oitc); + } + + public boolean click(int index, ClickType clickType, net.minecraft.world.inventory.ClickType action){ + GuiElementInterface element = this.getSlot(index); + if(element != null && clickType != ClickType.MOUSE_DOUBLE_CLICK) { + ItemStack itemStack = element.getItemStack(); + Component name = itemStack.getHoverName(); + if(name.getString().equalsIgnoreCase("§7§lClassic FFA")){ + LobbyUtil.sendGame(this.player, "normal ffa", true); + this.close(); + } + if(name.getString().equalsIgnoreCase("§7§lFFA")){ + this.setFFALayout(); + } + if(name.getString().equalsIgnoreCase("§f§lMinigames")){ + this.setMinigamesLayout(); + } + if(name.getString().equalsIgnoreCase("§c§lBedwars")){ + LobbyUtil.sendGame(this.player, "bedwars", true); + this.close(); + } + if(name.getString().equalsIgnoreCase("§f§lOITC")){ + LobbyUtil.sendGame(this.player, "oitc", true); + this.close(); + } + + if(name.getString().equalsIgnoreCase("§b§lDuels")){ + LobbyUtil.sendGame(this.player, "duels", true); + this.close(); + } + + + if(name.getString().toLowerCase().contains("hub")){ + LobbyUtil.leaveAllGames(this.player, true); + } + + if(itemStack.getItem() != Items.BLACK_STAINED_GLASS_PANE && itemStack.getItem() != Items.AIR && itemStack.getItem() != Items.NETHERITE_SWORD && itemStack.getItem() != Items.COMPASS){ + this.close(); + } + } + return super.click(index, clickType, action); + } + public static void openMainGUI(ServerPlayer player) { + PlayGUI shop = new PlayGUI(MenuType.GENERIC_9x3, player, false); + shop.setTitle(title); + shop.setMainLayout(); + shop.open(); + } +} diff --git a/src/main/java/com/nexia/core/gui/RankGUI.java b/src/main/java/com/nexia/core/gui/RankGUI.java new file mode 100644 index 00000000..01cf6eb3 --- /dev/null +++ b/src/main/java/com/nexia/core/gui/RankGUI.java @@ -0,0 +1,104 @@ +package com.nexia.core.gui; + +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import eu.pb4.sgui.api.ClickType; +import eu.pb4.sgui.api.elements.GuiElementInterface; +import eu.pb4.sgui.api.gui.SimpleGui; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; + +public class RankGUI extends SimpleGui { + static final TextComponent title = new TextComponent("Prefix Menu"); + public RankGUI(MenuType type, ServerPlayer player, boolean includePlayer) { + super(type, player, includePlayer); + } + + private void fillEmptySlots(ItemStack itemStack, int slots){ + for(int i = 0; i < slots; i++){ + this.setSlot(i, itemStack); + /* + GuiElementInterface element = this.getSlot(i); + if(element != null && element.getItemStack().getItem() == null || element.getItemStack().getItem() == Items.AIR){ + this.setSlot(i, itemStack); + } + */ + } + } + private void setMainLayout(ServerPlayer player){ + ItemStack emptySlot = new ItemStack(Items.BLACK_STAINED_GLASS_PANE, 1); + emptySlot.setHoverName(new TextComponent("")); + + fillEmptySlots(emptySlot, 36); + int slot = 10; + int airSlots = 10; + for(int air = 0; air < 14; air++){ + if(airSlots == 17) { + airSlots = 19; + } + this.setSlot(airSlots, new ItemStack(Items.AIR)); + airSlots++; + } + for(int i = 0; i < 8; i++){ + if(slot == 17) { + slot = 19; + } + + if(PlayerUtil.hasPermission(player.createCommandSourceStack(), "nexia.prefix." + Main.config.ranks[i], 4) && PlayerUtil.hasTag(player, Main.config.ranks[i])){ + ItemStack enchantedItem = new ItemStack(Items.NAME_TAG, 1); + enchantedItem.enchant(Enchantments.SHARPNESS, 1); + enchantedItem.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + enchantedItem.setHoverName(new TextComponent(Main.config.ranks[i]).withStyle(ChatFormatting.BOLD, ChatFormatting.LIGHT_PURPLE)); + this.setSlot(slot, enchantedItem); + slot++; + } else if(PlayerUtil.hasPermission(player.createCommandSourceStack(), "nexia.prefix." + Main.config.ranks[i], 4)){ + ItemStack changedItem = new ItemStack(Items.NAME_TAG, 1); + changedItem.setHoverName(new TextComponent(Main.config.ranks[i]).withStyle(ChatFormatting.WHITE)); + this.setSlot(slot, changedItem); + slot++; + } else if(PlayerUtil.hasTag(player, Main.config.ranks[i])) { + ItemStack enchantedItem = new ItemStack(Items.NAME_TAG, 1); + enchantedItem.enchant(Enchantments.SHARPNESS, 1); + enchantedItem.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + enchantedItem.setHoverName(new TextComponent(Main.config.ranks[i]).withStyle(ChatFormatting.BOLD, ChatFormatting.LIGHT_PURPLE)); + this.setSlot(slot, enchantedItem); + slot++; + } + } + } + + public boolean click(int index, ClickType clickType, net.minecraft.world.inventory.ClickType action){ + GuiElementInterface element = this.getSlot(index); + if(element != null && clickType != ClickType.MOUSE_DOUBLE_CLICK) { + ItemStack itemStack = element.getItemStack(); + Component name = itemStack.getHoverName(); + + if(itemStack.getItem() != Items.BLACK_STAINED_GLASS_PANE && itemStack.getItem() != Items.AIR){ + this.player.sendMessage(ChatFormat.format("{b1}Your prefix has been set to: {b2}{b}{}", name.getString()), Util.NIL_UUID); + + for(int i = 0; i < 8; i++){ + this.player.removeTag(Main.config.ranks[i]); + } + + this.player.addTag(name.getString().toLowerCase()); + this.setMainLayout(this.player); + } + + } + return super.click(index, clickType, action); + } + public static void openRankGUI(ServerPlayer player) { + RankGUI shop = new RankGUI(MenuType.GENERIC_9x4, player, false); + shop.setTitle(title); + shop.setMainLayout(player); + shop.open(); + } +} diff --git a/src/main/java/com/nexia/core/loader/CommandLoader.java b/src/main/java/com/nexia/core/loader/CommandLoader.java new file mode 100644 index 00000000..7e3a3996 --- /dev/null +++ b/src/main/java/com/nexia/core/loader/CommandLoader.java @@ -0,0 +1,63 @@ +package com.nexia.core.loader; + +import com.mojang.brigadier.tree.CommandNode; +import com.nexia.core.commands.player.*; +import com.nexia.core.commands.player.duels.*; +import com.nexia.core.commands.player.oitc.OitcCommand; +import com.nexia.core.commands.staff.*; +import com.nexia.core.utilities.misc.NxCmdUtil; +import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.minecraft.commands.CommandSourceStack; + +public class CommandLoader { + + public static void registerCommands() { + + /* + CommandRegistrationCallback.EVENT.register((dispatcher, bl) -> { + for (CommandNode node : dispatcher.getRoot().getChildren()) { + NxCmdUtil.alterCommand(node); + } + }); + + */ + + CommandRegistrationCallback.EVENT.register(DefaultGameRulesCommand::register); + CommandRegistrationCallback.EVENT.register(ProtectionMapCommand::register); + CommandRegistrationCallback.EVENT.register(PrefixCommand::register); + CommandRegistrationCallback.EVENT.register(RankCommand::register); + CommandRegistrationCallback.EVENT.register(RoundPosCommand::register); + CommandRegistrationCallback.EVENT.register(PingCommand::register); + + + CommandRegistrationCallback.EVENT.register(DuelCommand::register); + CommandRegistrationCallback.EVENT.register(QueueCommand::register); + CommandRegistrationCallback.EVENT.register(MapCommand::register); + + CommandRegistrationCallback.EVENT.register(OitcCommand::register); + + CommandRegistrationCallback.EVENT.register(MuteCommand::register); + CommandRegistrationCallback.EVENT.register(UnMuteCommand::register); + + CommandRegistrationCallback.EVENT.register(PlayCommand::register); + CommandRegistrationCallback.EVENT.register(LeaveCommand::register); + + CommandRegistrationCallback.EVENT.register(BanCommand::register); + CommandRegistrationCallback.EVENT.register(UnBanCommand::register); + CommandRegistrationCallback.EVENT.register(StatsCommand::register); + CommandRegistrationCallback.EVENT.register(RulesCommand::register); + CommandRegistrationCallback.EVENT.register(DiscordCommand::register); + CommandRegistrationCallback.EVENT.register(HelpCommand::register); + CommandRegistrationCallback.EVENT.register(ReportCommand::register); + + CommandRegistrationCallback.EVENT.register(MessageCommand::registerMsg); + CommandRegistrationCallback.EVENT.register(MessageCommand::registerReply); + + CommandRegistrationCallback.EVENT.register(RandomCommand::register); + + CommandRegistrationCallback.EVENT.register(HealCommand::register); + CommandRegistrationCallback.EVENT.register(BwReloadShopCommand::register); + CommandRegistrationCallback.EVENT.register(BwReloadTeamColorsCommand::register); + CommandRegistrationCallback.EVENT.register(BwSkipQueueCommand::register); + } +} diff --git a/src/main/java/com/nexia/core/mixin/block/AnvilBlockMixin.java b/src/main/java/com/nexia/core/mixin/block/AnvilBlockMixin.java new file mode 100644 index 00000000..68aeb7c4 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/block/AnvilBlockMixin.java @@ -0,0 +1,32 @@ +package com.nexia.core.mixin.block; + +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.AnvilBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(AnvilBlock.class) +public class AnvilBlockMixin { + + @Inject(method = "use", cancellable = true, at = @At("HEAD")) + private void use(BlockState blockState, Level level, BlockPos blockPos, Player player, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable cir) { + if (!(player instanceof ServerPlayer)) return; + ServerPlayer serverPlayer = (ServerPlayer) player; + + if (BwUtil.isInBedWars(serverPlayer)) { + cir.setReturnValue(InteractionResult.FAIL); + return; + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/block/BlockMixin.java b/src/main/java/com/nexia/core/mixin/block/BlockMixin.java new file mode 100644 index 00000000..ab6b526b --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/block/BlockMixin.java @@ -0,0 +1,35 @@ +package com.nexia.core.mixin.block; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Block.class) +public class BlockMixin { + + @Inject(method = "dropResources(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntity;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/item/ItemStack;)V", + at = @At("HEAD"), + cancellable = true) + private static void dropResources(BlockState blockState, Level level, BlockPos blockPos, BlockEntity blockEntity, Entity breakerEntity, ItemStack tool, CallbackInfo ci) { + if (!(level instanceof ServerLevel)) return; + ServerLevel serverLevel = (ServerLevel) level; + + if (BwAreas.isBedWarsWorld(serverLevel) && !(BwUtil.dropResources(blockState))) { + ci.cancel(); + + } + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/block/StructureBlockEntityMixin.java b/src/main/java/com/nexia/core/mixin/block/StructureBlockEntityMixin.java new file mode 100644 index 00000000..d07e3f7c --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/block/StructureBlockEntityMixin.java @@ -0,0 +1,80 @@ +package com.nexia.core.mixin.block; + +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.util.StringUtil; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.StructureBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.templatesystem.BlockRotProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Random; + +@Mixin(StructureBlockEntity.class) +public class StructureBlockEntityMixin extends BlockEntity { + @Shadow private String author; + + @Shadow private float integrity; + + @Shadow private Mirror mirror; + + @Shadow private Rotation rotation; + + @Shadow private long seed; + + @Shadow private BlockPos structurePos; + + @Shadow private BlockPos structureSize; + + @Shadow private boolean ignoreEntities; + + public StructureBlockEntityMixin(BlockEntityType blockEntityType) { + super(blockEntityType); + } + + private static Random createRandom(long l) { + return l == 0L ? new Random(Util.getMillis()) : new Random(l); + } + + @Redirect(method = "loadStructure(Lnet/minecraft/server/level/ServerLevel;ZLnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;)Z", at = @At("HEAD")) + public boolean loadStructure(ServerLevel serverLevel, boolean bl, StructureTemplate structureTemplate) { + BlockPos blockPos = this.getBlockPos(); + if (!StringUtil.isNullOrEmpty(structureTemplate.getAuthor())) { + this.author = structureTemplate.getAuthor(); + } + + BlockPos blockPos2 = structureTemplate.getSize(); + boolean bl2 = this.structureSize.equals(blockPos2); + if (!bl2) { + this.structureSize = blockPos2; + this.setChanged(); + BlockState blockState = serverLevel.getBlockState(blockPos); + serverLevel.sendBlockUpdated(blockPos, blockState, blockState, 3); + } + + if (bl && !bl2) { + return false; + } else { + StructurePlaceSettings structurePlaceSettings = (new StructurePlaceSettings()).setMirror(this.mirror).setRotation(this.rotation).setIgnoreEntities(this.ignoreEntities).setChunkPos((ChunkPos)null); + if (this.integrity < 1.0F) { + structurePlaceSettings.clearProcessors().addProcessor(new BlockRotProcessor(Mth.clamp(this.integrity, 0.0F, 1.0F))).setRandom(createRandom(this.seed)); + } + + BlockPos blockPos3 = blockPos.offset(this.structurePos); + structureTemplate.placeInWorldChunk(serverLevel, blockPos3, structurePlaceSettings, createRandom(this.seed)); + return true; + } + } +} diff --git a/src/main/java/com/nexia/core/mixin/entity/AbstractArrowMixin.java b/src/main/java/com/nexia/core/mixin/entity/AbstractArrowMixin.java new file mode 100644 index 00000000..dde20d0b --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/AbstractArrowMixin.java @@ -0,0 +1,39 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import net.minecraft.world.entity.projectile.AbstractArrow; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(AbstractArrow.class) +public class AbstractArrowMixin { + + private int arrowDamage; + + @Redirect(method = "onHitEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;ceil(D)I")) + private int modifyArrowDamage(double vanillaCalculated) { + + if (BwAreas.isBedWarsWorld(((AbstractArrow)(Object)this).level)) { + vanillaCalculated *= 0.625; + } + + arrowDamage = (int)Math.ceil(vanillaCalculated); + return arrowDamage; + } + + @Redirect(method = "onHitEntity", at = @At(value = "INVOKE", target = "Ljava/lang/Math;min(JJ)J")) + private long modifyCritDamage(long vanillaCalculated, long intMaxValue) { + + if (BwAreas.isBedWarsWorld(((AbstractArrow)(Object)this).level)) { + long critBoost = arrowDamage / 4 + 1; + arrowDamage = (int)Math.min(arrowDamage + critBoost, Integer.MAX_VALUE); + + } else { + arrowDamage = (int)Math.min(vanillaCalculated, intMaxValue); + } + + return arrowDamage; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/AbstractHurtingProjectileMixin.java b/src/main/java/com/nexia/core/mixin/entity/AbstractHurtingProjectileMixin.java new file mode 100644 index 00000000..a2127e3f --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/AbstractHurtingProjectileMixin.java @@ -0,0 +1,30 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; +import net.minecraft.world.entity.projectile.LargeFireball; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(AbstractHurtingProjectile.class) +public abstract class AbstractHurtingProjectileMixin extends Entity { + + public AbstractHurtingProjectileMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + // Slow down fireballs + @Inject(method = "getInertia", cancellable = true, at = @At("HEAD")) + private void getInertia(CallbackInfoReturnable cir) { + if (BwAreas.isBedWarsWorld(level) && (Object)this instanceof LargeFireball) { + cir.setReturnValue(BwUtil.getFireballInertia()); + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/AbstractLivingEntityMixin.java b/src/main/java/com/nexia/core/mixin/entity/AbstractLivingEntityMixin.java new file mode 100644 index 00000000..c535b41d --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/AbstractLivingEntityMixin.java @@ -0,0 +1,56 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.core.Main; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.ShieldItem; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + + +@Mixin(LivingEntity.class) +public abstract class AbstractLivingEntityMixin extends Entity { + public AbstractLivingEntityMixin(EntityType type, Level world) { + super(type, world); + } + + @Shadow public abstract double getAttributeValue(Attribute attribute); + + @Shadow public abstract ItemStack getBlockingItem(); + + /** + * @author Shinkume, NotCoded + * @reason Modification of knockback. + */ + @Overwrite + public void knockback(float f, double d, double e) { + double g = this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE); + ItemStack itemStack = this.getBlockingItem(); + if (!itemStack.isEmpty()) { + g = Math.min(1.0, g + (double) ShieldItem.getShieldKnockbackResistanceValue(itemStack)); + } + + f = (float)((double)f * (1.0 - g)); + + if (f > 0.0F && Main.config.modifiedKnockback) { + this.hasImpulse = true; + Vec3 vec3 = this.getDeltaMovement(); + Vec3 vec32 = (new Vec3(d, 0.0, e)).normalize().scale((double)f); + this.setDeltaMovement(vec3.x / 2.0 - vec32.x, this.onGround ? Math.min(0.4, (double)f * 0.75) : Math.min(0.4, vec3.y * 0.5 + (double)f * 0.675), vec3.z / 2.0 - vec32.z); + } else if ((!(f <= 0.0F)) && !Main.config.modifiedKnockback) { + this.hasImpulse = true; + Vec3 vec3 = this.getDeltaMovement(); + Vec3 vec32 = (new Vec3(d, 0.0, e)).normalize().scale((double)f); + this.setDeltaMovement(vec3.x / 2.0 - vec32.x, this.onGround ? Math.min(0.4, (double)f * 0.75) : Math.min(0.4, vec3.y + (double)f * 0.5), vec3.z / 2.0 - vec32.z); + } + } +} diff --git a/src/main/java/com/nexia/core/mixin/entity/ArmorStandMixin.java b/src/main/java/com/nexia/core/mixin/entity/ArmorStandMixin.java new file mode 100644 index 00000000..32ef548a --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ArmorStandMixin.java @@ -0,0 +1,34 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.ffa.utilities.FfaAreas; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ArmorStand.class) +public abstract class ArmorStandMixin extends LivingEntity { + + protected ArmorStandMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + @Inject(method = "interactAt", cancellable = true, at = @At("HEAD")) + private void canTakeItem(Player player, Vec3 vec3, InteractionHand interactionHand, CallbackInfoReturnable cir) { + + // Disable interacting with armor stands in ffa + if (FfaAreas.isFfaWorld(level) && !player.isCreative()) { + cir.setReturnValue(InteractionResult.FAIL); + } + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/EntityMixin.java b/src/main/java/com/nexia/core/mixin/entity/EntityMixin.java new file mode 100644 index 00000000..f55530bd --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/EntityMixin.java @@ -0,0 +1,41 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.core.games.util.LobbyUtil; +import net.minecraft.commands.CommandSource; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.Nameable; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Set; + +@Mixin(Entity.class) +public abstract class EntityMixin implements Nameable, CommandSource { + + @Shadow public abstract Set getTags(); + + @Inject(method = "isInvulnerableTo", at = @At("HEAD"), cancellable = true) + private void hurt(DamageSource damageSource, CallbackInfoReturnable cir) { + if (damageSource == DamageSource.FALL && getTags().contains(LobbyUtil.NO_FALL_DAMAGE_TAG) || damageSource == DamageSource.FALL && getTags().contains("ffa")) { + cir.setReturnValue(true); + } + } + + @Redirect(method = "baseTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getY()D")) + private double modifyVoidY(Entity instance) { + double voidY = -32; + + if (instance.level instanceof ServerLevel) { + ServerLevel serverLevel = (ServerLevel)instance.level; + } + + return instance.getY() + -64 - voidY; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/FishingBobberEntity.java b/src/main/java/com/nexia/core/mixin/entity/FishingBobberEntity.java new file mode 100644 index 00000000..9bf0e83f --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/FishingBobberEntity.java @@ -0,0 +1,47 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.core.Main; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.FishingHook; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(FishingHook.class) +public abstract class FishingBobberEntity extends Entity { + + + public FishingBobberEntity(EntityType type, Level world) { + super(type, world); + } + + @Shadow private Entity hookedIn; + + @Shadow @Nullable public abstract Player getPlayerOwner(); + + /** + * @author NotCoded, Shinkume + * @reason Make fishing rod hook pull towards the player. + */ + @Overwrite + public void bringInHookedEntity() { + Entity entity = this.getPlayerOwner(); + if (entity != null) { + Vec3 vec3; + vec3 = (new Vec3(entity.getX() - this.getX(), entity.getY() - this.getY(), entity.getZ() - this.getZ())).scale(0.1); + if(Main.config.modifiedRods) { + vec3 = (new Vec3(entity.getX() - this.getX()/2, entity.getY() - this.getY()/2, entity.getZ() - this.getZ()/2)).scale(0.2); + } + this.hookedIn.setDeltaMovement(this.hookedIn.getDeltaMovement().add(vec3)); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/nexia/core/mixin/entity/ItemEntityMixin.java b/src/main/java/com/nexia/core/mixin/entity/ItemEntityMixin.java new file mode 100644 index 00000000..c8dce151 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ItemEntityMixin.java @@ -0,0 +1,24 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemEntity.class) +public class ItemEntityMixin { + + @Inject(at = @At("TAIL"), method = "merge(Lnet/minecraft/world/entity/item/ItemEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V") + private static void mergeTail(ItemEntity itemEntity, ItemStack itemStack, ItemStack itemStack2, CallbackInfo ci) { + + if (BwAreas.isBedWarsWorld(itemEntity.level)) { + BwUtil.afterItemMerge(itemEntity); + } + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/ItemFrameMixin.java b/src/main/java/com/nexia/core/mixin/entity/ItemFrameMixin.java new file mode 100644 index 00000000..b351f626 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ItemFrameMixin.java @@ -0,0 +1,33 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.ffa.utilities.FfaAreas; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.decoration.ItemFrame; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ItemFrame.class) +public abstract class ItemFrameMixin extends Entity { + + public ItemFrameMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + @Inject(method = "interact", cancellable = true, at = @At("HEAD")) + private void canTakeItem(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + + // Disable interacting with armor stands in ffa + if (FfaAreas.isFfaWorld(level) && !player.isCreative()) { + cir.setReturnValue(InteractionResult.FAIL); + return; + } + + } +} diff --git a/src/main/java/com/nexia/core/mixin/entity/LargeFireballMixin.java b/src/main/java/com/nexia/core/mixin/entity/LargeFireballMixin.java new file mode 100644 index 00000000..f2be3da4 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/LargeFireballMixin.java @@ -0,0 +1,39 @@ +package com.nexia.core.mixin.entity; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.projectile.Fireball; +import net.minecraft.world.entity.projectile.LargeFireball; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(LargeFireball.class) +public class LargeFireballMixin extends Fireball { + + public LargeFireballMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + // Set fireball entity to fireball + @ModifyArg(method = "onHit", index = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Explosion$BlockInteraction;)Lnet/minecraft/world/level/Explosion;")) + protected Entity onHitSetEntity(@Nullable Entity entity) { + return this; + } + + // Remove fire from fireballs + @ModifyArg(method = "onHit", index = 5, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Explosion$BlockInteraction;)Lnet/minecraft/world/level/Explosion;")) + protected boolean onHitRemoveFire(boolean doFire) { + return false; + } + + // Make block destruction mobGriefing independent + @ModifyArg(method = "onHit", index = 6, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;explode(Lnet/minecraft/world/entity/Entity;DDDFZLnet/minecraft/world/level/Explosion$BlockInteraction;)Lnet/minecraft/world/level/Explosion;")) + protected Explosion.BlockInteraction onHitSetBlockInteraction(Explosion.BlockInteraction blockInteraction) { + return Explosion.BlockInteraction.DESTROY; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/LivingEntityMixin.java b/src/main/java/com/nexia/core/mixin/entity/LivingEntityMixin.java new file mode 100644 index 00000000..1a87a368 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/LivingEntityMixin.java @@ -0,0 +1,46 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.CombatRules; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(LivingEntity.class) +public class LivingEntityMixin { + + // Make void death instant + @ModifyArg(method = "hurt", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;actuallyHurt(Lnet/minecraft/world/damagesource/DamageSource;F)V")) + protected float hurt(DamageSource damageSource, float value) { + if((Object)this instanceof ServerPlayer){ + ServerPlayer player = (ServerPlayer)(Object)this; + + if(PlayerDataManager.get(player).gameMode == PlayerGameMode.DUELS) { + return value; + } + } + if (damageSource == DamageSource.OUT_OF_WORLD) return 1000000.0F; + return value; + } + + @Redirect(method = "getDamageAfterMagicAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/CombatRules;getDamageAfterMagicAbsorb(FF)F")) + private float redirectArmorProtCalculation(float damage, float protection) { + if ((Object)this instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer)(Object)this; + + if (BwAreas.isBedWarsWorld(player.getLevel())) { + return BwUtil.playerProtCalculation(damage, protection); + } + + } + return CombatRules.getDamageAfterMagicAbsorb(damage, protection); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/ThrownEggMixin.java b/src/main/java/com/nexia/core/mixin/entity/ThrownEggMixin.java new file mode 100644 index 00000000..19110530 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ThrownEggMixin.java @@ -0,0 +1,24 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.custom.BwBridgeEgg; +import net.minecraft.world.entity.projectile.ThrownEgg; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Random; + +@Mixin(ThrownEgg.class) +public class ThrownEggMixin { + + @Redirect(method = "onHit", at = @At(value = "INVOKE", ordinal = 0, target = "Ljava/util/Random;nextInt(I)I")) + private int modifyChickenSpawnOdds(Random random, int bound) { + + // Spawns chicken if return value is 0 + if ((Object)this instanceof BwBridgeEgg) { + return 1; + } + return random.nextInt(bound); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/ThrownEnderpearlMixin.java b/src/main/java/com/nexia/core/mixin/entity/ThrownEnderpearlMixin.java new file mode 100644 index 00000000..9d15156d --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ThrownEnderpearlMixin.java @@ -0,0 +1,34 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.projectile.ThrowableItemProjectile; +import net.minecraft.world.entity.projectile.ThrownEnderpearl; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(ThrownEnderpearl.class) +public abstract class ThrownEnderpearlMixin extends ThrowableItemProjectile { + + public ThrownEnderpearlMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + @ModifyArg(method = "onHit", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + private float pearlDamage(float damage) { + if (getOwner() instanceof ServerPlayer) { + ServerPlayer thrower = (ServerPlayer) getOwner(); + + if (BwUtil.isInBedWars(thrower)) { + return BwUtil.getPearlDamage(); + } + + } + return damage; + } + + +} diff --git a/src/main/java/com/nexia/core/mixin/entity/ThrownTridentMixin.java b/src/main/java/com/nexia/core/mixin/entity/ThrownTridentMixin.java new file mode 100644 index 00000000..95b2afb2 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/entity/ThrownTridentMixin.java @@ -0,0 +1,44 @@ +package com.nexia.core.mixin.entity; + +import com.nexia.minigames.games.bedwars.custom.BwTrident; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.AbstractArrow; +import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ThrownTrident.class) +public abstract class ThrownTridentMixin extends AbstractArrow { + protected ThrownTridentMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + /** + * @author NotCoded + * @reason Remove trident despawning. + */ + @Overwrite + public void tickDespawn(){ + if(this.pickup != Pickup.ALLOWED) { + super.tickDespawn(); + } + } + + @Redirect(method = "onHitEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/enchantment/EnchantmentHelper;getDamageBonus(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/entity/LivingEntity;)F")) + private float getDamageBonus(ItemStack itemStack, LivingEntity livingEntity) { + float bonus = EnchantmentHelper.getDamageBonus(itemStack, livingEntity); + + if ((Object)this instanceof BwTrident) { + bonus -= 1; + } + + return bonus; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/AxeItemMixin.java b/src/main/java/com/nexia/core/mixin/item/AxeItemMixin.java new file mode 100644 index 00000000..5f76f9a2 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/AxeItemMixin.java @@ -0,0 +1,32 @@ +package com.nexia.core.mixin.item; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.AxeItem; +import net.minecraft.world.item.context.UseOnContext; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(AxeItem.class) +public class AxeItemMixin { + + @Inject(method = "useOn", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/context/UseOnContext;getPlayer()Lnet/minecraft/world/entity/player/Player;")) + private void stripeWood(UseOnContext context, CallbackInfoReturnable cir) { + Player playerEntity = context.getPlayer(); + if (!(playerEntity instanceof ServerPlayer)) return; + ServerPlayer player = (ServerPlayer) playerEntity; + + if (BwAreas.isBedWarsWorld(context.getLevel())) { + if (!BwPlayerEvents.beforeStripWood(player, context)) { + cir.setReturnValue(InteractionResult.PASS); + return; + } + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/BlockItemMixin.java b/src/main/java/com/nexia/core/mixin/item/BlockItemMixin.java new file mode 100644 index 00000000..5f5fe2a4 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/BlockItemMixin.java @@ -0,0 +1,32 @@ +package com.nexia.core.mixin.item; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.BlockPlaceContext; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(BlockItem.class) +public abstract class BlockItemMixin { + + @Inject(method = "place", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/BlockItem;placeBlock(Lnet/minecraft/world/item/context/BlockPlaceContext;Lnet/minecraft/world/level/block/state/BlockState;)Z")) + private void beforePlace(BlockPlaceContext context, CallbackInfoReturnable cir) { + ServerPlayer player = (ServerPlayer)context.getPlayer(); + if (player == null) return; + BlockPos blockPos = context.getClickedPos(); + ServerLevel level = player.getLevel(); + + if (BwAreas.isBedWarsWorld(player.getLevel()) && !BwPlayerEvents.beforePlace(player, context)) { + cir.setReturnValue(InteractionResult.PASS); + return; + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/CompassItemMixin.java b/src/main/java/com/nexia/core/mixin/item/CompassItemMixin.java new file mode 100644 index 00000000..2c487391 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/CompassItemMixin.java @@ -0,0 +1,33 @@ +package com.nexia.core.mixin.item; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.CompassItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CompassItem.class) +public class CompassItemMixin { + + Level level; + @Inject(at = @At("HEAD"), method = "inventoryTick") + private void tick(ItemStack itemStack, Level level, Entity entity, int i, boolean bl, CallbackInfo ci) { + this.level = level; + } + + // Not require a lodestone block in order to track someone in oldbedwars + @Redirect(method = "inventoryTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;remove(Ljava/lang/String;)V")) + private void existsAtPosition(CompoundTag instance, String string) { + + if (BwAreas.isBedWarsWorld(level)) return; + instance.remove(string); + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/CraftingMixin.java b/src/main/java/com/nexia/core/mixin/item/CraftingMixin.java new file mode 100644 index 00000000..8897467c --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/CraftingMixin.java @@ -0,0 +1,55 @@ +package com.nexia.core.mixin.item; + +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.inventory.CraftingMenu; +import net.minecraft.world.inventory.ResultContainer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CraftingMenu.class) +public class CraftingMixin { + + @Shadow @Final private Player player; + // Set crafter to player + private static ServerPlayer crafter; + @Inject(method = "slotChangedCraftingGrid", at = @At("HEAD")) + private static void craft(int i, Level level, Player player, CraftingContainer craftingContainer, ResultContainer resultContainer, CallbackInfo ci) { + if (player instanceof ServerPlayer) { + crafter = (ServerPlayer) player; + } + } + + // Disable crafting for bedwars players + @ModifyArg(method = "slotChangedCraftingGrid", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/ResultContainer;setItem(ILnet/minecraft/world/item/ItemStack;)V")) + private static ItemStack setCraftResult(ItemStack itemStack) { + if (crafter == null) return itemStack; + + if (BwUtil.isInBedWars(crafter)) { + ItemStackUtil.sendInventoryRefreshPacket(crafter); + return ItemStack.EMPTY; + } + return itemStack; + } + + @ModifyArg(method = "slotChangedCraftingGrid", index = 2, at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundContainerSetSlotPacket;(IILnet/minecraft/world/item/ItemStack;)V")) + private static ItemStack setCraftResultPacketItem(ItemStack itemStack) { + if (crafter == null) return itemStack; + + if (BwUtil.isInBedWars(crafter)) { + return ItemStack.EMPTY; + } + return itemStack; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/EggItemMixin.java b/src/main/java/com/nexia/core/mixin/item/EggItemMixin.java new file mode 100644 index 00000000..c277d689 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/EggItemMixin.java @@ -0,0 +1,44 @@ +package com.nexia.core.mixin.item; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ThrownEgg; +import net.minecraft.world.item.EggItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(EggItem.class) +public class EggItemMixin { + + private InteractionHand hand; + + @Inject(method = "use", at = @At("HEAD")) + private void use(Level level, Player player, InteractionHand interactionHand, CallbackInfoReturnable> cir) { + this.hand = interactionHand; + } + + @Redirect(method = "use", at = @At(value = "NEW", target = "net/minecraft/world/entity/projectile/ThrownEgg")) + private ThrownEgg setThrownEgg(Level level, LivingEntity livingEntity) { + + if (livingEntity instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer) livingEntity; + + if (BwAreas.isBedWarsWorld(level)) { + return BwPlayerEvents.throwEgg(player, player.getItemInHand(hand)); + } + + } + return new ThrownEgg(level, livingEntity); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/PotionItemMixin.java b/src/main/java/com/nexia/core/mixin/item/PotionItemMixin.java new file mode 100644 index 00000000..68297973 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/PotionItemMixin.java @@ -0,0 +1,52 @@ +package com.nexia.core.mixin.item; + +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.PotionItem; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(PotionItem.class) +public class PotionItemMixin { + + private ServerPlayer player = null; + + @Inject(method = "finishUsingItem", at = @At("HEAD")) + private void finishUsingItem(ItemStack itemStack, Level level, LivingEntity livingEntity, CallbackInfoReturnable cir) { + if (livingEntity instanceof ServerPlayer) { + this.player = (ServerPlayer) livingEntity; + } + } + + @Inject(method = "finishUsingItem", at = @At("RETURN")) + private void finishedUsingItem(ItemStack itemStack, Level level, LivingEntity livingEntity, CallbackInfoReturnable cir) { + if (this.player == null) return; + + if (BwUtil.isBedWarsPlayer(this.player)) { + BwPlayerEvents.drankPotion(this.player, itemStack); + } + } + + @Redirect(method = "finishUsingItem", at = @At(value = "NEW", target = "net/minecraft/world/item/ItemStack")) + private ItemStack setItemAfterDrink(ItemLike itemLike) { + if (player == null) return new ItemStack(Items.GLASS_BOTTLE); + + if (BwUtil.isBedWarsPlayer(player)) { + ItemStackUtil.sendInventoryRefreshPacket(player); + return ItemStack.EMPTY; + } + + return new ItemStack(Items.GLASS_BOTTLE); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/item/TridentItemMixin.java b/src/main/java/com/nexia/core/mixin/item/TridentItemMixin.java new file mode 100644 index 00000000..b2816d0c --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/TridentItemMixin.java @@ -0,0 +1,44 @@ +package com.nexia.core.mixin.item; + + +import com.nexia.ffa.utilities.FfaAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TridentItem; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TridentItem.class) +public class TridentItemMixin { + + @Redirect(method = "releaseUsing", at = @At(value = "NEW", target = "net/minecraft/world/entity/projectile/ThrownTrident")) + private ThrownTrident setThrownTrident(Level level, LivingEntity livingEntity, ItemStack itemStack) { + + if (livingEntity instanceof ServerPlayer) { + ServerPlayer player = (ServerPlayer) livingEntity; + + if (BwUtil.isBedWarsPlayer(player)) { + return BwPlayerEvents.throwTrident(player, itemStack); + } + + } + return new ThrownTrident(level, livingEntity, itemStack); + } + + + @Inject(method = "releaseUsing", at = @At(value = "HEAD"), cancellable = true) + public void changeHoldTime(ItemStack itemStack, Level level, LivingEntity livingEntity, int i, CallbackInfo ci) { + if(livingEntity instanceof Player player){ + if(FfaAreas.isInFfaSpawn(player)) { ci.cancel(); } + } + } +} diff --git a/src/main/java/com/nexia/core/mixin/item/UseItemMixin.java b/src/main/java/com/nexia/core/mixin/item/UseItemMixin.java new file mode 100644 index 00000000..2b94eca9 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/item/UseItemMixin.java @@ -0,0 +1,32 @@ +package com.nexia.core.mixin.item; + +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.gui.PlayGUI; +import com.nexia.core.gui.RankGUI; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Item.class) +public class UseItemMixin { + @Inject(method = "use", at = @At("HEAD")) + private void changeUse(Level level, Player player, InteractionHand interactionHand, CallbackInfoReturnable> cir){ + PlayerGameMode gameMode = PlayerDataManager.get(player).gameMode; + if(player.getItemInHand(interactionHand).getDisplayName().getString().toLowerCase().contains("gamemode selector") && gameMode == PlayerGameMode.LOBBY) { + PlayGUI.openMainGUI((ServerPlayer) player); + } + if(player.getItemInHand(interactionHand).getDisplayName().getString().toLowerCase().contains("prefix selector") && gameMode == PlayerGameMode.LOBBY) { + RankGUI.openRankGUI((ServerPlayer) player); + } + } +} diff --git a/src/main/java/com/nexia/core/mixin/misc/AbstractContainerMenuMixin.java b/src/main/java/com/nexia/core/mixin/misc/AbstractContainerMenuMixin.java new file mode 100644 index 00000000..541b7aaf --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/AbstractContainerMenuMixin.java @@ -0,0 +1,28 @@ +package com.nexia.core.mixin.misc; + +import com.nexia.core.utilities.misc.EventUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AbstractContainerMenu.class) +public class AbstractContainerMenuMixin { + + @Inject(method = "removed", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;drop(Lnet/minecraft/world/item/ItemStack;Z)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void removed(Player player, CallbackInfo ci) { + if (!(player instanceof ServerPlayer serverPlayer)) return; + + if (!EventUtil.dropItem(serverPlayer, player.inventory.getCarried())) { + if (player.inventory.add(player.inventory.getCarried())) { + player.inventory.setCarried(ItemStack.EMPTY); + } + ci.cancel(); + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/misc/BanCommandMixin.java b/src/main/java/com/nexia/core/mixin/misc/BanCommandMixin.java new file mode 100644 index 00000000..bf49e24e --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/BanCommandMixin.java @@ -0,0 +1,18 @@ +package com.nexia.core.mixin.misc; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.commands.BanPlayerCommands; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(BanPlayerCommands.class) +public class BanCommandMixin { + @Inject(method = "register", cancellable = true, at = @At("HEAD")) + private static void register(CommandDispatcher commandDispatcher, CallbackInfo ci) { + // Fuck you v2.0 + ci.cancel(); + } +} diff --git a/src/main/java/com/nexia/core/mixin/misc/CommandSourceStackMixin.java b/src/main/java/com/nexia/core/mixin/misc/CommandSourceStackMixin.java new file mode 100644 index 00000000..ecd86a94 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/CommandSourceStackMixin.java @@ -0,0 +1,33 @@ +package com.nexia.core.mixin.misc; + +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CommandSourceStack.class) +public class CommandSourceStackMixin { + + @Shadow @Final private CommandSource source; + + @Shadow @Final private @Nullable Entity entity; + + @Inject(method = "broadcastToAdmins", cancellable = true, at = @At("HEAD")) + private void broadcastToAdmins(Component component, CallbackInfo ci) { + + // If sourced from datapack + if (source instanceof MinecraftServer && entity != null) { + ci.cancel(); + return; + } + + } +} diff --git a/src/main/java/com/nexia/core/mixin/misc/ExplosionMixin.java b/src/main/java/com/nexia/core/mixin/misc/ExplosionMixin.java new file mode 100644 index 00000000..48eab6ac --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/ExplosionMixin.java @@ -0,0 +1,69 @@ +package com.nexia.core.mixin.misc; + +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.ExplosionDamageCalculator; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + +import java.util.Optional; + +@Mixin(Explosion.class) +public class ExplosionMixin { + + @Shadow @Final private @Nullable Entity source; + + @Shadow @Final private Level level; + + @Redirect(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ExplosionDamageCalculator;getBlockExplosionResistance(Lnet/minecraft/world/level/Explosion;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/material/FluidState;)Ljava/util/Optional;")) + protected Optional modifyExplosionResistance(ExplosionDamageCalculator instance, Explosion explosion, BlockGetter blockGetter, BlockPos blockPos, BlockState blockState, FluidState fluidState) { + if (blockState.isAir() && fluidState.isEmpty()) return Optional.empty(); + + float resistance = blockState.getBlock().getExplosionResistance(); + if (BwAreas.isBedWarsWorld(level)) { + resistance = BwUtil.modifyBlockExplosionRes(blockPos, blockState, source, resistance); + } + return Optional.of(Math.max(resistance, fluidState.getExplosionResistance())); + } + + @Redirect(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ExplosionDamageCalculator;shouldBlockExplode(Lnet/minecraft/world/level/Explosion;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;F)Z")) + protected boolean shouldExplode(ExplosionDamageCalculator instance, Explosion explosion, BlockGetter blockGetter, BlockPos blockPos, BlockState blockState, float resistance) { + + if (BwAreas.isBedWarsWorld(level)) { + if (!BwUtil.shouldExplode(blockPos, blockState)) return false; + } + + return instance.shouldBlockExplode(explosion, blockGetter, blockPos, blockState, resistance); + } + + @ModifyArgs(method = "explode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;add(DDD)Lnet/minecraft/world/phys/Vec3;")) + protected void modifyKnockback(Args args) { + + if (BwAreas.isBedWarsWorld(level)) { + BwUtil.modifyExplosionKb(source, args); + } + + } + + @ModifyArg(method = "explode", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + protected float modifyDamage(float damage) { + + if (BwAreas.isBedWarsWorld(level)) { + return BwUtil.getExplosionDamage(damage); + } + + return damage; + } + +} \ No newline at end of file diff --git a/src/main/java/com/nexia/core/mixin/misc/MinecraftServerMixin.java b/src/main/java/com/nexia/core/mixin/misc/MinecraftServerMixin.java new file mode 100644 index 00000000..8881f215 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/MinecraftServerMixin.java @@ -0,0 +1,33 @@ +package com.nexia.core.mixin.misc; + +import com.nexia.core.utilities.time.ServerTime; +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + + boolean firstTickPassed = false; + + @Inject(at = @At("HEAD"), method = "tickChildren") + private void tickHead(CallbackInfo ci) { + if (!firstTickPassed) { + firstTickPassed = true; + ServerTime.firstTick((MinecraftServer)(Object)this); + } + } + + @Inject(at = @At("TAIL"), method = "tickChildren") + private void tickTail(CallbackInfo ci) { + ServerTime.everyTick(); + } + + @Inject(method = "stopServer", at = @At("HEAD")) + protected void stopServer(CallbackInfo ci) { + ServerTime.stopServer(); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/misc/MsgCommandMixin.java b/src/main/java/com/nexia/core/mixin/misc/MsgCommandMixin.java new file mode 100644 index 00000000..da70efeb --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/MsgCommandMixin.java @@ -0,0 +1,20 @@ +package com.nexia.core.mixin.misc; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.commands.MsgCommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MsgCommand.class) +public class MsgCommandMixin { + + @Inject(method = "register", cancellable = true, at = @At("HEAD")) + private static void register(CommandDispatcher commandDispatcher, CallbackInfo ci) { + // Fuck you + ci.cancel(); + } + +} diff --git a/src/main/java/com/nexia/core/mixin/misc/PardonCommandMixin.java b/src/main/java/com/nexia/core/mixin/misc/PardonCommandMixin.java new file mode 100644 index 00000000..0cb4fc4b --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/misc/PardonCommandMixin.java @@ -0,0 +1,19 @@ +package com.nexia.core.mixin.misc; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.commands.BanPlayerCommands; +import net.minecraft.server.commands.PardonCommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PardonCommand.class) +public class PardonCommandMixin { + @Inject(method = "register", cancellable = true, at = @At("HEAD")) + private static void register(CommandDispatcher commandDispatcher, CallbackInfo ci) { + // Fuck you v2.1 + ci.cancel(); + } +} diff --git a/src/main/java/com/nexia/core/mixin/player/FoodDataMixin.java b/src/main/java/com/nexia/core/mixin/player/FoodDataMixin.java new file mode 100644 index 00000000..88990252 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/FoodDataMixin.java @@ -0,0 +1,54 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import com.nexia.minigames.games.oitc.OitcGame; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.food.FoodData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(FoodData.class) +public abstract class FoodDataMixin { + + @Shadow private int foodLevel; + Player player; + @Inject(at = @At("HEAD"), method = "tick") + private void tick(Player player, CallbackInfo ci) { + this.player = player; + } + + // Nerf healing while in combat + @ModifyArg(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;heal(F)V")) + private float heal(float par1) { + if (!(player instanceof ServerPlayer serverPlayer)) return 1f; + + if (BwAreas.isBedWarsWorld(serverPlayer.level)) { + return 0.5f; + } + + if(OitcGame.isOITCPlayer(serverPlayer)) { + return 0.0f; + } + + return 1f; + } + + @Inject(method = "tick", at = @At("TAIL")) + private void modifyHunger(Player player, CallbackInfo ci) { + if (!(player instanceof ServerPlayer serverPlayer)) return; + + if (BwUtil.isInBedWars(serverPlayer)) { + BwPlayerEvents.afterHungerTick((FoodData)(Object)this); + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/player/PlayerListMixin.java b/src/main/java/com/nexia/core/mixin/player/PlayerListMixin.java new file mode 100644 index 00000000..efe32c07 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/PlayerListMixin.java @@ -0,0 +1,191 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.Main; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.chat.PlayerMutes; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.ffa.utilities.player.SavedPlayerData; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.*; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.login.ServerLoginPacketListener; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import net.minecraft.stats.Stats; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArgs; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + +@Mixin(PlayerList.class) +public abstract class PlayerListMixin { + + // Create player data on join + @Inject(at = @At("HEAD"), method = "placeNewPlayer") + private void placeNewPlayerHead(Connection connection, ServerPlayer player, CallbackInfo ci) { + PlayerDataManager.addPlayerData(player); + com.nexia.ffa.utilities.player.PlayerDataManager.addPlayerData(player); + com.nexia.minigames.games.duels.util.player.PlayerDataManager.addPlayerData(player); + if(player.getStats().getValue(Stats.CUSTOM.get(Stats.LEAVE_GAME)) <= 1) { + if(Main.config.serverFirstJoinCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverFirstJoinCommands, null)){ + for(int i = 0; i < Main.config.serverFirstJoinCommands.length; i++){ + PlayerUtil.executeServerCommand(Main.config.serverFirstJoinCommands[i], player); + } + } else if(Main.config.serverFirstJoinCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverFirstJoinCommands, null)){ + PlayerUtil.executeServerCommand(Main.config.serverFirstJoinCommands[0], player); + } + if(Main.config.playerFirstJoinCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.playerFirstJoinCommands, null)){ + for(int i = 0; i < Main.config.playerFirstJoinCommands.length; i++){ + PlayerUtil.executePlayerCommand(player, Main.config.playerFirstJoinCommands[i]); + } + } else if(Main.config.playerFirstJoinCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.playerFirstJoinCommands, null)){ + PlayerUtil.executePlayerCommand(player, Main.config.playerFirstJoinCommands[0]); + } + } + if(Main.config.serverJoinCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverJoinCommands, null)){ + for(int i = 0; i < Main.config.serverJoinCommands.length; i++){ + PlayerUtil.executeServerCommand(Main.config.serverJoinCommands[i], player); + } + } else if(Main.config.serverJoinCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverJoinCommands, null)){ + PlayerUtil.executeServerCommand(Main.config.serverJoinCommands[0], player); + } + if(Main.config.playerJoinCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverJoinCommands, null)){ + for(int i = 0; i < Main.config.playerJoinCommands.length; i++){ + PlayerUtil.executePlayerCommand(player, Main.config.playerJoinCommands[i]); + } + } else if(Main.config.playerJoinCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.playerJoinCommands, null)){ + PlayerUtil.executePlayerCommand(player, Main.config.playerJoinCommands[0]); + } + } + + // Create player data on join + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void placeNewPlayerTail(Connection connection, ServerPlayer player, CallbackInfo ci) { + LobbyUtil.leaveAllGames(player, true); + player.sendMessage(new TextComponent(ChatFormat.separatorLine("Welcome")), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Welcome {b2}{} {b1}to {b2}Nexia{b1}!", player.getScoreboardName()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Players online: {b2}{}", Main.server.getPlayerCount()), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Read the rules: {b2}/rules"), Util.NIL_UUID); + player.sendMessage(ChatFormat.format(" §8» {b1}Join our discord: ").append(new TextComponent(ChatFormat.brandColor2 + "\247n" + Main.config.discordLink).withStyle(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, Main.config.discordLink)))), Util.NIL_UUID); + player.sendMessage(new TextComponent(ChatFormat.separatorLine(null)), Util.NIL_UUID); + } + + // Force-leave on server leave + + @Inject(at = @At("HEAD"), method = "remove") + private void removeHead(ServerPlayer player, CallbackInfo ci) { + LobbyUtil.leaveAllGames(player, false); + if(Main.config.serverLeaveCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverLeaveCommands, null)){ + for(int i = 0; i < Main.config.serverLeaveCommands.length; i++){ + PlayerUtil.executeServerCommand(Main.config.serverLeaveCommands[i], player); + } + } else if(Main.config.serverFirstJoinCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.serverFirstJoinCommands, null)){ + PlayerUtil.executeServerCommand(Main.config.serverLeaveCommands[0], player); + } + if(Main.config.playerLeaveCommands.length >= 2 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.playerLeaveCommands, null)){ + for(int i = 0; i < Main.config.playerLeaveCommands.length; i++){ + PlayerUtil.executePlayerCommand(player, Main.config.playerLeaveCommands[i]); + } + } else if(Main.config.playerLeaveCommands.length <= 1 && !ChatFormat.hasWhiteSpacesOrSpaces(Main.config.playerLeaveCommands, null)){ + PlayerUtil.executePlayerCommand(player, Main.config.playerLeaveCommands[0]); + } + } + + // Clear player data on leave + @Inject(at = @At("TAIL"), method = "remove") + private void removeTail(ServerPlayer player, CallbackInfo ci) { + PlayerDataManager.removePlayerData(player); + com.nexia.ffa.utilities.player.PlayerDataManager.removePlayerData(player); + com.nexia.minigames.games.duels.util.player.PlayerDataManager.removePlayerData(player); + } + + @Inject(at = @At("RETURN"), method = "respawn") + private void respawned(ServerPlayer oldPlayer, boolean bl, CallbackInfoReturnable cir) { + ServerPlayer player = PlayerUtil.getFixedPlayer(oldPlayer); + + if (BwUtil.isInBedWars(player)) { + BwPlayerEvents.respawned(player); + } + } + + @ModifyArgs(method = "broadcastMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ClientboundChatPacket;(Lnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/ChatType;Ljava/util/UUID;)V")) + private void handleChat(Args args) { + try { + Component component = args.get(0); + ServerPlayer player = ServerTime.minecraftServer.getPlayerList().getPlayer(args.get(2)); + + if(Main.config.statusMessages){ + String key = ((TranslatableComponent) component).getKey(); + if ( + key.contains("multiplayer.player.join") + ) { + args.set(0, joinFormat(component, player)); + } + if ( + key.contains("multiplayer.player.left") + ) { + args.set(0, leaveFormat(component, player)); + } + + } + + if(!PlayerMutes.muted(player)){ + args.set(0, chatFormat(player, component)); + } + + } catch (Exception ignored) {} + } + + private static Component joinFormat(Component original, ServerPlayer joinPlayer) { + try { + String name = String.valueOf(joinPlayer.getScoreboardName()); + if(ChatFormat.hasWhiteSpacesOrSpaces(null, name)) { return original; } + if(joinPlayer.getStats().getValue(Stats.CUSTOM.get(Stats.LEAVE_GAME)) < 1) { return ChatFormat.format(Main.config.firstJoinMessage.replaceAll("%player%", name)); } + return ChatFormat.format(Main.config.joinMessage.replaceAll("%player%", name)); + } catch (Exception var8) { + return original; + } + } + + private static Component leaveFormat(Component original, ServerPlayer leavePlayer) { + try { + String name = String.valueOf(leavePlayer.getScoreboardName()); + if(ChatFormat.hasWhiteSpacesOrSpaces(null, name)) { return original; } + return ChatFormat.format(Main.config.leaveMessage.replaceAll("%player%", name)); + } catch (Exception var8) { + return original; + } + } + + private static Component chatFormat(ServerPlayer player, Component original) { + try { + TranslatableComponent component = (TranslatableComponent) original; + Object[] args = component.getArgs(); + + MutableComponent name = (MutableComponent) args[0]; + MutableComponent suffix = new TextComponent(" » ").withStyle(ChatFormatting.GRAY); + + String messageString = (String) args[1]; + MutableComponent message = new TextComponent(messageString).withStyle(ChatFormatting.WHITE); + + return name.append(suffix).append(message); + + } catch (Exception e) { + return original; + } + } + +} diff --git a/src/main/java/com/nexia/core/mixin/player/PlayerMixin.java b/src/main/java/com/nexia/core/mixin/player/PlayerMixin.java new file mode 100644 index 00000000..d614b430 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/PlayerMixin.java @@ -0,0 +1,152 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.Main; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import com.nexia.minigames.games.oitc.OitcGame; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.CombatRules; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.Arrow; +import net.minecraft.world.item.ItemCooldowns; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Player.class) +public abstract class PlayerMixin extends LivingEntity { + + @Shadow @Final public Inventory inventory; + + @Shadow public abstract ItemCooldowns getCooldowns(); + + protected PlayerMixin(EntityType entityType, Level level) { + super(entityType, level); + } + + + /** + * @author NotCoded + * @reason Make shield break actually play the sound to other players. + */ + @Overwrite + public boolean disableShield(float f) { + this.getCooldowns().addCooldown(Items.SHIELD, (int)(f * 20.0F)); + this.stopUsingItem(); + this.level.broadcastEntityEvent(this, (byte)30); + if((this.getLastDamageSource() != null && this.getLastDamageSource().getEntity() instanceof Player attacker) && Main.config.betterShields){ + //this.level.broadcastEntityEvent(attacker, (byte)30); + + SoundSource soundSource = null; + for (SoundSource source : SoundSource.values()) { + soundSource = source; + } + PlayerUtil.sendSound((ServerPlayer) attacker, new EntityPos(attacker.position()), SoundEvents.SHIELD_BREAK, soundSource, 2, 1); + } + return true; + } + + @Inject(method = "hurt", cancellable = true, at = @At("HEAD")) + private void beforeHurt(DamageSource damageSource, float damage, CallbackInfoReturnable cir) { + if (!((Object) this instanceof ServerPlayer player)) return; + + if (PlayerUtil.hasTag(player, LobbyUtil.NO_DAMAGE_TAG)) { + cir.setReturnValue(false); + } + + if (damageSource.getEntity() instanceof ServerPlayer && PlayerUtil.hasTag((ServerPlayer) damageSource.getEntity(), LobbyUtil.NO_DAMAGE_TAG)) { + cir.setReturnValue(false); + } + } + + @Inject(method = "actuallyHurt", at = @At("TAIL")) + private void afterHurt(DamageSource damageSource, float damage, CallbackInfo ci) { + if (!((Object) this instanceof ServerPlayer player)) return; + + if (BwUtil.isBedWarsPlayer(player)) { + BwPlayerEvents.afterHurt(player, damageSource); + } + } + + @Redirect(method = "actuallyHurt",at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F")) + private float modifyArmorCalculation(Player instance, DamageSource damageSource, float damage) { + + hurtArmor(damageSource, damage); + if (!((Object) this instanceof ServerPlayer player)) return vanillaArmorCalculation(damageSource, damage); + + if (BwUtil.isBedWarsPlayer(player)) { + return BwUtil.playerArmorCalculation(player, damageSource, damage); + } + + return vanillaArmorCalculation(damageSource, damage); + } + + public float vanillaArmorCalculation(DamageSource damageSource, float damage) { + if (!damageSource.isBypassArmor()) { + damage = CombatRules.getDamageAfterAbsorb(damage, this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); + } + return damage; + } + + @Inject(method = "drop*", cancellable = true, at = @At("HEAD")) + private void drop1(boolean dropAll, CallbackInfoReturnable cir) { + if (!((Object) this instanceof ServerPlayer player)) return; + + ItemStack dropped = inventory.getItem(inventory.selected); + + if (FfaUtil.isFfaPlayer(player)) { + cir.setReturnValue(false); + return; + } + if(LobbyUtil.isLobbyWorld(player.getLevel())){ + cir.setReturnValue(false); + return; + } + if (BwUtil.isBedWarsPlayer(player)) { + if (!BwUtil.canDropItem(dropped)) { + ItemStackUtil.sendInventoryRefreshPacket(player); + cir.setReturnValue(false); + return; + } + } + + if(OitcGame.isOITCPlayer(player)){ + cir.setReturnValue(false); + return; + } + + } + + @Inject(method = "getAttackDelay", cancellable = true, at = @At("HEAD")) + private void getAttackDelay(CallbackInfoReturnable cir) { + if (!((Object) this instanceof ServerPlayer player)) return; + + if (BwUtil.isBedWarsPlayer(player)) { + BwUtil.setAttackSpeed(player); + } + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/player/ServerGamePacketListenerMixin.java b/src/main/java/com/nexia/core/mixin/player/ServerGamePacketListenerMixin.java new file mode 100644 index 00000000..e8eea175 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/ServerGamePacketListenerMixin.java @@ -0,0 +1,133 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.utilities.chat.PlayerMutes; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.misc.EventUtil; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import com.nexia.minigames.games.oitc.OitcGame; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.*; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerGamePacketListenerImpl.class) +public class ServerGamePacketListenerMixin { + + @Shadow public ServerPlayer player; + + @Inject(method = "handleChat", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/players/PlayerList;broadcastMessage(Lnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/ChatType;Ljava/util/UUID;)V")) + private void handleChat(ServerboundChatPacket serverboundChatPacket, CallbackInfo ci) { + if (PlayerMutes.muted(player)) { + ci.cancel(); + } + } + + @Inject(method = "handleInteract", cancellable = true, at = @At("HEAD")) + private void mobInteract(ServerboundInteractPacket packet, CallbackInfo ci) { + ServerLevel level = player.getLevel(); + + if (BwAreas.isBedWarsWorld(level) && !BwPlayerEvents.interact(player, packet)) { + ci.cancel(); + + } + } + + @Inject(method = "handleUseItemOn", at = @At("HEAD"), cancellable = true) + private void handleUseItemOn(ServerboundUseItemOnPacket packet, CallbackInfo ci) { + + if (BwUtil.isInBedWars(player)) { + if (!BwPlayerEvents.useItem(player, packet.getHand())) { + ci.cancel(); + BlockPos blockPos = packet.getHitResult().getBlockPos().relative(packet.getHitResult().getDirection()); + player.connection.send(new ClientboundBlockUpdatePacket(blockPos, player.level.getBlockState(blockPos))); + return; + } + } + + } + + @Inject(method = "handleUseItem", at = @At("HEAD"), cancellable = true) + private void handleUseItem(ServerboundUseItemPacket serverboundUseItemPacket, CallbackInfo ci) { + + if (BwUtil.isInBedWars(player)) { + if (!BwPlayerEvents.useItem(player, serverboundUseItemPacket.getHand())) { + ci.cancel(); + } + } + } + + @Inject(method = "handleContainerClick", cancellable = true, at = @At("HEAD")) + private void handleContainerClick(ServerboundContainerClickPacket clickPacket, CallbackInfo ci) { + int containerId = clickPacket.getContainerId(); + int slot = clickPacket.getSlotNum(); + ItemStack itemStack = clickPacket.getItem(); + + if ((clickPacket.getClickType() == ClickType.THROW || slot == -999)) { + if (!EventUtil.dropItem(player, itemStack)) { + ItemStackUtil.sendInventoryRefreshPacket(player); + ci.cancel(); + return; + } + } + + if (BwUtil.isBedWarsPlayer(player)) { + if (!BwPlayerEvents.containerClick(player, clickPacket)) { + ItemStackUtil.sendInventoryRefreshPacket(player); + ci.cancel(); + return; + } + } + + if (FfaUtil.isFfaPlayer(player)) { + // If clicks on crafting slot + if (containerId == 0 && slot >= 1 && slot <= 4) { + ItemStackUtil.sendInventoryRefreshPacket(player); + ci.cancel(); + return; + } + } + + } + + @Inject(method = "handlePlayerAction", cancellable = true, at = @At("HEAD")) + private void handlePlayerAction(ServerboundPlayerActionPacket actionPacket, CallbackInfo ci) { + ServerboundPlayerActionPacket.Action action = actionPacket.getAction(); + Inventory inv = player.inventory; + + if ((action == ServerboundPlayerActionPacket.Action.DROP_ITEM || + action == ServerboundPlayerActionPacket.Action.DROP_ALL_ITEMS) && + !EventUtil.dropItem(player, inv.getItem(player.inventory.selected))) { + player.connection.send(new ClientboundContainerSetSlotPacket(0, 36 + inv.selected, inv.getSelected())); + ci.cancel(); + return; + } + } + + @Inject(method = "handleTeleportToEntityPacket", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;teleportTo(Lnet/minecraft/server/level/ServerLevel;DDDFF)V")) + private void handleSpectatorTeleport(ServerboundTeleportToEntityPacket packet, CallbackInfo ci) { + + if (BwUtil.isInBedWars(player)) { + if (!BwPlayerEvents.spectatorTeleport(player, packet)) { + ci.cancel(); + return; + } + } + + if(OitcGame.isOITCPlayer(player)){ + ci.cancel(); + return; + } + } +} diff --git a/src/main/java/com/nexia/core/mixin/player/ServerLoginPacketListenerMixin.java b/src/main/java/com/nexia/core/mixin/player/ServerLoginPacketListenerMixin.java new file mode 100644 index 00000000..daa33da3 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/ServerLoginPacketListenerMixin.java @@ -0,0 +1,33 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import java.util.Arrays; + +@Mixin(ServerLoginPacketListenerImpl.class) +public class ServerLoginPacketListenerMixin { + + @ModifyArg(method = "handleAcceptedLogin", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerLoginPacketListenerImpl;disconnect(Lnet/minecraft/network/chat/Component;)V")) + private Component handleAcceptedLogin(Component original) { + if (!(original instanceof TranslatableComponent)) return original; + TranslatableComponent component = (TranslatableComponent) original; + + if (component.getKey().contains("banned")) { + component = new TranslatableComponent("§c§lYou have been banned.\n§7Reason: §d" + component.getString().split("Reason: ")[1] + "\n§7You can appeal your ban at §d" + Main.config.discordLink); + } + + return component; + } + +} diff --git a/src/main/java/com/nexia/core/mixin/player/ServerPlayerGameModeMixin.java b/src/main/java/com/nexia/core/mixin/player/ServerPlayerGameModeMixin.java new file mode 100644 index 00000000..d9b80ea8 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/ServerPlayerGameModeMixin.java @@ -0,0 +1,45 @@ +package com.nexia.core.mixin.player; + +import com.nexia.core.utilities.item.BlockUtil; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ServerPlayerGameMode.class) +public class ServerPlayerGameModeMixin { + + @Shadow public ServerPlayer player; + + @Shadow public ServerLevel level; + + private boolean isBed = false; + + @Inject(at = @At("HEAD"), method = "destroyBlock", cancellable = true) + private void destroyBlock(BlockPos blockPos, CallbackInfoReturnable cir) { + + if (BwAreas.isBedWarsWorld(level) && !BwPlayerEvents.beforeBreakBlock(player, blockPos)) { + cir.setReturnValue(false); + } + + isBed = BlockUtil.blockToText(player.level.getBlockState(blockPos)).endsWith("_bed"); + } + + @Inject(at = @At("RETURN"), method = "destroyBlock") + private void destroyBlockTail(BlockPos blockPos, CallbackInfoReturnable cir) { + + if (BwUtil.isBedWarsPlayer(player) && isBed) { + BwPlayerEvents.bedBroken(player, blockPos); + } + + } + +} diff --git a/src/main/java/com/nexia/core/mixin/player/ServerPlayerMixin.java b/src/main/java/com/nexia/core/mixin/player/ServerPlayerMixin.java new file mode 100644 index 00000000..a0826e61 --- /dev/null +++ b/src/main/java/com/nexia/core/mixin/player/ServerPlayerMixin.java @@ -0,0 +1,82 @@ +package com.nexia.core.mixin.player; + +import com.mojang.authlib.GameProfile; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayerEvents; +import com.nexia.minigames.games.duels.DuelGameMode; +import com.nexia.minigames.games.duels.DuelsGame; +import com.nexia.minigames.games.oitc.OitcGame; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayer.class) +public abstract class ServerPlayerMixin extends Player { + + @Shadow public abstract ServerLevel getLevel(); + + @Shadow public abstract void playNotifySound(SoundEvent soundEvent, SoundSource soundSource, float f, float g); + + public ServerPlayerMixin(Level level, BlockPos blockPos, float f, GameProfile gameProfile) { + super(level, blockPos, f, gameProfile); + } + + @Inject(method = "die", at = @At("HEAD")) + private void die(DamageSource damageSource, CallbackInfo ci) { + ServerPlayer player = (ServerPlayer)(Object)this; + + if (FfaUtil.isFfaPlayer(player)) { + FfaUtil.leaveOrDie(player, damageSource); + + } else if (BwAreas.isBedWarsWorld(getLevel())) { + BwPlayerEvents.death(player); + } + else if(PlayerDataManager.get(player).gameMode == PlayerGameMode.OITC){ + OitcGame.death(player, damageSource); + } + else if(PlayerDataManager.get(player).gameMode == PlayerGameMode.DUELS){ + DuelsGame.death(player, damageSource); + } + } + + /* + @Inject(method = "die", at = @At("RETURN")) + private void dieReturn(DamageSource damageSource, CallbackInfo ci){ + ServerPlayer player = (ServerPlayer)(Object)this; + + if(PlayerDataManager.get(player).gameMode == PlayerGameMode.DUELS){ + DuelsGame.death(player, damageSource); + } + } + + */ + + @Redirect(method = "doCloseContainer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;removed(Lnet/minecraft/world/entity/player/Player;)V")) + private void removed(AbstractContainerMenu instance, Player player) { + + if (FfaUtil.isFfaPlayer(player)) { + player.inventory.add(inventory.getCarried()); + player.inventory.setCarried(ItemStack.EMPTY); + return; + } + + player.containerMenu.removed(player); + } + +} diff --git a/src/main/java/com/nexia/core/utilities/chat/ChatFormat.java b/src/main/java/com/nexia/core/utilities/chat/ChatFormat.java new file mode 100644 index 00000000..3afc992c --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/chat/ChatFormat.java @@ -0,0 +1,152 @@ +package com.nexia.core.utilities.chat; + +import net.minecraft.network.chat.TextComponent; +import org.apache.commons.lang3.tuple.MutablePair; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class ChatFormat { + + // Colors + public static String colorIndex = "\247"; + public static String bold = "\247l"; + public static String strikeThrough = "\247m"; + public static String underLine = "\247n"; + public static String reset = "\247r"; + + public static String chatColor2 = "\2477"; + + public static String normalColor = "\247f"; + public static String systemColor = "\2477"; + + public static String brandColor1 = "\247d"; + + public static String brandColor2 = "\2475"; + + + public static String failColor = "\247c"; + + public static String lineColor = "\2478"; + public static String lineTitleColor = brandColor1; + + + private static final HashMap colorSymbols = getColorSymbols(); + private static HashMap getColorSymbols() { + HashMap colorSymbols = new HashMap<>(); + colorSymbols.put("{b1}", "\247d"); + colorSymbols.put("{b2}", "\2475"); + colorSymbols.put("{b}", "\247l"); // Bold + colorSymbols.put("{ul}", "\247n"); // Underline + colorSymbols.put("{s}", "\2477"); // System + colorSymbols.put("{f}", "\247c"); // Fail + colorSymbols.put("{n}", "\247f"); // Normal + return colorSymbols; + } + + public static String formatString(String string, Object... objects) { + + int objectIndex = 0; + String objectKey = "{}"; + + main: + for (int i = 0; i < string.length(); i++) { + if (string.charAt(i) != '{') continue; + + // Add color formatting + for (Map.Entry entry : colorSymbols.entrySet()) { + MutablePair pair = replaceAt(string, entry.getKey(), entry.getValue(), i); + if (pair == null) continue; + string = pair.left; + i = pair.right; + continue main; + } + + // Add objects + if (objects.length <= objectIndex) continue; + MutablePair pair = replaceAt(string, objectKey, objects[objectIndex].toString(), i); + if (pair == null) continue; + string = pair.left; + i = pair.right; + objectIndex++; + } + return string; + } + + public static TextComponent format(String string, Object... objects) { + return new TextComponent(formatString(string, objects)); + } + + + // Returns null if cannot replace + @Nullable + public static MutablePair replaceAt(String string, String key, String replacement, int index, boolean ignoreCase) { + if (string.length() < index + key.length()) return null; + + String keySubString = string.substring(index, index + key.length()); + if (ignoreCase) { + if (!keySubString.equalsIgnoreCase(key)) return null; + } else { + if (!keySubString.equals(key)) return null; + } + + string = string.substring(0, index) + + replacement + + string.substring(index + key.length()); + + index += replacement.length() - 1; + return MutablePair.of(string, index); + } + + public static MutablePair replaceAt(String string, String key, String replacement, int index) { + return replaceAt(string, key, replacement, index, false); + } + + public static TextComponent formatFail(String string) { + return new TextComponent(failColor + string); + } + + public static String separatorLine(String title) { + String spaces = " "; + + if (title != null) { + int lineLength = spaces.length() - Math.round((float)title.length() * 1.33F) - 4; + return lineColor + strikeThrough + spaces.substring(0, lineLength / 2) + + reset + lineColor + "[ " + lineTitleColor + title + lineColor + " ]" + + lineColor + strikeThrough + spaces.substring(0, (lineLength + 1) / 2); + + } else { + return lineColor + strikeThrough + spaces; + } + } + + public static boolean hasWhiteSpacesOrSpaces(@Nullable String[] strings, @Nullable String string){ + if(strings == null && string != null){ + return string.matches(".*\\s+.*") || string.matches(""); + } else { + for (String s : strings) { + if (s.matches(".*\\s+.*") || s.matches("")) { + return true; + } + } + } + return false; + } + + public static String removeColors(String string) { + StringBuilder stringBuilder = new StringBuilder(string); + + for (int i = 0; i < stringBuilder.length(); i++) { + if (stringBuilder.charAt(i) == '§') { + stringBuilder.deleteCharAt(i); + if (stringBuilder.length() > i) { + stringBuilder.deleteCharAt(i); + } + i--; + } + } + return stringBuilder.toString(); + } + +} diff --git a/src/main/java/com/nexia/core/utilities/chat/PlayerMutes.java b/src/main/java/com/nexia/core/utilities/chat/PlayerMutes.java new file mode 100644 index 00000000..29a23065 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/chat/PlayerMutes.java @@ -0,0 +1,76 @@ +package com.nexia.core.utilities.chat; + +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.player.SavedPlayerData; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; + +public class PlayerMutes { + + public static void mute(CommandSourceStack sender, ServerPlayer muted, int duration, String reason) { + if (PlayerUtil.hasPermission(muted.createCommandSourceStack(), "nexia.staff.mute", 1)) { + sender.sendSuccess(ChatFormat.format("{f}You can't mute staff."), false); + return; + } + + SavedPlayerData mutedData = PlayerDataManager.get(muted).savedData; + long currentMuteTime = mutedData.muteEnd - System.currentTimeMillis(); + + if (currentMuteTime > 0) { + sender.sendSuccess(ChatFormat.format("{s}This player has already been muted for {f}{}{s}." + + "\n{s}Reason: {f}{}", muteTimeToText(currentMuteTime), mutedData.muteReason), false); + return; + } + + mutedData.muteEnd = System.currentTimeMillis() + duration; + mutedData.muteReason = reason; + + sender.sendSuccess(ChatFormat.format("{s}Muted {b2}{} {s}for {b2}{}{s}." + + "\n{s}Reason: {b2}{}", muted.getScoreboardName(), muteTimeToText(duration), reason), false); + muted.sendMessage(ChatFormat.format("{s}You have been muted for {f}{}{s}." + + "\n{s}Reason: {f}{}", muteTimeToText(duration), reason), Util.NIL_UUID); + + } + + public static void unMute(CommandSourceStack sender, ServerPlayer unMuted) { + SavedPlayerData unMutedData = PlayerDataManager.get(unMuted).savedData; + long currentMuteTime = unMutedData.muteEnd - System.currentTimeMillis(); + + if (currentMuteTime <= 0) { + sender.sendSuccess(ChatFormat.format("{s}This player is not muted."), false); + return; + } + + unMutedData.muteEnd = System.currentTimeMillis(); + unMutedData.muteReason = null; + + sender.sendSuccess(ChatFormat.format("{s}Unmuted {b2}{}{s}.", unMuted.getScoreboardName()), false); + unMuted.sendMessage(ChatFormat.format("{s}You have been unmuted."), Util.NIL_UUID); + } + + public static boolean muted(ServerPlayer player) { + SavedPlayerData savedData = PlayerDataManager.get(player).savedData; + long muteTime = savedData.muteEnd - System.currentTimeMillis(); + String reason = savedData.muteReason; + + if (muteTime > 0) { + player.sendMessage(ChatFormat.format("{s}You have been muted for {f}{}{s}." + + "\n{s}Reason: {f}{}", muteTimeToText(muteTime), reason), Util.NIL_UUID); + return true; + } + return false; + } + + private static String muteTimeToText(long millis) { + long seconds = millis / 1000; + long minutes = seconds / 60; + seconds %= 60; + long hours = minutes / 60; + minutes %= 60; + + return hours + "h, " + minutes + "m, " + seconds + "s"; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/chat/RomanNumbers.java b/src/main/java/com/nexia/core/utilities/chat/RomanNumbers.java new file mode 100644 index 00000000..9ba2c02a --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/chat/RomanNumbers.java @@ -0,0 +1,33 @@ +package com.nexia.core.utilities.chat; + +import java.util.TreeMap; + +public class RomanNumbers { + + private final static TreeMap romanNumbers = getRomanNumbers(); + private static TreeMap getRomanNumbers() { + TreeMap romanNumbers = new TreeMap<>(); + romanNumbers.put(1000, "M"); + romanNumbers.put(900, "CM"); + romanNumbers.put(500, "D"); + romanNumbers.put(400, "CD"); + romanNumbers.put(100, "C"); + romanNumbers.put(90, "XC"); + romanNumbers.put(50, "L"); + romanNumbers.put(40, "XL"); + romanNumbers.put(10, "X"); + romanNumbers.put(9, "IX"); + romanNumbers.put(5, "V"); + romanNumbers.put(4, "IV"); + romanNumbers.put(1, "I"); + return romanNumbers; + } + + public static String intToRoman(int number) { + int floor = romanNumbers.floorKey(number); + if (number == floor) { + return romanNumbers.get(number); + } + return romanNumbers.get(floor) + intToRoman(number - floor); + } +} diff --git a/src/main/java/com/nexia/core/utilities/http/DiscordWebhook.java b/src/main/java/com/nexia/core/utilities/http/DiscordWebhook.java new file mode 100644 index 00000000..3b1c0527 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/http/DiscordWebhook.java @@ -0,0 +1,391 @@ +package com.nexia.core.utilities.http; + +import javax.net.ssl.HttpsURLConnection; +import java.awt.Color; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +// Copied from k3kdude +// https://gist.github.com/k3kdude/fba6f6b37594eae3d6f9475330733bdb +public class DiscordWebhook { + + private final String url; + private String content; + private String username; + private String avatarUrl; + private boolean tts; + private List embeds = new ArrayList<>(); + + /** + * Constructs a new DiscordWebhook instance + * + * @param url The webhook URL obtained in Discord + */ + public DiscordWebhook(String url) { + this.url = url; + } + + public void setContent(String content) { + this.content = content; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + public void setTts(boolean tts) { + this.tts = tts; + } + + public void addEmbed(EmbedObject embed) { + this.embeds.add(embed); + } + + public void execute() throws IOException { + if (this.content == null && this.embeds.isEmpty()) { + throw new IllegalArgumentException("Set content or add at least one EmbedObject"); + } + + JSONObject json = new JSONObject(); + + json.put("content", this.content); + json.put("username", this.username); + json.put("avatar_url", this.avatarUrl); + json.put("tts", this.tts); + + if (!this.embeds.isEmpty()) { + List embedObjects = new ArrayList<>(); + + for (EmbedObject embed : this.embeds) { + JSONObject jsonEmbed = new JSONObject(); + + jsonEmbed.put("title", embed.getTitle()); + jsonEmbed.put("description", embed.getDescription()); + jsonEmbed.put("url", embed.getUrl()); + + if (embed.getColor() != null) { + Color color = embed.getColor(); + int rgb = color.getRed(); + rgb = (rgb << 8) + color.getGreen(); + rgb = (rgb << 8) + color.getBlue(); + + jsonEmbed.put("color", rgb); + } + + EmbedObject.Footer footer = embed.getFooter(); + EmbedObject.Image image = embed.getImage(); + EmbedObject.Thumbnail thumbnail = embed.getThumbnail(); + EmbedObject.Author author = embed.getAuthor(); + List fields = embed.getFields(); + + if (footer != null) { + JSONObject jsonFooter = new JSONObject(); + + jsonFooter.put("text", footer.getText()); + jsonFooter.put("icon_url", footer.getIconUrl()); + jsonEmbed.put("footer", jsonFooter); + } + + if (image != null) { + JSONObject jsonImage = new JSONObject(); + + jsonImage.put("url", image.getUrl()); + jsonEmbed.put("image", jsonImage); + } + + if (thumbnail != null) { + JSONObject jsonThumbnail = new JSONObject(); + + jsonThumbnail.put("url", thumbnail.getUrl()); + jsonEmbed.put("thumbnail", jsonThumbnail); + } + + if (author != null) { + JSONObject jsonAuthor = new JSONObject(); + + jsonAuthor.put("name", author.getName()); + jsonAuthor.put("url", author.getUrl()); + jsonAuthor.put("icon_url", author.getIconUrl()); + jsonEmbed.put("author", jsonAuthor); + } + + List jsonFields = new ArrayList<>(); + for (EmbedObject.Field field : fields) { + JSONObject jsonField = new JSONObject(); + + jsonField.put("name", field.getName()); + jsonField.put("value", field.getValue()); + jsonField.put("inline", field.isInline()); + + jsonFields.add(jsonField); + } + + jsonEmbed.put("fields", jsonFields.toArray()); + embedObjects.add(jsonEmbed); + } + + json.put("embeds", embedObjects.toArray()); + } + + URL url = new URL(this.url); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("User-Agent", "Java-DiscordWebhook-BY-Gelox_"); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + + OutputStream stream = connection.getOutputStream(); + stream.write(json.toString().getBytes()); + stream.flush(); + stream.close(); + + connection.getInputStream().close(); //I'm not sure why but it doesn't work without getting the InputStream + connection.disconnect(); + } + + public static class EmbedObject { + private String title; + private String description; + private String url; + private Color color; + + private Footer footer; + private Thumbnail thumbnail; + private Image image; + private Author author; + private List fields = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public String getUrl() { + return url; + } + + public Color getColor() { + return color; + } + + public Footer getFooter() { + return footer; + } + + public Thumbnail getThumbnail() { + return thumbnail; + } + + public Image getImage() { + return image; + } + + public Author getAuthor() { + return author; + } + + public List getFields() { + return fields; + } + + public EmbedObject setTitle(String title) { + this.title = title; + return this; + } + + public EmbedObject setDescription(String description) { + this.description = description; + return this; + } + + public EmbedObject setUrl(String url) { + this.url = url; + return this; + } + + public EmbedObject setColor(Color color) { + this.color = color; + return this; + } + + public EmbedObject setFooter(String text, String icon) { + this.footer = new Footer(text, icon); + return this; + } + + public EmbedObject setThumbnail(String url) { + this.thumbnail = new Thumbnail(url); + return this; + } + + public EmbedObject setImage(String url) { + this.image = new Image(url); + return this; + } + + public EmbedObject setAuthor(String name, String url, String icon) { + this.author = new Author(name, url, icon); + return this; + } + + public EmbedObject addField(String name, String value, boolean inline) { + this.fields.add(new Field(name, value, inline)); + return this; + } + + private class Footer { + private String text; + private String iconUrl; + + private Footer(String text, String iconUrl) { + this.text = text; + this.iconUrl = iconUrl; + } + + private String getText() { + return text; + } + + private String getIconUrl() { + return iconUrl; + } + } + + private class Thumbnail { + private String url; + + private Thumbnail(String url) { + this.url = url; + } + + private String getUrl() { + return url; + } + } + + private class Image { + private String url; + + private Image(String url) { + this.url = url; + } + + private String getUrl() { + return url; + } + } + + private class Author { + private String name; + private String url; + private String iconUrl; + + private Author(String name, String url, String iconUrl) { + this.name = name; + this.url = url; + this.iconUrl = iconUrl; + } + + private String getName() { + return name; + } + + private String getUrl() { + return url; + } + + private String getIconUrl() { + return iconUrl; + } + } + + private class Field { + private String name; + private String value; + private boolean inline; + + private Field(String name, String value, boolean inline) { + this.name = name; + this.value = value; + this.inline = inline; + } + + private String getName() { + return name; + } + + private String getValue() { + return value; + } + + private boolean isInline() { + return inline; + } + } + } + + private class JSONObject { + + private final HashMap map = new HashMap<>(); + + void put(String key, Object value) { + if (value != null) { + map.put(key, value); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + Set> entrySet = map.entrySet(); + builder.append("{"); + + int i = 0; + for (Map.Entry entry : entrySet) { + Object val = entry.getValue(); + builder.append(quote(entry.getKey())).append(":"); + + if (val instanceof String) { + builder.append(quote(String.valueOf(val))); + } else if (val instanceof Integer) { + builder.append(Integer.valueOf(String.valueOf(val))); + } else if (val instanceof Boolean) { + builder.append(val); + } else if (val instanceof JSONObject) { + builder.append(val.toString()); + } else if (val.getClass().isArray()) { + builder.append("["); + int len = Array.getLength(val); + for (int j = 0; j < len; j++) { + builder.append(Array.get(val, j).toString()).append(j != len - 1 ? "," : ""); + } + builder.append("]"); + } + + builder.append(++i == entrySet.size() ? "}" : ","); + } + + return builder.toString(); + } + + private String quote(String string) { + return "\"" + string + "\""; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/nexia/core/utilities/item/BlockUtil.java b/src/main/java/com/nexia/core/utilities/item/BlockUtil.java new file mode 100644 index 00000000..01cdbb23 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/item/BlockUtil.java @@ -0,0 +1,61 @@ +package com.nexia.core.utilities.item; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BedPart; + +public class BlockUtil { + + public static void placeBed(Level level, BedBlock block, BlockPos blockPos, float headRotation) { + + int intRotation = (Math.round(headRotation / 90) * 90) % 360; + if (intRotation <= -180) intRotation += 360; + if (intRotation > 180) intRotation -= 360; + Rotation rotation; + Direction direction; + + switch (intRotation) { + case 90 -> { + rotation = Rotation.CLOCKWISE_90; + direction = Direction.WEST; + } + case 180 -> { + rotation = Rotation.CLOCKWISE_180; + direction = Direction.NORTH; + } + case -90 -> { + rotation = Rotation.COUNTERCLOCKWISE_90; + direction = Direction.EAST; + } + default -> { + rotation = Rotation.NONE; + direction = Direction.SOUTH; + } + } + + BlockState blockState = block.defaultBlockState(); + blockState = blockState.setValue(BedBlock.FACING, rotation.rotate(blockState.getValue(BedBlock.FACING))); + blockState = blockState.setValue(BedBlock.PART, BedPart.HEAD); + level.setBlock(blockPos, blockState, 3); + + blockState = blockState.setValue(BedBlock.PART, BedPart.FOOT); + level.setBlock(blockPos.relative(direction), blockState, 3); + } + + public static String blockToText(BlockState blockState) { + return blockToText(blockState.getBlock()); + } + + public static String blockToText(Block block) { + String[] splitRegistryKey = Registry.BLOCK.getKey(block).toString().split(":"); + if (splitRegistryKey.length < 2) return splitRegistryKey[0]; + return splitRegistryKey[1]; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/item/InventoryUtil.java b/src/main/java/com/nexia/core/utilities/item/InventoryUtil.java new file mode 100644 index 00000000..9a8f5c3a --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/item/InventoryUtil.java @@ -0,0 +1,158 @@ +package com.nexia.core.utilities.item; + +import com.natamus.collective_fabric.functions.PlayerFunctions; +import com.natamus.collective_fabric.functions.StringFunctions; +import com.nexia.core.utilities.chat.ChatFormat; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.Util; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class InventoryUtil { + + static String dirpath = FabricLoader.getInstance().getConfigDir().toString() + "/nexia/inventories"; + + public static void sendHandItemPacket(ServerPlayer player, InteractionHand hand) { + if (hand == InteractionHand.MAIN_HAND) { + sendInvSlotPacket(player, player.inventory.selected); + } else if (hand == InteractionHand.OFF_HAND) { + sendInvSlotPacket(player, 40); + } + } + + public static void sendInvSlotPacket(ServerPlayer player, int slot) { + int packetSlot; + + if (slot < 9) { + packetSlot = 36 + slot; + } else if (slot < 36) { + packetSlot = slot; + } else if (slot < 40) { + packetSlot = 44 - slot; + } else if (slot == 40) { + packetSlot = 45; + } else { + return; + } + + player.connection.send(new ClientboundContainerSetSlotPacket(0, packetSlot, player.inventory.getItem(slot))); + } + + public static boolean writeGearStringToFile(String filename, String gearstring, @Nullable String path) { + File dir = new File(dirpath); + dir.mkdirs(); + + try { + PrintWriter writer = new PrintWriter(dirpath + "/" + filename + ".txt", StandardCharsets.UTF_8); + if(path != null){ + writer = new PrintWriter(dirpath + path + "/" + filename + ".txt", StandardCharsets.UTF_8); + } + writer.println(gearstring); + writer.close(); + } catch (Exception e) { + return false; + } + return true; + } + + public static String getGearStringFromFile(String filename, @Nullable String path) { + File dir = new File(dirpath); + if(path != null){ + dir = new File(dirpath + path); + } + File file = new File(dirpath + "/" + filename + ".txt"); + + String gearstring = ""; + if (dir.isDirectory() && file.isFile()) { + try { + gearstring = new String(Files.readAllBytes(Paths.get(dirpath + "/" + filename + ".txt", new String[0]))); + } + catch (IOException ignored) { } + } + + return gearstring; + } + + public static String getListOfInventories(@Nullable String path) { + StringBuilder inventories = new StringBuilder(); + + File folder = new File(dirpath); + if(path != null) { + folder = new File(dirpath + path); + } + if (!folder.isDirectory()) { + return inventories.toString(); + } + + File[] listOfFiles = folder.listFiles(); + for (File listOfFile : listOfFiles) { + if (listOfFile.isFile()) { + if (!inventories.toString().equals("")) { + inventories.append(", "); + } + inventories.append(listOfFile.getName().replace(".txt", "")); + } + } + + return inventories.toString(); + } + public static int saveInventory(ServerPlayer player, String name, @Nullable String path, boolean silent) { + if (name.trim() == "") { + if(!silent){ + player.sendMessage(ChatFormat.formatFail("The inventory name " + name + " is invalid."), Util.NIL_UUID); + } + return 0; + } + + String gearstring = PlayerFunctions.getPlayerGearString(player); + if (StringFunctions.sequenceCount(gearstring, "\n") < 40) { + if(!silent){ + player.sendMessage(ChatFormat.formatFail("Something went wrong while generating the save file content for your inventory."), Util.NIL_UUID); + } + return 0; + } + + if (!InventoryUtil.writeGearStringToFile(name, gearstring, path)) { + player.sendMessage(ChatFormat.formatFail("Something went wrong while saving the content of your inventory as '" + name + "'."), Util.NIL_UUID); + return 0; + } + + if(!silent){ + player.sendMessage(ChatFormat.format("{b1}Successfully saved your inventory as {b2}'{}'{b1}.", name), Util.NIL_UUID); + } + return 1; + } + + public static int setInventory(ServerPlayer player, String name, @Nullable String path, boolean silent) { + if (name.trim() == "") { + if(!silent){ + player.sendMessage(ChatFormat.formatFail("The inventory name '" + name + "' is invalid."), Util.NIL_UUID); + } + return 0; + } + + String gearstring = InventoryUtil.getGearStringFromFile(name, path); + if (gearstring == "") { + if(!silent){ + player.sendMessage(ChatFormat.formatFail("Unable to load the content of the inventory with the name '" + name + "'."), Util.NIL_UUID); + } + return 0; + } + + PlayerFunctions.setPlayerGearFromString(player, gearstring); + if(!silent){ + player.sendMessage(ChatFormat.format("{b1}Your inventory has been set to the preset {b2}{}{b1}.", name), Util.NIL_UUID); + } + return 1; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/item/ItemDisplayUtil.java b/src/main/java/com/nexia/core/utilities/item/ItemDisplayUtil.java new file mode 100644 index 00000000..6c696f27 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/item/ItemDisplayUtil.java @@ -0,0 +1,59 @@ +package com.nexia.core.utilities.item; + +import net.minecraft.nbt.*; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; + +public class ItemDisplayUtil { + + public static void addLore(ItemStack itemStack, String string, int line) { + CompoundTag compoundTag = itemStack.getOrCreateTag(); + CompoundTag display = compoundTag.getCompound("display"); + ListTag listTag = display.getList("Lore", 8); + + string = "{\"text\":\"" + string + "\"}"; + if (line < 0) { + listTag.add(listTag.size() + line + 1, StringTag.valueOf(string)); + } else { + listTag.add(line, StringTag.valueOf(string)); + } + + display.put("Lore", listTag); + compoundTag.put("display", display); + itemStack.setTag(compoundTag); + } + + public static void removeLore(ItemStack itemStack, int line) { + CompoundTag compoundTag = itemStack.getOrCreateTag(); + CompoundTag display = compoundTag.getCompound("display"); + ListTag listTag = display.getList("Lore", 8); + + if (line < 0) { + if (listTag.size() > 0) listTag.remove(listTag.size() - 1); + } else { + if (listTag.size() > line) { + listTag.remove(line); + } + } + + if (listTag.size() > 0) display.put("Lore", listTag); + else display.remove("Lore"); + + if (display.getAllKeys().size() > 0) compoundTag.put("display", display); + else compoundTag.remove("display"); + + itemStack.setTag(compoundTag); + } + + public static void addGlint(ItemStack itemStack) { + try { + if (!itemStack.isEnchanted()) { + ListTag listTag = new ListTag(); + listTag.add(new CompoundTag()); + itemStack.getOrCreateTag().put("Enchantments", listTag); + } + } catch (Exception ignored) {} + } + +} diff --git a/src/main/java/com/nexia/core/utilities/item/ItemStackUtil.java b/src/main/java/com/nexia/core/utilities/item/ItemStackUtil.java new file mode 100644 index 00000000..4f239c9e --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/item/ItemStackUtil.java @@ -0,0 +1,137 @@ +package com.nexia.core.utilities.item; + +import net.minecraft.core.NonNullList; +import net.minecraft.core.Registry; +import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.*; +import net.minecraft.world.item.enchantment.Enchantment; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.Level; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class ItemStackUtil { + + public static HashMap armorTiers = getArmorTiers(); + private static HashMap getArmorTiers() { + HashMap tiers = new HashMap<>(); + tiers.put(ArmorMaterials.LEATHER, 1); + tiers.put(ArmorMaterials.GOLD, 2); + tiers.put(ArmorMaterials.CHAIN, 3); + tiers.put(ArmorMaterials.IRON, 4); + tiers.put(ArmorMaterials.TURTLE, 4); + tiers.put(ArmorMaterials.DIAMOND, 5); + tiers.put(ArmorMaterials.NETHERITE, 6); + return tiers; + } + + public static int getArmorTier(Item item) { + if (!(item instanceof ArmorItem)) return 0; + + ArmorItem armorItem = (ArmorItem) item; + ArmorMaterial material = armorItem.getMaterial(); + if (!armorTiers.containsKey(material)) return 0; + + return armorTiers.get(material); + } + + public static boolean hasRoomFor(Inventory inventory, ItemStack itemStack) { + int count = itemStack.getCount(); + + for (ItemStack slot : inventory.items) { + if (slot.getItem() == Items.AIR) { + count -= 64; + } else if (isSameItem(slot, itemStack)) { + count -= 64 - slot.getCount(); + } + if (count <= 0) return true; + } + return false; + } + + public static boolean hasItem(Inventory inventory, Item item) { + for (int i = 0; i < 41; i++) { + if (inventory.getItem(i).getItem() == item) return true; + } + return false; + } + + public static boolean isSameItem(ItemStack itemStack1, ItemStack itemStack2) { + if (itemStack1.getItem() != itemStack2.getItem()) return false; + + if (itemStack1.getTag() == null) { + return itemStack2.getTag() == null; + } else { + return itemStack1.getTag().equals(itemStack2.getTag()); + } + } + + public static ItemStack stringToItemStack(String string) { + try { + String[] itemStack = string.split(" "); + String item = itemStack[itemStack.length-1]; + int amount = 1; + if (itemStack.length > 1) amount = Integer.parseInt(itemStack[0]); + + return new ItemStack(itemFromString(item), amount); + } catch (Exception exception) { + return null; + } + } + + public static String ItemStackToString(ItemStack itemStack, boolean reversible) { + if(reversible){ + return itemStack.getCount() + " " + itemStack.getDescriptionId().split("\\.")[2]; + } + return itemStack.getDescriptionId(); + } + + public static ArrayList getInvItems(ServerPlayer player) { + ArrayList fullInv = new ArrayList<>(); + fullInv.addAll(player.inventory.items); + fullInv.addAll(player.inventory.armor); + fullInv.addAll(player.inventory.offhand); + return fullInv; + } + + public static void sendInventoryRefreshPacket(ServerPlayer player) { + NonNullList i = NonNullList.create(); + for (int j = 0; j < player.containerMenu.slots.size(); ++j) { + ItemStack itemStack = player.containerMenu.slots.get(j).getItem(); + i.add(itemStack.isEmpty() ? ItemStack.EMPTY : itemStack); + } + player.refreshContainer(player.containerMenu, i); + } + + public static Item itemFromString(String name) { + name = name.toLowerCase(); + return Registry.ITEM.get(new ResourceLocation(name)); + } + + public static void enchant(ItemStack itemStack, Enchantment enchantment, int level) { + Map enchantments = EnchantmentHelper.getEnchantments(itemStack); + if (level > 0) enchantments.put(enchantment, level); + else enchantments.remove(enchantment); + EnchantmentHelper.setEnchantments(enchantments, itemStack); + } + + public static ItemStack getContainerClickItem(ServerPlayer player, ServerboundContainerClickPacket packet) { + int slot = packet.getSlotNum(); + + if (slot >= 0 && slot < player.containerMenu.slots.size()) { + return player.containerMenu.getSlot(slot).getItem(); + } else if (slot == -999) { + return player.inventory.getCarried(); + } else { + return null; + } + } +} diff --git a/src/main/java/com/nexia/core/utilities/misc/EventUtil.java b/src/main/java/com/nexia/core/utilities/misc/EventUtil.java new file mode 100644 index 00000000..aa16a264 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/misc/EventUtil.java @@ -0,0 +1,17 @@ +package com.nexia.core.utilities.misc; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.duels.DuelsSpawn; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +public class EventUtil { + + public static boolean dropItem(ServerPlayer player, ItemStack itemStack) { + + return !FfaUtil.isFfaPlayer(player) || !DuelsSpawn.isInHub(player) || !LobbyUtil.isLobbyWorld(player.getLevel()); + } + + +} diff --git a/src/main/java/com/nexia/core/utilities/misc/NxCmdUtil.java b/src/main/java/com/nexia/core/utilities/misc/NxCmdUtil.java new file mode 100644 index 00000000..c4167d2a --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/misc/NxCmdUtil.java @@ -0,0 +1,44 @@ +package com.nexia.core.utilities.misc; + +import com.mojang.brigadier.tree.CommandNode; +import me.lucko.fabric.api.permissions.v0.Permissions; +import net.minecraft.commands.CommandSourceStack; + +import java.util.function.Predicate; +public class NxCmdUtil { + + public static final String PREFIX = "minecraft.command."; + + public static void alterCommand(CommandNode child) { + var name = child.getName(); + var packageName = commandPackageName(child); + if (packageName == null || !packageName.startsWith("net.minecraft")) { + return; + } + try { + var field = CommandNode.class.getDeclaredField("requirement"); + field.setAccessible(true); + Predicate original = child.getRequirement(); + field.set(child, original.or((source) -> Permissions.check(source, NxCmdUtil.PREFIX + name, false))); + } catch (NoSuchFieldException | IllegalAccessException ignored) { + } + } + + private static String commandPackageName(CommandNode node) { + var command = node.getCommand(); + if (command != null) { + return command.getClass().getPackageName(); + } + var redirect = node.getRedirect(); + if (redirect != null) { + return commandPackageName(redirect); + } + for (var child : node.getChildren()) { + var childResult = commandPackageName(child); + if (childResult != null) { + return childResult; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/nexia/core/utilities/misc/NxFileUtil.java b/src/main/java/com/nexia/core/utilities/misc/NxFileUtil.java new file mode 100644 index 00000000..8576a8a5 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/misc/NxFileUtil.java @@ -0,0 +1,20 @@ +package com.nexia.core.utilities.misc; + +import com.nexia.core.Main; + +import java.io.File; + +public class NxFileUtil { + + public static String makeDir(String string) { + new File(string).mkdirs(); + return string; + } + + public static String addConfigDir(String string) { + string = Main.modConfigDir + "/" + string; + new File(string).mkdirs(); + return string; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/misc/RandomUtil.java b/src/main/java/com/nexia/core/utilities/misc/RandomUtil.java new file mode 100644 index 00000000..3792fec2 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/misc/RandomUtil.java @@ -0,0 +1,13 @@ +package com.nexia.core.utilities.misc; + +import java.util.Random; + +public class RandomUtil { + public static int randomInt(int minimum, int maximum){ + return new Random().nextInt(maximum - minimum + 1) + minimum; + } + + public static float randomFloat(float minimum, float maximum){ + return new Random().nextFloat(maximum - minimum + 1) + minimum; + } +} diff --git a/src/main/java/com/nexia/core/utilities/player/PlayerData.java b/src/main/java/com/nexia/core/utilities/player/PlayerData.java new file mode 100644 index 00000000..eaf1a6d2 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/player/PlayerData.java @@ -0,0 +1,41 @@ +package com.nexia.core.utilities.player; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.pos.EntityPos; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.Level; + +import java.time.LocalDateTime; + +public class PlayerData { + + // Stuff saved into files + public SavedPlayerData savedData; + + // Stuff not saved in files + public PlayerGameMode gameMode; + public ResourceKey spawnWorld; + public EntityPos spawnPoint; + + public ServerPlayer combatTagPlayer; + public LocalDateTime combatTagEnd; + + public ServerPlayer lastMessageSender; + + public PlayerData(SavedPlayerData savedData) { + this.savedData = savedData; + + this.combatTagPlayer = null; + this.combatTagEnd = LocalDateTime.now(); + + this.gameMode = PlayerGameMode.LOBBY; + this.spawnWorld = LobbyUtil.lobbyWorld.dimension(); + this.spawnPoint = LobbyUtil.lobbySpawn; + + this.lastMessageSender = null; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/player/PlayerDataManager.java b/src/main/java/com/nexia/core/utilities/player/PlayerDataManager.java new file mode 100644 index 00000000..f2627bff --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/player/PlayerDataManager.java @@ -0,0 +1,76 @@ +package com.nexia.core.utilities.player; + +import com.google.gson.Gson; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.world.entity.player.Player; + +import java.io.File; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.UUID; + +public class PlayerDataManager { + + static String dataDirectory = FabricLoader.getInstance().getConfigDir().toString() + "/nexia/core"; + static String playerDataDirectory = dataDirectory + "/playerdata"; + + static HashMap allPlayerData = new HashMap<>(); + + public static PlayerData get(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) { + addPlayerData(player); + } + return allPlayerData.get(player.getUUID()); + } + + public static void addPlayerData(Player player) { + PlayerData playerData = new PlayerData(loadPlayerData(player)); + allPlayerData.put(player.getUUID(), playerData); + } + + public static void removePlayerData(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) return; + savePlayerData(player); + allPlayerData.remove(player.getUUID()); + } + + private static void savePlayerData(Player player) { + try { + + PlayerData playerData = get(player); + Gson gson = new Gson(); + String json = gson.toJson(playerData.savedData); + + String directory = getDataDir(); + FileWriter fileWriter = new FileWriter(directory + "/" + player.getUUID() + ".json"); + fileWriter.write(json); + fileWriter.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static SavedPlayerData loadPlayerData(Player player) { + try { + + String directory = getDataDir(); + String json = Files.readString(Path.of(directory + "/" + player.getUUID() + ".json")); + + Gson gson = new Gson(); + return gson.fromJson(json, SavedPlayerData.class); + + } catch (Exception e) { + return new SavedPlayerData(); + } + } + + private static String getDataDir() { + new File(playerDataDirectory).mkdirs(); + return playerDataDirectory; + } + + +} diff --git a/src/main/java/com/nexia/core/utilities/player/PlayerUtil.java b/src/main/java/com/nexia/core/utilities/player/PlayerUtil.java new file mode 100644 index 00000000..96a00491 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/player/PlayerUtil.java @@ -0,0 +1,197 @@ +package com.nexia.core.utilities.player; + +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.time.ServerTime; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundSetTitlesPacket; +import net.minecraft.network.protocol.game.ClientboundSoundPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class PlayerUtil { + + public static void broadcast(List players, String string) { + broadcast(players, new TextComponent(string)); + } + + public static void sendActionbar(ServerPlayer player, String string){ + player.connection.send(new ClientboundSetTitlesPacket(ClientboundSetTitlesPacket.Type.ACTIONBAR, + ChatFormat.format(string))); + } + + public static void broadcast(List players, Component component) { + for (ServerPlayer player : players) { + player.sendMessage(component, Util.NIL_UUID); + } + } + + public static void sendTitle(ServerPlayer player, String title, String sub, int in, int stay, int out) { + player.connection.send(new ClientboundSetTitlesPacket(in, stay, out)); + player.connection.send(new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.TITLE, new TextComponent(title))); + player.connection.send(new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.SUBTITLE, new TextComponent(sub))); + } + + public static void broadcastTitle(List players, String title, String subtitle, int in, int stay, int out) { + ClientboundSetTitlesPacket titlePacket = new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.TITLE, new TextComponent(title)); + ClientboundSetTitlesPacket subtitlePacket = new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.SUBTITLE, new TextComponent(subtitle)); + + for (ServerPlayer player : players) { + player.connection.send(new ClientboundSetTitlesPacket(in, stay, out)); + player.connection.send(titlePacket); + player.connection.send(subtitlePacket); + } + } + + public static void broadcastTitle(List players, String title, String subtitle) { + ClientboundSetTitlesPacket titlePacket = new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.TITLE, new TextComponent(title)); + ClientboundSetTitlesPacket subtitlePacket = new ClientboundSetTitlesPacket( + ClientboundSetTitlesPacket.Type.SUBTITLE, new TextComponent(subtitle)); + + for (ServerPlayer player : players) { + sendDefaultTitleLength(player); + player.connection.send(titlePacket); + player.connection.send(subtitlePacket); + } + } + + private static void sendDefaultTitleLength(ServerPlayer player) { + player.connection.send(new ClientboundSetTitlesPacket(10, 60, 20)); + } + + public static void broadcastSound(List players, SoundEvent soundEvent, SoundSource soundSource, float volume, float pitch) { + for (ServerPlayer player : players) { + sendSound(player, soundEvent, soundSource, volume, pitch); + } + } + + public static void sendSound(ServerPlayer player, SoundEvent soundEvent, SoundSource soundSource, float volume, float pitch) { + sendSound(player, new EntityPos(player), soundEvent, soundSource, volume, pitch); + } + + public static void sendSound(ServerPlayer player, EntityPos position, SoundEvent soundEvent, SoundSource soundSource, float volume, float pitch) { + player.connection.send(new ClientboundSoundPacket(soundEvent, soundSource, + position.x, position.y, position.z, 16f * volume, pitch)); + } + + public static ServerPlayer getFixedPlayer(ServerPlayer serverPlayer) { + if (serverPlayer == null) return null; + return ServerTime.minecraftServer.getPlayerList().getPlayer(serverPlayer.getUUID()); + } + + public static void resetHealthStatus(ServerPlayer player) { + player.setInvulnerable(false); + player.removeAllEffects(); + player.setHealth(player.getMaxHealth()); + player.getFoodData().setFoodLevel(20); + } + + public static Object[] getTags(@NotNull ServerPlayer player){ + return player.getTags().toArray(); + } + + public static boolean hasTag(@NotNull ServerPlayer player, @NotNull String tag){ + Object[] result = getTags(player); + if(result.length > 0) { + for (Object s : result) { + if (String.valueOf(s).equalsIgnoreCase(tag)) { + return true; + } + } + return String.valueOf(result[0]).equalsIgnoreCase(tag); + } else { + return false; + } + } + + public static boolean doesPlayerExist(String player){ + return (getPlayerFromName(player) != null); + } + + public static ServerPlayer getPlayerFromName(String player){ + return Main.server.getPlayerList().getPlayerByName(player); + } + + public static String returnSetPlayer(ServerPlayer player, String command){ + return command.replaceAll("%player%", String.valueOf(player.getScoreboardName())); + } + + public static void executeServerCommand(String command, ServerPlayer player){ + if(player != null){ + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), returnSetPlayer(player, command)); + } else { + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), command); + } + } + + public static void executePlayerCommand(ServerPlayer player, String command) { + if(player != null){ + Main.server.getCommands().performCommand(player.createCommandSourceStack(), returnSetPlayer(player, command)); + } + } + + public static boolean hasPermission(@NotNull CommandSourceStack permission, @Nullable String command, @Nullable int level) { + return me.lucko.fabric.api.permissions.v0.Permissions.check(permission, command, level); + } + + public static ServerPlayer getPlayerAttacker(Entity attackerEntity) { + if (attackerEntity == null) return null; + + if (attackerEntity instanceof ServerPlayer) { + return (ServerPlayer) attackerEntity; + + } else if (attackerEntity instanceof Projectile) { + Entity projectileOwner = ((Projectile) attackerEntity).getOwner(); + if (!(projectileOwner instanceof ServerPlayer)) return null; + return (ServerPlayer) projectileOwner; + } + return null; + } + + public static boolean couldCrit(ServerPlayer player) { + return !player.isOnGround() && player.fallDistance > 0.0f && !player.hasEffect(MobEffects.BLINDNESS) + && !player.onClimbable() && !player.isInWater() && !player.isPassenger(); + } + + public static void removeItem(ServerPlayer player, Item item, int count) { + for (ItemStack itemStack : ItemStackUtil.getInvItems(player)) { + if (itemStack.getItem() != item) continue; + + if (itemStack.getCount() >= count) { + itemStack.shrink(count); + break; + } else { + count -= itemStack.getCount(); + itemStack.shrink(itemStack.getCount()); + } + } + } + + public static boolean containsUuid(List playerList, ServerPlayer player) { + for (ServerPlayer listPlayer : playerList) { + if (listPlayer.getUUID().equals(player.getUUID())) return true; + } + return false; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/player/SavedPlayerData.java b/src/main/java/com/nexia/core/utilities/player/SavedPlayerData.java new file mode 100644 index 00000000..ede5b886 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/player/SavedPlayerData.java @@ -0,0 +1,13 @@ +package com.nexia.core.utilities.player; + +public class SavedPlayerData { + + public long muteEnd; + public String muteReason; + + public SavedPlayerData() { + this.muteEnd = System.currentTimeMillis(); + this.muteReason = null; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/pos/BlockVec3.java b/src/main/java/com/nexia/core/utilities/pos/BlockVec3.java new file mode 100644 index 00000000..a62faa07 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/pos/BlockVec3.java @@ -0,0 +1,91 @@ +package com.nexia.core.utilities.pos; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; + +public class BlockVec3 { + + public int x; + public int y; + public int z; + + public BlockVec3(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public BlockVec3(double x, double y, double z) { + this.x = (int)Math.floor(x); + this.y = (int)Math.floor(y); + this.z = (int)Math.floor(z); + } + + public BlockVec3(Vec3 vec3) { + this.x = (int)Math.floor(vec3.x); + this.y = (int)Math.floor(vec3.y); + this.z = (int)Math.floor(vec3.z); + } + + public BlockVec3(BlockPos blockPos) { + this.x = blockPos.getX(); + this.y = blockPos.getY(); + this.z = blockPos.getZ(); + } + + public BlockVec3(EntityPos entityPos) { + this.x = (int)Math.floor(entityPos.x); + this.y = (int)Math.floor(entityPos.y); + this.z = (int)Math.floor(entityPos.z); + } + + + public BlockVec3(Entity entity) { + this.x = (int)Math.floor(entity.getX()); + this.y = (int)Math.floor(entity.getY()); + this.z = (int)Math.floor(entity.getZ()); + } + + public BlockVec3 clone() { + return new BlockVec3(this.x, this.y, this.z); + } + + public BlockVec3 c() { + return this.clone(); + } + + public BlockVec3 add(int x, int y, int z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public BlockVec3 add(double x, double y, double z) { + return this.add((int)x, (int)y, (int)z ); + } + + public BlockVec3 add(BlockVec3 blockVec3) { + this.x += blockVec3.x; + this.y += blockVec3.y; + this.z += blockVec3.z; + return this; + } + + public BlockVec3 subtract(BlockVec3 blockVec3) { + this.x -= blockVec3.x; + this.y -= blockVec3.y; + this.z -= blockVec3.z; + return this; + } + + public BlockPos toBlockPos() { + return new BlockPos(this.x, this.y, this.z); + } + + public String toString() { + return "[x: " + this.x + ", y: " + this.y + ", z: " + this.z + "]"; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/pos/EntityPos.java b/src/main/java/com/nexia/core/utilities/pos/EntityPos.java new file mode 100644 index 00000000..fc3f8b68 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/pos/EntityPos.java @@ -0,0 +1,98 @@ +package com.nexia.core.utilities.pos; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; + +public class EntityPos { + + public double x; + public double y; + public double z; + public float yaw; + public float pitch; + + public EntityPos(double x, double y, double z, float yaw, float pitch) { + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + } + + public EntityPos(double x, double y, double z) { + this(x, y, z, 0, 0); + } + + public EntityPos(Entity entity) { + this(entity.getX(), entity.getY(), entity.getZ(), entity.yRot, entity.xRot); + } + + public EntityPos(BlockPos blockPos) { + this(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } + + public EntityPos(Vec3 vec3) { + this(vec3.x, vec3.y, vec3.z); + } + + public EntityPos clone() { + return new EntityPos(this.x, this.y, this.z, this.yaw, this.pitch); + } + + public EntityPos c() { + return this.clone(); + } + + public EntityPos add(double x, double y, double z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public EntityPos add(EntityPos entityPos) { + this.x += entityPos.x; + this.y += entityPos.y; + this.z += entityPos.z; + return this; + } + + public EntityPos between(EntityPos pos2) { + return new EntityPos((this.x + pos2.x) / 2, (this.y + pos2.y) / 2, (this.z + pos2.z) / 2); + } + + public BlockPos toBlockPos() { + return new BlockPos(this.x, this.y, this.z); + } + + public BlockVec3 toBlockVec3() { + return new BlockVec3(this.x, this.y, this.z); + } + + public double distance(EntityPos entityPos2) { + double x = this.x - entityPos2.x; + double y = this.y - entityPos2.y; + double z = this.z - entityPos2.z; + return Math.sqrt(x*x + y*y + z*z); + } + + public void teleportPlayer(ServerLevel level, ServerPlayer player) { + player.teleportTo(level, this.x, this.y, this.z, this.yaw, this.pitch); + } + + public String toString() { + return "[x: " + this.x + ", y: " + this.y + ", z: " + this.z + ", yaw: " + this.yaw + ", pitch: " + this.pitch + "]"; + } + + public boolean isInRadius(EntityPos entityPos2, float radius) { + double x = this.x - entityPos2.x; + double y = this.y - entityPos2.y; + double z = this.z - entityPos2.z; + return x*x + y*y + z*z <= radius*radius; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/pos/PositionUtil.java b/src/main/java/com/nexia/core/utilities/pos/PositionUtil.java new file mode 100644 index 00000000..b0168c91 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/pos/PositionUtil.java @@ -0,0 +1,15 @@ +package com.nexia.core.utilities.pos; + +import net.minecraft.core.BlockPos; + +public class PositionUtil { + + public static boolean isBetween(BlockPos corner1, BlockPos corner2, BlockPos between) { + return corner1.getX() <= between.getX() && corner2.getX() >= between.getX() && + corner1.getY() <= between.getY() && corner2.getY() >= between.getY() && + corner1.getZ() <= between.getZ() && corner2.getZ() >= between.getZ(); + } + + + +} diff --git a/src/main/java/com/nexia/core/utilities/pos/ProtectionBlock.java b/src/main/java/com/nexia/core/utilities/pos/ProtectionBlock.java new file mode 100644 index 00000000..26370c04 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/pos/ProtectionBlock.java @@ -0,0 +1,15 @@ +package com.nexia.core.utilities.pos; + +import net.minecraft.world.level.block.Block; + +public class ProtectionBlock { + public Block block; + public boolean canBuild; + public String noBuildMessage; + + public ProtectionBlock(Block block, boolean canBuild, String noBuildMessage) { + this.block = block; + this.canBuild = canBuild; + this.noBuildMessage = noBuildMessage; + } +} diff --git a/src/main/java/com/nexia/core/utilities/pos/ProtectionMap.java b/src/main/java/com/nexia/core/utilities/pos/ProtectionMap.java new file mode 100644 index 00000000..949e8f39 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/pos/ProtectionMap.java @@ -0,0 +1,134 @@ +package com.nexia.core.utilities.pos; + +import com.google.gson.Gson; +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.Block; + +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ProtectionMap { + + public byte[][][] map; + public ProtectionBlock[] blocksByIds; + public ProtectionBlock notListedBlock; + public byte notListedBlockId; + public String outsideMessage; + + public ProtectionMap(ServerPlayer player, BlockPos corner1, BlockPos corner2, String filePath, ProtectionBlock[] listedBlocks, ProtectionBlock notListedBlock, String outSideMessage) { + this.blocksByIds = listedBlocks; + this.notListedBlock = notListedBlock; + this.notListedBlockId = (byte)blocksByIds.length; + this.outsideMessage = outSideMessage; + this.map = new byte + [corner2.getX() - corner1.getX() + 1] + [corner2.getY() - corner1.getY() + 1] + [corner2.getZ() - corner1.getZ() + 1]; + this.createMap(player, corner1); + this.exportMap(player, filePath); + } + + private ProtectionMap(byte[][][] map, ProtectionBlock[] listedBlocks, ProtectionBlock notListedBlock, String outsideMessage) { + this.map = map; + this.blocksByIds = listedBlocks; + this.notListedBlock = notListedBlock; + this.notListedBlockId = (byte)listedBlocks.length; + this.outsideMessage = outsideMessage; + } + + private void createMap(ServerPlayer player, BlockPos corner1) { + ServerLevel world = player.getLevel(); + int blockCount = 0; + + for (int x = 0; x < this.map.length; x++) { + for (int y = 0; y < this.map[0].length; y++) { + for (int z = 0; z < this.map[0][0].length; z++) { + BlockPos blockPos = corner1.offset(x, y, z); + byte i = this.getMapBlockId(world.getBlockState(blockPos).getBlock()); + this.map[x][y][z] = i; + if (!getMappingBlock(i).canBuild) blockCount++; + } + } + } + + player.sendMessage(new TextComponent("\2477Map created successfully with " + + ChatFormat.brandColor2 + blockCount + " \2477protected blocks"), Util.NIL_UUID); + } + + private void exportMap(ServerPlayer player, String filePath) { + try { + Gson gson = new Gson(); + String json = gson.toJson(this.map); + + FileWriter fileWriter = new FileWriter(filePath); + fileWriter.write(json); + fileWriter.close(); + + player.sendMessage(new TextComponent("\2477Successfully exported protection map."), Util.NIL_UUID); + + } catch (Exception e) { + e.printStackTrace(); + player.sendMessage(new TextComponent("\2477Failed to export protection map."), Util.NIL_UUID); + } + } + + public static ProtectionMap importMap(String filePath, ProtectionBlock[] listedBlocks, ProtectionBlock notListedBlock, + String outsideMessage) { + byte[][][] map; + try { + String possibleJson = Files.readString(Path.of(filePath)); + Gson gson = new Gson(); + map = gson.fromJson(possibleJson, byte[][][].class); + } catch (Exception e) { + System.out.println(Main.MOD_NAME + ": Failed to import protection map from " + filePath); + return null; + } + return new ProtectionMap(map, listedBlocks, notListedBlock, outsideMessage); + } + + private byte getMapBlockId(Block block) { + for (byte i = 0; i < this.blocksByIds.length; i++) { + if (block == this.blocksByIds[i].block) return i; + } + return this.notListedBlockId; + } + + public ProtectionBlock getMappingBlock(byte id) { + if (this.blocksByIds.length > id) return blocksByIds[id]; + return notListedBlock; + } + + public boolean canBuiltAt(BlockPos mapCorner1, BlockPos buildPos) { + return this.canBuiltAt(mapCorner1, buildPos, null, false); + } + + public boolean canBuiltAt(BlockPos mapCorner1, BlockPos buildPos, ServerPlayer player, boolean sendMessage) { + sendMessage = sendMessage && player != null; + + BlockPos mapPos = buildPos.subtract(mapCorner1); + + if (mapPos.getX() < 0 || mapPos.getX() >= map.length || + mapPos.getY() < 0 || mapPos.getY() >= map[0].length || + mapPos.getZ() < 0 || mapPos.getZ() >= map[0][0].length) { + if (sendMessage) player.sendMessage(ChatFormat.formatFail(outsideMessage), Util.NIL_UUID); + return false; + } + + byte id = this.map[mapPos.getX()][mapPos.getY()][mapPos.getZ()]; + ProtectionBlock protectionBlock = this.getMappingBlock(id); + + if (!protectionBlock.canBuild) { + if (sendMessage) player.sendMessage(ChatFormat.formatFail(protectionBlock.noBuildMessage), Util.NIL_UUID); + return false; + } + return true; + } + +} diff --git a/src/main/java/com/nexia/core/utilities/time/ServerTime.java b/src/main/java/com/nexia/core/utilities/time/ServerTime.java new file mode 100644 index 00000000..6d895db8 --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/time/ServerTime.java @@ -0,0 +1,77 @@ +package com.nexia.core.utilities.time; + +import com.nexia.core.Main; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.ffa.utilities.FfaAreas; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.duels.DuelsGame; +import com.nexia.minigames.games.duels.DuelsSpawn; +import com.nexia.minigames.games.oitc.OitcGame; +import com.nexia.minigames.games.oitc.OitcSpawn; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import xyz.nucleoid.fantasy.Fantasy; + +public class ServerTime { + + static int totalTickCount = -1; + static int totalSecondCount = -1; + + public static MinecraftServer minecraftServer = null; + + public static Fantasy fantasy = null; + + public static void firstTick(MinecraftServer server) { + minecraftServer = server; + Main.server = server; + + fantasy = Fantasy.get(minecraftServer); + LobbyUtil.setLobbyWorld(minecraftServer); + DuelsSpawn.setDuelWorld(minecraftServer); + FfaAreas.setFfaWorld(minecraftServer); + OitcGame.firstTick(minecraftServer); + + BwGame.firstTick(); + DuelsGame.starting(); + } + + public static void stopServer() { + try { + for(ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()){ + player.connection.disconnect(ChatFormat.formatFail("The server is restarting!")); + } + DuelsGame.starting(); + BwAreas.clearQueueBuild(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void everyTick() { + totalTickCount++; + + BwGame.tick(); + + if (totalTickCount % 5 == 0) { + FfaUtil.fiveTick(); + } + + // Most second methods are also handled here to avoid too many methods from being executed at the same time + switch (totalTickCount % 20) { + case 0 -> everySecond(); + case 2 -> FfaUtil.ffaSecond(); + case 4 -> {} + case 6 -> BwGame.bedWarsSecond(); + } + + } + + static void everySecond() { + totalSecondCount++; + OitcGame.second(); + } + +} diff --git a/src/main/java/com/nexia/core/utilities/time/TickUtil.java b/src/main/java/com/nexia/core/utilities/time/TickUtil.java new file mode 100644 index 00000000..2f86f39b --- /dev/null +++ b/src/main/java/com/nexia/core/utilities/time/TickUtil.java @@ -0,0 +1,27 @@ +package com.nexia.core.utilities.time; + +public class TickUtil { + + private static final int[] unitsInUnits = {20, 60, 60, 24}; + + public static String[] minuteTimeStamp(int ticks) { + int[] numbers = {0, 0, ticks}; + int lastIndex = numbers.length - 1; + + for (int i = 0; i < lastIndex; i++) { + numbers[lastIndex - i - 1] = numbers[lastIndex - i] / unitsInUnits[i]; + numbers[lastIndex - i] %= unitsInUnits[i]; + } + + String[] text = new String[numbers.length]; + + for (int i = 0; i < numbers.length; i++) { + text[i] = "" + numbers[i]; + if (text[i].length() == 1) text[i] = "0" + text[i]; + } + + return text; + } + + +} diff --git a/src/main/java/com/nexia/ffa/Main.java b/src/main/java/com/nexia/ffa/Main.java new file mode 100644 index 00000000..e14086c5 --- /dev/null +++ b/src/main/java/com/nexia/ffa/Main.java @@ -0,0 +1,17 @@ +package com.nexia.ffa; + +import com.nexia.ffa.config.ModConfig; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.sargunvohra.mcmods.autoconfig1u.serializer.GsonConfigSerializer; +import net.fabricmc.api.ModInitializer; + +public class Main implements ModInitializer { + + public static ModConfig config; + + @Override + public void onInitialize() { + AutoConfig.register(ModConfig.class, GsonConfigSerializer::new); + config = AutoConfig.getConfigHolder(ModConfig.class).getConfig(); + } +} diff --git a/src/main/java/com/nexia/ffa/config/ModConfig.java b/src/main/java/com/nexia/ffa/config/ModConfig.java new file mode 100644 index 00000000..42fe5b9d --- /dev/null +++ b/src/main/java/com/nexia/ffa/config/ModConfig.java @@ -0,0 +1,15 @@ +package com.nexia.ffa.config; + +import me.sargunvohra.mcmods.autoconfig1u.ConfigData; +import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; +import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.Comment; + +@Config(name = "nexia-ffa") +public class ModConfig implements ConfigData { + @Comment("The coordinates of the spawn") + public double[] spawnCoordinates = {0.5, 80.0, 0.5}; + + @Comment("The name of the ffa world.") + public String worldName = "ffa:map"; + +} diff --git a/src/main/java/com/nexia/ffa/mixin/ServerPlayerMixin.java b/src/main/java/com/nexia/ffa/mixin/ServerPlayerMixin.java new file mode 100644 index 00000000..b9727dd6 --- /dev/null +++ b/src/main/java/com/nexia/ffa/mixin/ServerPlayerMixin.java @@ -0,0 +1,145 @@ +package com.nexia.ffa.mixin; + +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.ffa.utilities.FfaUtil; +import com.nexia.ffa.utilities.player.PlayerDataManager; +import com.nexia.ffa.utilities.player.SavedPlayerData; +import net.minecraft.Util; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.text.DecimalFormat; + +@Mixin(ServerPlayer.class) +public class ServerPlayerMixin { + public float calculateHealth(float health){ + float fixedHealth = Float.parseFloat(new DecimalFormat("#.#").format(health / 2)); + + if(fixedHealth < 0.5){ + return 0.5f; + } + if(fixedHealth >= 10){ + return 10f; + } + + if(fixedHealth % 1 == 0){ return fixedHealth; } + + if(Float.parseFloat(new DecimalFormat("0.#").format(fixedHealth)) >= 0.5){ + return Float.parseFloat(new DecimalFormat("#.5").format(fixedHealth)); + } + return Float.parseFloat(new DecimalFormat("#.0").format(fixedHealth)); + } + + public void calculateDeath(ServerPlayer player){ + SavedPlayerData data = PlayerDataManager.get(player).savedData; + data.deaths++; + if(data.killstreak > data.bestKillstreak){ + data.bestKillstreak = data.killstreak; + } + + if(data.killstreak >= 5) { + for (int i = 0; i < Main.server.getPlayerCount(); i++) { + if (FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))) { + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(ChatFormat.format("§8[§c☠§8] §c{n}{} {s}has lost their killstreak of §c{b}{}{s}.", player.getScoreboardName(), data.killstreak), Util.NIL_UUID); + } + } + } + data.killstreak = 0; + } + + public void calculateKill(ServerPlayer player){ + SavedPlayerData data = PlayerDataManager.get(player).savedData; + data.killstreak++; + if(data.killstreak > data.bestKillstreak){ + data.bestKillstreak = data.killstreak; + } + data.kills++; + player.heal(player.getMaxHealth()); + + if(data.killstreak % 5 == 0){ + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(ChatFormat.format("§8[§c☠§8] §a{n}{} {s}now has a killstreak of §c{b}{}{s}!", player.getScoreboardName(), data.killstreak), Util.NIL_UUID); + } + } + } + } + + + @Inject(method = "die", at = @At("HEAD")) + private void onPlayerDeath(DamageSource source, CallbackInfo info) { + ServerPlayer victim = (ServerPlayer) (Object) this; + boolean attackerNull = !(source.getEntity() instanceof ServerPlayer); + boolean victimTag = FfaUtil.isFfaPlayer(victim); + + if((attackerNull && victimTag) || (!attackerNull && source.getEntity() == victim && victimTag)){ + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(new TextComponent("§7Wow, §c☠ " + victim.getScoreboardName() + " §7somehow killed themselves."), Util.NIL_UUID); + } + } + return; + } + if(victimTag){ + calculateDeath(victim); + } + if(attackerNull) { return; } + ServerPlayer attacker = (ServerPlayer) source.getEntity(); + boolean attackerTag = FfaUtil.isFfaPlayer(attacker); + + if(attackerTag && victimTag){ + Item handItem = attacker.getMainHandItem().getItem(); + if(handItem == Items.NETHERITE_SWORD){ + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(new TextComponent("§c☠ " + victim.getScoreboardName() + " §7was killed by §a\uD83D\uDDE1 " + source.getEntity().getScoreboardName() + " §7with §c" + calculateHealth(attacker.getHealth()) + "❤ §7left."), Util.NIL_UUID); + } + } + } else if(handItem == Items.TRIDENT){ + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(new TextComponent("§c☠ " + victim.getScoreboardName() + " §7was killed by §a\uD83D\uDD31 " + source.getEntity().getScoreboardName() + " §7with §c" + calculateHealth(attacker.getHealth()) + "❤ §7left."), Util.NIL_UUID); + } + } + } else if(handItem == Items.NETHERITE_AXE){ + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(new TextComponent("§c☠ " + victim.getScoreboardName() + " §7was killed by §a\uD83E\uDE93 " + source.getEntity().getScoreboardName() + " §7with §c" + calculateHealth(attacker.getHealth()) + "❤ §7left."), Util.NIL_UUID); + } + } + } else { + for(int i = 0; i < Main.server.getPlayerCount(); i++){ + if(FfaUtil.isFfaPlayer(PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]))){ + PlayerUtil.getPlayerFromName(Main.server.getPlayerNames()[i]).sendMessage(new TextComponent("§c☠ " + victim.getScoreboardName() + " §7was killed by §a◆ " + source.getEntity().getScoreboardName() + " §7with §c" + calculateHealth(attacker.getHealth()) + "❤ §7left."), Util.NIL_UUID); + } + } + } + } + + if(attackerTag){ + calculateKill(attacker); + } + + /* + SavedPlayerData victimData = PlayerDataManager.get(victim).savedData; + SavedPlayerData attackerData = PlayerDataManager.get(attacker).savedData; + + victimData.lastHitEntity = null; + attackerData.lastHitEntity = null; + + */ + + + //info.cancel(); + } +} diff --git a/src/main/java/com/nexia/ffa/utilities/FfaAreas.java b/src/main/java/com/nexia/ffa/utilities/FfaAreas.java new file mode 100644 index 00000000..7fa0b690 --- /dev/null +++ b/src/main/java/com/nexia/ffa/utilities/FfaAreas.java @@ -0,0 +1,43 @@ +package com.nexia.ffa.utilities; + +import com.nexia.core.utilities.pos.BlockVec3; +import com.nexia.ffa.Main; +import com.nexia.core.utilities.pos.EntityPos; +import net.minecraft.core.BlockPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; + +public class FfaAreas { + public static ServerLevel ffaWorld = null; + public static EntityPos spawn = new EntityPos(Main.config.spawnCoordinates[0], Main.config.spawnCoordinates[1], Main.config.spawnCoordinates[2], 0, 0); + public static BlockPos spawnCorner1 = spawn.toBlockPos().offset(-9, -12, -9); + public static BlockPos spawnCorner2 = spawn.toBlockPos().offset(9, 12, 9); + + public static BlockPos ffaCorner1 = spawn.toBlockPos().offset(-120, -spawn.y, -120); + public static BlockPos ffaCorner2 = spawn.toBlockPos().offset(250, -spawn.y + 255, 80); + + public FfaAreas() { + } + + public static boolean isFfaWorld(Level level) { + return level.dimension().toString().contains(Main.config.worldName); + } + + public static boolean isInFfaSpawn(Player player) { + BlockVec3 pos = new BlockVec3(player.position()); + return pos.x >= spawnCorner1.getX() && pos.x <= spawnCorner2.getX() && + pos.y >= spawnCorner1.getY() && pos.y <= spawnCorner2.getY() && + pos.z >= spawnCorner1.getZ() && pos.z <= spawnCorner2.getZ(); + } + + public static void setFfaWorld(MinecraftServer server) { + for (ServerLevel level : server.getAllLevels()) { + if (isFfaWorld(level)) { + ffaWorld = level; + break; + } + } + } +} diff --git a/src/main/java/com/nexia/ffa/utilities/FfaUtil.java b/src/main/java/com/nexia/ffa/utilities/FfaUtil.java new file mode 100644 index 00000000..d2847450 --- /dev/null +++ b/src/main/java/com/nexia/ffa/utilities/FfaUtil.java @@ -0,0 +1,189 @@ +package com.nexia.ffa.utilities; + +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.ffa.utilities.player.PlayerData; +import com.nexia.ffa.utilities.player.PlayerDataManager; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.phys.AABB; + +import java.util.*; +import java.util.function.Predicate; + +import static com.nexia.ffa.utilities.FfaAreas.*; + +public class FfaUtil { + public static ArrayList wasInSpawn = new ArrayList(); + public static HashMap invItems; + + public static boolean isFfaPlayer(Player player) { + return PlayerUtil.hasTag((ServerPlayer) player, "ffa") || com.nexia.core.utilities.player.PlayerDataManager.get((ServerPlayer) player).gameMode == PlayerGameMode.FFA; + } + + public static void ffaSecond() { + if (ffaWorld == null) return; + for (ServerPlayer player : ffaWorld.players()) { + if (!isFfaPlayer(player)) continue; + + if (FfaAreas.isInFfaSpawn(player)) { + player.addTag("no_damage"); + } else { + player.removeTag("no_damage"); + } + } + } + + public static void fiveTick() { + for (ServerPlayer player : ffaWorld.players()) { + if(wasInSpawn.contains(player.getUUID()) && !FfaAreas.isInFfaSpawn(player)){ + wasInSpawn.remove(player.getUUID()); + saveInventory(player); + PlayerUtil.sendActionbar(player, "§8Your inventory layout was saved."); + } + } + } + public static void saveInventory(Player player){ + PlayerData playerData = PlayerDataManager.get(player); + Inventory newInv = playerData.FfaInventory = new Inventory(player); + + for(int i = 0; i < newInv.getContainerSize(); ++i) { + newInv.setItem(i, player.inventory.getItem(i).copy()); + } + } + + + public static void leaveOrDie(ServerPlayer player, DamageSource damageSource) { + player.inventory.setCarried(ItemStack.EMPTY); + clearThrownTridents(player); + + if (damageSource != null) { + ServerPlayer attacker = PlayerUtil.getPlayerAttacker(damageSource.getEntity()); + if (attacker != null) { + clearThrownTridents(attacker); + setInventory(attacker); + } + } + + FfaUtil.setInventory(player); + FfaUtil.wasInSpawn.add(player.getUUID()); + } + + private static boolean addFromOldInv(ServerPlayer player, ItemStack itemStack) { + PlayerData playerData = PlayerDataManager.get(player); + Inventory invLayout = playerData.FfaInventory; + if (invLayout == null) return false; + + for (int i = 0; i < 41; i++) { + Item item = invLayout.getItem(i).getItem(); + if (itemStack.getItem() == item && player.inventory.getItem(i).isEmpty()) { + player.inventory.setItem(i, itemStack); + return true; + } + } + return false; + } + + public static void setInventory(ServerPlayer player){ + HashMap availableItems = (HashMap)invItems.clone(); + Inventory newInv = new Inventory(player); + Inventory oldInv = PlayerDataManager.get(player).FfaInventory; + int i; + + if (oldInv != null) { + for(i = 0; i < 41; ++i) { + Item item = oldInv.getItem(i).getItem(); + + Iterator> it = availableItems.entrySet().iterator(); + + while(it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + if (((ItemStack)entry.getValue()).getItem() == item) { + ItemStack itemStack = ((ItemStack)entry.getValue()).copy(); + newInv.setItem(i, itemStack); + it.remove(); + break; + } + } + } + } + + for (Map.Entry integerItemStackEntry : availableItems.entrySet()) { + Map.Entry entry = (Map.Entry) integerItemStackEntry; + ItemStack itemStack = ((ItemStack) entry.getValue()).copy(); + if (newInv.getItem(entry.getKey()).isEmpty()) { + newInv.setItem(entry.getKey(), itemStack); + } else { + newInv.add(itemStack); + } + } + + for(i = 0; i < 41; ++i) { + ItemStack itemStack = newInv.getItem(i); + if (itemStack == null) { + itemStack = ItemStack.EMPTY; + } + + player.inventory.setItem(i, itemStack); + } + + ItemStackUtil.sendInventoryRefreshPacket(player); + } + + public static void clearThrownTridents(ServerPlayer player) { + BlockPos c1 = ffaCorner1.offset(-10, -ffaCorner1.getY(), -10); + BlockPos c2 = ffaCorner2.offset(10, 319 - ffaCorner2.getY(), 10); + AABB aabb = new AABB(c1, c2); + Predicate predicate = o -> true; + for (ThrownTrident trident : ffaWorld.getEntities(EntityType.TRIDENT, aabb, predicate)) { + if (trident.getOwner() != null && trident.getOwner().getUUID().equals(player.getUUID())) { + trident.remove(); + } + } + } + static { + invItems = new HashMap<>(); + ItemStack sword = new ItemStack(Items.NETHERITE_SWORD); + sword.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(0, sword); + + ItemStack trident = new ItemStack(Items.TRIDENT); + trident.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(1, trident); + + ItemStack axe = new ItemStack(Items.NETHERITE_AXE); + axe.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(2, axe); + + ItemStack helmet = new ItemStack(Items.DIAMOND_HELMET); + helmet.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 2); + helmet.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(39, helmet); + + ItemStack chestplate = new ItemStack(Items.DIAMOND_CHESTPLATE); + chestplate.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 2); + chestplate.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(38, chestplate); + + ItemStack leggings = new ItemStack(Items.DIAMOND_LEGGINGS); + leggings.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 2); + leggings.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(37, leggings); + + ItemStack boots = new ItemStack(Items.DIAMOND_BOOTS); + boots.enchant(Enchantments.ALL_DAMAGE_PROTECTION, 2); + boots.getOrCreateTag().putBoolean("Unbreakable", true); + invItems.put(36, boots); + } +} diff --git a/src/main/java/com/nexia/ffa/utilities/player/PlayerData.java b/src/main/java/com/nexia/ffa/utilities/player/PlayerData.java new file mode 100644 index 00000000..e6c3f05a --- /dev/null +++ b/src/main/java/com/nexia/ffa/utilities/player/PlayerData.java @@ -0,0 +1,16 @@ +package com.nexia.ffa.utilities.player; + +import net.minecraft.world.entity.player.Inventory; + +public class PlayerData { + public SavedPlayerData savedData; + + public Inventory FfaInventory; + + + public PlayerData(SavedPlayerData savedData) { + this.savedData = savedData; + + this.FfaInventory = null; + } +} diff --git a/src/main/java/com/nexia/ffa/utilities/player/PlayerDataManager.java b/src/main/java/com/nexia/ffa/utilities/player/PlayerDataManager.java new file mode 100644 index 00000000..50b098ae --- /dev/null +++ b/src/main/java/com/nexia/ffa/utilities/player/PlayerDataManager.java @@ -0,0 +1,80 @@ + + +package com.nexia.ffa.utilities.player; + +import com.google.gson.Gson; +import java.io.File; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.UUID; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.world.entity.player.Player; + +public class PlayerDataManager { + static String dataDirectory = FabricLoader.getInstance().getConfigDir().toString() + "/nexia/ffa"; + static String playerDataDirectory; + static HashMap allPlayerData; + + + + public PlayerDataManager() { + } + + public static PlayerData get(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) { + addPlayerData(player); + } + + return (PlayerData)allPlayerData.get(player.getUUID()); + } + + public static void addPlayerData(Player player) { + PlayerData playerData = new PlayerData(loadPlayerData(player)); + allPlayerData.put(player.getUUID(), playerData); + } + + public static void removePlayerData(Player player) { + if (allPlayerData.containsKey(player.getUUID())) { + savePlayerData(player); + allPlayerData.remove(player.getUUID()); + } + } + + private static void savePlayerData(Player player) { + try { + PlayerData playerData = get(player); + Gson gson = new Gson(); + String json = gson.toJson(playerData.savedData); + String directory = getDataDir(); + FileWriter fileWriter = new FileWriter(directory + "/" + player.getUUID() + ".json"); + fileWriter.write(json); + fileWriter.close(); + } catch (Exception var6) { + var6.printStackTrace(); + } + + } + + private static SavedPlayerData loadPlayerData(Player player) { + try { + String directory = getDataDir(); + String json = Files.readString(Path.of(directory + "/" + player.getUUID() + ".json")); + Gson gson = new Gson(); + return (SavedPlayerData)gson.fromJson(json, SavedPlayerData.class); + } catch (Exception var4) { + return new SavedPlayerData(); + } + } + + private static String getDataDir() { + (new File(playerDataDirectory)).mkdirs(); + return playerDataDirectory; + } + + static { + playerDataDirectory = dataDirectory + "/playerdata"; + allPlayerData = new HashMap(); + } +} diff --git a/src/main/java/com/nexia/ffa/utilities/player/SavedPlayerData.java b/src/main/java/com/nexia/ffa/utilities/player/SavedPlayerData.java new file mode 100644 index 00000000..c1455dbf --- /dev/null +++ b/src/main/java/com/nexia/ffa/utilities/player/SavedPlayerData.java @@ -0,0 +1,16 @@ +package com.nexia.ffa.utilities.player; + +public class SavedPlayerData { + + public int kills; + public int killstreak; + public int bestKillstreak; + public int deaths; + public SavedPlayerData() { + this.kills = 0; + this.killstreak = 0; + this.bestKillstreak = 0; + + this.deaths = 0; + } +} diff --git a/src/main/java/com/nexia/minigames/Main.java b/src/main/java/com/nexia/minigames/Main.java new file mode 100644 index 00000000..81701dac --- /dev/null +++ b/src/main/java/com/nexia/minigames/Main.java @@ -0,0 +1,17 @@ +package com.nexia.minigames; + +import com.nexia.minigames.config.ModConfig; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.sargunvohra.mcmods.autoconfig1u.serializer.GsonConfigSerializer; +import net.fabricmc.api.ModInitializer; + +public class Main implements ModInitializer { + + public static ModConfig config; + + @Override + public void onInitialize() { + AutoConfig.register(ModConfig.class, GsonConfigSerializer::new); + config = AutoConfig.getConfigHolder(ModConfig.class).getConfig(); + } +} diff --git a/src/main/java/com/nexia/minigames/config/ModConfig.java b/src/main/java/com/nexia/minigames/config/ModConfig.java new file mode 100644 index 00000000..8602dacc --- /dev/null +++ b/src/main/java/com/nexia/minigames/config/ModConfig.java @@ -0,0 +1,13 @@ +package com.nexia.minigames.config; + +import me.sargunvohra.mcmods.autoconfig1u.ConfigData; +import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; + +import java.util.ArrayList; + +@Config(name = "nexia-minigames") +public class ModConfig implements ConfigData { + public ArrayList duelsMaps = new ArrayList<>(); + + public ArrayList oitcMaps = new ArrayList<>(); +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/BwGame.java b/src/main/java/com/nexia/minigames/games/bedwars/BwGame.java new file mode 100644 index 00000000..063d1787 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/BwGame.java @@ -0,0 +1,330 @@ +package com.nexia.minigames.games.bedwars; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.misc.NxFileUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.BlockVec3; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.custom.BwTrident; +import com.nexia.minigames.games.bedwars.players.BwPlayers; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import com.nexia.minigames.games.bedwars.upgrades.BwApplyTraps; +import com.nexia.minigames.games.bedwars.upgrades.BwApplyUpgrades; +import com.nexia.minigames.games.bedwars.upgrades.BwTrap; +import com.nexia.minigames.games.bedwars.util.BwGen; +import com.nexia.minigames.games.bedwars.util.BwPlayerTracker; +import com.nexia.minigames.games.bedwars.util.BwScoreboard; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.Util; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.*; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.scores.PlayerTeam; + +import java.util.*; +import java.util.function.Predicate; + +public class BwGame { + + public static final String bedWarsDirectory = NxFileUtil.addConfigDir("bedwars"); + + public static boolean isGameActive = false; + public static int gameTicks = 0; + public static final int gameLength = 30 * 60 + 1; + private static final List gameEndWarningTimes = Arrays.asList(60, 30, 15, 10, 5, 4, 3, 2, 1); + + public static final int playersInTeam = 1; + public static int maxPlayerCount = 0; + + public static ArrayList queueList = new ArrayList<>(); + public static final int requiredPlayers = playersInTeam * 2; + + public static final int queueWaitTime = 15; + private static final List announcedQueueSeconds = Arrays.asList(15, 10, 5, 4, 3, 2, 1); + public static int queueCountdown = queueWaitTime; + public static boolean isQueueCountdownActive = false; + + public static boolean winScreen = false; + protected static final int winScreenTime = 7; + protected static int winScreenTimer = winScreenTime; + + public static ArrayList spectatorList = new ArrayList<>(); + public static PlayerTeam spectatorTeam; + + public static HashMap respawningList = new HashMap<>(); + public static final int respawnTime = 5; + + public static HashMap invulnerabilityList = new HashMap<>(); + public static final int invulnerabilityTime = 1; + public static HashMap invisiblePlayerArmor = new HashMap<>(); + public static HashMap> gameTridents = new HashMap<>(); + + public static ArrayList winners = new ArrayList<>(); + public static Integer winnerColor = null; + + // ----- TICK METHODS -------------------------------------------------------------------------------- + + public static void firstTick() { + BwAreas.setBedWarsWorld(ServerTime.minecraftServer); + BwAreas.spawnQueueBuild(); + BwTeam.resetTeams(); + BwUtil.createSpectatorTeam(); + } + + public static void tick() { + gameTicks++; + BwGen.genTick(); + BwUtil.utilTick(); + BwAreas.tick(); + invulnerabilityTick(); + respawnTick(); + + if (gameTicks == 1) tickAfterStart(); + } + + private static void respawnTick() { + for (Iterator it = respawningList.keySet().iterator(); it.hasNext(); ) { + ServerPlayer player = it.next(); + int timeLeft = respawningList.get(player); + + if (timeLeft % 20 == 0) { + PlayerUtil.sendTitle(player, "\247cYou died!", + ChatFormat.brandColor1 + "Respawning in " + + ChatFormat.brandColor2 + timeLeft / 20 + ChatFormat.brandColor1 + "...", + 0, 20, 10); + } + respawningList.replace(player, timeLeft - 1); + if (respawningList.get(player) <= 0) { + it.remove(); + if (BwUtil.isBedWarsPlayer(player)) BwPlayers.sendToSpawn(PlayerUtil.getFixedPlayer(player)); + } + } + } + + private static void invulnerabilityTick() { + for (Iterator it = invulnerabilityList.keySet().iterator(); it.hasNext(); ) { + ServerPlayer player = it.next(); + invulnerabilityList.replace(player, invulnerabilityList.get(player) - 1); + if (invulnerabilityList.get(player) <= 0) { + it.remove(); + if (BwUtil.isBedWarsPlayer(player)) player.setInvulnerable(false); + } + } + } + + // ----- SECOND METHODS -------------------------------------------------------------------------------- + + public static void bedWarsSecond() { + if (isQueueCountdownActive) queueSecond(); + if (winScreen) winScreenSecond(); + + if (isGameActive && !winScreen) { + BwPlayerTracker.trackerSecond(); + BwApplyUpgrades.upgradeSecond(); + BwApplyTraps.trapSecond(); + timerSecond(); + } + } + + private static void queueSecond() { + if (queueCountdown <= 0) { + isQueueCountdownActive = false; + queueCountdown = queueWaitTime; + startBedWars(); + } else if (announcedQueueSeconds.contains(queueCountdown)) { + PlayerUtil.broadcast(queueList, ChatFormat.chatColor2 + "The game will start in " + + ChatFormat.brandColor2 + queueCountdown + ChatFormat.chatColor2 + " seconds."); + } + if (queueCountdown > 0 && queueCountdown <= 3) { + PlayerUtil.broadcastSound(queueList, SoundEvents.NOTE_BLOCK_PLING, SoundSource.MASTER, 0.05f, 1f); + } + queueCountdown--; + + } + + private static void winScreenSecond() { + + if (winScreenTimer <= winScreenTime && winScreenTimer >= 3) { + BwTeam.winnerRockets(winners, winnerColor); + } + + if (winScreenTimer <= 0) { + winScreen = false; + winScreenTimer = winScreenTime; + resetBedWars(); + } + + winScreenTimer--; + } + + private static void timerSecond() { + BwScoreboard.updateTimer(); + + int secondsLeft = gameLength - gameTicks / 20; + + if (secondsLeft <= gameEndWarningTimes.get(0)) { + for (Integer warningTime : gameEndWarningTimes) { + if (secondsLeft == warningTime) { + PlayerUtil.broadcast(BwPlayers.getViewers(), + "\2477The game will end in \247c" + secondsLeft + " \2477seconds."); + break; + } + } + } + + if (secondsLeft <= 0) { + endBedwars(); + } + } + + // ----- START & END -------------------------------------------------------------------------------- + + public static void startQueueCountdown() { + queueCountdown = queueWaitTime; + isQueueCountdownActive = true; + } + + public static void endQueueCountdown() { + queueCountdown = queueWaitTime; + isQueueCountdownActive = false; + } + + public static void startBedWars() { + gameTicks = 0; + + BwAreas.clearBedWarsMap(); + BwAreas.clearQueueBuild(); + clearGameEntities(); + + BwTeam.spreadIntoTeams(queueList); + BwTeam.setSpawns(); + BwTeam.createBeds(); + BwGen.createGens(); + BwTrap.resetTrapLocations(); + BwScoreboard.setUpScoreboard(); + preparePlayers(); + + isGameActive = true; + } + + private static void preparePlayers() { + for (BwTeam team : BwTeam.allTeams.values()) { + if (team.players == null) continue; + + for (ServerPlayer player : team.players) { + player.getEnderChestInventory().clearContent(); + player.inventory.clearContent(); + BwPlayers.sendToSpawn(player); + player.sendMessage(new TextComponent(ChatFormat.chatColor2 + "The game has started. " + + ChatFormat.brandColor2 + "Good luck" + ChatFormat.chatColor2 + "!"), Util.NIL_UUID); + PlayerUtil.sendTitle(player, + "You're " + team.textColor + team.displayName + "\247f!", "", 0, 60, 20); + player.removeTag(LobbyUtil.NO_DAMAGE_TAG); + } + } + } + + private static void tickAfterStart() { + PlayerUtil.broadcastSound(BwPlayers.getPlayers(), + SoundEvents.NOTE_BLOCK_PLING, SoundSource.MASTER, 0.05f, 2f); + } + + public static void endBedwars() { + winScreen = true; + + ArrayList aliveTeams = BwTeam.getAliveTeams(); + if (aliveTeams.size() == 1) { + winners = aliveTeams.get(0).players; + winnerColor = aliveTeams.get(0).armorColor; + } else { + winners = new ArrayList<>(); + winnerColor = null; + } + + sendGameEndMessage(); + + invisiblePlayerArmor.clear(); + spectatorList.addAll(BwPlayers.getPlayers()); + respawningList.clear(); + clearGameEntities(); + BwTeam.resetTeams(); + BwGen.resetGens(); + } + + private static void sendGameEndMessage() { + ArrayList aliveTeams = BwTeam.getAliveTeams(); + + String victoryTitle = "\2476Victory!"; + String gameEndTitle = "\2476Game Over!"; + int[] time = {5, 80, 20}; + + if (aliveTeams.size() == 1) { + BwTeam winnerTeam = aliveTeams.get(0); + String whoWon; + if (winnerTeam.players.size() == 1) { + whoWon = winnerTeam.textColor + winnerTeam.players.get(0).getScoreboardName(); + } else { + whoWon = winnerTeam.textColor + winnerTeam.displayName + " team"; + } + + String subtitle = whoWon + "\2477 has won the game!"; + PlayerUtil.broadcastTitle(winnerTeam.players, victoryTitle, subtitle, time[0], time[1], time[2]); + PlayerUtil.broadcastTitle(spectatorList, gameEndTitle, subtitle, time[0], time[1], time[2]); + PlayerUtil.broadcast(BwPlayers.getViewers(), subtitle); + + } else { + String subtitle = "\2477The game ended in a draw!"; + PlayerUtil.broadcastTitle(BwPlayers.getViewers(), gameEndTitle, subtitle, time[0], time[1], time[2]); + PlayerUtil.broadcast(BwPlayers.getViewers(), subtitle); + } + } + + private static void clearGameEntities() { + BlockVec3 corner1 = new BlockVec3(BwAreas.bedWarsCorner1); + corner1.y = 0; + BlockVec3 corner2 = new BlockVec3(BwAreas.bedWarsCorner2); + corner2.y = 300; + AABB aabb = new AABB(corner1.toBlockPos(), corner2.toBlockPos()); + Predicate predicate = o -> true; + for (Entity entity : BwAreas.bedWarsWorld.getEntities(EntityType.ITEM, aabb, predicate)) { + entity.remove(); + } + entityLoop: for (Entity entity : BwAreas.bedWarsWorld.getEntities(EntityType.ARMOR_STAND, aabb, predicate)) { + for (String tag : entity.getTags()) { + if (tag.equals(BwGen.genTimerDisplayTag) || tag.equals(BwGen.genItemDisplayTag)) { + entity.remove(); + continue entityLoop; + } + } + } + + for (ServerPlayer player : gameTridents.keySet()) { + for (BwTrident trident : gameTridents.get(player)) { + trident.remove(); + } + } + gameTridents.clear(); + } + + private static void resetBedWars() { + isGameActive = false; + + BwAreas.spawnQueueBuild(); + + for (ServerPlayer player : spectatorList) { + player = PlayerUtil.getFixedPlayer(player); + if (player == null) continue; + LobbyUtil.returnToLobby(player, true); + BwScoreboard.removeScoreboardFor(player); + } + spectatorList.clear(); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/areas/BwAreas.java b/src/main/java/com/nexia/minigames/games/bedwars/areas/BwAreas.java new file mode 100644 index 00000000..61bc768d --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/areas/BwAreas.java @@ -0,0 +1,168 @@ +package com.nexia.minigames.games.bedwars.areas; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.pos.BlockVec3; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.pos.ProtectionBlock; +import com.nexia.core.utilities.pos.ProtectionMap; +import com.nexia.minigames.games.bedwars.BwGame; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public class BwAreas { + + public static ServerLevel bedWarsWorld = null; + public static EntityPos bedWarsCenter = new EntityPos(0.5, 50, 0.5, 0, 0); + + private static final int bedWarsRadius = 120; + public static BlockPos bedWarsCorner1 = bedWarsCenter.toBlockPos().offset(-bedWarsRadius, -bedWarsCenter.y, -bedWarsRadius); + public static BlockPos bedWarsCorner2 = bedWarsCenter.toBlockPos().offset(bedWarsRadius, -bedWarsCenter.y + 255, bedWarsRadius); + public static int buildLimit = 80; + + public static EntityPos spectatorSpawn = bedWarsCenter.c().add(0, 20, 0); + + public static EntityPos queueSpawn = bedWarsCenter.c().add(0, -bedWarsCenter.y + 100, 0); + public static BlockVec3 queueC1 = queueSpawn.toBlockVec3().add(-8, -1, -8); + public static BlockVec3 queueC2 = queueSpawn.toBlockVec3().add(8, 7, 8); + + // Protection map related + + private static final String protMapFileName = "protectionMap.json"; + private static final String protMapFilePath = BwGame.bedWarsDirectory + "/" + protMapFileName; + + public static ProtectionBlock[] protMapBlocks = { + new ProtectionBlock(Blocks.VOID_AIR, true, null), + new ProtectionBlock(Blocks.AIR, true, null), + new ProtectionBlock(Blocks.BROWN_STAINED_GLASS, false, "You can't build here."), + new ProtectionBlock(Blocks.BROWN_STAINED_GLASS_PANE, false, "You can't build this close to generators."), + }; + public static ProtectionBlock notListedMapBlock = + new ProtectionBlock(null, false, "You can only break blocks placed by players."); + public static String outsideMessage = "You have reached the build limit"; + + public static ProtectionMap protectionMap = ProtectionMap.importMap( + protMapFilePath, protMapBlocks, notListedMapBlock, outsideMessage); + + public static void createProtectionMap(ServerPlayer player) { + protectionMap = new ProtectionMap(player, + bedWarsCorner1, bedWarsCorner2, protMapFilePath, protMapBlocks, notListedMapBlock, outsideMessage); + } + + // ------------------------------------------------------------ + + public static boolean isBedWarsWorld(Level level) { + return level.dimension().equals(BwDimension.LEVEL_KEY); + } + + public static void setBedWarsWorld(MinecraftServer minecraftServer) { + for (ServerLevel level : minecraftServer.getAllLevels()) { + if (isBedWarsWorld(level)) { + bedWarsWorld = level; + return; + } + } + } + + private static boolean isInsideBorder(BlockPos mapPos, byte[][][] map) { + return mapPos.getX() >= 0 && mapPos.getX() < map.length && + mapPos.getY() >= 0 && mapPos.getY() < map[0].length && + mapPos.getZ() >= 0 && mapPos.getZ() < map[0][0].length; + } + + public static boolean isImmuneBlock(BlockPos blockPos) { + return !canBuildAt(null, blockPos, false); + } + + public static boolean canBuildAt(@Nullable ServerPlayer player, BlockPos blockPos, boolean sendMessage) { + ProtectionMap protectionMap = BwAreas.protectionMap; + BlockPos mapPos = blockPos.subtract(bedWarsCorner1); + sendMessage = sendMessage && player != null; + + if (protectionMap == null) { + if (sendMessage) { + player.sendMessage(ChatFormat.formatFail("An error occurred, please inform the admins."), Util.NIL_UUID); + } + return false; + } + + if ((player != null && !isBedWarsWorld(player.getLevel())) || !isInsideBorder(mapPos, protectionMap.map)) { + if (sendMessage) { + player.sendMessage(ChatFormat.formatFail("You have reached the build limit."), Util.NIL_UUID); + } + return false; + } + + return protectionMap.canBuiltAt(bedWarsCorner1, blockPos, player, sendMessage); + } + + public static void tick() { + if (resetMap) { + if (mapResetTicks >= bedWarsCorner1.getY() && mapResetTicks <= buildLimit) { + clearOneLayer(mapResetTicks); + } + if (mapResetTicks > bedWarsCorner2.getY() && resetMap) { + resetMap = false; + } + mapResetTicks++; + } + + } + + // Map related --------------------------------------------------------------------------------- + + private static boolean resetMap = false; + private static int mapResetTicks = 0; + + public static void clearBedWarsMap() { + resetMap = true; + mapResetTicks = bedWarsCorner1.getY(); + } + + private static void clearOneLayer(int yLevel) { + for (BlockPos blockPos : BlockPos.betweenClosed( + bedWarsCorner1.getX(), yLevel, bedWarsCorner1.getZ(), + bedWarsCorner2.getX(), yLevel, bedWarsCorner2.getZ())) { + + BlockState blockState = bedWarsWorld.getBlockState(blockPos); + if (!(isImmuneBlock(blockPos) || blockState.getBlock() instanceof BedBlock)) { + bedWarsWorld.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3); + } + } + } + + public static void spawnQueueBuild() { + + for (BlockPos pos : BlockPos.betweenClosed( + queueC1.x, queueC1.y, queueC1.z, + queueC2.x, queueC2.y, queueC2.z)) { + + if (bedWarsWorld.getBlockState(pos).getBlock() == Blocks.AIR && ( + queueC1.x == pos.getX() || queueC2.x == pos.getX() || + queueC1.y == pos.getY() || queueC2.y == pos.getY() || + queueC1.z == pos.getZ() || queueC2.z == pos.getZ() )) { + + bedWarsWorld.setBlock(pos, Blocks.GLASS.defaultBlockState(), 3); + } + } + } + + public static void clearQueueBuild() { + for (BlockPos pos : BlockPos.betweenClosed( + queueC1.x, queueC1.y, queueC1.z, + queueC2.x, queueC2.y, queueC2.z)) { + + if (bedWarsWorld.getBlockState(pos).getBlock() == Blocks.GLASS) { + bedWarsWorld.setBlock(pos, Blocks.AIR.defaultBlockState(), 3); + } + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/areas/BwDimension.java b/src/main/java/com/nexia/minigames/games/bedwars/areas/BwDimension.java new file mode 100644 index 00000000..400ca684 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/areas/BwDimension.java @@ -0,0 +1,26 @@ +package com.nexia.minigames.games.bedwars.areas; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; + +public class BwDimension { + + public static final String DIMENSION_ID = "minigames"; + public static final String DIMENSION_NAME = "bedwars"; + + public static final ResourceKey LEVEL_KEY = ResourceKey.create( + Registry.DIMENSION_REGISTRY, new ResourceLocation(DIMENSION_ID, DIMENSION_NAME)); + + private static final ResourceKey DIMENSION_TYPE_KEY = ResourceKey.create( + Registry.DIMENSION_TYPE_REGISTRY, new ResourceLocation(DIMENSION_ID, "bedwars_type")); + + private static final ResourceKey BEDWARS_WORLD_KEY = ResourceKey.create( + Registry.LEVEL_STEM_REGISTRY, LEVEL_KEY.location()); + + public static void register() {} + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/custom/BwBridgeEgg.java b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwBridgeEgg.java new file mode 100644 index 00000000..bf65084b --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwBridgeEgg.java @@ -0,0 +1,68 @@ +package com.nexia.minigames.games.bedwars.custom; + +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.entity.projectile.ThrownEgg; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; + +public class BwBridgeEgg extends ThrownEgg { + + public static final int throwTime = 2 * 20; + public static final String itemTagKey = "BedWarsBridgeEgg"; + + public EntityPos[] previousPositions; + public int oldestPosIndex; + + public ServerPlayer owner; + public Block trailBlock; + public int age; + + public BwBridgeEgg(Level level, ServerPlayer player, Block trailBlock) { + super(level, player); + + this.owner = player; + this.trailBlock = trailBlock; + this.age = 0; + + previousPositions = new EntityPos[3]; + oldestPosIndex = 0; + } + + public void tick() { + if (age >= throwTime) this.kill(); + super.tick(); + + EntityPos oldestPos = previousPositions[oldestPosIndex]; + if (oldestPos != null) { + for (BlockPos blockPos : getSurroundingBlocks(oldestPos)) { + if (level.getBlockState(blockPos).getBlock() == Blocks.AIR) { + if (!BwAreas.canBuildAt(owner, blockPos, false)) continue; + level.setBlock(blockPos, trailBlock.defaultBlockState(), 3); + } + } + playSound(SoundEvents.CHICKEN_EGG, 1f, 1f); + } + + if (age > 0) { + previousPositions[oldestPosIndex] = new EntityPos(this.position()); + oldestPosIndex++; + if (oldestPosIndex >= previousPositions.length) oldestPosIndex = 0; + } + age++; + } + + private BlockPos[] getSurroundingBlocks(EntityPos oldest) { + return new BlockPos[]{ + oldest.c().add(0.5, 0, 0).toBlockPos(), + oldest.c().add(-0.5, 0, 0).toBlockPos(), + oldest.c().add(0, 0, 0.5).toBlockPos(), + oldest.c().add(0, 0, -0.5).toBlockPos() + }; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/custom/BwExplosiveSlime.java b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwExplosiveSlime.java new file mode 100644 index 00000000..5559a43f --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwExplosiveSlime.java @@ -0,0 +1,75 @@ +package com.nexia.minigames.games.bedwars.custom; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.monster.Slime; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.phys.Vec3; + +public class BwExplosiveSlime extends Slime { + + public int age; + public LivingEntity owner; + + public static int explodeTime = 47; + + public static boolean isBwExplosiveSlime(ItemStack itemStack) { + return itemStack.hasTag() && itemStack.getTag().getBoolean("BedWarsExplosiveSlime"); + } + + public BwExplosiveSlime(EntityType entityType, ServerPlayer player) { + super(entityType, player.getLevel()); + this.age = 0; + this.owner = player; + + this.setSize(1, true); + + float x = -Mth.sin((float) Math.toRadians(player.yRot)) * Mth.cos((float) Math.toRadians(player.xRot)); + float z = Mth.cos((float) Math.toRadians(player.yRot)) * Mth.cos((float) Math.toRadians(player.xRot)); + float y = -Mth.sin((float) Math.toRadians(player.xRot)); + + float distance = 0.5f; + Vec3 playerPos = player.getEyePosition(1).add(distance * x, distance * y - 0.25, distance * z); + this.moveTo(playerPos); + + float throwPower = 2.4f; + this.setDeltaMovement(player.getDeltaMovement().add(throwPower * x, throwPower * y + 0.1, throwPower * z)); + + this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_RESISTANCE, 100000, 4, false, false, false)); + + player.getLevel().addFreshEntity(this); + } + + public void tick() { + this.age++; + + if (age == 1) { + this.hasImpulse = true; + } + + if (this.age >= explodeTime) { + this.explode(); + } + + super.tick(); + } + + public void explode() { + this.remove(); + float power = 3f; + this.level.explode(this, this.getX(), this.getY(), this.getZ(), power, Explosion.BlockInteraction.BREAK); + } + + public void knockback(float y, double x, double z) { + float multiplier = 1.6f; + super.knockback(multiplier * y, multiplier * x, multiplier * z); + } + + protected void registerGoals() {} + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/custom/BwTrident.java b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwTrident.java new file mode 100644 index 00000000..4efdd3e9 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/custom/BwTrident.java @@ -0,0 +1,58 @@ +package com.nexia.minigames.games.bedwars.custom; + +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import java.util.ArrayList; + +public class BwTrident extends ThrownTrident { + + public static final int returnTime = 9 * 20; + public static final String itemTagKey = "BedWarsTrident"; + + public ItemStack itemStack; + public ServerPlayer owner; + public boolean willBeReturned; + public int age; + + public BwTrident(Level level, ServerPlayer player, ItemStack itemStack) { + super(level, player, itemStack); + this.itemStack = itemStack.copy(); + this.owner = player; + this.willBeReturned = !player.abilities.instabuild; + this.age = 0; + + if (BwUtil.isBedWarsPlayer(this.owner)) { + if (!BwGame.gameTridents.containsKey(this.owner)) { + BwGame.gameTridents.put(this.owner, new ArrayList<>()); + } + BwGame.gameTridents.get(this.owner).add(this); + } + } + + public void tick() { + super.tick(); + + if (age >= returnTime) { + this.remove(); + if (willBeReturned) { + owner.inventory.add(this.itemStack); + PlayerUtil.sendSound(owner, SoundEvents.TRIDENT_RETURN, SoundSource.NEUTRAL, 2f, 1f); + PlayerUtil.sendSound(owner, SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.05f, 2f); + } + } + age++; + } + + public void outOfWorld() { + this.noPhysics = true; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayerEvents.java b/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayerEvents.java new file mode 100644 index 00000000..56b975bb --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayerEvents.java @@ -0,0 +1,304 @@ +package com.nexia.minigames.games.bedwars.players; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.BlockUtil; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.custom.BwBridgeEgg; +import com.nexia.minigames.games.bedwars.custom.BwExplosiveSlime; +import com.nexia.minigames.games.bedwars.custom.BwTrident; +import com.nexia.minigames.games.bedwars.shop.BwShop; +import com.nexia.minigames.games.bedwars.shop.BwShopUpgradeables; +import com.nexia.minigames.games.bedwars.upgrades.BwUpgradeShop; +import com.nexia.minigames.games.bedwars.util.BwScoreboard; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ServerboundContainerClickPacket; +import net.minecraft.network.protocol.game.ServerboundInteractPacket; +import net.minecraft.network.protocol.game.ServerboundTeleportToEntityPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ThrownEgg; +import net.minecraft.world.entity.projectile.ThrownTrident; +import net.minecraft.world.food.FoodData; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.item.*; +import net.minecraft.world.item.alchemy.PotionUtils; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public class BwPlayerEvents { + + public static void tryToJoin(ServerPlayer player, boolean throughEvent) { + + if (BwUtil.isInBedWars(player)) { + player.sendMessage(ChatFormat.formatFail("You are already in the game."), Util.NIL_UUID); + return; + } + if (BwGame.queueList.size() >= BwGame.maxPlayerCount) { + player.sendMessage(ChatFormat.formatFail("The game is full."), Util.NIL_UUID); + return; + } + if (BwGame.isGameActive) { + BwPlayers.becomeSpectator(player); + return; + } + + BwPlayers.joinQueue(player); + } + + public static boolean spectatorTeleport(ServerPlayer player, ServerboundTeleportToEntityPacket packet) { + if (BwUtil.isBedWarsPlayer(player)) { + player.sendMessage(ChatFormat.formatFail("You can't spectate others while in the game."), Util.NIL_UUID); + return false; + } + + for (ServerLevel serverLevel : ServerTime.minecraftServer.getAllLevels()) { + Entity entity = packet.getEntity(serverLevel); + if (!(entity instanceof ServerPlayer)) continue; + ServerPlayer target = (ServerPlayer) entity; + + if (!BwUtil.isBedWarsPlayer(target)) { + player.sendMessage(ChatFormat.formatFail("You can't spectate players in other games."), Util.NIL_UUID); + return false; + } + } + return true; + } + + public static void afterHurt(ServerPlayer player, DamageSource damageSource) { + ServerPlayer attacker = PlayerUtil.getPlayerAttacker(damageSource.getEntity()); + if (attacker != null) { + PlayerDataManager.get(player).combatTagPlayer = attacker; + PlayerDataManager.get(attacker).combatTagPlayer = player; + if (player.hasEffect(MobEffects.INVISIBILITY)) { + player.removeEffect(MobEffects.INVISIBILITY); + } + } + } + + public static void death(ServerPlayer player) { + if (!BwPlayers.getPlayers().contains(player) || BwGame.winScreen) return; + + BwUtil.giveKillResources(player); + BwUtil.deathClearInventory(player); + BwShopUpgradeables.downgradePlayerTools(player); + + if (BwGame.gameTridents.containsKey(player)) { + for (BwTrident trident : BwGame.gameTridents.get(player)) { + trident.remove(); + } + BwGame.gameTridents.remove(player); + } + + BwUtil.announceDeath(player); + + BwTeam team = BwTeam.getPlayerTeam(player); + if (team != null && BwTeam.bedExists(team)) { + player.setGameMode(GameType.SPECTATOR); + BwGame.respawningList.put(player, BwGame.respawnTime * 20); + player.setRespawnPosition(BwAreas.bedWarsWorld.dimension(), + BwAreas.spectatorSpawn.toBlockPos(), BwAreas.spectatorSpawn.yaw, true, false); + } else { + BwPlayers.eliminatePlayer(player, true); + } + } + + public static void respawned(ServerPlayer player) { + boolean fixedTeamPlayer = BwTeam.fixTeamPlayer(player); + + if (!fixedTeamPlayer) { + // Fix spectator + for (int i = 0; i < BwGame.spectatorList.size(); i++) { + ServerPlayer spectator = BwGame.spectatorList.get(i); + if (spectator.getUUID().equals(player.getUUID())) { + BwGame.spectatorList.set(i, player); + return; + } + } + } + } + + // Usage of /leave command or disconnecting + public static void leaveInBedWars(ServerPlayer player) { + if (BwGame.queueList.contains(player)) { + BwPlayers.leaveQueue(player); + } else if (BwPlayers.getPlayers().contains(player)) { + BwPlayers.eliminatePlayer(player, false); + } else if (BwGame.spectatorList.contains(player)) { + BwGame.spectatorList.remove(player); + BwScoreboard.removeScoreboardFor(player); + } + } + + // Ability to shoot fireballs + public static boolean useItem(ServerPlayer player, InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); + + if (itemStack.getItem() == Items.FIRE_CHARGE) { + BwUtil.throwFireball(player, itemStack); + return false; + + } else if (BwExplosiveSlime.isBwExplosiveSlime(itemStack)) { + BwUtil.throwSlime(player, itemStack); + return false; + } + + return true; + } + + public static boolean beforeStripWood(ServerPlayer player, UseOnContext context) { + if (player.isCreative()) return true; + + BlockPos blockPos = context.getClickedPos(); + + if (!BwAreas.canBuildAt(player, blockPos, false)) { + return false; + } + + return true; + } + + public static boolean beforePlace(ServerPlayer player, BlockPlaceContext blockPlaceContext) { + if (player.isCreative()) return true; + + BlockPos blockPos = blockPlaceContext.getClickedPos(); + + if (!BwAreas.canBuildAt(player, blockPos, true)) { + return false; + } + if (BwUtil.placeTnt(player, blockPlaceContext)) { + return false; + } + + return true; + } + + public static boolean beforeBreakBlock(ServerPlayer player, BlockPos blockPos) { + if (player.isCreative()) return true; + + BwTeam team = BwTeam.getPlayerTeam(player); + BlockState blockState = BwAreas.bedWarsWorld.getBlockState(blockPos); + + if (team != null && BlockUtil.blockToText(blockState).equals(team.color + "_bed")) { + return false; + } + + if (!BwAreas.canBuildAt(player, blockPos, true)) { + return false; + } + + return true; + } + + public static void bedBroken(ServerPlayer player, BlockPos blockPos) { + for (BwTeam team : BwTeam.allTeams.values()) { + if (team.bedLocation == null) continue; + + if (new EntityPos(team.bedLocation).isInRadius(new EntityPos(blockPos), 1)) { + team.announceBedBreak(player, blockPos); + break; + } + } + } + + public static void drankPotion(ServerPlayer player, ItemStack potionItem) { + for (MobEffectInstance effect : PotionUtils.getMobEffects(potionItem)) { + if (effect.getEffect() == MobEffects.INVISIBILITY) { + + if (!BwGame.invisiblePlayerArmor.containsKey(player)) { + BwGame.invisiblePlayerArmor.put(player, player.inventory.armor.toArray(new ItemStack[0])); + } + break; + } + } + } + + public static ThrownEgg throwEgg(ServerPlayer player, ItemStack itemStack) { + CompoundTag compoundTag = itemStack.getTag(); + if (compoundTag == null || !compoundTag.getBoolean(BwBridgeEgg.itemTagKey)) { + return new ThrownEgg(player.level, player); + } + + Block trail = Blocks.AIR; + BwTeam team = BwTeam.getPlayerTeam(player); + if (team != null) { + trail = Registry.BLOCK.get(new ResourceLocation(team.color + "_wool")); + } + if (trail == Blocks.AIR) trail = Blocks.WHITE_WOOL; + + return new BwBridgeEgg(player.level, player, trail); + } + + public static ThrownTrident throwTrident(ServerPlayer player, ItemStack itemStack) { + CompoundTag compoundTag = itemStack.getTag(); + if (compoundTag == null || !compoundTag.getBoolean(BwTrident.itemTagKey)) { + return new ThrownTrident(player.level, player, itemStack); + } + + return new BwTrident(player.level, player, itemStack); + } + + public static boolean interact(Player player, ServerboundInteractPacket serverboundInteractPacket) { + if (serverboundInteractPacket.getAction() != ServerboundInteractPacket.Action.INTERACT) return true; + if (BwShop.bedWarsShopItems == null) return true; + if (!(player instanceof ServerPlayer)) return true; + + Entity entity = serverboundInteractPacket.getTarget(player.level); + if (!(entity instanceof Villager)) return true; + + if (entity.getTags().contains("bedWarsShop_item")) { + BwShop.openShopGui((ServerPlayer) player); + return false; + } + if (entity.getTags().contains("bedWarsShop_upgrade")) { + BwUpgradeShop.openShopGui((ServerPlayer) player); + return false; + } + + return true; + } + + public static boolean containerClick(ServerPlayer player, ServerboundContainerClickPacket packet) { + int containerId = packet.getContainerId(); + int slot = packet.getSlotNum(); + + if (containerId == 0 && slot >= 5 && slot <= 8) { + return false; + } + + ItemStack itemStack = ItemStackUtil.getContainerClickItem(player, packet); + + if ((itemStack != null) && (packet.getClickType() == ClickType.THROW || slot == -999)) { + if (!BwUtil.canDropItem(itemStack)) return false; + } + + return true; + } + + public static void afterHungerTick(FoodData foodData) { + foodData.setFoodLevel(20); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayers.java b/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayers.java new file mode 100644 index 00000000..a76effc1 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/players/BwPlayers.java @@ -0,0 +1,175 @@ +package com.nexia.minigames.games.bedwars.players; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.player.PlayerData; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.util.BwScoreboard; +import net.minecraft.server.ServerScoreboard; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.DyeableLeatherItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Blocks; + +import java.util.ArrayList; + +public class BwPlayers { + + public static final String BED_WARS_IN_GAME_TAG = "in_bedwars"; + + private static void setInBedWars(ServerPlayer player) { + PlayerData playerData = PlayerDataManager.get(player); + + if (!LobbyUtil.isLobbyWorld(player.level) || playerData.gameMode != PlayerGameMode.LOBBY) { + LobbyUtil.leaveAllGames(player, false); + } + + playerData.gameMode = PlayerGameMode.BEDWARS; + player.addTag(BwPlayers.BED_WARS_IN_GAME_TAG); + } + + public static void joinQueue(ServerPlayer player) { + setInBedWars(player); + + BwAreas.queueSpawn.teleportPlayer(BwAreas.bedWarsWorld, player); + player.setRespawnPosition(BwAreas.bedWarsWorld.dimension(), + BwAreas.queueSpawn.toBlockPos(), BwAreas.queueSpawn.yaw, true, false); + PlayerUtil.resetHealthStatus(player); + player.setGameMode(GameType.ADVENTURE); + player.setInvulnerable(true); + + BwGame.queueList.add(player); + if (!BwGame.isQueueCountdownActive && BwGame.queueList.size() >= BwGame.requiredPlayers) { + BwGame.startQueueCountdown(); + } + } + + public static void leaveQueue(ServerPlayer player) { + BwGame.queueList.remove(player); + if (BwGame.isQueueCountdownActive && BwGame.queueList.size() < BwGame.requiredPlayers) { + BwGame.endQueueCountdown(); + } + } + + public static void eliminatePlayer(ServerPlayer player, boolean becomeSpectator) { + BwTeam team = BwTeam.getPlayerTeam(player); + + if (team != null) { + String eliminationMessage = team.textColor + player.getScoreboardName() + " \2477has been eliminated"; + PlayerUtil.broadcast(getPlayers(), eliminationMessage); + + team.players.remove(player); + + ServerLevel world = BwAreas.bedWarsWorld; + if (team.players.size() < 1 && team.bedLocation != null && + world.getBlockState(team.bedLocation).getBlock() instanceof BedBlock) { + world.setBlock(team.bedLocation, Blocks.AIR.defaultBlockState(), 3); + } + } + + player.getEnderChestInventory().clearContent(); + BwScoreboard.updateScoreboard(); + + if (becomeSpectator) { + becomeSpectator(player); + } else { + BwScoreboard.removeScoreboardFor(player); + } + + if (BwTeam.getAliveTeams().size() < 2) { + BwGame.endBedwars(); + } + } + + public static void becomeSpectator(ServerPlayer player) { + setInBedWars(player); + + player.setGameMode(GameType.SPECTATOR); + BwAreas.spectatorSpawn.teleportPlayer(BwAreas.bedWarsWorld, player); + player.setRespawnPosition(BwAreas.bedWarsWorld.dimension(), + BwAreas.spectatorSpawn.toBlockPos(), BwAreas.spectatorSpawn.yaw, true, false); + + BwGame.spectatorList.add(player); + player.addTag(LobbyUtil.NO_RANK_DISPLAY_TAG); + ServerScoreboard scoreboard = ServerTime.minecraftServer.getScoreboard(); + scoreboard.addPlayerToTeam(player.getScoreboardName(), BwGame.spectatorTeam); + + BwScoreboard.sendBedWarsScoreboard(player); + BwScoreboard.sendLines(player); + } + + public static void sendToSpawn(ServerPlayer player) { + PlayerUtil.resetHealthStatus(player); + player.setGameMode(GameType.SURVIVAL); + giveSpawnItems(player); + player.setInvulnerable(true); + BwGame.invulnerabilityList.put(player, BwGame.invulnerabilityTime * 20); + + EntityPos respawnPos = BwAreas.bedWarsCenter; + BwTeam team = BwTeam.getPlayerTeam(player); + if (team != null && team.spawn != null) { + respawnPos = team.spawn; + } + player.setRespawnPosition(BwAreas.bedWarsWorld.dimension(), respawnPos.toBlockPos(), respawnPos.yaw, true, false); + player.teleportTo(BwAreas.bedWarsWorld, respawnPos.x, respawnPos.y, respawnPos.z, respawnPos.yaw, respawnPos.pitch); + } + + private static void giveSpawnItems(ServerPlayer player) { + ItemStack sword = new ItemStack(Items.STONE_SWORD); + sword.getOrCreateTag().putInt("Unbreakable", 1); + player.inventory.add(sword); + + if (player.inventory.getItem(36).isEmpty()) { + player.inventory.setItem(36, getArmorItem(Items.LEATHER_BOOTS, player)); + } + if (player.inventory.getItem(37).isEmpty()) { + player.inventory.setItem(37, getArmorItem(Items.LEATHER_LEGGINGS, player)); + } + player.inventory.setItem(38, getArmorItem(Items.LEATHER_CHESTPLATE, player)); + player.inventory.setItem(39, getArmorItem(Items.LEATHER_HELMET, player)); + } + + private static ItemStack getArmorItem(Item item, ServerPlayer player) { + ItemStack itemStack = item.getDefaultInstance(); + itemStack.getOrCreateTag().putInt("Unbreakable", 1); + + if (!(item instanceof DyeableLeatherItem)) return itemStack; + DyeableLeatherItem leatherItem = (DyeableLeatherItem) item; + + BwTeam team = BwTeam.getPlayerTeam(player); + if (team == null) return itemStack; + + leatherItem.setColor(itemStack, team.armorColor); + return itemStack; + } + + // Lists ---------------------------------------------------- + // A viewer is someone playing the game or spectating the game + + public static ArrayList getViewers() { + ArrayList viewers = new ArrayList<>(getPlayers()); + viewers.addAll(BwGame.spectatorList); + return viewers; + } + + public static ArrayList getPlayers() { + ArrayList list = new ArrayList<>(); + for (BwTeam team : BwTeam.allTeams.values()) { + if (team.players != null) { + list.addAll(team.players); + } + } + return list; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/players/BwTeam.java b/src/main/java/com/nexia/minigames/games/bedwars/players/BwTeam.java new file mode 100644 index 00000000..6e9cd0f5 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/players/BwTeam.java @@ -0,0 +1,287 @@ +package com.nexia.minigames.games.bedwars.players; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.BlockUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.upgrades.BwTrap; +import com.nexia.minigames.games.bedwars.upgrades.BwUpgrade; +import com.nexia.minigames.games.bedwars.util.BwScoreboard; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerScoreboard; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.scores.PlayerTeam; + +import java.util.*; +import java.util.function.Predicate; + +public class BwTeam { + + public String displayName; + public String color; + public String textColor; + public String textColorName; + public int armorColor; + + public PlayerTeam scoreboardTeam; + public BlockPos bedLocation = null; + public EntityPos spawn = null; + public EntityPos genLocation = null; + public ArrayList players = new ArrayList<>(); + + public HashMap upgrades = BwUpgrade.newUpgradeSet(); + public HashMap traps = BwTrap.newTrapSet(); + + private BwTeam(String displayName, String colorName, String colorId, String textColorName, int r, int g, int b) { + this.displayName = displayName; + this.color = colorName; + this.textColor = "\247" + colorId; + this.textColorName = textColorName; + this.armorColor = r * 65536 + g * 256 + b; + this.scoreboardTeam = getTeam(); + } + + private PlayerTeam getTeam() { + ServerScoreboard scoreboard = ServerTime.minecraftServer.getScoreboard(); + String teamName = "bw_" + color; + PlayerTeam playerTeam = scoreboard.getPlayerTeam(teamName); + + if (scoreboard.getPlayerTeam(teamName) == null) { + playerTeam = scoreboard.addPlayerTeam(teamName); + } + if (teamName.length() > 16) { + teamName = teamName.substring(16); + } + + playerTeam.setDisplayName(new TextComponent(teamName)); + playerTeam.setPlayerPrefix(new TextComponent(textColor + "\247lBW " )); + + return playerTeam; + } + + // Team list -------------------------------------------------------------------------- + + public static HashMap allTeams; + public static ArrayList teamsInOrder; + + public static void resetTeams() { + allTeams = new HashMap<>(); + teamsInOrder = new ArrayList<>(); + + addTeam(new BwTeam("Red", "red", "c", "red", 255, 0, 0)); + addTeam(new BwTeam("Orange", "orange", "6", "gold", 255, 130, 30)); + addTeam(new BwTeam("Yellow", "yellow", "e", "yellow", 255, 255, 0)); + addTeam(new BwTeam("Lime", "lime", "a", "green", 20, 255, 25)); + addTeam(new BwTeam("Green", "green", "2", "dark_green", 0, 120, 0)); + addTeam(new BwTeam("Aqua", "light_blue", "b", "aqua", 10, 240, 255)); + addTeam(new BwTeam("Blue", "blue", "9", "blue", 0, 50, 255)); + addTeam(new BwTeam("Purple", "purple", "d", "light_purple", 150, 0, 150)); + + BwGame.maxPlayerCount = BwGame.playersInTeam * allTeams.size(); + } + + private static void addTeam(BwTeam team) { + allTeams.put(team.color, team); + teamsInOrder.add(team); + } + + // PLayer util -------------------------------------------------------------------------- + + public static ArrayList getAliveTeams() { + ArrayList aliveTeams = new ArrayList<>(); + for (BwTeam team : allTeams.values()) { + if (team.players.size() > 0) { + aliveTeams.add(team); + } + } + return aliveTeams; + } + + public static void spreadIntoTeams(ArrayList queueList) { + ArrayList availableTeams = new ArrayList<>(); + ServerScoreboard scoreboard = ServerTime.minecraftServer.getScoreboard(); + Random random = BwAreas.bedWarsWorld.getRandom(); + + availableTeams.addAll(teamsInOrder); + + while (queueList.size() > 0 && availableTeams.size() > 0) { + ServerPlayer player = queueList.get(0); + + BwTeam team = availableTeams.get(random.nextInt(availableTeams.size())); + team.players.add(player); + if (team.players.size() >= BwGame.playersInTeam) availableTeams.remove(team); + + scoreboard.addPlayerToTeam(player.getScoreboardName(), team.scoreboardTeam); + player.addTag(LobbyUtil.NO_RANK_DISPLAY_TAG); + + queueList.remove(player); + } + + queueList.clear(); + } + + public static BwTeam getPlayerTeam(ServerPlayer player) { + for (BwTeam team : allTeams.values()) { + if (team.players.contains(player)) { + return team; + } + } + return null; + } + + public static boolean fixTeamPlayer(ServerPlayer player) { + for (BwTeam team : allTeams.values()) { + for (int i = 0; i < team.players.size(); i++) { + if (team.players.get(i).getUUID().equals(player.getUUID())) { + team.players.set(i, PlayerUtil.getFixedPlayer(player)); + return true; + } + } + } + return false; + } + + public static String getPlayerTeamColor(ServerPlayer player) { + BwTeam team = getPlayerTeam(player); + if (team == null) return null; + + return team.color; + } + + // Spawn util -------------------------------------------------------------------------- + + public static final String spawnTagStart = "spawnLocation_"; + public static final String bedTagStart = "bedLocation_"; + + public static void setSpawns() { + AABB aabb = new AABB(BwAreas.bedWarsCorner1, BwAreas.bedWarsCorner2); + Predicate predicate = o -> true; + + for (Entity entity : BwAreas.bedWarsWorld.getEntities(EntityType.ARMOR_STAND, aabb, predicate)) { + for (String tag : entity.getTags()) { + if (tag.startsWith(spawnTagStart)) { + tag = tag.replaceFirst(spawnTagStart, ""); + if (!allTeams.containsKey(tag)) return; + allTeams.get(tag).spawn = new EntityPos(entity); + } + } + } + } + + public static void createBeds() { + AABB aabb = new AABB(BwAreas.bedWarsCorner1, BwAreas.bedWarsCorner2); + Predicate predicate = o -> true; + + for (Entity entity : BwAreas.bedWarsWorld.getEntities(EntityType.ARMOR_STAND, aabb, predicate)) { + for (String tag : entity.getTags()) { + if (tag.startsWith(bedTagStart)) { + addPossibleBed(entity, tag); + } + } + } + } + + private static void addPossibleBed(Entity entity, String tag) { + tag = tag.replaceFirst(bedTagStart, ""); + if (!allTeams.containsKey(tag)) return; + + BwTeam team = allTeams.get(tag); + BlockPos blockPos = new BlockPos(entity.position()); + + if (team.players.size() < 1) { + BwAreas.bedWarsWorld.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3); + return; + } + + Block block = Registry.BLOCK.get(new ResourceLocation(team.color + "_bed")); + if (!(block instanceof BedBlock)) return; + BlockUtil.placeBed(BwAreas.bedWarsWorld, (BedBlock)block, blockPos, entity.yRot); + team.bedLocation = blockPos; + } + + public static boolean bedExists(BwTeam team) { + if (team.bedLocation == null) return false; + + BlockState blockState = BwAreas.bedWarsWorld.getBlockState(team.bedLocation); + return BlockUtil.blockToText(blockState).equals(team.color + "_bed"); + } + + // ----- UTILITIES ------------------------------------------------------------------------------- + + public static void reloadPlayerTeamColors(ServerPlayer player) { + MinecraftServer server = player.getServer(); + + for (BwTeam team : teamsInOrder) { + server.getCommands().performCommand(player.createCommandSourceStack(), + "team modify " + team.scoreboardTeam.getName() + " color " + team.textColorName); + } + } + + public static void winnerRockets(ArrayList winners, Integer winnerColor) { + if (winners.size() < 1 || winnerColor == null) return; + + ArrayList positions = new ArrayList<>(); + for (ServerPlayer player : winners) { + Random random = BwAreas.bedWarsWorld.getRandom(); + positions.add(new EntityPos(player).add(random.nextInt(9) - 4, 2, random.nextInt(9) - 4)); + } + positions.add(BwAreas.bedWarsCenter.c().add(0.5, 2, 0.5)); + + ItemStack itemStack = new ItemStack(Items.FIREWORK_ROCKET); + try { + itemStack.setTag(TagParser.parseTag("{Fireworks:{Explosions:[{Type:0,Flicker:1b,Trail:1b,Colors:[I;" + + winnerColor + "]}]}}")); + } catch (Exception ignored) {} + + for (EntityPos pos : positions) { + FireworkRocketEntity rocket = new FireworkRocketEntity(BwAreas.bedWarsWorld, pos.x, pos.y, pos.z, itemStack); + BwAreas.bedWarsWorld.addFreshEntity(rocket); + } + } + + public void announceBedBreak(ServerPlayer breaker, BlockPos blockPos) { + String breakerColor = ""; + BwTeam breakerTeam = getPlayerTeam(breaker); + if (breakerTeam != null) breakerColor = breakerTeam.textColor; + + PlayerUtil.broadcast(BwPlayers.getViewers(), textColor + displayName + " bed" + + ChatFormat.chatColor2 + " has been destroyed by " + breakerColor + breaker.getScoreboardName()); + PlayerUtil.broadcastTitle(players, "\247cBed Destroyed", ""); + + BwScoreboard.updateScoreboard(); + + for (ServerPlayer player : BwPlayers.getViewers()) { + float volume; + if (players.contains(player)) { + volume = 0.04f; + } else { + float distance = (float) new EntityPos(player).distance(new EntityPos(blockPos)); + distance = Math.min(distance, 20f); + volume = 0.03f - (distance * 0.001f); + } + PlayerUtil.sendSound(player, SoundEvents.ENDER_DRAGON_AMBIENT, SoundSource.MASTER, volume, 1f); + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/shop/BwLoadShop.java b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwLoadShop.java new file mode 100644 index 00000000..78c310dd --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwLoadShop.java @@ -0,0 +1,173 @@ +package com.nexia.minigames.games.bedwars.shop; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; +import com.nexia.core.utilities.item.ItemDisplayUtil; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.minigames.games.bedwars.BwGame; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +import java.io.FileReader; +import java.nio.file.Path; + +public class BwLoadShop { + + final static String shopFileName = "shop.json"; + + final static String itemsListKey = "items"; + final static String typeKey = "type"; + final static String upgradeableTypeKey = "upgradeabletype"; + + final static String upgradeableItemsKey = "items"; + + final static String slotKey = "slot"; + final static String itemKey = "item"; + final static String costItemKey = "costitem"; + final static String nameKey = "name"; + final static String descriptionKey = "description"; + final static String nbtKey = "nbt"; + + public static boolean loadBedWarsShop(boolean useDefaultOnFailure) { + JsonArray jsonArray = readBedWarsShopFile(); + + if (jsonArray == null) { + if (useDefaultOnFailure) { + System.out.println("Failed to read BedWars shop file, using villagers"); + } else { + System.out.println("Failed to read BedWars shop file"); + } + return false; + } + + loadShopFromJson(jsonArray); + return true; + } + + static JsonArray readBedWarsShopFile() { + try { + Path path = Path.of(BwGame.bedWarsDirectory + "/" + shopFileName); + if (!path.toFile().exists()) return null; + + JsonReader jsonReader = new JsonReader(new FileReader(path.toFile())); + JsonParser jsonParser = new JsonParser(); + JsonElement element = jsonParser.parse(jsonReader); + jsonReader.close(); + + if (!element.isJsonObject()) return null; + JsonObject object = (JsonObject) element; + + JsonElement kitElement = object.get(itemsListKey); + if (kitElement == null || !kitElement.isJsonArray()) return null; + return (JsonArray) kitElement; + } catch (Exception e) { + return null; + } + } + + static void loadShopFromJson(JsonArray jsonArray) { + BwShop.bedWarsShopItems = new ItemStack[BwShop.shopSize]; + BwShop.bedWarsUpgradeableItems = new ItemStack[28][]; + + for (JsonElement element : jsonArray) { + try { + JsonObject object = (JsonObject) element; + + int slot = object.get(slotKey).getAsInt(); + if (slot >= BwShop.shopSize || slot < 0) continue; + + String type = "default"; + if (object.has(typeKey)) type = object.get(typeKey).getAsString(); + + if (type.equals("upgradeable")) { + ItemStack[] upgradeableItems = getUpgradeableItems(object); + if (upgradeableItems != null) BwShop.bedWarsUpgradeableItems[slot] = upgradeableItems; + } else { + ItemStack itemStack = getItemStackFromJson(object); + if (itemStack != null) BwShop.bedWarsShopItems[slot] = itemStack; + } + + } catch (Exception e) { + System.out.println("Could not load item in BedWars shop"); + } + } + } + + static ItemStack[] getUpgradeableItems(JsonObject object) { + JsonArray upgradeableList = object.get(upgradeableItemsKey).getAsJsonArray(); + String upgradeableItemId = object.get(upgradeableTypeKey).getAsString(); + + int size = upgradeableList.size(); + if (size < 1) return null; + + ItemStack[] upgradeableItems = new ItemStack[size]; + for (int i = 0; i < size; i++) { + ItemStack upgradeableItem = getItemStackFromJson(upgradeableList.get(i).getAsJsonObject()); + if (upgradeableItem == null) continue; + + CompoundTag info = new CompoundTag(); + info.putString(BwShopUpgradeables.upgradeableIdKey, upgradeableItemId); + info.putInt(BwShopUpgradeables.upgradeableLevelKey, i); + upgradeableItem.getOrCreateTag().put(BwShopUpgradeables.upgradeableInfoKey, info); + + upgradeableItems[i] = upgradeableItem; + } + return upgradeableItems; + } + + static ItemStack getItemStackFromJson(JsonObject object) { + + ItemStack itemStack = ItemStackUtil.stringToItemStack(object.get(itemKey).getAsString()); + ItemStack costStack = ItemStackUtil.stringToItemStack(object.get(costItemKey).getAsString()); + + String nbt = "{}"; + if (object.has(nbtKey)) nbt = object.get(nbtKey).getAsString(); + String name = null; + if (object.has(nameKey)) name = object.get(nameKey).getAsString(); + String description = null; + if (object.has(descriptionKey)) description = object.get(descriptionKey).getAsString(); + + return turnIntoBedWarsItem(itemStack, costStack, name, description, nbt); + + } + + static ItemStack turnIntoBedWarsItem(ItemStack itemStack, ItemStack costStack, String name, String description, String nbt) { + try { + if (itemStack.getItem() == null || itemStack.getItem() == Items.AIR) return null; + if (costStack.getItem() == null || costStack.getItem() == Items.AIR) return null; + + CompoundTag compoundTag = itemStack.getOrCreateTag(); + compoundTag.putBoolean(BwShop.purchasableKey, true); + compoundTag.putString(BwShop.currencyItemKey, costStack.getItem().toString()); + compoundTag.putInt(BwShop.currencyAmountKey, costStack.getCount()); + + compoundTag.merge(TagParser.parseTag(nbt)); + if (itemStack.getMaxDamage() != 0) compoundTag.putInt("Unbreakable", 1); + + if (description != null) addDescriptionLore(itemStack, description); + if (name != null) itemStack.setHoverName(new TextComponent("\247f" + name)); + + return itemStack; + } catch (Exception e) { + return null; + } + } + + static void addDescriptionLore(ItemStack itemStack, String description) { + String[] lines = description.split("\\\\n"); + + ItemDisplayUtil.addLore(itemStack, " ", -1); + for (String line : lines) { + line = "\2477" + line; + ItemDisplayUtil.addLore(itemStack, line, -1); + } + ItemDisplayUtil.addLore(itemStack, " ", -1); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShop.java b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShop.java new file mode 100644 index 00000000..42de6b0a --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShop.java @@ -0,0 +1,159 @@ +package com.nexia.minigames.games.bedwars.shop; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import eu.pb4.sgui.api.ClickType; +import eu.pb4.sgui.api.elements.GuiElementInterface; +import eu.pb4.sgui.api.gui.SimpleGui; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundSoundPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SwordItem; + +public class BwShop extends SimpleGui { + + public static ItemStack[] bedWarsShopItems = null; + public static ItemStack[][] bedWarsUpgradeableItems = null; + final static int shopHeight = 4; + final static int shopLength = 7; + final static int shopSize = shopHeight * shopLength; + + final static String purchasableKey = "bedWarsPurchasable"; + final static TextComponent title = new TextComponent("Item Shop"); + + final static String currencyItemKey = "bwCurrencyItem"; + final static String currencyAmountKey = "bwCurrencyAmount"; + + public BwShop(MenuType type, ServerPlayer player, boolean includePlayer) { + super(type, player, includePlayer); + } + + public static void openShopGui(ServerPlayer player) { + BwShop shop = new BwShop(MenuType.GENERIC_9x6, player, false); + shop.setTitle(title); + for (int i = 0; i < bedWarsShopItems.length; i++) { + ItemStack itemStack = BwShopUtil.getShopItem(player, i); + if (itemStack == null) continue; + itemStack = BwShopUtil.toGuiItem(itemStack); + if (itemStack == null) continue; + + shop.setSlot(indexToGuiSlot(i), itemStack); + } + shop.open(); + } + + public boolean click(int index, ClickType clickType, net.minecraft.world.inventory.ClickType action) { + GuiElementInterface element = this.getSlot(index); + if (element != null) { + ItemStack itemStack = element.getItemStack(); + CompoundTag tag = itemStack.getOrCreateTag(); + + int targetSlot = -1; + if (clickType.numKey) targetSlot = clickType.value - 1; + + if (tag.contains(purchasableKey) && tag.getBoolean(purchasableKey) && clickType != ClickType.MOUSE_DOUBLE_CLICK) { + this.tryToPurchase(player, itemStack.copy(), index, targetSlot); + } + } + return super.click(index, clickType, action); + } + + private void tryToPurchase(ServerPlayer player, ItemStack soldItem, int slot, int targetInvSlot) { + ItemStack cost = BwShopUtil.getCost(soldItem); + if (cost == null) return; + + boolean isArmorItem = soldItem.getItem() instanceof ArmorItem; + boolean isSword = soldItem.getItem() instanceof SwordItem; + boolean isUpgradeable = soldItem.getOrCreateTag().contains(BwShopUpgradeables.upgradeableInfoKey); + + if (isArmorItem) { + int playerLevel = ItemStackUtil.getArmorTier(player.inventory.getItem(36).getItem()); + int soldItemLevel = ItemStackUtil.getArmorTier(soldItem.getItem()); + if (soldItemLevel < playerLevel) { + sendFail(player, "You already have better armor."); + return; + } + if (soldItemLevel == playerLevel) { + sendFail(player, "You already have this armor."); + return; + } + } + if (isUpgradeable && BwShopUpgradeables.hasSameUpgradeItem(player, soldItem)) { + sendFail(player, "You have reached the maximum level of this item"); + return; + } + if (player.inventory.countItem(cost.getItem()) < cost.getCount()) { + sendFail(player, "You can't afford this item"); + return; + } + soldItem = BwShopUtil.setBlockColor(player, soldItem); + BwShopUtil.removeShopNbt(soldItem); + if (!ItemStackUtil.hasRoomFor(player.inventory, soldItem) && !isArmorItem) { + sendFail(player, "Your inventory is full."); + return; + } + purchase(player, soldItem, cost, slot, targetInvSlot, isUpgradeable, isArmorItem, isSword); + + } + + private void purchase(ServerPlayer player, ItemStack soldItem, ItemStack cost, int slot, int targetInvSlot, + boolean isUpgradeable, boolean isArmorItem, boolean isSword) { + + PlayerUtil.removeItem(player, cost.getItem(), cost.getCount()); + playPurchaseSound(player, false); + + if (isUpgradeable) { + BwShopUpgradeables.replaceUpgradeItem(player, soldItem, targetInvSlot); + ItemStack shopSlotItem = BwShopUpgradeables.getUpgradeItem(player, guiSlotToIndex(slot)); + this.setSlot(slot, BwShopUtil.toGuiItem(shopSlotItem)); + + } else if (isArmorItem) { + BwShopUtil.giveArmorItems(player, soldItem); + + } else if (isSword) { + BwShopUtil.giveSword(player, soldItem); + + } else { + BwShopUtil.giveItem(player, soldItem, targetInvSlot); + } + } + + public static void sendFail(ServerPlayer player, String text) { + player.sendMessage(ChatFormat.formatFail(text), player.getUUID()); + playPurchaseSound(player, true); + } + + static int indexToGuiSlot(int index) { + return 9 * (index % shopHeight + 1) + index / shopHeight + 1; + } + + static int guiSlotToIndex(int guiSlot) { + return shopHeight * (guiSlot % 9 - 1) + guiSlot / 9 - 1; + } + + public static void playPurchaseSound(ServerPlayer player, boolean fail) { + SoundEvent soundEvent; + float volume; + float pitch; + if (!fail) { + soundEvent = SoundEvents.NOTE_BLOCK_PLING; + volume = 0.6f; + pitch = 2.0f; + } else { + soundEvent = SoundEvents.ENDERMAN_TELEPORT; + volume = 0.6f; + pitch = 1.0f; + } + player.connection.send(new ClientboundSoundPacket(soundEvent, SoundSource.MASTER, + player.getX(), player.getY(), player.getZ(), volume * 16, pitch)); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopCost.java b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopCost.java new file mode 100644 index 00000000..8b8b6924 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopCost.java @@ -0,0 +1,45 @@ +package com.nexia.minigames.games.bedwars.shop; + +import com.nexia.core.utilities.item.ItemDisplayUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class BwShopCost { + + final static String costFormat = "\247r\2477Cost: "; + + static void addCostLore(ItemStack itemStack, ItemStack cost) { + String color = "\247f"; + if (cost.getItem() == Items.IRON_INGOT) color = "\247f"; + if (cost.getItem() == Items.GOLD_INGOT) color = "\2476"; + if (cost.getItem() == Items.DIAMOND) color = "\247b"; + if (cost.getItem() == Items.EMERALD) color = "\2472"; + + String name = cost.getItem().toString(); + name = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); + if (cost.getCount() != 1) name += "s"; + + for (int i = 0; i < name.length(); i++) { + if (name.charAt(i) == '_') { + name = name.substring(0, i) + ' ' + name.substring(i+1); + if (name.length() > i+1) { + name = name.substring(0, i+1) + name.substring(i+1, i+2).toUpperCase() + name.substring(i+2); + } + } + } + name = costFormat + color + cost.getCount() + " " + name; + + ItemDisplayUtil.addLore(itemStack, name, 0); + } + + protected static void removeCostLore(ItemStack itemStack) { + CompoundTag compoundTag = itemStack.getOrCreateTag(); + ListTag listTag = compoundTag.getCompound("display").getList("Lore", 8); + if (listTag != null && listTag.size() > 0 && listTag.get(0).toString().contains(costFormat)) { + ItemDisplayUtil.removeLore(itemStack, 0); + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUpgradeables.java b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUpgradeables.java new file mode 100644 index 00000000..fd46514f --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUpgradeables.java @@ -0,0 +1,143 @@ +package com.nexia.minigames.games.bedwars.shop; + +import com.nexia.core.utilities.item.ItemStackUtil; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +import java.util.ArrayList; +import java.util.Optional; + +public class BwShopUpgradeables { + + final static String upgradeableInfoKey = "upgradeableInfo"; + final static String upgradeableIdKey = "id"; + final static String upgradeableLevelKey = "level"; + + protected static ItemStack getUpgradeItem(ServerPlayer player, int index) { + try { + ItemStack[] upgradeableItems = BwShop.bedWarsUpgradeableItems[index]; + ItemStack comparedStack = upgradeableItems[0].copy(); + int currentLevel; + + currentLevel = getPlayerUpgradeLevel(player, comparedStack); + + if (upgradeableItems.length > currentLevel + 1) { + return upgradeableItems[currentLevel + 1]; + } + return upgradeableItems[currentLevel]; + + } catch (Exception e) { + return null; + } + } + + private static int getPlayerUpgradeLevel(ServerPlayer player, ItemStack comparedStack) { + int currentLevel = -1; + for (ItemStack invItem : ItemStackUtil.getInvItems(player)) { + + if (!isSameUpgradeType(comparedStack, invItem)) continue; + Optional invItemLevel = getItemUpgradeLevel(invItem); + if (invItemLevel.isPresent() && invItemLevel.get() > currentLevel) { + currentLevel = invItemLevel.get(); + } + + } + return currentLevel; + } + + protected static boolean hasSameUpgradeItem(ServerPlayer player, ItemStack itemStack) { + for (ItemStack invItem : ItemStackUtil.getInvItems(player)) { + if (!isSameUpgradeType(invItem, itemStack)) continue; + Optional invLevel = getItemUpgradeLevel(invItem); + Optional itemLevel = getItemUpgradeLevel(itemStack); + if (invLevel.isPresent() && invLevel.equals(itemLevel)) return true; + } + return false; + } + + protected static boolean isSameUpgradeType(ItemStack itemStack1, ItemStack itemStack2) { + if (itemStack1.getItem() == Items.AIR || itemStack2.getItem() == Items.AIR) return false; + + String type1 = getItemUpgradeType(itemStack1); + if (type1 == null) return false; + String type2 = getItemUpgradeType(itemStack2); + if (type2 == null) return false; + + return type1.equals(type2); + } + + protected static String getItemUpgradeType(ItemStack itemStack) { + CompoundTag tag = itemStack.getTag(); + if (tag == null) return null; + if (!tag.contains(upgradeableInfoKey) || !(tag.get(upgradeableInfoKey) instanceof CompoundTag)) return null; + + CompoundTag info = (CompoundTag)tag.get(upgradeableInfoKey); + if (info == null) return null; + + String string = info.getString(upgradeableIdKey); + if (string.equals("")) return null; + return string; + } + + protected static Optional getItemUpgradeLevel(ItemStack itemStack) { + CompoundTag tag = itemStack.getTag(); + if (tag == null) return Optional.empty(); + if (!tag.contains(upgradeableInfoKey) || !(tag.get(upgradeableInfoKey) instanceof CompoundTag)) return Optional.empty(); + + CompoundTag info = (CompoundTag)tag.get(upgradeableInfoKey); + if (info == null) return Optional.empty(); + if (!info.contains(upgradeableLevelKey, 99)) return Optional.empty(); + return Optional.of(info.getInt(upgradeableLevelKey)); + } + + protected static void replaceUpgradeItem(ServerPlayer player, ItemStack newItem, int targetSlot) { + ArrayList inventory = ItemStackUtil.getInvItems(player); + + for (int i = 0; i < inventory.size(); i++) { + ItemStack invItem = inventory.get(i); + if (!isSameUpgradeType(invItem, newItem)) continue; + + Optional invLevel = getItemUpgradeLevel(invItem); + Optional newLevel = getItemUpgradeLevel(newItem); + + if (invLevel.isPresent() && newLevel.isPresent() && newLevel.get()-invLevel.get() > 0) { + player.inventory.setItem(i, newItem); + return; + } + } + BwShopUtil.giveItem(player, newItem, targetSlot); + } + + public static ArrayList getActiveUpgradeables() { + ArrayList activeUpgradeItems = new ArrayList<>(); + for (ItemStack[] itemStacks : BwShop.bedWarsUpgradeableItems) { + if (itemStacks == null || itemStacks.length < 1) continue; + activeUpgradeItems.add(itemStacks); + } + return activeUpgradeItems; + } + + public static void downgradePlayerTools(ServerPlayer player) { + ArrayList allUpgradeables = getActiveUpgradeables(); + int invSize = player.inventory.items.size(); + + inv: for (int i = 0; i < invSize; i++) { + ItemStack invItem = player.inventory.getItem(i); + + Optional invItemLevel = getItemUpgradeLevel(invItem); + if (invItemLevel.isEmpty() || invItemLevel.get() < 1) continue; + + for (ItemStack[] upgradeables : allUpgradeables) { + if (!isSameUpgradeType(invItem, upgradeables[0])) continue; + + ItemStack newItem = upgradeables[invItemLevel.get() - 1].copy(); + BwShopUtil.removeShopNbt(newItem); + player.inventory.setItem(i, newItem); + continue inv; + } + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUtil.java b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUtil.java new file mode 100644 index 00000000..58268ab0 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/shop/BwShopUtil.java @@ -0,0 +1,167 @@ +package com.nexia.minigames.games.bedwars.shop; + +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +public class BwShopUtil { + + protected static ItemStack getShopItem(ServerPlayer player, int index) { + ItemStack itemStack = BwShop.bedWarsShopItems[index]; + + if (itemStack != null) { + return itemStack; + } else { + return BwShopUpgradeables.getUpgradeItem(player, index); + } + } + + protected static ItemStack toGuiItem(ItemStack itemStack) { + itemStack = itemStack.copy(); + ItemStack cost = getCost(itemStack); + if (cost == null) return null; + BwShopCost.addCostLore(itemStack, cost); + return itemStack; + } + + protected static void removeShopNbt(ItemStack itemStack) { + CompoundTag tag = itemStack.getTag(); + if (tag == null) return; + + BwShopCost.removeCostLore(itemStack); + tag.remove(BwShop.purchasableKey); + tag.remove(BwShop.currencyItemKey); + tag.remove(BwShop.currencyAmountKey); + + if (tag.getAsString().equals("{}")) itemStack.setTag(null); + else itemStack.setTag(tag); + } + + protected static ItemStack getCost(ItemStack soldItem) { + try { + CompoundTag compoundTag = soldItem.getOrCreateTag(); + Item item = Registry.ITEM.get(new ResourceLocation(compoundTag.getString(BwShop.currencyItemKey))); + int amount = compoundTag.getInt(BwShop.currencyAmountKey); + return new ItemStack(item, amount); + } catch (Exception e) { + return null; + } + } + + protected static void giveItem(ServerPlayer player, ItemStack itemStack, int targetSlot) { + itemStack = itemStack.copy(); + + // Try to set item into hotkeyed slot + if (targetSlot >= 0 && targetSlot < 36) { + ItemStack invItem = player.inventory.getItem(targetSlot); + if (combineIfSameItem(itemStack, invItem)) { + if (itemStack.getCount() == 0) return; + } else { + player.inventory.setItem(targetSlot, itemStack); + player.inventory.add(invItem); + return; + } + } + + // Try to combine item with existing items of same type in inventory + for (int i = 0; i < player.inventory.items.size() + 1; i++) { + int slot = i; + if (i == 36) slot = 40; + ItemStack invItem = player.inventory.getItem(slot); + combineIfSameItem(itemStack, invItem); + if (itemStack.getCount() == 0) return; + } + + // Normal give except moves resource items out of the way + for (int i = 0; i < player.inventory.items.size() + 1; i++) { + int slot = i; + if (i == 36) slot = 40; + ItemStack invItem = player.inventory.getItem(slot); + if (invItem.isEmpty()) { + player.inventory.setItem(slot, itemStack); + return; + } else if (BwUtil.isBedWarsCurrency(invItem)) { + player.inventory.setItem(slot, itemStack); + player.inventory.add(invItem); + return; + } + } + } + + private static boolean combineIfSameItem(ItemStack itemStack, ItemStack invItem) { + if (!ItemStackUtil.isSameItem(itemStack, invItem)) return false; + + int availableSpace = invItem.getMaxStackSize() - invItem.getCount(); + if (availableSpace >= itemStack.getCount()) { + invItem.setCount(invItem.getCount() + itemStack.getCount()); + itemStack.setCount(0); + } else { + invItem.setCount(invItem.getMaxStackSize()); + itemStack.shrink(availableSpace); + } + return true; + } + + protected static void giveSword(ServerPlayer player, ItemStack soldItem) { + Inventory inv = player.inventory; + + for (int i = 0; i < inv.items.size(); i++) { + if (BwUtil.isDefaultSword(inv.getItem(i))) { + inv.setItem(i, soldItem); + return; + } + } + giveItem(player, soldItem, -1); + } + + protected static void giveArmorItems(Player player, ItemStack armorPiece) { + String type = armorPiece.getItem().toString().split("_")[0]; + + String bootsItemName = type + "_boots"; + player.inventory.setItem(36, armorItemFromName(bootsItemName)); + + String leggingsItemName = type + "_leggings"; + player.inventory.setItem(37, armorItemFromName(leggingsItemName)); + } + + private static ItemStack armorItemFromName(String itemName) { + Item bootsItem = ItemStackUtil.itemFromString(itemName); + ItemStack boots = new ItemStack(bootsItem); + boots.getOrCreateTag().putInt("Unbreakable", 1); + return boots; + } + + protected static ItemStack setBlockColor(ServerPlayer player, ItemStack original) { + try { + String color = BwTeam.getPlayerTeamColor(player); + Item item = original.getItem(); + + if (original.getItem() == Items.WHITE_WOOL) { + item = Registry.ITEM.get(new ResourceLocation(item.toString().replace("white", color))); + } else if (original.getItem() == Items.GLASS) { + item = Registry.ITEM.get(new ResourceLocation(color + "_stained_glass")); + } else { + return original; + } + + if (item == Items.AIR) return original; + + ItemStack newItem = new ItemStack(item, original.getCount()); + if (original.hasTag()) newItem.setTag(original.getTag()); + return newItem; + + } catch (Exception e) { + return original; + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyTraps.java b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyTraps.java new file mode 100644 index 00000000..1455a7dd --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyTraps.java @@ -0,0 +1,74 @@ +package com.nexia.minigames.games.bedwars.upgrades; + +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; + +import java.util.HashMap; + +public class BwApplyTraps { + + public static void trapSecond() { + // Loop all bedwars players + for (BwTeam attackerTeam : BwTeam.allTeams.values()) { + for (ServerPlayer attacker : attackerTeam.players) { + if (BwGame.respawningList.containsKey(attacker)) continue; + + // Loop all bedwars bases + for (EntityPos trapPos : BwTrap.trapLocations.keySet()) { + BwTeam defenderTeam = BwTrap.trapLocations.get(trapPos); + if (defenderTeam == attackerTeam || !trapPos.isInRadius(new EntityPos(attacker), 19)) continue; + + // Loop all traps in a bedwars base + boolean trapActivated = false; + HashMap teamTrapSet = BwTrap.trapLocations.get(trapPos).traps; + for (String trapKey : teamTrapSet.keySet()) { + BwTrap trap = teamTrapSet.get(trapKey); + if (trap == null || !trap.bought) continue; + trapSetOff(attacker, trapKey); + trapActivated = true; + trap.bought = false; + } + if (trapActivated) alarmDefenders(defenderTeam); + break; + } + } + } + } + + private static void trapSetOff( ServerPlayer attacker, String trapKey) { + switch (trapKey) { + case BwTrap.TRAP_KEY_ALARM -> alarmTrap(attacker); + case BwTrap.TRAP_KEY_SLOWNESS -> slownessTrap(attacker); + case BwTrap.TRAP_KEY_MINING_FATIGUE -> miningTrap(attacker); + } + } + + private static void alarmDefenders(BwTeam defenderTeam) { + PlayerUtil.broadcastSound(defenderTeam.players, SoundEvents.ENDERMAN_TELEPORT, SoundSource.MASTER, 0.8f, 1.0f); + PlayerUtil.broadcastTitle(defenderTeam.players, "\247cTrap Triggered!", ""); + } + + private static void alarmTrap(ServerPlayer attacker) { + if (attacker.hasEffect(MobEffects.INVISIBILITY)) { + attacker.removeEffect(MobEffects.INVISIBILITY); + } + } + + private static void slownessTrap(ServerPlayer attacker) { + int seconds = 10; + attacker.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, seconds * 20)); + attacker.addEffect(new MobEffectInstance(MobEffects.BLINDNESS, seconds * 20)); + } + + private static void miningTrap(ServerPlayer attacker) { + attacker.addEffect(new MobEffectInstance(MobEffects.DIG_SLOWDOWN, 10 * 20)); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyUpgrades.java b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyUpgrades.java new file mode 100644 index 00000000..d5ff31b2 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwApplyUpgrades.java @@ -0,0 +1,84 @@ +package com.nexia.minigames.games.bedwars.upgrades; + +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import com.nexia.minigames.games.bedwars.util.BwUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SwordItem; +import net.minecraft.world.item.TridentItem; +import net.minecraft.world.item.enchantment.Enchantments; + +public class BwApplyUpgrades { + + public static void upgradeSecond() { + for (BwTeam team : BwTeam.allTeams.values()) { + if (team == null || team.players == null) continue; + + applySharpness(team); + applyProtection(team); + applyHaste(team); + applyHealing(team); + } + } + + private static void applySharpness(BwTeam team) { + int level = team.upgrades.get(BwUpgrade.UPGRADE_KEY_SHARPNESS).level; + + for (ServerPlayer player : team.players) { + for (ItemStack itemStack : player.inventory.items) { + if (itemStack.getItem() instanceof SwordItem || itemStack.getItem() instanceof TridentItem) { + ItemStackUtil.enchant(itemStack, Enchantments.SHARPNESS, level); + } + } + } + } + + private static void applyProtection(BwTeam team) { + int level = team.upgrades.get(BwUpgrade.UPGRADE_KEY_PROTECTION).level; + if (level < 1) return; + + for (ServerPlayer player : team.players) { + NonNullList armor = player.inventory.armor; + ItemStackUtil.enchant(armor.get(0), Enchantments.ALL_DAMAGE_PROTECTION, level); + ItemStackUtil.enchant(armor.get(1), Enchantments.ALL_DAMAGE_PROTECTION, level); + ItemStackUtil.enchant(armor.get(2), Enchantments.ALL_DAMAGE_PROTECTION, level); + ItemStackUtil.enchant(armor.get(3), Enchantments.ALL_DAMAGE_PROTECTION, level); + } + } + + private static void applyHaste(BwTeam team) { + int level = team.upgrades.get(BwUpgrade.UPGRADE_KEY_HASTE).level; + if (level < 1) return; + + int effectLevel = level - 1; + for (ServerPlayer player : team.players) { + if (!player.hasEffect(MobEffects.DIG_SPEED) || player.getEffect(MobEffects.DIG_SPEED).getAmplifier() != effectLevel) { + player.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 1000000, effectLevel, false, false)); + BwUtil.setAttackSpeed(player); + } + } + } + + private static void applyHealing(BwTeam team) { + int level = team.upgrades.get(BwUpgrade.UPGRADE_KEY_HEALING).level; + if (level < 1) return; + + for (ServerPlayer player : team.players) { + if (team.spawn.isInRadius(new EntityPos(player), 24)) { + if (!player.hasEffect(MobEffects.REGENERATION)) { + player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, Integer.MAX_VALUE, level - 1, false, false)); + } + + } else if (player.hasEffect(MobEffects.REGENERATION) && + player.getEffect(MobEffects.REGENERATION).getAmplifier() == 0) { + player.removeEffect(MobEffects.REGENERATION); + } + } + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwTrap.java b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwTrap.java new file mode 100644 index 00000000..93368a1f --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwTrap.java @@ -0,0 +1,64 @@ +package com.nexia.minigames.games.bedwars.upgrades; + +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +import java.util.HashMap; + +public class BwTrap { + + // Nbt key for hashmap keys + public static final String TRAP_TAG_KEY = "trapType"; + + // Hashmap keys + public static final String TRAP_KEY_SLOWNESS = "slowness"; + public static final String TRAP_KEY_MINING_FATIGUE = "mining_fatigue"; + public static final String TRAP_KEY_ALARM = "alarm"; + + public static HashMap trapLocations = new HashMap<>(); + + public static void resetTrapLocations() { + trapLocations.clear(); + if (BwTeam.allTeams == null) return; + for (BwTeam team : BwTeam.allTeams.values()) { + if (team.genLocation == null || team.bedLocation == null) continue; + EntityPos trapLocation = team.genLocation.between(new EntityPos(team.bedLocation)); + trapLocation.y += 6; + trapLocations.put(trapLocation, team); + } + } + + public ItemStack displayItem; + public int displayRow; + public int displayColumn; + public boolean bought; + + private BwTrap(int displayRow, int displayColumn, ItemStack displayItem) { + this.displayItem = displayItem; + this.displayRow = displayRow; + this.displayColumn = displayColumn; + this.bought = false; + } + + public static HashMap newTrapSet() { + HashMap newSet = new HashMap<>(); + + newSet.put(TRAP_KEY_SLOWNESS, new BwTrap(2, 2, + BwUpgrade.upgradeItemStack(Items.TRIPWIRE_HOOK, "Slowness & Blindness Trap", "" + + "Inflicts intruders with slowness\nand blindness for 10 seconds."))); + newSet.put(TRAP_KEY_MINING_FATIGUE, new BwTrap(2, 3, + BwUpgrade.upgradeItemStack(Items.STONE_PICKAXE, "Mining Fatigue Trap", + "Inflicts intruders with mining\nfatigue for 10 seconds."))); + newSet.put(TRAP_KEY_ALARM, new BwTrap(2, 4, + BwUpgrade.upgradeItemStack(Items.REDSTONE_TORCH, "Alarm Trap", + "Reveals invisible enemies\nentering your base."))); + + for (String key : newSet.keySet()) { + newSet.get(key).displayItem.getOrCreateTag().putString(TRAP_TAG_KEY, key); + } + return newSet; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgrade.java b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgrade.java new file mode 100644 index 00000000..fd43e2b8 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgrade.java @@ -0,0 +1,81 @@ +package com.nexia.minigames.games.bedwars.upgrades; + +import com.nexia.core.utilities.item.ItemDisplayUtil; +import com.nexia.minigames.games.bedwars.util.BwGen; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import java.util.HashMap; + +public class BwUpgrade { + + // Nbt key for hashmap keys + public static final String UPGRADE_TAG_KEY = "upgradeType"; + + // Hashmap keys + public static final String UPGRADE_KEY_SHARPNESS = "sharpness"; + public static final String UPGRADE_KEY_PROTECTION = "protection"; + public static final String UPGRADE_KEY_HASTE = "haste"; + public static final String UPGRADE_KEY_HEALING = "healing"; + public static final String UPGRADE_KEY_GENERATOR = "generator"; + + public int level; + public int[] costs; + + public ItemStack displayItem; + public int displayRow; + public int displayColumn; + + private BwUpgrade(int[] costs, int displayRow, int displayColumn, ItemStack displayItem) { + this.level = 0; + this.costs = costs; + + this.displayItem = displayItem; + this.displayRow = displayRow; + this.displayColumn = displayColumn; + } + + public static HashMap newUpgradeSet() { + HashMap newSet = new HashMap<>(); + + newSet.put(UPGRADE_KEY_SHARPNESS, new BwUpgrade(new int[]{4}, 0, 1, + upgradeItemStack(Items.DIAMOND_SWORD, "Sharpened Blades", "" + + "Grants sharpness enchantment\nto all swords and tridents\nfor your team."))); + newSet.put(UPGRADE_KEY_PROTECTION, new BwUpgrade(new int[]{2, 4, 8, 16}, 0, 2, + upgradeItemStack(Items.IRON_CHESTPLATE, "Protection", "" + + "Grants protection enchantment\nto everyone in your team."))); + newSet.put(UPGRADE_KEY_HASTE, new BwUpgrade(new int[]{2, 4}, 0, 3, + upgradeItemStack(Items.GOLDEN_PICKAXE, "Haste", "" + + "Grants haste effect to\neveryone in your team."))); + newSet.put(UPGRADE_KEY_GENERATOR, new BwUpgrade(BwGen.upgradeCosts, 0, 4, + upgradeItemStack(Items.FURNACE, "Better Generators", + null))); + newSet.put(UPGRADE_KEY_HEALING, new BwUpgrade(new int[]{1}, 0, 5, + upgradeItemStack(Items.BEACON, "Heal Pool", "" + + "Grants regeneration effect to\nevery team member\nat home base."))); + + for (String key : newSet.keySet()) { + newSet.get(key).displayItem.getOrCreateTag().putString(UPGRADE_TAG_KEY, key); + } + return newSet; + } + + protected static ItemStack upgradeItemStack(Item item, String name, String description) { + ItemStack itemStack = new ItemStack(item); + itemStack.setHoverName(new TextComponent("\247r\247f" + name)); + + if (description != null) { + String[] lines = description.split("\n"); + ItemDisplayUtil.addLore(itemStack, " ", -1); + for (String line : lines) { + line = "\2477" + line; + ItemDisplayUtil.addLore(itemStack, line, -1); + } + ItemDisplayUtil.addLore(itemStack, " ", -1); + } + + return itemStack; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgradeShop.java b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgradeShop.java new file mode 100644 index 00000000..61fac145 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/upgrades/BwUpgradeShop.java @@ -0,0 +1,216 @@ +package com.nexia.minigames.games.bedwars.upgrades; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.chat.RomanNumbers; +import com.nexia.core.utilities.item.ItemDisplayUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import com.nexia.minigames.games.bedwars.shop.BwShop; +import com.nexia.minigames.games.bedwars.util.BwGen; +import eu.pb4.sgui.api.ClickType; +import eu.pb4.sgui.api.elements.GuiElementInterface; +import eu.pb4.sgui.api.gui.SimpleGui; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; + +import java.util.HashMap; + +public class BwUpgradeShop extends SimpleGui { + + final static TextComponent title = new TextComponent("Upgrade Shop"); + + public BwUpgradeShop(MenuType type, ServerPlayer player, boolean includePlayer) { + super(type, player, includePlayer); + } + + public static void openShopGui(ServerPlayer player) { + BwTeam team = BwTeam.getPlayerTeam(player); + if (team == null) return; + + BwUpgradeShop shop = new BwUpgradeShop(MenuType.GENERIC_9x5, player, false); + shop.setTitle(title); + shop.resetLayout(team); + shop.open(); + } + + private void resetLayout(BwTeam team) { + // Upgrades + HashMap upgrades = team.upgrades; + for (String key : upgrades.keySet()) { + BwUpgrade upgrade = upgrades.get(key); + ItemStack displayItem = upgrade.displayItem.copy(); + addUpgradeCostLore(displayItem, upgrade); + addStatusLore(displayItem, upgrade); + if (key.equals(BwUpgrade.UPGRADE_KEY_GENERATOR)) addGenUpgradeText(displayItem, team); + this.setSlot(rowColumnToGuiSlot(upgrade.displayRow, upgrade.displayColumn), displayItem); + } + // Traps + HashMap traps = team.traps; + for (String key : traps.keySet()) { + BwTrap trap = traps.get(key); + ItemStack displayItem = trap.displayItem.copy(); + addTrapCostLore(displayItem, team, trap); + this.setSlot(rowColumnToGuiSlot(trap.displayRow, trap.displayColumn), displayItem); + } + } + + public boolean click(int index, ClickType clickType, net.minecraft.world.inventory.ClickType action) { + GuiElementInterface element = this.getSlot(index); + if (element != null) { + if (clickType != ClickType.MOUSE_DOUBLE_CLICK) { + + ItemStack itemStack = element.getItemStack(); + CompoundTag tag = itemStack.getOrCreateTag(); + + if (tag.contains(BwUpgrade.UPGRADE_TAG_KEY)) { + purchaseUpgrade(player, itemStack.copy()); + } else if (tag.contains(BwTrap.TRAP_TAG_KEY)) { + purchaseTrap(player, itemStack.copy()); + } + + } + } + return super.click(index, clickType, action); + } + + private void purchaseUpgrade(ServerPlayer player, ItemStack upgradeItem) { + BwTeam team = BwTeam.getPlayerTeam(player); + if (team == null) return; + + CompoundTag tag = upgradeItem.getOrCreateTag(); + String upgradesMapKey = tag.getString(BwUpgrade.UPGRADE_TAG_KEY); + BwUpgrade upgrade = team.upgrades.get(upgradesMapKey); + if (upgrade == null) return; + + if (upgrade.level >= upgrade.costs.length) { + BwShop.sendFail(player, "You have reached the maximum level of this upgrade"); + return; + } + if (player.inventory.countItem(Items.DIAMOND) < upgrade.costs[upgrade.level]) { + BwShop.sendFail(player, "You can't afford this upgrade"); + return; + } + + PlayerUtil.removeItem(player, Items.DIAMOND, upgrade.costs[upgrade.level]); + upgrade.level++; + this.resetLayout(team); + BwShop.playPurchaseSound(player, false); + PlayerUtil.broadcast(team.players, ChatFormat.brandColor1 + player.getScoreboardName() + + ChatFormat.brandColor2 + " has purchased " + + ChatFormat.brandColor1 + ChatFormat.removeColors(upgradeItem.getHoverName().getString()) + + " " + RomanNumbers.intToRoman(upgrade.level)); + } + + private void purchaseTrap(ServerPlayer player, ItemStack trapItem) { + BwTeam team = BwTeam.getPlayerTeam(player); + if (team == null) return; + + CompoundTag tag = trapItem.getOrCreateTag(); + String trapsMapKey = tag.getString(BwTrap.TRAP_TAG_KEY); + BwTrap trap = team.traps.get(trapsMapKey); + if (trap == null) return; + + if (trap.bought) { + BwShop.sendFail(player, "You have already purchased this trap."); + return; + } + int cost = getTrapCost(team); + if (player.inventory.countItem(Items.DIAMOND) < cost) { + BwShop.sendFail(player, "You can't afford this trap."); + return; + } + + PlayerUtil.removeItem(player, Items.DIAMOND, cost); + trap.bought = true; + this.resetLayout(team); + BwShop.playPurchaseSound(player, false); + PlayerUtil.broadcast(team.players, ChatFormat.brandColor1 + player.getScoreboardName() + + ChatFormat.brandColor2 + " has purchased " + + ChatFormat.brandColor1 + ChatFormat.removeColors(trapItem.getHoverName().getString())); + } + + static int rowColumnToGuiSlot(int row, int column) { + return (1 + row) * 9 + 1 + column; + } + + static void addUpgradeCostLore(ItemStack itemStack, BwUpgrade upgrade) { + if (upgrade.level >= upgrade.costs.length) { + ItemDisplayUtil.addLore(itemStack, "\247aMaximum Level Reached", 0); + ItemDisplayUtil.addGlint(itemStack); + } else { + int cost = upgrade.costs[upgrade.level]; + String lore = "\2477Cost:\247b " + cost + " Diamond"; + if (cost != 1) lore += "s"; + ItemDisplayUtil.addLore(itemStack, lore, 0); + } + } + + static void addTrapCostLore(ItemStack itemStack, BwTeam team, BwTrap trap) { + if (trap.bought) { + ItemDisplayUtil.addLore(itemStack, "\247aPurchased!", 0); + ItemDisplayUtil.addGlint(itemStack); + } else { + int cost = getTrapCost(team); + String lore = "\2477Cost:\247b " + cost + " Diamond"; + if (cost != 1) lore += "s"; + ItemDisplayUtil.addLore(itemStack, lore, 0); + } + } + + static void addStatusLore(ItemStack itemStack, BwUpgrade upgrade) { + String lore = "\2477Status:\247f Level " + upgrade.level + "/" + upgrade.costs.length; + ItemDisplayUtil.addLore(itemStack, lore, 1); + } + + static void addGenUpgradeText(ItemStack itemStack, BwTeam team) { + if (team == null || team.upgrades.get(BwUpgrade.UPGRADE_KEY_GENERATOR) == null) return; + + int currentLevel = team.upgrades.get(BwUpgrade.UPGRADE_KEY_GENERATOR).level; + + ItemDisplayUtil.addLore(itemStack, "", -1); + addGenUpgradeLore(itemStack, currentLevel, BwGen.ironDelays, "\247fIron"); + addGenUpgradeLore(itemStack, currentLevel, BwGen.goldDelays, "\2476Gold"); + addGenUpgradeLore(itemStack, currentLevel, BwGen.emeraldDelays, "\2472Emeralds"); + ItemDisplayUtil.addLore(itemStack, "", -1); + } + + private static void addGenUpgradeLore(ItemStack itemStack, int currentLevel, int[] delays, String currency) { + if (currentLevel < 0 || currentLevel + 1 >= delays.length) return; + + float originalDelay = delays[0]; + for (int delay : delays) { + if (delay != 0) { + originalDelay = delay; + break; + } + } + float currentDelay = delays[currentLevel]; + float nextDelay = delays[currentLevel+1]; + float extraSpeed; + + if (currentDelay == nextDelay) return; + if (currentDelay == 0) extraSpeed = 1; + else if (nextDelay == 0) extraSpeed = originalDelay / currentDelay; + else extraSpeed = (currentDelay / nextDelay - 1) * originalDelay / currentDelay; + + int extraPercentage = Math.round(extraSpeed * 100); + String line = "% " + currency; + if (extraPercentage < 0) line = "\n247e" + extraPercentage + line; + else line = "\247e+" + extraPercentage + line; + + ItemDisplayUtil.addLore(itemStack, line, -1); + } + + private static int getTrapCost(BwTeam team) { + int price = 1; + for (BwTrap trap : team.traps.values()) { + if (trap.bought) price *= 2; + } + return price; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/util/BwGen.java b/src/main/java/com/nexia/minigames/games/bedwars/util/BwGen.java new file mode 100644 index 00000000..450e97bb --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/util/BwGen.java @@ -0,0 +1,224 @@ +package com.nexia.minigames.games.bedwars.util; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.ItemStackUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import com.nexia.minigames.games.bedwars.upgrades.BwUpgrade; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.phys.AABB; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.function.Predicate; + +public class BwGen { + + public Item resource; + public long delay; + public long cap; + public EntityPos location; + public ArmorStand timerDisplay; + public ArmorStand itemDisplay; + + private BwGen(Item resource, long delay, long cap, EntityPos location) { + this.resource = resource; + this.delay = delay; + this.cap = cap; + this.location = location; + summonTimerDisplay(); + summonItemDisplay(); + } + + // Generator list --------------------------------------------------------------------- + + public static HashMap> allGens = resetGens(); + + public static HashMap> resetGens() { + HashMap> gens = new HashMap<>(); + + gens.put(Items.IRON_INGOT, new ArrayList<>()); + gens.put(Items.GOLD_INGOT, new ArrayList<>()); + gens.put(Items.DIAMOND, new ArrayList<>()); + gens.put(Items.EMERALD, new ArrayList<>()); + + allGens = gens; + return gens; + } + + // Gen actions ------------------------------------------------------------------ + + // Item generator tag format: "{genTagStart}_{item type}_{delay in ticks}_{max amount}", example: gen_diamond_20_48 + // Team generator tag format: "{genTagStart}_{team color}", example: gen_red + public static final String genTagStart = "gen_"; + public static final String itemCapTagStart = "itemCap_"; + public static final String genTimerDisplayTag = "bedWarsGenTimer"; + public static final String genItemDisplayTag = "bedWarsGenItemDisplay"; + private static long ticks = 0; + + public static final int[] upgradeCosts = {2, 4, 6, 8}; + + public static final int[] ironDelays = {30, 20, 15, 15, 12}; + public static final int[] goldDelays = {120, 80, 60, 60, 48}; + public static final int[] emeraldDelays = {0, 0, 0, 1200, 800}; + + public static void genTick() { + ticks++; + if (BwGame.isGameActive) { + itemGenTick(); + teamGenTick(); + } + } + + private static void itemGenTick() { + for (ArrayList list : allGens.values()) { + if (list == null) continue; + + for (BwGen gen : list) { + if (gen == null) continue; + gen.rotateItemDisplay(); + + // Spawn in the item + long nextItemIn = gen.delay - (ticks % gen.delay); + if (nextItemIn == gen.delay) { + summonGenItem(gen.location, new ItemStack(gen.resource), gen.cap); + } + // Change timer number + if (gen.timerDisplay != null && nextItemIn % 20 == 0) { + gen.timerDisplay.setCustomName(new TextComponent( + ChatFormat.brandColor1 + "Spawns in " + + ChatFormat.brandColor2 + nextItemIn / 20 + ChatFormat.brandColor1 + " seconds")); + } + } + } + } + + private static void teamGenTick() { + if (BwTeam.allTeams == null) return; + + for (BwTeam team : BwTeam.allTeams.values()) { + if (team == null || team.genLocation == null) continue; + + int level; + if (!team.upgrades.containsKey(BwUpgrade.UPGRADE_KEY_GENERATOR)) level = 1; + else level = team.upgrades.get(BwUpgrade.UPGRADE_KEY_GENERATOR).level; + + if (ironDelays.length > level && ironDelays[level] > 0 && ticks % ironDelays[level] == 0) { + summonGenItem(team.genLocation, new ItemStack(Items.IRON_INGOT), 48); + } + if (goldDelays.length > level && goldDelays[level] > 0 && ticks % goldDelays[level] == 0) { + summonGenItem(team.genLocation, new ItemStack(Items.GOLD_INGOT), 8); + } + if (emeraldDelays.length > level && emeraldDelays[level] > 0 && ticks % emeraldDelays[level] == 0) { + summonGenItem(team.genLocation, new ItemStack(Items.EMERALD), 2); + } + } + } + + private void rotateItemDisplay() { + double amount = 1.5 + 5 * (1 + Math.sin(Math.toRadians(3 * ticks))); + itemDisplay.yRot += (float)amount; + } + + private static void summonGenItem(EntityPos pos, ItemStack itemStack, long cap) { + ItemEntity entity = new ItemEntity(BwAreas.bedWarsWorld, pos.x, pos.y, pos.z, itemStack); + entity.setDeltaMovement(0, 0, 0); + entity.addTag(itemCapTagStart + cap); + BwAreas.bedWarsWorld.addFreshEntity(entity); + } + + public static void createGens() { + ticks = 1; + AABB aabb = new AABB(BwAreas.bedWarsCorner1, BwAreas.bedWarsCorner2); + Predicate predicate = o -> true; + + for (Entity entity : BwAreas.bedWarsWorld.getEntities(EntityType.ARMOR_STAND, aabb, predicate)) { + for (String tag : entity.getTags()) { + if (tag.startsWith(genTagStart)) { + addPossibleGen(entity, tag); + } + } + } + } + + private static void addPossibleGen(Entity entity, String tag) { + tag = tag.replaceFirst(genTagStart, ""); + + if (BwTeam.allTeams.containsKey(tag)) { + BwTeam.allTeams.get(tag).genLocation = new EntityPos(entity); + return; + } + + ArrayList splitTag = new ArrayList<>(Arrays.asList(tag.split("_"))); + if (splitTag.size() < 3) return; + + long cap = getLastNumber(splitTag); + if (cap < 0) return; + long delay = getLastNumber(splitTag); + if (delay < 0) return; + Item item = ItemStackUtil.itemFromString(StringUtils.join(splitTag, "_")); + if (!allGens.containsKey(item)) return; + + BwGen gen = new BwGen(item, delay, cap, new EntityPos(entity)); + allGens.get(item).add(gen); + } + + private void summonTimerDisplay() { + EntityPos pos = this.location; + ArmorStand armorStand = new ArmorStand(BwAreas.bedWarsWorld, pos.x, pos.y + 2.4, pos.z); + armorStand.setCustomNameVisible(true); + armorStand.addTag(genTimerDisplayTag); + makeInvisible(armorStand); + + BwAreas.bedWarsWorld.addFreshEntity(armorStand); + timerDisplay = armorStand; + } + + private void summonItemDisplay() { + EntityPos pos = this.location; + ArmorStand armorStand = new ArmorStand(BwAreas.bedWarsWorld, pos.x, pos.y + 1.8, pos.z); + armorStand.addTag(genItemDisplayTag); + makeInvisible(armorStand); + + Item item = Items.AIR; + if (resource == Items.DIAMOND) item = Items.DIAMOND_BLOCK; + else if (resource == Items.EMERALD) item = Items.EMERALD_BLOCK; + armorStand.setItemSlot(EquipmentSlot.HEAD, item.getDefaultInstance()); + + BwAreas.bedWarsWorld.addFreshEntity(armorStand); + itemDisplay = armorStand; + } + + private static void makeInvisible(ArmorStand armorStand) { + armorStand.setNoGravity(true); + armorStand.setInvisible(true); + + CompoundTag compoundTag = new CompoundTag(); + armorStand.addAdditionalSaveData(compoundTag); + compoundTag.putBoolean("Marker", true); + armorStand.readAdditionalSaveData(compoundTag); + } + + private static long getLastNumber(ArrayList splitTag) { + String stringCap = splitTag.get(splitTag.size() - 1); + if (!NumberUtils.isParsable(stringCap)) return -1; + long number = Math.round(NumberUtils.toDouble(stringCap)); + splitTag.remove(splitTag.size() - 1); + return number; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/util/BwPlayerTracker.java b/src/main/java/com/nexia/minigames/games/bedwars/util/BwPlayerTracker.java new file mode 100644 index 00000000..3d3237a3 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/util/BwPlayerTracker.java @@ -0,0 +1,97 @@ +package com.nexia.minigames.games.bedwars.util; + +import com.nexia.core.utilities.time.ServerTime; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.logging.log4j.LogManager; + +public class BwPlayerTracker { + + public static void trackerSecond() { + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + if (BwUtil.isBedWarsPlayer(player)) { + trackCompass(player); + } + } + } + + // Update all tracker compasses + public static void trackCompass(ServerPlayer player) { + + if (!BwUtil.isBedWarsPlayer(player)) return; + + Inventory inventory = player.inventory; + for (int i = 0; i < 36; i++) { + ItemStack itemStack = inventory.getItem(i); + if (!isTrackerCompass(itemStack)) continue; + trackClosestPlayer(player, itemStack); + } + + } + + static boolean isTrackerCompass(ItemStack itemStack) { + + if (!itemStack.getItem().toString().equals("compass")) return false; + + CompoundTag displayTag = itemStack.getTagElement("display"); + if (displayTag == null) return false; + + Tag lore = displayTag.get("Lore"); + if (lore == null || !lore.toString().contains("Bedwars Player Tracker")) return false; + + return true; + } + + static void trackClosestPlayer(ServerPlayer player, ItemStack itemStack) { + + MutablePair tracked = getClosestPlayer(player); + ServerPlayer trackedPlayer = tracked.left; + int distance = tracked.right.intValue(); + if (trackedPlayer == null) return; + + itemStack.setHoverName(new TextComponent( + "\247bTracking: " + trackedPlayer.getScoreboardName() + + "\2477 | " + "\247eDistance: " + distance)); + + BlockPos blockPos = new BlockPos(trackedPlayer.getX(), trackedPlayer.getY(), trackedPlayer.getZ()); + CompoundTag compoundTag = itemStack.getOrCreateTag(); + + compoundTag.put("LodestonePos", NbtUtils.writeBlockPos(blockPos)); + Level.RESOURCE_KEY_CODEC.encodeStart( + NbtOps.INSTANCE, player.level.dimension()).resultOrPartial(LogManager.getLogger()::error) + .ifPresent(tag -> compoundTag.put("LodestoneDimension", tag)); + compoundTag.putBoolean("LodestoneTracked", true); + + } + + static MutablePair getClosestPlayer(ServerPlayer player) { + ServerPlayer closestPlayer = null; + double closestPos = Double.MAX_VALUE; + + for (ServerPlayer trackable : player.getServer().getPlayerList().getPlayers()) { + if (BwUtil.isBedWarsPlayer(trackable) && !trackable.getUUID().equals(player.getUUID())) { + + double distance = (trackable.getX() - player.getX()) * (trackable.getX() - player.getX()) + + (trackable.getZ() - player.getZ()) * (trackable.getZ() - player.getZ()); + if (distance < closestPos) { + closestPlayer = trackable; + closestPos = distance; + } + + } + } + + closestPos = Math.sqrt(closestPos); + return new MutablePair<>(closestPlayer, closestPos); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/util/BwScoreboard.java b/src/main/java/com/nexia/minigames/games/bedwars/util/BwScoreboard.java new file mode 100644 index 00000000..1e5381b9 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/util/BwScoreboard.java @@ -0,0 +1,134 @@ +package com.nexia.minigames.games.bedwars.util; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.BlockUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.core.utilities.time.TickUtil; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.players.BwPlayers; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetScorePacket; +import net.minecraft.server.ServerScoreboard; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.Score; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; + +import java.util.ArrayList; + +public class BwScoreboard { + + private static final String objectiveName = "bedwars_sidebar"; + private static final TextComponent title = new TextComponent(ChatFormat.brandColor1 + "\247l Bedwars "); + + public static final int timerLine = 1; + + private static final Scoreboard scoreboard = new Scoreboard(); + private static final Objective objective = new Objective( + scoreboard, objectiveName, ObjectiveCriteria.DUMMY, + title, ObjectiveCriteria.RenderType.INTEGER); + + public static void setUpScoreboard() { + for (ServerPlayer player : BwPlayers.getPlayers()) { + sendBedWarsScoreboard(player); + } + updateScoreboard(); + } + + public static void sendBedWarsScoreboard(ServerPlayer player) { + player.connection.send(new ClientboundSetObjectivePacket(objective, 0)); + player.connection.send(new ClientboundSetDisplayObjectivePacket(1, objective)); + } + + public static void updateScoreboard() { + ServerLevel world = BwAreas.bedWarsWorld; + ArrayList lines = new ArrayList<>(); + + lines.add(" "); + for (BwTeam team : BwTeam.teamsInOrder) { + String statusDisplay = team.textColor + "\247l» \247r" + team.displayName + " "; + + if (team.players.size() < 1) { + statusDisplay += "\247c✘"; + } else if (team.bedLocation != null && BlockUtil.blockToText(world.getBlockState(team.bedLocation)).endsWith("_bed")) { + statusDisplay += "\247a✔"; + } else { + statusDisplay += "\247a" + team.players.size(); + } + + lines.add(statusDisplay); + } + lines.add(" "); + lines.add(" "); + + clearScoreboard(); + addLines(lines); + updateTimer(); + + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + sendLines(player); + } + } + + public static void updateTimer() { + int ticksLeft = BwGame.gameLength * 20 - BwGame.gameTicks - 1; + String[] timer = TickUtil.minuteTimeStamp(ticksLeft); + String line = "Time left: " + ChatFormat.brandColor2 + timer[0] + ":" + timer[1]; + BwScoreboard.replaceLine(BwScoreboard.timerLine, line); + } + + public static void replaceLine(int i, String newLine) { + for (Score score : scoreboard.getPlayerScores(objective)) { + if (score.getScore() != i) continue; + + String previous = score.getOwner(); + + scoreboard.resetPlayerScore(previous, objective); + scoreboard.getOrCreatePlayerScore(newLine, objective).setScore(i); + + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.REMOVE, objectiveName, previous, i)); + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.CHANGE, objectiveName, newLine, i)); + } + } + } + + private static void addLines(ArrayList lines) { + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + int scoreNumber = lines.size() - i; + + scoreboard.getOrCreatePlayerScore(line, objective).setScore(scoreNumber); + } + } + + public static void sendLines(ServerPlayer player) { + for (Score score : scoreboard.getPlayerScores(objective)) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.CHANGE, objectiveName, score.getOwner(), score.getScore())); + } + } + + private static void clearScoreboard() { + for (Score score : scoreboard.getPlayerScores(objective)) { + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.REMOVE, objectiveName, score.getOwner(), score.getScore())); + } + scoreboard.resetPlayerScore(score.getOwner(), objective); + } + } + + public static void removeScoreboardFor(ServerPlayer player) { + player.connection.send(new ClientboundSetDisplayObjectivePacket(1, null)); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/bedwars/util/BwUtil.java b/src/main/java/com/nexia/minigames/games/bedwars/util/BwUtil.java new file mode 100644 index 00000000..5312fe8c --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/bedwars/util/BwUtil.java @@ -0,0 +1,378 @@ +package com.nexia.minigames.games.bedwars.util; + +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.item.BlockUtil; +import com.nexia.core.utilities.player.PlayerDataManager; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.bedwars.BwGame; +import com.nexia.minigames.games.bedwars.areas.BwAreas; +import com.nexia.minigames.games.bedwars.custom.BwExplosiveSlime; +import com.nexia.minigames.games.bedwars.players.BwPlayers; +import com.nexia.minigames.games.bedwars.players.BwTeam; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerScoreboard; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.Mth; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.item.PrimedTnt; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.LargeFireball; +import net.minecraft.world.item.*; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + +import java.util.*; + +public class BwUtil { + + static HashMap explosiveCooldown = new HashMap<>(); + + public static void utilTick() { + invisibilityTick(); + explosionTick(); + } + + private static void invisibilityTick() { + for (Iterator it = BwGame.invisiblePlayerArmor.keySet().iterator(); it.hasNext(); ) { + ServerPlayer player = it.next(); + invisArmorCheck(player); + if (!player.hasEffect(MobEffects.INVISIBILITY)) { + regainInvisArmor(player); + it.remove(); + } + } + } + + private static void explosionTick() { + for (Iterator it = explosiveCooldown.keySet().iterator(); it.hasNext(); ) { + UUID uuid = it.next(); + explosiveCooldown.replace(uuid, explosiveCooldown.get(uuid) - 1); + if (explosiveCooldown.get(uuid) <= 0) { + it.remove(); + } + } + } + + public static boolean skipQueue() { + if (BwGame.queueCountdown > 1) { + BwGame.queueCountdown = 1; + return true; + } + return false; + } + + public static void createSpectatorTeam() { + MinecraftServer server = ServerTime.minecraftServer; + ServerScoreboard scoreboard = server.getScoreboard(); + + String teamName = "bw_spectator"; + PlayerTeam playerTeam = scoreboard.getPlayerTeam(teamName); + if (scoreboard.getPlayerTeam(teamName) == null) { + playerTeam = scoreboard.addPlayerTeam(teamName); + } + + playerTeam.setDisplayName(new TextComponent(teamName)); + playerTeam.setPlayerPrefix(new TextComponent("\2477\247lBW " )); + + server.getCommands().performCommand(server.createCommandSourceStack(), + "team modify " + teamName + " color gray"); + + BwGame.spectatorTeam = playerTeam; + } + + public static void throwSlime(ServerPlayer player, ItemStack itemStack) { + if (explosiveCooldown.containsKey(player.getUUID())) return; + + new BwExplosiveSlime(EntityType.SLIME, player); + itemStack.shrink(1); + + explosiveCooldown.put(player.getUUID(), 10); + } + + public static void throwFireball(ServerPlayer player, ItemStack itemStack) { + if (explosiveCooldown.containsKey(player.getUUID())) return; + + Level level = player.level; + Vec3 pos = player.position().add(0, 1, 0); + Vec3 angle = player.getLookAngle(); + float speed = 0.5f; + + LargeFireball fireball = new LargeFireball(level, player, angle.x, angle.y, angle.z); + fireball.teleportTo(pos.x, pos.y, pos.z); + fireball.setDeltaMovement(new Vec3(angle.x*speed, angle.y*speed, angle.z*speed)); + fireball.explosionPower = 2; + + level.addFreshEntity(fireball); + itemStack.setCount(itemStack.getCount() - 1); + explosiveCooldown.put(player.getUUID(), 10); + } + + private static void invisArmorCheck(ServerPlayer player) { + ItemStack[] storedArmor = BwGame.invisiblePlayerArmor.get(player); + if (storedArmor == null) return; + List currentArmor = player.inventory.armor; + + for (int i = 0; i < currentArmor.size() && i < storedArmor.length; i++) { + if (!currentArmor.get(i).isEmpty()) { + storedArmor[i] = currentArmor.get(i); + player.inventory.setItem(36 + i, ItemStack.EMPTY); + } + } + } + + private static void regainInvisArmor(ServerPlayer player) { + ItemStack[] armor = BwGame.invisiblePlayerArmor.get(player); + if (armor == null) return; + + for (int i = 0; i < armor.length; i++) { + player.inventory.setItem(36 + i, armor[i]); + } + } + + // Fuse tnt when placed in bedwars + public static boolean placeTnt(Player player, BlockPlaceContext blockPlaceContext) { + if (!blockPlaceContext.getItemInHand().getItem().toString().equals("tnt")) return false; + + ItemStack itemStack = blockPlaceContext.getItemInHand(); + if (!player.abilities.instabuild) itemStack.shrink(1); + explodeTnt(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos()); + return true; + } + + // Summon fused tnt + private static void explodeTnt(Level level, BlockPos blockPos) { + if (level.isClientSide) return; + PrimedTnt primedTnt = new PrimedTnt(level, (double)blockPos.getX() + 0.5, blockPos.getY(), (double)blockPos.getZ() + 0.5, null); + primedTnt.setFuse(60); + level.addFreshEntity(primedTnt); + level.playSound(null, primedTnt.getX(), primedTnt.getY(), primedTnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0f, 1.0f); + } + + public static boolean dropResources(BlockState blockState) { + return !BlockUtil.blockToText(blockState).endsWith("_bed"); + } + + public static boolean canDropItem(ItemStack itemStack) { + Item item = itemStack.getItem(); + if (item instanceof PickaxeItem || item instanceof AxeItem || item instanceof ShearsItem) { + return false; + } + if (BwUtil.isDefaultSword(itemStack)) { + return false; + } + return true; + } + + public static float playerArmorCalculation(ServerPlayer player, DamageSource damageSource, float damage) { + + if (damageSource.getEntity() instanceof ServerPlayer) { + ServerPlayer attacker = (ServerPlayer) damageSource.getEntity(); + float crit = PlayerUtil.couldCrit(attacker) ? 1.5f : 1f; + float nonCritDamage = damage / crit; + + // Nerf stronger weapons + float strongDamageNerf = 0.75f; // 1 if vanilla, less to nerf, more to buff + float strongDamage = 4f; // At what point a weapon is considered strong + + if (nonCritDamage > strongDamage) { + nonCritDamage = strongDamage * (1 - strongDamageNerf) + strongDamageNerf * nonCritDamage; + } + + damage = nonCritDamage * crit; + } + + if (!damageSource.isBypassArmor()) { + // Calculation without armor penetration + float protection = Mth.clamp(player.getArmorValue(), 0, 20); + damage = damage * (1.0f - protection / 25.0f); + // Make fights longer by buffing armor + damage *= 0.9f; + } + return damage; + } + + public static float playerProtCalculation(float originalDamage, float protection) { + protection = Mth.clamp(protection, 0.0f, 20.0f); + return originalDamage * (1.0f - protection / 48.0f); + } + + public static float getPearlDamage() { + return 0f; + } + + public static float modifyBlockExplosionRes(BlockPos blockPos, BlockState blockState, Entity source, float resistance) { + if (source instanceof PrimedTnt) resistance *= 1.125F; + if (source instanceof BwExplosiveSlime) resistance *= 2f; + + if (BwAreas.isImmuneBlock(blockPos)) { + if (resistance < 3F) resistance = 3F; + + } else if (blockState.getBlock() == Blocks.END_STONE) { + if (source instanceof LargeFireball || source instanceof BwExplosiveSlime) resistance = 1200F; + else resistance *= 0.75F; + + } else if (BlockUtil.blockToText(blockState).endsWith("_wool")) { + resistance *= 0.75F; + + } else if (BlockUtil.blockToText(blockState).endsWith("_stained_glass")) { + resistance = 1200F; + } + + return resistance; + } + + public static boolean shouldExplode(BlockPos blockPos, BlockState blockState) { + return !(BwAreas.isImmuneBlock(blockPos) || blockState.getBlock() instanceof BedBlock); + } + + public static float getExplosionDamage(float original) { + return 0.2F * original; + } + + public static void modifyExplosionKb(Entity source, Args kb) { + double horizontal = 1; + double vertical = 1; + + // Arg 0 is x, arg 1 is y, arg 2 is z + if (source instanceof LargeFireball) { + vertical *= 1.5f; + } else if (source instanceof PrimedTnt) { + horizontal *= 3.5f; + vertical *= 1.5f; + } else if (source instanceof BwExplosiveSlime) { + vertical *= 1.75f; + horizontal *= 1.75f; + } + + kb.set(0, (double)kb.get(0) * horizontal); + kb.set(1, (double)kb.get(1) * vertical); + kb.set(2, (double)kb.get(2) * horizontal); + } + + public static void setAttackSpeed(ServerPlayer player) { + UUID hasteUuid = UUID.fromString("AF8B6E3F-3328-4C0A-AA36-5BA2BB9DBEF3"); + AttributeInstance attackSpeed = player.getAttribute(Attributes.ATTACK_SPEED); + AttributeModifier modifier = attackSpeed.getModifier(hasteUuid); + + if (modifier != null) { + attackSpeed.removeModifier(hasteUuid); + } + player.connection.send(new ClientboundUpdateAttributesPacket( + player.getId(), player.getAttributes().getSyncableAttributes())); + } + + public static void giveKillResources(ServerPlayer victim) { + LivingEntity killCredit = victim.getKillCredit(); + if (killCredit instanceof ServerPlayer attacker) { + Inventory inventory = victim.inventory; + for (int i = 0; i < 36; i++) { + ItemStack itemStack = inventory.getItem(i); + if (isBedWarsCurrency(itemStack)) { + attacker.inventory.add(itemStack); + } + } + } + } + + public static void deathClearInventory(ServerPlayer player) { + Inventory inventory2 = player.inventory; + for (int i = 0; i < inventory2.items.size(); i++) { + if (!isTool(inventory2.items.get(i))) { + inventory2.items.set(i, new ItemStack(Items.AIR)); + } + } + if (!isTool(inventory2.offhand.get(0))) inventory2.offhand.set(0, new ItemStack(Items.AIR)); + } + + public static void afterItemMerge(ItemEntity itemEntity) { + for (String tag : itemEntity.getTags()) { + if (!tag.startsWith(BwGen.itemCapTagStart)) continue; + try { + int cap = Integer.parseInt(tag.replace(BwGen.itemCapTagStart, "")); + if (itemEntity.getItem().getCount() > cap) { + itemEntity.getItem().setCount(cap); + } + } catch (Exception e) { + break; + } + + } + } + + public static void announceDeath(ServerPlayer player) { + String mainColor = ChatFormat.chatColor2; + String message = mainColor + player.getCombatTracker().getDeathMessage().getString(); + + message = replaceDisplayName(message, mainColor, player); + + Entity killCredit = player.getKillCredit(); + if (killCredit instanceof ServerPlayer attacker) { + message = replaceDisplayName(message, mainColor, attacker); + } + + PlayerUtil.broadcast(BwPlayers.getViewers(), message); + } + + public static String replaceDisplayName(String message, String mainColor, ServerPlayer player) { + if (player == null) return message; + + BwTeam team = BwTeam.getPlayerTeam(player); + if (team == null) return message; + + return message.replace(player.getDisplayName().getString(), + team.textColor + player.getScoreboardName() + mainColor); + } + + public static float getFireballInertia() { + return 0.875f; + } + + public static boolean isDefaultSword(ItemStack itemStack) { + return itemStack != null && itemStack.getItem() == Items.STONE_SWORD; + } + + static boolean isTool(ItemStack itemStack) { + String itemString = itemStack.getItem().toString(); + return itemString.endsWith("axe") || itemStack.getItem() == Items.SHEARS; + } + + public static boolean isBedWarsCurrency(ItemStack itemStack) { + Item item = itemStack.getItem(); + return item == Items.IRON_INGOT || item == Items.GOLD_INGOT || item == Items.DIAMOND || item == Items.EMERALD; + } + + public static boolean isInBedWars(ServerPlayer player) { + return PlayerDataManager.get(player).gameMode == PlayerGameMode.BEDWARS; + } + + public static boolean isBedWarsPlayer(ServerPlayer player) { + if (!isInBedWars(player)) return false; + for (ServerPlayer serverPlayer : BwPlayers.getPlayers()) { + if (serverPlayer.getUUID().equals(player.getUUID())) return true; + } + return false; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/duels/DuelGameMode.java b/src/main/java/com/nexia/minigames/games/duels/DuelGameMode.java new file mode 100644 index 00000000..5af05f2a --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/DuelGameMode.java @@ -0,0 +1,40 @@ +package com.nexia.minigames.games.duels; + +import net.minecraft.server.level.ServerPlayer; + +import java.util.ArrayList; + +public class DuelGameMode { + String id; + + public static String[] duels = {"LEAVE", "AXE", "SWORD_ONLY", "TRIDENT_ONLY", "HOE_ONLY", "FFA", "BOW_ONLY", "UHC", "VANILLA"}; + + public static ArrayList AXE_QUEUE = new ArrayList<>(); + public static final DuelGameMode AXE = new DuelGameMode("axe"); + + public static ArrayList SWORD_ONLY_QUEUE = new ArrayList<>(); + public static final DuelGameMode SWORD_ONLY = new DuelGameMode("sword_only"); + + public static ArrayList TRIDENT_ONLY_QUEUE = new ArrayList<>(); + public static final DuelGameMode TRIDENT_ONLY = new DuelGameMode("trident_only"); + + public static ArrayList HOE_ONLY_QUEUE = new ArrayList<>(); + public static final DuelGameMode HOE_ONLY = new DuelGameMode("hoe_only"); + + public static ArrayList BOW_ONLY_QUEUE = new ArrayList<>(); + public static final DuelGameMode BOW_ONLY = new DuelGameMode("bow"); + + public static ArrayList VANILLA_QUEUE = new ArrayList<>(); + public static final DuelGameMode VANILLA = new DuelGameMode("vanilla"); + + public static ArrayList UHC_QUEUE = new ArrayList<>(); + public static final DuelGameMode UHC = new DuelGameMode("uhc"); + + public static ArrayList FFA_QUEUE = new ArrayList<>(); + public static final DuelGameMode FFA = new DuelGameMode("ffa"); + + public static final DuelGameMode LOBBY = new DuelGameMode("lobby"); + public DuelGameMode(String id) { + this.id = id; + } +} diff --git a/src/main/java/com/nexia/minigames/games/duels/DuelsGame.java b/src/main/java/com/nexia/minigames/games/duels/DuelsGame.java new file mode 100644 index 00000000..40c36a8e --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/DuelsGame.java @@ -0,0 +1,184 @@ +package com.nexia.minigames.games.duels; + +import com.nexia.core.Main; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.misc.NxFileUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.duels.gamemodes.GamemodeHandler; +import com.nexia.minigames.games.duels.util.player.PlayerData; +import com.nexia.minigames.games.duels.util.player.PlayerDataManager; +import net.minecraft.Util; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.level.GameType; + +import static com.nexia.minigames.games.duels.gamemodes.GamemodeHandler.removeQueue; + +public class DuelsGame { + + public static final String duelsDirectory = NxFileUtil.addConfigDir("duels"); + + public static void leave(ServerPlayer player){ + if(player.getLastDamageSource() != null && player.getLastDamageSource().getEntity() instanceof ServerPlayer){ + DuelsGame.death(player, player.getLastDamageSource()); + return; + } + PlayerData data = PlayerDataManager.get(player); + data.isLeaving = true; + data.inviting = false; + data.inDuel = false; + data.duelPlayer = null; + removeQueue(player, data.gameMode.id, true); + data.gameMode = DuelGameMode.LOBBY; + } + + public static void starting(){ + DuelGameMode.AXE_QUEUE.clear(); + DuelGameMode.SWORD_ONLY_QUEUE.clear(); + DuelGameMode.FFA_QUEUE.clear(); + DuelGameMode.TRIDENT_ONLY_QUEUE.clear(); + DuelGameMode.HOE_ONLY_QUEUE.clear(); + DuelGameMode.UHC_QUEUE.clear(); + DuelGameMode.BOW_ONLY_QUEUE.clear(); + DuelGameMode.VANILLA_QUEUE.clear(); + + for(ServerLevel level : ServerTime.minecraftServer.getAllLevels()){ + String[] split = level.dimension().toString().replaceAll("]", "").split(":"); + if(split[1].toLowerCase().contains("duels") && !split[2].toLowerCase().contains("hub")){ + //GamemodeHandler.deleteWorld(split[2]); + Main.server.sendMessage(new TextComponent(split[0]), Util.NIL_UUID); + Main.server.sendMessage(new TextComponent(split[1]), Util.NIL_UUID); + Main.server.sendMessage(new TextComponent(split[2]), Util.NIL_UUID); + } + } + } + + public static String returnCommandMap(String mapname){ + int[] pos = new int[]{0, 0, 0}; + if(mapname.equalsIgnoreCase("desert")){ + pos[0] = -80; + pos[1] = -45; + pos[2] = -80; + } else if(mapname.equalsIgnoreCase("city")){ + pos[0] = -45; + pos[1] = -13; + pos[2] = -30; + } else if(mapname.equalsIgnoreCase("nethflat") || mapname.equalsIgnoreCase(("netheriteflat"))) { + pos[0] = -35; + pos[1] = -3; + pos[2] = -50; + } else if(mapname.equalsIgnoreCase("plains")) { + pos[0] = -30; + pos[1] = -20; + pos[2] = -39; + } else if(mapname.equalsIgnoreCase("sky")){ + pos[0] = -33; + pos[1] = -6; + pos[2] = -19; + } + + return "setblock 0 80 0 minecraft:structure_block{mode:'LOAD',name:'duels:" + mapname.toLowerCase() + "'" + ",posX:" + pos[0] + ",posY:" + pos[1] + ",posZ:" + pos[2] + "}"; + } + + public static void death(ServerPlayer victim, DamageSource source){ + if((source != null && source.getEntity() instanceof ServerPlayer attacker) && (victim != null)){ + PlayerData victimData = PlayerDataManager.get(victim); + PlayerData attackerData = PlayerDataManager.get(attacker); + + if((victimData.inDuel && attackerData.inDuel) && (victimData.duelPlayer.getStringUUID().equalsIgnoreCase(attacker.getStringUUID())) && attackerData.duelPlayer.getStringUUID().equalsIgnoreCase(victim.getStringUUID())){ + ServerLevel duelLevel = attacker.getLevel(); + + if(!victimData.isLeaving){ + victim.teleportTo(DuelsSpawn.duelWorld, DuelsSpawn.spawn.x, DuelsSpawn.spawn.y, DuelsSpawn.spawn.z, DuelsSpawn.spawn.yaw, DuelsSpawn.spawn.pitch); + victim.setRespawnPosition(DuelsSpawn.duelWorld.dimension(), DuelsSpawn.spawn.toBlockPos(), DuelsSpawn.spawn.yaw, true, false); + } + victimData.isLeaving = false; + victimData.inviting = false; + victimData.inDuel = false; + victimData.duelPlayer = null; + removeQueue(victim, victimData.gameMode.id, true); + victimData.gameMode = DuelGameMode.LOBBY; + + if(!attackerData.isLeaving) { + attacker.teleportTo(DuelsSpawn.duelWorld, DuelsSpawn.spawn.x, DuelsSpawn.spawn.y, DuelsSpawn.spawn.z, DuelsSpawn.spawn.yaw, DuelsSpawn.spawn.pitch); + attacker.setRespawnPosition(DuelsSpawn.duelWorld.dimension(), DuelsSpawn.spawn.toBlockPos(), DuelsSpawn.spawn.yaw, true, false); + } + attackerData.isLeaving = false; + attackerData.inviting = false; + attackerData.inDuel = false; + attackerData.duelPlayer = null; + removeQueue(attacker, attackerData.gameMode.id, true); + attackerData.gameMode = DuelGameMode.LOBBY; + + attackerData.savedData.wins++; + victimData.savedData.loss++; + + attacker.sendMessage(ChatFormat.format("{b2}{} {b1}has won the duel!", attacker.getScoreboardName()), Util.NIL_UUID); + victim.sendMessage(ChatFormat.format("{b2}{} {b1}has won the duel!", attacker.getScoreboardName()), Util.NIL_UUID); + + PlayerUtil.resetHealthStatus(attacker); + PlayerUtil.resetHealthStatus(victim); + + attacker.inventory.clearContent(); + victim.inventory.clearContent(); + + attacker.setGameMode(GameType.ADVENTURE); + victim.setGameMode(GameType.ADVENTURE); + + GamemodeHandler.deleteWorld(duelLevel.dimension().toString().replaceAll("]", "").split(":")[2]); + } + } + if(((source == null) || (!(source.getEntity() instanceof ServerPlayer))) && (victim != null && PlayerDataManager.get(victim).duelPlayer != null)) { + PlayerData victimData = PlayerDataManager.get(victim); + ServerPlayer attacker = victimData.duelPlayer; + PlayerData attackerData = PlayerDataManager.get(attacker); + + if ((victimData.inDuel && attackerData.inDuel) && (victimData.duelPlayer.getStringUUID().equalsIgnoreCase(attacker.getStringUUID())) && attackerData.duelPlayer.getStringUUID().equalsIgnoreCase(victim.getStringUUID())) { + ServerLevel duelLevel = attacker.getLevel(); + + if(!victimData.isLeaving){ + victim.teleportTo(DuelsSpawn.duelWorld, DuelsSpawn.spawn.x, DuelsSpawn.spawn.y, DuelsSpawn.spawn.z, DuelsSpawn.spawn.yaw, DuelsSpawn.spawn.pitch); + victim.setRespawnPosition(DuelsSpawn.duelWorld.dimension(), DuelsSpawn.spawn.toBlockPos(), DuelsSpawn.spawn.yaw, true, false); + } + victimData.isLeaving = false; + victimData.inviting = false; + victimData.inDuel = false; + victimData.duelPlayer = null; + removeQueue(victim, victimData.gameMode.id, true); + victimData.gameMode = DuelGameMode.LOBBY; + + if(!attackerData.isLeaving){ + attacker.teleportTo(DuelsSpawn.duelWorld, DuelsSpawn.spawn.x, DuelsSpawn.spawn.y, DuelsSpawn.spawn.z, DuelsSpawn.spawn.yaw, DuelsSpawn.spawn.pitch); + attacker.setRespawnPosition(DuelsSpawn.duelWorld.dimension(), DuelsSpawn.spawn.toBlockPos(), DuelsSpawn.spawn.yaw, true, false); + } + attackerData.isLeaving = false; + attackerData.inviting = false; + attackerData.inDuel = false; + attackerData.duelPlayer = null; + removeQueue(attacker, attackerData.gameMode.id, true); + attackerData.gameMode = DuelGameMode.LOBBY; + + attacker.sendMessage(ChatFormat.format("{b2}{} {b1}has won the duel!", attacker.getScoreboardName()), Util.NIL_UUID); + victim.sendMessage(ChatFormat.format("{b2}{} {b1}has won the duel!", attacker.getScoreboardName()), Util.NIL_UUID); + + attackerData.savedData.wins++; + victimData.savedData.loss++; + + attacker.setGameMode(GameType.ADVENTURE); + victim.setGameMode(GameType.ADVENTURE); + + attacker.inventory.clearContent(); + victim.inventory.clearContent(); + + PlayerUtil.resetHealthStatus(attacker); + PlayerUtil.resetHealthStatus(victim); + + GamemodeHandler.deleteWorld(duelLevel.dimension().toString().replaceAll("]", "").split(":")[2]); + } + } + + } +} diff --git a/src/main/java/com/nexia/minigames/games/duels/DuelsSpawn.java b/src/main/java/com/nexia/minigames/games/duels/DuelsSpawn.java new file mode 100644 index 00000000..e3a0fd6b --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/DuelsSpawn.java @@ -0,0 +1,32 @@ +package com.nexia.minigames.games.duels; + +import com.nexia.core.utilities.pos.EntityPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; + +public class DuelsSpawn { + public static ServerLevel duelWorld = null; + public static EntityPos spawn = new EntityPos(0, 80, 0, 0, 0); + + public DuelsSpawn() { + } + + public static boolean isDuelsWorld(Level level) { + return level.dimension().toString().contains("duels:hub"); + } + + public static boolean isInHub(Player player) { + return player.level == duelWorld; + } + + public static void setDuelWorld(MinecraftServer server) { + for (ServerLevel level : server.getAllLevels()) { + if (isDuelsWorld(level)) { + duelWorld = level; + break; + } + } + } +} diff --git a/src/main/java/com/nexia/minigames/games/duels/gamemodes/GamemodeHandler.java b/src/main/java/com/nexia/minigames/games/duels/gamemodes/GamemodeHandler.java new file mode 100644 index 00000000..6a6bb9e9 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/gamemodes/GamemodeHandler.java @@ -0,0 +1,353 @@ +package com.nexia.minigames.games.duels.gamemodes; + +import com.nexia.core.Main; +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.misc.RandomUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.pos.EntityPos; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.games.duels.DuelGameMode; +import com.nexia.minigames.games.duels.DuelsGame; +import com.nexia.minigames.games.duels.DuelsSpawn; +import com.nexia.minigames.games.duels.util.player.PlayerData; +import com.nexia.minigames.games.duels.util.player.PlayerDataManager; +import net.minecraft.Util; +import net.minecraft.core.Registry; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.GameType; +import org.jetbrains.annotations.Nullable; +import xyz.nucleoid.fantasy.RuntimeWorldConfig; + +import java.util.UUID; + +public class GamemodeHandler { + + public static DuelGameMode identifyGamemode(String gameMode){ + if(gameMode.equalsIgnoreCase("axe")){ + return DuelGameMode.AXE; + } + + if(gameMode.equalsIgnoreCase("bow_only")){ + return DuelGameMode.BOW_ONLY; + } + + if(gameMode.equalsIgnoreCase("vanilla")){ + return DuelGameMode.VANILLA; + } + + if(gameMode.equalsIgnoreCase("sword_only")){ + return DuelGameMode.SWORD_ONLY; + } + + if(gameMode.equalsIgnoreCase("ffa")){ + return DuelGameMode.FFA; + } + + if(gameMode.equalsIgnoreCase("hoe_only")){ + return DuelGameMode.HOE_ONLY; + } + + if(gameMode.equalsIgnoreCase("uhc")){ + return DuelGameMode.UHC; + } + + if(gameMode.equalsIgnoreCase("trident_only")){ + return DuelGameMode.TRIDENT_ONLY; + } + + if(gameMode.equalsIgnoreCase("lobby") || gameMode.equalsIgnoreCase("leave")){ + return DuelGameMode.LOBBY; + } + return null; + } + + public static void joinQueue(ServerPlayer player, String stringGameMode, boolean silent){ + DuelGameMode gameMode = GamemodeHandler.identifyGamemode(stringGameMode); + + if(gameMode == null){ + if(!silent){ + player.sendMessage(ChatFormat.formatFail("Invalid gamemode!"), Util.NIL_UUID); + } + return; + } + + if(gameMode == DuelGameMode.LOBBY){ + LobbyUtil.sendGame(player, "duels", false); + return; + } + + if(!silent){ + player.sendMessage(ChatFormat.format("{b1}You have queued up for {b2}{}{b1}.", stringGameMode.toUpperCase()), Util.NIL_UUID); + } + + removeQueue(player, stringGameMode, true); + + + if(gameMode == DuelGameMode.AXE){ + DuelGameMode.AXE_QUEUE.add(player); + if(DuelGameMode.AXE_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.AXE_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.SWORD_ONLY){ + DuelGameMode.SWORD_ONLY_QUEUE.add(player); + if(DuelGameMode.SWORD_ONLY_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.SWORD_ONLY_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.TRIDENT_ONLY){ + DuelGameMode.TRIDENT_ONLY_QUEUE.add(player); + if(DuelGameMode.TRIDENT_ONLY_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.TRIDENT_ONLY_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.HOE_ONLY){ + DuelGameMode.HOE_ONLY_QUEUE.add(player); + if(DuelGameMode.HOE_ONLY_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.HOE_ONLY_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.BOW_ONLY){ + DuelGameMode.BOW_ONLY_QUEUE.add(player); + if(DuelGameMode.BOW_ONLY_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.BOW_ONLY_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.VANILLA){ + DuelGameMode.VANILLA_QUEUE.add(player); + if(DuelGameMode.VANILLA_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.VANILLA_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.UHC){ + DuelGameMode.UHC_QUEUE.add(player); + if(DuelGameMode.UHC_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.UHC_QUEUE.get(0), stringGameMode, null,false); + } + } + + if(gameMode == DuelGameMode.FFA){ + DuelGameMode.FFA_QUEUE.add(player); + if(DuelGameMode.FFA_QUEUE.size() >= 2){ + GamemodeHandler.joinGamemode(player, DuelGameMode.FFA_QUEUE.get(0), stringGameMode, null,false); + } + } + } + + public static void removeQueue(ServerPlayer player, String stringGameMode, boolean silent){ + DuelGameMode gameMode = GamemodeHandler.identifyGamemode(stringGameMode); + if(gameMode == null){ + if(!silent){ + player.sendMessage(ChatFormat.formatFail("Invalid gamemode!"), Util.NIL_UUID); + } + return; + } + + if(!silent){ + player.sendMessage(ChatFormat.format("{b1}You have left the queue for {b2}{}{b1}.", stringGameMode.toUpperCase()), Util.NIL_UUID); + } + + if(gameMode == DuelGameMode.AXE){ + DuelGameMode.AXE_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.BOW_ONLY) { + DuelGameMode.BOW_ONLY_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.VANILLA) { + DuelGameMode.VANILLA_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.SWORD_ONLY){ + DuelGameMode.SWORD_ONLY_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.FFA){ + DuelGameMode.FFA_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.UHC){ + DuelGameMode.UHC_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.TRIDENT_ONLY){ + DuelGameMode.TRIDENT_ONLY_QUEUE.remove(player); + } + + if(gameMode == DuelGameMode.HOE_ONLY){ + DuelGameMode.HOE_ONLY_QUEUE.remove(player); + } + + } + + public static void challengePlayer(ServerPlayer executor, ServerPlayer player, String stringGameMode, @Nullable String selectedmap){ + DuelGameMode gameMode = GamemodeHandler.identifyGamemode(stringGameMode); + if(gameMode == null){ + executor.sendMessage(ChatFormat.formatFail("Invalid gamemode!"), Util.NIL_UUID); + return; + } + if(executor == player) { + executor.sendMessage(ChatFormat.formatFail("You cannot duel yourself!"), Util.NIL_UUID); + return; + } + + PlayerData executorData = PlayerDataManager.get(executor); + PlayerData playerData = PlayerDataManager.get(player); + + if(executorData.inDuel || playerData.inDuel) { + executor.sendMessage(ChatFormat.formatFail("The player is currently dueling someone."), Util.NIL_UUID); + return; + } + + String map = selectedmap; + if(map == null) { + map = com.nexia.minigames.Main.config.duelsMaps.get(RandomUtil.randomInt(0, com.nexia.minigames.Main.config.duelsMaps.size()-1)); + } else { + map = selectedmap; + if (!com.nexia.minigames.Main.config.duelsMaps.contains(map.toLowerCase())) { + executor.sendMessage(ChatFormat.formatFail("Invalid map!"), Util.NIL_UUID); + return; + } + } + + executorData.inviteMap = map; + executorData.inviting = true; + executorData.invitingPlayer = player; + + if(playerData.inviting && playerData.invitingPlayer != null && playerData.invitingPlayer == executor && executorData.inviteMap.equalsIgnoreCase(playerData.inviteMap)){ + GamemodeHandler.joinGamemode(executor, player, stringGameMode, map, true); + } else if(!executorData.inviteMap.equalsIgnoreCase(playerData.inviteMap) && (playerData.invitingPlayer == null || !playerData.invitingPlayer.getStringUUID().equalsIgnoreCase(executor.getStringUUID())) && playerData.gameMode == DuelGameMode.LOBBY){ + player.sendMessage(ChatFormat.format("{b1}Dueled {b2}{} {b1}on map {b2} {b1}with kit {}", player.getScoreboardName(), map, stringGameMode), Util.NIL_UUID); + TextComponent message = new TextComponent(ChatFormat.brandColor2 + executor.getScoreboardName() + ChatFormat.brandColor1 + " has challenged you to a duel!"); + + TextComponent kit = new TextComponent(ChatFormat.brandColor2 + "Kit: " + ChatFormat.brandColor1 + stringGameMode.toUpperCase()); + TextComponent mapName = new TextComponent(ChatFormat.brandColor2 + "Map: " + ChatFormat.brandColor1 + map.toUpperCase()); + + TextComponent yes = new TextComponent("§8[§aACCEPT§8] "); + yes.withStyle(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/duel " + executor.getScoreboardName() + " " + stringGameMode + " " + selectedmap))); + + TextComponent no = new TextComponent("§8[§cIGNORE§8]"); + + TextComponent option = new TextComponent(""); + + option.append(yes); + option.append(no); + + player.sendMessage(message, Util.NIL_UUID); + player.sendMessage(kit, Util.NIL_UUID); + player.sendMessage(mapName, Util.NIL_UUID); + player.sendMessage(option, Util.NIL_UUID); + } + } + public static void joinGamemode(ServerPlayer invitor, ServerPlayer player, String stringGameMode, @Nullable String selectedmap, boolean silent){ + DuelGameMode gameMode = GamemodeHandler.identifyGamemode(stringGameMode); + if(gameMode == null){ + if(!silent){ + invitor.sendMessage(ChatFormat.formatFail("Invalid gamemode!"), Util.NIL_UUID); + } + return; + } + + ServerLevel duelLevel = createWorld(); + String map = selectedmap; + if(map == null){ + map = com.nexia.minigames.Main.config.duelsMaps.get(RandomUtil.randomInt(0, com.nexia.minigames.Main.config.duelsMaps.size()-1)); + } + String name = duelLevel.dimension().toString().replaceAll("]", "").split(":")[2]; + + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), "/execute in duels:" + name + " run forceload add 0 0"); + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), "/execute in duels:" + name + " run " + DuelsGame.returnCommandMap(map)); + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), "/execute in duels:" + name + " run setblock 1 80 0 minecraft:redstone_block"); + + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), "/execute in duels:" + name + " if block 0 80 0 minecraft:structure_block run setblock 0 80 0 air"); + Main.server.getCommands().performCommand(Main.server.createCommandSourceStack(), "/execute in duels:" + name + " if block 1 80 0 minecraft:redstone_block run setblock 1 80 0 air"); + + + PlayerData invitorData = PlayerDataManager.get(invitor); + PlayerData playerData = PlayerDataManager.get(player); + + player.removeTag(LobbyUtil.NO_DAMAGE_TAG); + player.removeTag(LobbyUtil.NO_FALL_DAMAGE_TAG); + + invitor.removeTag(LobbyUtil.NO_DAMAGE_TAG); + invitor.removeTag(LobbyUtil.NO_FALL_DAMAGE_TAG); + + PlayerUtil.resetHealthStatus(player); + PlayerUtil.resetHealthStatus(invitor); + + player.teleportTo(duelLevel, 30, 85, 0, 0, 0); + EntityPos playerPos = new EntityPos(0, 85, 0, 0, 0); + player.setRespawnPosition(duelLevel.dimension(), playerPos.toBlockPos(), playerPos.yaw, true, false); + playerData.inviting = false; + playerData.invitingPlayer = null; + playerData.inDuel = true; + playerData.duelPlayer = invitor; + + invitor.teleportTo(duelLevel, -30, 85, 0, 0, 0); + EntityPos invitorPos = new EntityPos(0, 85, 0, 0, 0); + invitor.setRespawnPosition(duelLevel.dimension(), invitorPos.toBlockPos(), invitorPos.yaw, true, false); + invitorData.inviting = false; + invitorData.invitingPlayer = null; + invitorData.inDuel = true; + invitorData.duelPlayer = player; + + player.setGameMode(GameType.SURVIVAL); + invitor.setGameMode(GameType.SURVIVAL); + + removeQueue(player, stringGameMode, true); + removeQueue(invitor, stringGameMode, true); + + /* + InventoryUtil.setInventory(player, stringGameMode.toLowerCase(), "/duels", true); + InventoryUtil.setInventory(invitor, stringGameMode.toLowerCase(), "/duels", true); + */ + + player.sendMessage(ChatFormat.format("{b1}Your opponent: {b2}{}", invitor.getScoreboardName()), Util.NIL_UUID); + invitor.sendMessage(ChatFormat.format("{b1}Your opponent: {b2}{}", player.getScoreboardName()), Util.NIL_UUID); + + ServerTime.minecraftServer.getCommands().performCommand(ServerTime.minecraftServer.createCommandSourceStack(), "/execute as " + player.getScoreboardName() + " run loadinventory " + stringGameMode.toLowerCase() + " " + player.getScoreboardName()); + ServerTime.minecraftServer.getCommands().performCommand(ServerTime.minecraftServer.createCommandSourceStack(), "/execute as " + invitor.getScoreboardName() + " run loadinventory " + stringGameMode.toLowerCase() + " " + invitor.getScoreboardName()); + + playerData.gameMode = gameMode; + invitorData.gameMode = gameMode; + } + + public static ServerLevel createWorld(){ + RuntimeWorldConfig config = new RuntimeWorldConfig() + .setDimensionType(DuelsSpawn.duelWorld.dimensionType()) + .setGenerator(DuelsSpawn.duelWorld.getChunkSource().getGenerator()) + .setDifficulty(Difficulty.HARD) + .setGameRule(GameRules.RULE_KEEPINVENTORY, false) + .setGameRule(GameRules.RULE_MOBGRIEFING, false) + .setGameRule(GameRules.RULE_WEATHER_CYCLE, false) + .setGameRule(GameRules.RULE_DAYLIGHT, false) + .setGameRule(GameRules.RULE_DO_IMMEDIATE_RESPAWN, false) + .setGameRule(GameRules.RULE_DOMOBSPAWNING, false) + .setGameRule(GameRules.RULE_SHOWDEATHMESSAGES, false) + .setGameRule(GameRules.RULE_SPAWN_RADIUS, 0) + .setGameRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS, false) + .setTimeOfDay(6000); + + return ServerTime.fantasy.getOrOpenPersistentWorld(ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation("duels", UUID.randomUUID().toString())).location(), config).asWorld(); + } + + public static void deleteWorld(String id) { + ServerTime.fantasy.getOrOpenPersistentWorld(new ResourceLocation("duels", id), null).delete(); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerData.java b/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerData.java new file mode 100644 index 00000000..da10f009 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerData.java @@ -0,0 +1,41 @@ +package com.nexia.minigames.games.duels.util.player; + +import com.nexia.minigames.games.duels.DuelGameMode; +import net.minecraft.server.level.ServerPlayer; + +public class PlayerData { + + // Stuff saved into files + public SavedPlayerData savedData; + + // Stuff not saved in files + public DuelGameMode gameMode; + + public boolean inDuel; + + public boolean inviting; + + public ServerPlayer invitingPlayer; + + public boolean isDead; + + public boolean isLeaving; + + public String inviteMap; + + public ServerPlayer duelPlayer; + + public PlayerData(SavedPlayerData savedData) { + this.savedData = savedData; + + this.inDuel = false; + this.inviting = false; + this.gameMode = DuelGameMode.LOBBY; + this.invitingPlayer = null; + this.isDead = false; + this.isLeaving = false; + this.duelPlayer = null; + this.inviteMap = ""; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerDataManager.java b/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerDataManager.java new file mode 100644 index 00000000..fa847944 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/util/player/PlayerDataManager.java @@ -0,0 +1,76 @@ +package com.nexia.minigames.games.duels.util.player; + +import com.google.gson.Gson; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.world.entity.player.Player; + +import java.io.File; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.UUID; + +public class PlayerDataManager { + + static String dataDirectory = FabricLoader.getInstance().getConfigDir().toString() + "/nexia/duels"; + static String playerDataDirectory = dataDirectory + "/playerdata"; + + static HashMap allPlayerData = new HashMap<>(); + + public static PlayerData get(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) { + addPlayerData(player); + } + return allPlayerData.get(player.getUUID()); + } + + public static void addPlayerData(Player player) { + PlayerData playerData = new PlayerData(loadPlayerData(player)); + allPlayerData.put(player.getUUID(), playerData); + } + + public static void removePlayerData(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) return; + savePlayerData(player); + allPlayerData.remove(player.getUUID()); + } + + private static void savePlayerData(Player player) { + try { + + PlayerData playerData = get(player); + Gson gson = new Gson(); + String json = gson.toJson(playerData.savedData); + + String directory = getDataDir(); + FileWriter fileWriter = new FileWriter(directory + "/" + player.getUUID() + ".json"); + fileWriter.write(json); + fileWriter.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static SavedPlayerData loadPlayerData(Player player) { + try { + + String directory = getDataDir(); + String json = Files.readString(Path.of(directory + "/" + player.getUUID() + ".json")); + + Gson gson = new Gson(); + return gson.fromJson(json, SavedPlayerData.class); + + } catch (Exception e) { + return new SavedPlayerData(); + } + } + + private static String getDataDir() { + new File(playerDataDirectory).mkdirs(); + return playerDataDirectory; + } + + +} diff --git a/src/main/java/com/nexia/minigames/games/duels/util/player/SavedPlayerData.java b/src/main/java/com/nexia/minigames/games/duels/util/player/SavedPlayerData.java new file mode 100644 index 00000000..333fb05d --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/duels/util/player/SavedPlayerData.java @@ -0,0 +1,13 @@ +package com.nexia.minigames.games.duels.util.player; + +public class SavedPlayerData { + + public int wins; + + public int loss; + public SavedPlayerData() { + this.wins = 0; + this.loss = 0; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/OitcGame.java b/src/main/java/com/nexia/minigames/games/oitc/OitcGame.java new file mode 100644 index 00000000..46e99fd9 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/OitcGame.java @@ -0,0 +1,235 @@ +package com.nexia.minigames.games.oitc; + +import com.nexia.core.games.util.LobbyUtil; +import com.nexia.core.games.util.PlayerGameMode; +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.misc.RandomUtil; +import com.nexia.core.utilities.player.PlayerUtil; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.minigames.Main; +import com.nexia.minigames.games.oitc.util.OitcScoreboard; +import com.nexia.minigames.games.oitc.util.player.PlayerData; +import com.nexia.minigames.games.oitc.util.player.PlayerDataManager; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.GameType; + +import java.util.ArrayList; + +public class OitcGame { + + + + public static ArrayList alive = new ArrayList<>(); + + public static ArrayList spectator = new ArrayList<>(); + + public static ArrayList deathPlayers = new ArrayList<>(); + + public static String mapName = "city"; + + // Both timers counted in seconds. + public static int gameTime = 300; + + public static int queueTime = 30; + + public static ArrayList queue = new ArrayList<>(); + + public static boolean isStarted = false; + + + public static void leave(ServerPlayer player) { + death(player, player.getLastDamageSource()); + + PlayerData data = PlayerDataManager.get(player); + OitcGame.spectator.remove(player); + data.isSpectating = false; + OitcScoreboard.removeScoreboardFor(player); + data.kills = 0; + + player.removeTag("in_oitc_game"); + + PlayerUtil.resetHealthStatus(player); + player.setGameMode(GameType.ADVENTURE); + + player.inventory.clearContent(); + player.inventory.setCarried(ItemStack.EMPTY); + player.getEnderChestInventory().clearContent(); + + player.removeTag("oitc"); + OitcGame.death(player, player.getLastDamageSource()); + + PlayerDataManager.get(player).gameMode = OitcGameMode.LOBBY; + } + + public static void second() { + if(OitcGame.isStarted) { + OitcScoreboard.updateScoreboard(); + if(OitcGame.gameTime-- <= 0){ + stopGame(); + } else { + OitcGame.gameTime--; + } + } + + if(!OitcGame.isStarted && OitcGame.queue.size() >= 2){ + if(OitcGame.queueTime-- <= 0){ + startGame(); + } else { + OitcGame.queueTime--; + } + for(ServerPlayer player : OitcGame.queue){ + PlayerUtil.sendActionbar(player, "Time: " + OitcGame.queueTime); + } + } + + for(ServerPlayer player : OitcGame.deathPlayers){ + PlayerData data = PlayerDataManager.get(player); + if(data.isDeathTime && data.deathTime++ <= 0){ + data.deathTime = 5; + data.isDeathTime = false; + spawnInRandomPos(player); + player.setGameMode(GameType.ADVENTURE); + } + } + } + + public static void joinQueue(ServerPlayer player) { + PlayerData data = PlayerDataManager.get(player); + data.isDeathTime = false; + data.deathTime = 5; + data.kills = 0; + player.setHealth(20f); + if(OitcGame.isStarted){ + OitcGame.spectator.add(player); + data.isSpectating = true; + PlayerDataManager.get(player).gameMode = OitcGameMode.SPECTATOR; + } else { + OitcGame.queue.add(player); + data.isSpectating = false; + PlayerUtil.broadcast(OitcGame.queue, ChatFormat.format("{b2}{} {b1}has joined the game.", player.getScoreboardName())); + } + + ServerLevel world = ServerTime.fantasy.getOrOpenPersistentWorld(new ResourceLocation("oitc", OitcGame.mapName), null).asWorld(); + player.teleportTo(world, 0, 100, 0, 0, 0); + player.setRespawnPosition(world.dimension(), new BlockPos(0, 100, 0), 0, true, false); + } + + public static void startGame() { + if(OitcGame.queueTime <= 0){ + OitcGame.isStarted = true; + OitcGame.gameTime = 300; + OitcGame.alive.addAll(OitcGame.queue); + + ServerLevel world = ServerTime.fantasy.getOrOpenPersistentWorld(new ResourceLocation("oitc", OitcGame.mapName), null).asWorld(); + + ItemStack sword = new ItemStack(Items.STONE_SWORD); + sword.getOrCreateTag().putBoolean("Unbreakable", true); + sword.hideTooltipPart(ItemStack.TooltipPart.UNBREAKABLE); + + ItemStack bow = new ItemStack(Items.BOW); + bow.enchant(Enchantments.POWER_ARROWS, 255); + bow.getOrCreateTag().putBoolean("Unbreakable", true); + bow.hideTooltipPart(ItemStack.TooltipPart.ENCHANTMENTS); + bow.hideTooltipPart(ItemStack.TooltipPart.UNBREAKABLE); + + for(ServerPlayer player : OitcGame.alive) { + player.inventory.setItem(0, sword); + player.inventory.setItem(1, bow); + player.inventory.setItem(8, new ItemStack(Items.ARROW)); + BlockPos pos = new BlockPos(0, 80, 0); + + PlayerDataManager.get(player).gameMode = OitcGameMode.PLAYING; + + player.addTag("in_oitc_game"); + player.removeTag(LobbyUtil.NO_DAMAGE_TAG); + + player.teleportTo(world, pos.getX(), pos.getY(), pos.getZ(), 0, 0); + player.setRespawnPosition(world.dimension(), pos, 0, true, false); + } + + OitcScoreboard.setUpScoreboard(); + + OitcGame.spectator.clear(); + OitcGame.queue.clear(); + } + } + + public static void stopGame() { + if(OitcGame.queueTime <= 0){ + OitcGame.isStarted = false; + OitcGame.gameTime = 0; + + for(ServerPlayer player : OitcGame.alive){ + OitcScoreboard.removeScoreboardFor(player); + player.removeTag("in_oitc_game"); + LobbyUtil.sendGame(player, "oitc", false); + } + + OitcGame.alive.clear(); + OitcGame.spectator.clear(); + OitcGame.queue.clear(); + + OitcGame.mapName = Main.config.oitcMaps.get(RandomUtil.randomInt(0, Main.config.oitcMaps.size())); + } + } + + public static void spawnInRandomPos(ServerPlayer player){ + String map = OitcGame.mapName; + } + + public static boolean isOITCPlayer(ServerPlayer player){ + return com.nexia.core.utilities.player.PlayerDataManager.get(player).gameMode == PlayerGameMode.OITC; + } + + public static void death(ServerPlayer victim, DamageSource source){ + PlayerData victimData = PlayerDataManager.get(victim); + if(!victimData.isDeathTime && !OitcGame.deathPlayers.contains(victim)) { + ServerPlayer attacker = null; + try { + attacker = (ServerPlayer) source.getEntity(); + } catch(Exception ignored){ + try { + attacker = (ServerPlayer) victim.getLastDamageSource().getEntity(); + } catch (Exception ignored2) { + } + } + + if(attacker != null){ + PlayerData attackerData = PlayerDataManager.get(attacker); + attackerData.kills++; + attackerData.savedData.kills++; + attacker.setHealth(20f); + attacker.addItem(new ItemStack(Items.ARROW)); + } + + OitcGame.deathPlayers.add(victim); + + victimData.deathTime = 5; + victimData.isDeathTime = true; + } + } + + public static void firstTick(MinecraftServer server){ + OitcSpawn.setOitcWorld(server); + + queue.clear(); + alive.clear(); + spectator.clear(); + deathPlayers.clear(); + + mapName = "city"; + + isStarted = false; + queueTime = 30; + gameTime = 300; + } +} + diff --git a/src/main/java/com/nexia/minigames/games/oitc/OitcGameMode.java b/src/main/java/com/nexia/minigames/games/oitc/OitcGameMode.java new file mode 100644 index 00000000..1c953939 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/OitcGameMode.java @@ -0,0 +1,14 @@ +package com.nexia.minigames.games.oitc; +public class OitcGameMode { + String id; + + public static final OitcGameMode PLAYING = new OitcGameMode("playing"); + + public static final OitcGameMode SPECTATOR = new OitcGameMode("spectator"); + + public static final OitcGameMode LOBBY = new OitcGameMode("lobby"); + + public OitcGameMode(String id) { + this.id = id; + } +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/OitcSpawn.java b/src/main/java/com/nexia/minigames/games/oitc/OitcSpawn.java new file mode 100644 index 00000000..d7b35b44 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/OitcSpawn.java @@ -0,0 +1,32 @@ +package com.nexia.minigames.games.oitc; + +import com.nexia.core.utilities.pos.EntityPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; + +public class OitcSpawn { + public static ServerLevel oitcWorld = null; + public static EntityPos spawn = new EntityPos(0, 80, 0, 0, 0); + + public OitcSpawn() { + } + + public static boolean isOitcWorld(Level level) { + return level.dimension().toString().contains("oitc:hub"); + } + + public static boolean isInHub(Player player) { + return player.level == oitcWorld; + } + + public static void setOitcWorld(MinecraftServer server) { + for (ServerLevel level : server.getAllLevels()) { + if (isOitcWorld(level)) { + oitcWorld = level; + break; + } + } + } +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/util/OitcScoreboard.java b/src/main/java/com/nexia/minigames/games/oitc/util/OitcScoreboard.java new file mode 100644 index 00000000..21d24e2e --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/util/OitcScoreboard.java @@ -0,0 +1,138 @@ +package com.nexia.minigames.games.oitc.util; + +import com.nexia.core.utilities.chat.ChatFormat; +import com.nexia.core.utilities.time.ServerTime; +import com.nexia.core.utilities.time.TickUtil; +import com.nexia.minigames.games.oitc.OitcGame; +import com.nexia.minigames.games.oitc.OitcSpawn; +import com.nexia.minigames.games.oitc.util.player.PlayerDataManager; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; +import net.minecraft.network.protocol.game.ClientboundSetScorePacket; +import net.minecraft.server.ServerScoreboard; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.Score; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; + +import java.util.ArrayList; + +public class OitcScoreboard { + + private static final String objectiveName = "oitc_sidebar"; + private static final TextComponent title = new TextComponent(ChatFormat.brandColor1 + "\247l OITC "); + + public static final int timerLine = 1; + + private static final Scoreboard scoreboard = new Scoreboard(); + private static final Objective objective = new Objective( + scoreboard, objectiveName, ObjectiveCriteria.DUMMY, + title, ObjectiveCriteria.RenderType.INTEGER); + + public static void setUpScoreboard() { + for (ServerPlayer player : OitcGame.alive) { + sendOITCScoreboard(player); + } + updateScoreboard(); + } + + public static void sendOITCScoreboard(ServerPlayer player) { + player.connection.send(new ClientboundSetObjectivePacket(objective, 0)); + player.connection.send(new ClientboundSetDisplayObjectivePacket(1, objective)); + } + + public static void updateScoreboard() { + ArrayList lines = new ArrayList<>(); + + lines.add(" "); + lines.add(" "); + lines.add(" "); + + clearScoreboard(); + addLines(lines); + updateTimer(); + + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + updateStats(player); + sendLines(player); + } + } + + public static void updateTimer() { + String[] timer = TickUtil.minuteTimeStamp(OitcGame.gameTime * 20); + String time = "Time left: " + ChatFormat.brandColor2 + timer[0] + ":" + timer[1]; + OitcScoreboard.replaceLine(OitcScoreboard.timerLine, time); + } + + public static void updateStats(ServerPlayer player) { + String kills = "Kills: " + ChatFormat.brandColor2 + PlayerDataManager.get(player).kills; + OitcScoreboard.replaceLinePlayer(2, kills, player); + } + + public static void replaceLine(int i, String newLine) { + for (Score score : scoreboard.getPlayerScores(objective)) { + if (score.getScore() != i) continue; + + String previous = score.getOwner(); + + scoreboard.resetPlayerScore(previous, objective); + scoreboard.getOrCreatePlayerScore(newLine, objective).setScore(i); + + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.REMOVE, objectiveName, previous, i)); + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.CHANGE, objectiveName, newLine, i)); + } + } + } + + public static void replaceLinePlayer(int i, String newLine, ServerPlayer player){ + for(Score score : scoreboard.getPlayerScores(objective)){ + if (score.getScore() != i) continue; + + String previous = score.getOwner(); + + scoreboard.resetPlayerScore(previous, objective); + scoreboard.getOrCreatePlayerScore(newLine, objective).setScore(i); + + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.REMOVE, objectiveName, previous, i)); + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.CHANGE, objectiveName, newLine, i)); + } + } + + private static void addLines(ArrayList lines) { + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + int scoreNumber = lines.size() - i; + + scoreboard.getOrCreatePlayerScore(line, objective).setScore(scoreNumber); + } + } + + public static void sendLines(ServerPlayer player) { + for (Score score : scoreboard.getPlayerScores(objective)) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.CHANGE, objectiveName, score.getOwner(), score.getScore())); + } + } + + private static void clearScoreboard() { + for (Score score : scoreboard.getPlayerScores(objective)) { + for (ServerPlayer player : ServerTime.minecraftServer.getPlayerList().getPlayers()) { + player.connection.send(new ClientboundSetScorePacket( + ServerScoreboard.Method.REMOVE, objectiveName, score.getOwner(), score.getScore())); + } + scoreboard.resetPlayerScore(score.getOwner(), objective); + } + } + + public static void removeScoreboardFor(ServerPlayer player) { + player.connection.send(new ClientboundSetDisplayObjectivePacket(1, null)); + } + +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerData.java b/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerData.java new file mode 100644 index 00000000..acff9a36 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerData.java @@ -0,0 +1,31 @@ +package com.nexia.minigames.games.oitc.util.player; + +import com.nexia.minigames.games.oitc.OitcGameMode; + +public class PlayerData { + + // Stuff saved into files + public SavedPlayerData savedData; + + // Stuff not saved in files + public OitcGameMode gameMode; + + public int kills; + + public boolean isSpectating; + + public boolean isDeathTime; + + public int deathTime; + + public PlayerData(SavedPlayerData savedData) { + this.savedData = savedData; + + this.gameMode = OitcGameMode.LOBBY; + this.kills = 0; + this.isSpectating = false; + this.isDeathTime = false; + this.deathTime = 5; + } + +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerDataManager.java b/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerDataManager.java new file mode 100644 index 00000000..1a408003 --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/util/player/PlayerDataManager.java @@ -0,0 +1,76 @@ +package com.nexia.minigames.games.oitc.util.player; + +import com.google.gson.Gson; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.world.entity.player.Player; + +import java.io.File; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.UUID; + +public class PlayerDataManager { + + static String dataDirectory = FabricLoader.getInstance().getConfigDir().toString() + "/nexia/oitc"; + static String playerDataDirectory = dataDirectory + "/playerdata"; + + static HashMap allPlayerData = new HashMap<>(); + + public static PlayerData get(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) { + addPlayerData(player); + } + return allPlayerData.get(player.getUUID()); + } + + public static void addPlayerData(Player player) { + PlayerData playerData = new PlayerData(loadPlayerData(player)); + allPlayerData.put(player.getUUID(), playerData); + } + + public static void removePlayerData(Player player) { + if (!allPlayerData.containsKey(player.getUUID())) return; + savePlayerData(player); + allPlayerData.remove(player.getUUID()); + } + + private static void savePlayerData(Player player) { + try { + + PlayerData playerData = get(player); + Gson gson = new Gson(); + String json = gson.toJson(playerData.savedData); + + String directory = getDataDir(); + FileWriter fileWriter = new FileWriter(directory + "/" + player.getUUID() + ".json"); + fileWriter.write(json); + fileWriter.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static SavedPlayerData loadPlayerData(Player player) { + try { + + String directory = getDataDir(); + String json = Files.readString(Path.of(directory + "/" + player.getUUID() + ".json")); + + Gson gson = new Gson(); + return gson.fromJson(json, SavedPlayerData.class); + + } catch (Exception e) { + return new SavedPlayerData(); + } + } + + private static String getDataDir() { + new File(playerDataDirectory).mkdirs(); + return playerDataDirectory; + } + + +} diff --git a/src/main/java/com/nexia/minigames/games/oitc/util/player/SavedPlayerData.java b/src/main/java/com/nexia/minigames/games/oitc/util/player/SavedPlayerData.java new file mode 100644 index 00000000..3cdbc37e --- /dev/null +++ b/src/main/java/com/nexia/minigames/games/oitc/util/player/SavedPlayerData.java @@ -0,0 +1,16 @@ +package com.nexia.minigames.games.oitc.util.player; + +public class SavedPlayerData { + + public int wins; + + public int loss; + + public int kills; + public SavedPlayerData() { + this.wins = 0; + this.loss = 0; + this.kills = 0; + } + +} diff --git a/src/main/resources/assets/nexia/icon.png b/src/main/resources/assets/nexia/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..abd8afdab12040d0023c731297979f443aa0852a GIT binary patch literal 20884 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 m{Qsxm=0.7.4", + "minecraft": "1.16.x", + "fabric": "*" + } +} diff --git a/src/main/resources/ffa.mixins.json b/src/main/resources/ffa.mixins.json new file mode 100644 index 00000000..1a0284cf --- /dev/null +++ b/src/main/resources/ffa.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.nexia.ffa.mixin", + "compatibilityLevel": "JAVA_17", + "server": [ + "ServerPlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}