From cd048f8bb7424ffe8405044d10c711ea05110cc5 Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Sun, 19 May 2024 15:55:40 +0200 Subject: [PATCH] Add MLG challenge and experimental toggle command --- .github/assets/README-Modrinth.md | 13 +- README.md | 13 +- build.gradle.kts | 32 ++- data/challenges.json | 13 +- data/data/flatworld-data.dat | 156 +++++++++++ data/language/mchallenge/en.yml | 24 ++ gradle.properties | 7 +- .../de/miraculixx/mchallenge/MChallenge.kt | 10 +- .../mchallenge/commands/ChallengeCommand.kt | 2 +- .../mchallenge/commands/TestCommand.kt | 41 +++ .../utils/ExperimentalFeatureCommand.kt | 49 ++++ .../modules/challenges/Challenges.kt | 19 +- .../modules/challenges/StatusChanger.kt | 2 + .../challenges/interfaces/HuntChallenge.kt | 4 +- .../modules/mods/force/huntDeath/DeathHunt.kt | 3 +- .../modules/mods/misc/mlg/MLGChallenge.kt | 259 ++++++++++++++++++ .../modules/mods/misc/mlg/MLGType.kt | 17 ++ 17 files changed, 634 insertions(+), 30 deletions(-) create mode 100644 data/data/flatworld-data.dat create mode 100644 src/main/kotlin/de/miraculixx/mchallenge/commands/TestCommand.kt create mode 100644 src/main/kotlin/de/miraculixx/mchallenge/commands/utils/ExperimentalFeatureCommand.kt create mode 100644 src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGChallenge.kt create mode 100644 src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGType.kt diff --git a/.github/assets/README-Modrinth.md b/.github/assets/README-Modrinth.md index 9a66c61..5a3ed35 100644 --- a/.github/assets/README-Modrinth.md +++ b/.github/assets/README-Modrinth.md @@ -36,7 +36,7 @@ All challenges can be combined and played in multiplayer or singleplayer (some r You have an idea for a new Challenge? We would be happy to hear it in our [discord](https://dc.mutils.net)! -**▪ Current Challenge count** ⇒ ``66`` +**▪ Current Challenge count** ⇒ ``67`` > **Important**
@@ -276,6 +276,17 @@ The world gone crazy and every few seconds the gravity switches between 4 new gr 🏷️ **Tags** - `Medium` - Slightly more difficult than normal Minecraft but still very doable +
Death Hunt +Die to all given deaths in a specific order to finish! Use /deathhunt to modify + +--- + +⚙️ **Settings** +- `No settings` + +🏷️ **Tags** +- `Fun` - Easy Challenge with fun in focus +- `Force` - You receive a task and are forced to finish it to proceed
*Currently requires full access*
Rhythm Craft Turn Minecraft into a full Rhythmgame! Every action needs to be done on the beat to follow the world rhythm. diff --git a/README.md b/README.md index ae1573a..2619199 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ All challenges can be combined and played in multiplayer or singleplayer (some r You have an idea for a new Challenge? We would be happy to hear it in our [discord](https://dc.mutils.net)! -**▪ Current Challenge count** ⇒ ``66`` +**▪ Current Challenge count** ⇒ ``67`` > **Important**
@@ -269,6 +269,17 @@ The world gone crazy and every few seconds the gravity switches between 4 new gr 🏷️ **Tags** - `Medium` - Slightly more difficult than normal Minecraft but still very doable +
Death Hunt +Die to all given deaths in a specific order to finish! Use /deathhunt to modify + +--- + +⚙️ **Settings** +- `No settings` + +🏷️ **Tags** +- `Fun` - Easy Challenge with fun in focus +- `Force` - You receive a task and are forced to finish it to proceed
*Currently requires full access*
Rhythm Craft Turn Minecraft into a full Rhythmgame! Every action needs to be done on the beat to follow the world rhythm. diff --git a/build.gradle.kts b/build.gradle.kts index f901bfe..95eb02c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import org.yaml.snakeyaml.Yaml plugins { kotlin("jvm") version "1.9.23" kotlin("plugin.serialization") version "1.9.23" - id("io.papermc.paperweight.userdev") version "1.7.0" + id("io.papermc.paperweight.userdev") version "1.7.1" id("xyz.jpenilla.run-paper") version "2.3.0" id("net.minecrell.plugin-yml.bukkit") version "0.6.0" id("com.modrinth.minotaur") version "2.+" @@ -27,6 +27,8 @@ val projectName = properties["name"] as String repositories { mavenLocal() mavenCentral() + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven("https://dl.cloudsmith.io/public/matyrobbrt/javanbt/maven/") } paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION @@ -36,14 +38,15 @@ dependencies { // Kotlin libraries library(kotlin("stdlib")) - library("org.jetbrains.kotlinx:kotlinx-serialization-json:1.+") - library("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.+") + library("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.+") + library("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.+") // MC Libraries implementation("de.miraculixx:mc-commons:1.0.1") implementation("de.miraculixx:kpaper-light:1.2.1") - library("dev.jorel:commandapi-bukkit-shade-mojang-mapped:9.4.0") - library("dev.jorel:commandapi-bukkit-kotlin:9.4.0") + implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:9.5.0-SNAPSHOT") + implementation("dev.jorel:commandapi-bukkit-kotlin:9.5.0-SNAPSHOT") + implementation("io.github.matyrobbrt:javanbt:0.0.3") // Internal APIs implementation("de.miraculixx:mbridge:1.0.0") @@ -68,9 +71,10 @@ tasks { shadowJar { dependencies { include { - it.moduleGroup == "de.miraculixx" + it.moduleGroup == "de.miraculixx" || it.moduleGroup == "dev.jorel" } } + relocate("dev.jorel.commandapi", "de.miraculixx.mchallenge.commandapi") } } @@ -99,25 +103,27 @@ bukkit { modrinth { token.set(properties["modrinthToken"] as String) - projectId.set(properties["modrinthProjectId"] as? String ?: projectName) + projectId.set(properties["modrinthProjectId"] as? String ?: properties["name"] as String) versionNumber.set(version as String) - versionType.set("release") // Can also be `beta` or `alpha` - versionName.set("MChallenge - $version") + versionType.set(properties["publishState"] as String) + uploadFile.set(tasks.jar) + versionName = "MChallenge - ${properties["version"]}" outlet.mcVersionRange = properties["supportedVersions"] as String outlet.allowedReleaseTypes = setOf(ReleaseType.RELEASE) gameVersions.addAll(outlet.mcVersions()) loaders.addAll(buildList { add("paper") add("purpur") - if (foliaSupport) add("folia") }) dependencies { - optional.project("timer") - optional.project("mweb") + // The scope can be `required`, `optional`, `incompatible`, or `embedded` + // The type can either be `project` or `version` + required.project("mweb") } - // Project sync + changelog = "- Fix error caused by latest Paper API changes regarding brigadier commands" + syncBodyFrom = rootProject.file(".github/assets/README-Modrinth.md").readText() } diff --git a/data/challenges.json b/data/challenges.json index 9f65ee7..2aa796b 100644 --- a/data/challenges.json +++ b/data/challenges.json @@ -1,8 +1,17 @@ [ + { + "key": "MLG", + "version": 118, + "item": "water_bucket", + "tags": [ + "HARD" + ], + "new": true + }, { "key": "DEATH_HUNT", - "version": 105, - "block": "totem_of_undying", + "version": 116, + "item": "totem_of_undying", "tags": [ "FUN", "FORCE" diff --git a/data/data/flatworld-data.dat b/data/data/flatworld-data.dat new file mode 100644 index 0000000..1838bcf --- /dev/null +++ b/data/data/flatworld-data.dat @@ -0,0 +1,156 @@ +"": { + Data: { + WanderingTraderSpawnChance: 0 + BorderCenterZ: 0.0 + Difficulty: 1B + BorderSizeLerpTime: 0L + raining: 0B + Time: 0L + GameType: 0 + BorderCenterX: 0.0 + BorderWarningBlocks: 5.0 + BorderDamagePerBlock: 0.2 + WorldGenSettings: { + bonus_chest: 0B + seed: -8199817837703319721L + generate_features: 0B + dimensions: { + "minecraft:overworld": { + generator: { + settings: { + features: 0B + biome: "minecraft:the_void" + layers: [ + { + block: "minecraft:bedrock" + height: 1 + }, + { + block: "minecraft:%BLOCK%" + height: 1 + }, + ] + lakes: 0B + } + type: "minecraft:flat" + } + type: "minecraft:overworld" + } + "minecraft:the_nether": { + generator: { + settings: "minecraft:nether" + biome_source: { + preset: "minecraft:nether" + type: "minecraft:multi_noise" + } + type: "minecraft:noise" + } + type: "minecraft:the_nether" + } + "minecraft:the_end": { + generator: { + settings: "minecraft:end" + biome_source: { + type: "minecraft:the_end" + } + type: "minecraft:noise" + } + type: "minecraft:the_end" + } + } + } + DragonFight: { + NeedsStateScanning: 1B + DragonKilled: 0B + PreviouslyKilled: 0B + } + BorderSizeLerpTarget: 59999968.0 + Version: { + Series: main + Snapshot: 0B + Id: 3839 + Name: "%VERSION%" + } + DayTime: 0L + initialized: 1B + WasModded: 1B + allowCommands: 0B + WanderingTraderSpawnDelay: 0 + CustomBossEvents: {} + GameRules: { + globalSoundEvents: "true" + tntExplosionDropDecay: "false" + enderPearlsVanishOnDeath: "true" + doFireTick: "false" + maxCommandChainLength: "65536" + doVinesSpread: "true" + lavaSourceConversion: "false" + disableElytraMovementCheck: "false" + forgiveDeadPlayers: "true" + commandBlockOutput: "true" + playersNetherPortalCreativeDelay: "1" + maxEntityCramming: "24" + doMobSpawning: "false" + universalAnger: "false" + playersSleepingPercentage: "100" + snowAccumulationHeight: "1" + doImmediateRespawn: "false" + blockExplosionDropDecay: "true" + naturalRegeneration: "true" + doMobLoot: "true" + fallDamage: "true" + doEntityDrops: "true" + randomTickSpeed: "0" + playersNetherPortalDefaultDelay: "80" + spawnRadius: "10" + freezeDamage: "true" + sendCommandFeedback: "true" + doWardenSpawning: "true" + fireDamage: "true" + reducedDebugInfo: "false" + waterSourceConversion: "true" + projectilesCanBreakBlocks: "true" + announceAdvancements: "true" + drowningDamage: "true" + spawnChunkRadius: "2" + disableRaids: "false" + doWeatherCycle: "true" + mobExplosionDropDecay: "true" + doDaylightCycle: "true" + showDeathMessages: "true" + doTileDrops: "true" + doInsomnia: "true" + keepInventory: "false" + doLimitedCrafting: "false" + mobGriefing: "true" + doTraderSpawning: "true" + commandModificationBlockLimit: "32768" + logAdminCommands: "true" + spectatorsGenerateChunks: "true" + doPatrolSpawning: "true" + maxCommandForkCount: "65536" + } + thunderTime: 17243 + SpawnY: -62 + rainTime: 23770 + hardcore: 0B + SpawnZ: 0 + DifficultyLocked: 0B + SpawnX: 0 + clearWeatherTime: 0 + thundering: 0B + SpawnAngle: 0.0F + version: 19133 + BorderSafeZone: 5.0 + LastPlayed: 1716035560012L + BorderWarningTime: 15.0 + ScheduledEvents: [] + LevelName: "%NAME%" + BorderSize: 59999968.0 + DataVersion: 3839 + DataPacks: { + Enabled: [ vanilla, file/bukkit, paper, update_1_21 ] + Disabled: [ bundle, trade_rebalance, ] + } + } +} \ No newline at end of file diff --git a/data/language/mchallenge/en.yml b/data/language/mchallenge/en.yml index 5726441..61a21ff 100644 --- a/data/language/mchallenge/en.yml +++ b/data/language/mchallenge/en.yml @@ -95,6 +95,7 @@ event: noDoubleKill: " killed the same mob twice in a row!" hitOrder: " was not allowed do damage a mob" crushedAnvils: " was hit by an anvil!" + mlg: " failed the MLG!" mob_hunt: collect: " found !" success: "Congratulations! You found all mobs" @@ -154,6 +155,8 @@ event: current: "Current Health ⇒ " next: "Next health draining in (%)" enable-flight: "This Challenge requires to allow flight in your server.properties! Otherwise players will be kicked spontaneously for flying." + experimental: + missing: "Missing experimental features ! '>Click here to enable it (requires rejoin)" @@ -412,6 +415,9 @@ items: DEATH_HUNT: n: "Death Hunt" l: "Die to all given deaths in a specific order
to finish!
Use /deathhunt to modify" + MLG: + n: "MLG Challenge" + l: "Once a while you are forced to perform
a random MLG! Can you survive the fall?" chS: FLY: power: @@ -795,6 +801,24 @@ items: interval: n: "Interval" l: "Timing for each health draining" + MLG: + delay: + n: "Delay" + l: "Minimum and maximum duration
between MLGs" + minDelay: + n: "Min Delay" + maxDelay: + n: "Max Delay" + height: + n: "Height" + l: "Minimum and maximum height
in blocks to perform the MLG" + minHeight: + n: "Min Height" + maxHeight: + n: "Max Height" + hardMLGs: + n: "Hard MLGs" + l: "Activate to perform the hardest
MLGs. Only for skilled players!" tags: FUN: diff --git a/gradle.properties b/gradle.properties index 346f16f..04b3fee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,15 @@ kotlin.code.style=official # Project Settings - set name in settings.gradle.kts -version=116 +version=118 group=de.miraculixx name=MChallenge description=MUtils Challenges - Play various Challenges that can modify the game slightly to completely against or in coop with your friends! author=Miraculixx +publishState=release modrinthProjectId=challenge # Minecraft Settings -gameVersion=1.20.4 -supportedVersions=>=1.16 +gameVersion=1.20.6 +supportedVersions=>=1.20.5 foliaSupport=false diff --git a/src/main/kotlin/de/miraculixx/mchallenge/MChallenge.kt b/src/main/kotlin/de/miraculixx/mchallenge/MChallenge.kt index b81c50a..8c178f0 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/MChallenge.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/MChallenge.kt @@ -8,10 +8,7 @@ import de.miraculixx.mbridge.MUtilsBridge import de.miraculixx.mbridge.MUtilsBridge.Companion.debug import de.miraculixx.mbridge.data.MUtilsModule import de.miraculixx.mbridge.data.MUtilsPlatform -import de.miraculixx.mchallenge.commands.ChallengeCommand -import de.miraculixx.mchallenge.commands.CompetitionCommand -import de.miraculixx.mchallenge.commands.CustomRulesCommand -import de.miraculixx.mchallenge.commands.ModuleCommand +import de.miraculixx.mchallenge.commands.* import de.miraculixx.mchallenge.commands.utils.* import de.miraculixx.mchallenge.modules.ChallengeManager import de.miraculixx.mchallenge.modules.challenges.challenges @@ -25,6 +22,7 @@ import de.miraculixx.mcommons.minorVersion import de.miraculixx.mcommons.text.* import dev.jorel.commandapi.CommandAPI import dev.jorel.commandapi.CommandAPIBukkitConfig +import io.papermc.paper.command.brigadier.PluginVanillaCommandWrapper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -92,6 +90,8 @@ class MChallenge : KPaper() { CompetitionCommand() ConfigManager.addConfigurable(PositionCommand()) ConfigManager.addConfigurable(BackpackCommand()) + TestCommand() + ExperimentalFeatureCommand // Load configuration prefix = cmp("MChallenge", cHighlight) + _prefixSeparator @@ -144,9 +144,9 @@ class MChallenge : KPaper() { override fun shutdown() { - CommandAPI.onDisable() ChallengeManager.shutDown() ConfigManager.save() + CommandAPI.onDisable() } } diff --git a/src/main/kotlin/de/miraculixx/mchallenge/commands/ChallengeCommand.kt b/src/main/kotlin/de/miraculixx/mchallenge/commands/ChallengeCommand.kt index 34f8415..76badaf 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/commands/ChallengeCommand.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/commands/ChallengeCommand.kt @@ -23,7 +23,7 @@ import de.miraculixx.mcommons.text.* import dev.jorel.commandapi.arguments.LiteralArgument import dev.jorel.commandapi.kotlindsl.* import org.bukkit.Sound -import java.util.Locale +import java.util.* class ChallengeCommand { private var apiCooldown = false diff --git a/src/main/kotlin/de/miraculixx/mchallenge/commands/TestCommand.kt b/src/main/kotlin/de/miraculixx/mchallenge/commands/TestCommand.kt new file mode 100644 index 0000000..6b1d5f1 --- /dev/null +++ b/src/main/kotlin/de/miraculixx/mchallenge/commands/TestCommand.kt @@ -0,0 +1,41 @@ +package de.miraculixx.mchallenge.commands + +import de.miraculixx.kpaper.extensions.server +import dev.jorel.commandapi.kotlindsl.* +import net.minecraft.world.flag.FeatureFlagSet +import net.minecraft.world.flag.FeatureFlags +import org.bukkit.Material +import org.bukkit.World +import org.bukkit.WorldCreator +import org.bukkit.WorldType +import org.bukkit.block.data.BlockData +import org.bukkit.craftbukkit.CraftServer +import org.bukkit.inventory.ItemStack +import java.util.UUID + +class TestCommand { + val command = commandTree("test") { + literalArgument("flat") { + blockStateArgument("block") { + playerExecutor { player, args -> + val block = args[0] as BlockData + val world = WorldCreator(UUID.randomUUID().toString()) + .type(WorldType.FLAT) + .environment(World.Environment.NORMAL) + .generatorSettings("{\"layers\": [{\"block\": \"bedrock\", \"height\": 1}, {\"block\": \"${block.material.name.lowercase()}\", \"height\": 2}], \"biome\":\"the_void\"}") + .createWorld() + player.teleportAsync(world!!.spawnLocation) + } + } + } + + literalArgument("experimantal") { + playerExecutor { player, _ -> + val worldData = (server as CraftServer).handle.server.worldData + worldData.dataConfiguration = worldData.dataConfiguration.expandFeatures(FeatureFlagSet.of(FeatureFlags.UPDATE_1_21)) + player.sendMessage("Experimental features enabled") + player.inventory.addItem(ItemStack(Material.WIND_CHARGE), ItemStack(Material.MACE)) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/de/miraculixx/mchallenge/commands/utils/ExperimentalFeatureCommand.kt b/src/main/kotlin/de/miraculixx/mchallenge/commands/utils/ExperimentalFeatureCommand.kt new file mode 100644 index 0000000..c9119ea --- /dev/null +++ b/src/main/kotlin/de/miraculixx/mchallenge/commands/utils/ExperimentalFeatureCommand.kt @@ -0,0 +1,49 @@ +package de.miraculixx.mchallenge.commands.utils + +import de.miraculixx.kpaper.extensions.onlinePlayers +import de.miraculixx.kpaper.extensions.server +import de.miraculixx.mcommons.text.* +import dev.jorel.commandapi.arguments.ArgumentSuggestions +import dev.jorel.commandapi.kotlindsl.anyExecutor +import dev.jorel.commandapi.kotlindsl.commandTree +import dev.jorel.commandapi.kotlindsl.literalArgument +import dev.jorel.commandapi.kotlindsl.textArgument +import net.kyori.adventure.text.format.NamedTextColor +import net.minecraft.world.flag.FeatureFlagRegistry +import net.minecraft.world.flag.FeatureFlagSet +import org.bukkit.craftbukkit.CraftServer + +object ExperimentalFeatureCommand { + val enabledFeatures = mutableSetOf() + + private val command = commandTree("experimental-features") { + withPermission("command.experimental") + + literalArgument("enable") { + textArgument("feature") { + replaceSuggestions(ArgumentSuggestions.strings("update_1_21", "bundle", "trade_rebalance")) + anyExecutor { sender, args -> + val feature = args[0] as String + + try { + val worldData = (server as CraftServer).handle.server.worldData + val newFeature = FeatureFlagRegistry.Builder("main").createVanilla(feature) + worldData.dataConfiguration = worldData.dataConfiguration.expandFeatures(FeatureFlagSet.of(newFeature)) + } catch (e: Exception) { + sender.sendMessage(prefix + cmp("Something went wrong! Error: ${e.message}")) + } + + enabledFeatures.add(feature) + onlinePlayers.forEach { + it.kick( + cmp("New Feature Enabled", cHighlight) + + cmp("\n ", NamedTextColor.WHITE, strikethrough = true) + + cmp("\nPlease rejoin to access all new features") + + cmp("\nNew Experiment: ") + cmp(feature, cMark) + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/Challenges.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/Challenges.kt index f4cba64..286e7cf 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/Challenges.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/Challenges.kt @@ -72,7 +72,8 @@ enum class Challenges(val filter: Set, val icon: Icon, val status HIT_ORDER(setOf(ChallengeTags.MEDIUM), Icon("DIAMOND_AXE")), TICK_RATE(setOf(ChallengeTags.MEDIUM, ChallengeTags.BETA), Icon("CLOCK")), RHYTHM_CRAFT(setOf(ChallengeTags.HARD, ChallengeTags.BETA), Icon("NOTE_BLOCK")), - DEATH_HUNT(setOf(ChallengeTags.FUN, ChallengeTags.FORCE), Icon("TOTEM_OF_UNDYING")) + DEATH_HUNT(setOf(ChallengeTags.FUN, ChallengeTags.FORCE), Icon("TOTEM_OF_UNDYING")), + MLG(setOf(ChallengeTags.HARD), Icon("WATER_BUCKET")), ; @@ -325,6 +326,22 @@ enum class Challenges(val filter: Set, val icon: Icon, val status RHYTHM_CRAFT -> mapOf() DEATH_HUNT -> mapOf() + + MLG -> mapOf( + "delay" to ChallengeSectionSetting( + "CLOCK", mapOf( + "minDelay" to ChallengeIntSetting("GOLD_NUGGET", 120, "s", max = 600, min = 30, step = 10), + "maxDelay" to ChallengeIntSetting("GOLD_INGOT", 240, "s", max = 600, min = 60, step = 10) + ) + ), + "height" to ChallengeSectionSetting( + "RABBIT_FOOT", mapOf( + "minHeight" to ChallengeIntSetting("IRON_NUGGET", 50, "b", max = 200, min = 30, step = 5), + "maxHeight" to ChallengeIntSetting("IRON_INGOT", 100, "b", max = 200, min = 40, step = 5) + ) + ), + "hardMLGs" to ChallengeBoolSetting("OAK_BOAT", true) + ) } } } \ No newline at end of file diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/StatusChanger.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/StatusChanger.kt index e41639c..7db3ed1 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/StatusChanger.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/StatusChanger.kt @@ -15,6 +15,7 @@ import de.miraculixx.mchallenge.modules.mods.misc.checkpoints.Checkpoints import de.miraculixx.mchallenge.modules.mods.misc.ghost.Ghost import de.miraculixx.mchallenge.modules.mods.misc.gravity.GravityManager import de.miraculixx.mchallenge.modules.mods.misc.inTime.InTime +import de.miraculixx.mchallenge.modules.mods.misc.mlg.MLGChallenge import de.miraculixx.mchallenge.modules.mods.misc.realistic.Realistic import de.miraculixx.mchallenge.modules.mods.misc.rhythm.RhythmCraft import de.miraculixx.mchallenge.modules.mods.misc.rocket.Rocket @@ -121,6 +122,7 @@ class StatusChanger { // Challenges.HALLOWEEN -> HalloweenChallenge() Challenges.HP_DRAIN -> HPDrain() Challenges.DEATH_HUNT -> DeathHunt() + Challenges.MLG -> MLGChallenge() } } diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/interfaces/HuntChallenge.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/interfaces/HuntChallenge.kt index a258b45..443c461 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/interfaces/HuntChallenge.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/challenges/interfaces/HuntChallenge.kt @@ -54,7 +54,7 @@ abstract class HuntChallenge(name: String, val key: String) : CommandChalleng abstract fun getTranslationKey(): String? fun nextEntry(playerName: String, audience: Audience) { - broadcast(prefix, "event.$key.collect", listOf(playerName, getTranslationKey()?.let { "" } ?: "")) + broadcast(prefix, "event.$key.collect", listOf(playerName, getTranslationKey() ?: "")) audience.playSound(Sound.sound(Key.key("entity.chicken.egg"), Sound.Source.MASTER, 1f, 1.2f)) currentTarget = if (remainingEntries.isEmpty()) { broadcast(prefix, "event.$key.success") @@ -67,7 +67,7 @@ abstract class HuntChallenge(name: String, val key: String) : CommandChalleng private fun calcBar() { val collectedAmount = maxEntries - remainingEntries.size - val target = getTranslationKey()?.let { "" } ?: "Finished" + val target = getTranslationKey()?.let { "$it" } ?: "Finished" bar.name(miniMessage.deserialize("$typeName: $target ($collectedAmount/$maxEntries)")) } diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/force/huntDeath/DeathHunt.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/force/huntDeath/DeathHunt.kt index 19114de..a97498e 100644 --- a/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/force/huntDeath/DeathHunt.kt +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/force/huntDeath/DeathHunt.kt @@ -12,6 +12,7 @@ import de.miraculixx.mchallenge.modules.challenges.interfaces.HuntChallenge import de.miraculixx.mchallenge.utils.serializer.Serializer import de.miraculixx.mcommons.extensions.enumOf import de.miraculixx.mcommons.text.cHighlight +import de.miraculixx.mcommons.text.cMark import io.papermc.paper.event.entity.TameableDeathMessageEvent import net.kyori.adventure.audience.Audience import net.kyori.adventure.text.TranslatableComponent @@ -78,7 +79,7 @@ class DeathHunt : Challenge, HuntChallenge("deathhunt", "death_hunt") { stopHunt() } - override fun getTranslationKey() = currentTarget?.let { "Player/Pet':'Something'>" } + override fun getTranslationKey() = currentTarget?.let { "" } private fun extractValidKeys(): List { val rawJson = javaClass.getResourceAsStream("/data/deathKeys.json")?.readBytes()?.decodeToString() ?: "{}" diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGChallenge.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGChallenge.kt new file mode 100644 index 0000000..a2b8046 --- /dev/null +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGChallenge.kt @@ -0,0 +1,259 @@ +package de.miraculixx.mchallenge.modules.mods.misc.mlg + +import de.miraculixx.challenge.api.modules.challenges.Challenge +import de.miraculixx.kpaper.event.listen +import de.miraculixx.kpaper.event.register +import de.miraculixx.kpaper.event.unregister +import de.miraculixx.kpaper.extensions.broadcast +import de.miraculixx.kpaper.extensions.onlinePlayers +import de.miraculixx.kpaper.runnables.task +import de.miraculixx.kpaper.runnables.taskRunLater +import de.miraculixx.mchallenge.commands.utils.ExperimentalFeatureCommand +import de.miraculixx.mchallenge.modules.ChallengeManager +import de.miraculixx.mchallenge.modules.challenges.Challenges +import de.miraculixx.mchallenge.modules.challenges.challenges +import de.miraculixx.mchallenge.modules.challenges.getSetting +import de.miraculixx.mchallenge.modules.global.DeathListener +import de.miraculixx.mcommons.extensions.title +import de.miraculixx.mcommons.text.cMark +import de.miraculixx.mcommons.text.cmp +import de.miraculixx.mcommons.text.emptyComponent +import de.miraculixx.mcommons.text.prefix +import org.bukkit.* +import org.bukkit.entity.* +import org.bukkit.event.entity.PlayerDeathEvent +import org.bukkit.event.player.PlayerMoveEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataType +import org.bukkit.potion.PotionEffectType +import kotlin.random.Random + +class MLGChallenge : Challenge { + private val delayTime: IntRange + private val mlgHeights: IntRange + private val mlgTypes: Set + + private val surfaceBlocks = setOf( + Material.OXIDIZED_CUT_COPPER, Material.EMERALD_BLOCK, Material.MYCELIUM, + Material.PODZOL, Material.CHISELED_STONE_BRICKS, Material.BRICKS, + Material.HONEYCOMB_BLOCK, Material.SMOOTH_STONE, Material.DARK_PRISMARINE, + Material.REINFORCED_DEEPSLATE + ) + private val surfaceNetherBlocks = setOf( + Material.POLISHED_BLACKSTONE, Material.CRIMSON_NYLIUM, Material.BASALT, + Material.SHROOMLIGHT, Material.WARPED_NYLIUM, Material.NETHER_BRICKS, + ) + private val surfaceEndBlocks = setOf( + Material.END_STONE_BRICKS, Material.PURPUR_BLOCK, Material.OBSIDIAN + ) + + private val playerInventory = mutableMapOf>() + private val playerLocation = mutableMapOf() + private val mlgWorlds = mutableListOf() + private var running = false + private var mlgActive = false + + + init { + val settings = challenges.getSetting(Challenges.MLG).settings + val delaySection = settings["delay"]?.toSection()?.getValue() + delayTime = (delaySection?.get("minDelay")?.toInt()?.getValue() ?: (60 * 2))..(delaySection?.get("maxDelay")?.toInt()?.getValue() ?: (60 * 4)) + + val heightSelection = settings["height"]?.toSection()?.getValue() + mlgHeights = (heightSelection?.get("minHeight")?.toInt()?.getValue() ?: 50)..(heightSelection?.get("maxHeight")?.toInt()?.getValue() ?: 100) + + mlgTypes = if (settings["hardMLGs"]?.toBool()?.getValue() == true) MLGType.entries.toSet() + else MLGType.entries.filter { !it.hard }.toSet() + } + + override fun start(): Boolean { + if (!ExperimentalFeatureCommand.enabledFeatures.contains("update_1_21")) { + broadcast(prefix, "event.experimental.missing", listOf("update_1_21")) + task?.cancel() + return false + } + onMove.register() + onDeath.register() + return true + } + + override fun register() { + running = true + } + + override fun unregister() { + running = false + } + + override fun stop() { + task?.cancel() + onMove.unregister() + onDeath.unregister() + cleanUpMLG(false) + } + + private val checkMLGList = mutableSetOf() + private val onMove = listen(register = false) { + val player = it.player + if (!mlgActive || player.isDead || player.gameMode != GameMode.SURVIVAL) return@listen + if (checkMLGList.contains(player)) return@listen + if (it.to.blockY in -63..-61) { + checkMLGList.add(player) + taskRunLater(5) { + if (player.isDead) { + checkMLGList.remove(player) + return@taskRunLater + } + player.playSound(player, Sound.BLOCK_NOTE_BLOCK_COW_BELL, 1f, 1.3f) + } + taskRunLater(10) { + checkMLGList.remove(player) + if (player.isDead) return@taskRunLater + player.teleportAsync(playerLocation.remove(player) ?: return@taskRunLater) + player.inventory.contents = playerInventory[player] ?: emptyArray() + player.persistentDataContainer.remove(DeathListener.key) + + // Finish this MLG drop + taskRunLater(20 * 5) { cleanUpMLG(true) } + } + } + } + + private val onDeath = listen(register = false) { + val player = it.player + player.respawnLocation = playerLocation.remove(player) + if (player.persistentDataContainer.has(DeathListener.key)) { + taskRunLater(20 * 5) { cleanUpMLG(true) } + } + } + + private fun cleanUpMLG(check: Boolean) { + if (check && !mlgActive) return + mlgActive = false + playerLocation.forEach { (player, location) -> + player.teleport(location) + player.inventory.contents = playerInventory[player] ?: emptyArray() + player.persistentDataContainer.remove(DeathListener.key) + } + playerLocation.clear() + playerInventory.clear() + if (check && !onlinePlayers.any { it.gameMode == GameMode.SURVIVAL }) { + ChallengeManager.stopChallenges() + } + mlgWorlds.forEach { world -> + Bukkit.unloadWorld(world, false) + world.worldFolder.deleteRecursively() + } + mlgWorlds.clear() + } + + private var tillNext = delayTime.random() + private var nextMLG = MLGType.WATER_BUCKET + private val task = task(true, 0, 20) { + if (!running) return@task + println(tillNext) + + if (tillNext <= 0) { + tillNext = delayTime.random() + // MLG + val worlds = onlinePlayers.map { player -> + val randomBlock = when (player.world.environment) { + World.Environment.NORMAL -> surfaceBlocks.random() + World.Environment.NETHER -> surfaceNetherBlocks.random() + World.Environment.THE_END -> surfaceEndBlocks.random() + else -> Material.GRASS_BLOCK + } + val world = WorldCreator("MLG-World-${player.name}") + .environment(World.Environment.NORMAL) + .generateStructures(false) + .type(WorldType.FLAT) + .generatorSettings("{\"layers\": [{\"block\": \"bedrock\", \"height\": 1}, {\"block\": \"${randomBlock.name.lowercase()}\", \"height\": 1}], \"biome\":\"the_void\"}") + .createWorld() + world?.let { + mlgWorlds.add(it) + it.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true) + } + world to player + } + + val mlgItem = when (nextMLG) { + MLGType.WATER_BUCKET -> ItemStack(Material.WATER_BUCKET) + MLGType.COBWEB -> ItemStack(Material.COBWEB, 2) + MLGType.SLIME -> ItemStack(Material.SLIME_BLOCK) + MLGType.ENDER_PEARL -> ItemStack(Material.ENDER_PEARL) + MLGType.HORSE -> ItemStack(Material.AIR) + MLGType.PIG -> ItemStack(Material.AIR) + MLGType.VINE_LADDER -> ItemStack(if (Random.nextBoolean()) Material.VINE else Material.LADDER) + MLGType.BOAT -> ItemStack(Material.OAK_BOAT) + MLGType.MINECART -> ItemStack(Material.MINECART) + MLGType.TWISTING_VINE -> ItemStack(Material.TWISTING_VINES, 2) + MLGType.WINDCHARGE -> ItemStack(Material.WIND_CHARGE) + MLGType.MACE -> ItemStack(Material.MACE) + } + val randomHeight = mlgHeights.random() + worlds.forEach { (world, player) -> + world ?: return@forEach + + when (nextMLG) { + MLGType.HORSE -> { + world.spawn(Location(world, 0.0, -62.0, 0.0), Horse::class.java).apply { + setAdult() + setAI(false) + isGlowing = true + } + } + + MLGType.PIG -> { + world.spawn(Location(world, 0.0, -62.0, 0.0), Pig::class.java).apply { + setAdult() + setAI(false) + setSaddle(true) + isGlowing = true + } + } + + MLGType.VINE_LADDER -> { + world.getBlockAt(0, -62, 1).type = Material.GOLD_BLOCK + world.getBlockAt(0, -61, 1).type = Material.GOLD_BLOCK + } + + MLGType.MINECART -> world.getBlockAt(0, -62, 0).type = Material.RAIL + MLGType.MACE -> { + val set = setOf(EntityType.PIG, EntityType.SILVERFISH, EntityType.IRON_GOLEM, EntityType.CHICKEN, EntityType.FROG) + val entity = world.spawnEntity(Location(world, 0.0, -62.0, 0.0), set.random()) as LivingEntity + entity.setAI(false) + entity.isGlowing = true + } + + else -> Unit + } + + playerLocation[player] = player.location + player.teleportAsync(Location(world, 0.5, randomHeight.toDouble() - 64.0, 0.5, 0f, 90f)) + player.playSound(player, Sound.ENTITY_ENDERMAN_TELEPORT, 1f, 1f) + playerInventory[player] = player.inventory.contents + player.inventory.clear() + player.inventory.setItem(4, mlgItem) + player.inventory.heldItemSlot = 4 + player.persistentDataContainer.set(DeathListener.key, PersistentDataType.STRING, "mlg") + player.setGravity(false) + player.removePotionEffect(PotionEffectType.SLOW_FALLING) + } + mlgActive = true + taskRunLater(30) { + onlinePlayers.forEach { p -> + p.setGravity(true) + p.clearTitle() + } + } + + } else if (tillNext == 1) { + nextMLG = MLGType.entries.random() + onlinePlayers.forEach { player -> + player.playSound(player, Sound.ENTITY_PUFFER_FISH_BLOW_UP, 1f, 1.3f) + player.title(emptyComponent(), cmp("MLG ${nextMLG.name.replace('_', ' ')}", cMark)) + } + tillNext-- + } else tillNext-- + } +} \ No newline at end of file diff --git a/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGType.kt b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGType.kt new file mode 100644 index 0000000..5b90e97 --- /dev/null +++ b/src/main/kotlin/de/miraculixx/mchallenge/modules/mods/misc/mlg/MLGType.kt @@ -0,0 +1,17 @@ +package de.miraculixx.mchallenge.modules.mods.misc.mlg + +enum class MLGType(val hard: Boolean) { + WATER_BUCKET(false), + COBWEB(false), + SLIME(false), + ENDER_PEARL(false), + HORSE(false), + PIG(false), + VINE_LADDER(false), + WINDCHARGE(false), + MACE(false), + + BOAT(true), + MINECART(true), + TWISTING_VINE(true), +} \ No newline at end of file