full card structure in place, card binder working.
Some checks failed
Build / build (push) Has been cancelled
BIN
Full Art/passive_mobs/axolotl.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Full Art/passive_mobs/bee.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Full Art/passive_mobs/camel.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
Full Art/passive_mobs/cat.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
Full Art/passive_mobs/chicken.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
Full Art/passive_mobs/cod.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/cow.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Full Art/passive_mobs/donkey.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/fox.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
Full Art/passive_mobs/frog.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/glow_squid.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
Full Art/passive_mobs/goat.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
Full Art/passive_mobs/horse.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/llama.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/mooshroom.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
Full Art/passive_mobs/mule.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
Full Art/passive_mobs/ocelot.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/panda.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Full Art/passive_mobs/parrot.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
Full Art/passive_mobs/pig.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Full Art/passive_mobs/polar_bear.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Full Art/passive_mobs/rabbit.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
Full Art/passive_mobs/salmon.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Full Art/passive_mobs/sheep.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
Full Art/passive_mobs/sniffer.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
Full Art/passive_mobs/squid.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
Full Art/passive_mobs/strider.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
Full Art/passive_mobs/turtle.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
Full Art/passive_mobs/wolf.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
74
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.
|
||||
817
TTCardReference.txt
Normal file
@@ -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
|
||||
7
assets/minecraft/models/item/filled_map.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "item/filled_map",
|
||||
"layer1": "item/filled_map_markings"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
626
net/minecraft/client/renderer/ItemInHandRenderer.java
Normal file
@@ -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.<AbstractClientPlayer>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.<AbstractClientPlayer>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;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
net/neoforged/neoforge/client/event/RenderHandEvent.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
BIN
scripts/PassiveMobArtGenerator$MobArt.class
Normal file
460
scripts/PassiveMobArtGenerator.java
Normal file
@@ -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<String> features) {
|
||||
}
|
||||
|
||||
private static final List<MobArt> 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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<CardDefinition> 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<CardDefinition> CARD_DEFINITIONS = createDefinitions();
|
||||
private static final Map<ResourceLocation, CardDefinition> 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<CardDefinition> createDefinitions() {
|
||||
List<CardDefinition> 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<CardDefinition> 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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CardBinderMenu> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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<ItemStack> 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<ItemStack> cardStacks = CardRegistry.all().stream()
|
||||
.map(card -> CardItem.createCardStack(card.id(), TriadItems.TRIAD_CARD.get()))
|
||||
.toList();
|
||||
registration.addExtraItemStacks(cardStacks);
|
||||
}
|
||||
}
|
||||
@@ -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<ItemStack> 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());
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
|
||||
312
src/main/java/com/trunksbomb/minetriad/menu/CardBinderMenu.java
Normal file
@@ -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<ItemStack> 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<ItemStack> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Item> TRIAD_CARD = ITEMS.register("triad_card", () -> new CardItem(DEFAULT_PROPERTIES.stacksTo(1)));
|
||||
public static final DeferredItem<Item> DECK_BOX = ITEMS.register("deck_box", () -> new DeckBoxItem(DEFAULT_PROPERTIES.stacksTo(1)));
|
||||
public static final DeferredItem<Item> CARD_BINDER = ITEMS.registerSimpleItem("card_binder", DEFAULT_PROPERTIES.stacksTo(1));
|
||||
public static final DeferredItem<Item> CARD_BINDER = ITEMS.register(
|
||||
"card_binder",
|
||||
() -> new CardBinderItem(DEFAULT_PROPERTIES.stacksTo(1).component(DataComponents.CONTAINER, ItemContainerContents.EMPTY)));
|
||||
public static final DeferredItem<BlockItem> DUEL_TABLE = ITEMS.registerSimpleBlockItem("duel_table", TriadBlocks.DUEL_TABLE);
|
||||
|
||||
private TriadItems() {
|
||||
|
||||
@@ -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<MenuType<?>> MENUS = DeferredRegister.create(Registries.MENU, MineTriad.MOD_ID);
|
||||
|
||||
public static final DeferredHolder<MenuType<?>, MenuType<CardBinderMenu>> CARD_BINDER = MENUS.register(
|
||||
"card_binder",
|
||||
() -> IMenuTypeExtension.create(CardBinderMenu::new));
|
||||
|
||||
private TriadMenus() {
|
||||
}
|
||||
|
||||
public static void register(IEventBus eventBus) {
|
||||
MENUS.register(eventBus);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/main/resources/assets/minetriad/textures/gui/card_binder.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 139 B |
BIN
src/main/resources/assets/minetriad/textures/item/card_back.png
Normal file
|
After Width: | Height: | Size: 496 B |
|
After Width: | Height: | Size: 431 B |
|
After Width: | Height: | Size: 505 B |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 532 B |
BIN
src/main/resources/assets/minetriad/textures/item/cards/bat.png
Normal file
|
After Width: | Height: | Size: 416 B |
|
After Width: | Height: | Size: 434 B |
BIN
src/main/resources/assets/minetriad/textures/item/cards/bee.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 425 B |
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 429 B |
BIN
src/main/resources/assets/minetriad/textures/item/cards/c418.png
Normal file
|
After Width: | Height: | Size: 436 B |
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/main/resources/assets/minetriad/textures/item/cards/cat.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src/main/resources/assets/minetriad/textures/item/cards/cod.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 519 B |
|
After Width: | Height: | Size: 441 B |
BIN
src/main/resources/assets/minetriad/textures/item/cards/cow.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src/main/resources/assets/minetriad/textures/item/cards/cpw.png
Normal file
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 478 B |
|
After Width: | Height: | Size: 513 B |
|
After Width: | Height: | Size: 452 B |
|
After Width: | Height: | Size: 448 B |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 496 B |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 522 B |
|
After Width: | Height: | Size: 449 B |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 511 B |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 444 B |
BIN
src/main/resources/assets/minetriad/textures/item/cards/fox.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
src/main/resources/assets/minetriad/textures/item/cards/frog.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 435 B |
|
After Width: | Height: | Size: 6.5 KiB |
BIN
src/main/resources/assets/minetriad/textures/item/cards/goat.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 451 B |