diff --git a/Full Art/passive_mobs/axolotl.png b/Full Art/passive_mobs/axolotl.png new file mode 100644 index 0000000..ebcb53a Binary files /dev/null and b/Full Art/passive_mobs/axolotl.png differ diff --git a/Full Art/passive_mobs/bee.png b/Full Art/passive_mobs/bee.png new file mode 100644 index 0000000..509daa6 Binary files /dev/null and b/Full Art/passive_mobs/bee.png differ diff --git a/Full Art/passive_mobs/camel.png b/Full Art/passive_mobs/camel.png new file mode 100644 index 0000000..c157fd8 Binary files /dev/null and b/Full Art/passive_mobs/camel.png differ diff --git a/Full Art/passive_mobs/cat.png b/Full Art/passive_mobs/cat.png new file mode 100644 index 0000000..b07ee26 Binary files /dev/null and b/Full Art/passive_mobs/cat.png differ diff --git a/Full Art/passive_mobs/chicken.png b/Full Art/passive_mobs/chicken.png new file mode 100644 index 0000000..a4e07d5 Binary files /dev/null and b/Full Art/passive_mobs/chicken.png differ diff --git a/Full Art/passive_mobs/cod.png b/Full Art/passive_mobs/cod.png new file mode 100644 index 0000000..e3ab30b Binary files /dev/null and b/Full Art/passive_mobs/cod.png differ diff --git a/Full Art/passive_mobs/cow.png b/Full Art/passive_mobs/cow.png new file mode 100644 index 0000000..5813fa1 Binary files /dev/null and b/Full Art/passive_mobs/cow.png differ diff --git a/Full Art/passive_mobs/donkey.png b/Full Art/passive_mobs/donkey.png new file mode 100644 index 0000000..aaf6dd9 Binary files /dev/null and b/Full Art/passive_mobs/donkey.png differ diff --git a/Full Art/passive_mobs/fox.png b/Full Art/passive_mobs/fox.png new file mode 100644 index 0000000..1bcc5df Binary files /dev/null and b/Full Art/passive_mobs/fox.png differ diff --git a/Full Art/passive_mobs/frog.png b/Full Art/passive_mobs/frog.png new file mode 100644 index 0000000..909b416 Binary files /dev/null and b/Full Art/passive_mobs/frog.png differ diff --git a/Full Art/passive_mobs/glow_squid.png b/Full Art/passive_mobs/glow_squid.png new file mode 100644 index 0000000..a95a5d3 Binary files /dev/null and b/Full Art/passive_mobs/glow_squid.png differ diff --git a/Full Art/passive_mobs/goat.png b/Full Art/passive_mobs/goat.png new file mode 100644 index 0000000..808a205 Binary files /dev/null and b/Full Art/passive_mobs/goat.png differ diff --git a/Full Art/passive_mobs/horse.png b/Full Art/passive_mobs/horse.png new file mode 100644 index 0000000..9dcf7aa Binary files /dev/null and b/Full Art/passive_mobs/horse.png differ diff --git a/Full Art/passive_mobs/llama.png b/Full Art/passive_mobs/llama.png new file mode 100644 index 0000000..757b8f4 Binary files /dev/null and b/Full Art/passive_mobs/llama.png differ diff --git a/Full Art/passive_mobs/mooshroom.png b/Full Art/passive_mobs/mooshroom.png new file mode 100644 index 0000000..774185f Binary files /dev/null and b/Full Art/passive_mobs/mooshroom.png differ diff --git a/Full Art/passive_mobs/mule.png b/Full Art/passive_mobs/mule.png new file mode 100644 index 0000000..93aec6e Binary files /dev/null and b/Full Art/passive_mobs/mule.png differ diff --git a/Full Art/passive_mobs/ocelot.png b/Full Art/passive_mobs/ocelot.png new file mode 100644 index 0000000..7ce9f59 Binary files /dev/null and b/Full Art/passive_mobs/ocelot.png differ diff --git a/Full Art/passive_mobs/panda.png b/Full Art/passive_mobs/panda.png new file mode 100644 index 0000000..d173621 Binary files /dev/null and b/Full Art/passive_mobs/panda.png differ diff --git a/Full Art/passive_mobs/parrot.png b/Full Art/passive_mobs/parrot.png new file mode 100644 index 0000000..d29ce7e Binary files /dev/null and b/Full Art/passive_mobs/parrot.png differ diff --git a/Full Art/passive_mobs/pig.png b/Full Art/passive_mobs/pig.png new file mode 100644 index 0000000..76bc317 Binary files /dev/null and b/Full Art/passive_mobs/pig.png differ diff --git a/Full Art/passive_mobs/polar_bear.png b/Full Art/passive_mobs/polar_bear.png new file mode 100644 index 0000000..5fef5b7 Binary files /dev/null and b/Full Art/passive_mobs/polar_bear.png differ diff --git a/Full Art/passive_mobs/rabbit.png b/Full Art/passive_mobs/rabbit.png new file mode 100644 index 0000000..82fb6f8 Binary files /dev/null and b/Full Art/passive_mobs/rabbit.png differ diff --git a/Full Art/passive_mobs/salmon.png b/Full Art/passive_mobs/salmon.png new file mode 100644 index 0000000..c99f454 Binary files /dev/null and b/Full Art/passive_mobs/salmon.png differ diff --git a/Full Art/passive_mobs/sheep.png b/Full Art/passive_mobs/sheep.png new file mode 100644 index 0000000..90d3599 Binary files /dev/null and b/Full Art/passive_mobs/sheep.png differ diff --git a/Full Art/passive_mobs/sniffer.png b/Full Art/passive_mobs/sniffer.png new file mode 100644 index 0000000..4e784c8 Binary files /dev/null and b/Full Art/passive_mobs/sniffer.png differ diff --git a/Full Art/passive_mobs/squid.png b/Full Art/passive_mobs/squid.png new file mode 100644 index 0000000..25ca4f7 Binary files /dev/null and b/Full Art/passive_mobs/squid.png differ diff --git a/Full Art/passive_mobs/strider.png b/Full Art/passive_mobs/strider.png new file mode 100644 index 0000000..bfea496 Binary files /dev/null and b/Full Art/passive_mobs/strider.png differ diff --git a/Full Art/passive_mobs/turtle.png b/Full Art/passive_mobs/turtle.png new file mode 100644 index 0000000..28f918e Binary files /dev/null and b/Full Art/passive_mobs/turtle.png differ diff --git a/Full Art/passive_mobs/wolf.png b/Full Art/passive_mobs/wolf.png new file mode 100644 index 0000000..8b736ba Binary files /dev/null and b/Full Art/passive_mobs/wolf.png differ diff --git a/README.md b/README.md index b421122..af75d42 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,61 @@ +# Mine Triad -Installation information -======= +Mine Triad is a NeoForge mod for Minecraft 1.21.1 that brings a Minecraft-themed take on Triple Triad into the game. Collect custom cards, organize them in a binder, and challenge a built-in training opponent on a physical duel table placed in the world. -This template repository can be directly cloned to get you started with a new -mod. Simply create a new repository cloned from this one, by following the -instructions provided by [GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template). +This project is currently focused on delivering a polished core card game experience: high-quality card presentation, a full starter card pool, an in-world board, and a clean collection UI. -Once you have your clone, simply open the repository in the IDE of your choice. The usual recommendation for an IDE is either IntelliJ IDEA or Eclipse. +## Features -If at any point you are missing libraries in your IDE, or you've run into problems you can -run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything -{this does not affect your code} and then start the process again. +- 110 unique Triad cards across 10 progression levels +- Minecraft-themed card roster with custom names, art, rarity tiers, and stat layouts +- In-world `Duel Table` block with a playable 3x3 Triple Triad-style board +- Solo duels against a built-in `Training Duelist` +- Custom first-person and item rendering for cards +- `Card Binder` UI for organizing and previewing your collection by level +- JEI integration so every card variant can be browsed directly in item lookup +- Separate creative tabs for cards and gameplay items -Mapping Names: -============ -By default, the MDK is configured to use the official mapping names from Mojang for methods and fields -in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this -license. For the latest license text, refer to the mapping file itself, or the reference copy here: -https://github.com/NeoForged/NeoForm/blob/main/Mojang.md +## What Is Implemented Right Now -Additional Resources: -========== -Community Documentation: https://docs.neoforged.net/ -NeoForged Discord: https://discord.neoforged.net/ +Mine Triad already includes a complete playable duel loop: + +1. Put at least 5 Triad Cards in your inventory. +2. Place a Duel Table. +3. Right-click the top face of the table to start a duel. +4. Hold one of your duel cards and click an open board space to play your turn. +5. Alternate turns with the Training Duelist until the board is full. +6. Right-click the table again after the result screen to finish the match and return cards. + +The current AI opponent evaluates legal moves and prefers plays that capture the most cards. + +## Card Set Overview + +- `Levels 1-3`: common, lower-power wildlife and passive mob inspired cards +- `Levels 4-5`: uncommon hostile mob focused cards +- `Levels 6-7`: rare advanced and elite encounter cards +- `Levels 8-10`: legendary endgame, structure, item, and community-inspired cards + +The stat layouts are intentionally modeled after the progression curve of classic Triple Triad, but the card identities are reimagined around Minecraft. + +## Items And Blocks + +### Triad Card + +The core collectible item. Each stack stores its own card identity as item data, renders as an individual card, and shows its level, sides, and rarity in the tooltip. + +### Duel Table + +The main gameplay block used to play matches in-world. The board state is shown directly on the table during a duel, and the block prevents normal survival breaking while a match is active. + +### Card Binder + +A collection binder with 110 slots arranged by card level. It supports: + +- per-level paging +- persistent storage inside the binder item +- previewing the selected card in the UI +- locked held-slot behavior so the binder cannot be moved out from under the menu + +### Deck Box + +The item is present in the current build as part of the gameplay set, but deck management behavior is not implemented yet. \ No newline at end of file diff --git a/TTCardReference.txt b/TTCardReference.txt new file mode 100644 index 0000000..e99c83a --- /dev/null +++ b/TTCardReference.txt @@ -0,0 +1,817 @@ +-Level 1 Monster Cards- +----------------------- + + 1 + 5 4 + 1 +Geezard +Non Elemental +1 Card = 5 Screws + + 5 + 3 1 + 1 +Funguar +Non Elemental +1 Card = 1 M-Stone Piece + + 1 + 5 3 + 3 +Bite Bug +Non Elemental +1 Card = 1 M-Stone Piece + + 6 + 2 1 + 1 +Red Bat +Non Elemental +1 Card = 1 Vampire Fang + + 2 + 5 3 + 1 +Blobra +Non Elemental +4 Cards = 1 Rune Armlet + + 2 + 4 1 + 4 +Gayla +Thunder Elemental +1 Card = 1 Mystery Fluid + + 1 + 1 5 + 4 +Gesper +Non Elemental +1 Card = 1 Black Hole + + 3 + 1 5 + 2 +Fastitocalon-F +Earth Elemental +5 Cards = 1 Water Crystal + + 2 + 1 1 + 6 +Blood Soul +Non Elemental +1 Card = 1 Zombie Powder + + 4 + 3 2 + 4 +Caterchipillar +Non Elemental +1 Card = 1 Spider Web + + 2 + 6 1 + 2 +Cockatrice +Thunder Elemental +1 Card = 1 Cockatrice Pinion + +---------------------------- + +-Level 2 Monster Cards- +----------------------- + + 7 + 1 1 + 3 +Grat +Non Elemental +1 Card = 1 Magic Stone + + 6 + 3 2 + 2 +Buel +Non Elemental +1 Card = 1 Magic Stone + + 5 + 4 3 + 3 +Mesmerize +Non Elemental +1 Card = 1 Mesmerize Blade + + 6 + 3 1 + 4 +Glacial Eye +Ice Elemental +1 Card = 1 Arctic Wind + + 3 + 3 4 + 5 +Belhelmel +Non Elemental +1 Card = 1 Saw Blade + + 5 + 5 3 + 2 +Thrustaevis +Wind Elemental +1 Card = 1 Shear Feather + + 5 + 5 1 + 3 +Anacondaur +Poison Elemental +1 Card = 1 Venom Fang + + 5 + 2 2 + 5 +Creeps +Thunder Elemental +1 Card = 1 Coral Fragment + + 4 + 2 4 + 5 +Grendal +Thunder Elemental +1 Card = 1 Dragon Fin + + 3 + 7 2 + 1 +Jelleye +Non Elemental +1 Card = 1 Magic Stone + + 5 + 3 2 + 5 +Grand Mantis +Non Elemental +1 Card = 1 Sharp Spike + +---------------------------- + +-Level 3 Monster Cards- +----------------------- + + 6 + 2 6 + 3 +Forbidden +Non Elemental +1 Card = 1 Betrayal Sword + + 6 + 6 3 + 1 +Armadodo +Earth Elemental +1 Card = 1 Dino Bone + + 3 + 5 5 + 5 +Tri-Face +Poison Elemental +1 Card = 1 Curse Spike + + 7 + 3 5 + 1 +Fastitocalon +Earth Elemental +1 Card = 1 Water Crystal + + 7 + 3 1 + 5 +Snow Lion +Ice Elemental +1 Card = 1 North Wind + + 5 + 3 6 + 3 +Ochu +Non Elemental +1 Card = 1 Ochu Tentacle + + 5 + 4 6 + 2 +SAM08G +Fire Elemental +1 Card = 1 Running Fire + + 4 + 2 4 + 7 +Death Claw +Fire Elemental +1 Card = 1 Sharp Spike + + 6 + 3 2 + 6 +Cactuar +Non Elemental +1 Card = 1 Cactus Thorn + + 3 + 4 6 + 4 +Tonberry +Non Elemental +1 Card = 1 Chef's Knife + + 7 + 5 2 + 3 +Abyss Worm +Earth Elemental +1 Card = 1 Windmill + +---------------------------- + +-Level 4 Monster Cards- +----------------------- + + 2 + 7 6 + 3 +Turtapod +Non Elemental +5 Cards = 1 Healing Mail + + 6 + 5 5 + 4 +Vysage +Non Elemental +1 Card = 1 Wizard Stone + + 4 + 7 6 + 2 +T-Rexaur +Non Elemental +2 Cards = 1 Dino Bone + + 2 + 3 7 + 6 +Bomb +Fire Elemental +1 Card = 1 Bomb Fragment + + 1 + 7 6 + 4 +Blitz +Thunder Elemental +1 Card = 1 Dynamite Stone + + 7 + 6 3 + 1 +Wendigo +Non Elemental +1 Card = 1 Steel Orb + + 7 + 4 4 + 4 +Torama +Non Elemental +5 Cards = 1 Life Ring + + 3 + 6 7 + 3 +Imp +Non Elemental +1 Card = 1 Wizard Stone + + 6 + 3 2 + 7 +Blue Dragon +Poison Elemental +4 Cards = 1 Fury Fragment + + 4 + 6 5 + 5 +Adamantoise +Earth Elemental +3 Cards = 1 Turtle Shell + + 7 + 3 5 + 4 +HexDragon +Non Elemental +1 Card = 1 Sharp Spike + +---------------------------- + +-Level 5 Monster Cards- +----------------------- + + 6 + 5 5 + 6 +Iron Giant +Non Elemental +3 Cards = 1 Star Fragment + + 3 + 7 6 + 5 +Behemoth +Earth Elemental +10 Cards = 1 Barrier + + 7 + 3 6 + 5 +Chimera +Water Elemental +10 Cards = 1 Regen Ring + + 3 + 1 A + 2 +Pupu (Rare Card - Need to Complete the UFO Chase to Acquire) +Non Elemental +1 Card = 1 Hungry Cookpot + + 6 + 7 2 + 6 +Elastoid +Non Elemental +1 Card = 1 Steel Pipe + + 5 + 4 5 + 7 +GIM47N +Non Elemental +1 Card = 10 Fast Ammo + + 7 + 2 7 + 4 +Malboro +Poison Elemental +4 Cards = 1 Malboro Tentacle + + 7 + 4 2 + 7 +Ruby Dragon +Fire Elemental +10 Cards = 1 Inferno Ring + + 5 + 6 3 + 7 +Elnoyle +Non Elemental +10 Cards = 1 Energy Crystal + + 4 + 4 7 + 6 +Tonberry King +Non Elemental +1 Card = 1 Chef's Knife + + 6 + 7 6 + 2 +Wedge, Biggs +Non Elemental +1 Card = 1 X Potion + +---------------------------- + +-Level 6 Boss Cards- +-------------------- + + 2 + 4 8 + 8 +Fujin, Rajin +Non Elemental +1 Card = 1 X Potion + + 7 + 4 8 + 3 +Elvoret +Wind Elemental +1 Card = 1o Death Stones + + 4 + 3 8 + 7 +X-ATM092 +Non Elemental +2 Cards = 1 Turtle Shell + + 7 + 5 2 + 8 +Granaldo +Non Elemental +1 Card = 1 G-Returner + + 1 + 3 8 + 8 +Gerogero +Poison Elemental +10 Cards = 1 Circlet + + 8 + 2 2 + 8 +Iguion +Non Elemental +1 Card = 1 Cocktrice Pinion + + 6 + 5 8 + 4 +Abadon +Non Elemental + 1 Card = 30 Dark Ammo + + 4 + 6 8 + 5 +Trauma +Non Elemental +1 Card = 30 Demolition Ammo + + 1 + 8 8 + 4 +Oilboyle +Non Elemental +1 Card = 30 Fire Ammo + + 6 + 4 5 + 8 +Shumi Tribe +Non Elemental +5 Cards = 1 Gambler's Spirit + + 7 + 1 5 + 8 +Krysta +Non Elemental +1 Card = 10 Holy Stones + +---------------------------- + +-Level 7 Boss Cards- +-------------------- + + 8 + 8 4 + 4 +Propagator +Non Elemental +1 Card = 1 G-Mega Potion + + 8 + 4 8 + 4 +Jumbo Cactuar +Non Elemental +1 Card = 1 Cactus Thorn + + 8 + 8 6 + 6 +Tri-Point +Thunder Elemental +40 Cards = 1 Jet Engine + + 5 + 8 6 + 6 +Gargantua +Non Elemental +10 Cards = 1 Strength Love + + 8 + 3 6 + 7 +Moblie Type B +Non Elemental +1 Card = 10 Shell Stones + + 8 + 8 3 + 5 +Sphinxara +Non Elemental +1 Card = 1 G-Mega Potion + + 8 + 4 8 + 5 +Tiamat +Non Elemental +1 Card = 10 Flare Stones + + 5 + 5 7 + 8 +BGH251F2 +Non Elemental +1 Card = 10 Protect Stones + + 6 + 7 8 + 4 +Red Giant +Non Elemental +1 Card = 5 Meteor Stones + + 1 + 7 8 + 7 +Catoblepas +Non Elemental +1 Card = 1 Rename Card + + 7 + 8 7 + 1 +Ultima Weapon +Non Elemental +1 Card = 1 Ultima Stone + +---------------------------- + +-Level 8 GF Cards- (All Rare Cards) +------------------ + + 4 + 9 4 + 8 +Chubby Chocobo +Non Elemental +1 Card = 100 LuvLuvG's + + 9 + 3 6 + 7 +Angelo +Wind Elemental +1 Card = 100 Elixers + + 3 + 6 7 + 9 +Gilgamesh +Non Elemental +1 Card = 10 Holy Wars + + 9 + 2 3 + 9 +Mini Mog +Ice Elemental +1 Card = 100 Pet Houses + + 9 + 4 4 + 8 +Chicobo +Poison Elemental +1 Card = 100 Gysahi Greens + + 2 + 4 9 + 9 +Quezacoyl +Thunder Elemental +1 Card = 100 Dynamite Stones + + 6 + 9 7 + 4 +Shiva +Ice Elemental +1 Card = 100 North Winds + + 9 + 8 6 + 2 +Ifrit +Fire Elemental +1 Card = 3 Elemental Attacks + + 8 + 2 9 + 6 +Siren +Non Elemental +1 Card = 3 Status Attacks + + 5 + 9 1 + 9 +Sacred +Earth Elemental +1 Card = 100 Dino Bones + + 9 + 9 5 + 2 +Minotaur +Earth Elemental +1 Card = 10 Adamantines + +---------------------------- + +-Level 9 GF Cards- (All Rare Cards) +------------------ + + 8 + 4 4 + 8 +Carbuncle +Non Elemental +1 Card = 3 Glow Curtains + + 5 + 3 8 + 8 +Diablos +Non Elemental +1 Card = 100 Black Holes + + 7 + 7 A + 1 +Leviathan +Water Elemental +1 Card = 3 Doc's Codes + + 8 + 5 A + 3 +Odin +Non Elemental +1 Card = 100 Dead Spirits + + 8 + 7 1 + 7 +Pandemona +Wind Elemental +1 Card = 100 Windmills + + 7 + A 4 + 6 +Cerberus +Non Elemental +1 Card = 100 Lightweights + + 9 + 2 A + 4 +Alexander +Holy Elemental +1 Card = 3 Moon Curtains + + 7 + A 2 + 7 +Phoenix +Fire Elemental +1 Card = 3 Phoneix Spirits + + 8 + 6 8 + 2 +Bahamut +Non Elemental +1 Card = 100 Mega Elixers + + 3 + A 1 + A +Doomtrain +Poison Elemental +1 Card = 3 Status Guards + + 4 + A 4 + 9 +Eden +Non Elemental +1 Card = 3 Monk's Codes + +---------------------------- + +-Level 10 Player's Cards- (All Rare) +------------------------ + + A + 8 7 + 2 +Ward +Non Elemental +1 Card = 3 Gaea's Rings + + 6 + A 7 + 6 +Kiros +Non Elemental +1 Card = 3 Accelerators + + 5 + 9 A + 3 +Laguana +Non Elemental +1 Card = 100 Heros + + A + 4 8 + 6 +Selphie +Non Elemental +1 Card = 3 Elemental Guards + + 9 + 2 6 + A +Quistis +Non Elemental +1 Card = 3 Samantha Souls + + 2 + A 6 + 9 +Irvine +Non Elemental +1 Card = 3 Rocket Engines + + 8 + 6 5 + A +Zell +Non Elemental +1 Card = 3 Hyper Wrists + + 4 + A A + 2 +Rinoa +Non Elemental +1 Card = 3 Magic Armlets + + A + 3 A + 3 +Edea +Non Elemental +1 Card = 3 Royal Crowns + + 6 + 4 9 + A +Seifer +Non Elemental +1 Card = 3 Diamond Armors + + A + 9 4 + 6 +Squall +Non Elemental +1 Card = 3 Three Stars \ No newline at end of file diff --git a/assets/minecraft/models/item/filled_map.json b/assets/minecraft/models/item/filled_map.json new file mode 100644 index 0000000..077d253 --- /dev/null +++ b/assets/minecraft/models/item/filled_map.json @@ -0,0 +1,7 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "item/filled_map", + "layer1": "item/filled_map_markings" + } +} diff --git a/build.gradle b/build.gradle index 732df31..cfc4ab6 100644 --- a/build.gradle +++ b/build.gradle @@ -124,6 +124,8 @@ configurations { } dependencies { + compileOnly "curse.maven:jei-238222:7225068" + // Dev-only utility mods for local runs. localRuntime "curse.maven:jade-324717:6155158" localRuntime "curse.maven:jei-238222:7225068" diff --git a/net/minecraft/client/renderer/ItemInHandRenderer.java b/net/minecraft/client/renderer/ItemInHandRenderer.java new file mode 100644 index 0000000..659f7d5 --- /dev/null +++ b/net/minecraft/client/renderer/ItemInHandRenderer.java @@ -0,0 +1,626 @@ +package net.minecraft.client.renderer; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Axis; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.CrossbowItem; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.MapItem; +import net.minecraft.world.level.saveddata.maps.MapId; +import net.minecraft.world.level.saveddata.maps.MapItemSavedData; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.joml.Matrix4f; + +@OnlyIn(Dist.CLIENT) +public class ItemInHandRenderer { + private static final RenderType MAP_BACKGROUND = RenderType.text(ResourceLocation.withDefaultNamespace("textures/map/map_background.png")); + private static final RenderType MAP_BACKGROUND_CHECKERBOARD = RenderType.text( + ResourceLocation.withDefaultNamespace("textures/map/map_background_checkerboard.png") + ); + private static final float ITEM_SWING_X_POS_SCALE = -0.4F; + private static final float ITEM_SWING_Y_POS_SCALE = 0.2F; + private static final float ITEM_SWING_Z_POS_SCALE = -0.2F; + private static final float ITEM_HEIGHT_SCALE = -0.6F; + private static final float ITEM_POS_X = 0.56F; + private static final float ITEM_POS_Y = -0.52F; + private static final float ITEM_POS_Z = -0.72F; + private static final float ITEM_PRESWING_ROT_Y = 45.0F; + private static final float ITEM_SWING_X_ROT_AMOUNT = -80.0F; + private static final float ITEM_SWING_Y_ROT_AMOUNT = -20.0F; + private static final float ITEM_SWING_Z_ROT_AMOUNT = -20.0F; + private static final float EAT_JIGGLE_X_ROT_AMOUNT = 10.0F; + private static final float EAT_JIGGLE_Y_ROT_AMOUNT = 90.0F; + private static final float EAT_JIGGLE_Z_ROT_AMOUNT = 30.0F; + private static final float EAT_JIGGLE_X_POS_SCALE = 0.6F; + private static final float EAT_JIGGLE_Y_POS_SCALE = -0.5F; + private static final float EAT_JIGGLE_Z_POS_SCALE = 0.0F; + private static final double EAT_JIGGLE_EXPONENT = 27.0; + private static final float EAT_EXTRA_JIGGLE_CUTOFF = 0.8F; + private static final float EAT_EXTRA_JIGGLE_SCALE = 0.1F; + private static final float ARM_SWING_X_POS_SCALE = -0.3F; + private static final float ARM_SWING_Y_POS_SCALE = 0.4F; + private static final float ARM_SWING_Z_POS_SCALE = -0.4F; + private static final float ARM_SWING_Y_ROT_AMOUNT = 70.0F; + private static final float ARM_SWING_Z_ROT_AMOUNT = -20.0F; + private static final float ARM_HEIGHT_SCALE = -0.6F; + private static final float ARM_POS_SCALE = 0.8F; + private static final float ARM_POS_X = 0.8F; + private static final float ARM_POS_Y = -0.75F; + private static final float ARM_POS_Z = -0.9F; + private static final float ARM_PRESWING_ROT_Y = 45.0F; + private static final float ARM_PREROTATION_X_OFFSET = -1.0F; + private static final float ARM_PREROTATION_Y_OFFSET = 3.6F; + private static final float ARM_PREROTATION_Z_OFFSET = 3.5F; + private static final float ARM_POSTROTATION_X_OFFSET = 5.6F; + private static final int ARM_ROT_X = 200; + private static final int ARM_ROT_Y = -135; + private static final int ARM_ROT_Z = 120; + private static final float MAP_SWING_X_POS_SCALE = -0.4F; + private static final float MAP_SWING_Z_POS_SCALE = -0.2F; + private static final float MAP_HANDS_POS_X = 0.0F; + private static final float MAP_HANDS_POS_Y = 0.04F; + private static final float MAP_HANDS_POS_Z = -0.72F; + private static final float MAP_HANDS_HEIGHT_SCALE = -1.2F; + private static final float MAP_HANDS_TILT_SCALE = -0.5F; + private static final float MAP_PLAYER_PITCH_SCALE = 45.0F; + private static final float MAP_HANDS_Z_ROT_AMOUNT = -85.0F; + private static final float MAPHAND_X_ROT_AMOUNT = 45.0F; + private static final float MAPHAND_Y_ROT_AMOUNT = 92.0F; + private static final float MAPHAND_Z_ROT_AMOUNT = -41.0F; + private static final float MAP_HAND_X_POS = 0.3F; + private static final float MAP_HAND_Y_POS = -1.1F; + private static final float MAP_HAND_Z_POS = 0.45F; + private static final float MAP_SWING_X_ROT_AMOUNT = 20.0F; + private static final float MAP_PRE_ROT_SCALE = 0.38F; + private static final float MAP_GLOBAL_X_POS = -0.5F; + private static final float MAP_GLOBAL_Y_POS = -0.5F; + private static final float MAP_GLOBAL_Z_POS = 0.0F; + private static final float MAP_FINAL_SCALE = 0.0078125F; + private static final int MAP_BORDER = 7; + private static final int MAP_HEIGHT = 128; + private static final int MAP_WIDTH = 128; + private static final float BOW_CHARGE_X_POS_SCALE = 0.0F; + private static final float BOW_CHARGE_Y_POS_SCALE = 0.0F; + private static final float BOW_CHARGE_Z_POS_SCALE = 0.04F; + private static final float BOW_CHARGE_SHAKE_X_SCALE = 0.0F; + private static final float BOW_CHARGE_SHAKE_Y_SCALE = 0.004F; + private static final float BOW_CHARGE_SHAKE_Z_SCALE = 0.0F; + private static final float BOW_CHARGE_Z_SCALE = 0.2F; + private static final float BOW_MIN_SHAKE_CHARGE = 0.1F; + private final Minecraft minecraft; + private ItemStack mainHandItem = ItemStack.EMPTY; + private ItemStack offHandItem = ItemStack.EMPTY; + private float mainHandHeight; + private float oMainHandHeight; + private float offHandHeight; + private float oOffHandHeight; + private final EntityRenderDispatcher entityRenderDispatcher; + private final ItemRenderer itemRenderer; + + public ItemInHandRenderer(Minecraft minecraft, EntityRenderDispatcher entityRenderDispatcher, ItemRenderer itemRenderer) { + this.minecraft = minecraft; + this.entityRenderDispatcher = entityRenderDispatcher; + this.itemRenderer = itemRenderer; + } + + public void renderItem( + LivingEntity entity, + ItemStack itemStack, + ItemDisplayContext displayContext, + boolean leftHand, + PoseStack poseStack, + MultiBufferSource buffer, + int seed + ) { + if (!itemStack.isEmpty()) { + this.itemRenderer + .renderStatic( + entity, + itemStack, + displayContext, + leftHand, + poseStack, + buffer, + entity.level(), + seed, + OverlayTexture.NO_OVERLAY, + entity.getId() + displayContext.ordinal() + ); + } + } + + /** + * Return the angle to render the Map + */ + private float calculateMapTilt(float pitch) { + float f = 1.0F - pitch / 45.0F + 0.1F; + f = Mth.clamp(f, 0.0F, 1.0F); + return -Mth.cos(f * (float) Math.PI) * 0.5F + 0.5F; + } + + private void renderMapHand(PoseStack poseStack, MultiBufferSource buffer, int packedLight, HumanoidArm side) { + PlayerRenderer playerrenderer = (PlayerRenderer)this.entityRenderDispatcher.getRenderer(this.minecraft.player); + poseStack.pushPose(); + float f = side == HumanoidArm.RIGHT ? 1.0F : -1.0F; + poseStack.mulPose(Axis.YP.rotationDegrees(92.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(45.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(f * -41.0F)); + poseStack.translate(f * 0.3F, -1.1F, 0.45F); + if (side == HumanoidArm.RIGHT) { + playerrenderer.renderRightHand(poseStack, buffer, packedLight, this.minecraft.player); + } else { + playerrenderer.renderLeftHand(poseStack, buffer, packedLight, this.minecraft.player); + } + + poseStack.popPose(); + } + + private void renderOneHandedMap( + PoseStack poseStack, MultiBufferSource buffer, int packedLight, float equippedProgress, HumanoidArm hand, float swingProgress, ItemStack stack + ) { + float f = hand == HumanoidArm.RIGHT ? 1.0F : -1.0F; + poseStack.translate(f * 0.125F, -0.125F, 0.0F); + if (!this.minecraft.player.isInvisible()) { + poseStack.pushPose(); + poseStack.mulPose(Axis.ZP.rotationDegrees(f * 10.0F)); + this.renderPlayerArm(poseStack, buffer, packedLight, equippedProgress, swingProgress, hand); + poseStack.popPose(); + } + + poseStack.pushPose(); + poseStack.translate(f * 0.51F, -0.08F + equippedProgress * -1.2F, -0.75F); + float f1 = Mth.sqrt(swingProgress); + float f2 = Mth.sin(f1 * (float) Math.PI); + float f3 = -0.5F * f2; + float f4 = 0.4F * Mth.sin(f1 * (float) (Math.PI * 2)); + float f5 = -0.3F * Mth.sin(swingProgress * (float) Math.PI); + poseStack.translate(f * f3, f4 - 0.3F * f2, f5); + poseStack.mulPose(Axis.XP.rotationDegrees(f2 * -45.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(f * f2 * -30.0F)); + this.renderMap(poseStack, buffer, packedLight, stack); + poseStack.popPose(); + } + + private void renderTwoHandedMap(PoseStack poseStack, MultiBufferSource buffer, int packedLight, float pitch, float equippedProgress, float swingProgress) { + float f = Mth.sqrt(swingProgress); + float f1 = -0.2F * Mth.sin(swingProgress * (float) Math.PI); + float f2 = -0.4F * Mth.sin(f * (float) Math.PI); + poseStack.translate(0.0F, -f1 / 2.0F, f2); + float f3 = this.calculateMapTilt(pitch); + poseStack.translate(0.0F, 0.04F + equippedProgress * -1.2F + f3 * -0.5F, -0.72F); + poseStack.mulPose(Axis.XP.rotationDegrees(f3 * -85.0F)); + if (!this.minecraft.player.isInvisible()) { + poseStack.pushPose(); + poseStack.mulPose(Axis.YP.rotationDegrees(90.0F)); + this.renderMapHand(poseStack, buffer, packedLight, HumanoidArm.RIGHT); + this.renderMapHand(poseStack, buffer, packedLight, HumanoidArm.LEFT); + poseStack.popPose(); + } + + float f4 = Mth.sin(f * (float) Math.PI); + poseStack.mulPose(Axis.XP.rotationDegrees(f4 * 20.0F)); + poseStack.scale(2.0F, 2.0F, 2.0F); + this.renderMap(poseStack, buffer, packedLight, this.mainHandItem); + } + + private void renderMap(PoseStack poseStack, MultiBufferSource buffer, int packedLight, ItemStack stack) { + poseStack.mulPose(Axis.YP.rotationDegrees(180.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F)); + poseStack.scale(0.38F, 0.38F, 0.38F); + poseStack.translate(-0.5F, -0.5F, 0.0F); + poseStack.scale(0.0078125F, 0.0078125F, 0.0078125F); + MapId mapid = stack.get(DataComponents.MAP_ID); + MapItemSavedData mapitemsaveddata = MapItem.getSavedData(stack, this.minecraft.level); + VertexConsumer vertexconsumer = buffer.getBuffer(mapitemsaveddata == null ? MAP_BACKGROUND : MAP_BACKGROUND_CHECKERBOARD); + Matrix4f matrix4f = poseStack.last().pose(); + vertexconsumer.addVertex(matrix4f, -7.0F, 135.0F, 0.0F).setColor(-1).setUv(0.0F, 1.0F).setLight(packedLight); + vertexconsumer.addVertex(matrix4f, 135.0F, 135.0F, 0.0F).setColor(-1).setUv(1.0F, 1.0F).setLight(packedLight); + vertexconsumer.addVertex(matrix4f, 135.0F, -7.0F, 0.0F).setColor(-1).setUv(1.0F, 0.0F).setLight(packedLight); + vertexconsumer.addVertex(matrix4f, -7.0F, -7.0F, 0.0F).setColor(-1).setUv(0.0F, 0.0F).setLight(packedLight); + if (mapitemsaveddata != null) { + this.minecraft.gameRenderer.getMapRenderer().render(poseStack, buffer, mapid, mapitemsaveddata, false, packedLight); + } + } + + private void renderPlayerArm(PoseStack poseStack, MultiBufferSource buffer, int packedLight, float equippedProgress, float swingProgress, HumanoidArm side) { + boolean flag = side != HumanoidArm.LEFT; + float f = flag ? 1.0F : -1.0F; + float f1 = Mth.sqrt(swingProgress); + float f2 = -0.3F * Mth.sin(f1 * (float) Math.PI); + float f3 = 0.4F * Mth.sin(f1 * (float) (Math.PI * 2)); + float f4 = -0.4F * Mth.sin(swingProgress * (float) Math.PI); + poseStack.translate(f * (f2 + 0.64000005F), f3 + -0.6F + equippedProgress * -0.6F, f4 + -0.71999997F); + poseStack.mulPose(Axis.YP.rotationDegrees(f * 45.0F)); + float f5 = Mth.sin(swingProgress * swingProgress * (float) Math.PI); + float f6 = Mth.sin(f1 * (float) Math.PI); + poseStack.mulPose(Axis.YP.rotationDegrees(f * f6 * 70.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(f * f5 * -20.0F)); + AbstractClientPlayer abstractclientplayer = this.minecraft.player; + poseStack.translate(f * -1.0F, 3.6F, 3.5F); + poseStack.mulPose(Axis.ZP.rotationDegrees(f * 120.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(200.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(f * -135.0F)); + poseStack.translate(f * 5.6F, 0.0F, 0.0F); + PlayerRenderer playerrenderer = (PlayerRenderer)this.entityRenderDispatcher.getRenderer(abstractclientplayer); + if (flag) { + playerrenderer.renderRightHand(poseStack, buffer, packedLight, abstractclientplayer); + } else { + playerrenderer.renderLeftHand(poseStack, buffer, packedLight, abstractclientplayer); + } + } + + private void applyEatTransform(PoseStack poseStack, float partialTick, HumanoidArm arm, ItemStack stack, Player player) { + float f = (float)player.getUseItemRemainingTicks() - partialTick + 1.0F; + float f1 = f / (float)stack.getUseDuration(player); + if (f1 < 0.8F) { + float f2 = Mth.abs(Mth.cos(f / 4.0F * (float) Math.PI) * 0.1F); + poseStack.translate(0.0F, f2, 0.0F); + } + + float f3 = 1.0F - (float)Math.pow((double)f1, 27.0); + int i = arm == HumanoidArm.RIGHT ? 1 : -1; + poseStack.translate(f3 * 0.6F * (float)i, f3 * -0.5F, f3 * 0.0F); + poseStack.mulPose(Axis.YP.rotationDegrees((float)i * f3 * 90.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(f3 * 10.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)i * f3 * 30.0F)); + } + + private void applyBrushTransform(PoseStack poseStack, float partialTick, HumanoidArm arm, ItemStack stack, Player player, float equippedProgress) { + this.applyItemArmTransform(poseStack, arm, equippedProgress); + float f = (float)(player.getUseItemRemainingTicks() % 10); + float f1 = f - partialTick + 1.0F; + float f2 = 1.0F - f1 / 10.0F; + float f3 = -90.0F; + float f4 = 60.0F; + float f5 = 150.0F; + float f6 = -15.0F; + int i = 2; + float f7 = -15.0F + 75.0F * Mth.cos(f2 * 2.0F * (float) Math.PI); + if (arm != HumanoidArm.RIGHT) { + poseStack.translate(0.1, 0.83, 0.35); + poseStack.mulPose(Axis.XP.rotationDegrees(-80.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(-90.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(f7)); + poseStack.translate(-0.3, 0.22, 0.35); + } else { + poseStack.translate(-0.25, 0.22, 0.35); + poseStack.mulPose(Axis.XP.rotationDegrees(-80.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(90.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(0.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(f7)); + } + } + + private void applyItemArmAttackTransform(PoseStack poseStack, HumanoidArm hand, float swingProgress) { + int i = hand == HumanoidArm.RIGHT ? 1 : -1; + float f = Mth.sin(swingProgress * swingProgress * (float) Math.PI); + poseStack.mulPose(Axis.YP.rotationDegrees((float)i * (45.0F + f * -20.0F))); + float f1 = Mth.sin(Mth.sqrt(swingProgress) * (float) Math.PI); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)i * f1 * -20.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(f1 * -80.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees((float)i * -45.0F)); + } + + private void applyItemArmTransform(PoseStack poseStack, HumanoidArm hand, float equippedProg) { + int i = hand == HumanoidArm.RIGHT ? 1 : -1; + poseStack.translate((float)i * 0.56F, -0.52F + equippedProg * -0.6F, -0.72F); + } + + public void renderHandsWithItems(float partialTicks, PoseStack poseStack, MultiBufferSource.BufferSource buffer, LocalPlayer playerEntity, int combinedLight) { + float f = playerEntity.getAttackAnim(partialTicks); + InteractionHand interactionhand = MoreObjects.firstNonNull(playerEntity.swingingArm, InteractionHand.MAIN_HAND); + float f1 = Mth.lerp(partialTicks, playerEntity.xRotO, playerEntity.getXRot()); + ItemInHandRenderer.HandRenderSelection iteminhandrenderer$handrenderselection = evaluateWhichHandsToRender(playerEntity); + float f2 = Mth.lerp(partialTicks, playerEntity.xBobO, playerEntity.xBob); + float f3 = Mth.lerp(partialTicks, playerEntity.yBobO, playerEntity.yBob); + poseStack.mulPose(Axis.XP.rotationDegrees((playerEntity.getViewXRot(partialTicks) - f2) * 0.1F)); + poseStack.mulPose(Axis.YP.rotationDegrees((playerEntity.getViewYRot(partialTicks) - f3) * 0.1F)); + if (iteminhandrenderer$handrenderselection.renderMainHand) { + float f4 = interactionhand == InteractionHand.MAIN_HAND ? f : 0.0F; + float f5 = 1.0F - Mth.lerp(partialTicks, this.oMainHandHeight, this.mainHandHeight); + if(!net.neoforged.neoforge.client.ClientHooks.renderSpecificFirstPersonHand(InteractionHand.MAIN_HAND, poseStack, buffer, combinedLight, partialTicks, f1, f4, f5, this.mainHandItem)) + this.renderArmWithItem(playerEntity, partialTicks, f1, InteractionHand.MAIN_HAND, f4, this.mainHandItem, f5, poseStack, buffer, combinedLight); + } + + if (iteminhandrenderer$handrenderselection.renderOffHand) { + float f6 = interactionhand == InteractionHand.OFF_HAND ? f : 0.0F; + float f7 = 1.0F - Mth.lerp(partialTicks, this.oOffHandHeight, this.offHandHeight); + if(!net.neoforged.neoforge.client.ClientHooks.renderSpecificFirstPersonHand(InteractionHand.OFF_HAND, poseStack, buffer, combinedLight, partialTicks, f1, f6, f7, this.offHandItem)) + this.renderArmWithItem(playerEntity, partialTicks, f1, InteractionHand.OFF_HAND, f6, this.offHandItem, f7, poseStack, buffer, combinedLight); + } + + buffer.endBatch(); + } + + @VisibleForTesting + static ItemInHandRenderer.HandRenderSelection evaluateWhichHandsToRender(LocalPlayer player) { + ItemStack itemstack = player.getMainHandItem(); + ItemStack itemstack1 = player.getOffhandItem(); + boolean flag = itemstack.is(Items.BOW) || itemstack1.is(Items.BOW); + boolean flag1 = itemstack.is(Items.CROSSBOW) || itemstack1.is(Items.CROSSBOW); + if (!flag && !flag1) { + return ItemInHandRenderer.HandRenderSelection.RENDER_BOTH_HANDS; + } else if (player.isUsingItem()) { + return selectionUsingItemWhileHoldingBowLike(player); + } else { + return isChargedCrossbow(itemstack) + ? ItemInHandRenderer.HandRenderSelection.RENDER_MAIN_HAND_ONLY + : ItemInHandRenderer.HandRenderSelection.RENDER_BOTH_HANDS; + } + } + + private static ItemInHandRenderer.HandRenderSelection selectionUsingItemWhileHoldingBowLike(LocalPlayer player) { + ItemStack itemstack = player.getUseItem(); + InteractionHand interactionhand = player.getUsedItemHand(); + if (!itemstack.is(Items.BOW) && !itemstack.is(Items.CROSSBOW)) { + return interactionhand == InteractionHand.MAIN_HAND && isChargedCrossbow(player.getOffhandItem()) + ? ItemInHandRenderer.HandRenderSelection.RENDER_MAIN_HAND_ONLY + : ItemInHandRenderer.HandRenderSelection.RENDER_BOTH_HANDS; + } else { + return ItemInHandRenderer.HandRenderSelection.onlyForHand(interactionhand); + } + } + + private static boolean isChargedCrossbow(ItemStack stack) { + return stack.is(Items.CROSSBOW) && CrossbowItem.isCharged(stack); + } + + private void renderArmWithItem( + AbstractClientPlayer player, + float partialTicks, + float pitch, + InteractionHand hand, + float swingProgress, + ItemStack stack, + float equippedProgress, + PoseStack poseStack, + MultiBufferSource buffer, + int combinedLight + ) { + if (!player.isScoping()) { + boolean flag = hand == InteractionHand.MAIN_HAND; + HumanoidArm humanoidarm = flag ? player.getMainArm() : player.getMainArm().getOpposite(); + poseStack.pushPose(); + if (stack.isEmpty()) { + if (flag && !player.isInvisible()) { + this.renderPlayerArm(poseStack, buffer, combinedLight, equippedProgress, swingProgress, humanoidarm); + } + } else if (stack.getItem() instanceof MapItem) { + if (flag && this.offHandItem.isEmpty()) { + this.renderTwoHandedMap(poseStack, buffer, combinedLight, pitch, equippedProgress, swingProgress); + } else { + this.renderOneHandedMap(poseStack, buffer, combinedLight, equippedProgress, humanoidarm, swingProgress, stack); + } + } else if (stack.getItem() instanceof CrossbowItem) { + boolean flag1 = CrossbowItem.isCharged(stack); + boolean flag2 = humanoidarm == HumanoidArm.RIGHT; + int i = flag2 ? 1 : -1; + if (player.isUsingItem() && player.getUseItemRemainingTicks() > 0 && player.getUsedItemHand() == hand) { + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + poseStack.translate((float)i * -0.4785682F, -0.094387F, 0.05731531F); + poseStack.mulPose(Axis.XP.rotationDegrees(-11.935F)); + poseStack.mulPose(Axis.YP.rotationDegrees((float)i * 65.3F)); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)i * -9.785F)); + float f9 = (float)stack.getUseDuration(player) - ((float)player.getUseItemRemainingTicks() - partialTicks + 1.0F); + float f13 = f9 / (float)CrossbowItem.getChargeDuration(stack, player); + if (f13 > 1.0F) { + f13 = 1.0F; + } + + if (f13 > 0.1F) { + float f16 = Mth.sin((f9 - 0.1F) * 1.3F); + float f3 = f13 - 0.1F; + float f4 = f16 * f3; + poseStack.translate(f4 * 0.0F, f4 * 0.004F, f4 * 0.0F); + } + + poseStack.translate(f13 * 0.0F, f13 * 0.0F, f13 * 0.04F); + poseStack.scale(1.0F, 1.0F, 1.0F + f13 * 0.2F); + poseStack.mulPose(Axis.YN.rotationDegrees((float)i * 45.0F)); + } else { + float f = -0.4F * Mth.sin(Mth.sqrt(swingProgress) * (float) Math.PI); + float f1 = 0.2F * Mth.sin(Mth.sqrt(swingProgress) * (float) (Math.PI * 2)); + float f2 = -0.2F * Mth.sin(swingProgress * (float) Math.PI); + poseStack.translate((float)i * f, f1, f2); + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + this.applyItemArmAttackTransform(poseStack, humanoidarm, swingProgress); + if (flag1 && swingProgress < 0.001F && flag) { + poseStack.translate((float)i * -0.641864F, 0.0F, 0.0F); + poseStack.mulPose(Axis.YP.rotationDegrees((float)i * 10.0F)); + } + } + + this.renderItem( + player, + stack, + flag2 ? ItemDisplayContext.FIRST_PERSON_RIGHT_HAND : ItemDisplayContext.FIRST_PERSON_LEFT_HAND, + !flag2, + poseStack, + buffer, + combinedLight + ); + } else { + boolean flag3 = humanoidarm == HumanoidArm.RIGHT; + if (!net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(stack).applyForgeHandTransform(poseStack, minecraft.player, humanoidarm, stack, partialTicks, equippedProgress, swingProgress)) // FORGE: Allow items to define custom arm animation + if (player.isUsingItem() && player.getUseItemRemainingTicks() > 0 && player.getUsedItemHand() == hand) { + int k = flag3 ? 1 : -1; + switch (stack.getUseAnimation()) { + case NONE: + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + break; + case EAT: + case DRINK: + this.applyEatTransform(poseStack, partialTicks, humanoidarm, stack, player); + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + break; + case BLOCK: + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + break; + case BOW: + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + poseStack.translate((float)k * -0.2785682F, 0.18344387F, 0.15731531F); + poseStack.mulPose(Axis.XP.rotationDegrees(-13.935F)); + poseStack.mulPose(Axis.YP.rotationDegrees((float)k * 35.3F)); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)k * -9.785F)); + float f8 = (float)stack.getUseDuration(player) - ((float)player.getUseItemRemainingTicks() - partialTicks + 1.0F); + float f12 = f8 / 20.0F; + f12 = (f12 * f12 + f12 * 2.0F) / 3.0F; + if (f12 > 1.0F) { + f12 = 1.0F; + } + + if (f12 > 0.1F) { + float f15 = Mth.sin((f8 - 0.1F) * 1.3F); + float f18 = f12 - 0.1F; + float f20 = f15 * f18; + poseStack.translate(f20 * 0.0F, f20 * 0.004F, f20 * 0.0F); + } + + poseStack.translate(f12 * 0.0F, f12 * 0.0F, f12 * 0.04F); + poseStack.scale(1.0F, 1.0F, 1.0F + f12 * 0.2F); + poseStack.mulPose(Axis.YN.rotationDegrees((float)k * 45.0F)); + break; + case SPEAR: + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + poseStack.translate((float)k * -0.5F, 0.7F, 0.1F); + poseStack.mulPose(Axis.XP.rotationDegrees(-55.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees((float)k * 35.3F)); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)k * -9.785F)); + float f7 = (float)stack.getUseDuration(player) - ((float)player.getUseItemRemainingTicks() - partialTicks + 1.0F); + float f11 = f7 / 10.0F; + if (f11 > 1.0F) { + f11 = 1.0F; + } + + if (f11 > 0.1F) { + float f14 = Mth.sin((f7 - 0.1F) * 1.3F); + float f17 = f11 - 0.1F; + float f19 = f14 * f17; + poseStack.translate(f19 * 0.0F, f19 * 0.004F, f19 * 0.0F); + } + + poseStack.translate(0.0F, 0.0F, f11 * 0.2F); + poseStack.scale(1.0F, 1.0F, 1.0F + f11 * 0.2F); + poseStack.mulPose(Axis.YN.rotationDegrees((float)k * 45.0F)); + break; + case BRUSH: + this.applyBrushTransform(poseStack, partialTicks, humanoidarm, stack, player, equippedProgress); + } + } else if (player.isAutoSpinAttack()) { + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + int j = flag3 ? 1 : -1; + poseStack.translate((float)j * -0.4F, 0.8F, 0.3F); + poseStack.mulPose(Axis.YP.rotationDegrees((float)j * 65.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees((float)j * -85.0F)); + } else { + float f5 = -0.4F * Mth.sin(Mth.sqrt(swingProgress) * (float) Math.PI); + float f6 = 0.2F * Mth.sin(Mth.sqrt(swingProgress) * (float) (Math.PI * 2)); + float f10 = -0.2F * Mth.sin(swingProgress * (float) Math.PI); + int l = flag3 ? 1 : -1; + poseStack.translate((float)l * f5, f6, f10); + this.applyItemArmTransform(poseStack, humanoidarm, equippedProgress); + this.applyItemArmAttackTransform(poseStack, humanoidarm, swingProgress); + } + + this.renderItem( + player, + stack, + flag3 ? ItemDisplayContext.FIRST_PERSON_RIGHT_HAND : ItemDisplayContext.FIRST_PERSON_LEFT_HAND, + !flag3, + poseStack, + buffer, + combinedLight + ); + } + + poseStack.popPose(); + } + } + + public void tick() { + this.oMainHandHeight = this.mainHandHeight; + this.oOffHandHeight = this.offHandHeight; + LocalPlayer localplayer = this.minecraft.player; + ItemStack itemstack = localplayer.getMainHandItem(); + ItemStack itemstack1 = localplayer.getOffhandItem(); + if (ItemStack.matches(this.mainHandItem, itemstack)) { + this.mainHandItem = itemstack; + } + + if (ItemStack.matches(this.offHandItem, itemstack1)) { + this.offHandItem = itemstack1; + } + + if (localplayer.isHandsBusy()) { + this.mainHandHeight = Mth.clamp(this.mainHandHeight - 0.4F, 0.0F, 1.0F); + this.offHandHeight = Mth.clamp(this.offHandHeight - 0.4F, 0.0F, 1.0F); + } else { + float f = localplayer.getAttackStrengthScale(1.0F); + boolean requipM = net.neoforged.neoforge.client.ClientHooks.shouldCauseReequipAnimation(this.mainHandItem, itemstack, localplayer.getInventory().selected); + boolean requipO = net.neoforged.neoforge.client.ClientHooks.shouldCauseReequipAnimation(this.offHandItem, itemstack1, -1); + + if (!requipM && this.mainHandItem != itemstack) + this.mainHandItem = itemstack; + if (!requipO && this.offHandItem != itemstack1) + this.offHandItem = itemstack1; + + this.mainHandHeight += Mth.clamp((!requipM ? f * f * f : 0.0F) - this.mainHandHeight, -0.4F, 0.4F); + this.offHandHeight += Mth.clamp((float)(!requipO ? 1 : 0) - this.offHandHeight, -0.4F, 0.4F); + } + + if (this.mainHandHeight < 0.1F) { + this.mainHandItem = itemstack; + } + + if (this.offHandHeight < 0.1F) { + this.offHandItem = itemstack1; + } + } + + public void itemUsed(InteractionHand hand) { + if (hand == InteractionHand.MAIN_HAND) { + this.mainHandHeight = 0.0F; + } else { + this.offHandHeight = 0.0F; + } + } + + @OnlyIn(Dist.CLIENT) + @VisibleForTesting + static enum HandRenderSelection { + RENDER_BOTH_HANDS(true, true), + RENDER_MAIN_HAND_ONLY(true, false), + RENDER_OFF_HAND_ONLY(false, true); + + final boolean renderMainHand; + final boolean renderOffHand; + + private HandRenderSelection(boolean renderMainHand, boolean renderOffHand) { + this.renderMainHand = renderMainHand; + this.renderOffHand = renderOffHand; + } + + public static ItemInHandRenderer.HandRenderSelection onlyForHand(InteractionHand hand) { + return hand == InteractionHand.MAIN_HAND ? RENDER_MAIN_HAND_ONLY : RENDER_OFF_HAND_ONLY; + } + } +} diff --git a/net/neoforged/neoforge/client/event/RenderHandEvent.java b/net/neoforged/neoforge/client/event/RenderHandEvent.java new file mode 100644 index 0000000..31cdb5e --- /dev/null +++ b/net/neoforged/neoforge/client/event/RenderHandEvent.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired before a hand is rendered in the first person view. + * + *

