forked from mirrors/cobblemon
Compare commits
222
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
870397ebb9 | ||
|
|
e2ccb20402 | ||
|
|
4ddc167254 | ||
|
|
56ad481304 | ||
|
|
cc7a9ae44a | ||
|
|
664f79f3a7 | ||
|
|
558907eb5c | ||
|
|
370b689ab4 | ||
|
|
e2740babc5 | ||
|
|
3393ab17d8 | ||
|
|
645029a9c7 | ||
|
|
f168744ccd | ||
|
|
67210cb5f9 | ||
|
|
779fbb4f38 | ||
|
|
ce04f546e7 | ||
|
|
a36437ea16 | ||
|
|
aa5af84283 | ||
|
|
e3031d2618 | ||
|
|
9e44ba1fb1 | ||
|
|
1af66d4175 | ||
|
|
144213cee8 | ||
|
|
cc95b4450e | ||
|
|
4bda306a3b | ||
|
|
2ee556dc31 | ||
|
|
4457df9aba | ||
|
|
6dfb073242 | ||
|
|
e1bd0d688f | ||
|
|
0bce2d8645 | ||
|
|
dfe240f1d3 | ||
|
|
f1ac0fe99f | ||
|
|
c2f575bf22 | ||
|
|
5f76966678 | ||
|
|
beddea1ecb | ||
|
|
04236fcf33 | ||
|
|
05e87b7faf | ||
|
|
e9d298834a | ||
|
|
ce113cbb80 | ||
|
|
61ebe7124a | ||
|
|
462c0b35b8 | ||
|
|
0b55c88ec4 | ||
|
|
942da6b2f6 | ||
|
|
224c38e0a7 | ||
|
|
056917f7df | ||
|
|
2aa9379987 | ||
|
|
3a6a3253a3 | ||
|
|
1580a06a8e | ||
|
|
e2bfdd3e76 | ||
|
|
1d24565aa6 | ||
|
|
2d80402688 | ||
|
|
20614b6b07 | ||
|
|
b32b5a66a4 | ||
|
|
ad92804fce | ||
|
|
ca93955134 | ||
|
|
01d99676f9 | ||
|
|
2c8fd29cf1 | ||
|
|
a9eec5f568 | ||
|
|
e54eaa728b | ||
|
|
4e550151e5 | ||
|
|
67f5259742 | ||
|
|
eb783e13ba | ||
|
|
68237e7161 | ||
|
|
ec9f7665e2 | ||
|
|
f19ae0120e | ||
|
|
d674ff1d40 | ||
|
|
37d6f58536 | ||
|
|
2ce23e072d | ||
|
|
d37dfee032 | ||
|
|
ee567a63b6 | ||
|
|
868dd9cc44 | ||
|
|
3f9b0002a9 | ||
|
|
e07b15c8fc | ||
|
|
44c5c21fb5 | ||
|
|
9d13e86487 | ||
|
|
afa395cfeb | ||
|
|
831903b8e3 | ||
|
|
d382f48e2d | ||
|
|
2b2b44a535 | ||
|
|
ac9d85929e | ||
|
|
88c981bd6a | ||
|
|
f0baa21b1f | ||
|
|
1d5d78b953 | ||
|
|
f771248936 | ||
|
|
35850ea996 | ||
|
|
caaccf0e67 | ||
|
|
e5de8bd26f | ||
|
|
9528d375f4 | ||
|
|
9aac7dc8e3 | ||
|
|
0bebd2a71e | ||
|
|
55874e6a84 | ||
|
|
cda0e10524 | ||
|
|
153f888cf5 | ||
|
|
6972b7d49d | ||
|
|
d7f442e2d0 | ||
|
|
cc15c0a487 | ||
|
|
4ee628ea7a | ||
|
|
64c0001aff | ||
|
|
320b2ff528 | ||
|
|
cf8a1f5447 | ||
|
|
696d98f7cd | ||
|
|
909b90aea1 | ||
|
|
04296ae04e | ||
|
|
025dea4201 | ||
|
|
a0630ff738 | ||
|
|
2a7503db06 | ||
|
|
1480bd97c2 | ||
|
|
3bb5ecd2b6 | ||
|
|
e14e44a1c5 | ||
|
|
46cc276e2e | ||
|
|
0ed413070d | ||
|
|
f66f5ffdf9 | ||
|
|
ca280a7b3e | ||
|
|
50cb670eee | ||
|
|
cfd3eb6c4c | ||
|
|
85284bff9d | ||
|
|
fa4d33f725 | ||
|
|
4ce42cdf6d | ||
|
|
1b8562ead8 | ||
|
|
b9d9b906fb | ||
|
|
38cfd7eca8 | ||
|
|
2ba2eea801 | ||
|
|
e9d079a223 | ||
|
|
e2e42ee91c | ||
|
|
4b0208201b | ||
|
|
bec3f97733 | ||
|
|
16cfe37dac | ||
|
|
7381da863c | ||
|
|
b0902825fa | ||
|
|
31c489cba0 | ||
|
|
6a2f21d75a | ||
|
|
e2c42a57c7 | ||
|
|
8592b53216 | ||
|
|
49dcf0049f | ||
|
|
076bee923f | ||
|
|
aacdb33827 | ||
|
|
53e9ece930 | ||
|
|
87f08d9bab | ||
|
|
a50a6326af | ||
|
|
3c0eec833e | ||
|
|
5b0cfdffcb | ||
|
|
d788e0402e | ||
|
|
a347dc5e2a | ||
|
|
b2fe158a7b | ||
|
|
faab7e8df0 | ||
|
|
9070e198f1 | ||
|
|
45b2a0db4d | ||
|
|
65d39050ea | ||
|
|
5da16df5ae | ||
|
|
24fc4b05ab | ||
|
|
173902feb1 | ||
|
|
fd5f08fe84 | ||
|
|
d430ad6ca1 | ||
|
|
1309c18292 | ||
|
|
b6deebac97 | ||
|
|
83711f2dcb | ||
|
|
18e1a767af | ||
|
|
6ae3efb461 | ||
|
|
84e1934134 | ||
|
|
9aed9342ee | ||
|
|
e4d0bf39ca | ||
|
|
847fa6842e | ||
|
|
08aa4f29bd | ||
|
|
539218381b | ||
|
|
9dff39b4d7 | ||
|
|
1e578e94e5 | ||
|
|
ec35c9b9bb | ||
|
|
0deaeaeb7d | ||
|
|
ec3d9e1a56 | ||
|
|
814f84ae42 | ||
|
|
904be475ef | ||
|
|
5edecbe81b | ||
|
|
f357033404 | ||
|
|
4800ae6649 | ||
|
|
60b0690c1e | ||
|
|
82e31dd766 | ||
|
|
130e767e75 | ||
|
|
b1a1d509ec | ||
|
|
0698cf495e | ||
|
|
6047660405 | ||
|
|
aaf3495f83 | ||
|
|
ae6529c9fd | ||
|
|
45a96b2a42 | ||
|
|
4212903c67 | ||
|
|
be47dbcede | ||
|
|
9556c57c69 | ||
|
|
f72b65bfac | ||
|
|
6121700c9b | ||
|
|
91c968e743 | ||
|
|
8fed6f4823 | ||
|
|
34e4b39d75 | ||
|
|
a21c70ce46 | ||
|
|
8865a71f4c | ||
|
|
142cadc187 | ||
|
|
66f4c2531d | ||
|
|
3f8ddd5f7d | ||
|
|
c076e14cbd | ||
|
|
d724eb068b | ||
|
|
d3ac263e29 | ||
|
|
a6b7fe1f17 | ||
|
|
ec714ccb84 | ||
|
|
ab0a129ba1 | ||
|
|
a3f272b2aa | ||
|
|
77d91ed979 | ||
|
|
9907ba17c1 | ||
|
|
a6f0e19a44 | ||
|
|
7ee064b04d | ||
|
|
f2e4c735f7 | ||
|
|
5345bd321f | ||
|
|
eab8d0063b | ||
|
|
6877977234 | ||
|
|
60cb81362e | ||
|
|
088acb8e86 | ||
|
|
77f59f06cb | ||
|
|
a0882c6105 | ||
|
|
d50eb52172 | ||
|
|
5e5cadd314 | ||
|
|
75b06f82f0 | ||
|
|
2ab050aa60 | ||
|
|
6d2f645e4a | ||
|
|
9f937e29f2 | ||
|
|
abc61a96c8 | ||
|
|
b1d10822d4 | ||
|
|
09d984456d |
+152
@@ -12,6 +12,7 @@
|
||||
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
|
||||
- Added the various log blocks as cosmetic items for Timburr and Komala.
|
||||
- Added cosmetics for Spoink, Gurdurr, Conkeldurr, Squirtle Line, Sneasler, Sandile line, Treecko line, Braixen, Delphox, and Dragonite.
|
||||
- Added Gilded Chest cosmetics for Chest Gimmighoul and Gholdengo
|
||||
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
|
||||
- Added Pokémon markings, toggleable within the summary.
|
||||
- Added `/boxcount` command to change PC boxes amount
|
||||
@@ -38,6 +39,7 @@
|
||||
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
|
||||
- Ground type Pokémon are immune to lightning damage
|
||||
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
|
||||
- Added optional `filePath` parameter to MoLang functions to allow for global stores (ex. `../global/`). Path must end in a forward slash.
|
||||
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
|
||||
- Galarica Nut bushes now generate on beaches.
|
||||
- Some Pokémon now pitch their bodies in the direction they're moving.
|
||||
@@ -197,6 +199,8 @@
|
||||
- Tropius
|
||||
- Nosepass
|
||||
- Probopass
|
||||
- Sneasel
|
||||
- Weavile
|
||||
- Sneasler
|
||||
- Braixen
|
||||
- Delphox
|
||||
@@ -239,6 +243,7 @@
|
||||
- Volcarona
|
||||
- Dwebble
|
||||
- Crustle
|
||||
- Mr. Mime
|
||||
- Flygon
|
||||
- Pichu
|
||||
- Pikachu
|
||||
@@ -257,6 +262,21 @@
|
||||
- Klinklang
|
||||
- Baltoy
|
||||
- Claydol
|
||||
- Mamoswine
|
||||
- Rufflet
|
||||
- Braviary
|
||||
- Girafarig
|
||||
- Farigiraf
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Venusaur
|
||||
- Parasect
|
||||
- Bastiodon
|
||||
- Amaura
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Cleffa
|
||||
@@ -335,13 +355,34 @@
|
||||
- Murkrow
|
||||
- Zorua
|
||||
- Zoroark
|
||||
- Mime.Jr
|
||||
- Mr. Mime
|
||||
- Pichu
|
||||
- Pikachu
|
||||
- Raichu
|
||||
- Alolan Raichu
|
||||
- Dusknoir
|
||||
- Deino
|
||||
- Zweilous
|
||||
- Hydreigon
|
||||
- Dreepy
|
||||
- Drakloak
|
||||
- Dragapult
|
||||
- Mamoswine
|
||||
- Rookidee
|
||||
- Corvisquire
|
||||
- Corviknight
|
||||
- Venipede
|
||||
- Whirlipede
|
||||
- Scolipede
|
||||
- Farigiraf
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Gimmighoul
|
||||
- Gholdengo
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -404,6 +445,7 @@
|
||||
- Updated `PokemonSentEvent` parameters to include the Position and Level of the Pokémon being sent out.
|
||||
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
|
||||
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
|
||||
- Roseli Berry Trees now naturally generate in their preferred biomes.
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -485,6 +527,7 @@
|
||||
- Fixed global species features... not working. Since they were created. Oops.
|
||||
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
|
||||
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
|
||||
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -616,6 +659,115 @@
|
||||
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
|
||||
- Fixed `background` field and added `textColor` field for dialogues.
|
||||
|
||||
### Particles
|
||||
Added new/updated particles for the following moves:
|
||||
- Absorb
|
||||
- Acid
|
||||
- Acid Spray
|
||||
- Aerial Ace
|
||||
- Amnesia
|
||||
- Aurora Beam
|
||||
- Bite
|
||||
- Body Slam
|
||||
- Bubble
|
||||
- Bubble Beam
|
||||
- Bulldoze
|
||||
- Bullet Punch
|
||||
- Close Combat
|
||||
- Confusion
|
||||
- Crunch
|
||||
- Cotton Guard
|
||||
- Double Team
|
||||
- Dragon Claw
|
||||
- Egg Bomb
|
||||
- Ember
|
||||
- Eruption
|
||||
- Explosion
|
||||
- Fire Blast
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Flame Wheel
|
||||
- Frost Breath
|
||||
- Frustration
|
||||
- Giga Drain
|
||||
- Growl
|
||||
- Haze
|
||||
- Hidden Power
|
||||
- Horn Attack
|
||||
- Hyper Fang
|
||||
- Hypnosis
|
||||
- Ice Beam
|
||||
- Ice Punch
|
||||
- Ice Shard
|
||||
- Icy Wind
|
||||
- Infestation
|
||||
- Kinesis
|
||||
- Lava Plume
|
||||
- Leech Life
|
||||
- Leech Seed
|
||||
- Leer
|
||||
- Lick
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Mist
|
||||
- Mud Bomb
|
||||
- MudSport
|
||||
- Mystical Fire
|
||||
- Nasty Plot
|
||||
- Poison Fang
|
||||
- Poison Gas
|
||||
- Poison Powder
|
||||
- Population Bomb
|
||||
- Pound
|
||||
- Powder
|
||||
- Powder Snow
|
||||
- Present
|
||||
- Protect
|
||||
- Psybeam
|
||||
- Psychic
|
||||
- Pursuit
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Return
|
||||
- Rock Throw
|
||||
- Sand Attack
|
||||
- Scary Face
|
||||
- Scratch
|
||||
- Seed Bomb
|
||||
- Seismic Toss
|
||||
- Self Destruct
|
||||
- Shadow Ball
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Sleep Powder
|
||||
- Sludge Bomb
|
||||
- Smokescreen
|
||||
- Spike
|
||||
- Spore
|
||||
- Stomp
|
||||
- String Shot
|
||||
- Stun Spore
|
||||
- Super Fang
|
||||
- Swords Dance
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunderbolt
|
||||
- Thunder Shock
|
||||
- Thunder Wave
|
||||
- Toxic
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
- Will-O-Wisp
|
||||
- Withdraw
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
### Additions
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.inventory.MenuType.MenuSupplier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(MenuType.class)
|
||||
public interface MenuTypeInvoker {
|
||||
@Invoker("<init>")
|
||||
static <T extends AbstractContainerMenu> MenuType<T> create(MenuSupplier<T> factory, FeatureFlagSet requiredFeatures) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -56,10 +56,7 @@ import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.DexDataNbtBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataJsonBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBackend
|
||||
import com.cobblemon.mod.common.api.storage.player.adapter.*
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
@@ -342,8 +339,12 @@ object Cobblemon {
|
||||
val pokedexNbtFactory = CachedPlayerDataStoreFactory(DexDataNbtBackend())
|
||||
pokedexNbtFactory.setup(server)
|
||||
|
||||
val tmMoveFactory = CachedPlayerDataStoreFactory(TMMoveNbtBackend())
|
||||
tmMoveFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalJsonFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexNbtFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMoveFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
|
||||
if (config.storageFormat == "nbt") {
|
||||
NBTStoreAdapter(pokemonStoreRoot.absolutePath, useNestedFolders = true, folderPerClass = true)
|
||||
@@ -370,8 +371,14 @@ object Cobblemon {
|
||||
val pokedexMongoFactory = CachedPlayerDataStoreFactory(DexDataMongoBackend(mongoClient, config.mongoDBDatabaseName, "PokeDexCollection"))
|
||||
pokedexMongoFactory.setup(server)
|
||||
|
||||
val tmMovesMongoFactory = CachedPlayerDataStoreFactory(
|
||||
TMMoveMongoBackend(mongoClient, config.mongoDBDatabaseName, "TMMovesCollection")
|
||||
)
|
||||
tmMovesMongoFactory.setup(server)
|
||||
|
||||
playerDataManager.setFactory(generalMongoFactory, PlayerInstancedDataStoreTypes.GENERAL)
|
||||
playerDataManager.setFactory(pokedexMongoFactory, PlayerInstancedDataStoreTypes.POKEDEX)
|
||||
playerDataManager.setFactory(tmMovesMongoFactory, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
MongoDBStoreAdapter(mongoClient, config.mongoDBDatabaseName)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
LOGGER.error("MongoDB driver not found.")
|
||||
|
||||
@@ -39,6 +39,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
|
||||
|
||||
@JvmField
|
||||
val TM_SHELF: BlockEntityType<TMShelfBlockEntity> = this.create("tm_shelf", BlockEntityType.Builder.of(::TMShelfBlockEntity, CobblemonBlocks.TM_SHELF).build(null))
|
||||
|
||||
@JvmField
|
||||
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
|
||||
|
||||
@@ -48,6 +51,8 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
|
||||
@JvmField
|
||||
val TM_MACHINE: BlockEntityType<TMMachineBlockEntity> = this.create("tm_machine", BlockEntityType.Builder.of(::TMMachineBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
@@ -117,18 +118,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val STRIPPED_APRICORN_WOOD = log("stripped_apricorn_wood")
|
||||
@JvmField
|
||||
val APRICORN_PLANKS = this.create("apricorn_planks",
|
||||
Block(
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS)
|
||||
.strength(2.0f, 3.0f).sound(SoundType.WOOD)
|
||||
)
|
||||
)
|
||||
val APRICORN_PLANKS = this.create("apricorn_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_LEAVES = leaves("apricorn_leaves")
|
||||
@JvmField
|
||||
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(APRICORN_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(APRICORN_PLANKS.defaultMapColor()).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(APRICORN_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
@@ -136,16 +132,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
|
||||
|
||||
@JvmField //if you wonder why we don't copy of OAK_WALL_SIGN, then I can tell you its because it uses Properties#dropsLike to point to the SIGN loot table, but we cant use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
|
||||
val APRICORN_WALL_SIGN = this.create(
|
||||
"apricorn_wall_sign",
|
||||
CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE)
|
||||
)
|
||||
@JvmField // If you wonder why we don't copy OAK_WALL_SIGN, then I can tell you it's because it uses Properties#dropsLike to point to the SIGN loot table, but we can't use that in our setup due to a deeper rooted issue, so falling back to this and writing dedicated (duplicated) loot tables
|
||||
val APRICORN_WALL_SIGN = this.create("apricorn_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_HANGING_SIGN = this.create(
|
||||
"apricorn_hanging_sign",
|
||||
CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE)
|
||||
)
|
||||
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
|
||||
@JvmField
|
||||
@@ -189,9 +179,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(
|
||||
MapColor.PLANT
|
||||
).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
.isValidSpawn { _: BlockState?, _: BlockGetter?, _: BlockPos?, entity: EntityType<*>? ->
|
||||
entity === EntityType.OCELOT || entity === EntityType.PARROT
|
||||
}.isSuffocating { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
@@ -205,7 +193,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD).ignitedByLava().forceSolidOn()))
|
||||
@JvmField
|
||||
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
|
||||
@JvmField
|
||||
@@ -248,31 +236,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@@ -281,6 +269,51 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = this.create("type_gem_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0f, 10.0f)))
|
||||
|
||||
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
|
||||
|
||||
@JvmField val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("type_gem_block_normal")
|
||||
@JvmField val TYPE_GEM_BLOCK_FIRE = typeGemBlock("type_gem_block_fire")
|
||||
@JvmField val TYPE_GEM_BLOCK_WATER = typeGemBlock("type_gem_block_water")
|
||||
@JvmField val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("type_gem_block_electric")
|
||||
@JvmField val TYPE_GEM_BLOCK_GRASS = typeGemBlock("type_gem_block_grass")
|
||||
@JvmField val TYPE_GEM_BLOCK_ICE = typeGemBlock("type_gem_block_ice")
|
||||
@JvmField val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("type_gem_block_fighting")
|
||||
@JvmField val TYPE_GEM_BLOCK_POISON = typeGemBlock("type_gem_block_poison")
|
||||
@JvmField val TYPE_GEM_BLOCK_GROUND = typeGemBlock("type_gem_block_ground")
|
||||
@JvmField val TYPE_GEM_BLOCK_FLYING = typeGemBlock("type_gem_block_flying")
|
||||
@JvmField val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("type_gem_block_psychic")
|
||||
@JvmField val TYPE_GEM_BLOCK_BUG = typeGemBlock("type_gem_block_bug")
|
||||
@JvmField val TYPE_GEM_BLOCK_ROCK = typeGemBlock("type_gem_block_rock")
|
||||
@JvmField val TYPE_GEM_BLOCK_GHOST = typeGemBlock("type_gem_block_ghost")
|
||||
@JvmField val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("type_gem_block_dragon")
|
||||
@JvmField val TYPE_GEM_BLOCK_DARK = typeGemBlock("type_gem_block_dark")
|
||||
@JvmField val TYPE_GEM_BLOCK_STEEL = typeGemBlock("type_gem_block_steel")
|
||||
@JvmField val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("type_gem_block_fairy")
|
||||
|
||||
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
|
||||
|
||||
@JvmField val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("type_gem_cluster_normal", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("type_gem_cluster_fire", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_WATER = typeGemCluster("type_gem_cluster_water", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("type_gem_cluster_electric", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("type_gem_cluster_grass", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ICE = typeGemCluster("type_gem_cluster_ice", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("type_gem_cluster_fighting", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_POISON = typeGemCluster("type_gem_cluster_poison", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("type_gem_cluster_ground", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("type_gem_cluster_flying", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("type_gem_cluster_psychic", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_BUG = typeGemCluster("type_gem_cluster_bug", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("type_gem_cluster_rock", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("type_gem_cluster_ghost", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("type_gem_cluster_dragon", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_DARK = typeGemCluster("type_gem_cluster_dark", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("type_gem_cluster_steel", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"))
|
||||
@JvmField val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("type_gem_cluster_fairy", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"))
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
|
||||
@JvmField
|
||||
@@ -454,7 +487,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
@JvmField
|
||||
val SOUL_CAMPFIRE = create("soul_campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{9}, true))
|
||||
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
||||
@JvmField
|
||||
@@ -490,9 +522,25 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
|
||||
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMMachineBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.COLOR_GREEN)
|
||||
.sound(SoundType.METAL)
|
||||
.pushReaction(PushReaction.BLOCK)
|
||||
.strength(2F)
|
||||
.noOcclusion()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@JvmField
|
||||
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val TM_SHELF = create("tm_shelf", TMShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
|
||||
|
||||
@@ -723,4 +771,28 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(SoundType.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
private fun typeGemBlock(name: String): Block {
|
||||
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0f, 6.0f)))
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemBlocks[identifier] = typeGemBlock
|
||||
|
||||
return typeGemBlock
|
||||
}
|
||||
|
||||
fun typeGemBlocks() = typeGemBlocks.toMap()
|
||||
|
||||
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
|
||||
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0f, 3.0f).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
|
||||
|
||||
TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
|
||||
|
||||
val identifier = cobblemonResource(name)
|
||||
typeGemClusters[identifier] = gemClusterBlock
|
||||
|
||||
return gemClusterBlock
|
||||
}
|
||||
|
||||
fun typeGemClusters() = typeGemClusters.toMap()
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
import net.minecraft.world.level.GameRules
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
@@ -56,6 +56,11 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val TM_MOVE: DataComponentType<TMMoveComponent> = create("tm_move", DataComponentType.builder<TMMoveComponent>()
|
||||
.persistent(TMMoveComponent.CODEC)
|
||||
.networkSynchronized(TMMoveComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
|
||||
.persistent(IngredientComponent.CODEC)
|
||||
.networkSynchronized(IngredientComponent.PACKET_CODEC)
|
||||
@@ -83,6 +88,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:tm_move"), TM_MOVE)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
|
||||
@@ -400,7 +400,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
|
||||
@JvmField
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, 0.28f, false)
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
|
||||
@JvmField
|
||||
@@ -457,6 +457,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
@JvmField
|
||||
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
|
||||
@JvmField
|
||||
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@@ -695,7 +699,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
|
||||
@JvmField
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
@@ -1191,6 +1195,95 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CORE = blockItem("type_gem_core", CobblemonBlocks.TYPE_GEM_CORE)
|
||||
/* @JvmField
|
||||
val TYPE_GEM_BLOCK = blockItem("type_gem_block", CobblemonBlocks.TYPE_GEM_BLOCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER = blockItem("type_gem_cluster", CobblemonBlocks.TYPE_GEM_CLUSTER)
|
||||
@JvmField
|
||||
val LARGE_TYPE_GEM = blockItem("large_type_gem", CobblemonBlocks.LARGE_TYPE_GEM)
|
||||
@JvmField
|
||||
val MEDIUM_TYPE_GEM = blockItem("medium_type_gem", CobblemonBlocks.MEDIUM_TYPE_GEM)
|
||||
@JvmField
|
||||
val SMALL_TYPE_GEM = blockItem("small_type_gem", CobblemonBlocks.SMALL_TYPE_GEM)*/
|
||||
/*
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_NORMAL = blockItem("type_gem_cluster_normal", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIRE = blockItem("type_gem_cluster_fire", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_WATER = blockItem("type_gem_cluster_water", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ELECTRIC = blockItem("type_gem_cluster_electric", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GRASS = blockItem("type_gem_cluster_grass", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ICE = blockItem("type_gem_cluster_ice", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FIGHTING = blockItem("type_gem_cluster_fighting", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_POISON = blockItem("type_gem_cluster_poison", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GROUND = blockItem("type_gem_cluster_ground", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FLYING = blockItem("type_gem_cluster_flying", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_PSYCHIC = blockItem("type_gem_cluster_psychic", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_BUG = blockItem("type_gem_cluster_bug", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_ROCK = blockItem("type_gem_cluster_rock", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_GHOST = blockItem("type_gem_cluster_ghost", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DRAGON = blockItem("type_gem_cluster_dragon", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_DARK = blockItem("type_gem_cluster_dark", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_STEEL = blockItem("type_gem_cluster_steel", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_CLUSTER_FAIRY = blockItem("type_gem_cluster_fairy", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY)
|
||||
*/
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_NORMAL = blockItem("type_gem_block_normal", CobblemonBlocks.TYPE_GEM_BLOCK_NORMAL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIRE = blockItem("type_gem_block_fire", CobblemonBlocks.TYPE_GEM_BLOCK_FIRE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_WATER = blockItem("type_gem_block_water", CobblemonBlocks.TYPE_GEM_BLOCK_WATER)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ELECTRIC = blockItem("type_gem_block_electric", CobblemonBlocks.TYPE_GEM_BLOCK_ELECTRIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GRASS = blockItem("type_gem_block_grass", CobblemonBlocks.TYPE_GEM_BLOCK_GRASS)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ICE = blockItem("type_gem_block_ice", CobblemonBlocks.TYPE_GEM_BLOCK_ICE)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FIGHTING = blockItem("type_gem_block_fighting", CobblemonBlocks.TYPE_GEM_BLOCK_FIGHTING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_POISON = blockItem("type_gem_block_poison", CobblemonBlocks.TYPE_GEM_BLOCK_POISON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GROUND = blockItem("type_gem_block_ground", CobblemonBlocks.TYPE_GEM_BLOCK_GROUND)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FLYING = blockItem("type_gem_block_flying", CobblemonBlocks.TYPE_GEM_BLOCK_FLYING)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_PSYCHIC = blockItem("type_gem_block_psychic", CobblemonBlocks.TYPE_GEM_BLOCK_PSYCHIC)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_BUG = blockItem("type_gem_block_bug", CobblemonBlocks.TYPE_GEM_BLOCK_BUG)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_ROCK = blockItem("type_gem_block_rock", CobblemonBlocks.TYPE_GEM_BLOCK_ROCK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_GHOST = blockItem("type_gem_block_ghost", CobblemonBlocks.TYPE_GEM_BLOCK_GHOST)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DRAGON = blockItem("type_gem_block_dragon", CobblemonBlocks.TYPE_GEM_BLOCK_DRAGON)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_DARK = blockItem("type_gem_block_dark", CobblemonBlocks.TYPE_GEM_BLOCK_DARK)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_STEEL = blockItem("type_gem_block_steel", CobblemonBlocks.TYPE_GEM_BLOCK_STEEL)
|
||||
@JvmField
|
||||
val TYPE_GEM_BLOCK_FAIRY = blockItem("type_gem_block_fairy", CobblemonBlocks.TYPE_GEM_BLOCK_FAIRY)
|
||||
|
||||
@JvmField
|
||||
val POKEROD_SMITHING_TEMPLATE = create("pokerod_smithing_template", SmithingTemplateItem(
|
||||
Component.translatable("item.minecraft.fishing_rod").blue(),
|
||||
@@ -1415,44 +1508,49 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
@JvmField
|
||||
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Properties()))
|
||||
@JvmField
|
||||
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Properties()))
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
val NORMAL_GEM = noSettingsItem("normal_gem")
|
||||
val NORMAL_GEM = this.create("normal_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL, Item.Properties()))
|
||||
@JvmField
|
||||
val FIRE_GEM = noSettingsItem("fire_gem")
|
||||
val FIRE_GEM = this.create("fire_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE, Item.Properties()))
|
||||
@JvmField
|
||||
val WATER_GEM = noSettingsItem("water_gem")
|
||||
val WATER_GEM = this.create("water_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_WATER, Item.Properties()))
|
||||
@JvmField
|
||||
val GRASS_GEM = noSettingsItem("grass_gem")
|
||||
val GRASS_GEM = this.create("grass_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS, Item.Properties()))
|
||||
@JvmField
|
||||
val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
||||
val ELECTRIC_GEM = this.create("electric_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC, Item.Properties()))
|
||||
@JvmField
|
||||
val ICE_GEM = noSettingsItem("ice_gem")
|
||||
val ICE_GEM = this.create("ice_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ICE, Item.Properties()))
|
||||
@JvmField
|
||||
val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
||||
val FIGHTING_GEM = this.create("fighting_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING, Item.Properties()))
|
||||
@JvmField
|
||||
val POISON_GEM = noSettingsItem("poison_gem")
|
||||
val POISON_GEM = this.create("poison_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_POISON, Item.Properties()))
|
||||
@JvmField
|
||||
val GROUND_GEM = noSettingsItem("ground_gem")
|
||||
val GROUND_GEM = this.create("ground_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND, Item.Properties()))
|
||||
@JvmField
|
||||
val FLYING_GEM = noSettingsItem("flying_gem")
|
||||
val FLYING_GEM = this.create("flying_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING, Item.Properties()))
|
||||
@JvmField
|
||||
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
||||
val PSYCHIC_GEM = this.create("psychic_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC, Item.Properties()))
|
||||
@JvmField
|
||||
val BUG_GEM = noSettingsItem("bug_gem")
|
||||
val BUG_GEM = this.create("bug_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_BUG, Item.Properties()))
|
||||
@JvmField
|
||||
val ROCK_GEM = noSettingsItem("rock_gem")
|
||||
val ROCK_GEM = this.create("rock_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK, Item.Properties()))
|
||||
@JvmField
|
||||
val GHOST_GEM = noSettingsItem("ghost_gem")
|
||||
val GHOST_GEM = this.create("ghost_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST, Item.Properties()))
|
||||
@JvmField
|
||||
val DRAGON_GEM = noSettingsItem("dragon_gem")
|
||||
val DRAGON_GEM = this.create("dragon_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON, Item.Properties()))
|
||||
@JvmField
|
||||
val DARK_GEM = noSettingsItem("dark_gem")
|
||||
val DARK_GEM = this.create("dark_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_DARK, Item.Properties()))
|
||||
@JvmField
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
val STEEL_GEM = this.create("steel_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL, Item.Properties()))
|
||||
@JvmField
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
val FAIRY_GEM = this.create("fairy_gem", TypeGemItem(CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY, Item.Properties()))
|
||||
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
@@ -19,7 +20,8 @@ import net.minecraft.world.inventory.MenuType
|
||||
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
|
||||
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
|
||||
@@ -28,9 +28,7 @@ import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.*
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
|
||||
@@ -132,9 +130,7 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotification
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.*
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
@@ -151,6 +147,7 @@ import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -170,11 +167,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
@@ -202,6 +195,7 @@ import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.TMMachineTeachMoveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -222,6 +216,7 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
@@ -553,6 +548,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Block entity
|
||||
list.add(PacketRegisterInfo(AdjustBlockEntityViewerCountPacket.ID, AdjustBlockEntityViewerCountPacket::decode, AdjustBlockEntityViewerCountHandler))
|
||||
list.add(PacketRegisterInfo(TMMachineTeachMovePacket.ID, TMMachineTeachMovePacket::decode, TMMachineTeachMoveHandler))
|
||||
|
||||
// Move select packets
|
||||
list.add(PacketRegisterInfo(MoveSelectedPacket.ID, MoveSelectedPacket::decode, MoveSelectedHandler))
|
||||
@@ -584,6 +580,10 @@ object CobblemonNetwork {
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
// TM Machine
|
||||
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
|
||||
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
|
||||
|
||||
// Behaviour Packets
|
||||
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
|
||||
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
|
||||
|
||||
@@ -195,6 +195,22 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("block.tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val TM_USE = this.create("item.tm.use")
|
||||
@JvmField
|
||||
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
|
||||
@JvmField
|
||||
val TM_MACHINE_OFF = this.create("block.tm_machine.off")
|
||||
@JvmField
|
||||
val TM_MACHINE_ON = this.create("block.tm_machine.on")
|
||||
@JvmField
|
||||
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
|
||||
@JvmField
|
||||
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
|
||||
@JvmField
|
||||
val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
|
||||
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_BREAK = this.create("block.evolution_stone_block.break")
|
||||
@JvmField
|
||||
|
||||
+3
-39
@@ -14,7 +14,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Optional
|
||||
import java.util.*
|
||||
|
||||
class PlantTumblestoneContext(val pos: BlockPos, val block: TumblestoneBlock)
|
||||
|
||||
@@ -29,42 +29,6 @@ class PlantTumblestoneCriterion(
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
return context.block.canGrow(context.pos, player.level())
|
||||
return context.block.canGrow(player.level().getBlockState(context.pos), context.pos, player.level())
|
||||
}
|
||||
}
|
||||
|
||||
///**
|
||||
// * A criterion condition for when a tumblestone block is planted
|
||||
// * @param id The identifier of the criterion
|
||||
// * @param predicate The predicate for the criterion
|
||||
// *
|
||||
// * @author Aethen
|
||||
// * @since 02/28/2024
|
||||
// */
|
||||
//class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
// SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
// override fun toJson(json: JsonObject) {
|
||||
// // Add properties to json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// override fun fromJson(json: JsonObject) {
|
||||
// // Parse properties from json if needed. None needed for this criterion
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Checks if the tumblestone block can grow at the given position
|
||||
// * @param player The player that planted the tumblestone block
|
||||
// * @param context The context of the criterion
|
||||
// * @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
// */
|
||||
// override fun matches(player: ServerPlayer, context: PlantTumblestoneContext): Boolean {
|
||||
// return context.tumbleStoneBlock.canGrow(context.pos, player.level())
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * The context of the [PlantTumblestoneCriterionCondition]
|
||||
// * @param pos The position of the tumblestone block
|
||||
// * @param tumbleStoneBlock The tumblestone block
|
||||
// */
|
||||
//open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"activity": "minecraft:idle",
|
||||
"tasksByPriority": {
|
||||
"1": [
|
||||
"heal_using_healing_machine",
|
||||
|
||||
+2
-1
@@ -53,7 +53,8 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
|
||||
it is PokemonEntity &&
|
||||
it != currentLeader &&
|
||||
it.pokemon.storeCoordinates.get()?.store !is PartyStore &&
|
||||
it.getHerdSize() < it.behaviour.herd.maxSize
|
||||
it.getHerdSize() < it.behaviour.herd.maxSize &&
|
||||
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
|
||||
}.forEach { possibleLeader ->
|
||||
possibleLeader as PokemonEntity
|
||||
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
|
||||
|
||||
+18
-26
@@ -9,44 +9,36 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
class FleeAttackerTaskConfig : SingleTaskConfig {
|
||||
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
|
||||
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
|
||||
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
|
||||
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
speedMultiplier,
|
||||
desiredDistance
|
||||
).asVariables()
|
||||
override fun getVariables(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): List<MoLangConfigVariable> {
|
||||
return listOf(avoidDurationTicks).asVariables()
|
||||
}
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
|
||||
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
|
||||
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
): BehaviorControl<in LivingEntity> {
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.HURT_BY_ENTITY,
|
||||
MemoryModuleType.AVOID_TARGET
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
|
||||
}
|
||||
}
|
||||
@@ -162,11 +162,11 @@ on the entity type and this piggy-backs off the Minecraft list of aggressive mob
|
||||
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### flee_attacker
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
|
||||
### walk_away_from_avoid_target
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its avoid target.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the avoid target. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the target that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### flee_nearest_hostile
|
||||
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
|
||||
|
||||
@@ -78,8 +78,9 @@ interface TaskConfig {
|
||||
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
|
||||
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
|
||||
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
|
||||
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
|
||||
cobblemonResource("walk_away_from_avoid_target") to WalkAwayFromAvoidTargetTaskConfig::class.java,
|
||||
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
|
||||
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
|
||||
cobblemonResource("fly_in_circles") to FlyInCirclesTaskConfig::class.java,
|
||||
cobblemonResource("run_script") to RunScript::class.java,
|
||||
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
|
||||
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
class WalkAwayFromAvoidTargetTaskConfig : SingleTaskConfig {
|
||||
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
|
||||
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.7).asExpressible()
|
||||
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition,
|
||||
speedMultiplier,
|
||||
desiredDistance
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
|
||||
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
|
||||
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
|
||||
|
||||
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,12 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val TRADE_EVENT_POST = EventObservable<TradeEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val RIDE_EVENT_PRE = CancelableObservable<RidePokemonEvent.Pre>()
|
||||
|
||||
@JvmField
|
||||
val RIDE_EVENT_POST = EventObservable<RidePokemonEvent.Post>()
|
||||
|
||||
@JvmField
|
||||
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
|
||||
|
||||
|
||||
@@ -8,11 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -22,10 +20,12 @@ import net.minecraft.world.item.ItemStack
|
||||
* @param bait The ItemStack of the bait that is set on the rod.
|
||||
*/
|
||||
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
val context = server()?.let { server ->
|
||||
mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server.registryAccess())
|
||||
)
|
||||
} ?: emptyMap()
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* The base for all events related to riding a Pokemon.
|
||||
*
|
||||
* @see [Pre]
|
||||
* @see [Post]
|
||||
*/
|
||||
interface RidePokemonEvent {
|
||||
val player: ServerPlayer
|
||||
val pokemon: PokemonEntity
|
||||
|
||||
class Pre(
|
||||
override val player: ServerPlayer,
|
||||
override val pokemon: PokemonEntity
|
||||
) : RidePokemonEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
class Post(
|
||||
override val player: ServerPlayer,
|
||||
override val pokemon: PokemonEntity
|
||||
) : RidePokemonEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@ import com.cobblemon.mod.common.api.pokemon.experience.SidemodExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.riding.Rideable
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
@@ -488,27 +489,20 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("spawn_npc") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val npcClass = params.getString(3)
|
||||
|
||||
val pos = BlockPos(x, y, z)
|
||||
|
||||
if (!Level.isInSpawnableBounds(pos)) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val npc = NPCEntity(world) ?: return@put DoubleValue.ZERO
|
||||
npc.moveTo(pos, npc.yRot, npc.xRot)
|
||||
npc.npc = NPCClasses.getByIdentifier(npcClass.asIdentifierDefaultingNamespace()) ?: return@put DoubleValue.ZERO
|
||||
npc.initialize(1)
|
||||
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val npcClass = params.getStringOrNull(3)?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
val level = params.getInt(4)
|
||||
if(npcClass == null) return@put DoubleValue.ZERO
|
||||
val npc = NPCEntity(world)
|
||||
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
|
||||
npc.npc = npcClass
|
||||
npc.initialize(level)
|
||||
if (world.addFreshEntity(npc)) {
|
||||
return@put npc.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
return@put npc.asMoLangValue()
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("play_sound_on_server") { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
@@ -682,13 +676,13 @@ object MoLangFunctions {
|
||||
map.put("party") { player.party().struct }
|
||||
map.put("pc") { player.pc().struct }
|
||||
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
|
||||
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
|
||||
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
|
||||
map.put("data") { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
|
||||
map.put("save_data") { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
|
||||
map.put("in_battle") { DoubleValue(player.isInBattle()) }
|
||||
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map.put("get_npc_data") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put data.map[npcId]!!
|
||||
} else {
|
||||
@@ -698,9 +692,9 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("get_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
@@ -708,15 +702,16 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
map.put("set_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val path = params.getStringOrNull(4)
|
||||
val data = Cobblemon.molangData.load(player.uuid, path)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid)
|
||||
Cobblemon.molangData.save(player.uuid, path)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
@@ -999,6 +994,9 @@ object MoLangFunctions {
|
||||
|
||||
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
|
||||
}
|
||||
map.put("discard") {
|
||||
entity.discard()
|
||||
}
|
||||
if (entity is PosableEntity) {
|
||||
map.put("play_animation") { params ->
|
||||
val animation = params.getString(0)
|
||||
@@ -1585,6 +1583,13 @@ object MoLangFunctions {
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("ride_boosts") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in RidingStat.entries) {
|
||||
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat) ?: 0.0) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("natdex_number") {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
@@ -1790,6 +1795,21 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_ride_boost") { params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
|
||||
|
||||
pokemon.setRideBoost(stat, value)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("add_ride_boost") { params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) } ?: return@put DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@put DoubleValue.ZERO
|
||||
|
||||
return@put DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
|
||||
}
|
||||
map.put("initialize_moveset") { params ->
|
||||
val preferLatest = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.initializeMoveset(preferLatest)
|
||||
@@ -2124,12 +2144,17 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
map.put("data") { params ->
|
||||
val data = Cobblemon.molangData.load(UUID(0L, 0L))
|
||||
return@put data
|
||||
Cobblemon.molangData.load(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(0)
|
||||
)
|
||||
}
|
||||
|
||||
map.put("save_data") {
|
||||
Cobblemon.molangData.save(UUID(0L, 0L))
|
||||
map.put("save_data") { params ->
|
||||
Cobblemon.molangData.save(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(index = 0)
|
||||
)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ class MoveSet : Iterable<Move> {
|
||||
*/
|
||||
fun getMoves() = moves.filterNotNull()
|
||||
fun getMovesWithNulls() = moves.toList()
|
||||
fun getMoveTemplates() = getMoves().map { move -> move.template }
|
||||
fun hasSpace() = moves.any { it == null }
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokedex
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -21,6 +24,7 @@ import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.UUID
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
class PokedexManager(
|
||||
override val uuid: UUID,
|
||||
|
||||
@@ -464,7 +464,7 @@ open class PokemonProperties {
|
||||
val stack = ItemStack(result.item)
|
||||
stack.applyComponents(result.components)
|
||||
if (stack.isEmpty) return@let
|
||||
pokemon.swapHeldItem(stack)
|
||||
pokemon.swapHeldItem(stack, decrement = true, aiCanDrop = false)
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
@@ -20,7 +21,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
class Interpreter(
|
||||
val loadMove: (JsonElement, Learnset) -> Boolean
|
||||
val loadMove: (JsonElement, Learnset) -> Boolean
|
||||
) {
|
||||
companion object {
|
||||
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
|
||||
@@ -28,10 +29,10 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
val str = element.takeIf { it.isJsonPrimitive }?.asString ?: return@Interpreter false
|
||||
if (str.startsWith(prefix)) {
|
||||
Moves.getByName(str.substringAfter(":"))
|
||||
?.let {
|
||||
list(learnset).add(it)
|
||||
return@Interpreter true
|
||||
}
|
||||
?.let {
|
||||
list(learnset).add(it)
|
||||
return@Interpreter true
|
||||
}
|
||||
}
|
||||
return@Interpreter false
|
||||
}
|
||||
@@ -69,13 +70,12 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
}
|
||||
|
||||
val interpreters = mutableListOf(
|
||||
tmInterpreter,
|
||||
eggInterpreter,
|
||||
tutorInterpreter,
|
||||
legacyInterpreter,
|
||||
specialInterpreter,
|
||||
levelUpInterpreter,
|
||||
formChangeInterpreter
|
||||
tmInterpreter,
|
||||
eggInterpreter,
|
||||
tutorInterpreter,
|
||||
legacyInterpreter,
|
||||
specialInterpreter,levelUpInterpreter,
|
||||
formChangeInterpreter
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,11 +93,25 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
val formChangeMoves = mutableListOf<MoveTemplate>()
|
||||
|
||||
fun getLevelUpMovesUpTo(level: Int) = levelUpMoves
|
||||
.entries
|
||||
.filter { it.key <= level }
|
||||
.sortedBy { it.key }
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
.entries
|
||||
.filter { it.key <= level }
|
||||
.sortedBy { it.key }
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
|
||||
fun tmLearnableMoves(): MutableList<MoveTemplate> {
|
||||
val moves = mutableListOf<MoveTemplate>()
|
||||
|
||||
moves.addAll(tmMoves)
|
||||
moves.addAll(tutorMoves)
|
||||
moves.addAll(eggMoves)
|
||||
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
|
||||
moves.addAll(evolutionMoves)
|
||||
moves.addAll(formChangeMoves)
|
||||
|
||||
return moves
|
||||
}
|
||||
|
||||
|
||||
fun getAllLegalMoves(): Set<MoveTemplate> {
|
||||
return levelUpMoves.values.flatten().toSet() +
|
||||
|
||||
+14
-4
@@ -62,10 +62,19 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
}
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
@@ -448,8 +457,9 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val hoverSpeed = 0.1
|
||||
|
||||
// Yaw locally when at or below hoverSpeed
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = if (currSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
|
||||
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
@@ -463,8 +473,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
|
||||
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt((RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))) + pitchInfluencedRollCorrection
|
||||
val pitchInfluencedRollCorrection = 0.0 // if ( currSpeed > hoverSpeed * 2) 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0) else 0.0
|
||||
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
|
||||
//yaw, pitch, roll
|
||||
|
||||
+83
-81
@@ -87,30 +87,17 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
checkTooHigh(settings, state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
fun checkTooHigh(
|
||||
override fun tick(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val pos = vehicle.position()
|
||||
val level = Minecraft.getInstance().player?.level() ?: return
|
||||
val hit = level.clip(
|
||||
ClipContext(
|
||||
pos,
|
||||
pos.subtract(0.0, heightLimit, 0.0),
|
||||
ClipContext.Block.COLLIDER,
|
||||
ClipContext.Fluid.NONE,
|
||||
vehicle
|
||||
)
|
||||
)
|
||||
|
||||
state.tooHigh.set(hit.type == HitResult.Type.MISS)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
@@ -121,8 +108,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
val stam = state.stamina.get()
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
|
||||
val newStam = if (state.tooHigh.get()) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamDrainRate * 2)
|
||||
val newStam = min(1.0f,stam + stamDrainRate)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -142,11 +128,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -157,9 +139,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(driver.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -174,26 +154,19 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
//In degrees per second
|
||||
val handling = 220.0
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
val rotDiffInfl = sqrt(abs(rotDiff / maxYawDiff)) * rotDiff.sign
|
||||
|
||||
//Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
//y and driver y
|
||||
//This influences the speed of the turn based on how far in one direction you're looking
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
var turnRate = (handling.toFloat()) * rotDiffInfl
|
||||
turnRate -= (turnRate * 0.5f) * (RidingBehaviour.scaleToRange(vehicle.deltaMovement.length(), 0.0, topSpeed).coerceIn(0.0, 1.0)).pow(2).toFloat()
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
val turnRate = (handling.toFloat() / 20.0f)
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
// Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
@@ -218,9 +191,9 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
): Vec3 {
|
||||
// Convert these to their tick values
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
var newVelocity = state.rideVelocity.get()
|
||||
|
||||
// Check for collisions and if found, update the speed to reflect it
|
||||
if (vehicle.verticalCollision || vehicle.horizontalCollision) {
|
||||
@@ -230,59 +203,75 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
// align the velocity vector to be in local vehicle space
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
if (newVelocity.length() < 0.001) {
|
||||
// Zero out the vector to ignore lingering small values that influence
|
||||
// direction of movement when scaled by stored speed value.
|
||||
newVelocity = Vec3.ZERO
|
||||
}
|
||||
newVelocity = newVelocity.normalize().scale(state.speed.get())
|
||||
|
||||
// Vertical movement based on driver input.
|
||||
val vertInput = when {
|
||||
driver.jumping && state.stamina.get() != 0.0f -> 1.0
|
||||
driver.jumping -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
// Normalize and scale to accel so that diagonal movement
|
||||
// doesn't speed up the ride more than input in one direction
|
||||
val inputVel = Vec3(
|
||||
var inputVel = Vec3(
|
||||
driver.xxa.sign.toDouble(),
|
||||
vertInput,
|
||||
driver.zza.sign.toDouble()
|
||||
).normalize().scale(accel)
|
||||
).normalize()
|
||||
|
||||
// Scale down input force that opposes our current movement. How much it is scaled
|
||||
// down depends on our handling/skill
|
||||
// val handlingMod = if(newVelocity.length() > 0.001) 1 - handling*sqrt(newVelocity.length() / topSpeed)*( 0.5 * (1 + newVelocity.normalize().dot(inputVel.normalize()) * -1.0))
|
||||
// else 1.0
|
||||
/******************************************************
|
||||
* Boosting Logic
|
||||
*****************************************************/
|
||||
if (driver.level().isClientSide) {
|
||||
if(Minecraft.getInstance().options.keySprint.isDown() && state.stamina.get() == 1.0f && !state.isBoosting.get()) {
|
||||
state.isBoosting.set(true)
|
||||
val localBoostVel = if (inputVel.horizontalDistance() == 0.0) Vec3(0.0, 0.0, 1.0) else Vec3(inputVel.x, 0.0, inputVel.z)
|
||||
state.boostVec.set(localBoostVel)
|
||||
state.boostTicks.set(0)
|
||||
state.stamina.set(0.0f)
|
||||
}
|
||||
}
|
||||
val maxBoostTicks = 20
|
||||
val boostPower = (topSpeed * 20) * 0.1 * vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
if (state.isBoosting.get() && state.boostTicks.get() < maxBoostTicks) {
|
||||
newVelocity = newVelocity.add(state.boostVec.get().scale(boostPower / maxBoostTicks))
|
||||
state.boostTicks.set(state.boostTicks.get() + 1)
|
||||
} else if (state.isBoosting.get()) {
|
||||
state.isBoosting.set(false)
|
||||
}
|
||||
|
||||
newVelocity = newVelocity.add(inputVel)
|
||||
|
||||
// Air Friction/Resistance
|
||||
// When the dot between friction and input is -1 we want to apply none of it.
|
||||
// In short we want to avoid applying friction that opposes our input force.
|
||||
// otherwise we would not be able to reach our top speed when acceleration is
|
||||
// low.
|
||||
val frictionConst = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val frictionConst = 0.02
|
||||
var frictionForce = newVelocity.scale(-1.0).scale(frictionConst)
|
||||
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - 0.5 * (1 + -1.0 * frictionForce.normalize().dot(inputVel.normalize()))
|
||||
val frictionApplied = if(inputVel.lengthSqr() != 0.0) 1 - (0.5*(1 + -1.0 * (inputVel.normalize().dot(frictionForce.normalize()))))
|
||||
else 1.0
|
||||
frictionForce = frictionForce.scale((frictionApplied).pow(4))
|
||||
frictionForce = frictionForce.scale((frictionApplied))
|
||||
newVelocity = newVelocity.add(frictionForce)
|
||||
|
||||
// Apply height limit forces
|
||||
if (state.tooHigh.get()) {
|
||||
newVelocity = newVelocity.scale(0.96)
|
||||
if (state.stamina.get() == 0.0f) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.01, 0.0)
|
||||
}
|
||||
}
|
||||
// Ensure that if you're not applying force where the ride is facing then you apply a portion of debuff
|
||||
val strafeDebuff = 0.5
|
||||
val vehicleForwardVec = Vec3(0.0, 0.0, 1.0)
|
||||
val playerForwardVec = inputVel //.yRot(vehicle.yRot)
|
||||
val inputStrength = accel - (accel * strafeDebuff*(1 - vehicleForwardVec.dot(playerForwardVec).coerceIn(0.0, 1.0)))
|
||||
inputVel = inputVel.scale(inputStrength)
|
||||
|
||||
newVelocity = newVelocity.add(inputVel)
|
||||
|
||||
// Set speed to new magnitude to avoid the horrible things minecraft friction
|
||||
// does to our vectors :(
|
||||
state.speed.set(min(topSpeed, newVelocity.length()))
|
||||
|
||||
// Check to see if this new velocity will exceed top speed and if it will then cap it
|
||||
newVelocity = if (newVelocity.length() > topSpeed) newVelocity.normalize().scale(topSpeed) else newVelocity
|
||||
|
||||
val revertYawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
|
||||
state.rideVelocity.set((newVelocity.toVector3f().mul(revertYawAligned)).toVec3d())
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
@@ -355,7 +344,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return if (state.tooHigh.get()) 0.9f else 1.0f
|
||||
return if (state.isBoosting.get()) 1.1f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
@@ -445,21 +434,21 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 13.0, 6.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Acceleration in blocks per second per second
|
||||
// Acceleration in number of seconds to top speed
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', 7.0, 3.0)".asExpression()
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Amount of seconds before stamina depletion
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7, 3)".asExpression()
|
||||
// Amount of seconds between boosts
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 1, 4)".asExpression()
|
||||
private set
|
||||
|
||||
// Height in blocks above the ground before stamina loss
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 5.0)".asExpression()
|
||||
// Power of boost as a fraction of top speed
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.2, 0.2)".asExpression()
|
||||
private set
|
||||
|
||||
// Amount of air friction applied. 0.2 means 20 percent of the velocity is opposed per tick
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.1, 0.02)".asExpression()
|
||||
// Turn rate in degrees per second when stationary
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 360.0, 90)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
@@ -489,28 +478,41 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
|
||||
class HoverState : RidingBehaviourState() {
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var tooHigh = ridingState(false, Side.CLIENT)
|
||||
var isBoosting = ridingState(false, Side.BOTH)
|
||||
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
var boostTicks = ridingState(0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(speed.get())
|
||||
buffer.writeBoolean(tooHigh.get())
|
||||
buffer.writeBoolean(isBoosting.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
speed.set(buffer.readDouble(), forced = true)
|
||||
tooHigh.set(buffer.readBoolean(), forced = true)
|
||||
isBoosting.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
speed.set(0.0, forced = true)
|
||||
isBoosting.set(false, forced = true)
|
||||
boostVec.set(Vec3.ZERO, forced = true)
|
||||
boostTicks.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HoverState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.tooHigh.set(this.tooHigh.get(), forced = true)
|
||||
it.isBoosting.set(this.isBoosting.get(), forced = true)
|
||||
it.boostVec.set(this.boostVec.get(), forced = true)
|
||||
it.boostTicks.set(this.boostTicks.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is HoverState) return false
|
||||
if (previous.isBoosting.get() != isBoosting.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -70,11 +70,14 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player, input: Vec3) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
|
||||
+51
-27
@@ -81,13 +81,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and calculate sprinting and inAir state here
|
||||
handleSprinting(settings, state, vehicle, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
handleSprinting(state, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -100,16 +108,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
fun handleSprinting(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
if (state.sprinting.get()) {
|
||||
@@ -125,7 +130,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
var newStam = stam
|
||||
@@ -228,22 +232,22 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
): Vec3 {
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr) * 0.75
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
|
||||
var activeInput = false
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and set the horizontal
|
||||
* fields of the velocity vector to the magnitude of the
|
||||
* ones from deltaMovement. This is important as when a
|
||||
* collision happens the Deltamovement will reflect it
|
||||
* but rideVelocity won't.
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
|
||||
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
|
||||
if (vehicle.horizontalCollision) {
|
||||
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
@@ -282,12 +286,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
if (vehicle.onGround()) { newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z) }
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
} else if(vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
}
|
||||
|
||||
val fallingForce = gravity
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
|
||||
/******************************************************
|
||||
* Ground Friction
|
||||
@@ -299,8 +305,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
/******************************************************
|
||||
* Jump Logic
|
||||
*****************************************************/
|
||||
if (driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1) {
|
||||
newVelocity = Vec3(newVelocity.x, jumpForce, newVelocity.z)
|
||||
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
|
||||
// Spread out jumpforce so that variable height jumps are possible
|
||||
val jumpInputTicks = 6
|
||||
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
|
||||
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
} else {
|
||||
// Set delay before next jump is possible after hitting ground
|
||||
val tickJumpDelay = 3
|
||||
state.jumpTicks.set(-tickJumpDelay)
|
||||
}
|
||||
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
|
||||
// Tick off the delay once reaching the ground
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
}
|
||||
|
||||
//Zero out lateral velocity possibly picked up from a controller transition
|
||||
@@ -493,15 +512,15 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 0.8)), (1.0 / (20.0 * 3.0)))".asExpression()
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', 0.5, 2.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.5, 0.8)".asExpression()
|
||||
//Between a one block jump and a six block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.2, 0.45)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
|
||||
@@ -538,23 +557,27 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
class HorseState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
var jumpTicks = ridingState(0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(sprinting.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
buffer.writeInt(jumpTicks.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
jumpTicks.set(buffer.readInt(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
jumpTicks.set(0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = HorseState().also {
|
||||
@@ -562,6 +585,7 @@ class HorseState : RidingBehaviourState() {
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+16
-9
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
@@ -43,8 +42,10 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
abs(state.rideVelocity.get().horizontalDistance()) > 0.2
|
||||
})
|
||||
|
||||
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
if (state.jumpBuffer.get() != -1) {
|
||||
@@ -297,11 +298,8 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
if (state.rideVelocity.get().z > topSpeed * 0.8) {
|
||||
return Mth.lerp((state.rideVelocity.get().z - (topSpeed * 0.8)) / (topSpeed * 0.2), 1.0, 1.2).toFloat()
|
||||
}
|
||||
return 1.0f
|
||||
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier)
|
||||
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -314,7 +312,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
val isInWater = Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
return if (!isInWater) cobblemonResource("in_air") else cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
|
||||
@@ -395,6 +400,8 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
var sprintSpeedModifier = "1.5".asExpression()
|
||||
private set
|
||||
|
||||
var sprintFovModifier = "1.2".asExpression()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
|
||||
+73
-42
@@ -16,12 +16,6 @@ import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketState
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
@@ -29,11 +23,13 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -42,6 +38,7 @@ import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.hypot
|
||||
import kotlin.math.max
|
||||
@@ -74,18 +71,28 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
override fun speed(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
handleBoosting(state)
|
||||
handleBoosting(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
|
||||
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
if (state.stamina.get() >= 0.25f) {
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
@@ -145,18 +152,42 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr) else 1.0
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0) * boostMod
|
||||
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)) * boostMod
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
|
||||
/*********************************************
|
||||
* Breaching logic
|
||||
*********************************************/
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
val currVel = state.lastVelocity.get()
|
||||
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z))
|
||||
val breachBoost = 1.5
|
||||
if (!state.hasBreached.get()) {
|
||||
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
|
||||
// fun
|
||||
if (state.boosting.get()) {
|
||||
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
|
||||
}
|
||||
state.hasBreached.set(true)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
|
||||
val terminalVel = 2.0
|
||||
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
|
||||
}
|
||||
return state.lastVelocity.get()
|
||||
} else {
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
state.hasBreached.set(false)
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Handle collisions
|
||||
*********************************************/
|
||||
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
|
||||
state.speed.set(vehicle.deltaMovement.length())
|
||||
// not actually friction
|
||||
val frictionLimit = 0.1
|
||||
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
|
||||
state.speed.set(postColSpeed)
|
||||
}
|
||||
|
||||
val currSpeed = state.speed.get()
|
||||
val strafeFactor = runtime.resolveDouble(settings.strafeFactor)
|
||||
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor)
|
||||
@@ -178,9 +209,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
|
||||
//val inpVel = if (desiredVec.lengthSqr() > 0.001) desiredVec.normalize().scale(0.1) else Vec3.ZERO
|
||||
return vehicle.deltaMovement.add(desiredVec)
|
||||
//return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -207,7 +236,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
|
||||
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
@@ -218,16 +247,19 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
controller: OrientationController,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
|
||||
val maxRollCorrectionRate = 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
|
||||
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
if(state.noInputTime.get() > rollCorrectionTimer) {
|
||||
|
||||
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
@@ -249,12 +281,14 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = 10.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0
|
||||
val pitchArrivalDeg = 90.0
|
||||
if(state.noInputTime.get() > rollCorrectionTimer) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
|
||||
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
val pitchArrivalDeg = 45.0
|
||||
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
|
||||
|
||||
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
|
||||
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
@@ -288,7 +322,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
): Vec3 {
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (!vehicle.isInWater && !vehicle.isUnderWater)) {
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1 && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
state.noInputTime.set(state.noInputTime.get() + deltaTime)
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
@@ -339,7 +373,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * boostingHandleDebuff
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
|
||||
//Smooth out mouse input.
|
||||
@@ -384,7 +419,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult) * boostingHandleDebuff
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
@@ -393,11 +429,11 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val hoverSpeed = topSpeed * 0.5
|
||||
|
||||
// Yaw locally when at or below hoverSpeed
|
||||
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0)
|
||||
//else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f)
|
||||
// Yaw locally
|
||||
// Dampen yaw when rolled or pitched substantially
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
@@ -411,7 +447,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0)
|
||||
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
|
||||
|
||||
//yaw, pitch, roll
|
||||
@@ -452,7 +487,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
|
||||
return 0.0 //if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
@@ -461,14 +496,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.boosting.get()) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
|
||||
val boostMod = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.speed.get(), topSpeed, topSpeed * boostMod)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
return 1.0f
|
||||
}
|
||||
return if (state.boosting.get()) 1.2f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -548,7 +576,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LIQUID', 2.2, 1.2)".asExpression()
|
||||
// Yaw rate in degrees per second
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 100.0, 45.0)".asExpression()
|
||||
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.4, 0.2)".asExpression()
|
||||
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.3, 0.2)".asExpression()
|
||||
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 1.2, 0.8)".asExpression()
|
||||
// Top speed in Bl/s
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LIQUID', 15.0, 5.0)".asExpression()
|
||||
@@ -599,6 +627,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.CLIENT)
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var hasBreached = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -619,6 +648,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(true, forced = true)
|
||||
speed.set(0.0, forced = true)
|
||||
hasBreached.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -635,6 +665,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
it.boosting.set(this.boosting.get(), forced = true)
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.speed.set(this.speed.get(), forced = true)
|
||||
it.hasBreached.set(this.hasBreached.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -60,6 +61,8 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
pokemon.add(Pokemon.loadFromNBT(registryAccess, pokemonNBT))
|
||||
} catch(_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesNBT(pokemonNBT)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
|
||||
}
|
||||
}
|
||||
return this
|
||||
@@ -78,6 +81,8 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
pokemon.add(Pokemon.loadFromJSON(registryAccess, pokemonJSON))
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesJSON(pokemonJSON)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonJSON", e)
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
||||
@@ -77,6 +77,10 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
remove(pokemon)
|
||||
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
|
||||
set(position, pokemon)
|
||||
|
||||
// sync TM moves here
|
||||
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ import java.util.UUID
|
||||
|
||||
interface MoLangDataStoreFactory {
|
||||
fun markDirty(uuid: UUID)
|
||||
fun load(uuid: UUID) : VariableStruct
|
||||
fun load(uuid: UUID, filePath: String? = null) : VariableStruct
|
||||
}
|
||||
+7
-5
@@ -20,6 +20,8 @@ import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
|
||||
const val DEFAULT_PATH = "playermolangdata/"
|
||||
const val DEFAULT_FOLDER_STRUCTURE = "%s/%s.dat"
|
||||
lateinit var savePath: Path
|
||||
val cache = mutableMapOf<UUID, VariableStruct>()
|
||||
val dirty = mutableListOf<UUID>()
|
||||
@@ -57,7 +59,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
|
||||
dirty.add(uuid)
|
||||
}
|
||||
|
||||
override fun load(uuid: UUID): VariableStruct {
|
||||
override fun load(uuid: UUID, filePath: String?): VariableStruct {
|
||||
return if (cache.contains(uuid))
|
||||
cache[uuid]!!
|
||||
else {
|
||||
@@ -68,7 +70,7 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
|
||||
return data
|
||||
}
|
||||
|
||||
val nbt = NbtIo.readCompressed(file(uuid), NbtAccounter.unlimitedHeap())
|
||||
val nbt = NbtIo.readCompressed(file(uuid, filePath ?: DEFAULT_PATH), NbtAccounter.unlimitedHeap())
|
||||
|
||||
// If it's not a VariableStruct then someone's fucked around and will subsequently find out
|
||||
val data = readMoValueFromNBT(nbt) as VariableStruct
|
||||
@@ -77,8 +79,8 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
|
||||
}
|
||||
}
|
||||
|
||||
fun save(uuid: UUID) {
|
||||
val file = file(uuid)
|
||||
fun save(uuid: UUID, filePath: String? = null) {
|
||||
val file = file(uuid, filePath ?: DEFAULT_PATH)
|
||||
val data = cache[uuid] ?: return
|
||||
val nbt = writeMoValueToNBT(data)!! as CompoundTag
|
||||
file.toFile().parentFile.mkdirs()
|
||||
@@ -86,5 +88,5 @@ object NbtMoLangDataStoreFactory : MoLangDataStoreFactory {
|
||||
dirty -= uuid
|
||||
}
|
||||
|
||||
private fun file(uuid: UUID) = savePath.resolve("playermolangdata/${uuid.toString().substring(0, 2)}/$uuid.dat")
|
||||
private fun file(uuid: UUID, filePath: String = DEFAULT_PATH) = savePath.resolve(filePath + DEFAULT_FOLDER_STRUCTURE.format(uuid.toString().substring(0, 2), uuid))
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.party
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
@@ -169,6 +170,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
}
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesNBT(pokemonNBT)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +203,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
slots[slot] = Pokemon.loadFromJSON(registryAccess, pokemonJSON)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
handleInvalidSpeciesJSON(pokemonJSON)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonJSON", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.pc
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSortMode
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.storage.StoreCoordinates
|
||||
@@ -148,6 +148,8 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
pokemon[slot] = Pokemon.loadFromJSON(registryAccess, pokemonJson)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
pc.handleInvalidSpeciesJSON(pokemonJson)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonJson", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,6 +172,8 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
pokemon[slot] = Pokemon.loadFromNBT(registryAccess, pokemonNBT)
|
||||
} catch (_: InvalidSpeciesException) {
|
||||
pc.handleInvalidSpeciesNBT(pokemonNBT)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to read a pokémon: $pokemonNBT", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -27,6 +27,7 @@ data class GeneralPlayerData(
|
||||
var starterSelected: Boolean,
|
||||
var starterUUID: UUID?,
|
||||
var keyItems: MutableSet<ResourceLocation>,
|
||||
var tmSet: MutableSet<ResourceLocation>,
|
||||
var battleTheme: ResourceLocation?,
|
||||
val extraData: MutableMap<String, PlayerDataExtension>,
|
||||
) : InstancedPlayerData {
|
||||
@@ -44,6 +45,7 @@ data class GeneralPlayerData(
|
||||
starterSelected,
|
||||
starterUUID,
|
||||
advancementData.totalBattleVictoryCount == 0,
|
||||
tmSet,
|
||||
battleTheme
|
||||
)
|
||||
}
|
||||
|
||||
+22
@@ -8,10 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.MODID
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Executors
|
||||
@@ -66,6 +68,14 @@ open class PlayerInstancedDataStoreManager {
|
||||
.infiniteIterations()
|
||||
.tracker(ServerTaskTracker)
|
||||
.build()
|
||||
|
||||
saveTasks[PlayerInstancedDataStoreTypes.TM_MOVES] = ScheduledTask.Builder()
|
||||
.execute { saveAllOfOneType(PlayerInstancedDataStoreTypes.TM_MOVES) }
|
||||
.delay(30f)
|
||||
.interval(120f)
|
||||
.infiniteIterations()
|
||||
.tracker(ServerTaskTracker)
|
||||
.build()
|
||||
}
|
||||
|
||||
open fun get(playerId: UUID, dataType: PlayerInstancedDataStoreType): InstancedPlayerData {
|
||||
@@ -126,4 +136,16 @@ open class PlayerInstancedDataStoreManager {
|
||||
open fun getPokedexData(playerId: UUID): PokedexManager {
|
||||
return get(playerId, PlayerInstancedDataStoreTypes.POKEDEX) as PokedexManager
|
||||
}
|
||||
|
||||
fun getTMData(player: ServerPlayer): TMMoveManager {
|
||||
return getTMData(player.uuid)
|
||||
}
|
||||
|
||||
fun getTMData(playerId: UUID): TMMoveManager {
|
||||
val data = get(playerId, PlayerInstancedDataStoreTypes.TM_MOVES)
|
||||
if (data !is TMMoveManager) {
|
||||
Cobblemon.LOGGER.warn("getTMData: No TMMoveManager found for $playerId (got ${data?.javaClass?.name})")
|
||||
}
|
||||
return data as TMMoveManager
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -27,11 +28,22 @@ object PlayerInstancedDataStoreTypes {
|
||||
ClientPokedexManager::runAction,
|
||||
ClientPokedexManager::runIncremental
|
||||
))
|
||||
val TM_MOVES = register(PlayerInstancedDataStoreType(
|
||||
cobblemonResource("tm_moves"),
|
||||
ClientTMMoveManager::decode,
|
||||
ClientTMMoveManager::runAction,
|
||||
ClientTMMoveManager::runIncremental
|
||||
))
|
||||
|
||||
fun register(type: PlayerInstancedDataStoreType): PlayerInstancedDataStoreType {
|
||||
println("Registering PlayerInstancedDataStoreType: ${type.id}")
|
||||
types[type.id] = type
|
||||
return type
|
||||
}
|
||||
|
||||
fun getTypeById(id: ResourceLocation) = types[id]
|
||||
fun getTypeById(id: ResourceLocation): PlayerInstancedDataStoreType? {
|
||||
val type = types[id]
|
||||
if (type == null) println("Unknown PlayerInstancedDataStoreType ID: $id")
|
||||
return type
|
||||
}
|
||||
}
|
||||
+1
@@ -36,6 +36,7 @@ class PlayerDataJsonBackend: JsonBackedPlayerDataStoreBackend<GeneralPlayerData>
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf(),
|
||||
tmSet = mutableSetOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.location
|
||||
)}
|
||||
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ class PlayerDataMongoBackend(mongoClient: MongoClient, databaseName: String, col
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
extraData = mutableMapOf(),
|
||||
tmSet = mutableSetOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.location
|
||||
)}
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveJsonBackend :
|
||||
JsonBackedPlayerDataStoreBackend<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
|
||||
.create()
|
||||
|
||||
override val classToken = TypeToken.get(TMMoveManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
override fun initialize(store: TMMoveManager) {
|
||||
store.initialize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mongodb.client.MongoClient
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveMongoBackend(
|
||||
mongoClient: MongoClient,
|
||||
databaseName: String,
|
||||
collectionName: String
|
||||
) : MongoBackedPlayerDataStoreBackend<TMMoveManager>(
|
||||
mongoClient,
|
||||
databaseName,
|
||||
collectionName,
|
||||
PlayerInstancedDataStoreTypes.TM_MOVES
|
||||
) {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(TMMoveManager::class.java, CodecBackedAdapter(TMMoveManager.CODEC))
|
||||
.create()
|
||||
override val classToken = TypeToken.get(TMMoveManager::class.java)
|
||||
override val defaultData = defaultDataFunc
|
||||
|
||||
override fun initialize(store: TMMoveManager) {
|
||||
store.initialize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultDataFunc = { uuid: UUID -> TMMoveManager(uuid) }
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.tms.TMMoveManager
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveNbtBackend : NbtBackedPlayerData<TMMoveManager>("tm_moves", PlayerInstancedDataStoreTypes.TM_MOVES) {
|
||||
override val codec = TMMoveManager.CODEC
|
||||
override val defaultData = TMMoveJsonBackend.defaultDataFunc
|
||||
}
|
||||
+4
@@ -34,6 +34,7 @@ data class ClientGeneralPlayerData(
|
||||
var starterSelected: Boolean = false,
|
||||
var starterUUID: UUID? = null,
|
||||
var showChallengeLabel: Boolean = true,
|
||||
var tmSet: MutableSet<ResourceLocation> = mutableSetOf(),
|
||||
val battleTheme: ResourceLocation? = null
|
||||
) : ClientInstancedPlayerData {
|
||||
|
||||
@@ -46,6 +47,7 @@ data class ClientGeneralPlayerData(
|
||||
buf.writeNullable(starterUUID) { pb, value -> pb.writeString(value.toString()) }
|
||||
buf.writeNullable(resetStarters) { pb, value -> pb.writeBoolean(value) }
|
||||
buf.writeNullable(battleTheme) {pb, value -> pb.writeIdentifier(value)}
|
||||
buf.writeCollection(tmSet) { pb, value -> pb.writeIdentifier(value)}
|
||||
}
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
|
||||
@@ -56,6 +58,7 @@ data class ClientGeneralPlayerData(
|
||||
val showChallengeLabel = buffer.readBoolean()
|
||||
val starterUUID = buffer.readNullable { it.readString() }?.let { UUID.fromString(it) }
|
||||
val resetStarterPrompt = buffer.readNullable { it.readBoolean() }
|
||||
val tmSet = buffer.readCollection({ mutableSetOf<ResourceLocation>() }) { it.readIdentifier() }
|
||||
val battleTheme = buffer.readNullable { it.readIdentifier() }
|
||||
val data = ClientGeneralPlayerData(
|
||||
resetStarterPrompt,
|
||||
@@ -64,6 +67,7 @@ data class ClientGeneralPlayerData(
|
||||
starterSelected,
|
||||
starterUUID,
|
||||
showChallengeLabel,
|
||||
tmSet,
|
||||
battleTheme
|
||||
)
|
||||
//Weird to do this, but since the flag doesn't get passed to the decoded obj, do it here
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.player.client
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class ClientTMMoveManager(
|
||||
val learnedTMs: MutableSet<ResourceLocation>
|
||||
) : ClientInstancedPlayerData {
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf) {
|
||||
buf.writeCollection(learnedTMs) { _, id -> buf.writeString(id.toString()) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(buf: RegistryFriendlyByteBuf): SetClientPlayerDataPacket {
|
||||
val tms = buf.readCollection(
|
||||
{ mutableSetOf<ResourceLocation>() },
|
||||
{ buf.readString().let(ResourceLocation::tryParse) ?: ResourceLocation("minecraft", "empty") }
|
||||
)
|
||||
return SetClientPlayerDataPacket(
|
||||
PlayerInstancedDataStoreTypes.TM_MOVES,
|
||||
ClientTMMoveManager(tms)
|
||||
)
|
||||
}
|
||||
|
||||
fun runAction(data: ClientInstancedPlayerData) {
|
||||
if (data !is ClientTMMoveManager) return
|
||||
CobblemonClient.clientTMMoveData = data
|
||||
}
|
||||
|
||||
fun runIncremental(data: ClientInstancedPlayerData) {
|
||||
if (data !is ClientTMMoveManager) return
|
||||
CobblemonClient.clientTMMoveData.learnedTMs.addAll(data.learnedTMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val TUMBLESTONE_BRICKS = createTag("tumblestone_bricks")
|
||||
@JvmField val TUMBLESTONES = createTag("tumblestones")
|
||||
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
|
||||
@JvmField val TYPE_GEM_BLOCKS = createTag("type_gem_blocks")
|
||||
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
|
||||
@JvmField val WALL_SIGNS = createTag("wall_signs")
|
||||
@JvmField val WATER_STONE_ORES = createTag("water_stone_ores")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
abstract class AbstractTMMoveManager {
|
||||
abstract val uuid: UUID
|
||||
abstract val learnedTMs: MutableSet<ResourceLocation>
|
||||
abstract val storeType: PlayerInstancedDataStoreType
|
||||
abstract fun toClientData(): ClientInstancedPlayerData
|
||||
abstract fun toClientDataFrom(set: Set<ResourceLocation>): ClientInstancedPlayerData
|
||||
|
||||
open fun learn(tmId: ResourceLocation): Boolean {
|
||||
if (learnedTMs.add(tmId)) {
|
||||
syncClient(setOf(tmId))
|
||||
sendTMToast(tmId)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun sendTMToast(tmId: ResourceLocation) {
|
||||
val player = uuid.getPlayer() ?: return
|
||||
val tm = TechnicalMachines.tmMap[tmId] ?: return
|
||||
|
||||
val packet = ToastPacket(
|
||||
title = Component.literal("New TM Learned"), // todp lang for this
|
||||
description = tm.translatedMoveName(),
|
||||
icon = tm.createItemStack(),
|
||||
frameTexture = ResourceLocation.parse("minecraft:toast/advancement"),
|
||||
progress = -1F,
|
||||
progressColor = 0x00FF00,
|
||||
uuid = UUID.nameUUIDFromBytes(("tm_toast:${tmId}").toByteArray()),
|
||||
behaviour = ToastPacket.Behaviour.SHOW_OR_UPDATE,
|
||||
durationMs = 4000L
|
||||
)
|
||||
|
||||
player.sendPacket(packet)
|
||||
}
|
||||
|
||||
protected fun syncClient(updateSet: Set<ResourceLocation> = learnedTMs) {
|
||||
uuid.getPlayer()?.sendPacket(
|
||||
SetClientPlayerDataPacket(
|
||||
type = storeType,
|
||||
playerData = toClientDataFrom(updateSet),
|
||||
isIncremental = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
open fun markDirty() {}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.tms.obtain.ImpossibleObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PlayerHasAdvancementObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PlayerYObtainMethod
|
||||
import com.cobblemon.mod.common.tms.obtain.PokemonHasMoveObtainMethod
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
interface ObtainMethod {
|
||||
val passive: Boolean
|
||||
|
||||
/**
|
||||
* Checks if this [ObtainMethod] matches the given player.
|
||||
*/
|
||||
fun matches(player: ServerPlayer): Boolean
|
||||
|
||||
/**
|
||||
* Serializes this [ObtainMethod] to the buffer.
|
||||
*/
|
||||
fun writeToBuffer(buffer: RegistryFriendlyByteBuf)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Deserializes an [ObtainMethod] from the buffer.
|
||||
*/
|
||||
fun readFromBuffer(buffer: RegistryFriendlyByteBuf): ObtainMethod {
|
||||
val variant = buffer.readUtf()
|
||||
return when (variant) {
|
||||
"cobblemon:impossible" -> ImpossibleObtainMethod()
|
||||
"cobblemon:none" -> NoneObtainMethod()
|
||||
"cobblemon:advancement" -> {
|
||||
val advancement = buffer.readResourceLocation()
|
||||
PlayerHasAdvancementObtainMethod(advancement)
|
||||
}
|
||||
"cobblemon:y_level" -> {
|
||||
val yLevel = buffer.readVarInt()
|
||||
val operator = buffer.readUtf()
|
||||
PlayerYObtainMethod(yLevel, operator)
|
||||
}
|
||||
"cobblemon:pokemon_knows" -> {
|
||||
val moveId = buffer.readUtf()
|
||||
PokemonHasMoveObtainMethod(moveId)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown ObtainMethod variant: $variant")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A type adapter for [ObtainMethod]s.
|
||||
* For the default implementation see [CobblemonObtainMethodAdapter].
|
||||
*
|
||||
* @author Apion
|
||||
* @since November 22nd, 2023
|
||||
*/
|
||||
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
|
||||
/**
|
||||
* Register a [ObtainMethod] to be used by this adapter.
|
||||
*
|
||||
* @param type The [KClass] of the [ObtainMethod].
|
||||
* @param identifier The expected [ResourceLocation] in the parsed JSON.
|
||||
*/
|
||||
fun register(type: KClass<out ObtainMethod>, identifier: ResourceLocation)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientInstancedPlayerData
|
||||
import com.cobblemon.mod.common.api.tms.AbstractTMMoveManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.UUID
|
||||
|
||||
class TMMoveManager(
|
||||
override val uuid: UUID,
|
||||
override val learnedTMs: MutableSet<ResourceLocation> = mutableSetOf()
|
||||
) : AbstractTMMoveManager(), InstancedPlayerData {
|
||||
|
||||
override val storeType = PlayerInstancedDataStoreTypes.TM_MOVES
|
||||
|
||||
override fun initialize() {}
|
||||
|
||||
override fun toClientData(): ClientTMMoveManager {
|
||||
return ClientTMMoveManager(learnedTMs.toMutableSet())
|
||||
}
|
||||
|
||||
override fun toClientDataFrom(set: Set<ResourceLocation>): ClientTMMoveManager {
|
||||
return ClientTMMoveManager(set.toMutableSet())
|
||||
}
|
||||
|
||||
fun syncTMsFromPokemon(pokemon: Pokemon) {
|
||||
val learnableTMs = TechnicalMachines.tmMap.values
|
||||
.filter { tm -> pokemon.allAccessibleMoves.contains(tm.moveName) }
|
||||
|
||||
for (tm in learnableTMs) {
|
||||
learn(tm.id)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<TMMoveManager> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.xmap(UUID::fromString, UUID::toString)
|
||||
.fieldOf("uuid")
|
||||
.forGetter { it.uuid },
|
||||
Codec.list(ResourceLocation.CODEC)
|
||||
.xmap({ it.toMutableSet() }, { it.toList() })
|
||||
.fieldOf("learnedTMs")
|
||||
.forGetter { it.learnedTMs }
|
||||
).apply(instance) { uuid, learnedTMs ->
|
||||
TMMoveManager(uuid, learnedTMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class TechnicalMachine(
|
||||
val moveName: MoveTemplate,
|
||||
val recipe: List<TechnicalMachineRecipe>?,
|
||||
val obtainMethods: List<ObtainMethod> = emptyList(),
|
||||
val type: String
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Filters all available [TechnicalMachines] based on three optional arguments
|
||||
*
|
||||
* @param search A [String] to check against move names
|
||||
* @param type An [ElementalType] to check for
|
||||
* @param pokemon A [Pokemon] to check the [Learnset] of
|
||||
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
|
||||
*/
|
||||
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?, player: Player? = null): MutableSet<TechnicalMachine> {
|
||||
val tms = TechnicalMachines.tmMap.values.toMutableSet()
|
||||
|
||||
type?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (ElementalTypes.get(tm.type) != type) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pokemon?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (!pokemon.species.moves.tmLearnableMoves().contains(tm.moveName)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (!tm.translatedMoveName().string.contains(search, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player?.let {
|
||||
val learnedTMs = CobblemonClient.clientTMMoveData?.learnedTMs ?: return@let
|
||||
tms.retainAll { tm -> learnedTMs.contains(tm.id) }
|
||||
}
|
||||
|
||||
return tms
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var id: ResourceLocation
|
||||
|
||||
/**
|
||||
* Gets the recipe list limited to the provided amount to match the slot amount of the TM Machine.
|
||||
*
|
||||
* @param limit The amount of recipe item slots to take.
|
||||
* @return The recipe list limited to 3 ingredients.
|
||||
*/
|
||||
fun getClampedRecipe(limit: Int = 3): List<TechnicalMachineRecipe>? = recipe?.take(limit)
|
||||
|
||||
/**
|
||||
* Unlocks this [TechnicalMachine] for the player.
|
||||
*
|
||||
* @param player The [ServerPlayer] to give this [TechnicalMachine] to.
|
||||
* @return Whether the player was successfully granted the [TechnicalMachine]
|
||||
*/
|
||||
fun unlock(player: ServerPlayer): Boolean {
|
||||
if (!obtainMethods.any { it is NoneObtainMethod }) {
|
||||
player.sendSystemMessage(lang("tms.unlock_tm", moveName.displayName))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [MutableComponent] of the translated move name of this [TechnicalMachine]
|
||||
*
|
||||
* @return This [TechnicalMachine]'s move name, translated
|
||||
*/
|
||||
fun translatedMoveName(): MutableComponent = moveName.displayName
|
||||
|
||||
fun createItemStack(): ItemStack {
|
||||
val stack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
||||
TMMoveComponent.setTMMove(stack, moveName)
|
||||
return stack
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
|
||||
*/
|
||||
data class TechnicalMachineRecipe(
|
||||
val item: ResourceLocation,
|
||||
val count: Int
|
||||
)
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.registry.ItemTagCondition
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
|
||||
.registerTypeAdapter(MoveTemplate::class.java, MoveTemplateAdapter)
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
|
||||
override val resourcePath = "tms"
|
||||
override val id = cobblemonResource("technical_machines")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<TechnicalMachines>()
|
||||
|
||||
val tmMap = mutableMapOf<ResourceLocation, TechnicalMachine>()
|
||||
val moveToTM = mutableMapOf<MoveTemplate, TechnicalMachine>()
|
||||
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
|
||||
val passiveTms = mutableMapOf<ResourceLocation, TechnicalMachine>()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, TechnicalMachine>) {
|
||||
data.forEach { (id, tm) ->
|
||||
//TODO remove the logging
|
||||
Cobblemon.LOGGER.info("Processing TM: $id with moveName: ${tm.moveName}")
|
||||
if (tm.moveName == null) {
|
||||
Cobblemon.LOGGER.error("Failed to resolve move for TM: $id with moveName: ${tm.moveName}")
|
||||
} else {
|
||||
Cobblemon.LOGGER.info("Resolved Move for TM: $id -> ${tm.moveName.name}")
|
||||
}
|
||||
tmMap[id] = tm
|
||||
tm.id = id
|
||||
|
||||
moveToTM[tm.moveName] = tm
|
||||
|
||||
// Check for passive ObtainMethods
|
||||
//if (tm.obtainMethods.any { it.passive }) passiveTms[id] = tm
|
||||
}
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) { }
|
||||
|
||||
fun checkPassives(player: ServerPlayer) {
|
||||
val playerTms = Cobblemon.playerDataManager.getGenericData(player).tmSet
|
||||
passiveTms.forEach { (id, tm) ->
|
||||
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,12 @@ class ElementalType(
|
||||
val name: String,
|
||||
val displayName: MutableComponent,
|
||||
val hue: Int,
|
||||
val textureXMultiplier: Int,
|
||||
val textureXMultiplier: Int, val primaryColor: Int,
|
||||
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
|
||||
val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), "")
|
||||
) : ShowdownIdentifiable {
|
||||
val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), ""),
|
||||
val secondaryColor: Int,
|
||||
val typeGem: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, showdownId + "_gem"),
|
||||
) : ShowdownIdentifiable {
|
||||
|
||||
override fun showdownId(): String {
|
||||
return showdownId
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
@@ -21,135 +22,173 @@ object ElementalTypes {
|
||||
val NORMAL = register(
|
||||
name = "Normal",
|
||||
displayName = Component.translatable("cobblemon.type.normal"),
|
||||
hue = 0xE8E8DA,
|
||||
textureXMultiplier = 0
|
||||
hue = 0xDDDDCF,
|
||||
textureXMultiplier = 0,
|
||||
primaryColor = 0x8FA689,
|
||||
secondaryColor = 0xFFFBE2
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "Fire",
|
||||
displayName = Component.translatable("cobblemon.type.fire"),
|
||||
hue = 0xFF6E21,
|
||||
textureXMultiplier = 1
|
||||
hue = 0xE55C32,
|
||||
textureXMultiplier = 1,
|
||||
primaryColor = 0xF92D2D,
|
||||
secondaryColor = 0xF99B1D
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "Water",
|
||||
displayName = Component.translatable("cobblemon.type.water"),
|
||||
hue = 0x3FA5FF,
|
||||
textureXMultiplier = 2
|
||||
hue = 0x4A9BE8,
|
||||
textureXMultiplier = 2,
|
||||
primaryColor = 0x377AF6,
|
||||
secondaryColor = 0x50DDF9
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "Grass",
|
||||
displayName = Component.translatable("cobblemon.type.grass"),
|
||||
hue = 0x62D14F,
|
||||
textureXMultiplier = 3
|
||||
hue = 0x4DBC3C,
|
||||
textureXMultiplier = 3,
|
||||
primaryColor = 0x0EAE95,
|
||||
secondaryColor = 0xC6F554
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "Electric",
|
||||
displayName = Component.translatable("cobblemon.type.electric"),
|
||||
hue = 0xFFD314,
|
||||
textureXMultiplier = 4
|
||||
hue = 0xEFD128,
|
||||
textureXMultiplier = 4,
|
||||
primaryColor = 0xF7AB19,
|
||||
secondaryColor = 0xFFF257
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "Ice",
|
||||
displayName = Component.translatable("cobblemon.type.ice"),
|
||||
hue = 0x54F2F2,
|
||||
textureXMultiplier = 5
|
||||
hue = 0x6BC3EF,
|
||||
textureXMultiplier = 5,
|
||||
primaryColor = 0x41BAED,
|
||||
secondaryColor = 0x91F4F9
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "Fighting",
|
||||
displayName = Component.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xEF565D,
|
||||
textureXMultiplier = 6
|
||||
hue = 0xC44C5C,
|
||||
textureXMultiplier = 6,
|
||||
primaryColor = 0xC8356B,
|
||||
secondaryColor = 0xF36A3F
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "Poison",
|
||||
displayName = Component.translatable("cobblemon.type.poison"),
|
||||
hue = 0xD651FF,
|
||||
textureXMultiplier = 7
|
||||
hue = 0xA24BD8,
|
||||
textureXMultiplier = 7,
|
||||
primaryColor = 0x7B41DD,
|
||||
secondaryColor = 0xE568F4
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "Ground",
|
||||
displayName = Component.translatable("cobblemon.type.ground"),
|
||||
hue = 0xF4A453,
|
||||
textureXMultiplier = 8
|
||||
hue = 0xD89950,
|
||||
textureXMultiplier = 8,
|
||||
primaryColor = 0xE08835,
|
||||
secondaryColor = 0xFFD057
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "Flying",
|
||||
displayName = Component.translatable("cobblemon.type.flying"),
|
||||
hue = 0xB8B2FF,
|
||||
textureXMultiplier = 9
|
||||
hue = 0xBCC1FF,
|
||||
textureXMultiplier = 9,
|
||||
primaryColor = 0x6083F6,
|
||||
secondaryColor = 0xD5CCFA
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "Psychic",
|
||||
displayName = Component.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xFF5E9E,
|
||||
textureXMultiplier = 10
|
||||
hue = 0xD86AD6,
|
||||
textureXMultiplier = 10,
|
||||
primaryColor = 0xE553CE,
|
||||
secondaryColor = 0xF69DD5
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "Bug",
|
||||
displayName = Component.translatable("cobblemon.type.bug"),
|
||||
hue = 0xD3D319,
|
||||
textureXMultiplier = 11
|
||||
hue = 0xA2C831,
|
||||
textureXMultiplier = 11,
|
||||
primaryColor = 0x9FBE1B,
|
||||
secondaryColor = 0xE9FB5B
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "Rock",
|
||||
displayName = Component.translatable("cobblemon.type.rock"),
|
||||
hue = 0xB7A16E,
|
||||
textureXMultiplier = 12
|
||||
hue = 0xAA9666,
|
||||
textureXMultiplier = 12,
|
||||
primaryColor = 0x9F814A,
|
||||
secondaryColor = 0xEECB7A
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "Ghost",
|
||||
displayName = Component.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9C80F7,
|
||||
textureXMultiplier = 13
|
||||
hue = 0x9572E5,
|
||||
textureXMultiplier = 13,
|
||||
primaryColor = 0x4F57DD,
|
||||
secondaryColor = 0xC182F1
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "Dragon",
|
||||
displayName = Component.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x7580FF,
|
||||
textureXMultiplier = 14
|
||||
hue = 0x535DE8,
|
||||
textureXMultiplier = 14,
|
||||
primaryColor = 0x4E68E2,
|
||||
secondaryColor = 0x47C7F7
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "Dark",
|
||||
displayName = Component.translatable("cobblemon.type.dark"),
|
||||
hue = 0x587DA0,
|
||||
textureXMultiplier = 15
|
||||
hue = 0x5C6CB2,
|
||||
textureXMultiplier = 1,
|
||||
primaryColor = 0x465B99,
|
||||
secondaryColor = 0x689EBF
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "Steel",
|
||||
displayName = Component.translatable("cobblemon.type.steel"),
|
||||
hue = 0xABD1F4,
|
||||
textureXMultiplier = 16
|
||||
hue = 0xC3CCE0,
|
||||
textureXMultiplier = 16,
|
||||
primaryColor = 0x628CB9,
|
||||
secondaryColor = 0xD6F7F3
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "Fairy",
|
||||
displayName = Component.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xFF7FE5,
|
||||
textureXMultiplier = 17
|
||||
hue = 0xEA727E,
|
||||
textureXMultiplier = 17,
|
||||
primaryColor = 0xE2678C,
|
||||
secondaryColor = 0xF5A7B7
|
||||
)
|
||||
|
||||
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int): ElementalType {
|
||||
fun register(name: String, displayName: MutableComponent, hue: Int, textureXMultiplier: Int, primaryColor: Int, secondaryColor: Int): ElementalType {
|
||||
return ElementalType(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
hue = hue,
|
||||
textureXMultiplier = textureXMultiplier
|
||||
textureXMultiplier = textureXMultiplier,
|
||||
primaryColor = primaryColor,
|
||||
secondaryColor = secondaryColor
|
||||
).also {
|
||||
allTypes.add(it)
|
||||
}
|
||||
@@ -171,4 +210,4 @@ object ElementalTypes {
|
||||
fun count() = allTypes.size
|
||||
|
||||
fun all() = this.allTypes.toList()
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
@@ -42,6 +43,8 @@ abstract class GrowableStoneBlock(
|
||||
val nextStage: Block?
|
||||
) : DirectionalBlock(settings) {
|
||||
|
||||
abstract val growthChance: Int
|
||||
|
||||
private val upShape: VoxelShape = Block.box(
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
@@ -95,20 +98,26 @@ abstract class GrowableStoneBlock(
|
||||
.setValue(FACING, Direction.DOWN))
|
||||
}
|
||||
|
||||
abstract fun canGrow(pos: BlockPos, world: BlockGetter): Boolean
|
||||
abstract fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
|
||||
if (world.random.nextInt(growthChance) == 0 && canGrow(state, pos, world)) {
|
||||
val block = nextStage
|
||||
|
||||
if (block != null) {
|
||||
val newState = block.defaultBlockState().setValue(FACING, state.getValue(FACING)) as BlockState
|
||||
var newState = block.defaultBlockState()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
for (property in state.properties) {
|
||||
if (newState.hasProperty(property)) {
|
||||
newState = newState.setValue(property as Property<Comparable<Any>>, state.getValue(property) as Comparable<Any>)
|
||||
}
|
||||
}
|
||||
|
||||
world.setBlockAndUpdate(pos, newState)
|
||||
}
|
||||
}
|
||||
@@ -155,14 +164,4 @@ abstract class GrowableStoneBlock(
|
||||
else -> upShape
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<TMShelfBlock> = simpleCodec(::TMShelfBlock)
|
||||
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
|
||||
val SLOT_OCCUPIED_PROPERTIES = (0 until 14).map {
|
||||
BooleanProperty.create("slot_${it}_occupied")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val baseState = stateDefinition.any().setValue(FACING, Direction.NORTH)
|
||||
registerDefaultState(SLOT_OCCUPIED_PROPERTIES.fold(baseState) { state, prop ->
|
||||
state.setValue(prop, false)
|
||||
})
|
||||
}
|
||||
|
||||
override fun codec() = CODEC
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return TMShelfBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
SLOT_OCCUPIED_PROPERTIES.forEach { builder.add(it) }
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState {
|
||||
return defaultBlockState().setValue(FACING, ctx.horizontalDirection.opposite)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState, level: Level, pos: BlockPos,
|
||||
player: Player, hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return InteractionResult.PASS
|
||||
return entity.handleUseWithoutItem(state, level, pos, player, hit)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack, state: BlockState, level: Level, pos: BlockPos,
|
||||
player: Player, hand: InteractionHand, hit: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
return entity.handleUseItem(stack, state, level, pos, player, hit)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState) = true
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
||||
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
|
||||
return entity?.lastInteractedSlot?.plus(1) ?: 0
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState, level: BlockGetter,
|
||||
pos: BlockPos, context: net.minecraft.world.phys.shapes.CollisionContext
|
||||
): VoxelShape = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
@@ -44,15 +44,25 @@ class TumblestoneBlock(
|
||||
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
|
||||
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
|
||||
).apply(it, ::TumblestoneBlock) }
|
||||
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
|
||||
override val growthChance = 5
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.DOWN)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun canGrow(pos: BlockPos, world: BlockGetter): Boolean {
|
||||
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
|
||||
if (stage == MAX_STAGE) return false
|
||||
val iterator: Iterator<BlockPos> =
|
||||
BlockPos.betweenClosed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1))
|
||||
@@ -66,6 +76,8 @@ class TumblestoneBlock(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = stage < MAX_STAGE
|
||||
|
||||
override fun codec(): MapCodec<out DirectionalBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
|
||||
class TypeGemBlock(
|
||||
settings: Properties,
|
||||
stage: Int,
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<TypeGemBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
PrimitiveCodec.INT.fieldOf("stage").forGetter { it.stage },
|
||||
PrimitiveCodec.INT.fieldOf("height").forGetter { it.height },
|
||||
PrimitiveCodec.INT.fieldOf("xzOffset").forGetter { it.xzOffset },
|
||||
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage }
|
||||
).apply(it, ::TypeGemBlock) }
|
||||
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val MAX_STAGE = STAGE_1
|
||||
|
||||
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
|
||||
val FACING: DirectionProperty = DirectionalBlock.FACING
|
||||
}
|
||||
|
||||
override val growthChance = 1
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.DOWN) // Ensure facing is properly registered
|
||||
.setValue(SHOULD_GROW, true)
|
||||
)
|
||||
}
|
||||
|
||||
override fun canGrow(state: BlockState, pos: BlockPos, world: BlockGetter): Boolean {
|
||||
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean {
|
||||
return state.getValue(SHOULD_GROW) && stage < MAX_STAGE
|
||||
}
|
||||
|
||||
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (canGrow(state, pos, level)) {
|
||||
var nextState = nextStage?.defaultBlockState()
|
||||
if (nextState != null) {
|
||||
if (nextState.hasProperty(FACING)) {
|
||||
nextState = nextState.setValue(FACING, state.getValue(FACING)) // Preserve FACING for core
|
||||
}
|
||||
level.setBlockAndUpdate(pos, nextState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out DirectionalBlock?>? = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(FACING, SHOULD_GROW) // Ensure FACING is registered
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.AirBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.storage.loot.LootContext
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import javax.swing.text.html.HTML.Attribute.SHAPES
|
||||
|
||||
class TypeGemClusterBlock(
|
||||
settings: Properties,
|
||||
val nextStage: Block,
|
||||
val dropItemId: ResourceLocation
|
||||
) : DirectionalBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<TypeGemClusterBlock> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
propertiesCodec(),
|
||||
Block.CODEC.fieldOf("nextStage").forGetter { it.nextStage },
|
||||
ResourceLocation.CODEC.fieldOf("dropItem").forGetter { it.dropItemId }
|
||||
).apply(instance) { settings, nextStage, dropItemId ->
|
||||
TypeGemClusterBlock(settings, nextStage, dropItemId).also {
|
||||
gemToClusterMap[nextStage] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
val gemToClusterMap: MutableMap<Block, TypeGemClusterBlock> = mutableMapOf()
|
||||
|
||||
val SHOULD_GROW: BooleanProperty = BooleanProperty.create("should_grow")
|
||||
val FACING: DirectionProperty = DirectionalBlock.FACING
|
||||
val STAGE: IntegerProperty = IntegerProperty.create("stage", 0, 3)
|
||||
val STUNTED: BooleanProperty = BooleanProperty.create("stunted")
|
||||
|
||||
fun clusterFromGemBlock(gemBlock: Block): TypeGemClusterBlock? {
|
||||
return gemToClusterMap[gemBlock]
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.UP)
|
||||
.setValue(SHOULD_GROW, true)
|
||||
.setValue(STAGE, 0)
|
||||
.setValue(STUNTED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val facing = context.clickedFace
|
||||
val world = context.level
|
||||
val pos = context.clickedPos.relative(facing.opposite)
|
||||
val blockBelow = world.getBlockState(pos).block
|
||||
val shouldGrow = blockBelow is TypeGemCoreBlock
|
||||
|
||||
return defaultBlockState()
|
||||
.setValue(FACING, facing)
|
||||
.setValue(SHOULD_GROW, shouldGrow)
|
||||
.setValue(STAGE, 0)
|
||||
.setValue(STUNTED, false)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = state.getValue(SHOULD_GROW)
|
||||
|
||||
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (!state.getValue(SHOULD_GROW)) return
|
||||
|
||||
val currentStage = state.getValue(STAGE)
|
||||
|
||||
if (currentStage < 3) {
|
||||
// Progress through stages regardless of STUNTED
|
||||
level.setBlockAndUpdate(pos, state.setValue(STAGE, currentStage + 1))
|
||||
return
|
||||
}
|
||||
|
||||
// At STAGE_3
|
||||
val isStunted = state.getValue(STUNTED)
|
||||
val facing = state.getValue(FACING)
|
||||
|
||||
if (isStunted) {
|
||||
println("[TypeGemClusterBlock] Cluster at $pos is STUNTED at STAGE_3. Finalizing growth.")
|
||||
level.setBlockAndUpdate(pos, state.setValue(SHOULD_GROW, false))
|
||||
return
|
||||
}
|
||||
|
||||
// Log type of cluster based on nextStage
|
||||
val clusterType = level.registryAccess()
|
||||
.registryOrThrow(BuiltInRegistries.BLOCK.key())
|
||||
.getKey(nextStage)
|
||||
.toString()
|
||||
|
||||
println("[TypeGemClusterBlock] Attempting to grow into TypeGemBlock at $pos")
|
||||
println("[TypeGemClusterBlock] Cluster type: $clusterType")
|
||||
|
||||
// Check neighboring gem blocks
|
||||
val hasConflict = Direction.entries
|
||||
.filter { it != facing.opposite }
|
||||
.any { dir ->
|
||||
val neighborPos = pos.relative(dir)
|
||||
val neighborState = level.getBlockState(neighborPos)
|
||||
val neighborBlockId = BuiltInRegistries.BLOCK.getKey(neighborState.block)
|
||||
println("[TypeGemClusterBlock] Neighbor at $dir -> $neighborBlockId")
|
||||
isGem(neighborState)
|
||||
}
|
||||
|
||||
if (hasConflict) {
|
||||
println("[TypeGemClusterBlock] Found nearby TypeGemBlock(s). Stunting cluster at $pos.")
|
||||
level.setBlockAndUpdate(
|
||||
pos,
|
||||
state.setValue(STUNTED, true).setValue(SHOULD_GROW, false)
|
||||
)
|
||||
} else {
|
||||
println("[TypeGemClusterBlock] No conflicts. Converting cluster to TypeGemBlock.")
|
||||
var nextState = nextStage.defaultBlockState()
|
||||
if (nextState.hasProperty(FACING)) {
|
||||
nextState = nextState.setValue(FACING, facing)
|
||||
}
|
||||
level.setBlockAndUpdate(pos, nextState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun isGem(state: BlockState): Boolean {
|
||||
return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
|
||||
}
|
||||
|
||||
override fun getDrops(state: BlockState, params: LootParams.Builder): List<ItemStack> {
|
||||
val item = BuiltInRegistries.ITEM.getOptional(dropItemId).orElse(Items.AIR)
|
||||
val count = if (state.getValue(STAGE) >= 3) 2 else 1
|
||||
return listOf(ItemStack(item, count))
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: net.minecraft.world.level.BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: net.minecraft.world.phys.shapes.CollisionContext
|
||||
): VoxelShape {
|
||||
val stage = state.getValue(STAGE)
|
||||
val facing = state.getValue(FACING)
|
||||
|
||||
val (min, max) = when (stage) {
|
||||
0 -> 5.0 to 11.0
|
||||
1 -> 4.0 to 12.0
|
||||
2 -> 3.0 to 13.0
|
||||
3 -> 2.0 to 14.0
|
||||
else -> return Shapes.empty()
|
||||
}
|
||||
|
||||
return when (facing) {
|
||||
Direction.UP -> Shapes.box(min / 16, 0.0, min / 16, max / 16, (min + 1) / 16, max / 16)
|
||||
Direction.DOWN -> Shapes.box(min / 16, (16 - (min + 1)) / 16, min / 16, max / 16, 1.0, max / 16)
|
||||
Direction.NORTH -> Shapes.box(min / 16, min / 16, (16 - (min + 1)) / 16, max / 16, max / 16, 1.0)
|
||||
Direction.SOUTH -> Shapes.box((16 - max) / 16, min / 16, 0.0, (16 - min) / 16, max / 16, (min + 1) / 16)
|
||||
Direction.WEST -> Shapes.box((16 - (min + 1)) / 16, min / 16, min / 16, 1.0, max / 16, max / 16)
|
||||
Direction.EAST -> Shapes.box(0.0, min / 16, min / 16, (min + 1) / 16, max / 16, max / 16)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
val facing = state.getValue(FACING)
|
||||
if (direction == facing.opposite && !canSurvive(state, level, pos)) {
|
||||
return Blocks.AIR.defaultBlockState()
|
||||
}
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
val supportPos = pos.relative(state.getValue(FACING).opposite)
|
||||
val supportBlock = level.getBlockState(supportPos).block
|
||||
return supportBlock !is AirBlock
|
||||
}
|
||||
|
||||
|
||||
override fun codec(): MapCodec<out DirectionalBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(FACING, SHOULD_GROW, STAGE, STUNTED)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.STUNTED
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.WorldGenLevel
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.DirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import java.util.*
|
||||
|
||||
class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
|
||||
companion object {
|
||||
const val MAX_CONNECTED_GEMS = 30
|
||||
const val MIN_DISTANCE_BETWEEN_GEMS = 1
|
||||
const val CONTINUATION_CHANCE = 0.8f
|
||||
|
||||
val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
|
||||
mapOf(
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_normal") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_fire") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_water") to CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_electric") to CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_grass") to CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_ice") to CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_fighting") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_poison") to CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_ground") to CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_flying") to CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_psychic") to CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_bug") to CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_rock") to CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_ghost") to CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_dragon") to CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_dark") to CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_steel") to CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
|
||||
ResourceLocation.parse("cobblemon:type_gem_block_fairy") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
grow(level, pos, random)
|
||||
}
|
||||
|
||||
fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
|
||||
val desiredLength = MAX_CONNECTED_GEMS * percentage
|
||||
|
||||
while (true) {
|
||||
val growInfo = grow(level, pos, random, true)
|
||||
val grown = growInfo.first
|
||||
val clusterSize = growInfo.second
|
||||
|
||||
if (!grown || clusterSize >= desiredLength) break
|
||||
}
|
||||
}
|
||||
|
||||
private fun grow(
|
||||
level: WorldGenLevel,
|
||||
pos: BlockPos,
|
||||
random: RandomSource,
|
||||
forced: Boolean = false
|
||||
): Pair<Boolean, Int> {
|
||||
val connectedGems = getConnectedGems(level, pos)
|
||||
val gemCount = connectedGems.count { isGem(it.first) }
|
||||
val isOverLimit = gemCount >= MAX_CONNECTED_GEMS
|
||||
|
||||
val javaRandom = java.util.Random(random.nextLong())
|
||||
|
||||
for ((gemState, gemPos) in connectedGems.shuffled(javaRandom)) {
|
||||
val registryKey = BuiltInRegistries.BLOCK.getKey(gemState.block)
|
||||
val clusterBlock = BLOCK_TO_CLUSTER[registryKey]
|
||||
|
||||
for (dir in Direction.entries.shuffled(javaRandom)) {
|
||||
val targetPos = gemPos.relative(dir)
|
||||
val targetState = level.getBlockState(targetPos)
|
||||
|
||||
// Allow growth into air or existing clusters
|
||||
if (!(targetState.isAir || targetState.block is TypeGemClusterBlock)) continue
|
||||
if (!isPositionValidForGrowth(level, targetPos)) continue
|
||||
|
||||
// Place gem block
|
||||
val gemCopy = gemState.block.defaultBlockState()
|
||||
level.setBlock(targetPos, gemCopy, UPDATE_ALL)
|
||||
|
||||
// if forced immediately surround with clusters
|
||||
if (forced && clusterBlock != null) {
|
||||
for (clusterDir in Direction.entries) {
|
||||
val clusterPos = targetPos.relative(clusterDir)
|
||||
if (!level.getBlockState(clusterPos).isAir) continue
|
||||
|
||||
var clusterState = clusterBlock.defaultBlockState()
|
||||
|
||||
if (clusterState.hasProperty(DirectionalBlock.FACING)) {
|
||||
clusterState = clusterState.setValue(DirectionalBlock.FACING, clusterDir)
|
||||
}
|
||||
if (clusterState.hasProperty(SHOULD_GROW)) {
|
||||
clusterState = clusterState.setValue(SHOULD_GROW, true)
|
||||
}
|
||||
if (clusterState.hasProperty(STUNTED)) {
|
||||
clusterState = clusterState.setValue(STUNTED, isOverLimit)
|
||||
}
|
||||
if (clusterState.hasProperty(TypeGemClusterBlock.STAGE)) {
|
||||
val randomStage = random.nextInt(0, 4)
|
||||
clusterState = clusterState.setValue(TypeGemClusterBlock.STAGE, randomStage)
|
||||
}
|
||||
|
||||
//println("[TypeGemCoreBlock] Placing ${if (isOverLimit) "STUNTED" else "normal"} cluster at $clusterPos facing ${clusterDir.opposite}")
|
||||
level.setBlock(clusterPos, clusterState, UPDATE_ALL)
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(true, connectedGems.size)
|
||||
}
|
||||
}
|
||||
|
||||
println("[TypeGemCoreBlock] No valid spot found for gem placement.")
|
||||
return Pair(false, connectedGems.size)
|
||||
}
|
||||
|
||||
private fun getConnectedGems(level: WorldGenLevel, pos: BlockPos): List<Pair<BlockState, BlockPos>> {
|
||||
val connectedGems = mutableListOf<Pair<BlockState, BlockPos>>()
|
||||
val visited = mutableSetOf<BlockPos>()
|
||||
val queue: Queue<BlockPos> = LinkedList()
|
||||
|
||||
for (direction in Direction.entries) {
|
||||
val adjacent = pos.relative(direction)
|
||||
visited.add(adjacent)
|
||||
queue.add(adjacent)
|
||||
}
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
val current = queue.poll()
|
||||
val state = level.getBlockState(current)
|
||||
|
||||
if (!isGem(state)) continue
|
||||
connectedGems.add(state to current)
|
||||
|
||||
for (direction in Direction.entries) {
|
||||
val neighbor = current.relative(direction)
|
||||
if (visited.add(neighbor)) queue.add(neighbor)
|
||||
}
|
||||
}
|
||||
|
||||
return connectedGems
|
||||
}
|
||||
|
||||
private fun isPositionValidForGrowth(level: WorldGenLevel, pos: BlockPos): Boolean {
|
||||
return Direction.entries.none { dir ->
|
||||
val nearby = pos.relative(dir)
|
||||
isGem(level.getBlockState(nearby)) &&
|
||||
pos.distManhattan(nearby) < MIN_DISTANCE_BETWEEN_GEMS
|
||||
}
|
||||
}
|
||||
|
||||
private fun isGem(state: BlockState): Boolean {
|
||||
return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = true
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineBlock
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.world.ContainerHelper
|
||||
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.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.inventory.ContainerData
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
class TMMachineBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(CobblemonBlockEntities.TM_MACHINE, pos, state) {
|
||||
companion object {
|
||||
const val BURN_ACTIVE_TAG = "burnActive"
|
||||
const val BURN_PROGRESS_TAG = "burnProgress"
|
||||
const val REPEAT_PROCESS_TAG = "repeatProcess"
|
||||
const val ACTIVE_MOVE_TAG = "activeMove"
|
||||
|
||||
const val BURN_PROGRESS_PER_TICK = 2
|
||||
const val BURN_TOTAL_TIME = 200
|
||||
const val TOTAL_PROCESS_TIME = BURN_TOTAL_TIME + ((TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS) * BURN_PROGRESS_PER_TICK)
|
||||
|
||||
// Container data IDs
|
||||
const val BURN_PROGRESS_INDEX = 0
|
||||
const val BURN_ACTIVE_INDEX = 1
|
||||
const val REPEAT_PROCESS_INDEX = 2
|
||||
const val POSITION_X_INDEX = 3
|
||||
const val POSITION_Y_INDEX = 4
|
||||
const val POSITION_Z_INDEX = 5
|
||||
|
||||
fun serverTick(level: Level, pos: BlockPos, state: BlockState, blockEntity: TMMachineBlockEntity) {
|
||||
if (level.isClientSide) return
|
||||
val containerData = blockEntity.containerData
|
||||
|
||||
if (containerData.get(BURN_ACTIVE_INDEX) == 1) {
|
||||
val currentProgress = containerData.get(BURN_PROGRESS_INDEX)
|
||||
if (currentProgress < TOTAL_PROCESS_TIME) {
|
||||
val progressPerTick = if (currentProgress >= BURN_TOTAL_TIME) 1 else BURN_PROGRESS_PER_TICK
|
||||
containerData.set(BURN_PROGRESS_INDEX, currentProgress + progressPerTick)
|
||||
}
|
||||
}
|
||||
|
||||
val burnProgressValue = containerData.get(BURN_PROGRESS_INDEX)
|
||||
val postCraftTicks = if (burnProgressValue >= BURN_TOTAL_TIME) (burnProgressValue - BURN_TOTAL_TIME) else 0
|
||||
if (postCraftTicks > 0) {
|
||||
if (postCraftTicks == TMMachineScreen.CRAFT_TICKS) {
|
||||
blockEntity.craftTM()
|
||||
}
|
||||
if (postCraftTicks >= (TMMachineScreen.CRAFT_TICKS + TMMachineScreen.RESET_DISC_TICKS)) {
|
||||
val shouldRepeatProcess = containerData.get(REPEAT_PROCESS_INDEX) == 1
|
||||
if (!shouldRepeatProcess) containerData.set(BURN_ACTIVE_INDEX, 0) // Set active to false if only running once
|
||||
containerData.set(BURN_PROGRESS_INDEX, 0)
|
||||
blockEntity.setChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tmMachineInventory = TMMachineBlockInventory(this)
|
||||
var partialTicks: Float = 0F
|
||||
|
||||
var burnProgress : Int = 0
|
||||
var burnActive : Boolean = false
|
||||
var repeatProcess: Boolean = false
|
||||
var activeMove: String = ""
|
||||
|
||||
var containerData: ContainerData = object : ContainerData {
|
||||
override fun get(index: Int): Int {
|
||||
return when (index) {
|
||||
BURN_PROGRESS_INDEX -> burnProgress
|
||||
BURN_ACTIVE_INDEX -> if (burnActive) 1 else 0
|
||||
REPEAT_PROCESS_INDEX -> if (repeatProcess) 1 else 0
|
||||
POSITION_X_INDEX -> blockPos.x
|
||||
POSITION_Y_INDEX -> blockPos.y
|
||||
POSITION_Z_INDEX -> blockPos.z
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun set(index: Int, value: Int) {
|
||||
when (index) {
|
||||
BURN_PROGRESS_INDEX -> burnProgress = value
|
||||
BURN_ACTIVE_INDEX -> burnActive = value == 1
|
||||
REPEAT_PROCESS_INDEX -> repeatProcess = value == 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return 6
|
||||
}
|
||||
}
|
||||
|
||||
private fun craftTM() {
|
||||
if (!(level?.isClientSide ?: true)) {
|
||||
Moves.getByName(activeMove)?.let { move ->
|
||||
TechnicalMachines.moveToTM[move]?.let { tm ->
|
||||
val resultStack = getItem(TMMachineMenu.RESULT_SLOT)
|
||||
if (!resultStack.isEmpty && resultStack.count >= resultStack.maxStackSize) return
|
||||
|
||||
// Craft the TM
|
||||
val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
||||
.also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
||||
|
||||
if (!resultStack.isEmpty && !ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)) return
|
||||
|
||||
val blankTmStack = getItem(TMMachineMenu.BLANK_TM_SLOT)
|
||||
if (blankTmStack.item != CobblemonItems.BLANK_TM) return
|
||||
|
||||
val recipe = tm.getClampedRecipe() ?: emptyList()
|
||||
for ((index, ingredient) in recipe.withIndex()) {
|
||||
val slot = TMMachineMenu.INGREDIENT_SLOTS.first + index
|
||||
val provided = getItem(slot)
|
||||
val expected = level?.itemRegistry?.get(ingredient.item) ?: return
|
||||
if (provided.item != expected || provided.count < ingredient.count) return
|
||||
}
|
||||
|
||||
// Consume ingredients
|
||||
blankTmStack.shrink(1)
|
||||
for ((index, ingredient) in recipe.withIndex()) {
|
||||
getItem(TMMachineMenu.INGREDIENT_SLOTS.first + index).shrink(ingredient.count)
|
||||
}
|
||||
|
||||
// Add crafted TM to result slot
|
||||
if (resultStack.isEmpty) {
|
||||
setItem(TMMachineMenu.RESULT_SLOT, craftedTmStack)
|
||||
} else if (resultStack.count < resultStack.maxStackSize) {
|
||||
resultStack.grow(1)
|
||||
}
|
||||
|
||||
tmMachineInventory.setChanged()
|
||||
setChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createMenu(containerId: Int, inventory: Inventory): AbstractContainerMenu {
|
||||
return TMMachineMenu(containerId, inventory, tmMachineInventory, containerData)
|
||||
}
|
||||
|
||||
override fun saveAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(compound, registries)
|
||||
compound.putBoolean(BURN_ACTIVE_TAG, burnActive)
|
||||
compound.putBoolean(REPEAT_PROCESS_TAG, repeatProcess)
|
||||
compound.putInt(BURN_PROGRESS_TAG, burnProgress)
|
||||
compound.putString(ACTIVE_MOVE_TAG, activeMove)
|
||||
ContainerHelper.saveAllItems(compound, tmMachineInventory.items, registries)
|
||||
}
|
||||
|
||||
override fun loadAdditional(compound: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(compound, registries)
|
||||
burnActive = compound.getBoolean(BURN_ACTIVE_TAG)
|
||||
repeatProcess = compound.getBoolean(REPEAT_PROCESS_TAG)
|
||||
burnProgress = compound.getInt(BURN_PROGRESS_TAG)
|
||||
activeMove = compound.getString(ACTIVE_MOVE_TAG)
|
||||
ContainerHelper.loadAllItems(compound, tmMachineInventory.items, registries)
|
||||
}
|
||||
|
||||
override fun getDisplayName(): Component = Component.translatable("block.cobblemon.tm_machine")
|
||||
|
||||
override fun getDefaultName(): Component = Component.translatable("cobblemon.container.tm_machine")
|
||||
|
||||
override fun getItems(): NonNullList<ItemStack> = tmMachineInventory.items
|
||||
|
||||
override fun setItems(items: NonNullList<ItemStack>) {
|
||||
for (i in items.indices) {
|
||||
tmMachineInventory.setItem(i, items[i])
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener> = ClientboundBlockEntityDataPacket.create(this)
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag = saveWithoutMetadata(registryLookup)
|
||||
|
||||
override fun getContainerSize(): Int = tmMachineInventory.containerSize
|
||||
|
||||
override fun isEmpty(): Boolean = tmMachineInventory.isEmpty
|
||||
|
||||
override fun getItem(slot: Int): ItemStack = tmMachineInventory.getItem(slot)
|
||||
|
||||
override fun removeItem(slot: Int, amount: Int): ItemStack = tmMachineInventory.removeItem(slot, amount)
|
||||
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack = tmMachineInventory.removeItemNoUpdate(slot)
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) = tmMachineInventory.setItem(slot, stack)
|
||||
|
||||
override fun setChanged() {
|
||||
var currentState = blockState
|
||||
level?.getBlockState(worldPosition)?.let { state ->
|
||||
currentState = state
|
||||
var updated = false
|
||||
|
||||
if (state.hasProperty(TMMachineBlock.EMPTY)) {
|
||||
val isEmpty = getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty
|
||||
if (currentState.getValue(TMMachineBlock.EMPTY) != isEmpty) {
|
||||
if (!isEmpty) playSound(CobblemonSounds.TM_MACHINE_PLACE_DISC, 0.5F)
|
||||
currentState = currentState.setValue(TMMachineBlock.EMPTY, isEmpty)
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
if (state.hasProperty(TMMachineBlock.ACTIVE)) {
|
||||
val isActive = containerData.get(BURN_ACTIVE_INDEX) == 1
|
||||
if (currentState.getValue(TMMachineBlock.ACTIVE) != isActive) {
|
||||
currentState = currentState.setValue(TMMachineBlock.ACTIVE, isActive)
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
if (updated) level?.setBlockAndUpdate(worldPosition, currentState)
|
||||
}
|
||||
|
||||
// Notify the block entity's level that this block entity has changed
|
||||
level?.blockEntityChanged(worldPosition)
|
||||
|
||||
// Mark the chunk containing this block entity as dirty, ensuring it is saved
|
||||
level?.getChunkAt(worldPosition)?.setUnsaved(true)
|
||||
|
||||
// Update Neighbours
|
||||
level?.updateNeighborsAt(blockPos, currentState.block)
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean = tmMachineInventory.stillValid(player)
|
||||
|
||||
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean = tmMachineInventory.canPlaceItem(slot, stack)
|
||||
|
||||
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean = tmMachineInventory.canTakeItem(target, slot, stack)
|
||||
|
||||
fun playSound(soundEvent: SoundEvent, volume: Float = 1F, pitch: Float = 1F) {
|
||||
level?.playSound(null, worldPosition, soundEvent, SoundSource.BLOCKS, volume, pitch)
|
||||
}
|
||||
|
||||
class TMMachineBlockInventory(val blockEntity: TMMachineBlockEntity) : SimpleContainer(6) {
|
||||
override fun canTakeItem(target: Container, slot: Int, stack: ItemStack): Boolean =
|
||||
if (slot == 0) true else false
|
||||
|
||||
override fun canPlaceItem(slot: Int, stack: ItemStack): Boolean {
|
||||
if (blockEntity.containerData.get(BURN_ACTIVE_INDEX) == 1) {
|
||||
Moves.getByName(blockEntity.activeMove)?.let {
|
||||
val tm = TechnicalMachines.moveToTM[it] ?: return false
|
||||
val item = stack.item
|
||||
|
||||
return when (slot) {
|
||||
1 -> item == CobblemonItems.BLANK_TM
|
||||
2, 3, 4 -> {
|
||||
val recipe = tm.getClampedRecipe() ?: return false
|
||||
val recipeIndex = slot - 2
|
||||
if (recipeIndex >= recipe.size) return false
|
||||
val expected = blockEntity.level?.itemRegistry?.get(recipe[recipeIndex].item) ?: return false
|
||||
item == expected
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
blockEntity.level?.updateNeighborsAt(blockEntity.blockPos, blockEntity.blockState.block)
|
||||
super.setChanged()
|
||||
}
|
||||
|
||||
override fun startOpen(player: Player) {
|
||||
blockEntity.startOpen(player)
|
||||
}
|
||||
|
||||
override fun stopOpen(player: Player) {
|
||||
blockEntity.stopOpen(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.TMShelfBlock
|
||||
import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.Containers
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import java.util.OptionalInt
|
||||
|
||||
class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.TM_SHELF, pos, state) {
|
||||
val items: NonNullList<ItemStack> = NonNullList.withSize(14, ItemStack.EMPTY)
|
||||
var lastInteractedSlot: Int = -1
|
||||
|
||||
fun handleUseItem(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): ItemInteractionResult {
|
||||
if (stack.isEmpty) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
val slot = getHitSlot(hit, state).orElse(-1)
|
||||
if (slot !in 0 until 14) return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
if (!isValidItem(stack)) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
|
||||
val oldStack = items[slot]
|
||||
if (!oldStack.isEmpty) {
|
||||
if (!player.addItem(oldStack)) {
|
||||
Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, oldStack)
|
||||
}
|
||||
}
|
||||
|
||||
items[slot] = stack.copyWithCount(1)
|
||||
stack.shrink(1)
|
||||
lastInteractedSlot = slot
|
||||
updateBlockState(level, pos)
|
||||
markUpdated()
|
||||
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
}
|
||||
|
||||
fun handleUseWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
|
||||
val slot = getHitSlot(hit, state).orElse(-1)
|
||||
if (slot !in 0 until 14 || items[slot].isEmpty) return InteractionResult.PASS
|
||||
|
||||
val removed = items[slot]
|
||||
items[slot] = ItemStack.EMPTY
|
||||
if (!player.addItem(removed)) {
|
||||
Containers.dropItemStack(level, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, removed)
|
||||
}
|
||||
|
||||
lastInteractedSlot = slot
|
||||
updateBlockState(level, pos)
|
||||
|
||||
return InteractionResult.sidedSuccess(level.isClientSide)
|
||||
}
|
||||
|
||||
private fun updateBlockState(level: Level, pos: BlockPos) {
|
||||
val newState = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.fold(blockState) { acc, prop ->
|
||||
val index = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.indexOf(prop)
|
||||
acc.setValue(prop, !items[index].isEmpty)
|
||||
}
|
||||
level.setBlock(pos, newState, 3)
|
||||
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState))
|
||||
}
|
||||
|
||||
private fun getHitSlot(hit: BlockHitResult, state: BlockState): OptionalInt {
|
||||
val facing = state.getValue(TMShelfBlock.FACING)
|
||||
val relative = hit.location.subtract(hit.blockPos.x.toDouble(), hit.blockPos.y.toDouble(), hit.blockPos.z.toDouble())
|
||||
|
||||
val (x, y) = when (facing) {
|
||||
Direction.NORTH -> 1.0 - relative.x to relative.y
|
||||
Direction.SOUTH -> relative.x to relative.y
|
||||
Direction.WEST -> 1.0 - relative.z to relative.y
|
||||
Direction.EAST -> relative.z to relative.y
|
||||
else -> return OptionalInt.empty()
|
||||
}
|
||||
|
||||
val col = if (x < 0.5) 1 else 0
|
||||
val row = ((1.0 - y) * 7).toInt().coerceIn(0, 6)
|
||||
|
||||
return OptionalInt.of(row * 2 + col)
|
||||
}
|
||||
|
||||
fun isValidItem(stack: ItemStack): Boolean {
|
||||
return stack.item is TechnicalMachineItem
|
||||
|| stack.item == CobblemonItems.UPGRADE
|
||||
|| stack.item == CobblemonItems.DUBIOUS_DISC
|
||||
|| stack.item == Items.MUSIC_DISC_13
|
||||
|| stack.item == Items.MUSIC_DISC_CAT
|
||||
|| stack.item == Items.MUSIC_DISC_BLOCKS
|
||||
|| stack.item == Items.MUSIC_DISC_CHIRP
|
||||
|| stack.item == Items.MUSIC_DISC_FAR
|
||||
|| stack.item == Items.MUSIC_DISC_MALL
|
||||
|| stack.item == Items.MUSIC_DISC_MELLOHI
|
||||
|| stack.item == Items.MUSIC_DISC_STAL
|
||||
|| stack.item == Items.MUSIC_DISC_STRAD
|
||||
|| stack.item == Items.MUSIC_DISC_WARD
|
||||
|| stack.item == Items.MUSIC_DISC_11
|
||||
|| stack.item == Items.MUSIC_DISC_WAIT
|
||||
|| stack.item == Items.MUSIC_DISC_OTHERSIDE
|
||||
|| stack.item == Items.MUSIC_DISC_5
|
||||
|| stack.item == Items.MUSIC_DISC_RELIC
|
||||
|| stack.item == Items.MUSIC_DISC_PRECIPICE
|
||||
|| stack.item == Items.MUSIC_DISC_CREATOR
|
||||
|| stack.item == Items.MUSIC_DISC_CREATOR_MUSIC_BOX
|
||||
|| stack.item == Items.MUSIC_DISC_PIGSTEP
|
||||
}
|
||||
|
||||
fun markUpdated() {
|
||||
level?.setBlock(blockPos, blockState, 3)
|
||||
level?.sendBlockUpdated(blockPos, blockState, blockState, 3)
|
||||
setChanged()
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
|
||||
return saveWithoutMetadata(registries)
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): ClientboundBlockEntityDataPacket {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
ContainerHelper.saveAllItems(tag, items, true, registries)
|
||||
tag.putInt("LastInteractedSlot", lastInteractedSlot)
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
items.clear()
|
||||
ContainerHelper.loadAllItems(tag, items, registries)
|
||||
lastInteractedSlot = tag.getInt("LastInteractedSlot")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.Containers
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
class TMMachineBlock(properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
|
||||
|
||||
companion object {
|
||||
val EMPTY: BooleanProperty = BooleanProperty.create("empty")
|
||||
val FACING = BlockStateProperties.HORIZONTAL_FACING
|
||||
val WATERLOGGED = BlockStateProperties.WATERLOGGED
|
||||
val ACTIVE: BooleanProperty = BooleanProperty.create("active")
|
||||
val OPEN: BooleanProperty = BooleanProperty.create("open")
|
||||
|
||||
private val SHAPE: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.375, 1.0)
|
||||
|
||||
private val SHAPE_OPEN: VoxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0)
|
||||
|
||||
private val SHAPE_OPEN_NORTH: VoxelShape = Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
Shapes.box(0.0, 0.3125, 0.8125, 1.0, 0.4375, 0.9375),
|
||||
Shapes.box(0.0, 0.4375, 0.6875, 1.0, 0.5625, 0.8125),
|
||||
Shapes.box(0.0, 0.5625, 0.5625, 1.0, 0.6875, 0.6875),
|
||||
Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
|
||||
Shapes.box(0.0, 0.8125, 0.3125, 1.0, 0.9375, 0.4375),
|
||||
Shapes.box(0.0, 0.9375, 0.3125, 1.0, 1.0, 0.375)
|
||||
)
|
||||
|
||||
private val SHAPE_OPEN_SOUTH: VoxelShape = Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
Shapes.box(0.0, 0.3125, 0.0625, 1.0, 0.4375, 0.1875),
|
||||
Shapes.box(0.0, 0.4375, 0.1875, 1.0, 0.5625, 0.3125),
|
||||
Shapes.box(0.0, 0.5625, 0.3125, 1.0, 0.6875, 0.4375),
|
||||
Shapes.box(0.0, 0.6875, 0.4375, 1.0, 0.8125, 0.5625),
|
||||
Shapes.box(0.0, 0.8125, 0.5625, 1.0, 0.9375, 0.6875),
|
||||
Shapes.box(0.0, 0.9375, 0.625, 1.0, 1.0, 0.6875)
|
||||
)
|
||||
|
||||
private val SHAPE_OPEN_EAST: VoxelShape = Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
Shapes.box(0.0625, 0.3125, 0.0, 0.1875, 0.4375, 1.0),
|
||||
Shapes.box(0.1875, 0.4375, 0.0, 0.3125, 0.5625, 1.0),
|
||||
Shapes.box(0.3125, 0.5625, 0.0, 0.4375, 0.6875, 1.0),
|
||||
Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
|
||||
Shapes.box(0.5625, 0.8125, 0.0, 0.6875, 0.9375, 1.0),
|
||||
Shapes.box(0.625, 0.9375, 0.0, 0.6875, 1.0, 1.0)
|
||||
)
|
||||
|
||||
private val SHAPE_OPEN_WEST: VoxelShape = Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
Shapes.box(0.8125, 0.3125, 0.0, 0.9375, 0.4375, 1.0),
|
||||
Shapes.box(0.6875, 0.4375, 0.0, 0.8125, 0.5625, 1.0),
|
||||
Shapes.box(0.5625, 0.5625, 0.0, 0.6875, 0.6875, 1.0),
|
||||
Shapes.box(0.4375, 0.6875, 0.0, 0.5625, 0.8125, 1.0),
|
||||
Shapes.box(0.3125, 0.8125, 0.0, 0.4375, 0.9375, 1.0),
|
||||
Shapes.box(0.3125, 0.9375, 0.0, 0.375, 1.0, 1.0)
|
||||
)
|
||||
|
||||
val CODEC: MapCodec<TMMachineBlock> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Properties.CODEC.fieldOf("properties").forGetter { it.properties }
|
||||
).apply(instance, ::TMMachineBlock)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false)
|
||||
.setValue(ACTIVE, false)
|
||||
.setValue(EMPTY, true))
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, state: BlockState, blockEntityType: BlockEntityType<T?>): BlockEntityTicker<T?>? {
|
||||
return createTicker(level, blockEntityType as BlockEntityType<*>, CobblemonBlockEntities.TM_MACHINE) as BlockEntityTicker<T?>?
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected fun <T : BlockEntity> createTicker(level: Level, serverType: BlockEntityType<T>, clientType: BlockEntityType<out TMMachineBlockEntity>): BlockEntityTicker<T>? {
|
||||
return if (!level.isClientSide) createTickerHelper(serverType, clientType, TMMachineBlockEntity::serverTick)
|
||||
else null
|
||||
}
|
||||
|
||||
override fun fallOn(level: Level, state: BlockState, pos: BlockPos, entity: Entity, fallDistance: Float) {
|
||||
if (!level.isClientSide && (entity is LivingEntity) &&
|
||||
(entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) &&
|
||||
(entity.y >= (pos.y + 0.55)) &&
|
||||
state.hasProperty(OPEN) && state.getValue(OPEN)
|
||||
) {
|
||||
level.setBlockAndUpdate(pos, state.setValue(OPEN, false))
|
||||
level.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.TM_MACHINE_CLOSE, volume = 1F, pitch = 1F)
|
||||
}
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = TMMachineBlockEntity(pos, state)
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
|
||||
return defaultBlockState()
|
||||
.setValue(FACING, context.horizontalDirection.opposite)
|
||||
.setValue(WATERLOGGED, context.level.getFluidState(context.clickedPos).type == Fluids.WATER)
|
||||
.setValue(EMPTY, true)
|
||||
.setValue(ACTIVE, false)
|
||||
.setValue(OPEN, false)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hit: BlockHitResult): InteractionResult {
|
||||
if (!level.isClientSide) {
|
||||
if (player.isCrouching) {
|
||||
if (state.hasProperty(OPEN)) {
|
||||
val isOpen = state.getValue(OPEN)
|
||||
level.setBlockAndUpdate(pos, state.setValue(OPEN, !isOpen))
|
||||
level.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
sound = if (!isOpen) CobblemonSounds.TM_MACHINE_CLOSE else CobblemonSounds.TM_MACHINE_OPEN,
|
||||
volume = 1F,
|
||||
pitch = 1F
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (blockEntity is TMMachineBlockEntity) {
|
||||
player.openMenu(blockEntity)
|
||||
level.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
sound = CobblemonSounds.TM_MACHINE_ON,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
)
|
||||
level.gameEvent(player, GameEvent.BLOCK_OPEN, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, WATERLOGGED, EMPTY, ACTIVE, OPEN)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<TMMachineBlock> = CODEC
|
||||
|
||||
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState =
|
||||
if (state.getValue(WATERLOGGED)) Fluids.WATER.defaultFluidState() else super.getFluidState(state)
|
||||
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return if (state.getValue(OPEN)) {
|
||||
when (state.getValue(FACING)) {
|
||||
Direction.NORTH -> SHAPE_OPEN_NORTH
|
||||
Direction.SOUTH -> SHAPE_OPEN_SOUTH
|
||||
Direction.WEST -> SHAPE_OPEN_WEST
|
||||
Direction.EAST -> SHAPE_OPEN_EAST
|
||||
else -> Shapes.empty()
|
||||
}
|
||||
} else SHAPE
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, getter: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = if (state.getValue(OPEN)) SHAPE_OPEN else SHAPE
|
||||
|
||||
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
|
||||
Containers.dropContentsOnDestroy(state, newState, level, pos)
|
||||
super.onRemove(state, level, pos, newState, movedByPiston)
|
||||
}
|
||||
|
||||
override fun isSignalSource(state: BlockState): Boolean = true
|
||||
|
||||
override fun getSignal(state: BlockState, level: BlockGetter, pos: BlockPos, direction: Direction): Int {
|
||||
level.getBlockEntity(pos)?.let { blockEntity ->
|
||||
if (blockEntity is TMMachineBlockEntity && blockEntity.burnActive) return 15
|
||||
}
|
||||
return super.getSignal(state, level, pos, direction)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState): Boolean = true
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int = getSignal(state, level, pos, Direction.UP)
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonMenuType
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class TMMachineMenu(menuType: MenuType<*>, syncId: Int) : AbstractContainerMenu(menuType, syncId), ContainerListener {
|
||||
companion object {
|
||||
const val RESULT_SLOT = 0
|
||||
const val BLANK_TM_SLOT = 1
|
||||
val INGREDIENT_SLOTS = 2..4
|
||||
}
|
||||
|
||||
var playerInventory: Inventory? = null
|
||||
var tmMachineEntity: TMMachineBlockEntity? = null
|
||||
var inventory: Container? = null
|
||||
var containerData: ContainerData? = null
|
||||
|
||||
constructor(syncId: Int, playerInventory: Inventory) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.inventory = SimpleContainer(5)
|
||||
this.containerData = SimpleContainerData(6)
|
||||
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
|
||||
|
||||
this.addDataSlots(containerData)
|
||||
inventory?.startOpen(playerInventory.player)
|
||||
|
||||
val startX = 15
|
||||
val startY = 144
|
||||
val slotWidth = 18
|
||||
val slotHeight = 18
|
||||
|
||||
for (row in 0..2) {
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
|
||||
}
|
||||
}
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
||||
}
|
||||
|
||||
val container = inventory
|
||||
container?.let {
|
||||
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
||||
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
||||
for (index in INGREDIENT_SLOTS) {
|
||||
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
||||
}
|
||||
}
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
constructor(syncId: Int, playerInventory: Inventory, inventory: Container, containerData: ContainerData) : this(CobblemonMenuType.TM_MACHINE, syncId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.inventory = inventory
|
||||
this.containerData = containerData
|
||||
this.tmMachineEntity = (inventory as? TMMachineBlockEntity.TMMachineBlockInventory)?.blockEntity
|
||||
|
||||
this.addDataSlots(containerData)
|
||||
inventory.startOpen(playerInventory.player)
|
||||
|
||||
val startX = 15
|
||||
val startY = 144
|
||||
val slotWidth = 18
|
||||
val slotHeight = 18
|
||||
|
||||
for (row in 0..2) {
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, 9 + (row * 9) + col, startX + (slotWidth * col), startY + (slotHeight * row)))
|
||||
}
|
||||
}
|
||||
for (col in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, col, startX + (slotWidth * col), startY + 58))
|
||||
}
|
||||
|
||||
val container = inventory
|
||||
container.let {
|
||||
this.addSlot(TMResultSlot(container, RESULT_SLOT, startX + 37, startY - 28)) // Output slot
|
||||
this.addSlot(BlankTMSlot(container, BLANK_TM_SLOT, startX + 90, startY - 28)) // Blank TM
|
||||
for (index in INGREDIENT_SLOTS) {
|
||||
this.addSlot(Slot(container, index, startX + 114 + (18 * (index - INGREDIENT_SLOTS.first)), startY - 28))
|
||||
}
|
||||
}
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
override fun quickMoveStack(player: Player, index: Int): ItemStack {
|
||||
var itemStack = ItemStack.EMPTY
|
||||
val slot = this.slots[index]
|
||||
val maxPlayerInventoryIndex = Inventory.INVENTORY_SIZE - 1
|
||||
|
||||
if (slot.hasItem()) {
|
||||
val movedStack = slot.item
|
||||
itemStack = movedStack.copy()
|
||||
|
||||
// If in container slots, move to player inventory
|
||||
// 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
|
||||
if (index in 36..40) {
|
||||
if (!this.moveItemStackTo(movedStack, 0, maxPlayerInventoryIndex, false)) return ItemStack.EMPTY
|
||||
}
|
||||
// If in player inventory, move to container slots. 'endIndex' is non-inclusive
|
||||
else if (index <= maxPlayerInventoryIndex) {
|
||||
if (!this.moveItemStackTo(movedStack, 37, 40 + 1, false)) return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
if (movedStack.isEmpty) {
|
||||
slot.setByPlayer(ItemStack.EMPTY)
|
||||
} else {
|
||||
slot.setChanged()
|
||||
}
|
||||
}
|
||||
return itemStack
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return player.let { inventory?.stillValid(it) } ?: false
|
||||
}
|
||||
|
||||
fun getBurnProgressValue(): Int = this.containerData?.get(TMMachineBlockEntity.BURN_PROGRESS_INDEX) ?: 0
|
||||
|
||||
fun getBurnProgressRatio(): Float {
|
||||
val amount = getBurnProgressValue()
|
||||
val max = TMMachineBlockEntity.BURN_TOTAL_TIME
|
||||
return if (amount != 0) {
|
||||
Mth.clamp((amount.toFloat() / max.toFloat()), 0.0F, 1.0F)
|
||||
} else {
|
||||
0.0F
|
||||
}
|
||||
}
|
||||
|
||||
fun getPostCraftTicks(): Int {
|
||||
return if (getBurnProgressValue() >= TMMachineBlockEntity.BURN_TOTAL_TIME) {
|
||||
getBurnProgressValue() - TMMachineBlockEntity.BURN_TOTAL_TIME
|
||||
} else 0
|
||||
}
|
||||
|
||||
override fun removed(player: Player) {
|
||||
super.removed(player)
|
||||
tmMachineEntity?.setChanged()
|
||||
}
|
||||
|
||||
class TMResultSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
}
|
||||
|
||||
class BlankTMSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
|
||||
override fun mayPlace(stack: ItemStack): Boolean = super.mayPlace(stack) && (stack.item == CobblemonItems.BLANK_TM)
|
||||
}
|
||||
|
||||
override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
|
||||
val level = playerInventory?.player?.level()
|
||||
|
||||
// If ingredient slots have been modified and level is server side and process is active
|
||||
// 36: Output slot, 37: Blank TM slot, 38-40: Ingredient slots
|
||||
if ((dataSlotIndex in 37 .. 40) && (level?.isClientSide == false) && (containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) == 1)) {
|
||||
// Stop process if there are not enough ingredients after slots have been modified
|
||||
var validCost = !(inventory?.getItem(BLANK_TM_SLOT)?.isEmpty ?: true)
|
||||
|
||||
if (validCost) {
|
||||
Moves.getByName(tmMachineEntity?.activeMove ?: "")?.let { moveTemplate ->
|
||||
val serverLevel = level as ServerLevel
|
||||
TechnicalMachines.moveToTM[moveTemplate]?.let { tm ->
|
||||
val recipe = tm.getClampedRecipe() ?: emptyList()
|
||||
for ((index, ingredient) in recipe.withIndex()) {
|
||||
val slot = INGREDIENT_SLOTS.first + index
|
||||
val provided = inventory?.getItem(slot) ?: ItemStack.EMPTY
|
||||
val expected = serverLevel.itemRegistry.get(ingredient.item) ?: ItemStack.EMPTY.item
|
||||
|
||||
if (provided?.item != expected || provided.count < ingredient.count) {
|
||||
validCost = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!validCost) {
|
||||
containerData?.set(TMMachineBlockEntity.BURN_ACTIVE_INDEX, 0)
|
||||
containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
|
||||
}
|
||||
}
|
||||
|
||||
tmMachineEntity?.setChanged()
|
||||
inventory?.setChanged()
|
||||
playerInventory?.setChanged()
|
||||
broadcastChanges()
|
||||
}
|
||||
|
||||
override fun dataChanged(containerMenu: AbstractContainerMenu, dataSlotIndex: Int, value: Int) {
|
||||
broadcastChanges()
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.entity.TintBlockEntity
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
@@ -26,11 +27,13 @@ import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.client.gui.tmmachine.TMMachineScreen
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeSnackItemColorProvider
|
||||
@@ -73,6 +76,7 @@ import net.minecraft.client.renderer.blockentity.HangingSignRenderer
|
||||
import net.minecraft.client.renderer.blockentity.SignRenderer
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer
|
||||
import net.minecraft.client.resources.PlayerSkin
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import net.minecraft.world.InteractionHand
|
||||
@@ -87,6 +91,7 @@ object CobblemonClient {
|
||||
var battle: ClientBattle? = null
|
||||
var clientPlayerData = ClientGeneralPlayerData()
|
||||
var clientPokedexData = ClientPokedexManager(mutableMapOf())
|
||||
var clientTMMoveData = ClientTMMoveManager(mutableSetOf())
|
||||
|
||||
/** If true then we won't bother them anymore about choosing a starter even if it's a thing they can do. */
|
||||
var checkedStarterScreen = false
|
||||
@@ -103,6 +108,7 @@ object CobblemonClient {
|
||||
requests = ClientPlayerActionRequests()
|
||||
teamData = ClientPlayerTeamData()
|
||||
clientPokedexData = ClientPokedexManager(mutableMapOf())
|
||||
clientTMMoveData = ClientTMMoveManager(mutableSetOf())
|
||||
storage.onLogin()
|
||||
CobblemonDataProvider.canReload = false
|
||||
}
|
||||
@@ -154,9 +160,9 @@ object CobblemonClient {
|
||||
if (pokedexUsageContext.scanningGuiOpen &&
|
||||
!(selectedItem.`is`(CobblemonItemTags.POKEDEX)) &&
|
||||
!(player.offhandItem.`is`(CobblemonItemTags.POKEDEX) &&
|
||||
player.isUsingItem == true &&
|
||||
player.usedItemHand == InteractionHand.OFF_HAND
|
||||
)
|
||||
player.isUsingItem == true &&
|
||||
player.usedItemHand == InteractionHand.OFF_HAND
|
||||
)
|
||||
) {
|
||||
// Stop using Pokédex in main hand if player switches to a different slot in hotbar
|
||||
pokedexUsageContext.stopUsing(PokedexUsageContext.OPEN_SCANNER_BUFFER_TICKS + 1)
|
||||
@@ -165,10 +171,7 @@ object CobblemonClient {
|
||||
return@subscribe
|
||||
}
|
||||
|
||||
val nearbyPokemon = player.level().getEntities(
|
||||
player,
|
||||
AABB.ofSize(player.position(), 16.0, 16.0, 16.0)
|
||||
) { it is PokemonEntity }
|
||||
val nearbyPokemon = player.level().getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0)) { it is PokemonEntity }
|
||||
|
||||
nearbyPokemon?.forEach { entity ->
|
||||
if (entity is PokemonEntity && !entity.isSilent) {
|
||||
@@ -188,6 +191,7 @@ object CobblemonClient {
|
||||
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(AprijuiceTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(TechnicalMachineTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(PokePuffTooltipGenerator)
|
||||
}
|
||||
|
||||
@@ -268,6 +272,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
*CobblemonBlocks.berries().values.toTypedArray(),
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.GALARICA_NUT_BUSH,
|
||||
CobblemonBlocks.RESTORATION_TANK,
|
||||
CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE,
|
||||
@@ -278,6 +283,24 @@ object CobblemonClient {
|
||||
CobblemonBlocks.MEDIUM_BUDDING_BLACK_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_BLACK_TUMBLESTONE,
|
||||
CobblemonBlocks.BLACK_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
|
||||
CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY,
|
||||
CobblemonBlocks.SMALL_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
||||
@@ -302,7 +325,8 @@ object CobblemonClient {
|
||||
CobblemonBlocks.PINK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.RED_CAMPFIRE_POT,
|
||||
CobblemonBlocks.WHITE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.YELLOW_CAMPFIRE_POT
|
||||
CobblemonBlocks.YELLOW_CAMPFIRE_POT,
|
||||
CobblemonBlocks.TM_MACHINE
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -321,8 +345,7 @@ object CobblemonClient {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun onAddLayer(skinMap: Map<PlayerSkin.Model, EntityRenderer<out Player>>?) {
|
||||
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? =
|
||||
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
|
||||
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? = skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
@@ -330,29 +353,22 @@ object CobblemonClient {
|
||||
|
||||
private fun registerMenuScreens() {
|
||||
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
|
||||
MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
|
||||
}
|
||||
|
||||
private fun registerBlockEntityRenderers() {
|
||||
this.implementation.registerBlockEntityRenderer(
|
||||
CobblemonBlockEntities.HEALING_MACHINE,
|
||||
::HealingMachineRenderer
|
||||
)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HEALING_MACHINE, ::HealingMachineRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.BERRY, ::BerryBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.SIGN, ::SignRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(
|
||||
CobblemonBlockEntities.FOSSIL_ANALYZER,
|
||||
::FossilAnalyzerRenderer
|
||||
)
|
||||
this.implementation.registerBlockEntityRenderer(
|
||||
CobblemonBlockEntities.RESTORATION_TANK,
|
||||
::RestorationTankRenderer
|
||||
)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.TM_SHELF, ::TMShelfBlockEntityRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -363,12 +379,7 @@ object CobblemonClient {
|
||||
LOGGER.info("Registering Boat renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
|
||||
LOGGER.info("Registering Boat with Chest renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx ->
|
||||
CobblemonBoatRenderer(
|
||||
ctx,
|
||||
true
|
||||
)
|
||||
}
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
|
||||
LOGGER.info("Registering Generic Bedrock renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
|
||||
LOGGER.info("Registering Generic Bedrock Entity renderer")
|
||||
@@ -385,6 +396,7 @@ object CobblemonClient {
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
|
||||
implementation.registerItemColors(PonigiriItemColorProvider, CobblemonItems.PONIGIRI)
|
||||
implementation.registerItemColors(SinisterTeaItemColorProvider, CobblemonItems.SINISTER_TEA)
|
||||
implementation.registerItemColors(TechnicalMachineItemColorProvider, CobblemonItems.TECHNICAL_MACHINE)
|
||||
}
|
||||
|
||||
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
||||
@@ -411,14 +423,8 @@ object CobblemonClient {
|
||||
|
||||
private fun createBoatModelLayers() {
|
||||
CobblemonBoatType.entries.forEach { type ->
|
||||
this.implementation.registerLayer(
|
||||
CobblemonBoatRenderer.createBoatModelLayer(type, false),
|
||||
BoatModel::createBodyModel
|
||||
)
|
||||
this.implementation.registerLayer(
|
||||
CobblemonBoatRenderer.createBoatModelLayer(type, true),
|
||||
ChestBoatModel::createBodyModel
|
||||
)
|
||||
this.implementation.registerLayer(CobblemonBoatRenderer.createBoatModelLayer(type, false), BoatModel::createBodyModel)
|
||||
this.implementation.registerLayer(CobblemonBoatRenderer.createBoatModelLayer(type, true), ChestBoatModel::createBodyModel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+65
-69
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.interact.moveselect
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
@@ -21,6 +20,7 @@ import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -31,96 +31,92 @@ import net.minecraft.util.Mth
|
||||
|
||||
class MoveSlotButton(
|
||||
x: Int, y: Int,
|
||||
val move: MoveTemplate,
|
||||
val pp: Int,
|
||||
val ppMax: Int,
|
||||
val enabled: Boolean = true,
|
||||
var move: MoveTemplate? = null,
|
||||
var pp: Int = 0,
|
||||
var ppMax: Int = 0,
|
||||
var ppAsFraction: Boolean = true,
|
||||
var enabled: Boolean = true,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
|
||||
) : Button(x, y, WIDTH, HEIGHT, move?.name?.text() ?: lang("ui.moves"), onPress, CreateNarration { move?.name?.text() ?: lang("ui.moves") }), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
||||
private val moveDisabledResource = cobblemonResource("textures/gui/summary/summary_move_disabled.png")
|
||||
private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
||||
|
||||
const val WIDTH = 108
|
||||
const val HEIGHT = 22
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height && enabled
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, pPartialTicks: Float) {
|
||||
move?.let {
|
||||
isHovered = isMouseOver(mouseX.toDouble(), mouseY.toDouble()) && enabled
|
||||
val rgb = it.elementalType.hue.toRGB()
|
||||
val alpha = if (enabled) 1.0F else 0.7F
|
||||
val matrices = context.pose()
|
||||
|
||||
val moveTemplate = Moves.getByNameOrDummy(move.name)
|
||||
val rgb = moveTemplate.elementalType.hue.toRGB()
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (enabled) moveResource else moveDisabledResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
)
|
||||
|
||||
val alpha = if (enabled) 1.0 else 0.5
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT
|
||||
)
|
||||
|
||||
val matrices = context.pose()
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third,
|
||||
alpha = alpha
|
||||
)
|
||||
if (pp != -1 && ppMax != -1) {
|
||||
var movePPText = lang("ui.moves.pp", pp).bold()
|
||||
if (ppAsFraction) {
|
||||
movePPText = Component.literal("$pp/$ppMax").bold()
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
alpha = alpha
|
||||
)
|
||||
if (pp <= Mth.floor(ppMax / 2F)) {
|
||||
movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
|
||||
}
|
||||
}
|
||||
|
||||
if (pp != -1 && ppMax != -1) {
|
||||
var movePPText = Component.literal("$pp/$ppMax").bold()
|
||||
|
||||
if (pp <= Mth.floor(ppMax / 2F)) {
|
||||
movePPText = if (pp == 0) movePPText.red() else movePPText.gold()
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = movePPText,
|
||||
x = x + 93,
|
||||
y = y + 13,
|
||||
centered = true,
|
||||
opacity = alpha
|
||||
)
|
||||
}
|
||||
|
||||
// Type Icon
|
||||
TypeIcon(x = x + 2, y = y + 2, type = it.elementalType, opacity = alpha).render(context)
|
||||
|
||||
// Move Category
|
||||
MoveCategoryIcon(x = x + 66, y = y + 13.5, category = it.damageCategory, opacity = alpha).render(context)
|
||||
|
||||
// Move Name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = movePPText,
|
||||
x = x + 93,
|
||||
y = y + 13,
|
||||
centered = true
|
||||
text = it.displayName.bold(),
|
||||
x = x + 28,
|
||||
y = y + 2,
|
||||
shadow = true,
|
||||
opacity = alpha
|
||||
)
|
||||
}
|
||||
|
||||
// Type Icon
|
||||
TypeIcon(
|
||||
x = x + 2,
|
||||
y = y + 2,
|
||||
type = moveTemplate.elementalType
|
||||
).render(context)
|
||||
|
||||
// Move Category
|
||||
MoveCategoryIcon(
|
||||
x = x + 66,
|
||||
y = y + 13.5,
|
||||
category = move.damageCategory
|
||||
).render(context)
|
||||
|
||||
// Move Name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = move.displayName.bold(),
|
||||
x = x + 28,
|
||||
y = y + 2,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-53
@@ -36,9 +36,9 @@ class MoveSlotWidget(
|
||||
): SoundlessWidget(pX, pY, MOVE_WIDTH, MOVE_HEIGHT, Component.literal(move?.name ?: "")) {
|
||||
|
||||
companion object {
|
||||
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
||||
private val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
||||
private val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
|
||||
val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
||||
val moveOverlayResource = cobblemonResource("textures/gui/summary/summary_move_overlay.png")
|
||||
val moveSelectedOverlayResource = cobblemonResource("textures/gui/summary/summary_move_selected_overlay.png")
|
||||
|
||||
const val MOVE_WIDTH = 108
|
||||
const val MOVE_HEIGHT = 22
|
||||
@@ -50,6 +50,7 @@ class MoveSlotWidget(
|
||||
}.apply {
|
||||
addWidget(this)
|
||||
}
|
||||
|
||||
private val moveDownButton = ReorderMoveButton(x, y, false) {
|
||||
movesWidget.selectMove(null)
|
||||
movesWidget.reorderMove(this, false)
|
||||
@@ -67,46 +68,46 @@ class MoveSlotWidget(
|
||||
}.apply {
|
||||
addWidget(this)
|
||||
}
|
||||
|
||||
val elementalType:ElementalType = Moves.getByNameOrDummy(move?.name ?: "").getEffectiveElementalType(pokemon)
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val matrices = context.pose()
|
||||
isHovered = pMouseX >= x && pMouseY >= y && pMouseX < x + width && pMouseY < y + height
|
||||
if (move != null) {
|
||||
val moveTemplate = Moves.getByNameOrDummy(move.name)
|
||||
val rgb = elementalType.hue.toRGB()
|
||||
|
||||
if (movesWidget.selectedMove == move) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveSelectedOverlayResource,
|
||||
x = x - 1,
|
||||
y = y - 1,
|
||||
width = MOVE_WIDTH + 2,
|
||||
height = MOVE_HEIGHT + 2
|
||||
matrixStack = matrices,
|
||||
texture = moveSelectedOverlayResource,
|
||||
x = x - 1,
|
||||
y = y - 1,
|
||||
width = MOVE_WIDTH + 2,
|
||||
height = MOVE_HEIGHT + 2
|
||||
)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = MOVE_WIDTH,
|
||||
height = MOVE_HEIGHT,
|
||||
vOffset = if (isHovered) MOVE_HEIGHT else 0,
|
||||
textureHeight = MOVE_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
matrixStack = matrices,
|
||||
texture = moveResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = MOVE_WIDTH,
|
||||
height = MOVE_HEIGHT,
|
||||
vOffset = if (isHovered) MOVE_HEIGHT else 0,
|
||||
textureHeight = MOVE_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = MOVE_WIDTH,
|
||||
height = MOVE_HEIGHT
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = MOVE_WIDTH,
|
||||
height = MOVE_HEIGHT
|
||||
)
|
||||
|
||||
var movePPText = Component.literal("${move.currentPp}/${move.maxPp}").bold()
|
||||
@@ -116,36 +117,28 @@ class MoveSlotWidget(
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = movePPText,
|
||||
x = x + 93,
|
||||
y = y + 13,
|
||||
centered = true
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = movePPText,
|
||||
x = x + 93,
|
||||
y = y + 13,
|
||||
centered = true
|
||||
)
|
||||
|
||||
// Type Icon
|
||||
TypeIcon(
|
||||
x = x + 2,
|
||||
y = y + 2,
|
||||
type = elementalType
|
||||
).render(context)
|
||||
TypeIcon(x = x + 2, y = y + 2, type = elementalType).render(context)
|
||||
|
||||
// Move Category
|
||||
MoveCategoryIcon(
|
||||
x = x + 66,
|
||||
y = y + 13.5,
|
||||
category = move.damageCategory
|
||||
).render(context)
|
||||
MoveCategoryIcon(x = x + 66, y = y + 13.5, category = move.damageCategory).render(context)
|
||||
|
||||
// Move Name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = move.displayName.bold(),
|
||||
x = x + 28,
|
||||
y = y + 2,
|
||||
shadow = true
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = move.displayName.bold(),
|
||||
x = x + 28,
|
||||
y = y + 2,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
// Reorder Buttons
|
||||
@@ -158,9 +151,7 @@ class MoveSlotWidget(
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (isHovered) {
|
||||
movesWidget.selectMove(move)
|
||||
}
|
||||
if (isHovered) movesWidget.selectMove(move)
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+87
-95
@@ -74,140 +74,132 @@ class MoveSwapScreen(
|
||||
val rgb = elementalType.hue.toRGB()
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveResource,
|
||||
x = rowLeft,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
matrixStack = matrices,
|
||||
texture = moveResource,
|
||||
x = rowLeft,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = rowLeft,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth
|
||||
matrixStack = matrices,
|
||||
texture = moveOverlayResource,
|
||||
x = rowLeft,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = rowWidth
|
||||
)
|
||||
|
||||
// Type Icon
|
||||
TypeIcon(
|
||||
x = rowLeft - 9,
|
||||
y = tweakedRowTop,
|
||||
type = elementalType
|
||||
).render(context)
|
||||
TypeIcon(x = rowLeft - 9, y = tweakedRowTop, type = elementalType).render(context)
|
||||
|
||||
// Move Category
|
||||
MoveCategoryIcon(
|
||||
x = rowLeft + 77,
|
||||
y = tweakedRowTop + 1.5,
|
||||
category = move.damageCategory
|
||||
).render(context)
|
||||
MoveCategoryIcon(x = rowLeft + 77, y = tweakedRowTop + 1.5, category = move.damageCategory).render(context)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = move.displayName,
|
||||
x = rowLeft + 14,
|
||||
y = tweakedRowTop + 3.5,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
context = context,
|
||||
text = move.displayName,
|
||||
x = rowLeft + 14,
|
||||
y = tweakedRowTop + 3.5,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
// Move icons
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesPowerIconResource,
|
||||
x= (rowLeft + 10) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesPowerIconResource,
|
||||
x= (rowLeft + 10) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesAccuracyIconResource,
|
||||
x= (rowLeft + 30) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesAccuracyIconResource,
|
||||
x= (rowLeft + 30) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesEffectIconResource,
|
||||
x= (rowLeft + 53.5) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
matrixStack = matrices,
|
||||
texture = MovesWidget.movesEffectIconResource,
|
||||
x= (rowLeft + 53.5) / MovesWidget.SCALE,
|
||||
y = (tweakedRowTop + 11) / MovesWidget.SCALE,
|
||||
width = MovesWidget.MOVE_ICON_SIZE,
|
||||
height = MovesWidget.MOVE_ICON_SIZE,
|
||||
scale = MovesWidget.SCALE
|
||||
)
|
||||
|
||||
val movePower = if (move.power.toInt() > 0) move.power.toInt().toString().text() else "—".text()
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = movePower,
|
||||
x = rowLeft + 16.5,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
context = context,
|
||||
text = movePower,
|
||||
x = rowLeft + 16.5,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pane.movesWidget.format(move.accuracy).text(),
|
||||
x = rowLeft + 37,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
context = context,
|
||||
text = MovesWidget.format(move.accuracy).text(),
|
||||
x = rowLeft + 37,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pane.movesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
|
||||
x = rowLeft + 60.5,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
context = context,
|
||||
text = MovesWidget.format(move.effectChances.firstOrNull() ?: 0.0).text(),
|
||||
x = rowLeft + 60.5,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
val pp = move.pp + ppRaisedStages * move.pp / 5
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.moves.pp", pp),
|
||||
x = rowLeft + 76,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
context = context,
|
||||
text = lang("ui.moves.pp", pp),
|
||||
x = rowLeft + 76,
|
||||
y = tweakedRowTop + 12,
|
||||
scale = MovesWidget.SCALE,
|
||||
shadow = true
|
||||
)
|
||||
} else {
|
||||
// Forget move slot
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = forgetMoveOverlayResource,
|
||||
x = rowLeft - 9,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = 100,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2,
|
||||
matrixStack = matrices,
|
||||
texture = forgetMoveOverlayResource,
|
||||
x = rowLeft - 9,
|
||||
y = tweakedRowTop,
|
||||
height = SLOT_HEIGHT,
|
||||
width = 100,
|
||||
vOffset = if (isHovered) SLOT_HEIGHT else 0,
|
||||
textureHeight = SLOT_HEIGHT * 2
|
||||
)
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = forgetMoveIconResource,
|
||||
x = rowLeft + rowWidth / 2 - 7,
|
||||
y = tweakedRowTop + SLOT_HEIGHT / 2 - 4,
|
||||
height = 7,
|
||||
width = 7,
|
||||
vOffset = if (isHovered) 7 else 0,
|
||||
textureHeight = 14,
|
||||
matrixStack = matrices,
|
||||
texture = forgetMoveIconResource,
|
||||
x = rowLeft + rowWidth / 2 - 7,
|
||||
y = tweakedRowTop + SLOT_HEIGHT / 2 - 4,
|
||||
height = 7,
|
||||
width = 7,
|
||||
vOffset = if (isHovered) 7 else 0,
|
||||
textureHeight = 14
|
||||
)
|
||||
}
|
||||
|
||||
@@ -232,4 +224,4 @@ class MoveSwapScreen(
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-11
@@ -39,6 +39,11 @@ class MovesWidget(
|
||||
it.roundingMode = RoundingMode.CEILING
|
||||
}
|
||||
|
||||
fun format(input: Double): String {
|
||||
if (input <= 0) return "—"
|
||||
return "${decimalFormat.format(input)}%"
|
||||
}
|
||||
|
||||
private val movesBaseResource = cobblemonResource("textures/gui/summary/summary_moves_base.png")
|
||||
val movesPowerIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_power.png")
|
||||
val movesAccuracyIconResource = cobblemonResource("textures/gui/summary/summary_moves_icon_accuracy.png")
|
||||
@@ -65,11 +70,7 @@ class MovesWidget(
|
||||
addWidget(it)
|
||||
}
|
||||
|
||||
private var descriptionScrollList = MoveDescriptionScrollList(
|
||||
x + 69,
|
||||
y + 113,
|
||||
5
|
||||
)
|
||||
private var descriptionScrollList = MoveDescriptionScrollList(x + 69, y + 113, 5)
|
||||
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
@@ -208,11 +209,6 @@ class MovesWidget(
|
||||
)
|
||||
}
|
||||
|
||||
fun format(input: Double): String {
|
||||
if (input <= 0) return "—"
|
||||
return "${decimalFormat.format(input)}%"
|
||||
}
|
||||
|
||||
fun selectMove(move: Move?) {
|
||||
selectedMove = if (selectedMove == move || move == null) null else move
|
||||
descriptionScrollList.setMoveDescription(selectedMove?.description ?: "".text())
|
||||
@@ -232,4 +228,4 @@ class MovesWidget(
|
||||
if (descriptionScrollList.isHovered) descriptionScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class IconButton(
|
||||
var buttonX: Float,
|
||||
var buttonY: Float,
|
||||
val buttonWidth: Number,
|
||||
val buttonHeight: Number,
|
||||
var resource: ResourceLocation? = null,
|
||||
val scale: Float = 0.5F,
|
||||
val silent: Boolean = false,
|
||||
val clickAction: OnPress
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
|
||||
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
resource?.let {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = it,
|
||||
x = buttonX / scale,
|
||||
y = buttonY / scale,
|
||||
width = buttonWidth,
|
||||
height = buttonHeight,
|
||||
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) buttonHeight else 0,
|
||||
textureHeight = buttonHeight.toFloat() * 2,
|
||||
scale = scale
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (visible && isMouseOver(mouseX, mouseY)) super.mouseClicked(mouseX, mouseY, button)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
if (visible && !this.silent) soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pc.TextWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
class MoveSearchWidget(
|
||||
private var pX: Int,
|
||||
private var pY: Int,
|
||||
text: Component = "MoveSearchWidget".text(),
|
||||
update: () -> (Unit)
|
||||
): TextWidget(pX, pY, text = text, update = update) {
|
||||
|
||||
companion object {
|
||||
const val ICON_SIZE = 16
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private val iconFilterResource = cobblemonResource("textures/gui/tmmachine/icon_search.png")
|
||||
private val labelFilter = lang("ui.pokedex.search")
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
x = (x - 10.5) / SCALE,
|
||||
y = (y + 2.5) / SCALE,
|
||||
texture = iconFilterResource,
|
||||
width = ICON_SIZE,
|
||||
height = ICON_SIZE,
|
||||
vOffset = if (isFocused || !value.isEmpty() || iconHovered(mouseX, mouseY)) ICON_SIZE else 0,
|
||||
textureHeight = ICON_SIZE * 2,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
val input = applyTextCursor(value, labelFilter)
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = input.bold(),
|
||||
x = pX,
|
||||
y = pY + 2,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
renderCursor(context, input)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (iconHovered(mouseX.toInt(), mouseY.toInt())) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F, 0.25F))
|
||||
// Simulate clicking on text area if icon is clicked
|
||||
if (!isFocused) return super.mouseClicked(x + (width / 2.0), y + (height / 2.0), button)
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
fun iconHovered(mouseX: Int, mouseY: Int): Boolean = mouseX >= (x - 10.5) && mouseY >= (y + 2.5)
|
||||
&& mouseX <= ((x - 10.5) + (ICON_SIZE * SCALE)) && mouseY <= ((y + 2.5) + (ICON_SIZE * SCALE))
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSlotWidget
|
||||
import com.cobblemon.mod.common.client.gui.tmmachine.MovesScrollingWidget.ScrollSlot
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.util.Mth
|
||||
|
||||
class MovesScrollingWidget(val pX: Int, val pY: Int, val tmList: SettableObservable<MutableList<TechnicalMachine>>, val setTM: (TechnicalMachine?, Boolean) -> (Unit)): ScrollingWidget<ScrollSlot>(
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
left = pX,
|
||||
top = pY - HEIGHT,
|
||||
slotHeight = SLOT_HEIGHT + SLOT_SPACING
|
||||
) {
|
||||
companion object {
|
||||
const val WIDTH = 115
|
||||
const val HEIGHT = 82
|
||||
const val SLOT_HEIGHT = 22
|
||||
const val SLOT_SPACING = 4
|
||||
|
||||
val searchBar = cobblemonResource("textures/gui/tmmachine/search_bar.png")
|
||||
}
|
||||
|
||||
var highlightedMoveId: ResourceLocation? = null
|
||||
|
||||
init {
|
||||
tmList.subscribeIncludingCurrent { tmList ->
|
||||
replaceEntries(tmList.map { tm -> ScrollSlot(tm, setTM) })
|
||||
}
|
||||
}
|
||||
|
||||
fun setSlotHighlighted(id: ResourceLocation?) {
|
||||
highlightedMoveId = id
|
||||
for (child in children()) {
|
||||
if (child is ScrollSlot) {
|
||||
child.highlighted = child.tm.id == highlightedMoveId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition(): Int = rowLeft + width - 3
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = searchBar,
|
||||
x = left - 4,
|
||||
y = y - 19,
|
||||
width = 120,
|
||||
height = 19
|
||||
)
|
||||
super.renderWidget(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val xLeft = this.scrollbarPosition
|
||||
val xRight = xLeft + 3
|
||||
|
||||
val barHeight = this.bottom - this.y
|
||||
|
||||
var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
||||
yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
|
||||
var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
|
||||
if (yTop < this.y) {
|
||||
yTop = this.y
|
||||
}
|
||||
|
||||
context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
|
||||
context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
|
||||
}
|
||||
|
||||
override fun getEntry(index: Int): ScrollSlot = children()[index] as ScrollSlot
|
||||
|
||||
class ScrollSlot(val tm: TechnicalMachine, val setTM : (TechnicalMachine?, clicked: Boolean) -> Unit): Slot<ScrollSlot>() {
|
||||
var posX: Int = 0
|
||||
var posY: Int = 0
|
||||
var highlighted = false
|
||||
|
||||
override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {
|
||||
posX = x
|
||||
posY = y
|
||||
val moveTemplate = tm.moveName
|
||||
val rgb = moveTemplate.elementalType.hue.toRGB()
|
||||
val isHovered = hovered && (mouseX < x + (108)) // Prevent hover when over scrollbar
|
||||
val startPosY = y + SLOT_SPACING
|
||||
val startPosX = x + 1 // Hover border for move renders at x, so render move element to the right by 1
|
||||
|
||||
val matrices = context.pose()
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MoveSlotWidget.moveResource,
|
||||
x = startPosX,
|
||||
y = startPosY,
|
||||
width = MoveSlotWidget.MOVE_WIDTH,
|
||||
height = MoveSlotWidget.MOVE_HEIGHT,
|
||||
vOffset = if (isHovered) MoveSlotWidget.MOVE_HEIGHT else 0,
|
||||
textureHeight = MoveSlotWidget.MOVE_HEIGHT * 2,
|
||||
red = rgb.first,
|
||||
green = rgb.second,
|
||||
blue = rgb.third
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MoveSlotWidget.moveOverlayResource,
|
||||
x = startPosX,
|
||||
y = startPosY,
|
||||
width = MoveSlotWidget.MOVE_WIDTH,
|
||||
height = MoveSlotWidget.MOVE_HEIGHT
|
||||
)
|
||||
|
||||
if (highlighted || isHovered) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = MoveSlotWidget.moveSelectedOverlayResource,
|
||||
x = startPosX - 1,
|
||||
y = startPosY - 1,
|
||||
width = MoveSlotWidget.MOVE_WIDTH + 2,
|
||||
height = MoveSlotWidget.MOVE_HEIGHT + 2
|
||||
)
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.moves.pp", moveTemplate.pp).bold(),
|
||||
x = startPosX + 93,
|
||||
y = startPosY + 13,
|
||||
centered = true
|
||||
)
|
||||
|
||||
// Type Icon
|
||||
TypeIcon(
|
||||
x = x + 2,
|
||||
y = startPosY + 2,
|
||||
type = moveTemplate.elementalType
|
||||
).render(context)
|
||||
|
||||
// Move Category
|
||||
MoveCategoryIcon(
|
||||
x = x + 66,
|
||||
y = startPosY + 13.5,
|
||||
category = moveTemplate.damageCategory
|
||||
).render(context)
|
||||
|
||||
// Move Name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = moveTemplate.displayName.bold(),
|
||||
x = x + 28,
|
||||
y = startPosY + 2,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
if (isHovered) {
|
||||
setTM(tm, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
// Prevent clicking through scroll bar
|
||||
if (mouseX < posX + (108)) {
|
||||
setTM(tm, true)
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun getNarration(): Component {
|
||||
return tm.moveName.displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
|
||||
class StartButton(
|
||||
var buttonX: Float,
|
||||
var buttonY: Float,
|
||||
val clickAction: OnPress
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), WIDTH, HEIGHT, "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 22
|
||||
const val HEIGHT = 12
|
||||
|
||||
val base = cobblemonResource("textures/gui/tmmachine/button_start.png")
|
||||
val baseStop = cobblemonResource("textures/gui/tmmachine/button_stop.png")
|
||||
val baseDisabled = cobblemonResource("textures/gui/tmmachine/button_start_disabled.png")
|
||||
|
||||
val iconStart = cobblemonResource("textures/gui/tmmachine/button_icon_start.png")
|
||||
val iconStop = cobblemonResource("textures/gui/tmmachine/button_icon_stop.png")
|
||||
val iconRepeat = cobblemonResource("textures/gui/tmmachine/button_icon_repeat.png")
|
||||
}
|
||||
|
||||
var disabled = false
|
||||
var processing = false
|
||||
var shouldRepeat = false
|
||||
|
||||
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
|
||||
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
val resource = if (disabled) baseDisabled
|
||||
else (if (processing) baseStop else base)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = resource,
|
||||
x = buttonX,
|
||||
y = buttonY,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (processing) iconStop else (if (shouldRepeat) iconRepeat else iconStart),
|
||||
x = buttonX,
|
||||
y = buttonY,
|
||||
width = WIDTH,
|
||||
height = HEIGHT
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (active && isMouseOver(mouseX, mouseY)) {
|
||||
super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
if (visible && !disabled) {
|
||||
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
}
|
||||
+825
@@ -0,0 +1,825 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.ColourLibrary
|
||||
import com.cobblemon.mod.common.api.gui.MultiLineLabelK
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.interact.moveselect.MoveSlotButton
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.TMMachineTeachMovePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.isInventoryKeyPressed
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.math.ceil
|
||||
|
||||
class TMMachineScreen(containerMenu: TMMachineMenu, val inventory: Inventory, title: Component) : AbstractContainerScreen<TMMachineMenu>(containerMenu, inventory, title) {
|
||||
companion object {
|
||||
const val HEIGHT = 226
|
||||
const val WIDTH = 191
|
||||
|
||||
const val DISC_DIAMETER = 56
|
||||
|
||||
const val TYPE_SELECT_MODE = 1
|
||||
const val MOVE_SELECT_MODE = 2
|
||||
const val TM_BURN_MODE = 3
|
||||
|
||||
const val CRAFT_TICKS = 10 // Length for craft completion animation
|
||||
const val RESET_DISC_TICKS = 4 // Length for disc reset animation
|
||||
|
||||
val baseResource = cobblemonResource("textures/gui/tmmachine/base.png")
|
||||
val tmTray = cobblemonResource("textures/gui/tmmachine/tm_tray.png")
|
||||
val iconArrowLeft = cobblemonResource("textures/gui/tmmachine/icon_arrow_left.png")
|
||||
val iconArrowRight = cobblemonResource("textures/gui/tmmachine/icon_arrow_right.png")
|
||||
val emptyDiscSlot = cobblemonResource("textures/item/tms/blank_disc_empty_slot.png")
|
||||
|
||||
val discBase = cobblemonResource("textures/gui/tmmachine/tm_base.png")
|
||||
val discBorder = cobblemonResource("textures/gui/tmmachine/tm_border.png")
|
||||
val discOverlay = cobblemonResource("textures/gui/tmmachine/tm_base_overlay.png")
|
||||
val discReflective = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective.png")
|
||||
val discReflectiveRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_reflective_rotating.png")
|
||||
val discRotating = cobblemonResource("textures/gui/tmmachine/tm_overlay_rotating.png")
|
||||
}
|
||||
|
||||
var selectedTM: TechnicalMachine? = null
|
||||
var heldStackMove: MoveTemplate? = null
|
||||
var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
|
||||
var output: ItemStack? = null
|
||||
var mode: Int = TYPE_SELECT_MODE
|
||||
var sortType: ElementalType? = null
|
||||
var selectedPokemon: Pokemon? = null
|
||||
|
||||
var partySlotList: MutableSet<TMPartySlotWidget> = mutableSetOf()
|
||||
|
||||
lateinit var backButton: IconButton
|
||||
lateinit var startButton: StartButton
|
||||
lateinit var selectedMoveButton: MoveSlotButton
|
||||
lateinit var typesScrollList: TypesScrollingWidget
|
||||
lateinit var movesScrollingList: MovesScrollingWidget
|
||||
lateinit var moveSearchWidget: MoveSearchWidget
|
||||
|
||||
var discReflectionFrame: Int = 0
|
||||
var discRotationFrame: Int = 0
|
||||
|
||||
var initScreen: Boolean = false
|
||||
|
||||
override fun init() {
|
||||
imageWidth = WIDTH
|
||||
imageHeight = HEIGHT
|
||||
super.init()
|
||||
|
||||
partySlotList.clear()
|
||||
CobblemonClient.storage.party.forEachIndexed { index, pokemon ->
|
||||
partySlotList.add(TMPartySlotWidget(
|
||||
pX = leftPos - 60,
|
||||
pY = topPos + 9 + ((TMPartySlotWidget.HEIGHT + 3) * index),
|
||||
pokemon = pokemon,
|
||||
onPress = {
|
||||
if ((it as TMPartySlotWidget).clickable) {
|
||||
if (heldStackMove != null && pokemon != null) {
|
||||
heldStackMove?.let { move ->
|
||||
if (canLearnTMMove(move, pokemon) == TMPartySlotWidget.CAN_LEARN) {
|
||||
CobblemonNetwork.sendToServer(TMMachineTeachMovePacket(pokemon.uuid, menu.carried, move))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mode == TYPE_SELECT_MODE && pokemon != null) {
|
||||
setMoveList(pokemon = pokemon)
|
||||
setScreenFromMode(MOVE_SELECT_MODE)
|
||||
selectedPokemon = pokemon
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
for (slot in partySlotList) {
|
||||
if (!children().contains(slot)) addRenderableWidget(slot)
|
||||
}
|
||||
|
||||
typesScrollList = TypesScrollingWidget(
|
||||
pX = leftPos + 7,
|
||||
pY = topPos + 11,
|
||||
setType = { type ->
|
||||
sortType = type
|
||||
setMoveList()
|
||||
setScreenFromMode(MOVE_SELECT_MODE)
|
||||
}
|
||||
)
|
||||
if (!children().contains(typesScrollList)) addRenderableWidget(typesScrollList)
|
||||
|
||||
movesScrollingList = MovesScrollingWidget(
|
||||
pX = leftPos + 4,
|
||||
pY = topPos + 29,
|
||||
tmList,
|
||||
setTM = { tm, clicked -> setSelectedTM(tm, clicked) }
|
||||
)
|
||||
if (!children().contains(movesScrollingList)) addRenderableWidget(movesScrollingList)
|
||||
|
||||
moveSearchWidget = MoveSearchWidget(pX = leftPos + 16, pY = topPos + 13) {
|
||||
if (::movesScrollingList.isInitialized && moveSearchWidget.isFocused) {
|
||||
val input = moveSearchWidget.value
|
||||
setMoveList(filter = if (input.isBlank()) null else input)
|
||||
}
|
||||
}
|
||||
if (!children().contains(moveSearchWidget)) addRenderableWidget(moveSearchWidget)
|
||||
|
||||
backButton = IconButton(
|
||||
leftPos + 110F,
|
||||
topPos + 1F,
|
||||
18,
|
||||
18,
|
||||
iconArrowLeft
|
||||
) { setScreenFromMode(TYPE_SELECT_MODE) }
|
||||
if (!children().contains(backButton)) addRenderableWidget(backButton)
|
||||
|
||||
selectedMoveButton = MoveSlotButton(
|
||||
leftPos + 6,
|
||||
topPos + 6,
|
||||
move = selectedTM?.moveName,
|
||||
pp = selectedTM?.moveName?.pp ?: 0,
|
||||
ppAsFraction = false
|
||||
) {
|
||||
if (!isBurnActive()) {
|
||||
if (::movesScrollingList.isInitialized && movesScrollingList.children().isEmpty()) setMoveList()
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
setScreenFromMode(MOVE_SELECT_MODE)
|
||||
|
||||
// Clear active TM
|
||||
CobblemonNetwork.sendToServer(SetActiveTMPacket(null))
|
||||
}
|
||||
}
|
||||
if (!children().contains(selectedMoveButton)) addRenderableWidget(selectedMoveButton)
|
||||
|
||||
startButton = StartButton(leftPos + 5F, topPos + 35F) {
|
||||
if (!(it as StartButton).disabled) {
|
||||
selectedTM?.let {
|
||||
val burnActive = menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0
|
||||
CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
|
||||
TMMachineBlockEntity.BURN_ACTIVE_INDEX,
|
||||
burnActive xor 1 // Toggle active state, 1 ⇆ 0
|
||||
))
|
||||
|
||||
// If button is not active, set if process should repeat or not
|
||||
if (burnActive == 0) {
|
||||
CobblemonNetwork.sendToServer(SetTMMachineContainerDataPacket(
|
||||
TMMachineBlockEntity.REPEAT_PROCESS_INDEX,
|
||||
if (hasShiftDown()) 1 else 0
|
||||
))
|
||||
}
|
||||
|
||||
CobblemonNetwork.sendToServer(SetActiveTMPacket(it))
|
||||
inventory.setChanged()
|
||||
menu.broadcastChanges()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!children().contains(startButton)) addRenderableWidget(startButton)
|
||||
|
||||
setScreenFromMode(mode)
|
||||
initScreen = false
|
||||
}
|
||||
|
||||
override fun containerTick() {
|
||||
if (mode == TM_BURN_MODE) {
|
||||
selectedTM?.let { tm ->
|
||||
val resultStack = menu.inventory!!.getItem(TMMachineMenu.RESULT_SLOT)
|
||||
val craftedTmStack = ItemStack(CobblemonItems.TECHNICAL_MACHINE)
|
||||
.also { TMMoveComponent.setTMMove(it, tm.moveName) }
|
||||
|
||||
var validOutput = resultStack.isEmpty || (
|
||||
!resultStack.isEmpty
|
||||
&& (resultStack.count < resultStack.maxStackSize)
|
||||
&& ItemStack.isSameItemSameComponents(resultStack, craftedTmStack)
|
||||
)
|
||||
var validCost = !menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty // Blank TM slot
|
||||
|
||||
if (validOutput && validCost) {
|
||||
val recipe = tm.getClampedRecipe() ?: listOf()
|
||||
recipe.forEachIndexed { index, ingredient ->
|
||||
val recipeStack = BuiltInRegistries.ITEM.get(ingredient.item).defaultInstance.also { it.count = ingredient.count }
|
||||
val slotStack = menu.inventory!!.getItem(index + TMMachineMenu.INGREDIENT_SLOTS.first)
|
||||
if (!(ItemStack.isSameItem(recipeStack, slotStack) && slotStack.count >= recipeStack.count)) {
|
||||
recipeStack.item
|
||||
validCost = false
|
||||
return@forEachIndexed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (::startButton.isInitialized) {
|
||||
val discResetting = menu.getPostCraftTicks() > CRAFT_TICKS
|
||||
startButton.disabled = !(!discResetting && (validCost && validOutput))//!(validCost && validOutput)
|
||||
startButton.processing = isBurnActive()
|
||||
startButton.shouldRepeat = hasShiftDown()
|
||||
}
|
||||
|
||||
if (::selectedMoveButton.isInitialized) {
|
||||
selectedMoveButton.enabled = !isBurnActive()
|
||||
}
|
||||
|
||||
if (isBurnActive()) {
|
||||
val progress = menu.getBurnProgressRatio()
|
||||
if (progress > 0 && progress < 1) {
|
||||
discReflectionFrame = (discReflectionFrame + 1) % 4
|
||||
discRotationFrame = (discRotationFrame + 1) % 8
|
||||
}
|
||||
if ((progress == 1F) && (menu.getPostCraftTicks() == 0)) playSound(CobblemonSounds.TM_MACHINE_CRAFT)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if screen should be switched upon opening UI
|
||||
if (!initScreen) loadBurnScreenData()
|
||||
}
|
||||
|
||||
if (mode != MOVE_SELECT_MODE) {
|
||||
val heldStack = menu.carried
|
||||
if (heldStack.item == CobblemonItems.TECHNICAL_MACHINE) {
|
||||
// Store move of TM item picked up by player cursor
|
||||
TMMoveComponent.getTMMove(heldStack)?.let { move ->
|
||||
if (heldStackMove != move) heldStackMove = move
|
||||
if (mode == TYPE_SELECT_MODE) updatePartySlotStatus(heldStackMove)
|
||||
}
|
||||
|
||||
} else {
|
||||
if (heldStackMove != null) {
|
||||
heldStackMove = null
|
||||
updatePartySlotStatus(null)
|
||||
}
|
||||
}
|
||||
|
||||
// Allow slots to be clicked if player holding TM item
|
||||
if ((mode == TM_BURN_MODE)) {
|
||||
for (slot in partySlotList) slot.clickable = (heldStackMove == selectedTM?.moveName)
|
||||
updatePartySlotStatus(selectedTM?.moveName)
|
||||
} else if (mode == MOVE_SELECT_MODE) {
|
||||
for (slot in partySlotList) slot.clickable = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.containerTick()
|
||||
}
|
||||
|
||||
private fun loadBurnScreenData() {
|
||||
var tm: TechnicalMachine? = null
|
||||
|
||||
val blockPos = menu.containerData?.let {
|
||||
val x = it.get(TMMachineBlockEntity.POSITION_X_INDEX)
|
||||
val y = it.get(TMMachineBlockEntity.POSITION_Y_INDEX)
|
||||
val z = it.get(TMMachineBlockEntity.POSITION_Z_INDEX)
|
||||
BlockPos(x, y, z)
|
||||
}
|
||||
|
||||
inventory.player.level().getBlockEntity(blockPos)?.let { blockEntity ->
|
||||
if (blockEntity is TMMachineBlockEntity) {
|
||||
Moves.getByName(blockEntity.activeMove)?.let {
|
||||
tm = TechnicalMachines.moveToTM[it]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If burn currently in progress or if active TM set, set to burn screen
|
||||
if (isBurnActive() || tm != null) {
|
||||
setSelectedTM(tm, true)
|
||||
}
|
||||
|
||||
initScreen = true
|
||||
}
|
||||
|
||||
private fun isBurnActive(): Boolean = (menu.containerData?.get(TMMachineBlockEntity.BURN_ACTIVE_INDEX) ?: 0) == 1
|
||||
|
||||
private fun setScreenFromMode(screen: Int) {
|
||||
mode = screen
|
||||
when (mode) {
|
||||
TYPE_SELECT_MODE -> {
|
||||
toggleTMBurn(false)
|
||||
toggleMoveSelect(false)
|
||||
toggleTypeSelect(true)
|
||||
}
|
||||
MOVE_SELECT_MODE -> {
|
||||
toggleTMBurn(false)
|
||||
toggleTypeSelect(false)
|
||||
toggleMoveSelect(true)
|
||||
}
|
||||
else -> {
|
||||
toggleTypeSelect(false)
|
||||
toggleMoveSelect(false)
|
||||
toggleTMBurn(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMoveList(filter: String? = null, pokemon: Pokemon? = null) {
|
||||
val filteredList = TechnicalMachine.filterTms(filter, sortType, pokemon, player = inventory.player).toMutableList()
|
||||
if (sortType == null) filteredList.sortBy { it.type }
|
||||
tmList.set(filteredList)
|
||||
}
|
||||
|
||||
private fun toggleTypeSelect(isVisible: Boolean) {
|
||||
if (isVisible) {
|
||||
setSelectedTM(null, false)
|
||||
if (::moveSearchWidget.isInitialized) moveSearchWidget.value = ""
|
||||
if (selectedPokemon != null) selectedPokemon = null
|
||||
sortType = null
|
||||
}
|
||||
|
||||
if (::typesScrollList.isInitialized) {
|
||||
typesScrollList.apply {
|
||||
visible = isVisible
|
||||
scrollAmount = 0.0
|
||||
// Resize widget to toggle input detection as screen only detects first widget added for overlapping children
|
||||
if (isVisible) { setSize(TypesScrollingWidget.WIDTH, TypesScrollingWidget.HEIGHT) }
|
||||
else { setSize(0, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
for (slot in partySlotList) slot.clickable = isVisible
|
||||
}
|
||||
|
||||
private fun toggleMoveSelect(isVisible: Boolean) {
|
||||
if (::movesScrollingList.isInitialized) {
|
||||
movesScrollingList.apply {
|
||||
visible = isVisible
|
||||
scrollAmount = 0.0
|
||||
if (isVisible) { setSize(MovesScrollingWidget.WIDTH, MovesScrollingWidget.HEIGHT) }
|
||||
else { setSize(0, 0) }
|
||||
}
|
||||
}
|
||||
if (::moveSearchWidget.isInitialized) {
|
||||
moveSearchWidget.visible = isVisible
|
||||
}
|
||||
if (::backButton.isInitialized) {
|
||||
backButton.visible = isVisible
|
||||
}
|
||||
heldStackMove = null
|
||||
}
|
||||
|
||||
private fun toggleTMBurn(isVisible: Boolean) {
|
||||
if (!isVisible) setSelectedTM(null, false)
|
||||
if (::selectedMoveButton.isInitialized) selectedMoveButton.visible = isVisible
|
||||
if (::startButton.isInitialized) startButton.visible = isVisible
|
||||
}
|
||||
|
||||
fun canLearnTMMove(move: MoveTemplate, pokemon: Pokemon): Int {
|
||||
if (pokemon.moveSet.getMoveTemplates().contains(move) || pokemon.allAccessibleMoves.contains(move)) return TMPartySlotWidget.LEARNED
|
||||
val learnableMoves = pokemon.form.moves.tmLearnableMoves()
|
||||
return if (learnableMoves.contains(move)) TMPartySlotWidget.CAN_LEARN else TMPartySlotWidget.CANNOT_LEARN
|
||||
}
|
||||
|
||||
fun setSelectedTM(tm: TechnicalMachine?, clicked: Boolean) {
|
||||
if (selectedTM != tm) {
|
||||
selectedTM = tm
|
||||
updatePartySlotStatus(tm?.moveName)
|
||||
}
|
||||
|
||||
if (::movesScrollingList.isInitialized) {
|
||||
movesScrollingList.setSlotHighlighted(if (clicked) null else selectedTM?.id)
|
||||
}
|
||||
|
||||
if (clicked) {
|
||||
selectedTM?.let {
|
||||
if (::selectedMoveButton.isInitialized) {
|
||||
selectedMoveButton.apply {
|
||||
move = it.moveName
|
||||
pp = it.moveName.pp
|
||||
}
|
||||
setScreenFromMode(TM_BURN_MODE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePartySlotStatus(moveTemplate: MoveTemplate?) {
|
||||
for (slot in partySlotList) {
|
||||
if (slot.pokemon != null) {
|
||||
slot.teachable = if (moveTemplate != null) canLearnTMMove(moveTemplate, slot.pokemon) else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderDiscTypeOverlay(poseStack: PoseStack, isDiscRotating: Boolean, alpha: Float = 1F) {
|
||||
selectedTM?.let {
|
||||
val primaryRgb = it.moveName.elementalType.primaryColor.toRGB()
|
||||
val secondaryRgb = it.moveName.elementalType.secondaryColor.toRGB()
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discBase,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
red = primaryRgb.first,
|
||||
green = primaryRgb.second,
|
||||
blue = primaryRgb.third,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
if (isDiscRotating) {
|
||||
// Reflective rotation
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discReflectiveRotating,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
vOffset = DISC_DIAMETER * discReflectionFrame,
|
||||
textureHeight = DISC_DIAMETER * 4,
|
||||
red = secondaryRgb.first,
|
||||
green = secondaryRgb.second,
|
||||
blue = secondaryRgb.third,
|
||||
alpha = alpha
|
||||
)
|
||||
|
||||
// Groove rotation
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discRotating,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
vOffset = DISC_DIAMETER * discRotationFrame,
|
||||
textureHeight = DISC_DIAMETER * 8,
|
||||
red = secondaryRgb.first,
|
||||
green = secondaryRgb.second,
|
||||
blue = secondaryRgb.third,
|
||||
alpha = alpha
|
||||
)
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discReflective,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
red = secondaryRgb.first,
|
||||
green = secondaryRgb.second,
|
||||
blue = secondaryRgb.third,
|
||||
alpha = alpha
|
||||
)
|
||||
}
|
||||
|
||||
val postCraftTicks = menu.getPostCraftTicks()
|
||||
if (postCraftTicks > 0 && postCraftTicks < 8) {
|
||||
val opacity = when {
|
||||
postCraftTicks <= 5 -> postCraftTicks * 0.25F
|
||||
postCraftTicks > 5 -> (CRAFT_TICKS - postCraftTicks) * 0.25F // decreases by 0.25 each tick after 5
|
||||
else -> 0F
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discOverlay,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
alpha = opacity
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderDisc(poseStack: PoseStack, isDiscRotating: Boolean, baseAlpha: Float = 1F, overlayAlpha: Float = 1F) {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discBase,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
alpha = baseAlpha
|
||||
)
|
||||
|
||||
if (isDiscRotating) {
|
||||
// Reflective rotation
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discReflectiveRotating,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
vOffset = DISC_DIAMETER * discReflectionFrame,
|
||||
textureHeight = DISC_DIAMETER * 4,
|
||||
alpha = baseAlpha
|
||||
)
|
||||
|
||||
// Groove rotation
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discRotating,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
vOffset = DISC_DIAMETER * discRotationFrame,
|
||||
textureHeight = DISC_DIAMETER * 8,
|
||||
alpha = baseAlpha
|
||||
)
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discReflective,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
alpha = baseAlpha
|
||||
)
|
||||
}
|
||||
|
||||
if (menu.getPostCraftTicks() < CRAFT_TICKS) {
|
||||
renderDiscTypeOverlay(poseStack, isDiscRotating, overlayAlpha)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = discBorder,
|
||||
x = leftPos + 32,
|
||||
y = topPos + 50,
|
||||
width = DISC_DIAMETER,
|
||||
height = DISC_DIAMETER,
|
||||
alpha = baseAlpha
|
||||
)
|
||||
}
|
||||
|
||||
fun renderMoveInfo(context: GuiGraphics, mouseX: Int, mouseY: Int) {
|
||||
val currentTm = selectedTM
|
||||
val moveTemplate = currentTm?.moveName
|
||||
val scale = 0.5F
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = MovesWidget.movesPowerIconResource,
|
||||
x = leftPos + 127.5,
|
||||
y = topPos + 7.5,
|
||||
width = 5,
|
||||
height = 5
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = MovesWidget.movesAccuracyIconResource,
|
||||
x = leftPos + 127.5,
|
||||
y = topPos + 19.5,
|
||||
width = 5,
|
||||
height = 5
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = MovesWidget.movesEffectIconResource,
|
||||
x = leftPos + 127.5,
|
||||
y = topPos + 31.5,
|
||||
width = 5,
|
||||
height = 5
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.power"),
|
||||
x = leftPos + 134.5,
|
||||
y = topPos + 8,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.accuracy"),
|
||||
x = leftPos + 134.5,
|
||||
y = topPos + 20,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.effect"),
|
||||
x = leftPos + 134.5,
|
||||
y = topPos + 32,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
val movePower = if (moveTemplate != null && moveTemplate.power.toInt() > 0) moveTemplate.power.toInt().toString().text() else "—".text()
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = movePower,
|
||||
x = leftPos + 183,
|
||||
y = topPos + 8,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
val moveAccuracy = if (moveTemplate != null) MovesWidget.format(moveTemplate.accuracy).text() else "—".text()
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = moveAccuracy,
|
||||
x = leftPos + 183,
|
||||
y = topPos + 20,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
val moveEffect = if (moveTemplate != null) MovesWidget.format(moveTemplate.effectChances.firstOrNull() ?: 0.0).text() else "—".text()
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = moveEffect,
|
||||
x = leftPos + 183,
|
||||
y = topPos + 32,
|
||||
shadow = true,
|
||||
scale = scale
|
||||
)
|
||||
|
||||
if (moveTemplate != null) {
|
||||
context.pose().pushPose()
|
||||
context.pose().scale(scale, scale, 1F)
|
||||
MultiLineLabelK.create(
|
||||
component = moveTemplate.description,
|
||||
width = 55 / scale,
|
||||
maxLines = 5
|
||||
).renderLeftAligned(
|
||||
context = context,
|
||||
x = (leftPos + 127.5) / scale,
|
||||
y = (topPos + 44.5) / scale,
|
||||
ySpacing = 6 / scale,
|
||||
colour = ColourLibrary.WHITE,
|
||||
shadow = true
|
||||
)
|
||||
context.pose().popPose()
|
||||
|
||||
val recipe = selectedTM?.getClampedRecipe() ?: listOf()
|
||||
// Render material cost
|
||||
recipe.forEachIndexed { index, ingredient ->
|
||||
val itemX = (leftPos + 129 + (index * 18))
|
||||
val itemY = (topPos + 90)
|
||||
|
||||
val itemStack = BuiltInRegistries.ITEM.get(ingredient.item).defaultInstance.also { it.count = ingredient.count }
|
||||
context.renderItem(itemStack, itemX, itemY)
|
||||
context.renderItemDecorations(Minecraft.getInstance().font, itemStack, itemX, itemY)
|
||||
|
||||
if (mouseX > itemX && mouseX < (itemX + 16) && mouseY > itemY && mouseY < (itemY + 16)) {
|
||||
context.renderTooltip(Minecraft.getInstance().font, itemStack, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
val searchFocused = this::moveSearchWidget.isInitialized && moveSearchWidget.isFocused
|
||||
|
||||
if (isInventoryKeyPressed(minecraft, keyCode, scanCode)) {
|
||||
if (searchFocused) { return true }
|
||||
else {
|
||||
playSound(CobblemonSounds.TM_MACHINE_OFF)
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == InputConstants.KEY_ESCAPE) {
|
||||
if (searchFocused) {
|
||||
// Escape from text box
|
||||
this.focused = null
|
||||
return true
|
||||
} else {
|
||||
playSound(CobblemonSounds.TM_MACHINE_OFF)
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
override fun renderBackground(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {}
|
||||
|
||||
override fun renderBg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
|
||||
|
||||
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
super.renderTransparentBackground(graphics)
|
||||
|
||||
val matrices = graphics.pose()
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = baseResource,
|
||||
x = leftPos,
|
||||
y = topPos,
|
||||
width = WIDTH,
|
||||
height = HEIGHT
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = emptyDiscSlot,
|
||||
x = leftPos + 105,
|
||||
y = topPos + 116,
|
||||
width = 16,
|
||||
height = 16
|
||||
)
|
||||
|
||||
var label = if (mode != MOVE_SELECT_MODE) "block.cobblemon.tm_machine".asTranslated()
|
||||
else ( if (selectedPokemon != null) selectedPokemon!!.getDisplayName() else (if (sortType != null) lang("type.suffix", sortType!!.displayName) else lang("ui.moves")))
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = label.bold(),
|
||||
x = leftPos + 5,
|
||||
y = topPos + 1
|
||||
)
|
||||
|
||||
if (mode == TM_BURN_MODE) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tmTray,
|
||||
x = leftPos,
|
||||
y = topPos,
|
||||
width = 120,
|
||||
height = 112
|
||||
)
|
||||
|
||||
val burnProgressWidth = ceil(menu.getBurnProgressRatio() * 81).toInt()
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = leftPos + 33,
|
||||
y = topPos + 36,
|
||||
width = burnProgressWidth,
|
||||
height = 2
|
||||
)
|
||||
|
||||
if (!menu.inventory!!.getItem(TMMachineMenu.BLANK_TM_SLOT).isEmpty) {
|
||||
val postCraftTicks = menu.getPostCraftTicks()
|
||||
val baseOpacity = when {
|
||||
((postCraftTicks >= (CRAFT_TICKS + 1)) && (postCraftTicks <= (CRAFT_TICKS + RESET_DISC_TICKS))) -> (postCraftTicks - (CRAFT_TICKS + 1)) * 0.25F
|
||||
postCraftTicks == (CRAFT_TICKS) -> 0F
|
||||
else -> 1F
|
||||
}
|
||||
|
||||
val overlayOpacity = if (postCraftTicks < CRAFT_TICKS) menu.getBurnProgressRatio() else 0F
|
||||
renderDisc(matrices, (menu.getBurnProgressRatio() < 1F) && isBurnActive(), baseOpacity, overlayOpacity)
|
||||
}
|
||||
}
|
||||
|
||||
super.render(graphics, mouseX, mouseY, delta)
|
||||
|
||||
renderMoveInfo(graphics, mouseX, mouseY)
|
||||
|
||||
this.renderTooltip(graphics, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(partialTick: Float) {}
|
||||
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class TMPartySlotWidget(
|
||||
pX: Number,
|
||||
pY: Number,
|
||||
val pokemon: Pokemon?,
|
||||
onPress: OnPress
|
||||
) : Button(pX.toInt(), pY.toInt(), WIDTH, HEIGHT, Component.literal("PartyMember"), onPress, DEFAULT_NARRATION) {
|
||||
val state = FloatingState()
|
||||
var teachable: Int? = null
|
||||
var clickable: Boolean = true
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 57
|
||||
const val HEIGHT = 31
|
||||
private const val PORTRAIT_DIAMETER = 30
|
||||
const val HALF_SCALE = 0.5F
|
||||
|
||||
const val CAN_LEARN = 0
|
||||
const val CANNOT_LEARN = 1
|
||||
const val LEARNED = 2
|
||||
|
||||
private val slotResource = cobblemonResource("textures/gui/tmmachine/party_slot.png")
|
||||
private val slotLearnableResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable.png")
|
||||
private val slotLearnableDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_learnable_disabled.png")
|
||||
private val slotDisabledResource = cobblemonResource("textures/gui/tmmachine/party_slot_disabled.png")
|
||||
private val slotLearnedResource = cobblemonResource("textures/gui/tmmachine/party_slot_learned.png")
|
||||
val genderIconMale = cobblemonResource("textures/gui/pc/gender_icon_male.png")
|
||||
val genderIconFemale = cobblemonResource("textures/gui/pc/gender_icon_female.png")
|
||||
|
||||
private val canLearnLabel = lang("ui.tm_machine.can_learn")
|
||||
private val cannotLearnLabel = lang("ui.tm_machine.cannot_learn")
|
||||
private val learnedLabel = lang("ui.tm_machine.learned")
|
||||
}
|
||||
|
||||
private fun getResource(): ResourceLocation = when (teachable) {
|
||||
CAN_LEARN -> if (clickable) slotLearnableResource else slotLearnableDisabledResource
|
||||
CANNOT_LEARN -> slotDisabledResource
|
||||
LEARNED -> slotLearnedResource
|
||||
else -> slotResource
|
||||
}
|
||||
|
||||
private fun getLabel(): MutableComponent? = when (teachable) {
|
||||
CAN_LEARN -> canLearnLabel
|
||||
CANNOT_LEARN -> cannotLearnLabel
|
||||
LEARNED -> learnedLabel
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun renderWidget(context: net.minecraft.client.gui.GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
state.currentAspects = pokemon?.aspects ?: emptySet()
|
||||
val matrices = context.pose()
|
||||
|
||||
if (pokemon != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = getResource(),
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = if (clickable && isMouseOver(mouseX.toDouble(), mouseY.toDouble())) height else 0,
|
||||
textureHeight = height * 2,
|
||||
)
|
||||
|
||||
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
|
||||
val ballHeight = 22
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = ballIcon,
|
||||
x = (x - 2) / HALF_SCALE,
|
||||
y = (y - 3) / HALF_SCALE,
|
||||
height = ballHeight,
|
||||
width = 18,
|
||||
textureHeight = ballHeight * 2,
|
||||
scale = HALF_SCALE
|
||||
)
|
||||
|
||||
// Render Pokémon
|
||||
matrices.pushPose()
|
||||
matrices.translate(x + (PORTRAIT_DIAMETER / 2.0) - 2, y - 1.0, 0.0)
|
||||
matrices.scale(2.5F, 2.5F, 1F)
|
||||
drawProfilePokemon(
|
||||
species = pokemon.species.resourceIdentifier,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
scale = 4.5F,
|
||||
partialTicks = delta
|
||||
)
|
||||
matrices.popPose()
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = lang("ui.lv.number", pokemon.level),
|
||||
x = x + 54,
|
||||
y = y + 13,
|
||||
shadow = true,
|
||||
scale = HALF_SCALE
|
||||
)
|
||||
|
||||
if (pokemon.gender != Gender.GENDERLESS) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (pokemon.gender == Gender.MALE) genderIconMale else genderIconFemale,
|
||||
x = (x + 51) / HALF_SCALE,
|
||||
y = (y + 19) / HALF_SCALE,
|
||||
height = 8,
|
||||
width = 6,
|
||||
scale = HALF_SCALE
|
||||
)
|
||||
}
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = pokemon.getDisplayName(),
|
||||
x = x + ( if (pokemon.gender != Gender.GENDERLESS) 50 else 54),
|
||||
y = y + 19,
|
||||
shadow = true,
|
||||
scale = HALF_SCALE
|
||||
)
|
||||
|
||||
getLabel()?.let { label ->
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = label,
|
||||
x = x + (width / 2F),
|
||||
y = y + 26,
|
||||
centered = true,
|
||||
scale = HALF_SCALE
|
||||
)
|
||||
}
|
||||
|
||||
// Held Item
|
||||
val heldItem = pokemon.heldItemNoCopy()
|
||||
if (!heldItem.isEmpty) {
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = heldItem,
|
||||
x = x + 15.0,
|
||||
y = y + 13.5,
|
||||
scale = HALF_SCALE.toDouble(),
|
||||
matrixStack = matrices
|
||||
)
|
||||
}
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotDisabledResource,
|
||||
x = x + 55,
|
||||
y = y,
|
||||
width = 5,
|
||||
height = height,
|
||||
textureWidth = width,
|
||||
textureHeight = height * 2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tmmachine
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.tmmachine.TypesScrollingWidget.ScrollSlotRow
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.util.Mth
|
||||
|
||||
class TypesScrollingWidget(val pX: Int, val pY: Int, val setType: (ElementalType?) -> (Unit)): ScrollingWidget<ScrollSlotRow>(
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
left = pX,
|
||||
top = pY - HEIGHT,
|
||||
slotHeight = SLOT_SIZE + SLOT_SPACING
|
||||
) {
|
||||
companion object {
|
||||
const val WIDTH = 112
|
||||
const val HEIGHT = 100
|
||||
const val SLOT_SIZE = 22
|
||||
const val SLOT_SPACING = 5
|
||||
}
|
||||
|
||||
init {
|
||||
(listOf(null) + ElementalTypes.all()).chunked(4).forEachIndexed { index, listChunk ->
|
||||
addEntry(ScrollSlotRow(listChunk, setType))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition(): Int = rowLeft + width - 3
|
||||
|
||||
override fun renderScrollbar(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val xLeft = this.scrollbarPosition
|
||||
val xRight = xLeft + 3
|
||||
val barHeight = this.bottom - this.y
|
||||
|
||||
var yBottom = ((barHeight * barHeight).toFloat() / this.maxPosition.toFloat()).toInt()
|
||||
yBottom = Mth.clamp(yBottom, 32, barHeight - 8)
|
||||
var yTop = scrollAmount.toInt() * (barHeight - yBottom) / this.maxScroll + this.y
|
||||
if (yTop < this.y) {
|
||||
yTop = this.y
|
||||
}
|
||||
|
||||
context.fill(xLeft, this.y, xRight, this.bottom, FastColor.ARGB32.color(255, 75, 75, 75)) // background
|
||||
context.fill(xLeft,yTop, xRight, yTop + yBottom, FastColor.ARGB32.color(255, 141, 141, 141)) // base
|
||||
}
|
||||
|
||||
fun renderEntry(context: GuiGraphics, mouseX: Int, mouseY: Int, index: Int, x: Int, y: Int) {
|
||||
val entry = this.getEntry(index)
|
||||
entry.x = x
|
||||
entry.y = y
|
||||
entry.renderRow( context, y, x, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun getEntry(index: Int): ScrollSlotRow = children()[index] as ScrollSlotRow
|
||||
|
||||
override fun renderListItems(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val rowX = rowLeft
|
||||
|
||||
for (index in 0 until this.itemCount) {
|
||||
val rowY = this.getRowTop(index)
|
||||
val rowBottom = this.getRowBottom(index)
|
||||
if (rowBottom >= this.y && rowY <= this.bottom) {
|
||||
this.renderEntry(context!!, mouseX, mouseY, index, rowX, rowY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScrollSlotRow(val types: List<ElementalType?>, val setType : (ElementalType?) -> Unit): Slot<ScrollSlotRow>() {
|
||||
companion object {
|
||||
private val allSlotResource = cobblemonResource("textures/gui/tmmachine/type_slot_all.png")
|
||||
private val slotResource = cobblemonResource("textures/gui/tmmachine/type_slot.png")
|
||||
}
|
||||
|
||||
var x: Int = 0
|
||||
var y: Int = 0
|
||||
|
||||
fun renderRow(context: GuiGraphics, y: Int, x: Int, mouseX: Int, mouseY: Int) {
|
||||
types.forEachIndexed { index, type ->
|
||||
val matrices = context.pose()
|
||||
|
||||
val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
|
||||
val startPosY = y + SLOT_SPACING
|
||||
|
||||
val slotHovered = getHoveredSlotIndex(mouseX, mouseY) == index
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (type != null) slotResource else allSlotResource,
|
||||
x = startPosX,
|
||||
y = startPosY,
|
||||
width = SLOT_SIZE,
|
||||
height = SLOT_SIZE,
|
||||
vOffset = if (slotHovered) SLOT_SIZE else 0,
|
||||
textureHeight = SLOT_SIZE * 2
|
||||
)
|
||||
|
||||
if (type != null) {
|
||||
TypeIcon(startPosX + 2, startPosY + 2, type).render(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: GuiGraphics, index: Int, y: Int, x: Int, entryWidth: Int, entryHeight: Int, mouseX: Int, mouseY: Int, hovered: Boolean, tickDelta: Float) {}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
val hoverIndex = getHoveredSlotIndex(mouseX.toInt(), mouseY.toInt())
|
||||
if (hoverIndex > -1 && hoverIndex < types.size) {
|
||||
setType.invoke(types[hoverIndex])
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getHoveredSlotIndex(mouseX: Int, mouseY: Int): Int {
|
||||
types.forEachIndexed { index, _ ->
|
||||
val startPosX = x + ((SLOT_SPACING + SLOT_SIZE) * index)
|
||||
val startPosY = y + SLOT_SPACING + 1
|
||||
|
||||
if (mouseX in startPosX..(startPosX + SLOT_SIZE) && mouseY in startPosY..(startPosY + SLOT_SIZE)) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun getNarration(): Component {
|
||||
if (types.isNotEmpty()) {
|
||||
return "${types[0]}-${types[types.size - 1]}".text()
|
||||
}
|
||||
return "".text()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,27 +29,38 @@ class CobblemonToast(
|
||||
var description: Component,
|
||||
var frameTexture: ResourceLocation,
|
||||
var progress: Float,
|
||||
var progressColor: Int
|
||||
var progressColor: Int,
|
||||
var durationMs: Long? = null
|
||||
) : Toast {
|
||||
|
||||
constructor(packet: ToastPacket) : this(packet.uuid, packet.icon, packet.title, packet.description, packet.frameTexture, packet.progress, packet.progressColor)
|
||||
constructor(packet: ToastPacket) : this(packet.uuid, packet.icon, packet.title, packet.description, packet.frameTexture, packet.progress, packet.progressColor, packet.durationMs)
|
||||
|
||||
private var startTime: Long = -1
|
||||
private var lastProgress = 0F
|
||||
private var lastTime = 0L
|
||||
internal var nextVisibility: Toast.Visibility = Toast.Visibility.SHOW
|
||||
|
||||
override fun render(context: GuiGraphics, manager: ToastComponent, startTime: Long): Toast.Visibility {
|
||||
if (this.startTime == -1L) this.startTime = startTime
|
||||
|
||||
val localDuration = this.durationMs
|
||||
if (localDuration != null && startTime - this.startTime >= localDuration) {
|
||||
return Toast.Visibility.HIDE
|
||||
}
|
||||
|
||||
context.blitSprite(this.frameTexture, 0, 0, this.width(), this.height())
|
||||
val textRenderer = manager.minecraft.font
|
||||
context.drawString(textRenderer, this.title, 30, 7, this.title.style.color?.value ?: -1, false)
|
||||
context.drawString(textRenderer, this.description, 30, 18, this.description.style.color?.value ?: -1, false)
|
||||
context.renderFakeItem(this.icon, 8, 8)
|
||||
|
||||
if (this.hasProgressBar()) {
|
||||
context.fill(3, 28, 157, 29, -1)
|
||||
val f = Mth.clampedLerp(this.lastProgress, this.progress, (startTime - this.lastTime).toFloat() / 100F)
|
||||
context.fill(3, 28, (3F + 154F * f).toInt(), 29, this.progressColor)
|
||||
this.lastProgress = f
|
||||
}
|
||||
|
||||
this.lastTime = startTime
|
||||
return this.nextVisibility
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.OpenTMMPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
|
||||
object OpenTMMPacketHandler : ClientNetworkPacketHandler<OpenTMMPacket> {
|
||||
override fun handle(packet: OpenTMMPacket, client: Minecraft) {
|
||||
//client.setScreen()
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket> {
|
||||
override fun handle(packet: SetActiveTMPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val menu = player.containerMenu as? TMMachineMenu ?: return
|
||||
val inventory = menu.inventory ?: return
|
||||
|
||||
menu.tmMachineEntity?.let { blockEntity ->
|
||||
blockEntity.activeMove = packet.tm?.moveName?.name ?: ""
|
||||
blockEntity.setChanged()
|
||||
player.level().sendBlockUpdated(blockEntity.blockPos, blockEntity.blockState, blockEntity.blockState, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
inventory.setChanged()
|
||||
menu.broadcastChanges()
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.block.entity.TMMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object SetTMMachineContainerDataPacketHandler : ServerNetworkPacketHandler<SetTMMachineContainerDataPacket> {
|
||||
|
||||
override fun handle(packet: SetTMMachineContainerDataPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val menu = player.containerMenu as? TMMachineMenu ?: return
|
||||
val slotIndex = packet.dataSlotIndex
|
||||
val slotValue = packet.value
|
||||
|
||||
menu.containerData?.set(slotIndex, slotValue)
|
||||
|
||||
// If setting to not active, also reset progress
|
||||
if (slotIndex == TMMachineBlockEntity.BURN_ACTIVE_INDEX && slotValue == 0) {
|
||||
menu.containerData?.set(TMMachineBlockEntity.BURN_PROGRESS_INDEX, 0)
|
||||
}
|
||||
|
||||
menu.broadcastChanges()
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
|
||||
import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.renderer.LevelRenderer
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockAndTintGetter
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import org.joml.Matrix4f
|
||||
|
||||
class TMShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
|
||||
BlockEntityRenderer<TMShelfBlockEntity> {
|
||||
|
||||
private val slotWidthPixels = 6
|
||||
private val slotHeightPixels = 2
|
||||
private val textureSize = 16f
|
||||
|
||||
private val leftMargin = 1f
|
||||
private val rightMargin = 1f
|
||||
private val topMargin = 1f
|
||||
private val bottomMargin = 1f
|
||||
private val interRowSpacing = 0f
|
||||
|
||||
override fun render(
|
||||
entity: TMShelfBlockEntity,
|
||||
partialTicks: Float,
|
||||
poseStack: PoseStack,
|
||||
buffer: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val facing = entity.blockState.getValue(HorizontalDirectionalBlock.FACING)
|
||||
val rotation = when (facing) {
|
||||
Direction.NORTH -> 0f
|
||||
Direction.SOUTH -> 180f
|
||||
Direction.WEST -> 90f
|
||||
Direction.EAST -> -90f
|
||||
else -> 0f
|
||||
}
|
||||
|
||||
val lightGetter = entity.level as? BlockAndTintGetter
|
||||
val packedLight = lightGetter?.let { LevelRenderer.getLightColor(it, entity.blockPos) } ?: 0xF000F0
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.5, 0.5)
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(rotation))
|
||||
poseStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
val slotWidth = slotWidthPixels / textureSize
|
||||
val slotHeight = slotHeightPixels / textureSize
|
||||
val verticalSpacing = (slotHeightPixels + interRowSpacing) / textureSize
|
||||
val totalHeight = slotHeight * 7 + interRowSpacing * 6 / textureSize
|
||||
val startY = topMargin / textureSize
|
||||
|
||||
for ((index, item) in entity.items.withIndex()) {
|
||||
if (!item.isEmpty) {
|
||||
val row = index / 2
|
||||
val col = index % 2
|
||||
|
||||
val isLeftColumn = col == 0
|
||||
val posX = if (isLeftColumn)
|
||||
leftMargin / textureSize
|
||||
else
|
||||
(16f - rightMargin - slotWidthPixels) / textureSize
|
||||
|
||||
val posY = startY + (6 - row) * verticalSpacing
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(posX, posY, -0.001f)
|
||||
|
||||
val tex = getItemTexture(item)
|
||||
val colorize = item.item is TechnicalMachineItem
|
||||
val color = if (colorize) {
|
||||
TMMoveComponent.getTMMove(item)?.elementalType?.primaryColor ?: 0xAAAAAA
|
||||
} else 0xFFFFFF
|
||||
|
||||
renderSlotQuad(poseStack, buffer, tex, color, packedLight, slotWidth, slotHeight, tint = colorize)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun renderSlotQuad(
|
||||
poseStack: PoseStack,
|
||||
buffer: MultiBufferSource,
|
||||
texture: ResourceLocation,
|
||||
color: Int,
|
||||
light: Int,
|
||||
width: Float,
|
||||
height: Float,
|
||||
tint: Boolean
|
||||
) {
|
||||
val consumer: VertexConsumer = buffer.getBuffer(RenderType.text(texture))
|
||||
val matrix: Matrix4f = poseStack.last().pose()
|
||||
|
||||
val (r, g, b, a) = if (tint) {
|
||||
listOf(
|
||||
FastColor.ARGB32.red(color) / 255f,
|
||||
FastColor.ARGB32.green(color) / 255f,
|
||||
FastColor.ARGB32.blue(color) / 255f,
|
||||
1.0f
|
||||
)
|
||||
} else listOf(1f, 1f, 1f, 1f)
|
||||
|
||||
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 0f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 1f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 1f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 0f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
}
|
||||
|
||||
private fun getItemTexture(itemStack: ItemStack): ResourceLocation {
|
||||
val id = BuiltInRegistries.ITEM.getKey(itemStack.item)
|
||||
return cobblemonResource("textures/block/tm_shelf/${id.path}.png")
|
||||
}
|
||||
}
|
||||
+24
-12
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.render.camera
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.Seat
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController.setup
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -18,6 +17,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository.getPoser
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.mixin.accessor.CameraAccessor
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Camera
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
@@ -73,18 +73,31 @@ object MountedCameraRenderer
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
|
||||
var offset = Vector3f(0f, (currEyeHeight - (entity.bbHeight / 2)).toFloat(), 0f)
|
||||
|
||||
// Get additional offset from poser and add to the eyeHeight offset when first person
|
||||
// Get additional offset from poser and add to the eyeHeight offset
|
||||
if (!instance.isDetached) {
|
||||
offset.add(getFirstPersonOffset(model, locatorName))
|
||||
offset.add(
|
||||
getFirstPersonOffset(model, locatorName)
|
||||
)
|
||||
} else {
|
||||
offset.add(
|
||||
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName)
|
||||
)
|
||||
}
|
||||
|
||||
// Rotate Offset if needed
|
||||
if (controller.isActive()) {
|
||||
offset = controller.getRenderOrientation(instance.partialTickTime).transform(offset)
|
||||
} else {
|
||||
offset = Axis.YP.rotationDegrees(180f - instance.yRot).transform(offset)
|
||||
}
|
||||
|
||||
val position = locatorOffset.add(entityPos).add(Vec3(offset))
|
||||
return position
|
||||
val pos = locatorOffset.add(entityPos).toVector3f()
|
||||
val offsetDistance = offset.length()
|
||||
val offsetDirection = offset.mul(1 / offsetDistance)
|
||||
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
|
||||
return Vec3(offsetDirection.mul(maxZoom).add(pos))
|
||||
} else {
|
||||
val xRot = (-1 * Math.toRadians(instance.xRot.toDouble())).toFloat()
|
||||
|
||||
@@ -113,9 +126,8 @@ object MountedCameraRenderer
|
||||
val pos = entityPos.toVector3f().add(pivot)
|
||||
|
||||
// Get offset from poser
|
||||
val offset = getThirdPersonOffset(thirdPersonReverse, model, locatorName)
|
||||
val offset = getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName)
|
||||
|
||||
if (thirdPersonReverse) offset.z *= -1f
|
||||
val rotatedOffset = orientation.transform(offset)
|
||||
val offsetDistance = rotatedOffset.length()
|
||||
val offsetDirection = rotatedOffset.mul(1 / offsetDistance)
|
||||
@@ -130,18 +142,18 @@ object MountedCameraRenderer
|
||||
|
||||
private fun getThirdPersonOffset(
|
||||
thirdPersonReverse: Boolean,
|
||||
model: PosableModel,
|
||||
cameraOffsets: Map<String, Vec3>,
|
||||
locatorName: String
|
||||
): Vector3f {
|
||||
val cameraOffsets = model.thirdPersonCameraOffset
|
||||
|
||||
return if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
|
||||
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
|
||||
cameraOffsets[locatorName + "_reverse"]!!.toVector3f()
|
||||
} else if (cameraOffsets.containsKey(locatorName)) {
|
||||
cameraOffsets[locatorName]!!.toVector3f()
|
||||
} else {
|
||||
Vector3f(0f, 2f, 4f)
|
||||
}
|
||||
if (thirdPersonReverse) offset.z *= -1
|
||||
return offset
|
||||
}
|
||||
|
||||
private fun getFirstPersonOffset(model: PosableModel, locatorName: String): Vector3f {
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.color
|
||||
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.item.components.TMMoveComponent.Companion.getTMMove
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object TechnicalMachineItemColorProvider : ItemColor {
|
||||
override fun getColor(itemStack: ItemStack, layer: Int): Int {
|
||||
val moveType = getTMMove(itemStack)?.elementalType ?: ElementalTypes.NORMAL
|
||||
val color = if (layer == 0) moveType.primaryColor else moveType.secondaryColor
|
||||
return FastColor.ARGB32.opaque(color)
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -92,12 +92,15 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
|
||||
open var profileScale = 1F
|
||||
|
||||
/** Used for third person riding camera */
|
||||
open var thirdPersonCameraOffset = mutableMapOf<String, Vec3>()
|
||||
/** Used for third person riding camera with no view bobbing */
|
||||
open var thirdPersonCameraOffsetNoViewBobbing = mutableMapOf<String, Vec3>()
|
||||
|
||||
/** Used for third person riding camera */
|
||||
/** Used for third person riding camera with no view bobbing */
|
||||
open var thirdPersonPivotOffset = mutableMapOf<String, Vec3>()
|
||||
|
||||
/** Used for third person riding camera with view bobbing */
|
||||
open var thirdPersonCameraOffset = mutableMapOf<String, Vec3>()
|
||||
|
||||
/** Used for first person riding camera */
|
||||
open var firstPersonCameraOffset = mutableMapOf<String, Vec3>()
|
||||
|
||||
@@ -201,8 +204,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
|
||||
/**
|
||||
* Animation group : animation name [: primary]
|
||||
* e.g. "particle_dummy:animation.particle_dummy.dragon_claw_target:primary"
|
||||
* e.g. "particle_dummy:animation.particle.dummy.stat_up
|
||||
*/
|
||||
fun extractAnimation(string: String): ActiveAnimation? {
|
||||
val group = string.substringBefore(":")
|
||||
|
||||
+10
-1
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import com.cobblemon.mod.common.util.isDusk
|
||||
@@ -49,6 +49,15 @@ class PoseAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
if (json.has("isRideStyle")) {
|
||||
val styles = json.get("isRideStyle").asJsonArray.map { it.asString }
|
||||
conditionsList.add {
|
||||
val pokemon = it.getEntity() as? PokemonEntity ?: return@add false
|
||||
val rideStyle = pokemon.ridingController?.context?.style ?: return@add false
|
||||
return@add styles.any { it == rideStyle.name }
|
||||
}
|
||||
}
|
||||
|
||||
addCondition("isTouchingWater") { entity, expectedValue -> entity.isInWater == expectedValue }
|
||||
addCondition("isInWaterOrRain") { entity, expectedValue -> entity.isInWaterOrRain == expectedValue }
|
||||
addCondition("isUnderWater") { entity, expectedValue -> entity.isUnderWater == expectedValue }
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ArticunoModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("articuno")
|
||||
override val head = getPart("head")
|
||||
override val leftLeg = getPart("leftleg")
|
||||
override val rightLeg = getPart("rightleg")
|
||||
override val leftWing = getPart("leftwing")
|
||||
override val rightWing = getPart("rightwing")
|
||||
|
||||
override var portraitScale = 2.8F
|
||||
override var portraitTranslation = Vec3(-0.35, 0.6, 0.0)
|
||||
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hover: Pose
|
||||
lateinit var fly: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = UI_POSES + PoseType.STAND,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("articuno", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "walk",
|
||||
poseType = PoseType.WALK,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("articuno", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F)
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseTypes = setOf(PoseType.HOVER, PoseType.FLOAT),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("articuno", "air_idle"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("articuno", "air_fly"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("articuno", "faint") else null
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class MewtwoModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BimanualFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("mewtwo")
|
||||
override val head = getPart("head")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightArm = getPart("arm_right")
|
||||
|
||||
override var portraitScale = 2.8F
|
||||
override var portraitTranslation = Vec3(-0.15, 1.5, 0.0)
|
||||
|
||||
override var profileScale = 0.7F
|
||||
override var profileTranslation = Vec3(0.0, 0.61, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hover: Pose
|
||||
lateinit var fly: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("mewtwo", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = UI_POSES + PoseType.STAND,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mewtwo", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseType = PoseType.WALK,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mewtwo", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
|
||||
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mewtwo", "air_idle")
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mewtwo", "air_fly")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("mewtwo", "faint") else null
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class MoltresModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("moltres")
|
||||
override val head = getPart("head")
|
||||
override val leftLeg = getPart("leftleg")
|
||||
override val rightLeg = getPart("rightleg")
|
||||
override val leftWing = getPart("leftwing")
|
||||
override val rightWing = getPart("rightwing")
|
||||
|
||||
override var portraitScale = 2.8F
|
||||
override var portraitTranslation = Vec3(-0.9, 0.45, 0.0)
|
||||
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hover: Pose
|
||||
lateinit var fly: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = UI_POSES + PoseType.STAND,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("moltres", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseType = PoseType.WALK,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("moltres", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F)
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("moltres", "air_idle"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("moltres", "air_fly"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("moltres", "faint") else null
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class MrmimeModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("mr_mime")
|
||||
override val head = getPart("head")
|
||||
override val leftLeg = getPart("leg_left1")
|
||||
override val rightLeg = getPart("leg_right1")
|
||||
|
||||
override var portraitScale = 2.1F
|
||||
override var portraitTranslation = Vec3(-0.15, 1.25, 0.0)
|
||||
|
||||
override var profileScale = 0.7F
|
||||
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("mr_mime", "blink")}
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = STATIONARY_POSES + UI_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mr_mime", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("mr_mime", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("mrmime", "faint") else null
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ParasectModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("parasect")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 1.8F
|
||||
override var portraitTranslation = Vec3(-0.35, -0.8, 0.0)
|
||||
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(-0.05, 0.45, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = STATIONARY_POSES + UI_POSES,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
bedrock("parasect", "ground_idle"),
|
||||
singleBoneLook()
|
||||
)
|
||||
)
|
||||
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("parasect", "sleep"))
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = MOVING_POSES,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
bedrock("parasect", "ground_walk"),
|
||||
singleBoneLook()
|
||||
)
|
||||
)
|
||||
}
|
||||
override fun getFaintAnimation(state: PosableState) = bedrockStateful("parasect", "faint")
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SquirtleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
|
||||
|
||||
override var portraitScale = 2.02F
|
||||
override var portraitTranslation = Vec3(-0.12, -0.21, 0.0)
|
||||
override var portraitTranslation = Vec3(-0.26, -0.21, 0.0)
|
||||
|
||||
override var profileScale = 0.78F
|
||||
override var profileTranslation = Vec3(-0.04, 0.57, 0.0)
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ class WartortleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val head = getPart("head_ai")
|
||||
|
||||
override var portraitScale = 1.57F
|
||||
override var portraitTranslation = Vec3(-0.05, 0.54, 0.0)
|
||||
override var portraitTranslation = Vec3(-0.17, 0.54, 0.0)
|
||||
|
||||
override var profileScale = 0.69F
|
||||
override var profileTranslation = Vec3(-0.04, 0.69, 0.0)
|
||||
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ZapdosModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BiWingedFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("zapdos")
|
||||
override val head = getPart("head")
|
||||
override val leftLeg = getPart("leftleg")
|
||||
override val rightLeg = getPart("rightleg")
|
||||
override val leftWing = getPart("left_wing")
|
||||
override val rightWing = getPart("right_wing")
|
||||
|
||||
override var portraitScale = 2.7F
|
||||
override var portraitTranslation = Vec3(-0.9, -0.65, 0.0)
|
||||
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hover: Pose
|
||||
lateinit var fly: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + UI_POSES - PoseType.HOVER,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("zapdos", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.7F, amplitudeMultiplier = 0.85F),
|
||||
bedrock("zapdos", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseTypes = setOf(PoseType.HOVER, PoseType.FLOAT),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("zapdos", "air_idle"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("zapdos", "air_fly"),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.6F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("zapdos", "faint") else null
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class CrobatModel(root: ModelPart) : PokemonPosableModel(root) {
|
||||
override val rootPart = root.registerChildWithAllChildren("crobat")
|
||||
|
||||
override var portraitScale = 1.2F
|
||||
override var portraitTranslation = Vec3(-0.1, 1.8, 0.0)
|
||||
|
||||
override var profileScale = 0.5F
|
||||
override var profileTranslation = Vec3(0.0, 1.2, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hover: Pose
|
||||
lateinit var fly: Pose
|
||||
lateinit var battleidle: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("crobat", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("crobat", "blink") }
|
||||
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("crobat", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
|
||||
condition = { !it.isBattling },
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
bedrock("crobat", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
bedrock("crobat", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseType = PoseType.HOVER,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("crobat", "air_idle")
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseType = PoseType.FLY,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("crobat", "air_fly")
|
||||
)
|
||||
)
|
||||
|
||||
battleidle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("crobat", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("crobat", "faint") else null
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SneaselHisuianModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("sneasel_hisuian")
|
||||
override val head = getPart("head")
|
||||
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
|
||||
override var portraitScale = 2.16F
|
||||
override var portraitTranslation = Vec3(-0.18, -0.11, 0.0)
|
||||
|
||||
override var profileScale = 0.9F
|
||||
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("sneasel_hisuian", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("sneasel_hisuian", "blink") }
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("sneasel_hisuian", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("sneasel_hisuian", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
|
||||
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
|
||||
//bedrock("sneasel_hisuian", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SneaselModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("sneasel")
|
||||
override val head = getPart("head")
|
||||
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
|
||||
override var portraitScale = 2.16F
|
||||
override var portraitTranslation = Vec3(-0.18, -0.11, 0.0)
|
||||
|
||||
override var profileScale = 0.9F
|
||||
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("sneasel", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("sneasel", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("sneasel", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("sneasel", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F),
|
||||
BimanualSwingAnimation(this, swingPeriodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
|
||||
//bedrock("sneasel", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user