From 17ace006a41499ec80b190aa3bb6a8aa9a237e57 Mon Sep 17 00:00:00 2001 From: ArikSquad <75741608+ArikSquad@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:15:34 +0200 Subject: [PATCH 1/3] feat: implement more achievements --- .../achievements/bedwars/challenge.yml | 47 +- .../achievements/skyblock/challenge.yml | 1545 +++++++++++++++++ .../achievements/skyblock/seasonal.yml | 77 + .../achievements/skyblock/tiered.yml | 487 ++++++ .../achievements/skywars/challenge.yml | 515 ++++++ .../achievements/skywars/seasonal.yml | 132 ++ configuration/achievements/skywars/tiered.yml | 144 ++ .../commands/GameStateCommand.java | 35 + .../death/BedWarsCombatTracker.java | 118 ++ .../death/BedWarsDeathHandler.java | 237 +++ .../bedwarsgame/death/BedWarsDeathResult.java | 95 + .../bedwarsgame/death/BedWarsDeathType.java | 57 + .../bedwarsgame/events/ActionGameBreak.java | 7 +- .../events/ActionGameCombatTrack.java | 46 + .../bedwarsgame/events/ActionGameDeath.java | 176 +- .../events/ActionGamePlayerEvent.java | 4 +- .../swofty/type/bedwarsgame/game/Game.java | 13 +- .../bedwarslobby/commands/RejoinCommand.java | 1 + .../achievement/AchievementCategory.java | 3 +- .../achievement/PlayerAchievementHandler.java | 30 +- .../type/lobby/gui/GUIAchievementsMenu.java | 25 +- .../type/lobby/gui/GUIHypixelLeveling.java | 3 +- .../swofty/type/lobby/gui/GUIMyProfile.java | 3 +- 23 files changed, 3636 insertions(+), 164 deletions(-) create mode 100644 configuration/achievements/skyblock/challenge.yml create mode 100644 configuration/achievements/skyblock/seasonal.yml create mode 100644 configuration/achievements/skyblock/tiered.yml create mode 100644 configuration/achievements/skywars/challenge.yml create mode 100644 configuration/achievements/skywars/seasonal.yml create mode 100644 configuration/achievements/skywars/tiered.yml create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/commands/GameStateCommand.java create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsCombatTracker.java create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathResult.java create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java create mode 100644 type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameCombatTrack.java diff --git a/configuration/achievements/bedwars/challenge.yml b/configuration/achievements/bedwars/challenge.yml index 3e015f991..8600ded12 100644 --- a/configuration/achievements/bedwars/challenge.yml +++ b/configuration/achievements/bedwars/challenge.yml @@ -36,6 +36,22 @@ achievements: goal: 200 perGame: true + - id: "bedwars.you_cant_do_that" + name: "You can't do that!" + description: "Attempt to break your teams's bed." + type: CHALLENGE + points: 5 + trigger: "bedwars.break_own_bed" + goal: 1 + + - id: "bedwars.rejoining_the_dream" + name: "Rejoining the Dream" + description: "Use the /rejoin command to warp back into a Bed Wars game." + type: CHALLENGE + points: 5 + trigger: "bedwars.rejoin_used" + goal: 1 + - id: "bedwars.cant_wake_up" name: "Can't Wake Up" description: "Enter the Slumber Hotel temple" @@ -53,6 +69,22 @@ achievements: goal: 1 customCheck: true + - id: "bedwars.shear_luck" + name: "Shear Luck" + description: "Kill a player with shears!" + type: CHALLENGE + points: 5 + trigger: "bedwars.shear_kill" + goal: 1 + + - id: "bedwars.future_is_now" + name: "The Future is Now" + description: "Punch a chest to deposit your held item." + points: 5 + type: CHALLENGE + trigger: "bedwars.chest_deposit" + goal: 1 + - id: "bedwars.diamond_hoarder" name: "Diamond Hoarder" description: "Collect at least 50 Diamonds from generators in a game" @@ -184,7 +216,7 @@ achievements: # Page 2 achievements - id: "bedwars.its_dark_down_there" name: "It's dark down there" - description: "Fall into the void 5 times in a game" + description: "Fall into the void" type: CHALLENGE points: 5 trigger: "bedwars.void_falls" @@ -246,10 +278,10 @@ achievements: - id: "bedwars.stay_away" name: "Stay away from me!" - description: "Knock 5 enemies into the void in one game" + description: "Knock 5 enemies into the void in a single game using a Knockback Stick." type: CHALLENGE points: 10 - trigger: "bedwars.void_kills" + trigger: "bedwars.knockback_void_kill" goal: 5 perGame: true @@ -278,3 +310,12 @@ achievements: trigger: "bedwars.game_kills" goal: 10 perGame: true + + - id: "bed_wars.that_s_a_first" + name: "That's a First" + description: "Win your first game of Bed Wars." + type: CHALLENGE + points: 5 + trigger: "bedwars.thats_a_first_trigger" + goal: 1 + diff --git a/configuration/achievements/skyblock/challenge.yml b/configuration/achievements/skyblock/challenge.yml new file mode 100644 index 000000000..480daff16 --- /dev/null +++ b/configuration/achievements/skyblock/challenge.yml @@ -0,0 +1,1545 @@ +achievements: + - id: "skyblock.the_family" + name: "The Family" + description: "Show 20 Fishes to Coral." + type: CHALLENGE + points: 10 + trigger: "skyblock.the_family_trigger" + goal: 1 + + - id: "skyblock.the_jade_dragon_s_egg" + name: "The Jade Dragon's Egg" + description: "Buy a Jade Dragon Egg from the dragon in Galatea." + type: CHALLENGE + points: 10 + trigger: "skyblock.the_jade_dragon_s_egg_trigger" + goal: 1 + + - id: "skyblock.lifelong_contract" + name: "Lifelong Contract" + description: "Obtain the Seal of the Family." + type: CHALLENGE + points: 15 + trigger: "skyblock.lifelong_contract_trigger" + goal: 1 + + - id: "skyblock.multi_dimensional_slayer" + name: "Multi-Dimensional Slayer" + description: "Defeat a T5 Riftstalker Bloodfiend." + type: CHALLENGE + points: 15 + trigger: "skyblock.multi_dimensional_slayer_trigger" + goal: 1 + + - id: "skyblock.six_star_meal" + name: "Six-star Meal" + description: "Consume a McGrubber's Burger." + type: CHALLENGE + points: 5 + trigger: "skyblock.six_star_meal_trigger" + goal: 1 + + - id: "skyblock.no_enchants_needed" + name: "No Enchants Needed" + description: "Drink a Burning Potion." + type: CHALLENGE + points: 5 + trigger: "skyblock.no_enchants_needed_trigger" + goal: 1 + + - id: "skyblock.rough_deal" + name: "Rough Deal" + description: "Buy something from Tomioka." + type: CHALLENGE + points: 5 + trigger: "skyblock.rough_deal_trigger" + goal: 1 + + - id: "skyblock.infinite_darkness" + name: "Infinite Darkness" + description: "Kill a squid with the Ink Wand Ability." + type: CHALLENGE + points: 5 + trigger: "skyblock.infinite_darkness_trigger" + goal: 1 + + - id: "skyblock.seriously" + name: "Seriously?" + description: "Recombobulate an Aspect of the Jerry." + type: CHALLENGE + points: 10 + trigger: "skyblock.seriously_trigger" + goal: 1 + + - id: "skyblock.second_chance" + name: "Second Chance" + description: "Consume the Saving Grace." + type: CHALLENGE + points: 5 + trigger: "skyblock.second_chance_trigger" + goal: 1 + + - id: "skyblock.baited" + name: "Baited" + description: "Craft a Bait Ring." + type: CHALLENGE + points: 5 + trigger: "skyblock.baited_trigger" + goal: 1 + + - id: "skyblock.the_golden_dragon_s_egg" + name: "The Golden Dragon's Egg" + description: "Buy a Golden Dragon Egg from the dragon in the Crystal Hollows." + type: CHALLENGE + points: 15 + trigger: "skyblock.the_golden_dragon_s_egg_trigger" + goal: 1 + + - id: "skyblock.united_in_blood" + name: "United in Blood" + description: "Obtain a Gilded Midas Staff or Midas' Sword." + type: CHALLENGE + points: 5 + trigger: "skyblock.united_in_blood_trigger" + goal: 1 + + - id: "skyblock.dojo_grand_master" + name: "Dojo Grand Master" + description: "Obtain the Black Belt from Master Tao in the Dojo." + type: CHALLENGE + points: 10 + trigger: "skyblock.dojo_grand_master_trigger" + goal: 1 + + - id: "skyblock.still_sad" + name: "Still Sad" + description: "Talk to Sad Jacquelle after finding all 9 pieces of Montezuma's soul." + type: CHALLENGE + points: 15 + trigger: "skyblock.still_sad_trigger" + goal: 1 + + - id: "skyblock.evolution" + name: "Evolution" + description: "Complete 50 Shard Fusions." + type: CHALLENGE + points: 10 + trigger: "skyblock.evolution_trigger" + goal: 1 + + - id: "skyblock.agile" + name: "Agile" + description: "Drink an Agility potion." + type: CHALLENGE + points: 5 + trigger: "skyblock.agile_trigger" + goal: 1 + + - id: "skyblock.shine_bright" + name: "Shine Bright" + description: "Reach Agatha's Milestone 5." + type: CHALLENGE + points: 10 + trigger: "skyblock.shine_bright_trigger" + goal: 1 + + - id: "skyblock.true_adventurer" + name: "True Adventurer" + description: "Reach Catacombs level 40." + type: CHALLENGE + points: 10 + trigger: "skyblock.true_adventurer_trigger" + goal: 1 + + - id: "skyblock.glass_cannon" + name: "Glass Cannon" + description: "Wear the Elegant Tux." + type: CHALLENGE + points: 15 + trigger: "skyblock.glass_cannon_trigger" + goal: 1 + + - id: "skyblock.the_crux_of_it_all" + name: "The Crux of it All" + description: "Max out all milestones on a Crux Chronomicon." + type: CHALLENGE + points: 15 + trigger: "skyblock.the_crux_of_it_all_trigger" + goal: 1 + + - id: "skyblock.brain_power" + name: "Brain Power" + description: "Drink an Adrenaline Potion." + type: CHALLENGE + points: 5 + trigger: "skyblock.brain_power_trigger" + goal: 1 + + - id: "skyblock.not_worth_it_bro" + name: "Not Worth It, Bro" + description: "Have Wizardman decide not to help you." + type: CHALLENGE + points: 5 + trigger: "skyblock.not_worth_it_bro_trigger" + goal: 1 + + - id: "skyblock.precious_minerals" + name: "Precious Minerals" + description: "Wear a full set of Emerald Armor." + type: CHALLENGE + points: 5 + trigger: "skyblock.precious_minerals_trigger" + goal: 1 + + - id: "skyblock.inner_machinations" + name: "Inner Machinations" + description: "Max out the Enigma Cloak (including extra upgrades)." + type: CHALLENGE + points: 15 + trigger: "skyblock.inner_machinations_trigger" + goal: 1 + + - id: "skyblock.blaze_wrangler" + name: "Blaze Wrangler" + description: "Grapple 5 blazes at once." + type: CHALLENGE + points: 5 + trigger: "skyblock.blaze_wrangler_trigger" + goal: 1 + + - id: "skyblock.didn_t_mean_to" + name: "Didn't mean to!" + description: "Kill a Kalhuiki Youngling." + type: CHALLENGE + points: 5 + trigger: "skyblock.didn_t_mean_to_trigger" + goal: 1 + + - id: "skyblock.forgotten_shrine" + name: "Forgotten Shrine" + description: "Reactivate a Starborn Temple." + type: CHALLENGE + points: 10 + trigger: "skyblock.forgotten_shrine_trigger" + goal: 1 + + - id: "skyblock.buzzkill" + name: "Buzzkill" + description: "Unlock all of Neuroscientist Kat's rewards in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.buzzkill_trigger" + goal: 1 + + - id: "skyblock.water_blade" + name: "Water Blade" + description: "Kill a squid using the Prismarine Blade." + type: CHALLENGE + points: 5 + trigger: "skyblock.water_blade_trigger" + goal: 1 + + - id: "skyblock.i_am_groot" + name: "I Am Groot" + description: "Wear a set of Growth Armor with a Bonus 100 HP." + type: CHALLENGE + points: 5 + trigger: "skyblock.i_am_groot_trigger" + goal: 1 + + - id: "skyblock.humble_beginnings" + name: "Humble Beginnings" + description: "Catch all 18 types of Trophy Fish." + type: CHALLENGE + points: 5 + trigger: "skyblock.humble_beginnings_trigger" + goal: 1 + + - id: "skyblock.this_is_fair" + name: "This is fair" + description: "Kill a pig using the Pigman Sword." + type: CHALLENGE + points: 5 + trigger: "skyblock.this_is_fair_trigger" + goal: 1 + + - id: "skyblock.arcadia" + name: "Arcadia" + description: "[Co-op or you] Place 5 unique Islands on your private world." + type: CHALLENGE + points: 5 + trigger: "skyblock.arcadia_trigger" + goal: 1 + + - id: "skyblock.gottagofast" + name: "Gottagofast" + description: "Wear the full Speedster Armor." + type: CHALLENGE + points: 5 + trigger: "skyblock.gottagofast_trigger" + goal: 1 + + - id: "skyblock.handyman" + name: "Handyman" + description: "Tune the Beacon on Galatea." + type: CHALLENGE + points: 5 + trigger: "skyblock.handyman_trigger" + goal: 1 + + - id: "skyblock.forbidden_fruit" + name: "Forbidden Fruit" + description: "Wear a full set of Fermento Armor." + type: CHALLENGE + points: 10 + trigger: "skyblock.forbidden_fruit_trigger" + goal: 1 + + - id: "skyblock.saddle_up" + name: "Saddle Up!" + description: "[Co-op or you] Craft a saddle." + type: CHALLENGE + points: 5 + trigger: "skyblock.saddle_up_trigger" + goal: 1 + + - id: "skyblock.lapidarist" + name: "Lapidarist" + description: "Forge a perfect Gemstone." + type: CHALLENGE + points: 15 + trigger: "skyblock.lapidarist_trigger" + goal: 1 + + - id: "skyblock.the_perfect_heist" + name: "The Perfect Heist" + description: "Gather 3 Heavy Pearls from the Matriarch and escape without taking damage." + type: CHALLENGE + points: 10 + trigger: "skyblock.the_perfect_heist_trigger" + goal: 1 + + - id: "skyblock.dojo_master" + name: "Dojo Master" + description: "Reach the highest rank in any of the Dojo challenges." + type: CHALLENGE + points: 5 + trigger: "skyblock.dojo_master_trigger" + goal: 1 + + - id: "skyblock.always_sunny_in_skyblock" + name: "Always Sunny in SkyBlock" + description: "Open the Garden Time menu in The Garden." + type: CHALLENGE + points: 10 + trigger: "skyblock.always_sunny_in_skyblock_trigger" + goal: 1 + + - id: "skyblock.i_own_this_place" + name: "I own this place!" + description: "Kill all the mini-bosses on the Crimson Isle." + type: CHALLENGE + points: 5 + trigger: "skyblock.i_own_this_place_trigger" + goal: 1 + + - id: "skyblock.friar_lawrence" + name: "Friar Lawrence" + description: "Complete the Romero & Juliette questline." + type: CHALLENGE + points: 25 + trigger: "skyblock.friar_lawrence_trigger" + goal: 1 + + - id: "skyblock.to_space_we_go" + name: "To space we go!" + description: "Use a launch pad on your private island." + type: CHALLENGE + points: 5 + trigger: "skyblock.to_space_we_go_trigger" + goal: 1 + + - id: "skyblock.he_s_bac" + name: "He's Bac!" + description: "Kill Bacte in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.he_s_bac_trigger" + goal: 1 + + - id: "skyblock.flamin_hot" + name: "Flamin Hot" + description: "Consume 200 Magma Creams using the Magma Bow." + type: CHALLENGE + points: 5 + trigger: "skyblock.flamin_hot_trigger" + goal: 1 + + - id: "skyblock.snake_charmer" + name: "Snake Charmer" + description: "Mine a Living Metal Snake in the Rift." + type: CHALLENGE + points: 5 + trigger: "skyblock.snake_charmer_trigger" + goal: 1 + + - id: "skyblock.see_ya_later" + name: "See ya later!" + description: "Fully repair Reed's Boat in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.see_ya_later_trigger" + goal: 1 + + - id: "skyblock.the_prodigy" + name: "The Prodigy" + description: "Complete Through the Campfire song at any score." + type: CHALLENGE + points: 10 + trigger: "skyblock.the_prodigy_trigger" + goal: 1 + + - id: "skyblock.no_other_choice" + name: "No Other Choice" + description: "Solve the reverse murder case in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.no_other_choice_trigger" + goal: 1 + + - id: "skyblock.gotcha" + name: "Gotcha!" + description: "Hunt a mob with a Black Hole." + type: CHALLENGE + points: 5 + trigger: "skyblock.gotcha_trigger" + goal: 1 + + - id: "skyblock.a_face_to_remember" + name: "A Face to Remember" + description: "Obtain Seraphine's old face in the Rift." + type: CHALLENGE + points: 5 + trigger: "skyblock.a_face_to_remember_trigger" + goal: 1 + + - id: "skyblock.stubborn_gifter" + name: "Stubborn Gifter" + description: "Give 1000 total gifts." + type: CHALLENGE + points: 10 + trigger: "skyblock.stubborn_gifter_trigger" + goal: 1 + + - id: "skyblock.hsssss" + name: "Hsssss" + description: "Wear the Creeper Pants." + type: CHALLENGE + points: 5 + trigger: "skyblock.hsssss_trigger" + goal: 1 + + - id: "skyblock.i_call_that_mercy" + name: "I Call That... Mercy" + description: "Place all 5 Gemstones into the Gemstone Gauntlet." + type: CHALLENGE + points: 15 + trigger: "skyblock.i_call_that_mercy_trigger" + goal: 1 + + - id: "skyblock.long_way_down" + name: "Long Way Down..." + description: "Claim the Enigma soul at the top of the Beanstalk in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.long_way_down_trigger" + goal: 1 + + - id: "skyblock.beaconator_2_0" + name: "Beaconator 2.0" + description: "Power a Beacon of any kind." + type: CHALLENGE + points: 5 + trigger: "skyblock.beaconator_2_0_trigger" + goal: 1 + + - id: "skyblock.swaying_the_vote" + name: "Swaying the Vote" + description: "Appease all of Barry's protestors in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.swaying_the_vote_trigger" + goal: 1 + + - id: "skyblock.eye_for_an_i" + name: "Eye for an I" + description: "Calm The Bankster in the Rift." + type: CHALLENGE + points: 5 + trigger: "skyblock.eye_for_an_i_trigger" + goal: 1 + + - id: "skyblock.death_from_above" + name: "Death From Above" + description: "Kill a mob using the damage from the leaping sword ability." + type: CHALLENGE + points: 5 + trigger: "skyblock.death_from_above_trigger" + goal: 1 + + - id: "skyblock.i_believe_i_can_fly" + name: "I believe I can fly!" + description: "Wear a full set of Bat Person Armor." + type: CHALLENGE + points: 5 + trigger: "skyblock.i_believe_i_can_fly_trigger" + goal: 1 + + - id: "skyblock.kuudra_conundrum" + name: "Kuudra Conundrum" + description: "Defeat Kuudra." + type: CHALLENGE + points: 5 + trigger: "skyblock.kuudra_conundrum_trigger" + goal: 1 + + - id: "skyblock.what_is_this_place" + name: "What is this place..." + description: "Enter the secret room in The End." + type: CHALLENGE + points: 10 + trigger: "skyblock.what_is_this_place_trigger" + goal: 1 + + - id: "skyblock.scam" + name: "Scam?" + description: "Pay 1,000,000 Coins for a Fairy Soul." + type: CHALLENGE + points: 5 + trigger: "skyblock.scam_trigger" + goal: 1 + + - id: "skyblock.memories_of_a_bal" + name: "Memories of a Bal" + description: "Wear a full set of Divan's Armor." + type: CHALLENGE + points: 15 + trigger: "skyblock.memories_of_a_bal_trigger" + goal: 1 + + - id: "skyblock.gotta_go_fast" + name: "Gotta go fast!" + description: "Craft a Speed Artifact." + type: CHALLENGE + points: 15 + trigger: "skyblock.gotta_go_fast_trigger" + goal: 1 + + - id: "skyblock.frozen_monster" + name: "Frozen Monster" + description: "Kill a Yeti on the Jerry Island." + type: CHALLENGE + points: 15 + trigger: "skyblock.frozen_monster_trigger" + goal: 1 + + - id: "skyblock.animal_fishing" + name: "Animal Fishing" + description: "Fish an Oasis Sea Creature." + type: CHALLENGE + points: 5 + trigger: "skyblock.animal_fishing_trigger" + goal: 1 + + - id: "skyblock.expensive_brew" + name: "Expensive Brew" + description: "Brew a Tier VIII Potion." + type: CHALLENGE + points: 10 + trigger: "skyblock.expensive_brew_trigger" + goal: 1 + + - id: "skyblock.do_you_even_voodoo" + name: "Do you even Voodoo?" + description: "Kill a mob with the Voodoo Doll ability." + type: CHALLENGE + points: 10 + trigger: "skyblock.do_you_even_voodoo_trigger" + goal: 1 + + - id: "skyblock.sea_monsters" + name: "Sea Monsters" + description: "Obtain the Sea Creature Artifact." + type: CHALLENGE + points: 5 + trigger: "skyblock.sea_monsters_trigger" + goal: 1 + + - id: "skyblock.king_of_the_sea" + name: "King Of The Sea" + description: "Wear the Guardian Chestplate." + type: CHALLENGE + points: 5 + trigger: "skyblock.king_of_the_sea_trigger" + goal: 1 + + - id: "skyblock.yucky" + name: "Yucky!" + description: "Slay the Leech Supreme in the Rift." + type: CHALLENGE + points: 10 + trigger: "skyblock.yucky_trigger" + goal: 1 + + - id: "skyblock.librarian" + name: "Librarian" + description: "Complete 10 Dungeon Journals." + type: CHALLENGE + points: 5 + trigger: "skyblock.librarian_trigger" + goal: 1 + + - id: "skyblock.weird_science" + name: "Weird Science" + description: "Syphon 50 Attributes." + type: CHALLENGE + points: 10 + trigger: "skyblock.weird_science_trigger" + goal: 1 + + - id: "skyblock.gonna_put_a_spell_on_you" + name: "Gonna Put a Spell on You" + description: "Use the Hex for the first time." + type: CHALLENGE + points: 10 + trigger: "skyblock.gonna_put_a_spell_on_you_trigger" + goal: 1 + + - id: "skyblock.two_nine" + name: "Two/Nine" + description: "Find two pieces of Montezuma's Soul." + type: CHALLENGE + points: 5 + trigger: "skyblock.two_nine_trigger" + goal: 1 + + - id: "skyblock.storage_forever" + name: "Storage Forever" + description: "Craft a Greater Backpack." + type: CHALLENGE + points: 10 + trigger: "skyblock.storage_forever_trigger" + goal: 1 + + - id: "skyblock.king_of_the_chickens" + name: "King Of The Chickens" + description: "Craft the Feather Artifact." + type: CHALLENGE + points: 5 + trigger: "skyblock.king_of_the_chickens_trigger" + goal: 1 + + - id: "skyblock.long_way_gone" + name: "Long Way Gone" + description: "Secure a Timecharm in the Rift Gallery." + type: CHALLENGE + points: 5 + trigger: "skyblock.long_way_gone_trigger" + goal: 1 + + - id: "skyblock.it_worked" + name: "It worked?!" + description: "Retrieve a Rift-Transferrable item in the overworld." + type: CHALLENGE + points: 5 + trigger: "skyblock.it_worked_trigger" + goal: 1 + + - id: "skyblock.raaaiiinnnnbbooww" + name: "Raaaiiinnnnbbooww!" + description: "Wear a full set of Fairy Armor." + type: CHALLENGE + points: 5 + trigger: "skyblock.raaaiiinnnnbbooww_trigger" + goal: 1 + + - id: "skyblock.vanquished" + name: "Vanquished" + description: "Kill a Vanquisher." + type: CHALLENGE + points: 5 + trigger: "skyblock.vanquished_trigger" + goal: 1 + + - id: "skyblock.true_alchemist" + name: "True Alchemist" + description: "Obtain the Potion Affinity Artifact." + type: CHALLENGE + points: 5 + trigger: "skyblock.true_alchemist_trigger" + goal: 1 + + - id: "skyblock.king_of_the_pets" + name: "King Of The Pets" + description: "Obtain a pet score of 100 or more." + type: CHALLENGE + points: 10 + trigger: "skyblock.king_of_the_pets_trigger" + goal: 1 + + - id: "skyblock.there_can_only_be_one" + name: "There Can Only Be One" + description: "Kill a Tier 5 Revenant Boss." + type: CHALLENGE + points: 15 + trigger: "skyblock.there_can_only_be_one_trigger" + goal: 1 + + - id: "skyblock.legendary_rod" + name: "Legendary Rod" + description: "Fish using the Rod of Legends." + type: CHALLENGE + points: 5 + trigger: "skyblock.legendary_rod_trigger" + goal: 1 + + - id: "skyblock.shining_scales" + name: "Shining Scales" + description: "Fish up a gold tier Trophy Fish." + type: CHALLENGE + points: 5 + trigger: "skyblock.shining_scales_trigger" + goal: 1 + + - id: "skyblock.bountiful_harvest" + name: "Bountiful Harvest" + description: "Find a Squash while farming." + type: CHALLENGE + points: 10 + trigger: "skyblock.bountiful_harvest_trigger" + goal: 1 + + - id: "skyblock.the_one_bottle" + name: "The One Bottle" + description: "Craft a Titanic experience bottle." + type: CHALLENGE + points: 5 + trigger: "skyblock.the_one_bottle_trigger" + goal: 1 + + - id: "skyblock.cute_little_cube" + name: "Cute Little Cube" + description: "Kill the Magma Boss." + type: CHALLENGE + points: 10 + trigger: "skyblock.cute_little_cube_trigger" + goal: 1 + + - id: "skyblock.absorb_it_all" + name: "Absorb it all!" + description: "Wear a full set of Sponge or Shark Scale Armor." + type: CHALLENGE + points: 10 + trigger: "skyblock.absorb_it_all_trigger" + goal: 1 + + - id: "skyblock.player_of_the_people" + name: "Player of the People" + description: "Serve 50 Unique Visitors in The Garden." + type: CHALLENGE + points: 15 + trigger: "skyblock.player_of_the_people_trigger" + goal: 1 + + - id: "skyblock.welcome_to_my_factory" + name: "Welcome to my Factory" + description: "[Co-op or you] Place a Farm Crystal." + type: CHALLENGE + points: 5 + trigger: "skyblock.welcome_to_my_factory_trigger" + goal: 1 + + - id: "skyblock.higher_than_a_rabbit" + name: "Higher Than a Rabbit" + description: "Wear the Spider's Boots." + type: CHALLENGE + points: 5 + trigger: "skyblock.higher_than_a_rabbit_trigger" + goal: 1 + + - id: "skyblock.smells_better" + name: "Smells Better" + description: "Wash off the King's Scent using water." + type: CHALLENGE + points: 5 + trigger: "skyblock.smells_better_trigger" + goal: 1 + + - id: "skyblock.ghost_buster" + name: "Ghost Buster" + description: "Kill a Ghost." + type: CHALLENGE + points: 5 + trigger: "skyblock.ghost_buster_trigger" + goal: 1 + + - id: "skyblock.the_real_zoo_shady" + name: "The Real Zoo Shady" + description: "Have 20 different pets in your pet menu." + type: CHALLENGE + points: 20 + trigger: "skyblock.the_real_zoo_shady_trigger" + goal: 1 + + - id: "skyblock.the_next_generation" + name: "The Next Generation" + description: "Find a Golden Goblin by throwing a Goblin Egg." + type: CHALLENGE + points: 5 + trigger: "skyblock.the_next_generation_trigger" + goal: 1 + + - id: "skyblock.amalgamation" + name: "Amalgamation" + description: "Obtain a new shard from the Fusion Machine." + type: CHALLENGE + points: 5 + trigger: "skyblock.amalgamation_trigger" + goal: 1 + + - id: "skyblock.how_to_train_your_dragon" + name: "How to train your dragon?" + description: "Find the dragon room." + type: CHALLENGE + points: 10 + trigger: "skyblock.how_to_train_your_dragon_trigger" + goal: 1 + + - id: "skyblock.watch_me_shine" + name: "Watch Me Shine" + description: "Wear the Crystal Armor Set." + type: CHALLENGE + points: 15 + trigger: "skyblock.watch_me_shine_trigger" + goal: 1 + + - id: "skyblock.explosive_ending" + name: "Explosive Ending" + description: "Survive the Blast from the Unstable Dragon." + type: CHALLENGE + points: 5 + trigger: "skyblock.explosive_ending_trigger" + goal: 1 + + - id: "skyblock.knowledge_is_power" + name: "Knowledge is Power!" + description: "Equip the Textbook Item on a pet." + type: CHALLENGE + points: 5 + trigger: "skyblock.knowledge_is_power_trigger" + goal: 1 + + - id: "skyblock.caught_the_grinch" + name: "Caught the Grinch" + description: "Kill a Grinch on the Jerry Island." + type: CHALLENGE + points: 15 + trigger: "skyblock.caught_the_grinch_trigger" + goal: 1 + + - id: "skyblock.supreme_farmer" + name: "Supreme Farmer" + description: "Wear a full set of Farm Armor." + type: CHALLENGE + points: 5 + trigger: "skyblock.supreme_farmer_trigger" + goal: 1 + + - id: "skyblock.declaring_allegiance" + name: "Declaring Allegiance" + description: "Join a Crimson Isle Faction." + type: CHALLENGE + points: 5 + trigger: "skyblock.declaring_allegiance_trigger" + goal: 1 + + - id: "skyblock.night_eyes" + name: "Night Eyes" + description: "Craft a Night Vision Charm." + type: CHALLENGE + points: 5 + trigger: "skyblock.night_eyes_trigger" + goal: 1 + + - id: "skyblock.ring_ring_who_s_this" + name: "Ring Ring... Who's this?" + description: "Buy an Abiphone." + type: CHALLENGE + points: 5 + trigger: "skyblock.ring_ring_who_s_this_trigger" + goal: 1 + + - id: "skyblock.compost_collector" + name: "Compost Collector" + description: "Retrieve 8 compost from the Composter at once." + type: CHALLENGE + points: 5 + trigger: "skyblock.compost_collector_trigger" + goal: 1 + + - id: "skyblock.time_to_start_fishing" + name: "Time To Start Fishing" + description: "[Co-op or you] Place the Pond Island." + type: CHALLENGE + points: 5 + trigger: "skyblock.time_to_start_fishing_trigger" + goal: 1 + + - id: "skyblock.wow_that_s_useful" + name: "Wow, that's useful!" + description: "Use the /wiki, /wikithis, or /wikihand command to view the Official Hypixel Wiki." + type: CHALLENGE + points: 5 + trigger: "skyblock.wow_that_s_useful_trigger" + goal: 1 + + - id: "skyblock.at_the_speed_of_light" + name: "At the speed of light" + description: "[Co-op or you] Use Crystal Fuel." + type: CHALLENGE + points: 5 + trigger: "skyblock.at_the_speed_of_light_trigger" + goal: 1 + + - id: "skyblock.the_end_race" + name: "The End Race" + description: "Complete the End Race in under 42 Seconds." + type: CHALLENGE + points: 10 + trigger: "skyblock.the_end_race_trigger" + goal: 1 + + - id: "skyblock.i_m_fast_as_heck_boy" + name: "I'm fast as heck boy!!" + description: "Obtain the Cheetah Talisman from Guildford." + type: CHALLENGE + points: 10 + trigger: "skyblock.i_m_fast_as_heck_boy_trigger" + goal: 1 + + - id: "skyblock.it_never_ends" + name: "It Never Ends" + description: "Kill a zombie using the zombie sword." + type: CHALLENGE + points: 5 + trigger: "skyblock.it_never_ends_trigger" + goal: 1 + + - id: "skyblock.prepare_for_trouble" + name: "Prepare for trouble!" + description: "Kill Corleone." + type: CHALLENGE + points: 10 + trigger: "skyblock.prepare_for_trouble_trigger" + goal: 1 + + - id: "skyblock.should_ve_stayed_cool" + name: "Should've stayed cool" + description: "Melt to death in the Magma Fields." + type: CHALLENGE + points: 5 + trigger: "skyblock.should_ve_stayed_cool_trigger" + goal: 1 + + - id: "skyblock.helpful_hand" + name: "Helpful Hand" + description: "Give Fetchur the item he asks for." + type: CHALLENGE + points: 5 + trigger: "skyblock.helpful_hand_trigger" + goal: 1 + + - id: "skyblock.spiky" + name: "Spiky" + description: "Apply the Thorns 3 enchantment to an armor piece." + type: CHALLENGE + points: 5 + trigger: "skyblock.spiky_trigger" + goal: 1 + + - id: "skyblock.peak_of_the_mountain" + name: "Peak of the Mountain" + description: "Reach tier V of the Heart of the Mountain." + type: CHALLENGE + points: 15 + trigger: "skyblock.peak_of_the_mountain_trigger" + goal: 1 + + - id: "skyblock.mystical" + name: "Mystical" + description: "Use a Recombobulator 3000 to obtain a Mythic rarity." + type: CHALLENGE + points: 10 + trigger: "skyblock.mystical_trigger" + goal: 1 + + - id: "skyblock.big_game_fisher" + name: "Big Game Fisher" + description: "Kill a sea creature that requires fishing level 20 or higher." + type: CHALLENGE + points: 15 + trigger: "skyblock.big_game_fisher_trigger" + goal: 1 + + - id: "skyblock.magical_place" + name: "Magical Place" + description: "Find the Fairy Grotto." + type: CHALLENGE + points: 10 + trigger: "skyblock.magical_place_trigger" + goal: 1 + + - id: "skyblock.upgrades_people_upgrades" + name: "Upgrades people, Upgrades!" + description: "Recombobulate any item." + type: CHALLENGE + points: 10 + trigger: "skyblock.upgrades_people_upgrades_trigger" + goal: 1 + + - id: "skyblock.a_good_spider_is_a_dead_spider" + name: "A good spider is a dead spider" + description: "Kill the Broodmother." + type: CHALLENGE + points: 10 + trigger: "skyblock.a_good_spider_is_a_dead_spider_trigger" + goal: 1 + + - id: "skyblock.goblin_slayer" + name: "Goblin Slayer" + description: "Get 100 points during the Goblin Raid event." + type: CHALLENGE + points: 5 + trigger: "skyblock.goblin_slayer_trigger" + goal: 1 + + - id: "skyblock.sirius_business" + name: "Sirius Business" + description: "Participate in the Dark Auction." + type: CHALLENGE + points: 10 + trigger: "skyblock.sirius_business_trigger" + goal: 1 + + - id: "skyblock.the_flash" + name: "The Flash" + description: "Reach a speed of 500%" + type: CHALLENGE + points: 5 + trigger: "skyblock.the_flash_trigger" + goal: 1 + + - id: "skyblock.i_m_not_dead" + name: "I'm Not Dead!" + description: "Enter the Rift." + type: CHALLENGE + points: 5 + trigger: "skyblock.i_m_not_dead_trigger" + goal: 1 + + - id: "skyblock.worth_it" + name: "Worth it" + description: "Spend more than 200 levels on a single Sword enchantment." + type: CHALLENGE + points: 10 + trigger: "skyblock.worth_it_trigger" + goal: 1 + + - id: "skyblock.indiana_bones" + name: "Indiana Bones" + description: "Find 10 secrets in a single Dungeon run." + type: CHALLENGE + points: 10 + trigger: "skyblock.indiana_bones_trigger" + goal: 1 + + - id: "skyblock.hidden_secrets" + name: "Hidden Secrets" + description: "Find a Dark Monolith." + type: CHALLENGE + points: 5 + trigger: "skyblock.hidden_secrets_trigger" + goal: 1 + + - id: "skyblock.a_good_review" + name: "A Good Review" + description: "Feed Don Expresso some Tasty Mithril." + type: CHALLENGE + points: 5 + trigger: "skyblock.a_good_review_trigger" + goal: 1 + + - id: "skyblock.jerry" + name: "Jerry!!" + description: "Use an Inflatable Jerry." + type: CHALLENGE + points: 5 + trigger: "skyblock.jerry_trigger" + goal: 1 + + - id: "skyblock.friend_for_life" + name: "Friend for Life" + description: "Level up a pet to 100." + type: CHALLENGE + points: 20 + trigger: "skyblock.friend_for_life_trigger" + goal: 1 + + - id: "skyblock.next_level" + name: "Next Level" + description: "Upgrade an item to a dungeon item." + type: CHALLENGE + points: 10 + trigger: "skyblock.next_level_trigger" + goal: 1 + + - id: "skyblock.more_space" + name: "More Space" + description: "[Co-op or you] Expand a minion using the Minion Expander." + type: CHALLENGE + points: 5 + trigger: "skyblock.more_space_trigger" + goal: 1 + + - id: "skyblock.dullahan" + name: "Dullahan" + description: "Kill a Headless Horseman." + type: CHALLENGE + points: 5 + trigger: "skyblock.dullahan_trigger" + goal: 1 + + - id: "skyblock.fancy_farming" + name: "Fancy Farming" + description: "Change your Barn skin in The Garden." + type: CHALLENGE + points: 5 + trigger: "skyblock.fancy_farming_trigger" + goal: 1 + + - id: "skyblock.safety_first" + name: "Safety First" + description: "Obtain Stonk." + type: CHALLENGE + points: 5 + trigger: "skyblock.safety_first_trigger" + goal: 1 + + - id: "skyblock.overkill" + name: "Overkill" + description: "Drink a Critical 3 Potion." + type: CHALLENGE + points: 5 + trigger: "skyblock.overkill_trigger" + goal: 1 + + - id: "skyblock.wonderful_treasures" + name: "Wonderful Treasures" + description: "Open an Obsidian Chest." + type: CHALLENGE + points: 10 + trigger: "skyblock.wonderful_treasures_trigger" + goal: 1 + + - id: "skyblock.a_royal_meeting" + name: "A Royal Meeting" + description: "Obtain the King's Talisman." + type: CHALLENGE + points: 5 + trigger: "skyblock.a_royal_meeting_trigger" + goal: 1 + + - id: "skyblock.three_birds_one_arrow" + name: "Three Birds, One Arrow" + description: "Kill 3 monsters with one shot from the Runaan's Bow." + type: CHALLENGE + points: 5 + trigger: "skyblock.three_birds_one_arrow_trigger" + goal: 1 + + - id: "skyblock.zookeeper" + name: "Zookeeper" + description: "Buy a pet from Oringo during the Traveling Zoo." + type: CHALLENGE + points: 10 + trigger: "skyblock.zookeeper_trigger" + goal: 1 + + - id: "skyblock.rebirth" + name: "Rebirth" + description: "Kill a Fairy while you are a Ghost." + type: CHALLENGE + points: 5 + trigger: "skyblock.rebirth_trigger" + goal: 1 + + - id: "skyblock.s_squad" + name: "S+ Squad" + description: "Get an S+ Score in a Dungeon." + type: CHALLENGE + points: 15 + trigger: "skyblock.s_squad_trigger" + goal: 1 + + - id: "skyblock.bigger_storage_is_seeded" + name: "Bigger Storage Is Seeded" + description: "[Co-op or you] Place a Large Storage Chest." + type: CHALLENGE + points: 5 + trigger: "skyblock.bigger_storage_is_seeded_trigger" + goal: 1 + + - id: "skyblock.sacrifices_must_be_made" + name: "Sacrifices must be made" + description: "Salvage an item for Essence." + type: CHALLENGE + points: 5 + trigger: "skyblock.sacrifices_must_be_made_trigger" + goal: 1 + + - id: "skyblock.fortunate" + name: "Fortunate" + description: "Apply the Fortune enchantment to an item." + type: CHALLENGE + points: 5 + trigger: "skyblock.fortunate_trigger" + goal: 1 + + - id: "skyblock.the_flint_bros" + name: "The Flint Bros!" + description: "Find both Pat and Rick." + type: CHALLENGE + points: 5 + trigger: "skyblock.the_flint_bros_trigger" + goal: 1 + + - id: "skyblock.tough_choice" + name: "Tough Choice" + description: "Apply an Ultimate Enchantment on an item." + type: CHALLENGE + points: 5 + trigger: "skyblock.tough_choice_trigger" + goal: 1 + + - id: "skyblock.every_little_bit_helps" + name: "Every Little Bit Helps" + description: "Apply a Hot or Fuming Potato Book to an item." + type: CHALLENGE + points: 5 + trigger: "skyblock.every_little_bit_helps_trigger" + goal: 1 + + - id: "skyblock.advanced_transportation" + name: "Advanced Transportation" + description: "Sell an item using the Enchanted Hopper." + type: CHALLENGE + points: 5 + trigger: "skyblock.advanced_transportation_trigger" + goal: 1 + + - id: "skyblock.quite_the_crowd" + name: "Quite the Crowd" + description: "Have 5 visitors at once in The Garden." + type: CHALLENGE + points: 5 + trigger: "skyblock.quite_the_crowd_trigger" + goal: 1 + + - id: "skyblock.wham_pow" + name: "WHAM! POW!" + description: "Kill a Bat Piñata." + type: CHALLENGE + points: 5 + trigger: "skyblock.wham_pow_trigger" + goal: 1 + + - id: "skyblock.wanderer" + name: "Wanderer" + description: "Discover every Area of the Hub" + type: CHALLENGE + points: 5 + trigger: "skyblock.wanderer_trigger" + goal: 1 + + - id: "skyblock.suited_up" + name: "Suited Up" + description: "Wear all forms of equipment at the same time." + type: CHALLENGE + points: 5 + trigger: "skyblock.suited_up_trigger" + goal: 1 + + - id: "skyblock.flawless" + name: "Flawless" + description: "Beat a dungeon without anyone dying." + type: CHALLENGE + points: 10 + trigger: "skyblock.flawless_trigger" + goal: 1 + + - id: "skyblock.caretaker" + name: "Caretaker" + description: "Level up a pet to 80." + type: CHALLENGE + points: 10 + trigger: "skyblock.caretaker_trigger" + goal: 1 + + - id: "skyblock.treasure_fishing" + name: "Treasure Fishing" + description: "Fish up a Large Treasure." + type: CHALLENGE + points: 10 + trigger: "skyblock.treasure_fishing_trigger" + goal: 1 + + - id: "skyblock.dungeon_explorer" + name: "Dungeon Explorer" + description: "Get a score on exploration of 95 or more in a dungeon." + type: CHALLENGE + points: 10 + trigger: "skyblock.dungeon_explorer_trigger" + goal: 1 + + - id: "skyblock.smell_like_roses" + name: "Smell like roses" + description: "Take down an Endstone Protector." + type: CHALLENGE + points: 5 + trigger: "skyblock.smell_like_roses_trigger" + goal: 1 + + - id: "skyblock.happy_holidays" + name: "Happy Holidays" + description: "Collect all 20 White Gifts on the Jerry Island." + type: CHALLENGE + points: 5 + trigger: "skyblock.happy_holidays_trigger" + goal: 1 + + - id: "skyblock.deep_storage" + name: "Deep Storage" + description: "Gain an extra Ender Chest page from Elizabeth." + type: CHALLENGE + points: 5 + trigger: "skyblock.deep_storage_trigger" + goal: 1 + + - id: "skyblock.fully_evolved" + name: "Fully Evolved" + description: "Obtain a legendary pet." + type: CHALLENGE + points: 10 + trigger: "skyblock.fully_evolved_trigger" + goal: 1 + + - id: "skyblock.higher_enchants" + name: "Higher Enchants" + description: "Obtain a level 6 enchantment book." + type: CHALLENGE + points: 10 + trigger: "skyblock.higher_enchants_trigger" + goal: 1 + + - id: "skyblock.master_enchanter" + name: "Master Enchanter" + description: "Enchant something using 64 levels." + type: CHALLENGE + points: 5 + trigger: "skyblock.master_enchanter_trigger" + goal: 1 + + - id: "skyblock.speedrunner" + name: "Speedrunner" + description: "Beat a Dungeon Boss in under 4 minutes." + type: CHALLENGE + points: 10 + trigger: "skyblock.speedrunner_trigger" + goal: 1 + + - id: "skyblock.oh_shiny" + name: "Oh Shiny" + description: "Mine a Glowing Block on the End Island." + type: CHALLENGE + points: 5 + trigger: "skyblock.oh_shiny_trigger" + goal: 1 + + - id: "skyblock.cleanup_crew" + name: "Cleanup Crew" + description: "Finish clearing a plot in The Garden." + type: CHALLENGE + points: 5 + trigger: "skyblock.cleanup_crew_trigger" + goal: 1 + + - id: "skyblock.dragon_slayer" + name: "Dragon Slayer" + description: "Take down a dragon." + type: CHALLENGE + points: 5 + trigger: "skyblock.dragon_slayer_trigger" + goal: 1 + + - id: "skyblock.happy_new_year" + name: "Happy New Year" + description: "Obtain a New Year's Cake from the Baker." + type: CHALLENGE + points: 5 + trigger: "skyblock.happy_new_year_trigger" + goal: 1 + + - id: "skyblock.sweet_tooth" + name: "Sweet Tooth" + description: "Find a Purple Candy." + type: CHALLENGE + points: 5 + trigger: "skyblock.sweet_tooth_trigger" + goal: 1 + + - id: "skyblock.soul_hunter" + name: "Soul Hunter" + description: "Find 20 fairy souls." + type: CHALLENGE + points: 10 + trigger: "skyblock.soul_hunter_trigger" + goal: 1 + + - id: "skyblock.a_challenging_climb" + name: "A Challenging Climb" + description: "Scale the Spider's Den." + type: CHALLENGE + points: 5 + trigger: "skyblock.a_challenging_climb_trigger" + goal: 1 + + - id: "skyblock.mass_production" + name: "Mass Production" + description: "[Co-op or you] Craft a level XI minion." + type: CHALLENGE + points: 15 + trigger: "skyblock.mass_production_trigger" + goal: 1 + + - id: "skyblock.accessories_galore" + name: "Accessories Galore" + description: "[Co-op or you] Unlock the Greater Accessory Bag upgrade." + type: CHALLENGE + points: 5 + trigger: "skyblock.accessories_galore_trigger" + goal: 1 + + - id: "skyblock.super_fuel" + name: "Super Fuel" + description: "[Co-op or you] Upgrade a minion with the Enchanted Lava Bucket." + type: CHALLENGE + points: 5 + trigger: "skyblock.super_fuel_trigger" + goal: 1 + + - id: "skyblock.promise_fulfilled" + name: "Promise Fulfilled" + description: "Max out a promising tool." + type: CHALLENGE + points: 5 + trigger: "skyblock.promise_fulfilled_trigger" + goal: 1 + + - id: "skyblock.resourceful" + name: "Resourceful" + description: "Give Rhys the materials to enter the Dwarven Mines." + type: CHALLENGE + points: 5 + trigger: "skyblock.resourceful_trigger" + goal: 1 + + - id: "skyblock.heart_of_the_end" + name: "Heart of the End" + description: "Reach the Dragon's Nest in the End." + type: CHALLENGE + points: 5 + trigger: "skyblock.heart_of_the_end_trigger" + goal: 1 + + - id: "skyblock.time_to_go_on_vacation" + name: "Time to go on vacation" + description: "[Co-op or you] Upgrade a minion with the Super Compactor 3000." + type: CHALLENGE + points: 5 + trigger: "skyblock.time_to_go_on_vacation_trigger" + goal: 1 + + - id: "skyblock.into_the_deep" + name: "Into the Deep" + description: "Reach the obsidian sanctuary in the Deep Caverns." + type: CHALLENGE + points: 5 + trigger: "skyblock.into_the_deep_trigger" + goal: 1 + + - id: "skyblock.quest_complete" + name: "Quest complete!" + description: "Complete the Villager Quest." + type: CHALLENGE + points: 5 + trigger: "skyblock.quest_complete_trigger" + goal: 1 + + - id: "skyblock.combined_efforts" + name: "Combined efforts" + description: "Start a co-op." + type: CHALLENGE + points: 5 + trigger: "skyblock.combined_efforts_trigger" + goal: 1 + + - id: "skyblock.businessman" + name: "Businessman" + description: "Complete a trade with another player." + type: CHALLENGE + points: 5 + trigger: "skyblock.businessman_trigger" + goal: 1 + + - id: "skyblock.production_expanded" + name: "Production Expanded" + description: "[Co-op or you] Unlock a new minion slot." + type: CHALLENGE + points: 5 + trigger: "skyblock.production_expanded_trigger" + goal: 1 + + - id: "skyblock.lost_soul" + name: "Lost Soul" + description: "Find a fairy soul." + type: CHALLENGE + points: 5 + trigger: "skyblock.lost_soul_trigger" + goal: 1 + + - id: "skyblock.your_big_break" + name: "Your Big Break" + description: "Survive an entire SkyBlock year without dying." + type: CHALLENGE + points: 5 + trigger: "skyblock.your_big_break_trigger" + goal: 1 + + - id: "skyblock.your_adventure_begins" + name: "Your adventure begins..." + description: "Travel to hub from your island." + type: CHALLENGE + points: 5 + trigger: "skyblock.your_adventure_begins_trigger" + goal: 1 + diff --git a/configuration/achievements/skyblock/seasonal.yml b/configuration/achievements/skyblock/seasonal.yml new file mode 100644 index 000000000..d9ca74855 --- /dev/null +++ b/configuration/achievements/skyblock/seasonal.yml @@ -0,0 +1,77 @@ +achievements: + # Seasonal achievements from GUI - organized by season + + # ===== SUMMER SEASON ===== + - id: "skyblock.oasis_jake" + name: "Oasis Jake" + description: "Bring an Oasis Sheep Sea Creature to Jake in SkyBlock." + type: SEASONAL + season: SUMMER + points: 5 + trigger: "skyblock.oasis_jake_trigger" + goal: 1 + + - id: "skyblock.drought" + name: "Drought" + description: "Fish up a Water Hydra in the Oasis in SkyBlock." + type: SEASONAL + season: SUMMER + points: 10 + trigger: "skyblock.drought_trigger" + goal: 1 + + - id: "skyblock.ocean_in_a_bucket" + name: "Ocean in a Bucket" + description: "Craft a Magical Water Bucket in SkyBlock." + type: SEASONAL + season: SUMMER + points: 10 + trigger: "skyblock.ocean_in_a_bucket_trigger" + goal: 1 + + - id: "skyblock.cool_off" + name: "Cool Off" + description: "Vote for Marina in a SkyBlock Election." + type: SEASONAL + season: SUMMER + points: 5 + trigger: "skyblock.cool_off_trigger" + goal: 1 + + # ===== EASTER SEASON ===== + - id: "skyblock.egg_assault" + name: "Egg Assault" + description: "Kill a Zombie with eggs in SkyBlock." + type: SEASONAL + season: EASTER + points: 5 + trigger: "skyblock.egg_assault_trigger" + goal: 1 + + - id: "skyblock.egg_betrayer" + name: "Egg Betrayer" + description: "Kill a Chicken with eggs in SkyBlock." + type: SEASONAL + season: EASTER + points: 5 + trigger: "skyblock.egg_betrayer_trigger" + goal: 1 + + - id: "skyblock.egg_layer" + name: "Egg Layer" + description: "Lay 50 eggs with the Chicken Head in SkyBlock." + type: SEASONAL + season: EASTER + points: 10 + trigger: "skyblock.egg_layer_trigger" + goal: 1 + + # ===== HALLOWEEN SEASON ===== + - id: "skyblock.trick_or_treat_time" + name: "Trick or Treat Time!" + description: "Put a Halloween Minion Skin on any of your minions in SkyBlock." + type: SEASONAL + season: HALLOWEEN + points: 5 + trigger: "skyblock.trick_or_treat_time_trigger" + goal: 1 diff --git a/configuration/achievements/skyblock/tiered.yml b/configuration/achievements/skyblock/tiered.yml new file mode 100644 index 000000000..1407a7c47 --- /dev/null +++ b/configuration/achievements/skyblock/tiered.yml @@ -0,0 +1,487 @@ +achievements: + # Tiered achievements from GUI - each has 5 tiers (I-V) + + - id: "skyblock.festive_altruist" + name: "Festive Altruist" + description: "Give gifts to 5 different players" + type: TIERED + trigger: "skyblock.festive_altruist_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 10 + - tier: 3 + goal: 25 + points: 15 + - tier: 4 + goal: 50 + points: 20 + - tier: 5 + goal: 100 + points: 25 + + - id: "skyblock.treasury" + name: "Treasury" + description: "Unlock 10 Collections" + type: TIERED + trigger: "skyblock.treasury_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 20 + points: 10 + - tier: 3 + goal: 30 + points: 15 + - tier: 4 + goal: 40 + points: 20 + - tier: 5 + goal: 50 + points: 25 + + - id: "skyblock.gatherer" + name: "Gatherer" + description: "Achieve Foraging Level V" + type: TIERED + trigger: "skyblock.gatherer_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.augmentation" + name: "Augmentation" + description: "Achieve Enchanting Level V" + type: TIERED + trigger: "skyblock.augmentation_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.domesticator" + name: "Domesticator" + description: "Achieve Taming Level V" + type: TIERED + trigger: "skyblock.domesticator_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.harvester" + name: "Harvester" + description: "Achieve Farming Level V" + type: TIERED + trigger: "skyblock.harvester_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.excavator" + name: "Excavator" + description: "Achieve Mining Level V" + type: TIERED + trigger: "skyblock.excavator_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.hard_working_miner" + name: "Hard Working Miner" + description: "Complete 5 commissions" + type: TIERED + trigger: "skyblock.hard_working_miner_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 25 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 500 + points: 25 + + - id: "skyblock.minion_lover" + name: "Minion Lover" + description: "[Co-op or you] Craft 10 unique minions" + type: TIERED + trigger: "skyblock.minion_lover_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 25 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 500 + points: 25 + + - id: "skyblock.divan_s_treasures" + name: "Divan's Treasures" + description: "Find treasures in the Mines of Divan 30 times" + type: TIERED + trigger: "skyblock.divan_s_treasures_trigger" + tiers: + - tier: 1 + goal: 30 + points: 5 + - tier: 2 + goal: 60 + points: 10 + - tier: 3 + goal: 90 + points: 15 + - tier: 4 + goal: 120 + points: 20 + - tier: 5 + goal: 150 + points: 25 + + - id: "skyblock.combat" + name: "Combat" + description: "Achieve Combat Level V" + type: TIERED + trigger: "skyblock.combat_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.people_pleaser" + name: "People Pleaser" + description: "Serve 5 Total Visitors in The Garden" + type: TIERED + trigger: "skyblock.people_pleaser_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 25 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 500 + points: 25 + + - id: "skyblock.predator" + name: "Predator" + description: "Achieve Hunting Level V" + type: TIERED + trigger: "skyblock.predator_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 10 + - tier: 3 + goal: 15 + points: 15 + - tier: 4 + goal: 20 + points: 20 + - tier: 5 + goal: 25 + points: 25 + + - id: "skyblock.climbing_the_ranks" + name: "Climbing the Ranks" + description: "Reach SkyBlock Level 40" + type: TIERED + trigger: "skyblock.climbing_the_ranks_trigger" + tiers: + - tier: 1 + goal: 40 + points: 5 + - tier: 2 + goal: 80 + points: 10 + - tier: 3 + goal: 120 + points: 15 + - tier: 4 + goal: 160 + points: 20 + - tier: 5 + goal: 200 + points: 25 + + - id: "skyblock.goblin_killer" + name: "Goblin Killer" + description: "Kill 5 Golden Goblins" + type: TIERED + trigger: "skyblock.goblin_killer_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 10 + - tier: 3 + goal: 25 + points: 15 + - tier: 4 + goal: 50 + points: 20 + - tier: 5 + goal: 100 + points: 25 + + - id: "skyblock.angler" + name: "Angler" + description: "Achieve Fishing Level V" + type: TIERED + trigger: "skyblock.angler_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.concoctor" + name: "Concoctor" + description: "Achieve Alchemy Level V" + type: TIERED + trigger: "skyblock.concoctor_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 5 + - tier: 3 + goal: 15 + points: 10 + - tier: 4 + goal: 20 + points: 10 + - tier: 5 + goal: 25 + points: 15 + + - id: "skyblock.crystal_nucleus" + name: "Crystal Nucleus" + description: "Complete the Crystal Nucleus 5 times" + type: TIERED + trigger: "skyblock.crystal_nucleus_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 10 + points: 10 + - tier: 3 + goal: 15 + points: 15 + - tier: 4 + goal: 20 + points: 20 + - tier: 5 + goal: 25 + points: 25 + + - id: "skyblock.curator" + name: "Curator" + description: "Donate 10 items/sets to the Museum" + type: TIERED + trigger: "skyblock.curator_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 25 + points: 10 + - tier: 3 + goal: 50 + points: 15 + - tier: 4 + goal: 100 + points: 20 + - tier: 5 + goal: 150 + points: 25 + + - id: "skyblock.treasure_hunter" + name: "Treasure Hunter" + description: "Find 10 Secrets in dungeons" + type: TIERED + trigger: "skyblock.treasure_hunter_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 50 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 1 + points: 25 + + - id: "skyblock.slayer" + name: "Slayer" + description: "Get 10k Slayer exp" + type: TIERED + trigger: "skyblock.slayer_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 50 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 500 + points: 25 + + - id: "skyblock.dungeoneer" + name: "Dungeoneer" + description: "Reach Level 15 of any Dungeon Class" + type: TIERED + trigger: "skyblock.dungeoneer_trigger" + tiers: + - tier: 1 + goal: 15 + points: 5 + - tier: 2 + goal: 20 + points: 5 + - tier: 3 + goal: 25 + points: 10 + - tier: 4 + goal: 30 + points: 10 + - tier: 5 + goal: 35 + points: 15 + diff --git a/configuration/achievements/skywars/challenge.yml b/configuration/achievements/skywars/challenge.yml new file mode 100644 index 000000000..928289884 --- /dev/null +++ b/configuration/achievements/skywars/challenge.yml @@ -0,0 +1,515 @@ +achievements: + # Challenge achievements from GUI + + - id: "skywars.blasphemous" + name: "Blasphemous" + description: "Unlock the Fallen Angel Kit." + type: CHALLENGE + points: 25 + trigger: "skywars.blasphemous_trigger" + goal: 1 + + - id: "skywars.digging_through_the_descent" + name: "Digging Through The Descent" + description: "Unlock the Archeologist Kit in the Angel's Descent." + type: CHALLENGE + points: 25 + trigger: "skywars.digging_through_the_descent_trigger" + goal: 1 + + - id: "skywars.kit_connoisseur" + name: "Kit Connoisseur" + description: "Win a game of solo normal/insane SkyWars with every single kit." + type: CHALLENGE + points: 15 + trigger: "skywars.kit_connoisseur_trigger" + goal: 1 + + - id: "skywars.skewered" + name: "Skewered" + description: "Kill a player by pricking them to death with a cactus." + type: CHALLENGE + points: 10 + trigger: "skywars.skewered_trigger" + goal: 1 + + - id: "skywars.challenger" + name: "Challenger" + description: "Reach the first SkyWars Challenge Milestone." + type: CHALLENGE + points: 5 + trigger: "skywars.challenger_trigger" + goal: 1 + + - id: "skywars.mega_warrior" + name: "Mega Warrior" + description: "Kill 20 players in a single Mega game or 12 players in a single Mega Doubles game." + type: CHALLENGE + points: 10 + trigger: "skywars.mega_warrior_trigger" + goal: 1 + + - id: "skywars.ender_party" + name: "Ender Party" + description: "Have a corrupted, time warp and normal ender pearl in your inventory at the same time." + type: CHALLENGE + points: 5 + trigger: "skywars.ender_party_trigger" + goal: 1 + + - id: "skywars.teamwork" + name: "Teamwork" + description: "Win a Mega game with all your teammates alive." + type: CHALLENGE + points: 5 + trigger: "skywars.teamwork_trigger" + goal: 1 + + - id: "skywars.all_perks" + name: "All Perks!" + description: "Purchase all perks in any mode." + type: CHALLENGE + points: 10 + trigger: "skywars.all_perks_trigger" + goal: 1 + + - id: "skywars.no_chest_challenge" + name: "No Chest Challenge" + description: "Win a Mega game without opening any chest." + type: CHALLENGE + points: 10 + trigger: "skywars.no_chest_challenge_trigger" + goal: 1 + + - id: "skywars.slow_and_steady_wins_the_race" + name: "Slow and steady wins the race" + description: "Win a game using the sloth kit." + type: CHALLENGE + points: 10 + trigger: "skywars.slow_and_steady_wins_the_race_trigger" + goal: 1 + + - id: "skywars.killstolen" + name: "Killstolen" + description: "Get 5 assists in a single game of SkyWars." + type: CHALLENGE + points: 10 + trigger: "skywars.killstolen_trigger" + goal: 1 + + - id: "skywars.teamwork_makes_the_dream_work" + name: "Teamwork makes the dream work" + description: "Win a game of teams SkyWars with 15 or more kills across your team." + type: CHALLENGE + points: 5 + trigger: "skywars.teamwork_makes_the_dream_work_trigger" + goal: 1 + + - id: "skywars.challenge_master" + name: "Challenge Master" + description: "Win a game while doing 7 or more SkyWars Challenges." + type: CHALLENGE + points: 15 + trigger: "skywars.challenge_master_trigger" + goal: 1 + + - id: "skywars.challenge_pro" + name: "Challenge Pro" + description: "Win a game while doing 3 or more SkyWars Challenges." + type: CHALLENGE + points: 10 + trigger: "skywars.challenge_pro_trigger" + goal: 1 + + - id: "skywars.max_soul_well" + name: "Max Soul Well" + description: "Max out the capacity on your soul well." + type: CHALLENGE + points: 10 + trigger: "skywars.max_soul_well_trigger" + goal: 1 + + - id: "skywars.sniper" + name: "Sniper" + description: "Get 30 bow hits in one game." + type: CHALLENGE + points: 10 + trigger: "skywars.sniper_trigger" + goal: 1 + + - id: "skywars.archer_challenge" + name: "Archer Challenge" + description: "Win a game doing the Archer Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.archer_challenge_trigger" + goal: 1 + + - id: "skywars.ultimate_warrior_challenge" + name: "Ultimate Warrior Challenge" + description: "Win a game doing the Ultimate Warrior Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.ultimate_warrior_challenge_trigger" + goal: 1 + + - id: "skywars.half_health_challenge" + name: "Half Health Challenge" + description: "Win a game doing the Half Health Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.half_health_challenge_trigger" + goal: 1 + + - id: "skywars.uh_oh" + name: "Uh oh..." + description: "Spawn the mystery mob using the zookeeper kit." + type: CHALLENGE + points: 15 + trigger: "skywars.uh_oh_trigger" + goal: 1 + + - id: "skywars.challenge_achievements" + name: "Challenge Achievements" + description: "SkyWars Unlocked: 28/59 (47%) Points: 210/510 (41%)" + type: CHALLENGE + points: 5 + trigger: "skywars.challenge_achievements_trigger" + goal: 1 + + - id: "skywars.fear_me_mortals" + name: "Fear me mortals" + description: "Max out a Mega Kit." + type: CHALLENGE + points: 15 + trigger: "skywars.fear_me_mortals_trigger" + goal: 1 + + - id: "skywars.criminal" + name: "Criminal" + description: "Use the robbery perk to drop an item from another player." + type: CHALLENGE + points: 10 + trigger: "skywars.criminal_trigger" + goal: 1 + + - id: "skywars.no_block_challenge" + name: "No Block Challenge" + description: "Win a game doing the No Block Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.no_block_challenge_trigger" + goal: 1 + + - id: "skywars.uhc_challenge" + name: "UHC Challenge" + description: "Win a game doing the UHC Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.uhc_challenge_trigger" + goal: 1 + + - id: "skywars.trolololololol" + name: "Trolololololol" + description: "Win a game with the Troll Kit." + type: CHALLENGE + points: 5 + trigger: "skywars.trolololololol_trigger" + goal: 1 + + - id: "skywars.rookie_challenge" + name: "Rookie Challenge" + description: "Win a game doing the Rookie Challenge." + type: CHALLENGE + points: 5 + trigger: "skywars.rookie_challenge_trigger" + goal: 1 + + - id: "skywars.nick_cage" + name: "Nick Cage" + description: "Unlock Nicolas Cage." + type: CHALLENGE + points: 15 + trigger: "skywars.nick_cage_trigger" + goal: 1 + + - id: "skywars.speed_runner" + name: "Speed Runner" + description: "Win a Mega game before the last chest refill." + type: CHALLENGE + points: 10 + trigger: "skywars.speed_runner_trigger" + goal: 1 + + - id: "skywars.kill_streak" + name: "Kill Streak" + description: "Get 5 kills in 15 seconds." + type: CHALLENGE + points: 10 + trigger: "skywars.kill_streak_trigger" + goal: 1 + + - id: "skywars.solo_warrior" + name: "Solo warrior" + description: "Win a Mega game while joining without a party." + type: CHALLENGE + points: 10 + trigger: "skywars.solo_warrior_trigger" + goal: 1 + + - id: "skywars.mythical" + name: "Mythical" + description: "Unlock a Mythic Kit." + type: CHALLENGE + points: 5 + trigger: "skywars.mythical_trigger" + goal: 1 + + - id: "skywars.playing_it_safe" + name: "Playing it safe" + description: "Win a game of Lucky Block Mode while only opening Insane Lucky Blocks." + type: CHALLENGE + points: 10 + trigger: "skywars.playing_it_safe_trigger" + goal: 1 + + - id: "skywars.going_ham" + name: "Going Ham" + description: "Win a game of solo SkyWars having 8 or more kills." + type: CHALLENGE + points: 5 + trigger: "skywars.going_ham_trigger" + goal: 1 + + - id: "skywars.well_deserved" + name: "Well deserved" + description: "Gain a reward from a Ranked Season." + type: CHALLENGE + points: 5 + trigger: "skywars.well_deserved_trigger" + goal: 1 + + - id: "skywars.is_this_a_portal_game" + name: "Is this a Portal game?" + description: "Obtain a stack of ender pearls." + type: CHALLENGE + points: 5 + trigger: "skywars.is_this_a_portal_game_trigger" + goal: 1 + + - id: "skywars.speed_run" + name: "Speed Run" + description: "Win a Ranked game in less than a minute." + type: CHALLENGE + points: 10 + trigger: "skywars.speed_run_trigger" + goal: 1 + + - id: "skywars.sold_your_soul" + name: "Sold Your Soul" + description: "Make a donation to the Angel of Death." + type: CHALLENGE + points: 5 + trigger: "skywars.sold_your_soul_trigger" + goal: 1 + + - id: "skywars.attention_seeking" + name: "Attention Seeking" + description: "Have a bounty placed on you in Lucky Block Mode." + type: CHALLENGE + points: 10 + trigger: "skywars.attention_seeking_trigger" + goal: 1 + + - id: "skywars.hasta_la_vista" + name: "Hasta la Vista" + description: "Punch someone into the void with a punch bow." + type: CHALLENGE + points: 5 + trigger: "skywars.hasta_la_vista_trigger" + goal: 1 + + - id: "skywars.lucky_souls" + name: "Lucky Souls" + description: "Obtain 10 Mega Perks." + type: CHALLENGE + points: 10 + trigger: "skywars.lucky_souls_trigger" + goal: 1 + + - id: "skywars.baller" + name: "Baller!" + description: "Get 2 snowball kills in one game." + type: CHALLENGE + points: 5 + trigger: "skywars.baller_trigger" + goal: 1 + + - id: "skywars.rng" + name: "RNG" + description: "Win a game using a random kit." + type: CHALLENGE + points: 5 + trigger: "skywars.rng_trigger" + goal: 1 + + - id: "skywars.who_needs_teammates" + name: "Who needs teammates?" + description: "Summon a skeleton and a blaze." + type: CHALLENGE + points: 5 + trigger: "skywars.who_needs_teammates_trigger" + goal: 1 + + - id: "skywars.happy_meal" + name: "Happy Meal" + description: "Obtain a Divine Head or better." + type: CHALLENGE + points: 10 + trigger: "skywars.happy_meal_trigger" + goal: 1 + + - id: "skywars.corruption_lord" + name: "Corruption Lord" + description: "Win a Corrupted game." + type: CHALLENGE + points: 5 + trigger: "skywars.corruption_lord_trigger" + goal: 1 + + - id: "skywars.enderdragon" + name: "Enderdragon" + description: "Play a Game with an Enderdragon." + type: CHALLENGE + points: 10 + trigger: "skywars.enderdragon_trigger" + goal: 1 + + - id: "skywars.fist_of_fury" + name: "Fist of Fury!" + description: "Kill someone with your bare hands." + type: CHALLENGE + points: 10 + trigger: "skywars.fist_of_fury_trigger" + goal: 1 + + - id: "skywars.2fast3furious" + name: "2Fast3Furious" + description: "Get a kill in the first 10 seconds of the game." + type: CHALLENGE + points: 10 + trigger: "skywars.2fast3furious_trigger" + goal: 1 + + - id: "skywars.gone_fishing" + name: "Gone Fishing" + description: "Get a Fishing Rod kill." + type: CHALLENGE + points: 5 + trigger: "skywars.gone_fishing_trigger" + goal: 1 + + - id: "skywars.the_siege" + name: "The siege" + description: "Get the final kill of a game in your spawn island." + type: CHALLENGE + points: 10 + trigger: "skywars.the_siege_trigger" + goal: 1 + + - id: "skywars.so_much_choice" + name: "So much choice" + description: "Use the map selector." + type: CHALLENGE + points: 5 + trigger: "skywars.so_much_choice_trigger" + goal: 1 + + - id: "skywars.now_i_m_enchanted" + name: "Now I'm Enchanted!" + description: "Enchant a Diamond Sword." + type: CHALLENGE + points: 10 + trigger: "skywars.now_i_m_enchanted_trigger" + goal: 1 + + - id: "skywars.shiny_stuff" + name: "Shiny Stuff" + description: "Wear a full Diamond set of armor." + type: CHALLENGE + points: 10 + trigger: "skywars.shiny_stuff_trigger" + goal: 1 + + - id: "skywars.peacemaker" + name: "Peacemaker" + description: "Win a game without killing anyone." + type: CHALLENGE + points: 10 + trigger: "skywars.peacemaker_trigger" + goal: 1 + + - id: "skywars.max_perk" + name: "Max Perk!" + description: "Max out a tiered perk." + type: CHALLENGE + points: 10 + trigger: "skywars.max_perk_trigger" + goal: 1 + + - id: "skywars.mob_spawner" + name: "Mob Spawner" + description: "Spawn a mob." + type: CHALLENGE + points: 5 + trigger: "skywars.mob_spawner_trigger" + goal: 1 + + - id: "skywars.gotcha" + name: "Gotcha!" + description: "Kill someone while you have less than 1 heart left." + type: CHALLENGE + points: 10 + trigger: "skywars.gotcha_trigger" + goal: 1 + + - id: "skywars.3_2_1_go" + name: "3; 2; 1; GO" + description: "Be the first player to open a chest." + type: CHALLENGE + points: 5 + trigger: "skywars.3_2_1_go_trigger" + goal: 1 + + - id: "skywars.well_well" + name: "Well, well" + description: "Use the Soul Well." + type: CHALLENGE + points: 5 + trigger: "skywars.well_well_trigger" + goal: 1 + + - id: "skywars.touch_of_death" + name: "Touch of Death!" + description: "Knock someone into the void with a single punch." + type: CHALLENGE + points: 10 + trigger: "skywars.touch_of_death_trigger" + goal: 1 + + - id: "skywars.legendary" + name: "LEGENDARY!!!" + description: "Obtain a Legendary item in the Soul Well or by purchasing one from the shop." + type: CHALLENGE + points: 10 + trigger: "skywars.legendary_trigger" + goal: 1 + + - id: "skywars.gapple" + name: "Gapple!" + description: "Eat a Golden Apple." + type: CHALLENGE + points: 10 + trigger: "skywars.gapple_trigger" + goal: 1 + diff --git a/configuration/achievements/skywars/seasonal.yml b/configuration/achievements/skywars/seasonal.yml new file mode 100644 index 000000000..fe81a3b87 --- /dev/null +++ b/configuration/achievements/skywars/seasonal.yml @@ -0,0 +1,132 @@ +achievements: + # Seasonal achievements from GUI - organized by season + + # ===== SUMMER SEASON ===== + - id: "skywars.bbq_in_the_sky" + name: "BBQ in the Sky" + description: "Win a game of SkyWars Mega while using the Pyro Kit." + type: SEASONAL + season: SUMMER + points: 10 + trigger: "skywars.bbq_in_the_sky_trigger" + goal: 1 + + - id: "skywars.grillmaster" + name: "Grillmaster" + description: "Kill 5 players in a SkyWars game with the BBQ Kill Messages." + type: SEASONAL + season: SUMMER + points: 10 + trigger: "skywars.grillmaster_trigger" + goal: 1 + + - id: "skywars.out_fishing" + name: "Out Fishing" + description: "Win a game of SkyWars while using the Fisherman Kit." + type: SEASONAL + season: SUMMER + points: 5 + trigger: "skywars.out_fishing_trigger" + goal: 1 + + - id: "skywars.home_run" + name: "Home Run" + description: "Knock a player into the void while using the Baseball Player Kit in SkyWars." + type: SEASONAL + season: SUMMER + points: 5 + trigger: "skywars.home_run_trigger" + goal: 1 + + # ===== HALLOWEEN SEASON ===== + - id: "skywars.full_bat_mode" + name: "Full Bat Mode" + description: "Kill a player whilst blinded with the Batguy kit equipped in SkyWars." + type: SEASONAL + season: HALLOWEEN + points: 5 + trigger: "skywars.full_bat_mode_trigger" + goal: 1 + + - id: "skywars.tis_the_season" + name: "'Tis The Season" + description: "Complete the Harvest Season Special One-Time Quest in SkyWars." + type: SEASONAL + season: HALLOWEEN + points: 10 + trigger: "skywars.tis_the_season_trigger" + goal: 1 + + - id: "skywars.rising_dead" + name: "Rising Dead" + description: "Trigger the Necromancer Perk in SkyWars." + type: SEASONAL + season: HALLOWEEN + points: 5 + trigger: "skywars.rising_dead_trigger" + goal: 1 + + # ===== HOLIDAY SEASON ===== + - id: "skywars.snowball_sniper" + name: "Snowball Sniper" + description: "Throw 3 players into the void using snowballs in a game of SkyWars." + type: SEASONAL + season: HOLIDAY + points: 10 + trigger: "skywars.snowball_sniper_trigger" + goal: 1 + + - id: "skywars.spreading_the_love" + name: "Spreading the Love" + description: "Get a kill with the Merry Projectile Trail in SkyWars." + type: SEASONAL + season: HOLIDAY + points: 10 + trigger: "skywars.spreading_the_love_trigger" + goal: 1 + + - id: "skywars.snow_wars" + name: "Snow Wars" + description: "Win a SkyWars game using the Snowman Kit." + type: SEASONAL + season: HOLIDAY + points: 5 + trigger: "skywars.snow_wars_trigger" + goal: 1 + + - id: "skywars.slice_and_ice" + name: "Slice and Ice" + description: "Kill a player while sliding on ice in SkyWars." + type: SEASONAL + season: HOLIDAY + points: 5 + trigger: "skywars.slice_and_ice_trigger" + goal: 1 + + # ===== EASTER SEASON ===== + - id: "skywars.fill_the_void" + name: "Fill the Void" + description: "Throw 3 players in the void using eggs in a game of SkyWars." + type: SEASONAL + season: EASTER + points: 10 + trigger: "skywars.fill_the_void_trigger" + goal: 1 + + - id: "skywars.saving_the_planet" + name: "Saving the Planet" + description: "Kill 2 players in 10 seconds while using the Ecologist Kit in SkyWars." + type: SEASONAL + season: EASTER + points: 10 + trigger: "skywars.saving_the_planet_trigger" + goal: 1 + + - id: "skywars.close_enough" + name: "Close Enough?" + description: "Win a game of SkyWars using the Frog Kit." + type: SEASONAL + season: EASTER + points: 10 + trigger: "skywars.close_enough_trigger" + goal: 1 diff --git a/configuration/achievements/skywars/tiered.yml b/configuration/achievements/skywars/tiered.yml new file mode 100644 index 000000000..bf658bc14 --- /dev/null +++ b/configuration/achievements/skywars/tiered.yml @@ -0,0 +1,144 @@ +achievements: + # Tiered achievements from GUI - each has 5 tiers (I-V) + + - id: "skywars.cage_hoarder" + name: "Cage Hoarder" + description: "Unlock 1 cage" + type: TIERED + trigger: "skywars.cage_hoarder_trigger" + tiers: + - tier: 1 + goal: 1 + points: 5 + - tier: 2 + goal: 5 + points: 10 + - tier: 3 + goal: 10 + points: 15 + - tier: 4 + goal: 15 + points: 20 + - tier: 5 + goal: 20 + points: 25 + + - id: "skywars.you_re_a_star" + name: "You're a Star" + description: "Reach SkyWars level 3" + type: TIERED + trigger: "skywars.you_re_a_star_trigger" + tiers: + - tier: 1 + goal: 3 + points: 5 + - tier: 2 + goal: 6 + points: 10 + - tier: 3 + goal: 9 + points: 15 + - tier: 4 + goal: 12 + points: 20 + - tier: 5 + goal: 15 + points: 25 + + - id: "skywars.head_hoarder" + name: "Head Hoarder" + description: "Gather 5 Heads" + type: TIERED + trigger: "skywars.head_hoarder_trigger" + tiers: + - tier: 1 + goal: 5 + points: 5 + - tier: 2 + goal: 25 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 250 + points: 20 + - tier: 5 + goal: 1 + points: 25 + + - id: "skywars.solo_winner" + name: "Solo Winner" + description: "Win 10 games in Solo mode" + type: TIERED + trigger: "skywars.solo_winner_trigger" + tiers: + - tier: 1 + goal: 10 + points: 5 + - tier: 2 + goal: 50 + points: 10 + - tier: 3 + goal: 100 + points: 15 + - tier: 4 + goal: 500 + points: 20 + - tier: 5 + goal: 1 + points: 25 + + - id: "skywars.kit_hoarder_normal" + name: "Kit Hoarder (Normal)" + description: "Unlock 1 Normal kit" + type: TIERED + trigger: "skywars.kit_hoarder_normal_trigger" + tiers: + - tier: 1 + goal: 1 + points: 5 + - tier: 2 + goal: 5 + points: 10 + - tier: 3 + goal: 10 + points: 15 + - tier: 4 + goal: 15 + points: 20 + + - id: "skywars.kit_hoarder_insane" + name: "Kit Hoarder (Insane)" + description: "Unlock 1 Insane kit" + type: TIERED + trigger: "skywars.kit_hoarder_insane_trigger" + tiers: + - tier: 1 + goal: 1 + points: 5 + - tier: 2 + goal: 5 + points: 10 + - tier: 3 + goal: 10 + points: 15 + - tier: 4 + goal: 15 + points: 20 + + - id: "skywars.kit_hoarder_mega" + name: "Kit Hoarder (Mega)" + description: "Unlock 1 Mega kit" + type: TIERED + trigger: "skywars.kit_hoarder_mega_trigger" + tiers: + - tier: 1 + goal: 1 + points: 5 + - tier: 2 + goal: 5 + points: 10 + - tier: 3 + goal: 10 + points: 15 diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/commands/GameStateCommand.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/commands/GameStateCommand.java new file mode 100644 index 000000000..417d9d985 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/commands/GameStateCommand.java @@ -0,0 +1,35 @@ +package net.swofty.type.bedwarsgame.commands; + +import net.minestom.server.command.builder.arguments.ArgumentString; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.swofty.commons.bedwars.map.BedWarsMapsConfig; +import net.swofty.type.bedwarsgame.game.Game; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; +import net.swofty.type.generic.command.CommandParameters; +import net.swofty.type.generic.command.HypixelCommand; +import net.swofty.type.generic.user.categories.Rank; + +@CommandParameters(aliases = "gamestate", + description = "Changes something about the game state.", + usage = "/gamestate ", + permission = Rank.ADMIN, + allowsConsole = false) +public class GameStateCommand extends HypixelCommand { + @Override + public void registerUsage(MinestomCommand command) { + ArgumentString teamArg = ArgumentType.String("team"); + command.addSyntax((sender, context) -> { + if (!permissionCheck(sender)) return; + if (!(sender instanceof BedWarsPlayer player)) return; + String team = context.get("team"); + Game game = player.getGame(); + + if (game == null) { + player.sendMessage("§cYou are not in a game."); + return; + } + + game.recordBedDestroyed(BedWarsMapsConfig.TeamKey.valueOf(team)); + }, ArgumentType.Literal("breakBed"), teamArg); + } +} diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsCombatTracker.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsCombatTracker.java new file mode 100644 index 000000000..e09174562 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsCombatTracker.java @@ -0,0 +1,118 @@ +package net.swofty.type.bedwarsgame.death; + +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.tag.Tag; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public class BedWarsCombatTracker { + + public static final Tag LAST_ATTACKER_UUID = Tag.String("bedwars_last_attacker_uuid"); + public static final Tag LAST_ATTACKER_TIME = Tag.Long("bedwars_last_attacker_time"); + public static final Tag LAST_ATTACKER_WEAPON = Tag.Integer("bedwars_last_attacker_weapon"); + public static final long ASSIST_WINDOW_MS = 5000; + + /** + * Records that a player was attacked by another player. + * Should be called whenever a player takes damage from another player. + * + * @param victim The player who was attacked + * @param attacker The player who attacked + */ + public static void recordAttack(BedWarsPlayer victim, BedWarsPlayer attacker) { + if (victim == null || attacker == null || victim.equals(attacker)) { + return; + } + + victim.setTag(LAST_ATTACKER_UUID, attacker.getUuid().toString()); + victim.setTag(LAST_ATTACKER_TIME, System.currentTimeMillis()); + ItemStack weapon = attacker.getItemInMainHand(); + if (weapon != null && !weapon.isAir()) { + victim.setTag(LAST_ATTACKER_WEAPON, weapon.material().id()); + } else { + victim.removeTag(LAST_ATTACKER_WEAPON); + } + } + + /** + * Gets the player who last attacked the victim within the assist window. + * + * @param victim The player to check + * @return The last attacker if within the assist window, null otherwise + */ + @Nullable + public static BedWarsPlayer getRecentAttacker(BedWarsPlayer victim) { + if (victim == null) { + return null; + } + + String attackerUuidStr = victim.getTag(LAST_ATTACKER_UUID); + Long attackTime = victim.getTag(LAST_ATTACKER_TIME); + + if (attackerUuidStr == null || attackTime == null) { + return null; + } + + if (System.currentTimeMillis() - attackTime > ASSIST_WINDOW_MS) { + return null; + } + + try { + UUID attackerUuid = UUID.fromString(attackerUuidStr); + if (victim.getInstance() != null) { + return victim.getInstance().getPlayers().stream() + .filter(p -> p.getUuid().equals(attackerUuid)) + .filter(p -> p instanceof BedWarsPlayer) + .map(p -> (BedWarsPlayer) p) + .findFirst() + .orElse(null); + } + } catch (IllegalArgumentException ignored) { + } + + return null; + } + + public static void clearCombatData(BedWarsPlayer player) { + if (player == null) { + return; + } + + player.removeTag(LAST_ATTACKER_UUID); + player.removeTag(LAST_ATTACKER_TIME); + player.removeTag(LAST_ATTACKER_WEAPON); + } + + public static boolean hasRecentAttacker(BedWarsPlayer victim) { + return getRecentAttacker(victim) != null; + } + + @Nullable + public static Material getLastAttackerWeapon(BedWarsPlayer victim) { + if (victim == null) { + return null; + } + + String attackerUuidStr = victim.getTag(LAST_ATTACKER_UUID); + Long attackTime = victim.getTag(LAST_ATTACKER_TIME); + Integer weaponName = victim.getTag(LAST_ATTACKER_WEAPON); + + if (attackerUuidStr == null || attackTime == null || weaponName == null) { + return null; + } + + if (System.currentTimeMillis() - attackTime > ASSIST_WINDOW_MS) { + return null; + } + + try { + return Material.fromId(weaponName); + } catch (Exception ignored) { + return null; + } + } +} + diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java new file mode 100644 index 000000000..e338f6f28 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java @@ -0,0 +1,237 @@ +package net.swofty.type.bedwarsgame.death; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.damage.Damage; +import net.minestom.server.entity.damage.DamageType; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; +import net.swofty.type.bedwarsgame.game.Game; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BedWarsDeathHandler { + + private static final int VOID_Y_THRESHOLD = 1; + public static BedWarsDeathResult calculateDeath(@NotNull BedWarsPlayer victim, @NotNull Game game) { + TeamKey teamKey = victim.getTeamKey(); + boolean isFinalKill = teamKey != null && !game.getTeamManager().isBedAlive(teamKey); + + Damage lastDamage = victim.getLastDamageSource(); + BedWarsPlayer recentAttacker = BedWarsCombatTracker.getRecentAttacker(victim); + + BedWarsDeathResult.Builder builder = BedWarsDeathResult.builder() + .victim(victim) + .isFinalKill(isFinalKill); + + // No damage source at all + if (lastDamage == null) { + return handleNoDamageSource(builder, victim, recentAttacker); + } + + Entity attacker = lastDamage.getAttacker(); + DamageType damageType = lastDamage.getType().asValue(); + + // Direct player kill + if (attacker instanceof BedWarsPlayer killer) { + return handlePlayerKill(builder, victim, killer, damageType); + } + + // Mob kill + if (attacker != null) { + return handleMobKill(builder, attacker); + } + + // Environmental death - check for assist + return handleEnvironmentalDeath(builder, victim, damageType, recentAttacker); + } + + private static BedWarsDeathResult handleNoDamageSource(BedWarsDeathResult.Builder builder, + BedWarsPlayer victim, + @Nullable BedWarsPlayer recentAttacker) { + boolean isInVoid = victim.getPosition().y() <= VOID_Y_THRESHOLD; + + if (isInVoid) { + if (recentAttacker != null) { + return builder + .deathType(BedWarsDeathType.VOID_ASSISTED) + .assistPlayer(recentAttacker) + .weaponUsed(BedWarsCombatTracker.getLastAttackerWeapon(victim)) + .build(); + } + return builder.deathType(BedWarsDeathType.VOID).build(); + } + + return builder.deathType(BedWarsDeathType.GENERIC).build(); + } + + private static BedWarsDeathResult handlePlayerKill(BedWarsDeathResult.Builder builder, + BedWarsPlayer victim, + BedWarsPlayer killer, + DamageType damageType) { + BedWarsDeathType deathType; + + if (damageType == DamageType.OUT_OF_WORLD.asValue() || victim.getPosition().y() <= VOID_Y_THRESHOLD) { + deathType = BedWarsDeathType.PLAYER_VOID_KNOCK; + } else if (damageType == DamageType.FALL.asValue()) { + deathType = BedWarsDeathType.PLAYER_FALL_KNOCK; + } else if (damageType == DamageType.ARROW.asValue() || damageType == DamageType.TRIDENT.asValue()) { + deathType = BedWarsDeathType.PLAYER_PROJECTILE; + } else if (damageType == DamageType.EXPLOSION.asValue() || damageType == DamageType.PLAYER_EXPLOSION.asValue()) { + deathType = BedWarsDeathType.PLAYER_EXPLOSION; + } else if (damageType == DamageType.ON_FIRE.asValue() || damageType == DamageType.IN_FIRE.asValue()) { + deathType = BedWarsDeathType.PLAYER_FIRE; + } else { + deathType = BedWarsDeathType.PLAYER_MELEE; + } + + Material weaponUsed = null; + ItemStack weapon = killer.getItemInMainHand(); + if (weapon != null && !weapon.isAir()) { + weaponUsed = weapon.material(); + } + + return builder + .deathType(deathType) + .killer(killer) + .weaponUsed(weaponUsed) + .build(); + } + + private static BedWarsDeathResult handleMobKill(BedWarsDeathResult.Builder builder, Entity attacker) { + return builder + .deathType(BedWarsDeathType.MOB_KILL) + .attackerEntity(attacker) + .build(); + } + + private static BedWarsDeathResult handleEnvironmentalDeath(BedWarsDeathResult.Builder builder, + BedWarsPlayer victim, + DamageType damageType, + @Nullable BedWarsPlayer recentAttacker) { + boolean isInVoid = victim.getPosition().y() <= VOID_Y_THRESHOLD; + + // Void death + if (damageType == DamageType.OUT_OF_WORLD.asValue() || isInVoid) { + if (recentAttacker != null) { + return builder + .deathType(BedWarsDeathType.VOID_ASSISTED) + .assistPlayer(recentAttacker) + .weaponUsed(BedWarsCombatTracker.getLastAttackerWeapon(victim)) + .build(); + } + return builder.deathType(BedWarsDeathType.VOID).build(); + } + + // Fall death + if (damageType == DamageType.FALL.asValue()) { + if (recentAttacker != null) { + return builder + .deathType(BedWarsDeathType.FALL_ASSISTED) + .assistPlayer(recentAttacker) + .weaponUsed(BedWarsCombatTracker.getLastAttackerWeapon(victim)) + .build(); + } + return builder.deathType(BedWarsDeathType.FALL).build(); + } + + // Fire death + if (damageType == DamageType.ON_FIRE.asValue() || damageType == DamageType.IN_FIRE.asValue() + || damageType == DamageType.LAVA.asValue()) { + return builder.deathType(BedWarsDeathType.FIRE).build(); + } + + // Explosion death + if (damageType == DamageType.EXPLOSION.asValue()) { + return builder.deathType(BedWarsDeathType.EXPLOSION).build(); + } + + return builder.deathType(BedWarsDeathType.GENERIC).build(); + } + + public static Component createDeathMessage(@NotNull BedWarsDeathResult result) { + String victimName = result.victim().getUsername(); + String victimColor = getTeamColor(result.victim()); + + Component message = switch (result.deathType()) { + case PLAYER_MELEE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was slain by "); + case PLAYER_VOID_KNOCK -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was knocked into the void by "); + case PLAYER_FALL_KNOCK -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was knocked off by "); + case PLAYER_PROJECTILE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was shot by "); + case PLAYER_EXPLOSION -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was blown up by "); + case PLAYER_FIRE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was burned to death by "); + + case VOID_ASSISTED -> createAssistedMessage(victimName, victimColor, result.assistPlayer(), " was knocked into the void by "); + case FALL_ASSISTED -> createAssistedMessage(victimName, victimColor, result.assistPlayer(), " was knocked off by "); + + case VOID -> createSimpleMessage(victimName, victimColor, " fell into the void."); + case FALL -> createSimpleMessage(victimName, victimColor, " fell from a high place."); + case FIRE -> createSimpleMessage(victimName, victimColor, " burned to death."); + case EXPLOSION -> createSimpleMessage(victimName, victimColor, " blew up."); + + case MOB_KILL -> createMobKillMessage(victimName, victimColor, result.attackerEntity()); + + case GENERIC -> createSimpleMessage(victimName, victimColor, " died."); + }; + + // Append FINAL KILL if applicable + if (result.isFinalKill()) { + message = message.append( + MiniMessage.miniMessage().deserialize(" FINAL KILL!") + ); + } + + return message; + } + + private static Component createPlayerKillMessage(String victimName, String victimColor, + @Nullable BedWarsPlayer killer, String action) { + if (killer == null) { + return createSimpleMessage(victimName, victimColor, " died."); + } + + String killerColor = getTeamColor(killer); + + return Component.text(victimColor + victimName + "§7" + action + killerColor + killer.getUsername() + "§7."); + } + + private static Component createAssistedMessage(String victimName, String victimColor, + @Nullable BedWarsPlayer assistPlayer, String action) { + if (assistPlayer == null) { + return createSimpleMessage(victimName, victimColor, " died."); + } + + String assistColor = getTeamColor(assistPlayer); + + return Component.text(victimColor + victimName + "§7" + action + assistColor + assistPlayer.getUsername() + "§7."); + } + + private static Component createSimpleMessage(String victimName, String victimColor, String suffix) { + return Component.text(victimColor + victimName + "§7" + suffix); + } + + private static Component createMobKillMessage(String victimName, String victimColor, @Nullable Entity mob) { + String mobName = mob != null + ? mob.getEntityType().name().toLowerCase().replace("_", " ") + : "a mob"; + + return Component.text(victimColor + victimName + "§7 was killed by " + mobName + "§7."); + } + + private static String getTeamColor(@Nullable BedWarsPlayer player) { + if (player == null) { + return "§7"; + } + + TeamKey teamKey = player.getTeamKey(); + if (teamKey == null) { + return "§7"; + } + + return teamKey.chatColor(); + } +} + diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathResult.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathResult.java new file mode 100644 index 000000000..f31ff0d55 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathResult.java @@ -0,0 +1,95 @@ +package net.swofty.type.bedwarsgame.death; + +import net.minestom.server.entity.Entity; +import net.minestom.server.item.Material; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record BedWarsDeathResult( + @NotNull BedWarsDeathType deathType, + @NotNull BedWarsPlayer victim, + @Nullable BedWarsPlayer killer, + @Nullable BedWarsPlayer assistPlayer, + @Nullable Entity attackerEntity, + @Nullable Material weaponUsed, + boolean isFinalKill +) { + @Nullable + public BedWarsPlayer getKillCreditPlayer() { + return killer != null ? killer : assistPlayer; + } + + public boolean hasPlayerInvolvement() { + return killer != null || assistPlayer != null; + } + + public boolean isDirectKill() { + return killer != null && assistPlayer == null; + } + + public boolean isAssistedKill() { + return assistPlayer != null && killer == null; + } + + public boolean wasKilledWith(Material material) { + return weaponUsed != null && weaponUsed == material; + } + + public static class Builder { + private BedWarsDeathType deathType = BedWarsDeathType.GENERIC; + private BedWarsPlayer victim; + private BedWarsPlayer killer; + private BedWarsPlayer assistPlayer; + private Entity attackerEntity; + private Material weaponUsed; + private boolean isFinalKill; + + public Builder victim(@NotNull BedWarsPlayer victim) { + this.victim = victim; + return this; + } + + public Builder deathType(@NotNull BedWarsDeathType deathType) { + this.deathType = deathType; + return this; + } + + public Builder killer(@Nullable BedWarsPlayer killer) { + this.killer = killer; + return this; + } + + public Builder assistPlayer(@Nullable BedWarsPlayer assistPlayer) { + this.assistPlayer = assistPlayer; + return this; + } + + public Builder attackerEntity(@Nullable Entity attackerEntity) { + this.attackerEntity = attackerEntity; + return this; + } + + public Builder weaponUsed(@Nullable Material weaponUsed) { + this.weaponUsed = weaponUsed; + return this; + } + + public Builder isFinalKill(boolean isFinalKill) { + this.isFinalKill = isFinalKill; + return this; + } + + public BedWarsDeathResult build() { + if (victim == null) { + throw new IllegalStateException("Victim must be set"); + } + return new BedWarsDeathResult(deathType, victim, killer, assistPlayer, attackerEntity, weaponUsed, isFinalKill); + } + } + + public static Builder builder() { + return new Builder(); + } +} + diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java new file mode 100644 index 000000000..c3e59bf44 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java @@ -0,0 +1,57 @@ +package net.swofty.type.bedwarsgame.death; + +public enum BedWarsDeathType { + PLAYER_MELEE(" was slain by "), + PLAYER_VOID_KNOCK(" was knocked into the void by "), + PLAYER_FALL_KNOCK(" was knocked off by "), + PLAYER_PROJECTILE(" was shot by "), + PLAYER_EXPLOSION(" was blown up by "), + PLAYER_FIRE(" was burned to death by "), + + VOID_ASSISTED(" was knocked into the void by "), + FALL_ASSISTED(" was knocked off by "), + + VOID(" fell into the void."), + FALL(" fell from a high place."), + FIRE(" burned to death."), + EXPLOSION(" blew up."), + + MOB_KILL(" was slain by "), + + GENERIC(" died."); + + private final String messageFormat; + + BedWarsDeathType(String messageFormat) { + this.messageFormat = messageFormat; + } + + public String getMessageFormat() { + return messageFormat; + } + + public boolean involvesPlayer() { + return switch (this) { + case PLAYER_MELEE, PLAYER_VOID_KNOCK, PLAYER_FALL_KNOCK, PLAYER_PROJECTILE, + PLAYER_EXPLOSION, PLAYER_FIRE, VOID_ASSISTED, FALL_ASSISTED -> true; + default -> false; + }; + } + + public boolean isAssisted() { + return this == VOID_ASSISTED || this == FALL_ASSISTED; + } + + public boolean isDirectKill() { + return switch (this) { + case PLAYER_MELEE, PLAYER_VOID_KNOCK, PLAYER_FALL_KNOCK, + PLAYER_PROJECTILE, PLAYER_EXPLOSION, PLAYER_FIRE -> true; + default -> false; + }; + } + + public boolean isVoidDeath() { + return this == VOID || this == PLAYER_VOID_KNOCK || this == VOID_ASSISTED; + } +} + diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java index 5ebeaa6d5..c7b9964b4 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java @@ -7,14 +7,14 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.tag.Tag; +import net.swofty.commons.bedwars.map.BedWarsMapsConfig; +import net.swofty.commons.bedwars.map.BedWarsMapsConfig.MapTeam; +import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; import net.swofty.type.bedwarsgame.TypeBedWarsGameLoader; import net.swofty.type.bedwarsgame.game.Game; import net.swofty.type.bedwarsgame.game.GameStatus; import net.swofty.type.bedwarsgame.stats.BedWarsStatsRecorder; import net.swofty.type.bedwarsgame.user.BedWarsPlayer; -import net.swofty.commons.bedwars.map.BedWarsMapsConfig; -import net.swofty.commons.bedwars.map.BedWarsMapsConfig.MapTeam; -import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; import net.swofty.type.generic.event.EventNodes; import net.swofty.type.generic.event.HypixelEvent; import net.swofty.type.generic.event.HypixelEventClass; @@ -57,6 +57,7 @@ public void run(PlayerBlockBreakEvent event) { if (brokenBlockPosition.sameBlock(feetPoint) || brokenBlockPosition.sameBlock(headPoint)) { // This is team X's bed if (teamKey.getName().equalsIgnoreCase(playerTeamName)) { + player.getAchievementHandler().completeAchievement("bedwars.you_cant_do_that"); player.sendMessage("§cYou cannot break your own team's bed!"); event.setCancelled(true); return; diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameCombatTrack.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameCombatTrack.java new file mode 100644 index 000000000..4874d0664 --- /dev/null +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameCombatTrack.java @@ -0,0 +1,46 @@ +package net.swofty.type.bedwarsgame.events; + +import net.minestom.server.tag.Tag; +import net.swofty.pvp.events.FinalDamageEvent; +import net.swofty.type.bedwarsgame.TypeBedWarsGameLoader; +import net.swofty.type.bedwarsgame.death.BedWarsCombatTracker; +import net.swofty.type.bedwarsgame.game.Game; +import net.swofty.type.bedwarsgame.game.GameStatus; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; +import net.swofty.type.generic.event.EventNodes; +import net.swofty.type.generic.event.HypixelEvent; +import net.swofty.type.generic.event.HypixelEventClass; + +public class ActionGameCombatTrack implements HypixelEventClass { + + @HypixelEvent(node = EventNodes.ENTITY, requireDataLoaded = false) + public void run(FinalDamageEvent event) { + if (!(event.getEntity() instanceof BedWarsPlayer victim)) { + return; + } + + if (!victim.hasTag(Tag.String("gameId"))) { + return; + } + + String gameId = victim.getTag(Tag.String("gameId")); + Game game = TypeBedWarsGameLoader.getGameById(gameId); + + if (game == null || game.getGameStatus() != GameStatus.IN_PROGRESS) { + return; + } + + if (event.getDamage().getAttacker() instanceof BedWarsPlayer attacker) { + if (!victim.equals(attacker) && !isSameTeam(victim, attacker)) { + BedWarsCombatTracker.recordAttack(victim, attacker); + } + } + } + + private boolean isSameTeam(BedWarsPlayer player1, BedWarsPlayer player2) { + String team1 = player1.getTag(Tag.String("team")); + String team2 = player2.getTag(Tag.String("team")); + return team1 != null && team1.equals(team2); + } +} + diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameDeath.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameDeath.java index 8fb1d444d..626d73def 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameDeath.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameDeath.java @@ -2,18 +2,12 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.title.Title; import net.kyori.adventure.title.TitlePart; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; -import net.minestom.server.entity.Entity; import net.minestom.server.entity.GameMode; -import net.minestom.server.entity.Player; -import net.minestom.server.entity.damage.Damage; -import net.minestom.server.entity.damage.DamageType; import net.minestom.server.event.player.PlayerDeathEvent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; @@ -25,11 +19,15 @@ import net.swofty.commons.bedwars.map.BedWarsMapsConfig.MapTeam; import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; import net.swofty.type.bedwarsgame.TypeBedWarsGameLoader; +import net.swofty.type.bedwarsgame.death.BedWarsCombatTracker; +import net.swofty.type.bedwarsgame.death.BedWarsDeathHandler; +import net.swofty.type.bedwarsgame.death.BedWarsDeathResult; +import net.swofty.type.bedwarsgame.death.BedWarsDeathType; import net.swofty.type.bedwarsgame.game.Game; import net.swofty.type.bedwarsgame.game.GameStatus; -import net.swofty.type.bedwarsgame.stats.BedWarsStatsRecorder; import net.swofty.type.bedwarsgame.shop.impl.AxeShopItem; import net.swofty.type.bedwarsgame.shop.impl.PickaxeShopItem; +import net.swofty.type.bedwarsgame.stats.BedWarsStatsRecorder; import net.swofty.type.bedwarsgame.user.BedWarsPlayer; import net.swofty.type.generic.event.EventNodes; import net.swofty.type.generic.event.HypixelEvent; @@ -42,76 +40,6 @@ public class ActionGameDeath implements HypixelEventClass { - // TODO: this is horrible - private static Component calculateDeathMessage(Player player, TextColor victimTeamColor) { - Damage lastDamage = player.getLastDamageSource(); - - Component genericDeathMessage = Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" died.").color(NamedTextColor.GRAY)); - - if (lastDamage == null) { - if (player.getPosition().y() <= 1) { - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" fell into the void.").color(NamedTextColor.GRAY)); - } - - return genericDeathMessage; - } - - Entity attacker = lastDamage.getAttacker(); - DamageType damageType = lastDamage.getType().asValue(); - - // Case 1: Killed by another player - if (attacker instanceof BedWarsPlayer killer) { - String killerTeamColorName = killer.getTeamKey().chatColor(); - TextColor killerActualColor = NamedTextColor.GRAY; - - if (killerTeamColorName != null && !killerTeamColorName.isEmpty()) { - TextColor parsedColor = NamedTextColor.NAMES.value(killerTeamColorName.toLowerCase()); - if (parsedColor != null) { - killerActualColor = parsedColor; - } - } - - // Subcase 1.1: Knocked off / into void by a player - if (damageType == DamageType.OUT_OF_WORLD.asValue()) { - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" was knocked into the void by ").color(NamedTextColor.GRAY)) - .append(Component.text(killer.getUsername()).color(killerActualColor)) - .append(Component.text(".").color(NamedTextColor.GRAY)); - } else { - // Subcase 1.2: Regular kill by a player - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" was slain by ").color(NamedTextColor.GRAY)) - .append(Component.text(killer.getUsername()).color(killerActualColor)) - .append(Component.text(".").color(NamedTextColor.GRAY)); - } - } - // Case 2: Killed by a non-player entity (mob) - else if (attacker != null) { - String entityTypeName = attacker.getEntityType().name().toLowerCase().replace("_", " "); - // Consider a mapping for more friendly entity names if desired - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" was slain by a ").color(NamedTextColor.GRAY)) - .append(Component.text(entityTypeName).color(NamedTextColor.GRAY)) - .append(Component.text(".").color(NamedTextColor.GRAY)); - } else { - if (damageType == DamageType.OUT_OF_WORLD.asValue()) { - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" fell into the void.").color(NamedTextColor.GRAY)); - } else if (damageType == DamageType.FALL.asValue()) { - if (player.getPosition().y() <= 1) { - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" fell into the void.").color(NamedTextColor.GRAY)); - } - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" fell from a high place.").color(NamedTextColor.GRAY)); - } else { - return Component.text(player.getUsername()).color(victimTeamColor) - .append(Component.text(" died due to environmental causes.").color(NamedTextColor.GRAY)); - } - } - } @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) public void run(PlayerDeathEvent event) { @@ -145,66 +73,61 @@ public void run(PlayerDeathEvent event) { ItemStack[] inventory = player.getInventory().getItemStacks(); for (ItemStack item : inventory) { if (item.material() == Material.IRON_INGOT) { - iron = item.amount(); + iron += item.amount(); } else if (item.material() == Material.GOLD_INGOT) { - gold = item.amount(); + gold += item.amount(); } else if (item.material() == Material.DIAMOND) { - diamonds = item.amount(); + diamonds += item.amount(); } else if (item.material() == Material.EMERALD) { - emeralds = item.amount(); + emeralds += item.amount(); } } - if (player.getLastDamageSource() != null && player.getLastDamageSource().getSource() instanceof BedWarsPlayer k) { + + BedWarsDeathResult deathResult = BedWarsDeathHandler.calculateDeath(player, game); + if (deathResult.deathType() == BedWarsDeathType.VOID) { + player.getAchievementHandler().completeAchievement("bedwars.its_dark_down_there"); + } + if (deathResult.isFinalKill()) { + BedWarsStatsRecorder.recordFinalDeath(player, game.getBedwarsGameType()); + } + + BedWarsPlayer itemRecipient = deathResult.getKillCreditPlayer(); + if (itemRecipient != null) { if (iron > 0) { - k.getInventory().addItemStack(ItemStack.of(Material.IRON_INGOT, iron)); - k.sendMessage("§f+ " + iron + " iron"); + itemRecipient.getInventory().addItemStack(ItemStack.of(Material.IRON_INGOT, iron)); + itemRecipient.sendMessage("§f+ " + iron + " iron"); } if (gold > 0) { - k.getInventory().addItemStack(ItemStack.of(Material.GOLD_INGOT, gold)); - k.sendMessage("§e+ " + gold + " gold"); + itemRecipient.getInventory().addItemStack(ItemStack.of(Material.GOLD_INGOT, gold)); + itemRecipient.sendMessage("§e+ " + gold + " gold"); } if (diamonds > 0) { - k.getInventory().addItemStack(ItemStack.of(Material.DIAMOND, diamonds)); - k.sendMessage("§b+ " + diamonds + " diamond"); + itemRecipient.getInventory().addItemStack(ItemStack.of(Material.DIAMOND, diamonds)); + itemRecipient.sendMessage("§b+ " + diamonds + " diamond"); } if (emeralds > 0) { - k.getInventory().addItemStack(ItemStack.of(Material.EMERALD, emeralds)); - k.sendMessage("§a+ " + emeralds + " emerald"); - } - } - TeamKey teamKey = player.getTeamKey(); - TextColor victimTeamTextColor = NamedTextColor.GRAY; - if (teamKey != null) { - TextColor parsedColor = NamedTextColor.NAMES.value(teamKey.chatColor().toLowerCase()); - if (parsedColor != null) { - victimTeamTextColor = parsedColor; + itemRecipient.getInventory().addItemStack(ItemStack.of(Material.EMERALD, emeralds)); + itemRecipient.sendMessage("§a+ " + emeralds + " emerald"); } } + TeamKey teamKey = player.getTeamKey(); boolean bedExists = teamKey != null && game.getTeamManager().isBedAlive(teamKey); - Component deathMessage = calculateDeathMessage(player, victimTeamTextColor); - - if (!bedExists) { - // Append FINAL KILL! to the death message - deathMessage = deathMessage.append( - MiniMessage.miniMessage().deserialize(" FINAL KILL!") - ); + Component deathMessage = BedWarsDeathHandler.createDeathMessage(deathResult); + handleDeathTypeActions(deathResult, game); - // Record final kill stat for the killer - if (player.getLastDamageSource() != null && - player.getLastDamageSource().getSource() instanceof BedWarsPlayer killer) { - BedWarsStatsRecorder.recordFinalKill(killer, game.getBedwarsGameType()); - } + if (!bedExists && deathResult.getKillCreditPlayer() != null) { + BedWarsStatsRecorder.recordFinalKill(deathResult.getKillCreditPlayer(), game.getBedwarsGameType()); } + event.setChatMessage(deathMessage); - // Player is in an active game, handle custom death + BedWarsCombatTracker.clearCombatData(player); player.setGameMode(GameMode.SPECTATOR); player.getInventory().clear(); if (bedExists) { - // Regular death with respawn timer final Title.Times titleTimes = Title.Times.times(Duration.ofMillis(100), Duration.ofSeconds(1), Duration.ofMillis(100)); final AtomicInteger countdown = new AtomicInteger(5); final AtomicReference taskRef = new AtomicReference<>(); @@ -294,7 +217,34 @@ public void run(PlayerDeathEvent event) { player.setFlying(true); if (game != null) { - game.playerEliminated(player); // This will also call notifyPlayerOrBedStateChanged and checkForWinCondition + game.playerEliminated(player); + } + } + } + + private void handleDeathTypeActions(BedWarsDeathResult result, Game game) { + BedWarsPlayer victim = result.victim(); + BedWarsPlayer killer = result.killer(); + BedWarsPlayer assistPlayer = result.assistPlayer(); + + if (killer != null) { + BedWarsStatsRecorder.recordKill(killer, game.getBedwarsGameType()); + } else if (assistPlayer != null) { + BedWarsStatsRecorder.recordKill(assistPlayer, game.getBedwarsGameType()); + } + + BedWarsStatsRecorder.recordDeath(victim, game.getBedwarsGameType()); + + if (result.isFinalKill()) { + if (killer != null) killer.getAchievementHandler().addProgress("bedwars.bed_wars_killer", 1); + } + + BedWarsPlayer creditPlayer = result.getKillCreditPlayer(); + if (creditPlayer != null && result.weaponUsed() != null) { + if (result.weaponUsed() == Material.SHEARS) { + creditPlayer.getAchievementHandler().completeAchievement("bedwars.shear_luck"); + } else if (result.weaponUsed() == Material.STICK) { + creditPlayer.getAchievementHandler().addProgressByTrigger("bedwars.knockback_void_kill", 1); } } } diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java index ce429cea0..50b1815f9 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java @@ -153,7 +153,7 @@ public void run(PlayerBlockInteractEvent event) { @HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = false) public void run(PlayerStartDiggingEvent event) { Block block = event.getBlock(); - Player player = event.getPlayer(); + BedWarsPlayer player = (BedWarsPlayer) event.getPlayer(); ItemStack itemInHand = player.getItemInMainHand(); if (!player.hasTag(Tag.String("gameId"))) { @@ -203,6 +203,7 @@ public void run(PlayerStartDiggingEvent event) { player.sendMessage("§7You deposited x" + itemInHand.amount() + " " + itemInHand.material().name().toLowerCase().replace("_", " ") + " into your ender chest."); + player.getAchievementHandler().completeAchievement("bedwars.future_is_now"); } else { player.sendMessage("§cYour ender chest is full!"); } @@ -266,6 +267,7 @@ public void run(PlayerStartDiggingEvent event) { if (itemAdded) { player.setItemInMainHand(ItemStack.AIR); + player.getAchievementHandler().completeAchievement("bedwars.future_is_now"); } else { player.sendMessage("§cThe team chest is full!"); } diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java index df35d570c..5d63e6e02 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java @@ -8,6 +8,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.title.Title; import net.kyori.adventure.title.TitlePart; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; @@ -15,6 +16,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; import net.minestom.server.tag.Tag; import net.minestom.server.timer.Task; import net.minestom.server.timer.TaskSchedule; @@ -26,24 +28,20 @@ import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; import net.swofty.type.bedwarsgame.BedWarsGameScoreboard; import net.swofty.type.bedwarsgame.TypeBedWarsGameLoader; +import net.swofty.type.bedwarsgame.shop.impl.AxeShopItem; +import net.swofty.type.bedwarsgame.shop.impl.PickaxeShopItem; import net.swofty.type.bedwarsgame.stats.BedWarsStatsRecorder; import net.swofty.type.bedwarsgame.user.BedWarsPlayer; import net.swofty.type.bedwarsgame.user.ExperienceCause; import net.swofty.type.generic.user.HypixelPlayer; import org.tinylog.Logger; +import java.time.Duration; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import net.kyori.adventure.title.Title; -import net.minestom.server.item.Material; -import net.swofty.type.bedwarsgame.shop.impl.AxeShopItem; -import net.swofty.type.bedwarsgame.shop.impl.PickaxeShopItem; - -import java.time.Duration; - @Getter public final class Game { @@ -230,6 +228,7 @@ public void rejoin(BedWarsPlayer player) { getPlayersAsAudience().sendMessage( Component.text(teamColor + player.getUsername() + " §7reconnected.") ); + player.getAchievementHandler().completeAchievement(""); Logger.info("Player {} rejoined game {} (team: {})", player.getUsername(), gameId, info.getTeamKey().getName()); diff --git a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/commands/RejoinCommand.java b/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/commands/RejoinCommand.java index 733b1f876..3cf6020ad 100644 --- a/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/commands/RejoinCommand.java +++ b/type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/commands/RejoinCommand.java @@ -46,6 +46,7 @@ public void registerUsage(MinestomCommand command) { // Transfer player to the game server player.asProxyPlayer().transferToWithIndication(resp.server().uuid()); + player.getAchievementHandler().completeAchievement("bedwars.rejoining_the_dream"); } else { player.sendMessage("§cYou don't have an active game to rejoin!"); } diff --git a/type.generic/src/main/java/net/swofty/type/generic/achievement/AchievementCategory.java b/type.generic/src/main/java/net/swofty/type/generic/achievement/AchievementCategory.java index aa29d9313..26e74bfa2 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/achievement/AchievementCategory.java +++ b/type.generic/src/main/java/net/swofty/type/generic/achievement/AchievementCategory.java @@ -27,7 +27,8 @@ public enum AchievementCategory { PIT("Pit", "pit", Material.DIRT), WOOL_GAMES("Wool Games", "woolgames", Material.WHITE_WOOL), COPS_AND_CRIMS("Cops and Crims", "copsandcrims", Material.IRON_BARS), - GENERAL("General", "general", Material.NETHER_STAR); + GENERAL("General", "general", Material.NETHER_STAR), + SKYBLOCK("SkyBlock", "skyblock", Material.PLAYER_HEAD); private final String displayName; private final String configKey; diff --git a/type.generic/src/main/java/net/swofty/type/generic/achievement/PlayerAchievementHandler.java b/type.generic/src/main/java/net/swofty/type/generic/achievement/PlayerAchievementHandler.java index 93f0a6df2..f0b1f1bb8 100644 --- a/type.generic/src/main/java/net/swofty/type/generic/achievement/PlayerAchievementHandler.java +++ b/type.generic/src/main/java/net/swofty/type/generic/achievement/PlayerAchievementHandler.java @@ -1,6 +1,10 @@ package net.swofty.type.generic.achievement; import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.swofty.type.generic.data.HypixelDataHandler; import net.swofty.type.generic.data.datapoints.DatapointAchievementData; import net.swofty.type.generic.user.HypixelPlayer; @@ -153,15 +157,23 @@ private void onAchievementUnlocked(AchievementDefinition def, AchievementData.Ac tierText = " " + toRoman(progress.getCurrentTier()); } - player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); - player.sendMessage("§6§lACHIEVEMENT UNLOCKED: §a" + def.getName() + tierText); - player.sendMessage(""); - - int points = def.getType() == AchievementType.TIERED - ? def.getPointsForTier(progress.getCurrentTier()) - : def.getPoints(); - player.sendMessage("§7Reward: §e+" + points + " Achievement Points"); - player.sendMessage("§6§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"); + Component tierHover = Component.text(def.getName() + tierText, NamedTextColor.GREEN).appendNewline() + .append(Component.text(def.getDescription(), NamedTextColor.GRAY)) + .appendNewline().appendNewline() + .append(Component.text("Reward:", NamedTextColor.GRAY)) + .appendNewline() + .append(Component.text(" §8+§e5 §7Achievement Points")) + .appendNewline().appendNewline() + .append(Component.text("Click to open achievements menu!", NamedTextColor.YELLOW)); + + Component obf = Component.text("A", NamedTextColor.YELLOW, TextDecoration.OBFUSCATED); + player.sendMessage( + obf + .append(Component.text(">> Achievement Unlocked: ", NamedTextColor.GREEN).decorationIfAbsent(TextDecoration.OBFUSCATED, TextDecoration.State.FALSE)) + .append(Component.text(def.getName() + tierText, NamedTextColor.GOLD).hoverEvent(HoverEvent.showText(tierHover)).decorationIfAbsent(TextDecoration.OBFUSCATED, TextDecoration.State.FALSE)) + .append(Component.text(" <<", NamedTextColor.GREEN).decorationIfAbsent(TextDecoration.OBFUSCATED, TextDecoration.State.FALSE)) + .append(obf) + ); player.playSound(net.kyori.adventure.sound.Sound.sound( net.minestom.server.sound.SoundEvent.ENTITY_PLAYER_LEVELUP, diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIAchievementsMenu.java b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIAchievementsMenu.java index b2e8e35fe..8b9610c42 100644 --- a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIAchievementsMenu.java +++ b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIAchievementsMenu.java @@ -26,12 +26,13 @@ public void onOpen(InventoryGUIOpenEvent e) { set(createCategoryItem(1, AchievementCategory.GENERAL, Material.BOOK, handler)); set(createCategoryItem(2, "Housing", Material.DARK_OAK_DOOR, handler, AchievementCategory.GENERAL)); - set(createSkyBlockItem(3, handler)); set(createCategoryItem(4, AchievementCategory.ARCADE, Material.SLIME_BALL, handler)); set(createClassicGamesItem(5, handler)); set(createSeasonalItem(6, handler)); set(createLegacyItem(7, handler)); + set(createCategoryItem(3, AchievementCategory.SKYBLOCK, Material.PLAYER_HEAD, handler)); + set(createCategoryItem(19, AchievementCategory.TNT_GAMES, Material.TNT, handler)); set(createCategoryItem(20, AchievementCategory.BLITZ_SG, Material.DIAMOND_SWORD, handler)); set(createCategoryItem(21, AchievementCategory.MEGA_WALLS, Material.SOUL_SAND, handler)); @@ -211,28 +212,6 @@ public void run(InventoryPreClickEvent e, HypixelPlayer player) { }; } - private GUIClickableItem createSkyBlockItem(int slot, PlayerAchievementHandler handler) { - return new GUIClickableItem(slot) { - @Override - public ItemStack.Builder getItem(HypixelPlayer player) { - return ItemStackCreator.getStackHead( - "§aSkyBlock Achievements", - "d7cc6687423d0570d556ac53e0676cb563bbdd9717cd8269bdebed6f6d4e7bf8", - 1, - "§7Unlocked: §b0§7/§b333 §8(0%)", - "§7Points: §e0§7/§e3,085 §8(0%)", - "", - "§eClick to view achievements!" - ); - } - - @Override - public void run(InventoryPreClickEvent e, HypixelPlayer player) { - player.sendMessage("§cSkyBlock achievements are accessed from SkyBlock!"); - } - }; - } - private GUIItem createClassicGamesItem(int slot, PlayerAchievementHandler handler) { return new GUIItem(slot) { @Override diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIHypixelLeveling.java b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIHypixelLeveling.java index 0dcc65e00..2d6222cac 100644 --- a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIHypixelLeveling.java +++ b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIHypixelLeveling.java @@ -5,7 +5,6 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.swofty.commons.StringUtility; -import net.swofty.type.generic.experience.HypixelExperience; import net.swofty.type.generic.experience.LevelReward; import net.swofty.type.generic.experience.LevelRewardRegistry; import net.swofty.type.generic.experience.PlayerExperienceHandler; @@ -263,7 +262,7 @@ public ItemStack.Builder getItem(HypixelPlayer player) { return ItemStackCreator.getStack( nameColor + "Hypixel Level Reward " + level, - Material.MINECART, + claimed ? Material.MINECART : Material.CHEST_MINECART, 1, lore.toArray(new String[0]) ); diff --git a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIMyProfile.java b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIMyProfile.java index 81ca63644..b3df1311d 100644 --- a/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIMyProfile.java +++ b/type.lobby/src/main/java/net/swofty/type/lobby/gui/GUIMyProfile.java @@ -5,7 +5,6 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.swofty.commons.StringUtility; -import net.swofty.type.generic.experience.HypixelExperience; import net.swofty.type.generic.experience.PlayerExperienceHandler; import net.swofty.type.generic.gui.inventory.HypixelInventoryGUI; import net.swofty.type.generic.gui.inventory.ItemStackCreator; @@ -122,7 +121,7 @@ public ItemStack.Builder getItem(HypixelPlayer player) { public ItemStack.Builder getItem(HypixelPlayer player) { return ItemStackCreator.getStackHead( "§aCharacter Information", - "18614241b980319c02f5ee3ae1a7fc7ebf8b3fdd5301ed3d4e2159a80dae1d2c", + player.getSkin(), 1, "§7Rank: " + player.getRank().getPrefix().trim(), "§7Level: §6" + level, From 462e51daf6353fa961c8be6f3db5ba4270295a93 Mon Sep 17 00:00:00 2001 From: ArikSquad <75741608+ArikSquad@users.noreply.github.com> Date: Fri, 26 Dec 2025 21:30:58 +0200 Subject: [PATCH 2/3] feat: more achievements --- configuration/achievements/bedwars/challenge.yml | 15 +++++++++++---- settings.gradle.kts | 12 ++++++------ .../type/bedwarsgame/events/ActionGameBreak.java | 7 ++++++- .../type/bedwarsgame/events/ActionGamePlace.java | 1 + .../bedwarsgame/events/ActionGamePlayerEvent.java | 4 ++-- .../net/swofty/type/bedwarsgame/game/Game.java | 1 + .../swofty/type/bedwarsgame/game/TeamManager.java | 3 ++- .../swofty/type/bedwarsgame/gui/GUITeamShop.java | 12 ++++++++++++ 8 files changed, 41 insertions(+), 14 deletions(-) diff --git a/configuration/achievements/bedwars/challenge.yml b/configuration/achievements/bedwars/challenge.yml index 8600ded12..02e15ba36 100644 --- a/configuration/achievements/bedwars/challenge.yml +++ b/configuration/achievements/bedwars/challenge.yml @@ -234,11 +234,11 @@ achievements: - id: "bedwars.minefield" name: "Minefield" - description: "Place 10 traps in a single game" + description: "Place 3 traps in a single game" type: CHALLENGE points: 10 trigger: "bedwars.traps_placed" - goal: 10 + goal: 1 perGame: true - id: "bedwars.not_today" @@ -294,6 +294,13 @@ achievements: goal: 5 perGame: true + - id: "bedwars.sneaky_rusher" + name: "Sneaky Rusher" + description: "Destroy a bed while being invisible" + points: 5 + trigger: "bedwars.sneaky_rusher_trigger" + goal: 1 + - id: "bedwars.ultimate_defense" name: "Ultimate Defense" description: "Have 5 active upgrades at once" @@ -311,11 +318,11 @@ achievements: goal: 10 perGame: true - - id: "bed_wars.that_s_a_first" + - id: "bedwars.thats_a_first" name: "That's a First" description: "Win your first game of Bed Wars." type: CHALLENGE points: 5 - trigger: "bedwars.thats_a_first_trigger" + trigger: "bedwars.wins" goal: 1 diff --git a/settings.gradle.kts b/settings.gradle.kts index 8b6f466b2..2262241f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,15 +16,15 @@ include(":pvp") include(":type.prototypelobby") include(":type.thefarmingislands") -include("type.spidersden") -include("type.theend") -include("type.crimsonisle") +include(":type.spidersden") +include(":type.theend") +include(":type.crimsonisle") include(":type.goldmine") include(":type.deepcaverns") include(":type.dwarvenmines") -include("type.thepark") -include("type.galatea") -include("type.backwaterbayou") +include(":type.thepark") +include(":type.galatea") +include(":type.backwaterbayou") include(":type.island") include(":type.hub") include(":type.dungeonhub") diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java index c7b9964b4..61403d2fa 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGameBreak.java @@ -6,6 +6,7 @@ import net.minestom.server.event.player.PlayerBlockBreakEvent; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; +import net.minestom.server.potion.PotionEffect; import net.minestom.server.tag.Tag; import net.swofty.commons.bedwars.map.BedWarsMapsConfig; import net.swofty.commons.bedwars.map.BedWarsMapsConfig.MapTeam; @@ -63,7 +64,7 @@ public void run(PlayerBlockBreakEvent event) { return; } if (!game.getTeamManager().isBedAlive(teamKey)) { - // Bed already destroyed logically, block might linger if not cleared perfectly + // Bed already destroyed logically; block might linger if not cleared perfectly event.setCancelled(true); return; } @@ -72,6 +73,10 @@ public void run(PlayerBlockBreakEvent event) { player.getInstance().setBlock(feetPoint, Block.AIR); player.getInstance().setBlock(headPoint, Block.AIR); + if (player.hasEffect(PotionEffect.INVISIBILITY)) { + player.getAchievementHandler().completeAchievement("bedwars.sneaky_rusher"); // break an bed while invisible + } + for (BedWarsPlayer p : game.getPlayers()) { p.sendMessage(String.format("§c§lBED DESTRUCTION §r§cTeam %s's bed was destroyed by %s%s!", teamKey.getName(), teamKey.chatColor(), player.getUsername())); diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlace.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlace.java index 3d95a070c..8bc0370c9 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlace.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlace.java @@ -73,6 +73,7 @@ public void run(PlayerBlockPlaceEvent event) { } event.setBlock(event.getBlock().withTag(TypeBedWarsGameLoader.PLAYER_PLACED_TAG, true)); + player.getAchievementHandler().addProgressByTrigger("bedwars.blocks_placed", 1); } } diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java index 50b1815f9..a7e2eafd6 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionGamePlayerEvent.java @@ -203,7 +203,7 @@ public void run(PlayerStartDiggingEvent event) { player.sendMessage("§7You deposited x" + itemInHand.amount() + " " + itemInHand.material().name().toLowerCase().replace("_", " ") + " into your ender chest."); - player.getAchievementHandler().completeAchievement("bedwars.future_is_now"); + player.getAchievementHandler().addProgressByTrigger("bedwars.chest_deposit", 1); } else { player.sendMessage("§cYour ender chest is full!"); } @@ -267,7 +267,7 @@ public void run(PlayerStartDiggingEvent event) { if (itemAdded) { player.setItemInMainHand(ItemStack.AIR); - player.getAchievementHandler().completeAchievement("bedwars.future_is_now"); + player.getAchievementHandler().addProgressByTrigger("bedwars.chest_deposit", 1); } else { player.sendMessage("§cThe team chest is full!"); } diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java index 5d63e6e02..d7120a9e0 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/Game.java @@ -518,6 +518,7 @@ private void endGame(TeamKey winningTeam) { // Record win for players on the winning team if (finalWinningTeam != null && finalWinningTeam.getName().equalsIgnoreCase(player.getTeamName())) { BedWarsStatsRecorder.recordWin(player, bedwarsGameType); + player.getAchievementHandler().addProgressByTrigger("bedwars.wins", 1); } if (player.getGameMode() != GameMode.SPECTATOR) { diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/TeamManager.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/TeamManager.java index d0756fb1c..a503287e3 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/TeamManager.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/TeamManager.java @@ -15,6 +15,7 @@ import net.swofty.commons.bedwars.map.BedWarsMapsConfig.MapTeam; import net.swofty.commons.bedwars.map.BedWarsMapsConfig.TeamKey; import net.swofty.type.bedwarsgame.TypeBedWarsGameLoader; +import net.swofty.type.bedwarsgame.user.BedWarsPlayer; import org.tinylog.Logger; import java.util.*; @@ -174,7 +175,7 @@ public void removeTeamTrap(TeamKey teamKey, String trapKey) { } } - public List getPlayersOnTeam(TeamKey teamKey) { + public List getPlayersOnTeam(TeamKey teamKey) { return game.getPlayers().stream() .filter(player -> teamKey.getName().equals(player.getTag(Tag.String("team"))) && player.isOnline() diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/gui/GUITeamShop.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/gui/GUITeamShop.java index dcabaa452..bc2866570 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/gui/GUITeamShop.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/gui/GUITeamShop.java @@ -163,6 +163,12 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { Game playerGame = TypeBedWarsGameLoader.getPlayerGame(player); if (playerGame == null || tag == null) return; + int trapSize = playerGame.getTeamManager().getTeamTraps(tag).size(); + if (trapSize >= 3) { + player.sendMessage("§cYou can't have more traps than 3"); + playClickSound(player); + } + Trap trap = traps.get(index); int price = trap.getPrice(playerGame, tag); int owned = Arrays.stream(player.getInventory().getItemStacks()) @@ -173,11 +179,17 @@ public void run(InventoryPreClickEvent event, HypixelPlayer p) { playClickSound(player); return; } + BedWarsInventoryManipulator.removeItems(player, trap.getCurrency().getMaterial(), price); playerGame.getTeamManager().addTeamTrap(tag, trap.getKey()); broadcastTeamPurchase(playerGame, tag, player, trap.getName()); playBuySound(player); updateGUI(player); + if (trapSize == 2) { + for (BedWarsPlayer teamPlayer : game.getTeamManager().getPlayersOnTeam(player.getTeamKey())) { + teamPlayer.getAchievementHandler().completeAchievement("bedwars.minefield"); + } + } } @Override From c39e217be81c937f525a56f07050aeced8a64422 Mon Sep 17 00:00:00 2001 From: ArikSquad <75741608+ArikSquad@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:08:39 +0200 Subject: [PATCH 3/3] fix: use BedWarsDeathType for createDeathMessage --- .../death/BedWarsDeathHandler.java | 67 +++++++------------ .../bedwarsgame/death/BedWarsDeathType.java | 9 ++- 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java index e338f6f28..435f1ac40 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathHandler.java @@ -155,27 +155,18 @@ private static BedWarsDeathResult handleEnvironmentalDeath(BedWarsDeathResult.Bu public static Component createDeathMessage(@NotNull BedWarsDeathResult result) { String victimName = result.victim().getUsername(); String victimColor = getTeamColor(result.victim()); - - Component message = switch (result.deathType()) { - case PLAYER_MELEE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was slain by "); - case PLAYER_VOID_KNOCK -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was knocked into the void by "); - case PLAYER_FALL_KNOCK -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was knocked off by "); - case PLAYER_PROJECTILE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was shot by "); - case PLAYER_EXPLOSION -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was blown up by "); - case PLAYER_FIRE -> createPlayerKillMessage(victimName, victimColor, result.killer(), " was burned to death by "); - - case VOID_ASSISTED -> createAssistedMessage(victimName, victimColor, result.assistPlayer(), " was knocked into the void by "); - case FALL_ASSISTED -> createAssistedMessage(victimName, victimColor, result.assistPlayer(), " was knocked off by "); - - case VOID -> createSimpleMessage(victimName, victimColor, " fell into the void."); - case FALL -> createSimpleMessage(victimName, victimColor, " fell from a high place."); - case FIRE -> createSimpleMessage(victimName, victimColor, " burned to death."); - case EXPLOSION -> createSimpleMessage(victimName, victimColor, " blew up."); - - case MOB_KILL -> createMobKillMessage(victimName, victimColor, result.attackerEntity()); - - case GENERIC -> createSimpleMessage(victimName, victimColor, " died."); - }; + BedWarsDeathType deathType = result.deathType(); + String messageFormat = deathType.getMessageFormat(); + + Component message; + if (deathType.involvesPlayer()) { + BedWarsPlayer involvedPlayer = deathType.isAssisted() ? result.assistPlayer() : result.killer(); + message = createPlayerInvolvedMessage(victimName, victimColor, involvedPlayer, messageFormat); + } else if (deathType == BedWarsDeathType.MOB_KILL) { + message = createMobKillMessage(victimName, victimColor, messageFormat, result.attackerEntity()); + } else { + message = createSingleMessage(victimName, victimColor, messageFormat); + } // Append FINAL KILL if applicable if (result.isFinalKill()) { @@ -187,38 +178,28 @@ public static Component createDeathMessage(@NotNull BedWarsDeathResult result) { return message; } - private static Component createPlayerKillMessage(String victimName, String victimColor, - @Nullable BedWarsPlayer killer, String action) { - if (killer == null) { - return createSimpleMessage(victimName, victimColor, " died."); + private static Component createPlayerInvolvedMessage(String victimName, String victimColor, + @Nullable BedWarsPlayer otherPlayer, + String messageFormat) { + if (otherPlayer == null) { + return createSingleMessage(victimName, victimColor, messageFormat); } - String killerColor = getTeamColor(killer); - - return Component.text(victimColor + victimName + "§7" + action + killerColor + killer.getUsername() + "§7."); - } - - private static Component createAssistedMessage(String victimName, String victimColor, - @Nullable BedWarsPlayer assistPlayer, String action) { - if (assistPlayer == null) { - return createSimpleMessage(victimName, victimColor, " died."); - } - - String assistColor = getTeamColor(assistPlayer); - - return Component.text(victimColor + victimName + "§7" + action + assistColor + assistPlayer.getUsername() + "§7."); + String otherColor = getTeamColor(otherPlayer); + return Component.text(victimColor + victimName + "§7" + messageFormat + otherColor + otherPlayer.getUsername() + "§7."); } - private static Component createSimpleMessage(String victimName, String victimColor, String suffix) { - return Component.text(victimColor + victimName + "§7" + suffix); + private static Component createSingleMessage(String victimName, String victimColor, String messageFormat) { + return Component.text(victimColor + victimName + "§7" + messageFormat); } - private static Component createMobKillMessage(String victimName, String victimColor, @Nullable Entity mob) { + private static Component createMobKillMessage(String victimName, String victimColor, String messageFormat, + @Nullable Entity mob) { String mobName = mob != null ? mob.getEntityType().name().toLowerCase().replace("_", " ") : "a mob"; - return Component.text(victimColor + victimName + "§7 was killed by " + mobName + "§7."); + return Component.text(victimColor + victimName + "§7" + messageFormat + mobName + "§7."); } private static String getTeamColor(@Nullable BedWarsPlayer player) { diff --git a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java index c3e59bf44..647204f83 100644 --- a/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java +++ b/type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/death/BedWarsDeathType.java @@ -1,5 +1,8 @@ package net.swofty.type.bedwarsgame.death; +import lombok.Getter; + +@Getter public enum BedWarsDeathType { PLAYER_MELEE(" was slain by "), PLAYER_VOID_KNOCK(" was knocked into the void by "), @@ -26,11 +29,7 @@ public enum BedWarsDeathType { this.messageFormat = messageFormat; } - public String getMessageFormat() { - return messageFormat; - } - - public boolean involvesPlayer() { + public boolean involvesPlayer() { return switch (this) { case PLAYER_MELEE, PLAYER_VOID_KNOCK, PLAYER_FALL_KNOCK, PLAYER_PROJECTILE, PLAYER_EXPLOSION, PLAYER_FIRE, VOID_ASSISTED, FALL_ASSISTED -> true;