This event is {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}. + * If this event is cancelled, then the hand will not be rendered.

+ * + *

This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus}, + * only on the {@linkplain LogicalSide#CLIENT logical client}.

+ * + * @see RenderArmEvent + */ +public class RenderHandEvent extends Event implements ICancellableEvent { + private final InteractionHand hand; + private final PoseStack poseStack; + private final MultiBufferSource multiBufferSource; + private final int packedLight; + private final float partialTick; + private final float interpolatedPitch; + private final float swingProgress; + private final float equipProgress; + private final ItemStack stack; + + @ApiStatus.Internal + public RenderHandEvent(InteractionHand hand, PoseStack poseStack, MultiBufferSource multiBufferSource, int packedLight, + float partialTick, float interpolatedPitch, + float swingProgress, float equipProgress, ItemStack stack) { + this.hand = hand; + this.poseStack = poseStack; + this.multiBufferSource = multiBufferSource; + this.packedLight = packedLight; + this.partialTick = partialTick; + this.interpolatedPitch = interpolatedPitch; + this.swingProgress = swingProgress; + this.equipProgress = equipProgress; + this.stack = stack; + } + + /** + * {@return the hand being rendered} + */ + public InteractionHand getHand() { + return hand; + } + + /** + * {@return the pose stack used for rendering} + */ + public PoseStack getPoseStack() { + return poseStack; + } + + /** + * {@return the source of rendering buffers} + */ + public MultiBufferSource getMultiBufferSource() { + return multiBufferSource; + } + + /** + * {@return the amount of packed (sky and block) light for rendering} + * + * @see LightTexture + */ + public int getPackedLight() { + return packedLight; + } + + /** + * {@return the partial tick} + */ + public float getPartialTick() { + return partialTick; + } + + /** + * {@return the interpolated pitch of the player entity} + */ + public float getInterpolatedPitch() { + return interpolatedPitch; + } + + /** + * {@return the swing progress of the hand being rendered} + */ + public float getSwingProgress() { + return swingProgress; + } + + /** + * {@return the progress of the equip animation, from {@code 0.0} to {@code 1.0}} + */ + public float getEquipProgress() { + return equipProgress; + } + + /** + * {@return the item stack to be rendered} + */ + public ItemStack getItemStack() { + return stack; + } +} diff --git a/scripts/PassiveMobArtGenerator$MobArt.class b/scripts/PassiveMobArtGenerator$MobArt.class new file mode 100644 index 0000000..362cb88 Binary files /dev/null and b/scripts/PassiveMobArtGenerator$MobArt.class differ diff --git a/scripts/PassiveMobArtGenerator.java b/scripts/PassiveMobArtGenerator.java new file mode 100644 index 0000000..502bb5d --- /dev/null +++ b/scripts/PassiveMobArtGenerator.java @@ -0,0 +1,460 @@ +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Random; + +import javax.imageio.ImageIO; + +public final class PassiveMobArtGenerator { + private static final int MASTER_SIZE = 256; + private static final int CARD_SIZE = 64; + private static final int PIXEL = 4; + + private static final Path ROOT = Path.of("").toAbsolutePath(); + private static final Path FULL_ART_DIR = ROOT.resolve("Full Art").resolve("passive_mobs"); + private static final Path CARD_ART_DIR = ROOT.resolve("src") + .resolve("main") + .resolve("resources") + .resolve("assets") + .resolve("minetriad") + .resolve("textures") + .resolve("item") + .resolve("cards"); + + private record MobArt(String name, Color base, Color dark, Color light, Color backgroundTop, Color backgroundBottom, + Color accent, List features) { + } + + private static final List MOBS = List.of( + mob("axolotl", rgb(244, 173, 203), rgb(232, 144, 187), rgb(124, 62, 110), rgb(95, 173, 207), rgb(19, 76, 113), rgb(255, 214, 122), "gills"), + mob("bee", rgb(255, 213, 62), rgb(84, 57, 29), rgb(255, 246, 196), rgb(131, 200, 95), rgb(50, 112, 49), rgb(226, 248, 255), "wings", "stripe"), + mob("camel", rgb(193, 154, 101), rgb(128, 94, 57), rgb(232, 203, 155), rgb(241, 194, 120), rgb(189, 110, 49), rgb(77, 148, 192), "ears"), + mob("cat", rgb(217, 176, 92), rgb(105, 76, 44), rgb(247, 229, 174), rgb(142, 186, 228), rgb(71, 99, 143), rgb(158, 219, 110), "ears", "snout"), + mob("chicken", rgb(243, 246, 239), rgb(208, 72, 46), rgb(251, 205, 61), rgb(161, 212, 127), rgb(89, 130, 70), rgb(164, 199, 245), "beak", "crest"), + mob("cod", rgb(145, 117, 88), rgb(95, 76, 52), rgb(211, 185, 149), rgb(57, 136, 170), rgb(15, 62, 97), rgb(230, 120, 74), "fins"), + mob("cow", rgb(116, 77, 52), rgb(59, 37, 25), rgb(231, 219, 206), rgb(144, 196, 110), rgb(75, 115, 59), rgb(162, 212, 255), "horns", "snout", "spots"), + mob("donkey", rgb(143, 118, 92), rgb(81, 62, 44), rgb(218, 202, 182), rgb(131, 170, 210), rgb(66, 90, 129), rgb(125, 193, 98), "long_ears", "snout"), + mob("fox", rgb(222, 110, 57), rgb(108, 55, 34), rgb(250, 239, 221), rgb(145, 204, 130), rgb(68, 109, 71), rgb(247, 214, 119), "ears", "snout"), + mob("frog", rgb(132, 176, 92), rgb(70, 108, 54), rgb(208, 219, 171), rgb(121, 180, 116), rgb(63, 110, 68), rgb(255, 220, 128), "frog_eyes"), + mob("glow_squid", rgb(96, 172, 188), rgb(41, 87, 101), rgb(163, 231, 246), rgb(38, 52, 102), rgb(11, 18, 39), rgb(119, 243, 255), "tentacles", "glow"), + mob("goat", rgb(208, 196, 184), rgb(121, 110, 98), rgb(245, 240, 233), rgb(160, 196, 193), rgb(92, 124, 122), rgb(192, 171, 101), "horns", "beard"), + mob("horse", rgb(122, 83, 55), rgb(65, 44, 28), rgb(236, 219, 187), rgb(134, 188, 121), rgb(64, 111, 67), rgb(187, 223, 255), "ears", "snout"), + mob("llama", rgb(220, 197, 162), rgb(140, 112, 80), rgb(248, 236, 219), rgb(170, 197, 122), rgb(88, 118, 72), rgb(197, 122, 67), "long_ears"), + mob("mooshroom", rgb(187, 54, 44), rgb(98, 26, 24), rgb(239, 226, 208), rgb(114, 162, 94), rgb(61, 103, 52), rgb(237, 247, 215), "horns", "snout", "spots", "mushroom"), + mob("mule", rgb(133, 104, 80), rgb(76, 57, 42), rgb(220, 205, 186), rgb(150, 188, 132), rgb(76, 112, 74), rgb(113, 166, 211), "long_ears", "snout"), + mob("ocelot", rgb(220, 185, 82), rgb(112, 85, 33), rgb(249, 228, 158), rgb(117, 178, 113), rgb(56, 104, 59), rgb(236, 191, 94), "ears", "snout", "spots"), + mob("panda", rgb(235, 238, 242), rgb(42, 46, 52), rgb(255, 255, 255), rgb(137, 198, 124), rgb(70, 111, 64), rgb(198, 227, 141), "ears", "panda_eyes"), + mob("parrot", rgb(205, 61, 57), rgb(42, 88, 182), rgb(255, 214, 54), rgb(115, 190, 155), rgb(52, 94, 102), rgb(182, 238, 93), "beak", "crest", "wings"), + mob("pig", rgb(233, 167, 182), rgb(168, 99, 122), rgb(250, 208, 217), rgb(133, 193, 115), rgb(74, 116, 67), rgb(204, 230, 255), "snout"), + mob("polar_bear", rgb(238, 243, 247), rgb(152, 173, 191), rgb(255, 255, 255), rgb(146, 188, 217), rgb(82, 117, 149), rgb(195, 225, 255), "ears", "snout"), + mob("rabbit", rgb(187, 173, 162), rgb(117, 98, 86), rgb(240, 231, 222), rgb(166, 203, 111), rgb(87, 118, 63), rgb(255, 215, 145), "long_ears", "snout"), + mob("salmon", rgb(214, 120, 95), rgb(121, 76, 60), rgb(247, 183, 162), rgb(72, 146, 175), rgb(17, 69, 107), rgb(225, 212, 117), "fins"), + mob("sheep", rgb(233, 235, 241), rgb(128, 100, 74), rgb(255, 255, 255), rgb(142, 197, 117), rgb(81, 123, 68), rgb(168, 218, 255), "wool", "snout"), + mob("sniffer", rgb(127, 95, 66), rgb(73, 52, 39), rgb(180, 141, 106), rgb(102, 152, 88), rgb(54, 93, 55), rgb(222, 106, 58), "snout", "moss"), + mob("squid", rgb(133, 92, 176), rgb(63, 41, 91), rgb(212, 181, 236), rgb(58, 102, 153), rgb(20, 35, 74), rgb(185, 132, 236), "tentacles"), + mob("strider", rgb(209, 84, 72), rgb(111, 36, 36), rgb(246, 166, 133), rgb(231, 144, 71), rgb(129, 68, 35), rgb(101, 38, 38), "tentacles", "snout"), + mob("turtle", rgb(107, 150, 77), rgb(57, 92, 45), rgb(194, 176, 88), rgb(92, 166, 143), rgb(44, 100, 100), rgb(223, 232, 176), "shell"), + mob("wolf", rgb(181, 188, 197), rgb(91, 98, 109), rgb(231, 235, 239), rgb(118, 164, 188), rgb(63, 97, 115), rgb(209, 67, 57), "ears", "snout")); + + public static void main(String[] args) throws IOException { + Files.createDirectories(FULL_ART_DIR); + Files.createDirectories(CARD_ART_DIR); + + for (MobArt mob : MOBS) { + BufferedImage master = generateArt(mob); + ImageIO.write(master, "png", FULL_ART_DIR.resolve(mob.name() + ".png").toFile()); + ImageIO.write(scale(master, CARD_SIZE, CARD_SIZE), "png", CARD_ART_DIR.resolve(mob.name() + ".png").toFile()); + } + + System.out.println("Generated " + MOBS.size() + " passive mob paintings."); + } + + private static BufferedImage generateArt(MobArt mob) { + BufferedImage image = new BufferedImage(MASTER_SIZE, MASTER_SIZE, BufferedImage.TYPE_INT_ARGB); + Random random = new Random(mob.name().hashCode()); + paintBackground(image, mob, random); + + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g.setComposite(AlphaComposite.SrcOver); + + drawHead(g, mob, random); + addForeground(g, mob, random); + addAtmosphere(g, mob, random); + + g.dispose(); + return image; + } + + private static void paintBackground(BufferedImage image, MobArt mob, Random random) { + for (int y = 0; y < MASTER_SIZE; y++) { + double blend = (double) y / (MASTER_SIZE - 1); + double wave = Math.sin((y / (double) MASTER_SIZE) * Math.PI * 3.0) * 0.08; + Color row = mix(mob.backgroundTop(), mob.backgroundBottom(), clamp01(blend + wave)); + for (int x = 0; x < MASTER_SIZE; x++) { + double distanceX = x - (MASTER_SIZE / 2.0); + double distanceY = y - (MASTER_SIZE / 2.0); + double vignette = (distanceX * distanceX + distanceY * distanceY) / (MASTER_SIZE * (double) MASTER_SIZE); + Color shaded = shade(row, vignette * 0.65); + int noise = random.nextInt(17) - 8; + image.setRGB(x, y, shift(shaded, noise).getRGB()); + } + } + + Graphics2D g = image.createGraphics(); + for (int i = 0; i < 10; i++) { + int radius = random.nextInt(35) + 18; + int x = random.nextInt(MASTER_SIZE + 40) - 20; + int y = random.nextInt(MASTER_SIZE + 40) - 20; + g.setColor(alpha(mob.accent(), random.nextInt(56) + 40)); + g.fillOval(x, y, radius, radius); + } + + for (int i = 0; i < 55; i++) { + int x = random.nextInt(MASTER_SIZE); + int y = random.nextInt(MASTER_SIZE); + int size = random.nextInt(5) + 2; + g.setColor(alpha(tint(mob.accent(), 0.35), random.nextInt(31) + 18)); + g.fillRect(x, y, size, size); + } + g.dispose(); + } + + private static void drawHead(Graphics2D g, MobArt mob, Random random) { + Color outline = shade(mob.dark(), 0.18); + block(g, 16, 15, 32, 26, mob.base(), outline); + block(g, 18, 17, 28, 9, tint(mob.light(), 0.08), null); + block(g, 18, 26, 28, 13, mob.base(), null); + block(g, 22, 41, 20, 8, shade(mob.base(), 0.12), outline); + + if (has(mob, "spots")) { + spot(g, 20, 20, 4, 4, shade(mob.dark(), 0.08)); + spot(g, 39, 23, 5, 4, shade(mob.dark(), 0.08)); + spot(g, 24, 31, 4, 3, shade(mob.dark(), 0.08)); + } + + if (has(mob, "wool")) { + puff(g, 16, 14, mob, outline); + puff(g, 22, 11, mob, outline); + puff(g, 30, 10, mob, outline); + puff(g, 38, 12, mob, outline); + puff(g, 44, 15, mob, outline); + } + + if (has(mob, "moss")) { + block(g, 18, 14, 9, 4, rgb(92, 138, 78), null); + block(g, 37, 15, 7, 3, rgb(92, 138, 78), null); + block(g, 30, 18, 8, 3, rgb(92, 138, 78), null); + } + + if (has(mob, "shell")) { + block(g, 21, 21, 22, 17, mob.light(), outline); + for (int stripeX : new int[]{25, 32, 39}) { + block(g, stripeX, 23, 2, 13, tint(mob.base(), 0.1), null); + } + } + + if (has(mob, "frog_eyes")) { + eyeBump(g, 18, mob.light(), outline); + eyeBump(g, 40, mob.light(), outline); + } + + if (has(mob, "ears")) { + ear(g, 15, mob.base(), outline); + ear(g, 43, mob.base(), outline); + } + + if (has(mob, "long_ears")) { + longEar(g, 15, mob.base(), mob.light(), outline); + longEar(g, 44, mob.base(), mob.light(), outline); + } + + if (has(mob, "horns")) { + horn(g, 16, mob.light(), outline); + horn(g, 45, mob.light(), outline); + } + + if (has(mob, "gills")) { + block(g, 9, 22, 4, 12, tint(mob.base(), 0.06), outline); + block(g, 51, 22, 4, 12, tint(mob.base(), 0.06), outline); + } + + if (has(mob, "beard")) { + block(g, 30, 47, 4, 5, tint(mob.light(), 0.12), outline); + } + + if (has(mob, "mushroom")) { + mushroom(g, 15, outline); + mushroom(g, 39, outline); + } + + if (has(mob, "wings")) { + wing(g, 2, tint(mob.light(), 0.25), tint(mob.light(), 0.05)); + wing(g, 54, tint(mob.light(), 0.25), tint(mob.light(), 0.05)); + } + + if (has(mob, "tentacles")) { + int[] tentacles = {19, 24, 29, 34, 39, 44}; + for (int i = 0; i < tentacles.length; i++) { + block(g, tentacles[i], 41 + (i % 2), 3, 12, shade(mob.base(), 0.12), outline); + } + } + + if (has(mob, "fins")) { + fin(g, 8, tint(mob.light(), 0.18), outline); + fin(g, 51, tint(mob.light(), 0.18), outline); + block(g, 29, 11, 6, 7, tint(mob.light(), 0.18), outline); + } + + if (has(mob, "stripe")) { + block(g, 18, 22, 28, 3, mob.dark(), null); + block(g, 18, 28, 28, 3, mob.dark(), null); + block(g, 18, 34, 28, 3, mob.dark(), null); + } + + if (has(mob, "glow")) { + sparkle(g, 20, 18, 4, 4, tint(mob.accent(), 0.25)); + sparkle(g, 40, 21, 3, 3, tint(mob.accent(), 0.25)); + sparkle(g, 24, 31, 3, 3, tint(mob.accent(), 0.25)); + sparkle(g, 37, 34, 4, 4, tint(mob.accent(), 0.25)); + } + + if (has(mob, "panda_eyes")) { + eyePatch(g, 16, mob.dark(), outline); + eyePatch(g, 40, mob.dark(), outline); + } + + if (has(mob, "crest")) { + block(g, 28, 11, 8, 4, mob.accent(), outline); + } + + if (has(mob, "snout")) { + block(g, 24, 34, 16, 9, mob.light(), outline); + block(g, 27, 37, 3, 3, mob.dark(), null); + block(g, 34, 37, 3, 3, mob.dark(), null); + } + + Color eyeWhite = tint(mob.light(), 0.25); + Color eyeFill = ("panda".equals(mob.name()) || "wolf".equals(mob.name())) ? rgb(212, 61, 56) : mob.accent(); + eye(g, 21, eyeWhite, eyeFill, outline); + eye(g, 37, eyeWhite, eyeFill, outline); + + if ("cod".equals(mob.name()) || "salmon".equals(mob.name())) { + block(g, 18, 30, 4, 2, mob.dark(), null); + block(g, 42, 30, 4, 2, mob.dark(), null); + } + + switch (mob.name()) { + case "bee" -> block(g, 24, 35, 16, 6, tint(mob.base(), 0.1), outline); + case "parrot" -> { + block(g, 28, 33, 8, 10, mob.accent(), outline); + block(g, 31, 38, 2, 3, mob.dark(), null); + } + case "chicken" -> { + block(g, 27, 33, 10, 7, mob.accent(), outline); + block(g, 29, 40, 6, 3, rgb(220, 41, 38), null); + } + case "frog" -> block(g, 25, 37, 14, 3, shade(mob.base(), 0.2), null); + case "glow_squid" -> block(g, 18, 18, 28, 20, shade(mob.base(), 0.05), outline); + case "strider" -> { + block(g, 24, 17, 16, 10, shade(mob.base(), 0.08), outline); + block(g, 20, 24, 24, 4, tint(mob.light(), 0.1), null); + } + case "sniffer" -> { + block(g, 18, 24, 28, 16, shade(mob.base(), 0.04), outline); + block(g, 23, 35, 18, 6, rgb(229, 120, 67), outline); + } + default -> { + } + } + + Color shadow = shade(mob.base(), 0.18); + fill(g, 18, 40, 28, 1, shadow); + fill(g, 16, 39, 2, 2, shadow); + fill(g, 46, 39, 2, 2, shadow); + + for (int i = 0; i < 8; i++) { + int x = random.nextInt(33) + 14; + int y = random.nextInt(37) + 13; + if (random.nextDouble() < 0.35) { + fill(g, x, y, 1, 1, tint(mob.light(), 0.18)); + } + } + } + + private static void addForeground(Graphics2D g, MobArt mob, Random random) { + for (int i = 0; i < 12; i++) { + int x = random.nextInt(59); + int y = random.nextInt(15) + 48; + int height = random.nextInt(7) + 2; + fill(g, x, y, 2, height, tint(mob.backgroundTop(), 0.06)); + } + + if (List.of("cod", "salmon", "squid", "glow_squid", "axolotl", "turtle").contains(mob.name())) { + Color wave = tint(mob.backgroundTop(), 0.12); + for (int y : new int[]{45, 50, 55}) { + for (int x = 0; x < 64; x += 6) { + fill(g, x, y, 4, 1, wave); + } + } + } + + if (List.of("bee", "parrot").contains(mob.name())) { + fill(g, 10, 16, 2, 2, tint(mob.light(), 0.35)); + fill(g, 49, 16, 2, 2, tint(mob.light(), 0.35)); + } + + if (List.of("mooshroom", "cow", "sheep", "pig", "horse", "goat").contains(mob.name())) { + fill(g, 11, 52, 4, 6, shade(mob.dark(), 0.1)); + fill(g, 50, 52, 4, 6, shade(mob.dark(), 0.1)); + } + } + + private static void addAtmosphere(Graphics2D g, MobArt mob, Random random) { + for (int i = 0; i < 18; i++) { + int x = random.nextInt(MASTER_SIZE - 20) + 10; + int y = random.nextInt(MASTER_SIZE - 16) + 8; + int size = random.nextInt(5) + 3; + g.setColor(alpha(tint(mob.accent(), 0.25), random.nextInt(36) + 20)); + g.fillOval(x, y, size, size); + } + } + + private static BufferedImage scale(BufferedImage source, int width, int height) { + BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = scaled.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g.drawImage(source, 0, 0, width, height, null); + g.dispose(); + return scaled; + } + + private static MobArt mob(String name, Color base, Color dark, Color light, Color backgroundTop, Color backgroundBottom, + Color accent, String... features) { + return new MobArt(name, base, dark, light, backgroundTop, backgroundBottom, accent, List.of(features)); + } + + private static boolean has(MobArt mob, String feature) { + return mob.features().contains(feature); + } + + private static void puff(Graphics2D g, int x, int y, MobArt mob, Color outline) { + block(g, x, y, 6, 6, tint(mob.light(), 0.08), outline); + } + + private static void ear(Graphics2D g, int x, Color base, Color outline) { + block(g, x, 9, 6, 8, base, outline); + } + + private static void longEar(Graphics2D g, int x, Color base, Color light, Color outline) { + block(g, x, 4, 5, 14, base, outline); + block(g, x + 1, 7, 2, 8, tint(light, 0.14), null); + } + + private static void horn(Graphics2D g, int x, Color light, Color outline) { + block(g, x, 8, 3, 9, tint(light, 0.2), outline); + } + + private static void mushroom(Graphics2D g, int x, Color outline) { + block(g, x, 7, 10, 4, rgb(222, 204, 196), outline); + block(g, x + 2, 3, 6, 6, rgb(194, 51, 47), outline); + } + + private static void wing(Graphics2D g, int x, Color fill, Color outline) { + block(g, x, 20, 8, 14, fill, outline); + } + + private static void fin(Graphics2D g, int x, Color fill, Color outline) { + block(g, x, 25, 5, 10, fill, outline); + } + + private static void eyeBump(Graphics2D g, int x, Color fill, Color outline) { + block(g, x, 10, 6, 6, fill, outline); + } + + private static void eyePatch(Graphics2D g, int x, Color fill, Color outline) { + block(g, x, 24, 8, 8, fill, outline); + } + + private static void eye(Graphics2D g, int x, Color white, Color iris, Color outline) { + block(g, x, 24, 5, 5, white, outline); + block(g, x + 1, 25, 2, 2, iris, null); + } + + private static void sparkle(Graphics2D g, int x, int y, int w, int h, Color fill) { + block(g, x, y, w, h, fill, null); + } + + private static void spot(Graphics2D g, int x, int y, int w, int h, Color fill) { + block(g, x, y, w, h, fill, null); + } + + private static void block(Graphics2D g, int x, int y, int w, int h, Color fill, Color outline) { + fill(g, x, y, w, h, fill); + if (outline != null) { + fill(g, x, y, w, 1, outline); + fill(g, x, y + h - 1, w, 1, outline); + fill(g, x, y, 1, h, outline); + fill(g, x + w - 1, y, 1, h, outline); + } + } + + private static void fill(Graphics2D g, int x, int y, int w, int h, Color color) { + g.setColor(color); + g.fillRect(x * PIXEL, y * PIXEL, w * PIXEL, h * PIXEL); + } + + private static Color tint(Color color, double amount) { + return new Color( + clamp(color.getRed() + ((255 - color.getRed()) * amount)), + clamp(color.getGreen() + ((255 - color.getGreen()) * amount)), + clamp(color.getBlue() + ((255 - color.getBlue()) * amount)) + ); + } + + private static Color shade(Color color, double amount) { + return new Color( + clamp(color.getRed() * (1.0 - amount)), + clamp(color.getGreen() * (1.0 - amount)), + clamp(color.getBlue() * (1.0 - amount)) + ); + } + + private static Color shift(Color color, int amount) { + return new Color( + clamp(color.getRed() + amount), + clamp(color.getGreen() + amount), + clamp(color.getBlue() + amount) + ); + } + + private static Color mix(Color a, Color b, double amount) { + return new Color( + clamp(a.getRed() + ((b.getRed() - a.getRed()) * amount)), + clamp(a.getGreen() + ((b.getGreen() - a.getGreen()) * amount)), + clamp(a.getBlue() + ((b.getBlue() - a.getBlue()) * amount)) + ); + } + + private static Color alpha(Color color, int alpha) { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); + } + + private static Color rgb(int red, int green, int blue) { + return new Color(red, green, blue); + } + + private static int clamp(double value) { + return Math.max(0, Math.min(255, (int) Math.round(value))); + } + + private static double clamp01(double value) { + return Math.max(0.0, Math.min(1.0, value)); + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/MineTriad.java b/src/main/java/com/trunksbomb/minetriad/MineTriad.java index 0b82ed0..074bd49 100644 --- a/src/main/java/com/trunksbomb/minetriad/MineTriad.java +++ b/src/main/java/com/trunksbomb/minetriad/MineTriad.java @@ -8,6 +8,7 @@ import com.trunksbomb.minetriad.registry.TriadBlockEntities; import com.trunksbomb.minetriad.registry.TriadCreativeTabs; import com.trunksbomb.minetriad.registry.TriadDataComponents; import com.trunksbomb.minetriad.registry.TriadItems; +import com.trunksbomb.minetriad.registry.TriadMenus; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; @@ -23,6 +24,7 @@ public final class MineTriad { TriadBlocks.register(modEventBus); TriadBlockEntities.register(modEventBus); TriadItems.register(modEventBus); + TriadMenus.register(modEventBus); TriadCreativeTabs.register(modEventBus); } } diff --git a/src/main/java/com/trunksbomb/minetriad/MineTriadClient.java b/src/main/java/com/trunksbomb/minetriad/MineTriadClient.java index 04049cd..07fdc0f 100644 --- a/src/main/java/com/trunksbomb/minetriad/MineTriadClient.java +++ b/src/main/java/com/trunksbomb/minetriad/MineTriadClient.java @@ -1,15 +1,22 @@ package com.trunksbomb.minetriad; +import com.trunksbomb.minetriad.client.render.FirstPersonCardHandRenderer; import com.trunksbomb.minetriad.client.render.DuelTableBlockEntityRenderer; +import com.trunksbomb.minetriad.client.screen.CardBinderScreen; +import com.trunksbomb.minetriad.registry.TriadItems; import com.trunksbomb.minetriad.registry.TriadBlockEntities; +import com.trunksbomb.minetriad.registry.TriadMenus; import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; +import net.minecraft.client.gui.screens.MenuScreens; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.Mod; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.EntityRenderersEvent; +import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; +import net.neoforged.neoforge.client.event.RenderHandEvent; @Mod(value = MineTriad.MOD_ID, dist = Dist.CLIENT) public final class MineTriadClient { @@ -22,5 +29,31 @@ public final class MineTriadClient { public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerBlockEntityRenderer(TriadBlockEntities.DUEL_TABLE.get(), DuelTableBlockEntityRenderer::new); } + + @SubscribeEvent + public static void registerScreens(RegisterMenuScreensEvent event) { + event.register(TriadMenus.CARD_BINDER.get(), CardBinderScreen::new); + } + } + + @EventBusSubscriber(modid = MineTriad.MOD_ID, value = Dist.CLIENT) + public static final class ClientGameEvents { + @SubscribeEvent + public static void onRenderHand(RenderHandEvent event) { + if (!event.getItemStack().is(TriadItems.TRIAD_CARD.get())) { + return; + } + + event.setCanceled(true); + FirstPersonCardHandRenderer.render( + event.getHand(), + event.getPoseStack(), + event.getMultiBufferSource(), + event.getPackedLight(), + event.getInterpolatedPitch(), + event.getSwingProgress(), + event.getEquipProgress(), + event.getItemStack()); + } } } diff --git a/src/main/java/com/trunksbomb/minetriad/card/CardDefinition.java b/src/main/java/com/trunksbomb/minetriad/card/CardDefinition.java index 0212ae4..e52de80 100644 --- a/src/main/java/com/trunksbomb/minetriad/card/CardDefinition.java +++ b/src/main/java/com/trunksbomb/minetriad/card/CardDefinition.java @@ -6,6 +6,7 @@ import net.minecraft.resources.ResourceLocation; public record CardDefinition( ResourceLocation id, Component name, + int level, int top, int right, int bottom, @@ -13,6 +14,9 @@ public record CardDefinition( Rarity rarity) { public CardDefinition { + if (level < 1 || level > 10) { + throw new IllegalArgumentException("level must be between 1 and 10"); + } validateRank("top", top); validateRank("right", right); validateRank("bottom", bottom); diff --git a/src/main/java/com/trunksbomb/minetriad/card/CardRegistry.java b/src/main/java/com/trunksbomb/minetriad/card/CardRegistry.java index e9b9416..5c73e52 100644 --- a/src/main/java/com/trunksbomb/minetriad/card/CardRegistry.java +++ b/src/main/java/com/trunksbomb/minetriad/card/CardRegistry.java @@ -1,5 +1,6 @@ package com.trunksbomb.minetriad.card; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -11,14 +12,139 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; public final class CardRegistry { - private static final List CARD_DEFINITIONS = List.of( - define("slime", "Slime", 3, 5, 2, 4, Rarity.COMMON), - define("zombie", "Zombie", 5, 4, 6, 3, Rarity.COMMON), - define("skeleton", "Skeleton", 6, 5, 3, 2, Rarity.COMMON), - define("creeper", "Creeper", 2, 7, 5, 6, Rarity.UNCOMMON), - define("enderman", "Enderman", 8, 4, 7, 5, Rarity.RARE), - define("warden", "Warden", 9, 8, 7, 6, Rarity.LEGENDARY)); + private static final int[][][] TT_LEVEL_STATS = { + { + { 1, 4, 1, 5 }, // Geezard + { 1, 1, 1, 3 }, // Funguar + { 3, 3, 3, 5 }, // Bite Bug + { 1, 1, 1, 2 }, // Red Bat + { 1, 3, 1, 5 }, // Blobra + { 4, 1, 4, 4 }, // Gayla + { 4, 5, 4, 1 }, // Gesper + { 2, 5, 2, 1 }, // Fastitocalon-F + { 6, 1, 6, 1 }, // Blood Soul + { 4, 2, 4, 3 }, // Caterchipillar + { 2, 1, 2, 6 } // Cockatrice + }, + { + { 3, 1, 3, 1 }, // Grat + { 2, 2, 2, 3 }, // Buel + { 3, 3, 3, 4 }, // Mesmerize + { 4, 1, 4, 3 }, // Glacial Eye + { 5, 4, 5, 3 }, // Belhelmel + { 2, 3, 2, 5 }, // Thrustaevis + { 3, 1, 3, 5 }, // Anacondaur + { 5, 2, 5, 2 }, // Creeps + { 5, 4, 5, 2 }, // Grendal + { 1, 2, 1, 7 }, // Jelleye + { 5, 2, 5, 3 } // Grand Mantis + }, + { + { 3, 6, 3, 2 }, // Forbidden + { 1, 3, 1, 6 }, // Armadodo + { 5, 5, 5, 5 }, // Tri-Face + { 1, 5, 1, 3 }, // Fastitocalon + { 5, 1, 5, 3 }, // Snow Lion + { 3, 6, 3, 3 }, // Ochu + { 2, 6, 2, 4 }, // SAM08G + { 7, 4, 7, 2 }, // Death Claw + { 6, 2, 6, 3 }, // Cactuar + { 4, 6, 4, 4 }, // Tonberry + { 3, 2, 3, 5 } // Abyss Worm + }, + { + { 3, 6, 3, 7 }, // Turtapod + { 4, 5, 4, 5 }, // Vysage + { 2, 6, 2, 7 }, // T-Rexaur + { 6, 7, 6, 3 }, // Bomb + { 4, 6, 4, 7 }, // Blitz + { 1, 3, 1, 6 }, // Wendigo + { 4, 4, 4, 4 }, // Torama + { 3, 7, 3, 6 }, // Imp + { 7, 2, 7, 3 }, // Blue Dragon + { 5, 5, 5, 6 }, // Adamantoise + { 4, 5, 4, 3 } // HexDragon + }, + { + { 6, 5, 6, 5 }, // Iron Giant + { 5, 6, 5, 7 }, // Behemoth + { 5, 6, 5, 3 }, // Chimera + { 2, 10, 2, 1 }, // Pupu + { 6, 2, 6, 7 }, // Elastoid + { 7, 5, 7, 4 }, // GIM47N + { 4, 7, 4, 2 }, // Malboro + { 7, 2, 7, 4 }, // Ruby Dragon + { 7, 3, 7, 6 }, // Elnoyle + { 6, 7, 6, 4 }, // Tonberry King + { 2, 6, 2, 7 } // Wedge, Biggs + }, + { + { 8, 8, 8, 4 }, // Fujin, Rajin + { 3, 8, 3, 4 }, // Elvoret + { 7, 8, 7, 3 }, // X-ATM092 + { 8, 2, 8, 5 }, // Granaldo + { 8, 8, 8, 3 }, // Gerogero + { 8, 2, 8, 2 }, // Iguion + { 4, 8, 4, 5 }, // Abadon + { 5, 8, 5, 6 }, // Trauma + { 4, 8, 4, 8 }, // Oilboyle + { 8, 5, 8, 4 }, // Shumi Tribe + { 8, 5, 8, 1 } // Krysta + }, + { + { 4, 4, 4, 8 }, // Propagator + { 4, 8, 4, 4 }, // Jumbo Cactuar + { 6, 6, 6, 8 }, // Tri-Point + { 6, 6, 6, 8 }, // Gargantua + { 7, 6, 7, 3 }, // Mobile Type 8 + { 5, 3, 5, 8 }, // Sphinxara + { 5, 8, 5, 4 }, // Tiamat + { 8, 7, 8, 5 }, // BGH251F2 + { 4, 8, 4, 7 }, // Red Giant + { 7, 8, 7, 7 }, // Catoblepas + { 1, 7, 1, 8 } // Ultima Weapon + }, + { + { 8, 4, 8, 9 }, // Chubby Chocobo + { 7, 6, 7, 3 }, // Angelo + { 9, 7, 9, 6 }, // Gilgamesh + { 9, 3, 9, 2 }, // Mini Mog + { 8, 4, 8, 4 }, // Chicobo + { 9, 9, 9, 4 }, // Quezacotl + { 4, 7, 4, 9 }, // Shiva + { 2, 6, 2, 8 }, // Ifrit + { 6, 9, 6, 2 }, // Siren + { 9, 1, 9, 9 }, // Sacred + { 2, 5, 2, 9 } // Minotaur + }, + { + { 8, 4, 8, 4 }, // Carbuncle + { 8, 8, 8, 3 }, // Diablos + { 1, 10, 1, 7 }, // Leviathan + { 3, 10, 3, 5 }, // Odin + { 7, 1, 7, 7 }, // Pandemona + { 6, 4, 6, 10 }, // Cerberus + { 4, 10, 4, 2 }, // Alexander + { 7, 2, 7, 10 }, // Phoenix + { 2, 8, 2, 6 }, // Bahamut + { 10, 1, 10, 10 }, // Doomtrain + { 9, 4, 9, 10 } // Eden + }, + { + { 2, 7, 2, 8 }, // Ward + { 6, 7, 6, 10 }, // Kiros + { 3, 10, 3, 9 }, // Laguna + { 6, 8, 6, 4 }, // Selphie + { 10, 6, 10, 2 }, // Quistis + { 9, 6, 9, 10 }, // Irvine + { 10, 5, 10, 6 }, // Zell + { 2, 10, 2, 10 }, // Rinoa + { 3, 10, 3, 3 }, // Edea + { 10, 9, 10, 4 }, // Seifer + { 6, 4, 6, 9 } // Squall + } }; + private static final List CARD_DEFINITIONS = createDefinitions(); private static final Map CARD_LOOKUP = CARD_DEFINITIONS.stream() .collect(Collectors.toUnmodifiableMap(CardDefinition::id, Function.identity())); @@ -37,14 +163,170 @@ public final class CardRegistry { return definition; } - private static CardDefinition define(String path, String displayName, int top, int right, int bottom, int left, Rarity rarity) { + private static List createDefinitions() { + List cards = new ArrayList<>(); + + registerLevel(cards, 1, Rarity.COMMON, + seed("chicken", "Chicken"), + seed("rabbit", "Rabbit"), + seed("cod", "Cod"), + seed("salmon", "Salmon"), + seed("tropical_fish", "Tropical Fish"), + seed("pufferfish", "Pufferfish"), + seed("tadpole", "Tadpole"), + seed("bat", "Bat"), + seed("bee", "Bee"), + seed("pig", "Pig"), + seed("sheep", "Sheep")); + + registerLevel(cards, 2, Rarity.COMMON, + seed("cow", "Cow"), + seed("mooshroom", "Mooshroom"), + seed("goat", "Goat"), + seed("fox", "Fox"), + seed("cat", "Cat"), + seed("wolf", "Wolf"), + seed("frog", "Frog"), + seed("turtle", "Turtle"), + seed("parrot", "Parrot"), + seed("axolotl", "Axolotl"), + seed("ocelot", "Ocelot")); + + registerLevel(cards, 3, Rarity.COMMON, + seed("donkey", "Donkey"), + seed("mule", "Mule"), + seed("horse", "Horse"), + seed("llama", "Llama"), + seed("camel", "Camel"), + seed("panda", "Panda"), + seed("polar_bear", "Polar Bear"), + seed("dolphin", "Dolphin"), + seed("squid", "Squid"), + seed("glow_squid", "Glow Squid"), + seed("sniffer", "Sniffer")); + + registerLevel(cards, 4, Rarity.UNCOMMON, + seed("spider", "Spider"), + seed("cave_spider", "Cave Spider"), + seed("zombie", "Zombie"), + seed("husk", "Husk"), + seed("drowned", "Drowned"), + seed("skeleton", "Skeleton"), + seed("stray", "Stray"), + seed("bogged", "Bogged"), + seed("slime", "Slime"), + seed("magma_cube", "Magma Cube"), + seed("creeper", "Creeper")); + + registerLevel(cards, 5, Rarity.UNCOMMON, + seed("enderman", "Enderman"), + seed("witch", "Witch"), + seed("phantom", "Phantom"), + seed("silverfish", "Silverfish"), + seed("endermite", "Endermite"), + seed("blaze", "Blaze"), + seed("ghast", "Ghast"), + seed("piglin", "Piglin"), + seed("hoglin", "Hoglin"), + seed("zoglin", "Zoglin"), + seed("wither_skeleton", "Wither Skeleton")); + + registerLevel(cards, 6, Rarity.RARE, + seed("guardian", "Guardian"), + seed("shulker", "Shulker"), + seed("breeze", "Breeze"), + seed("pillager", "Pillager"), + seed("vindicator", "Vindicator"), + seed("evoker", "Evoker"), + seed("ravager", "Ravager"), + seed("zombified_piglin", "Zombified Piglin"), + seed("piglin_brute", "Piglin Brute"), + seed("zombie_villager", "Zombie Villager"), + seed("villager", "Villager")); + + registerLevel(cards, 7, Rarity.RARE, + seed("wandering_trader", "Wandering Trader"), + seed("trader_llama", "Trader Llama"), + seed("iron_golem", "Iron Golem"), + seed("snow_golem", "Snow Golem"), + seed("vex", "Vex"), + seed("allay", "Allay"), + seed("armadillo", "Armadillo"), + seed("skeleton_horse", "Skeleton Horse"), + seed("zombie_horse", "Zombie Horse"), + seed("elder_guardian", "Elder Guardian"), + seed("strider", "Strider")); + + registerLevel(cards, 8, Rarity.LEGENDARY, + seed("wither", "Wither"), + seed("ender_dragon", "Ender Dragon"), + seed("beacon", "Beacon"), + seed("conduit", "Conduit"), + seed("nether_star", "Nether Star"), + seed("dragon_egg", "Dragon Egg"), + seed("elytra", "Elytra"), + seed("totem_of_undying", "Totem of Undying"), + seed("ancient_city", "Ancient City"), + seed("trial_spawner", "Trial Spawner"), + seed("vault", "Vault")); + + registerLevel(cards, 9, Rarity.LEGENDARY, + seed("stronghold", "Stronghold"), + seed("ocean_monument", "Ocean Monument"), + seed("woodland_mansion", "Woodland Mansion"), + seed("nether_fortress", "Nether Fortress"), + seed("bastion_remnant", "Bastion Remnant"), + seed("end_city", "End City"), + seed("netherite", "Netherite"), + seed("poppy", "Poppy"), + seed("enchanted_golden_apple", "Enchanted Golden Apple"), + seed("mace", "Mace"), + seed("recovery_compass", "Recovery Compass")); + + registerLevel(cards, 10, Rarity.LEGENDARY, + seed("jeb", "Jeb"), + seed("dinnerbone", "Dinnerbone"), + seed("cpw", "cpw"), + seed("direwolf20", "DireWolf20"), + seed("xisumavoid", "XisumaVoid"), + seed("docm77", "Docm77"), + seed("ethoslab", "Etho"), + seed("mumbojumbo", "Mumbo Jumbo"), + seed("vintagebeef", "VintageBeef"), + seed("sphax", "Sphax"), + seed("c418", "C418")); + + return List.copyOf(cards); + } + + private static void registerLevel(List cards, int level, Rarity rarity, CardSeed... seeds) { + int[][] levelStats = TT_LEVEL_STATS[level - 1]; + if (seeds.length != levelStats.length) { + throw new IllegalStateException("Expected " + levelStats.length + " cards for level " + level + " but got " + seeds.length); + } + + for (int i = 0; i < seeds.length; i++) { + CardSeed seed = seeds[i]; + cards.add(define(level, seed.path(), seed.displayName(), levelStats[i], rarity)); + } + } + + private static CardDefinition define(int level, String path, String displayName, int[] pattern, Rarity rarity) { return new CardDefinition( ResourceLocation.fromNamespaceAndPath(MineTriad.MOD_ID, path), Component.literal(displayName), - top, - right, - bottom, - left, + level, + pattern[0], + pattern[1], + pattern[2], + pattern[3], rarity); } + + private static CardSeed seed(String path, String displayName) { + return new CardSeed(path, displayName); + } + + private record CardSeed(String path, String displayName) { + } } diff --git a/src/main/java/com/trunksbomb/minetriad/client/render/FirstPersonCardHandRenderer.java b/src/main/java/com/trunksbomb/minetriad/client/render/FirstPersonCardHandRenderer.java new file mode 100644 index 0000000..6670d00 --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/client/render/FirstPersonCardHandRenderer.java @@ -0,0 +1,184 @@ +package com.trunksbomb.minetriad.client.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.AbstractClientPlayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; + +public final class FirstPersonCardHandRenderer { + private FirstPersonCardHandRenderer() { + } + + public static void render( + InteractionHand hand, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + float pitch, + float swingProgress, + float equipProgress, + ItemStack stack) { + Minecraft minecraft = Minecraft.getInstance(); + AbstractClientPlayer player = minecraft.player; + if (player == null) { + return; + } + + HumanoidArm arm = hand == InteractionHand.MAIN_HAND ? player.getMainArm() : player.getMainArm().getOpposite(); + boolean useTwoHanded = hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty(); + + poseStack.pushPose(); + if (useTwoHanded) { + renderTwoHandedCard(minecraft, player, poseStack, buffer, packedLight, pitch, equipProgress, swingProgress, stack); + } else { + renderOneHandedCard(minecraft, player, poseStack, buffer, packedLight, equipProgress, arm, swingProgress, stack); + } + poseStack.popPose(); + } + + private static void renderTwoHandedCard( + Minecraft minecraft, + AbstractClientPlayer player, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + float pitch, + float equipProgress, + float swingProgress, + ItemStack stack) { + float rootSwing = Mth.sqrt(swingProgress); + float swingY = -0.2F * Mth.sin(swingProgress * (float) Math.PI); + float swingZ = -0.4F * Mth.sin(rootSwing * (float) Math.PI); + poseStack.translate(0.0F, -swingY / 2.0F, swingZ); + + float tilt = calculateCardTilt(pitch); + poseStack.translate(0.0F, 0.04F + equipProgress * -1.2F + tilt * -0.5F, -0.72F); + poseStack.mulPose(Axis.XP.rotationDegrees(tilt * -85.0F)); + + if (!player.isInvisible()) { + poseStack.pushPose(); + poseStack.mulPose(Axis.YP.rotationDegrees(90.0F)); + renderCardHand(minecraft, player, poseStack, buffer, packedLight, HumanoidArm.RIGHT); + renderCardHand(minecraft, player, poseStack, buffer, packedLight, HumanoidArm.LEFT); + poseStack.popPose(); + } + + float swingTilt = Mth.sin(rootSwing * (float) Math.PI); + poseStack.mulPose(Axis.XP.rotationDegrees(swingTilt * 20.0F)); + poseStack.scale(2.0F, 2.0F, 2.0F); + renderCenteredCard(poseStack, buffer, stack); + } + + private static void renderOneHandedCard( + Minecraft minecraft, + AbstractClientPlayer player, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + float equipProgress, + HumanoidArm arm, + float swingProgress, + ItemStack stack) { + float armSign = arm == HumanoidArm.RIGHT ? 1.0F : -1.0F; + poseStack.translate(armSign * 0.51F, -0.08F + equipProgress * -1.2F, -0.75F); + + float rootSwing = Mth.sqrt(swingProgress); + float swingSin = Mth.sin(rootSwing * (float) Math.PI); + float swingX = -0.5F * swingSin; + float swingY = 0.4F * Mth.sin(rootSwing * (float) (Math.PI * 2)); + float swingZ = -0.3F * Mth.sin(swingProgress * (float) Math.PI); + poseStack.translate(armSign * swingX, swingY - 0.3F * swingSin, swingZ); + poseStack.mulPose(Axis.XP.rotationDegrees(swingSin * -45.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(armSign * swingSin * -30.0F)); + + if (!player.isInvisible()) { + poseStack.pushPose(); + poseStack.mulPose(Axis.ZP.rotationDegrees(armSign * 10.0F)); + renderPlayerArm(minecraft, player, poseStack, buffer, packedLight, equipProgress, swingProgress, arm); + poseStack.popPose(); + } + + renderCenteredCard(poseStack, buffer, stack); + } + + private static void renderCenteredCard(PoseStack poseStack, MultiBufferSource buffer, ItemStack stack) { + poseStack.translate(0.0F, 0.0F, -0.18F); + poseStack.scale(0.513F, 0.513F, 0.513F); + TriadCardItemRenderer.renderCard(stack, poseStack, buffer, TriadCardItemRenderer.neutralPalette()); + } + + private static void renderCardHand( + Minecraft minecraft, + AbstractClientPlayer player, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + HumanoidArm arm) { + PlayerRenderer renderer = getPlayerRenderer(minecraft, player); + poseStack.pushPose(); + float armSign = arm == HumanoidArm.RIGHT ? 1.0F : -1.0F; + poseStack.mulPose(Axis.YP.rotationDegrees(92.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(45.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(armSign * -41.0F)); + poseStack.translate(armSign * 0.3F, -1.1F, 0.45F); + if (arm == HumanoidArm.RIGHT) { + renderer.renderRightHand(poseStack, buffer, packedLight, player); + } else { + renderer.renderLeftHand(poseStack, buffer, packedLight, player); + } + poseStack.popPose(); + } + + private static void renderPlayerArm( + Minecraft minecraft, + AbstractClientPlayer player, + PoseStack poseStack, + MultiBufferSource buffer, + int packedLight, + float equipProgress, + float swingProgress, + HumanoidArm arm) { + PlayerRenderer renderer = getPlayerRenderer(minecraft, player); + float armSign = arm == HumanoidArm.RIGHT ? 1.0F : -1.0F; + float swingRoot = Mth.sqrt(swingProgress); + float swingY = -0.3F * Mth.sin(swingRoot * (float) Math.PI); + float swingZ = 0.4F * Mth.sin(swingRoot * (float) (Math.PI * 2)); + float swingX = -0.4F * Mth.sin(swingProgress * (float) Math.PI); + poseStack.translate(armSign * (swingX + 0.64F), swingY + -0.6F + equipProgress * -0.6F, swingZ + -0.72F); + poseStack.mulPose(Axis.YP.rotationDegrees(armSign * 45.0F)); + float armSwing = Mth.sin(swingProgress * swingProgress * (float) Math.PI); + float armSwingRoot = Mth.sin(swingRoot * (float) Math.PI); + poseStack.mulPose(Axis.YP.rotationDegrees(armSign * armSwingRoot * 70.0F)); + poseStack.mulPose(Axis.ZP.rotationDegrees(armSign * armSwing * -20.0F)); + poseStack.translate(armSign * -1.0F, 3.6F, 3.5F); + poseStack.mulPose(Axis.ZP.rotationDegrees(armSign * 120.0F)); + poseStack.mulPose(Axis.XP.rotationDegrees(200.0F)); + poseStack.mulPose(Axis.YP.rotationDegrees(armSign * -135.0F)); + poseStack.translate(armSign * 5.6F, 0.0F, 0.0F); + if (arm == HumanoidArm.RIGHT) { + renderer.renderRightHand(poseStack, buffer, packedLight, player); + } else { + renderer.renderLeftHand(poseStack, buffer, packedLight, player); + } + } + + private static PlayerRenderer getPlayerRenderer(Minecraft minecraft, AbstractClientPlayer player) { + EntityRenderDispatcher dispatcher = minecraft.getEntityRenderDispatcher(); + return (PlayerRenderer) dispatcher.getRenderer(player); + } + + private static float calculateCardTilt(float pitch) { + float tilt = 1.0F - pitch / 67.5F + 0.1F; + tilt = Mth.clamp(tilt, 0.0F, 1.0F); + return -Mth.cos(tilt * (float) Math.PI) * 0.5F + 0.5F; + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/client/render/TriadCardItemRenderer.java b/src/main/java/com/trunksbomb/minetriad/client/render/TriadCardItemRenderer.java index 6323e02..5b9fb89 100644 --- a/src/main/java/com/trunksbomb/minetriad/client/render/TriadCardItemRenderer.java +++ b/src/main/java/com/trunksbomb/minetriad/client/render/TriadCardItemRenderer.java @@ -1,6 +1,8 @@ package com.trunksbomb.minetriad.client.render; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Axis; import com.trunksbomb.minetriad.card.CardDefinition; import com.trunksbomb.minetriad.card.CardRegistry; import com.trunksbomb.minetriad.card.CardStackData; @@ -15,18 +17,25 @@ import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.util.FormattedCharSequence; +import org.joml.Matrix4f; public final class TriadCardItemRenderer extends BlockEntityWithoutLevelRenderer { - private static final float TOP_VALUE_Y = 0.28F; - private static final float BOTTOM_VALUE_Y = -0.22F; - private static final float SIDE_VALUE_X = 0.27F; - private static final float SIDE_VALUE_Y = 0.03F; + private static final float CARD_MIN = -0.42F; + private static final float CARD_MAX = 0.42F; + private static final float FRONT_Z = 0.024F; + private static final float BACK_Z = -0.024F; + private static final float BORDER_Z = 0.026F; + private static final float VALUE_Z = 0.030F; + private static final float INNER_MIN = -0.37F; + private static final float INNER_MAX = 0.37F; private static TriadCardItemRenderer INSTANCE; private static final CardPalette NEUTRAL_PALETTE = new CardPalette( - new float[] {0.88F, 0.84F, 0.72F}, + new float[] {0.97F, 0.93F, 0.80F}, new float[] {0.20F, 0.22F, 0.28F}); private TriadCardItemRenderer(BlockEntityRenderDispatcher blockEntityRenderDispatcher, EntityModelSet entityModelSet) { @@ -43,55 +52,100 @@ public final class TriadCardItemRenderer extends BlockEntityWithoutLevelRenderer @Override public void renderByItem(ItemStack stack, ItemDisplayContext displayContext, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) { - renderCard(stack, poseStack, buffer, NEUTRAL_PALETTE); + renderCard(stack, poseStack, buffer, NEUTRAL_PALETTE, CardLayout.forDisplayContext(displayContext)); } public static void renderCard(ItemStack stack, PoseStack poseStack, MultiBufferSource buffer, CardPalette palette) { + renderCard(stack, poseStack, buffer, palette, CardLayout.BOARD); + } + + public static CardPalette neutralPalette() { + return NEUTRAL_PALETTE; + } + + public static void renderCardForDisplayContext( + ItemStack stack, + PoseStack poseStack, + MultiBufferSource buffer, + CardPalette palette, + ItemDisplayContext displayContext) { + renderCard(stack, poseStack, buffer, palette, CardLayout.forDisplayContext(displayContext)); + } + + private static void renderCard(ItemStack stack, PoseStack poseStack, MultiBufferSource buffer, CardPalette palette, CardLayout layout) { CardStackData cardData = stack.get(TriadDataComponents.CARD_DATA); CardDefinition card = cardData == null || cardData.equals(CardStackData.EMPTY) ? null : CardRegistry.get(cardData.cardId()); poseStack.pushPose(); - LevelRenderer.addChainedFilledBoxVertices( - poseStack, - buffer.getBuffer(RenderType.debugFilledBox()), - -0.42F, - -0.42F, - -0.015F, - 0.42F, - 0.42F, - -0.009F, - palette.border()[0], - palette.border()[1], - palette.border()[2], - 1.0F); - LevelRenderer.addChainedFilledBoxVertices( - poseStack, - buffer.getBuffer(RenderType.debugFilledBox()), - -0.32F, - -0.32F, - -0.026F, - 0.32F, - 0.32F, - -0.016F, - palette.face()[0], - palette.face()[1], - palette.face()[2], - 1.0F); - + drawBack(poseStack, buffer); + if (card != null) { + drawFrontArt(poseStack, buffer, artTexture(card)); + } else { + drawFallbackFace(poseStack, buffer, palette); + } + drawBorderOverlay(poseStack, buffer, palette); if (card != null) { Font font = Minecraft.getInstance().font; - drawValue(poseStack, buffer, font, Integer.toString(card.top()), 0.0F, TOP_VALUE_Y); - drawValue(poseStack, buffer, font, Integer.toString(card.bottom()), 0.0F, BOTTOM_VALUE_Y); - drawValue(poseStack, buffer, font, Integer.toString(card.left()), -SIDE_VALUE_X, SIDE_VALUE_Y); - drawValue(poseStack, buffer, font, Integer.toString(card.right()), SIDE_VALUE_X, SIDE_VALUE_Y); + drawValue(poseStack, buffer, font, displayRank(card.top()), 0.0F, layout.topY(), layout.valueScale()); + drawValue(poseStack, buffer, font, displayRank(card.bottom()), 0.0F, layout.bottomY(), layout.valueScale()); + drawValue(poseStack, buffer, font, displayRank(card.left()), -layout.sideX(), layout.sideY(), layout.valueScale()); + drawValue(poseStack, buffer, font, displayRank(card.right()), layout.sideX(), layout.sideY(), layout.valueScale()); } poseStack.popPose(); } - private static void drawValue(PoseStack poseStack, MultiBufferSource buffer, Font font, String value, float x, float y) { + private static String displayRank(int rank) { + return rank == 10 ? "A" : Integer.toString(rank); + } + + private static void drawFallbackFace(PoseStack poseStack, MultiBufferSource buffer, CardPalette palette) { + LevelRenderer.addChainedFilledBoxVertices( + poseStack, + buffer.getBuffer(RenderType.debugFilledBox()), + CARD_MIN, + CARD_MIN, + FRONT_Z, + CARD_MAX, + CARD_MAX, + FRONT_Z + 0.001F, + palette.face()[0], + palette.face()[1], + palette.face()[2], + 1.0F); + } + + private static void drawBack(PoseStack poseStack, MultiBufferSource buffer) { + drawTexturedQuad(poseStack, buffer, backTexture(), BACK_Z, true); + } + + private static void drawBorderOverlay(PoseStack poseStack, MultiBufferSource buffer, CardPalette palette) { + float[] border = palette.border(); + drawBorderStrip(poseStack, buffer, CARD_MIN, INNER_MAX, CARD_MAX, CARD_MAX, border); + drawBorderStrip(poseStack, buffer, CARD_MIN, CARD_MIN, CARD_MAX, INNER_MIN, border); + drawBorderStrip(poseStack, buffer, CARD_MIN, INNER_MIN, INNER_MIN, INNER_MAX, border); + drawBorderStrip(poseStack, buffer, INNER_MAX, INNER_MIN, CARD_MAX, INNER_MAX, border); + } + + private static void drawBorderStrip(PoseStack poseStack, MultiBufferSource buffer, float minX, float minY, float maxX, float maxY, float[] color) { + LevelRenderer.addChainedFilledBoxVertices( + poseStack, + buffer.getBuffer(RenderType.debugFilledBox()), + minX, + minY, + BORDER_Z, + maxX, + maxY, + BORDER_Z + 0.001F, + color[0], + color[1], + color[2], + 1.0F); + } + + private static void drawValue(PoseStack poseStack, MultiBufferSource buffer, Font font, String value, float x, float y, float scale) { poseStack.pushPose(); - poseStack.translate(x, y, 0.03F); - poseStack.scale(0.03F, -0.03F, 0.03F); + poseStack.translate(x, y, VALUE_Z); + poseStack.scale(scale, -scale, scale); float width = font.width(value); FormattedCharSequence sequence = FormattedCharSequence.forward(value, net.minecraft.network.chat.Style.EMPTY); font.drawInBatch8xOutline( @@ -106,6 +160,57 @@ public final class TriadCardItemRenderer extends BlockEntityWithoutLevelRenderer poseStack.popPose(); } + private static void drawFrontArt(PoseStack poseStack, MultiBufferSource buffer, ResourceLocation textureLocation) { + drawTexturedQuad(poseStack, buffer, textureLocation, FRONT_Z, false); + } + + private static void drawTexturedQuad(PoseStack poseStack, MultiBufferSource buffer, ResourceLocation textureLocation, float z, boolean reverseWinding) { + VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityCutoutNoCull(textureLocation)); + Matrix4f matrix = poseStack.last().pose(); + if (reverseWinding) { + addArtVertex(vertexConsumer, matrix, CARD_MIN, CARD_MIN, z, 0.0F, 1.0F, -1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MAX, CARD_MIN, z, 1.0F, 1.0F, -1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MAX, CARD_MAX, z, 1.0F, 0.0F, -1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MIN, CARD_MAX, z, 0.0F, 0.0F, -1.0F); + } else { + addArtVertex(vertexConsumer, matrix, CARD_MIN, CARD_MAX, z, 0.0F, 0.0F, 1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MAX, CARD_MAX, z, 1.0F, 0.0F, 1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MAX, CARD_MIN, z, 1.0F, 1.0F, 1.0F); + addArtVertex(vertexConsumer, matrix, CARD_MIN, CARD_MIN, z, 0.0F, 1.0F, 1.0F); + } + } + + private static void addArtVertex(VertexConsumer vertexConsumer, Matrix4f matrix, float x, float y, float z, float u, float v, float normalZ) { + vertexConsumer.addVertex(matrix, x, y, z) + .setColor(255, 255, 255, 255) + .setUv(u, v) + .setOverlay(OverlayTexture.NO_OVERLAY) + .setLight(LightTexture.FULL_BRIGHT) + .setNormal(0.0F, 0.0F, normalZ); + } + + private static ResourceLocation artTexture(CardDefinition card) { + ResourceLocation id = card.id(); + return ResourceLocation.fromNamespaceAndPath(id.getNamespace(), "textures/item/cards/" + id.getPath() + ".png"); + } + + private static ResourceLocation backTexture() { + return ResourceLocation.fromNamespaceAndPath("minetriad", "textures/item/card_back.png"); + } + public record CardPalette(float[] border, float[] face) { } + + private record CardLayout(float topY, float bottomY, float sideX, float sideY, float valueScale) { + private static final CardLayout BOARD = new CardLayout(0.41F, -0.29F, 0.35F, 0.06F, 0.03F); + private static final CardLayout ITEM = new CardLayout(0.26F, -0.17F, 0.21F, 0.03F, 0.03F); + + private static CardLayout forDisplayContext(ItemDisplayContext displayContext) { + return switch (displayContext) { + case GUI, GROUND, FIXED, FIRST_PERSON_LEFT_HAND, FIRST_PERSON_RIGHT_HAND, + THIRD_PERSON_LEFT_HAND, THIRD_PERSON_RIGHT_HAND -> ITEM; + default -> BOARD; + }; + } + } } diff --git a/src/main/java/com/trunksbomb/minetriad/client/screen/CardBinderScreen.java b/src/main/java/com/trunksbomb/minetriad/client/screen/CardBinderScreen.java new file mode 100644 index 0000000..c8749a2 --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/client/screen/CardBinderScreen.java @@ -0,0 +1,122 @@ +package com.trunksbomb.minetriad.client.screen; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.math.Axis; +import com.trunksbomb.minetriad.MineTriad; +import com.trunksbomb.minetriad.client.render.TriadCardItemRenderer; +import com.trunksbomb.minetriad.menu.CardBinderMenu; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; + +public class CardBinderScreen extends AbstractContainerScreen { + private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(MineTriad.MOD_ID, "textures/gui/card_binder.png"); + private static final ResourceLocation SELECTOR_TEXTURE = ResourceLocation.fromNamespaceAndPath(MineTriad.MOD_ID, "textures/gui/card_binder_selector.png"); + + private Button previousPageButton; + private Button nextPageButton; + private Button handsButton; + + public CardBinderScreen(CardBinderMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + imageWidth = 248; + imageHeight = 222; + inventoryLabelY = imageHeight - 92; + } + + @Override + protected void init() { + super.init(); + previousPageButton = addRenderableWidget(Button.builder(Component.literal("<"), button -> pressMenuButton(CardBinderMenu.BUTTON_PREVIOUS_PAGE)) + .bounds(leftPos + 48, topPos + 108, 20, 20) + .build()); + nextPageButton = addRenderableWidget(Button.builder(Component.literal(">"), button -> pressMenuButton(CardBinderMenu.BUTTON_NEXT_PAGE)) + .bounds(leftPos + 72, topPos + 108, 20, 20) + .build()); + handsButton = addRenderableWidget(Button.builder(Component.translatable("gui.minetriad.card_binder.hands"), button -> pressMenuButton(CardBinderMenu.BUTTON_HANDS)) + .bounds(leftPos + 48, topPos + 86, 44, 20) + .build()); + handsButton.active = false; + updateButtons(); + } + + @Override + public void containerTick() { + super.containerTick(); + updateButtons(); + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) { + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + guiGraphics.blit(TEXTURE, leftPos, topPos, 0, 0, imageWidth, imageHeight, imageWidth, imageHeight); + } + + @Override + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + guiGraphics.drawString(font, title, 8, 6, 0x2E2418, false); + guiGraphics.drawString(font, Component.translatable("gui.minetriad.card_binder.level", menu.currentLevel()), 48, 76, 0x2E2418, false); + guiGraphics.drawString(font, Component.translatable("gui.minetriad.card_binder.preview"), 138, 12, 0x2E2418, false); + guiGraphics.drawString(font, playerInventoryTitle, inventoryLabelX, inventoryLabelY, 0x2E2418, false); + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + renderBackground(guiGraphics, mouseX, mouseY, partialTick); + super.render(guiGraphics, mouseX, mouseY, partialTick); + renderSelection(guiGraphics); + renderPreview(guiGraphics, partialTick); + renderTooltip(guiGraphics, mouseX, mouseY); + } + + private void pressMenuButton(int buttonId) { + if (minecraft != null && minecraft.gameMode != null) { + minecraft.gameMode.handleInventoryButtonClick(menu.containerId, buttonId); + updateButtons(); + } + } + + private void updateButtons() { + previousPageButton.active = menu.currentPage() > 0; + nextPageButton.active = menu.currentPage() < CardBinderMenu.PAGE_COUNT - 1; + } + + private void renderSelection(GuiGraphics guiGraphics) { + int selectedSlotIndex = menu.selectedBinderSlot(); + if (selectedSlotIndex < 0 || selectedSlotIndex >= menu.slots.size()) { + return; + } + + Slot slot = menu.slots.get(selectedSlotIndex); + if (!slot.isActive() || !slot.hasItem()) { + return; + } + + guiGraphics.blit(SELECTOR_TEXTURE, leftPos + slot.x, topPos + slot.y, 0, 0, 16, 16, 16, 16); + } + + private void renderPreview(GuiGraphics guiGraphics, float partialTick) { + ItemStack previewStack = menu.previewStack(); + if (previewStack.isEmpty() || minecraft == null || minecraft.level == null) { + return; + } + + float rotation = ((minecraft.level.getGameTime() + partialTick) * 2.4F) % 360.0F; + + guiGraphics.pose().pushPose(); + guiGraphics.pose().translate(leftPos + 184.0F, topPos + 71.0F, 200.0F); + guiGraphics.pose().mulPose(Axis.XP.rotationDegrees(18.0F)); + guiGraphics.pose().mulPose(Axis.YP.rotationDegrees(rotation)); + guiGraphics.pose().scale(84.0F, -84.0F, 84.0F); + TriadCardItemRenderer.renderCard(previewStack, guiGraphics.pose(), minecraft.renderBuffers().bufferSource(), TriadCardItemRenderer.neutralPalette()); + minecraft.renderBuffers().bufferSource().endBatch(); + guiGraphics.pose().popPose(); + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/compat/jei/MineTriadJeiPlugin.java b/src/main/java/com/trunksbomb/minetriad/compat/jei/MineTriadJeiPlugin.java new file mode 100644 index 0000000..03b2ba5 --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/compat/jei/MineTriadJeiPlugin.java @@ -0,0 +1,54 @@ +package com.trunksbomb.minetriad.compat.jei; + +import java.util.List; + +import com.trunksbomb.minetriad.MineTriad; +import com.trunksbomb.minetriad.card.CardRegistry; +import com.trunksbomb.minetriad.card.CardStackData; +import com.trunksbomb.minetriad.item.CardItem; +import com.trunksbomb.minetriad.registry.TriadDataComponents; +import com.trunksbomb.minetriad.registry.TriadItems; + +import mezz.jei.api.IModPlugin; +import mezz.jei.api.JeiPlugin; +import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter; +import mezz.jei.api.registration.IExtraIngredientRegistration; +import mezz.jei.api.registration.ISubtypeRegistration; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + +@JeiPlugin +public final class MineTriadJeiPlugin implements IModPlugin { + private static final ResourceLocation PLUGIN_UID = ResourceLocation.fromNamespaceAndPath(MineTriad.MOD_ID, "jei_plugin"); + private static final ISubtypeInterpreter CARD_SUBTYPE = new ISubtypeInterpreter<>() { + @Override + public Object getSubtypeData(ItemStack stack, mezz.jei.api.ingredients.subtypes.UidContext context) { + CardStackData data = stack.get(TriadDataComponents.CARD_DATA); + return data == null || CardStackData.EMPTY.equals(data) ? null : data.cardId().toString(); + } + + @Override + public String getLegacyStringSubtypeInfo(ItemStack stack, mezz.jei.api.ingredients.subtypes.UidContext context) { + CardStackData data = stack.get(TriadDataComponents.CARD_DATA); + return data == null || CardStackData.EMPTY.equals(data) ? "" : data.cardId().toString(); + } + }; + + @Override + public ResourceLocation getPluginUid() { + return PLUGIN_UID; + } + + @Override + public void registerItemSubtypes(ISubtypeRegistration registration) { + registration.registerSubtypeInterpreter(TriadItems.TRIAD_CARD.get(), CARD_SUBTYPE); + } + + @Override + public void registerExtraIngredients(IExtraIngredientRegistration registration) { + List cardStacks = CardRegistry.all().stream() + .map(card -> CardItem.createCardStack(card.id(), TriadItems.TRIAD_CARD.get())) + .toList(); + registration.addExtraItemStacks(cardStacks); + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/item/CardBinderItem.java b/src/main/java/com/trunksbomb/minetriad/item/CardBinderItem.java new file mode 100644 index 0000000..6e7fe99 --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/item/CardBinderItem.java @@ -0,0 +1,31 @@ +package com.trunksbomb.minetriad.item; + +import com.trunksbomb.minetriad.menu.CardBinderMenu; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +public class CardBinderItem extends Item { + public CardBinderItem(Properties properties) { + super(properties); + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand usedHand) { + ItemStack stack = player.getItemInHand(usedHand); + if (player instanceof ServerPlayer serverPlayer) { + serverPlayer.openMenu( + new SimpleMenuProvider( + (containerId, inventory, menuPlayer) -> new CardBinderMenu(containerId, inventory, usedHand), + stack.getHoverName()), + buffer -> buffer.writeEnum(usedHand)); + } + return InteractionResultHolder.sidedSuccess(stack, level.isClientSide()); + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/item/CardItem.java b/src/main/java/com/trunksbomb/minetriad/item/CardItem.java index 2eb0b8d..bb036c0 100644 --- a/src/main/java/com/trunksbomb/minetriad/item/CardItem.java +++ b/src/main/java/com/trunksbomb/minetriad/item/CardItem.java @@ -30,7 +30,9 @@ public class CardItem extends Item { } CardDefinition card = CardRegistry.get(cardData.cardId()); - return card.name().copy().withStyle(card.rarity().formatting()); + return Component.literal("[MT:" + card.level() + "] ") + .append(card.name().copy()) + .withStyle(card.rarity().formatting()); } @Override @@ -42,6 +44,7 @@ public class CardItem extends Item { } CardDefinition card = CardRegistry.get(cardData.cardId()); + tooltipComponents.add(Component.literal("Level " + card.level()).withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.literal("Top " + card.top() + " Right " + card.right()).withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.literal("Bottom " + card.bottom() + " Left " + card.left()).withStyle(ChatFormatting.GRAY)); tooltipComponents.add(Component.literal(card.rarity().name()).withStyle(card.rarity().formatting())); diff --git a/src/main/java/com/trunksbomb/minetriad/menu/CardBinderMenu.java b/src/main/java/com/trunksbomb/minetriad/menu/CardBinderMenu.java new file mode 100644 index 0000000..28dc99a --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/menu/CardBinderMenu.java @@ -0,0 +1,312 @@ +package com.trunksbomb.minetriad.menu; + +import java.util.List; + +import com.trunksbomb.minetriad.card.CardDefinition; +import com.trunksbomb.minetriad.card.CardRegistry; +import com.trunksbomb.minetriad.card.CardStackData; +import com.trunksbomb.minetriad.registry.TriadDataComponents; +import com.trunksbomb.minetriad.registry.TriadItems; +import com.trunksbomb.minetriad.registry.TriadMenus; + +import net.minecraft.core.NonNullList; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.DataSlot; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.ItemContainerContents; + +public class CardBinderMenu extends AbstractContainerMenu { + public static final int CARDS_PER_LEVEL = 11; + public static final int PAGE_COUNT = 10; + public static final int BINDER_SLOT_COUNT = PAGE_COUNT * CARDS_PER_LEVEL; + public static final int BUTTON_PREVIOUS_PAGE = 0; + public static final int BUTTON_NEXT_PAGE = 1; + public static final int BUTTON_HANDS = 2; + public static final int NO_SELECTION = -1; + + private static final int SLOT_X_START = 8; + private static final int SLOT_Y_START = 22; + private static final int SLOT_SPACING = 18; + private final InteractionHand hand; + private final SimpleContainer binderContents; + private final int lockedHotbarSlot; + private int currentPage; + private int selectedBinderSlot = NO_SELECTION; + + public CardBinderMenu(int containerId, Inventory inventory, FriendlyByteBuf extraData) { + this(containerId, inventory, extraData.readEnum(InteractionHand.class)); + } + + public CardBinderMenu(int containerId, Inventory inventory, InteractionHand hand) { + super(TriadMenus.CARD_BINDER.get(), containerId); + this.hand = hand; + this.lockedHotbarSlot = hand == InteractionHand.MAIN_HAND ? inventory.selected : -1; + this.binderContents = new SimpleContainer(BINDER_SLOT_COUNT) { + @Override + public void setChanged() { + super.setChanged(); + saveBinderContents(inventory.player); + } + }; + + loadBinderContents(inventory.player); + addDataSlot(new DataSlot() { + @Override + public int get() { + return currentPage; + } + + @Override + public void set(int value) { + currentPage = Math.clamp(value, 0, PAGE_COUNT - 1); + } + }); + addDataSlot(new DataSlot() { + @Override + public int get() { + return selectedBinderSlot; + } + + @Override + public void set(int value) { + selectedBinderSlot = value >= 0 && value < BINDER_SLOT_COUNT ? value : NO_SELECTION; + } + }); + + addBinderSlots(); + addPlayerInventory(inventory); + } + + public int currentPage() { + return currentPage; + } + + public int currentLevel() { + return currentPage + 1; + } + + public int selectedBinderSlot() { + return selectedBinderSlot; + } + + public ItemStack previewStack() { + if (selectedBinderSlot < 0 || selectedBinderSlot >= BINDER_SLOT_COUNT) { + return ItemStack.EMPTY; + } + ItemStack stack = binderContents.getItem(selectedBinderSlot); + return stack.isEmpty() ? ItemStack.EMPTY : stack; + } + + @Override + public boolean stillValid(Player player) { + return player.getItemInHand(hand).is(TriadItems.CARD_BINDER.get()); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + Slot slot = slots.get(index); + if (!slot.hasItem()) { + return ItemStack.EMPTY; + } + + ItemStack source = slot.getItem(); + ItemStack copy = source.copy(); + + if (index < BINDER_SLOT_COUNT) { + if (!moveItemStackTo(source, BINDER_SLOT_COUNT, slots.size(), false)) { + return ItemStack.EMPTY; + } + } else { + int binderStart = binderRangeStartFor(source); + if (binderStart < 0 || !moveItemStackTo(source, binderStart, binderStart + CARDS_PER_LEVEL, false)) { + return ItemStack.EMPTY; + } + } + + if (source.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + normalizeSelection(); + + return copy; + } + + @Override + public boolean clickMenuButton(Player player, int id) { + if (id == BUTTON_PREVIOUS_PAGE && currentPage > 0) { + currentPage--; + broadcastChanges(); + return true; + } + if (id == BUTTON_NEXT_PAGE && currentPage < PAGE_COUNT - 1) { + currentPage++; + broadcastChanges(); + return true; + } + if (id == BUTTON_HANDS) { + return true; + } + return false; + } + + @Override + public void removed(Player player) { + clearSelection(); + saveBinderContents(player); + super.removed(player); + } + + @Override + public void clicked(int slotId, int button, ClickType clickType, Player player) { + if (slotId >= 0 && slotId < BINDER_SLOT_COUNT && clickType == ClickType.PICKUP && getCarried().isEmpty()) { + Slot slot = slots.get(slotId); + if (slot.hasItem()) { + if (selectedBinderSlot == slotId) { + super.clicked(slotId, 1, ClickType.PICKUP, player); + normalizeSelection(); + } else { + selectedBinderSlot = slotId; + broadcastChanges(); + } + saveBinderContents(player); + return; + } + } + + super.clicked(slotId, button, clickType, player); + normalizeSelection(); + saveBinderContents(player); + } + + private void addBinderSlots() { + for (int page = 0; page < PAGE_COUNT; page++) { + for (int slotOnPage = 0; slotOnPage < CARDS_PER_LEVEL; slotOnPage++) { + int slotIndex = page * CARDS_PER_LEVEL + slotOnPage; + int row = slotOnPage / 2; + int column = slotOnPage % 2; + int x = SLOT_X_START + column * SLOT_SPACING; + int y = SLOT_Y_START + row * SLOT_SPACING; + addSlot(new BinderSlot(binderContents, slotIndex, x, y, page, page + 1)); + } + } + } + + private void addPlayerInventory(Inventory inventory) { + for (int row = 0; row < 3; row++) { + for (int column = 0; column < 9; column++) { + addSlot(new Slot(inventory, column + row * 9 + 9, 8 + column * 18, 140 + row * 18)); + } + } + + for (int slot = 0; slot < 9; slot++) { + int inventoryIndex = slot; + int x = 8 + slot * 18; + int y = 198; + if (slot == lockedHotbarSlot) { + addSlot(new LockedSlot(inventory, inventoryIndex, x, y)); + } else { + addSlot(new Slot(inventory, inventoryIndex, x, y)); + } + } + } + + private void loadBinderContents(Player player) { + ItemStack binderStack = player.getItemInHand(hand); + ItemContainerContents contents = binderStack.getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY); + NonNullList items = NonNullList.withSize(BINDER_SLOT_COUNT, ItemStack.EMPTY); + contents.copyInto(items); + for (int i = 0; i < BINDER_SLOT_COUNT; i++) { + binderContents.setItem(i, i < items.size() ? items.get(i) : ItemStack.EMPTY); + } + } + + private void saveBinderContents(Player player) { + ItemStack binderStack = player.getItemInHand(hand); + if (!binderStack.is(TriadItems.CARD_BINDER.get())) { + return; + } + + NonNullList items = NonNullList.withSize(BINDER_SLOT_COUNT, ItemStack.EMPTY); + for (int i = 0; i < BINDER_SLOT_COUNT; i++) { + items.set(i, binderContents.getItem(i)); + } + binderStack.set(DataComponents.CONTAINER, ItemContainerContents.fromItems(items)); + } + + private void normalizeSelection() { + if (selectedBinderSlot < 0 || selectedBinderSlot >= BINDER_SLOT_COUNT || binderContents.getItem(selectedBinderSlot).isEmpty()) { + clearSelection(); + } + } + + private void clearSelection() { + selectedBinderSlot = NO_SELECTION; + } + + private static int binderRangeStartFor(ItemStack stack) { + CardStackData data = stack.get(TriadDataComponents.CARD_DATA); + if (!stack.is(TriadItems.TRIAD_CARD.get()) || data == null || data.equals(CardStackData.EMPTY)) { + return -1; + } + + CardDefinition definition = CardRegistry.get(data.cardId()); + return (definition.level() - 1) * CARDS_PER_LEVEL; + } + + private final class BinderSlot extends Slot { + private final int pageIndex; + private final int expectedLevel; + + private BinderSlot(SimpleContainer container, int slot, int x, int y, int pageIndex, int expectedLevel) { + super(container, slot, x, y); + this.pageIndex = pageIndex; + this.expectedLevel = expectedLevel; + } + + @Override + public boolean mayPlace(ItemStack stack) { + CardStackData data = stack.get(TriadDataComponents.CARD_DATA); + if (!stack.is(TriadItems.TRIAD_CARD.get()) || data == null || data.equals(CardStackData.EMPTY)) { + return false; + } + return CardRegistry.get(data.cardId()).level() == expectedLevel; + } + + @Override + public int getMaxStackSize() { + return 1; + } + + @Override + public boolean isActive() { + return pageIndex == currentPage; + } + } + + private static final class LockedSlot extends Slot { + private LockedSlot(Inventory inventory, int slot, int x, int y) { + super(inventory, slot, x, y); + } + + @Override + public boolean mayPickup(Player player) { + return false; + } + + @Override + public boolean mayPlace(ItemStack stack) { + return false; + } + } +} diff --git a/src/main/java/com/trunksbomb/minetriad/registry/TriadItems.java b/src/main/java/com/trunksbomb/minetriad/registry/TriadItems.java index 2d70ba7..09ebef9 100644 --- a/src/main/java/com/trunksbomb/minetriad/registry/TriadItems.java +++ b/src/main/java/com/trunksbomb/minetriad/registry/TriadItems.java @@ -2,10 +2,13 @@ package com.trunksbomb.minetriad.registry; import com.trunksbomb.minetriad.MineTriad; import com.trunksbomb.minetriad.item.CardItem; +import com.trunksbomb.minetriad.item.CardBinderItem; import com.trunksbomb.minetriad.item.DeckBoxItem; +import net.minecraft.core.component.DataComponents; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.component.ItemContainerContents; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; @@ -17,7 +20,9 @@ public final class TriadItems { public static final DeferredItem TRIAD_CARD = ITEMS.register("triad_card", () -> new CardItem(DEFAULT_PROPERTIES.stacksTo(1))); public static final DeferredItem DECK_BOX = ITEMS.register("deck_box", () -> new DeckBoxItem(DEFAULT_PROPERTIES.stacksTo(1))); - public static final DeferredItem CARD_BINDER = ITEMS.registerSimpleItem("card_binder", DEFAULT_PROPERTIES.stacksTo(1)); + public static final DeferredItem CARD_BINDER = ITEMS.register( + "card_binder", + () -> new CardBinderItem(DEFAULT_PROPERTIES.stacksTo(1).component(DataComponents.CONTAINER, ItemContainerContents.EMPTY))); public static final DeferredItem DUEL_TABLE = ITEMS.registerSimpleBlockItem("duel_table", TriadBlocks.DUEL_TABLE); private TriadItems() { diff --git a/src/main/java/com/trunksbomb/minetriad/registry/TriadMenus.java b/src/main/java/com/trunksbomb/minetriad/registry/TriadMenus.java new file mode 100644 index 0000000..ada6bf1 --- /dev/null +++ b/src/main/java/com/trunksbomb/minetriad/registry/TriadMenus.java @@ -0,0 +1,26 @@ +package com.trunksbomb.minetriad.registry; + +import com.trunksbomb.minetriad.MineTriad; +import com.trunksbomb.minetriad.menu.CardBinderMenu; + +import net.minecraft.core.registries.Registries; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public final class TriadMenus { + private static final DeferredRegister> MENUS = DeferredRegister.create(Registries.MENU, MineTriad.MOD_ID); + + public static final DeferredHolder, MenuType> CARD_BINDER = MENUS.register( + "card_binder", + () -> IMenuTypeExtension.create(CardBinderMenu::new)); + + private TriadMenus() { + } + + public static void register(IEventBus eventBus) { + MENUS.register(eventBus); + } +} diff --git a/src/main/resources/assets/minetriad/lang/en_us.json b/src/main/resources/assets/minetriad/lang/en_us.json index acf4216..c809301 100644 --- a/src/main/resources/assets/minetriad/lang/en_us.json +++ b/src/main/resources/assets/minetriad/lang/en_us.json @@ -4,5 +4,8 @@ "block.minetriad.duel_table": "Duel Table", "item.minetriad.triad_card": "Triad Card", "item.minetriad.deck_box": "Deck Box", - "item.minetriad.card_binder": "Card Binder" + "item.minetriad.card_binder": "Card Binder", + "gui.minetriad.card_binder.level": "Level %s", + "gui.minetriad.card_binder.preview": "Card Preview", + "gui.minetriad.card_binder.hands": "Hands" } diff --git a/src/main/resources/assets/minetriad/models/item/triad_card.json b/src/main/resources/assets/minetriad/models/item/triad_card.json index 880ce23..df8f185 100644 --- a/src/main/resources/assets/minetriad/models/item/triad_card.json +++ b/src/main/resources/assets/minetriad/models/item/triad_card.json @@ -1,6 +1,40 @@ { - "parent": "minecraft:item/generated", - "textures": { - "layer0": "minetriad:item/triad_card" + "parent": "minecraft:builtin/entity", + "display": { + "thirdperson_righthand": { + "rotation": [0, -90, 35], + "translation": [0, 2.5, 0], + "scale": [0.75, 0.75, 0.75] + }, + "thirdperson_lefthand": { + "rotation": [0, 90, -35], + "translation": [0, 2.5, 0], + "scale": [0.75, 0.75, 0.75] + }, + "firstperson_righthand": { + "rotation": [0, 0, 0], + "translation": [0, 0, 0], + "scale": [1, 1, 1] + }, + "firstperson_lefthand": { + "rotation": [0, 0, 0], + "translation": [0, 0, 0], + "scale": [1, 1, 1] + }, + "gui": { + "rotation": [0, 0, 0], + "translation": [8, 8, 0], + "scale": [1, 1, 1] + }, + "ground": { + "rotation": [0, 0, 0], + "translation": [0, 2, 0], + "scale": [0.5, 0.5, 0.5] + }, + "fixed": { + "rotation": [0, 180, 0], + "translation": [0, 0, 0], + "scale": [1, 1, 1] + } } } diff --git a/src/main/resources/assets/minetriad/textures/gui/card_binder.png b/src/main/resources/assets/minetriad/textures/gui/card_binder.png new file mode 100644 index 0000000..925e588 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/gui/card_binder.png differ diff --git a/src/main/resources/assets/minetriad/textures/gui/card_binder_selector.png b/src/main/resources/assets/minetriad/textures/gui/card_binder_selector.png new file mode 100644 index 0000000..23867b3 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/gui/card_binder_selector.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/card_back.png b/src/main/resources/assets/minetriad/textures/item/card_back.png new file mode 100644 index 0000000..da5bdfc Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/card_back.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/allay.png b/src/main/resources/assets/minetriad/textures/item/cards/allay.png new file mode 100644 index 0000000..09c9879 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/allay.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ancient_city.png b/src/main/resources/assets/minetriad/textures/item/cards/ancient_city.png new file mode 100644 index 0000000..c665283 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ancient_city.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/armadillo.png b/src/main/resources/assets/minetriad/textures/item/cards/armadillo.png new file mode 100644 index 0000000..b13d85a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/armadillo.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/axolotl.png b/src/main/resources/assets/minetriad/textures/item/cards/axolotl.png new file mode 100644 index 0000000..4c5a7ee Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/axolotl.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/bastion_remnant.png b/src/main/resources/assets/minetriad/textures/item/cards/bastion_remnant.png new file mode 100644 index 0000000..191fb2b Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/bastion_remnant.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/bat.png b/src/main/resources/assets/minetriad/textures/item/cards/bat.png new file mode 100644 index 0000000..13c00da Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/bat.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/beacon.png b/src/main/resources/assets/minetriad/textures/item/cards/beacon.png new file mode 100644 index 0000000..3e51df0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/beacon.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/bee.png b/src/main/resources/assets/minetriad/textures/item/cards/bee.png new file mode 100644 index 0000000..9c171b8 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/bee.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/blaze.png b/src/main/resources/assets/minetriad/textures/item/cards/blaze.png new file mode 100644 index 0000000..ecab985 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/blaze.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/bogged.png b/src/main/resources/assets/minetriad/textures/item/cards/bogged.png new file mode 100644 index 0000000..7331c5a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/bogged.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/breeze.png b/src/main/resources/assets/minetriad/textures/item/cards/breeze.png new file mode 100644 index 0000000..b05759b Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/breeze.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/c418.png b/src/main/resources/assets/minetriad/textures/item/cards/c418.png new file mode 100644 index 0000000..fc87f8a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/c418.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/camel.png b/src/main/resources/assets/minetriad/textures/item/cards/camel.png new file mode 100644 index 0000000..ecaf337 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/camel.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/cat.png b/src/main/resources/assets/minetriad/textures/item/cards/cat.png new file mode 100644 index 0000000..d3a31a3 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/cat.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/cave_spider.png b/src/main/resources/assets/minetriad/textures/item/cards/cave_spider.png new file mode 100644 index 0000000..896131d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/cave_spider.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/chicken.png b/src/main/resources/assets/minetriad/textures/item/cards/chicken.png new file mode 100644 index 0000000..54e7c09 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/chicken.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/cod.png b/src/main/resources/assets/minetriad/textures/item/cards/cod.png new file mode 100644 index 0000000..38fe71d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/cod.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/command_block.png b/src/main/resources/assets/minetriad/textures/item/cards/command_block.png new file mode 100644 index 0000000..07317c6 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/command_block.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/conduit.png b/src/main/resources/assets/minetriad/textures/item/cards/conduit.png new file mode 100644 index 0000000..8a4c32c Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/conduit.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/cow.png b/src/main/resources/assets/minetriad/textures/item/cards/cow.png new file mode 100644 index 0000000..0d50c01 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/cow.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/cpw.png b/src/main/resources/assets/minetriad/textures/item/cards/cpw.png new file mode 100644 index 0000000..30984be Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/cpw.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/creeper.png b/src/main/resources/assets/minetriad/textures/item/cards/creeper.png new file mode 100644 index 0000000..72a8479 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/creeper.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/dinnerbone.png b/src/main/resources/assets/minetriad/textures/item/cards/dinnerbone.png new file mode 100644 index 0000000..b8d98ff Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/dinnerbone.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/direwolf20.png b/src/main/resources/assets/minetriad/textures/item/cards/direwolf20.png new file mode 100644 index 0000000..f3c976f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/direwolf20.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/docm77.png b/src/main/resources/assets/minetriad/textures/item/cards/docm77.png new file mode 100644 index 0000000..2645ce5 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/docm77.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/dolphin.png b/src/main/resources/assets/minetriad/textures/item/cards/dolphin.png new file mode 100644 index 0000000..eb4c3ab Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/dolphin.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/donkey.png b/src/main/resources/assets/minetriad/textures/item/cards/donkey.png new file mode 100644 index 0000000..547de95 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/donkey.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/dragon_egg.png b/src/main/resources/assets/minetriad/textures/item/cards/dragon_egg.png new file mode 100644 index 0000000..a000e7b Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/dragon_egg.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/drowned.png b/src/main/resources/assets/minetriad/textures/item/cards/drowned.png new file mode 100644 index 0000000..6e508a0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/drowned.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/elder_guardian.png b/src/main/resources/assets/minetriad/textures/item/cards/elder_guardian.png new file mode 100644 index 0000000..c1f7b10 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/elder_guardian.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/elytra.png b/src/main/resources/assets/minetriad/textures/item/cards/elytra.png new file mode 100644 index 0000000..8467eed Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/elytra.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/enchanted_golden_apple.png b/src/main/resources/assets/minetriad/textures/item/cards/enchanted_golden_apple.png new file mode 100644 index 0000000..2bd55a0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/enchanted_golden_apple.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/end_city.png b/src/main/resources/assets/minetriad/textures/item/cards/end_city.png new file mode 100644 index 0000000..488e1f4 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/end_city.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ender_dragon.png b/src/main/resources/assets/minetriad/textures/item/cards/ender_dragon.png new file mode 100644 index 0000000..c377f5c Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ender_dragon.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/enderman.png b/src/main/resources/assets/minetriad/textures/item/cards/enderman.png new file mode 100644 index 0000000..c075acd Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/enderman.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/endermite.png b/src/main/resources/assets/minetriad/textures/item/cards/endermite.png new file mode 100644 index 0000000..32f59b5 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/endermite.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ethoslab.png b/src/main/resources/assets/minetriad/textures/item/cards/ethoslab.png new file mode 100644 index 0000000..898ec4b Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ethoslab.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/evoker.png b/src/main/resources/assets/minetriad/textures/item/cards/evoker.png new file mode 100644 index 0000000..257d748 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/evoker.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/fox.png b/src/main/resources/assets/minetriad/textures/item/cards/fox.png new file mode 100644 index 0000000..dd0fd37 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/fox.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/frog.png b/src/main/resources/assets/minetriad/textures/item/cards/frog.png new file mode 100644 index 0000000..7b5c687 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/frog.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ghast.png b/src/main/resources/assets/minetriad/textures/item/cards/ghast.png new file mode 100644 index 0000000..6af23ed Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ghast.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/glow_squid.png b/src/main/resources/assets/minetriad/textures/item/cards/glow_squid.png new file mode 100644 index 0000000..8b93b0e Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/glow_squid.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/goat.png b/src/main/resources/assets/minetriad/textures/item/cards/goat.png new file mode 100644 index 0000000..1847661 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/goat.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/guardian.png b/src/main/resources/assets/minetriad/textures/item/cards/guardian.png new file mode 100644 index 0000000..4a3b3a9 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/guardian.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/hoglin.png b/src/main/resources/assets/minetriad/textures/item/cards/hoglin.png new file mode 100644 index 0000000..2dd7a27 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/hoglin.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/horse.png b/src/main/resources/assets/minetriad/textures/item/cards/horse.png new file mode 100644 index 0000000..f940751 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/horse.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/husk.png b/src/main/resources/assets/minetriad/textures/item/cards/husk.png new file mode 100644 index 0000000..efb5036 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/husk.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/iron_golem.png b/src/main/resources/assets/minetriad/textures/item/cards/iron_golem.png new file mode 100644 index 0000000..456aae9 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/iron_golem.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/jeb.png b/src/main/resources/assets/minetriad/textures/item/cards/jeb.png new file mode 100644 index 0000000..eff33e9 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/jeb.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/llama.png b/src/main/resources/assets/minetriad/textures/item/cards/llama.png new file mode 100644 index 0000000..9dfeb0c Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/llama.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/mace.png b/src/main/resources/assets/minetriad/textures/item/cards/mace.png new file mode 100644 index 0000000..6e2d13a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/mace.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/magma_cube.png b/src/main/resources/assets/minetriad/textures/item/cards/magma_cube.png new file mode 100644 index 0000000..1f6f34c Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/magma_cube.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/mooshroom.png b/src/main/resources/assets/minetriad/textures/item/cards/mooshroom.png new file mode 100644 index 0000000..bdd4b27 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/mooshroom.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/mule.png b/src/main/resources/assets/minetriad/textures/item/cards/mule.png new file mode 100644 index 0000000..45242d0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/mule.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/mumbojumbo.png b/src/main/resources/assets/minetriad/textures/item/cards/mumbojumbo.png new file mode 100644 index 0000000..eb0429d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/mumbojumbo.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/nether_fortress.png b/src/main/resources/assets/minetriad/textures/item/cards/nether_fortress.png new file mode 100644 index 0000000..f181432 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/nether_fortress.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/nether_star.png b/src/main/resources/assets/minetriad/textures/item/cards/nether_star.png new file mode 100644 index 0000000..262bd79 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/nether_star.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/netherite.png b/src/main/resources/assets/minetriad/textures/item/cards/netherite.png new file mode 100644 index 0000000..7d88c3f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/netherite.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ocean_monument.png b/src/main/resources/assets/minetriad/textures/item/cards/ocean_monument.png new file mode 100644 index 0000000..64293a4 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ocean_monument.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ocelot.png b/src/main/resources/assets/minetriad/textures/item/cards/ocelot.png new file mode 100644 index 0000000..276e167 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ocelot.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/panda.png b/src/main/resources/assets/minetriad/textures/item/cards/panda.png new file mode 100644 index 0000000..2206f1f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/panda.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/parrot.png b/src/main/resources/assets/minetriad/textures/item/cards/parrot.png new file mode 100644 index 0000000..70df1a4 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/parrot.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/phantom.png b/src/main/resources/assets/minetriad/textures/item/cards/phantom.png new file mode 100644 index 0000000..64b6a9e Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/phantom.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/pig.png b/src/main/resources/assets/minetriad/textures/item/cards/pig.png new file mode 100644 index 0000000..df38878 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/pig.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/piglin.png b/src/main/resources/assets/minetriad/textures/item/cards/piglin.png new file mode 100644 index 0000000..98d6bc2 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/piglin.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/piglin_brute.png b/src/main/resources/assets/minetriad/textures/item/cards/piglin_brute.png new file mode 100644 index 0000000..60660ed Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/piglin_brute.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/pillager.png b/src/main/resources/assets/minetriad/textures/item/cards/pillager.png new file mode 100644 index 0000000..95321a2 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/pillager.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/polar_bear.png b/src/main/resources/assets/minetriad/textures/item/cards/polar_bear.png new file mode 100644 index 0000000..679d82e Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/polar_bear.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/poppy.png b/src/main/resources/assets/minetriad/textures/item/cards/poppy.png new file mode 100644 index 0000000..3853509 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/poppy.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/pufferfish.png b/src/main/resources/assets/minetriad/textures/item/cards/pufferfish.png new file mode 100644 index 0000000..c1f1ecd Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/pufferfish.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/rabbit.png b/src/main/resources/assets/minetriad/textures/item/cards/rabbit.png new file mode 100644 index 0000000..0072db5 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/rabbit.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/ravager.png b/src/main/resources/assets/minetriad/textures/item/cards/ravager.png new file mode 100644 index 0000000..3dda4d2 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/ravager.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/recovery_compass.png b/src/main/resources/assets/minetriad/textures/item/cards/recovery_compass.png new file mode 100644 index 0000000..cf379c0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/recovery_compass.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/salmon.png b/src/main/resources/assets/minetriad/textures/item/cards/salmon.png new file mode 100644 index 0000000..337792c Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/salmon.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/sheep.png b/src/main/resources/assets/minetriad/textures/item/cards/sheep.png new file mode 100644 index 0000000..926a028 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/sheep.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/shulker.png b/src/main/resources/assets/minetriad/textures/item/cards/shulker.png new file mode 100644 index 0000000..b819970 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/shulker.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/silverfish.png b/src/main/resources/assets/minetriad/textures/item/cards/silverfish.png new file mode 100644 index 0000000..4f0a348 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/silverfish.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/skeleton.png b/src/main/resources/assets/minetriad/textures/item/cards/skeleton.png new file mode 100644 index 0000000..ded4d62 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/skeleton.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/skeleton_horse.png b/src/main/resources/assets/minetriad/textures/item/cards/skeleton_horse.png new file mode 100644 index 0000000..5c1e0b4 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/skeleton_horse.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/slime.png b/src/main/resources/assets/minetriad/textures/item/cards/slime.png new file mode 100644 index 0000000..af38b16 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/slime.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/sniffer.png b/src/main/resources/assets/minetriad/textures/item/cards/sniffer.png new file mode 100644 index 0000000..e1efa26 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/sniffer.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/snow_golem.png b/src/main/resources/assets/minetriad/textures/item/cards/snow_golem.png new file mode 100644 index 0000000..a4107bc Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/snow_golem.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/sphax.png b/src/main/resources/assets/minetriad/textures/item/cards/sphax.png new file mode 100644 index 0000000..d3607c6 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/sphax.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/spider.png b/src/main/resources/assets/minetriad/textures/item/cards/spider.png new file mode 100644 index 0000000..3443c07 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/spider.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/squid.png b/src/main/resources/assets/minetriad/textures/item/cards/squid.png new file mode 100644 index 0000000..d86777f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/squid.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/stray.png b/src/main/resources/assets/minetriad/textures/item/cards/stray.png new file mode 100644 index 0000000..d0c9c05 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/stray.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/strider.png b/src/main/resources/assets/minetriad/textures/item/cards/strider.png new file mode 100644 index 0000000..bc56cb7 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/strider.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/stronghold.png b/src/main/resources/assets/minetriad/textures/item/cards/stronghold.png new file mode 100644 index 0000000..b058317 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/stronghold.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/tadpole.png b/src/main/resources/assets/minetriad/textures/item/cards/tadpole.png new file mode 100644 index 0000000..3adc69d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/tadpole.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/totem_of_undying.png b/src/main/resources/assets/minetriad/textures/item/cards/totem_of_undying.png new file mode 100644 index 0000000..c22fde5 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/totem_of_undying.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/trader_llama.png b/src/main/resources/assets/minetriad/textures/item/cards/trader_llama.png new file mode 100644 index 0000000..2b226d8 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/trader_llama.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/trial_spawner.png b/src/main/resources/assets/minetriad/textures/item/cards/trial_spawner.png new file mode 100644 index 0000000..410e5ec Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/trial_spawner.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/tropical_fish.png b/src/main/resources/assets/minetriad/textures/item/cards/tropical_fish.png new file mode 100644 index 0000000..264ef48 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/tropical_fish.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/turtle.png b/src/main/resources/assets/minetriad/textures/item/cards/turtle.png new file mode 100644 index 0000000..f5c2521 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/turtle.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/vault.png b/src/main/resources/assets/minetriad/textures/item/cards/vault.png new file mode 100644 index 0000000..a85fd07 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/vault.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/vex.png b/src/main/resources/assets/minetriad/textures/item/cards/vex.png new file mode 100644 index 0000000..4e39f82 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/vex.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/villager.png b/src/main/resources/assets/minetriad/textures/item/cards/villager.png new file mode 100644 index 0000000..fb3fa2a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/villager.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/vindicator.png b/src/main/resources/assets/minetriad/textures/item/cards/vindicator.png new file mode 100644 index 0000000..f69bb28 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/vindicator.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/vintagebeef.png b/src/main/resources/assets/minetriad/textures/item/cards/vintagebeef.png new file mode 100644 index 0000000..43e2d48 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/vintagebeef.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/wandering_trader.png b/src/main/resources/assets/minetriad/textures/item/cards/wandering_trader.png new file mode 100644 index 0000000..dc0df45 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/wandering_trader.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/warden.png b/src/main/resources/assets/minetriad/textures/item/cards/warden.png new file mode 100644 index 0000000..e46120a Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/warden.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/witch.png b/src/main/resources/assets/minetriad/textures/item/cards/witch.png new file mode 100644 index 0000000..4287cb0 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/witch.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/wither.png b/src/main/resources/assets/minetriad/textures/item/cards/wither.png new file mode 100644 index 0000000..f34422d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/wither.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/wither_skeleton.png b/src/main/resources/assets/minetriad/textures/item/cards/wither_skeleton.png new file mode 100644 index 0000000..c14ca5d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/wither_skeleton.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/wolf.png b/src/main/resources/assets/minetriad/textures/item/cards/wolf.png new file mode 100644 index 0000000..2300b77 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/wolf.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/woodland_mansion.png b/src/main/resources/assets/minetriad/textures/item/cards/woodland_mansion.png new file mode 100644 index 0000000..e6166a8 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/woodland_mansion.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/xisumavoid.png b/src/main/resources/assets/minetriad/textures/item/cards/xisumavoid.png new file mode 100644 index 0000000..577190f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/xisumavoid.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/zoglin.png b/src/main/resources/assets/minetriad/textures/item/cards/zoglin.png new file mode 100644 index 0000000..39c751f Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/zoglin.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/zombie.png b/src/main/resources/assets/minetriad/textures/item/cards/zombie.png new file mode 100644 index 0000000..d7ff90d Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/zombie.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/zombie_horse.png b/src/main/resources/assets/minetriad/textures/item/cards/zombie_horse.png new file mode 100644 index 0000000..2e01cff Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/zombie_horse.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/zombie_villager.png b/src/main/resources/assets/minetriad/textures/item/cards/zombie_villager.png new file mode 100644 index 0000000..a2d6062 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/zombie_villager.png differ diff --git a/src/main/resources/assets/minetriad/textures/item/cards/zombified_piglin.png b/src/main/resources/assets/minetriad/textures/item/cards/zombified_piglin.png new file mode 100644 index 0000000..00c9382 Binary files /dev/null and b/src/main/resources/assets/minetriad/textures/item/cards/zombified_piglin.png differ