mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 22:56:23 -04:00
Compare commits
163
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c921496b50 | ||
|
|
a9d1000384 | ||
|
|
225721aec4 | ||
|
|
ae9475959d | ||
|
|
0ecb4d32cf | ||
|
|
52a9e30dc1 | ||
|
|
4e417c0b19 | ||
|
|
7aba5dc962 | ||
|
|
1198bad42e | ||
|
|
aa18d9f90e | ||
|
|
3833bcf7c1 | ||
|
|
d85dc372a7 | ||
|
|
a5126f2158 | ||
|
|
ca8bcd966f | ||
|
|
12c7318625 | ||
|
|
4f917b94e8 | ||
|
|
9e82cb2516 | ||
|
|
bbf85d5804 | ||
|
|
9daa85750b | ||
|
|
6f7c3b0681 | ||
|
|
d262bb6136 | ||
|
|
7509934394 | ||
|
|
a0ae226506 | ||
|
|
0ca9f3a3ad | ||
|
|
c5c10e0eec | ||
|
|
274ce14e0f | ||
|
|
0dde8d27e6 | ||
|
|
559e728bc3 | ||
|
|
dcea1bc67f | ||
|
|
7dc451f344 | ||
|
|
b6ea6a6850 | ||
|
|
9ad9c4643b | ||
|
|
a7065e5c06 | ||
|
|
edf74d9ef4 | ||
|
|
768a6df247 | ||
|
|
5c9c8b0b3b | ||
|
|
085972f2d3 | ||
|
|
ddefd1aa24 | ||
|
|
53f441884e | ||
|
|
954c5a1027 | ||
|
|
c26ab8ff60 | ||
|
|
9ea9f9b307 | ||
|
|
fc5ba24e76 | ||
|
|
0fe496a290 | ||
|
|
df0eb21a9e | ||
|
|
79ed7da316 | ||
|
|
908a8b89a6 | ||
|
|
83d4d6f7e9 | ||
|
|
a588906f43 | ||
|
|
e890fd3e9e | ||
|
|
e7c95ac72c | ||
|
|
245765c6af | ||
|
|
f8ae4dcecd | ||
|
|
f694c7c821 | ||
|
|
a4824b6766 | ||
|
|
834533aa6a | ||
|
|
0c9cf2c83d | ||
|
|
fbffeff8dc | ||
|
|
8532ca2d38 | ||
|
|
722bf5ba7b | ||
|
|
eb0365d7ce | ||
|
|
ca1cf72d86 | ||
|
|
e12936fb66 | ||
|
|
9b85742c00 | ||
|
|
97c2b46192 | ||
|
|
847e5d7647 | ||
|
|
43e05ef7e7 | ||
|
|
aceaef14c5 | ||
|
|
b14eed17eb | ||
|
|
c168364ca4 | ||
|
|
069d9df3be | ||
|
|
7c40d4298d | ||
|
|
1a2c5851f4 | ||
|
|
5bf962897f | ||
|
|
db6533795b | ||
|
|
08c49d36db | ||
|
|
74cd758a0b | ||
|
|
596c0cd8c3 | ||
|
|
9af0879359 | ||
|
|
c42e6bb963 | ||
|
|
c12e6fd3ed | ||
|
|
78f97984c7 | ||
|
|
b65cec2916 | ||
|
|
63ef406058 | ||
|
|
4856e9feee | ||
|
|
e839813c2a | ||
|
|
c70f2a6684 | ||
|
|
860ec50071 | ||
|
|
93e26fb31f | ||
|
|
23e2e18019 | ||
|
|
a0d5a1fbad | ||
|
|
b1b5ef7236 | ||
|
|
ef3623534c | ||
|
|
e4554faa4f | ||
|
|
0e32b20bbd | ||
|
|
6ab2aa3413 | ||
|
|
16819bb0e7 | ||
|
|
0bf3488a01 | ||
|
|
1f3d3e90cf | ||
|
|
b1135e334c | ||
|
|
6cdeea39c3 | ||
|
|
8cf58d22f9 | ||
|
|
6eccc0e3e3 | ||
|
|
59d3475b31 | ||
|
|
efa8f6a81e | ||
|
|
9081dc3a19 | ||
|
|
c2b6421ff9 | ||
|
|
99af478165 | ||
|
|
873028d8e8 | ||
|
|
2db8728560 | ||
|
|
d0dbbe7850 | ||
|
|
a6280d223a | ||
|
|
4187b31daf | ||
|
|
ea33d86dbd | ||
|
|
8dc5c19210 | ||
|
|
3499111c92 | ||
|
|
28cd404bef | ||
|
|
407c4baff2 | ||
|
|
d0653c5b13 | ||
|
|
12212605cd | ||
|
|
ea49ea7818 | ||
|
|
725b8bc614 | ||
|
|
875cd2c1b9 | ||
|
|
db972aba4a | ||
|
|
dfc130cf55 | ||
|
|
7a7eed9c45 | ||
|
|
37c4d4c7fe | ||
|
|
2b5cac4361 | ||
|
|
c19a94a2d3 | ||
|
|
fbe6328f97 | ||
|
|
d205650373 | ||
|
|
eb4fa676ca | ||
|
|
2ec6ba382c | ||
|
|
65203b6d57 | ||
|
|
1e573f80bb | ||
|
|
819630a8db | ||
|
|
714412246b | ||
|
|
583e4360cf | ||
|
|
7972662537 | ||
|
|
a4fd86fc95 | ||
|
|
47417a309f | ||
|
|
4d11bd0ed8 | ||
|
|
db37721e9f | ||
|
|
c373d24fe1 | ||
|
|
2770cbbe35 | ||
|
|
699a47c633 | ||
|
|
47ebc6c6ca | ||
|
|
5a7da67d67 | ||
|
|
60feb21e49 | ||
|
|
00d7db6f48 | ||
|
|
5f7990a899 | ||
|
|
bdb47532b9 | ||
|
|
b5a40adcdf | ||
|
|
955422aab7 | ||
|
|
d112475c97 | ||
|
|
b652235791 | ||
|
|
cfa55095aa | ||
|
|
00e695a0e7 | ||
|
|
d933adfaf1 | ||
|
|
cb86c8d4dd | ||
|
|
bcac005fb1 | ||
|
|
aa6536b57c | ||
|
|
58a5412602 |
+80
-12
@@ -42,16 +42,31 @@
|
||||
- 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.
|
||||
- Added new advancements Didn't Stop To Think, We Need To Cook, and Pokémon Jockey!.
|
||||
- Berries can now be smelted into their respective dyes.
|
||||
- Added Syrupy Apples.
|
||||
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
#### Gen 2
|
||||
- Marill
|
||||
- Azumarill
|
||||
- Hoppip
|
||||
- Skiploom
|
||||
- Jumpluff
|
||||
- Dunsparce
|
||||
|
||||
#### Gen 3
|
||||
- Azurill
|
||||
- Meditite
|
||||
- Medicham
|
||||
- Electrike
|
||||
- Manectric
|
||||
- Spoink
|
||||
- Grumpig
|
||||
- Swablu
|
||||
- Altaria
|
||||
- Snorunt
|
||||
- Glalie
|
||||
- Latias
|
||||
@@ -65,6 +80,8 @@
|
||||
- Froslass
|
||||
- Glameow
|
||||
- Purugly
|
||||
- Skorupi
|
||||
- Drapion
|
||||
|
||||
#### Gen 5
|
||||
- Pansage
|
||||
@@ -87,10 +104,20 @@
|
||||
- Solosis
|
||||
- Duosion
|
||||
- Reuniclus
|
||||
- Tynamo
|
||||
- Eelektrik
|
||||
- Eelektross
|
||||
- Mienfoo
|
||||
- Mienshao
|
||||
- Sewaddle
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
|
||||
#### Gen 6
|
||||
- Skiddo
|
||||
- Gogoat
|
||||
- Espurr
|
||||
- Meowstic
|
||||
- Inkay
|
||||
- Malamar
|
||||
- Hawlucha
|
||||
@@ -99,13 +126,25 @@
|
||||
- Noivern
|
||||
|
||||
#### Gen 7
|
||||
- Yungoos
|
||||
- Gumshoos
|
||||
- Drampa
|
||||
- Alolan Grimer
|
||||
- Alolan Muk
|
||||
- Togedemaru
|
||||
|
||||
#### Gen 8
|
||||
- Skwovet
|
||||
- Greedent
|
||||
- Toxel
|
||||
- Toxtricity
|
||||
- Morpeko
|
||||
- Silicobra
|
||||
- Sandaconda
|
||||
- Sinistea
|
||||
- Polteageist
|
||||
- Galarian Corsola
|
||||
- Cursola
|
||||
|
||||
#### Gen 9
|
||||
- Smoliv
|
||||
@@ -118,6 +157,12 @@
|
||||
- Cyclizar
|
||||
- Poltchageist
|
||||
- Sinistcha
|
||||
- Capsakid
|
||||
- Scovillain
|
||||
- Tadbulb
|
||||
- Bellibolt
|
||||
- Toedscool
|
||||
- Toedscruel
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Garchomp
|
||||
@@ -139,6 +184,13 @@
|
||||
- Dratini
|
||||
- Dragonair
|
||||
- Dragonite
|
||||
- G. Corsola
|
||||
- Cursola
|
||||
- Dunsparce
|
||||
- Dudunsparce (both forms)
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Gyarados
|
||||
@@ -186,12 +238,21 @@
|
||||
- Cacturne
|
||||
- Taillow
|
||||
- Swellow
|
||||
- Added Syrupy Apples.
|
||||
- Seel
|
||||
- Dewgong
|
||||
- Honedge
|
||||
- Doublade
|
||||
- Aegislash
|
||||
- Drowzee
|
||||
- Hypno
|
||||
- Mudkip
|
||||
- Marshtomp
|
||||
- Swampert
|
||||
- Shelmet
|
||||
- Escavalier
|
||||
- Klink
|
||||
- Klank
|
||||
- Klinklang
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -245,6 +306,7 @@
|
||||
- Updated Pokémon state icons shown in party interfaces
|
||||
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
|
||||
- MoLang `run_action_effect` now works on Pokémon.
|
||||
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
|
||||
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
|
||||
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
|
||||
|
||||
@@ -290,8 +352,8 @@
|
||||
- Fix issue where locator X axis was not aligned with blockbench.
|
||||
- Fix issue where particle effects that play on frame 1 on sendout would never play (Gastly)
|
||||
- The Wiglett line will now sink in water again.
|
||||
- Fixed the Sprigatito line's portraits being zoomed in too far.
|
||||
- Fixed Floragato's battle cry animation from breaking.
|
||||
- Fixed the Sprigatito line's portraits being zoomed in too far.
|
||||
- Fixed Floragato's battle cry animation from breaking.
|
||||
- Fixed the block texture name for the Pep-Up Flower being inaccurate
|
||||
- Fixed issue where the restoration tank would not accept valid items from a hopper.
|
||||
- Fixed energy root not being shearable
|
||||
@@ -309,8 +371,12 @@
|
||||
- Improved flyers avoiding getting stuck on fence posts.
|
||||
- Fixed air balloon battle text not correctly displaying the Pokémon or item name
|
||||
- Fixed an issue where items retrieved from a Display case would disappear if a player's inventory is full
|
||||
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option
|
||||
- Fixed recoil eyes on Shellder & Cloyster.
|
||||
- Fixed Shroomish spinning at lightning speed when fainting (no longer allowed to let it rip.)
|
||||
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option.
|
||||
- Fixed a crash due to a ConcurrentModificationException that could occur during world generation.
|
||||
- Fixed Quaxly's animations showing hidden wing sets
|
||||
- Fixed Moon Ball moon phase logic to actually work correctly
|
||||
- Fixed `/pokedex printcalculations` to now show the correct percentage completed of the Pokedex
|
||||
- Fixed mod incompatibility with the `Raised` mod
|
||||
@@ -327,7 +393,7 @@
|
||||
- Renamed Cobblemon's creative tabs to start with "Cobblemon: " to distinguish Cobblemon's tabs from tabs for other mods.
|
||||
- Various items now have a rarity value.
|
||||
- Reworked observable handling in `Pokemon.kt` to cut down on RAM usage and clarify the file.
|
||||
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
|
||||
- Note: This will break mods that used our observable functionality there or in MoveSet, IVs, EVs, or BenchedMoves.
|
||||
- Using `Pokemon#onChange()` is now the way to mark a Pokémon as needing a save.
|
||||
- Using `[Pokemon].changeObservable` is now the way to get an `Observable` for any save-worthy changes.
|
||||
- Updated NPCEntity beam positioning to properly account for the baseScale property.
|
||||
@@ -358,7 +424,7 @@
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
- Pokemon no longer have a change observable
|
||||
|
||||
|
||||
### MoLang & Datapacks
|
||||
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
|
||||
- The `requiredContext` for an item interaction evolution
|
||||
@@ -392,6 +458,8 @@
|
||||
- Added Molang functions for Pokémon: `pokeball`, `held_item`, `remove_held_item`, `hyper_train_iv`, `validate_moveset`, `initialize_moveset`, and `add_exp`.
|
||||
- Added Molang functions for Pokémon: `aspects`, `form_aspects`, `unlearn_move`, `teach_learnable_moves`, `cosmetic_item`, and `remove_cosmetic_item`.
|
||||
- Added Molang functions for Pokémon: `ability`, `set_iv`, `set_ev`, `teach_move`, and `can_learn_move`.
|
||||
- Added Molang function `q.delete_variable(<struct>, <variable_name>)` to delete a value from a variable structure in MoLang data.
|
||||
- Added Molang function `q.delete_variables(<struct>)` to delete all values from a variable structure in MoLang data.
|
||||
- Adds Flows for `STARTER_CHOSEN`, `SHOULDER_MOUNTED`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
|
||||
- Adds Flows for `POKEMON_HEALED`, `POKEMON_SCANNED`, `BERRY_HARVEST`, `LOOT_DROPPED`, `POKEMON_SEEN`, `COLLECT_EGG`, `HATCH_EGG`, and `EXPERIENCE_GAINED`.
|
||||
- Adds Flows for `POKEMON_CATCH_RATE`, `BAIT_SET`, `BAIT_SET_PRE`, `BAIT_CONSUMED`, `POKEROD_CAST_PRE`, `POKEROD_CAST_POST`, `POKEROD_REEL`, and `BOBBER_SPAWN_POKEMON_PRE`.
|
||||
@@ -418,7 +486,7 @@
|
||||
- Added optional box argument to the /pc command.
|
||||
- Pokédex and Dialogue screens now close when the inventory keybind is pressed.
|
||||
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
|
||||
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
|
||||
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
|
||||
- Added debug renderer for posable entity locators.
|
||||
- Added crossover paintings from Close Combat, Premonition, Altar, Slumber, and Nomad.
|
||||
- Added optional box argument to the /pc command
|
||||
@@ -572,7 +640,7 @@
|
||||
- Fixed Vivichoke Dip and Leek & Potato Stew not returning a bowl upon consumption.
|
||||
- Fixed Fossil Restoration Tank not accepting Hay Bales as organic material.
|
||||
- Fixed Potion items applying double their intended healing value.
|
||||
- Fixed Fast Ball capture bonuses applying to all Pokémon, making it the Best Ball instead of the Mediocre Edge-Case Ball.
|
||||
- Fixed Fast Ball capture bonuses applying to all Pokémon, making it the Best Ball instead of the Mediocre Edge-Case Ball.
|
||||
- Fixed "learned new move" messages appearing for already-known moves on Pokémon evolutions.
|
||||
- Fixed Pokémon Item Models breaking shadows nearby when being placed in Display Cases or Item Frames.
|
||||
- Fixed berries not giving bonus yields when planted in their preferred biomes. I'm sure we've fixed that 5 times now.
|
||||
@@ -581,7 +649,7 @@
|
||||
- Fixed players with shouldered Pokémon not being able to rejoin their 1.5.2 worlds using 1.6.
|
||||
- Fixed `PokemonProperties` utilizing `ability=<some ability>` being treated as a forced ability even when it is a legal ability for the Pokémon.
|
||||
- Fixed type formatting in Pokédex scanner mode when dual types require two lines.
|
||||
- Fixed trading sometimes crashing the game or server.
|
||||
- Fixed trading sometimes crashing the game or server.
|
||||
- Fixed Wild shiny sounds not respecting the `shinyNoticeParticlesDistance` config setting.
|
||||
- Fixed Pokémon being able to evolve mid-battle.
|
||||
- Fixed NPC held items being able to be stolen by players. Don't be a thief!
|
||||
@@ -675,7 +743,7 @@
|
||||
- Stonjourner Henge Ruins, Luna Henge Ruins, and Sol Henge Ruins.
|
||||
- Submerged Shipwreck Cove and Lush Shipwreck Cove.
|
||||
- Pokémon Centers to all 5 village types.
|
||||
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 1
|
||||
- Alola Bias Cubone (built-in resource pack)
|
||||
@@ -998,7 +1066,7 @@
|
||||
- Big Roots and Energy Roots now share the same sounds as intended.
|
||||
- Fixed sounds made by blocks playing at a lower pitch than intended.
|
||||
- Fixed Chimchar and Monferno comically T posing whenever they sleep.
|
||||
- Fixed Cetitan's cry breaking its walk and sleep animations.
|
||||
- Fixed Cetitan's cry breaking its walk and sleep animations.
|
||||
- Fixed the Magby line not having any placeholder walk animations.
|
||||
- Fixed Duskull and Dusclops using skylight levels for their nether spawn data. There is no sun in the Nether!
|
||||
- Fixed Hisuian Zoroark using base Zoroark stats.
|
||||
@@ -1014,7 +1082,7 @@
|
||||
- Fixed Trevenant T-posing after 8 seconds of sleeping. I also do that.
|
||||
- Fixed Bewear T-posing for a bit if a wild one faints where you can see.
|
||||
- Fixed Noctowl's placeholder fly animation.
|
||||
- Corrected Lotad blinking animation.
|
||||
- Corrected Lotad blinking animation.
|
||||
- Fixed reviving items causing errors when used in battle.
|
||||
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
|
||||
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
|
||||
@@ -1104,7 +1172,7 @@
|
||||
- Added a new universal locator called "top".
|
||||
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
|
||||
- Added missing `minYaw` configuration to the `q.look()` function for JSON posers.
|
||||
- Some pose condition names have been changed:
|
||||
- Some pose condition names have been changed:
|
||||
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
|
||||
- `isSubermegedInWater` has been changed to `isUnderWater`
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface DoubleJump {
|
||||
boolean isDoubleJumping();
|
||||
}
|
||||
@@ -88,8 +88,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
|
||||
@Shadow @Final private static Map<Pose, EntityDimensions> POSES;
|
||||
@Shadow @Final public static EntityDimensions STANDING_DIMENSIONS;
|
||||
@Unique
|
||||
private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
@Shadow protected int jumpTriggerTime;
|
||||
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
|
||||
@@ -63,13 +63,15 @@ public abstract class ServerEntityMixin {
|
||||
|
||||
private void cobblemon$sendRidingStateChanges() {
|
||||
if (!(this.entity instanceof PokemonEntity pokemonEntity)) return;
|
||||
if (pokemonEntity.getRidingState() == null) return;
|
||||
if (pokemonEntity.getRiding() == null) return;
|
||||
var ridingBehaviour = pokemonEntity.getRiding();
|
||||
var ridingState = pokemonEntity.getRidingState();
|
||||
if (pokemonEntity.getRidingController() == null) return;
|
||||
var ridingController = pokemonEntity.getRidingController();
|
||||
if (ridingController.getContext() == null) return;
|
||||
var context = ridingController.getContext();
|
||||
var ridingBehaviour = context.getBehaviour();
|
||||
var ridingState = context.getState();
|
||||
var previousRidingState = pokemonEntity.getPreviousRidingState();
|
||||
if (previousRidingState != null && !ridingState.shouldSync(previousRidingState)) return;
|
||||
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour.getKey(), pokemonEntity.getRidingState(), null));
|
||||
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour, ridingState, null));
|
||||
}
|
||||
|
||||
private void cobblemon$sendOrientationChanges() {
|
||||
|
||||
@@ -10,7 +10,10 @@ package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -43,4 +46,10 @@ public abstract class ServerPlayerMixin {
|
||||
controllable.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
//TODO: Switch to sending out on load instead of preventing saving
|
||||
@ModifyExpressionValue(method = "addAdditionalSaveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z"))
|
||||
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
|
||||
return original && !(entity instanceof PokemonEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.DoubleJump;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
@@ -26,6 +27,7 @@ import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.Input;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -49,13 +51,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable{
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable, DoubleJump {
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
|
||||
@Unique boolean cobblemon$isDoubleJumping = false;
|
||||
|
||||
@Shadow
|
||||
private float jumpRidingScale;
|
||||
|
||||
@Shadow public Input input;
|
||||
|
||||
@Shadow private int autoJumpTime;
|
||||
|
||||
@Unique private int cobblemon$survivalJumpTriggerTime;
|
||||
|
||||
@Unique private boolean cobblemon$isJumping;
|
||||
|
||||
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
@@ -135,6 +147,33 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "aiStep", at = @At("HEAD"))
|
||||
private void cobblemon$aiStep(CallbackInfo ci) {
|
||||
this.cobblemon$isDoubleJumping = false;
|
||||
this.cobblemon$isJumping = this.input.jumping;
|
||||
}
|
||||
|
||||
@Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSprinting()Z", shift = At.Shift.BEFORE))
|
||||
private void cobblemon$updateDoubleJumping(CallbackInfo ci) {
|
||||
if (!this.cobblemon$isJumping && this.input.jumping && this.autoJumpTime <= 0 && !this.isSwimming()) {
|
||||
if (this.cobblemon$survivalJumpTriggerTime != 0) {
|
||||
this.cobblemon$isDoubleJumping = true;
|
||||
this.cobblemon$survivalJumpTriggerTime = 0;
|
||||
}
|
||||
else {
|
||||
this.cobblemon$survivalJumpTriggerTime = 7;
|
||||
}
|
||||
}
|
||||
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
|
||||
this.cobblemon$survivalJumpTriggerTime--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDoubleJumping() {
|
||||
return this.cobblemon$isDoubleJumping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
|
||||
@@ -19,8 +19,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -33,9 +31,9 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
|
||||
super(context, model, shadowRadius);
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "TAIL"))
|
||||
public void cobblemon$clientPlayerIcon(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer((Player) entity);
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer(player);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "getRenderOffset(Lnet/minecraft/client/player/AbstractClientPlayer;F)Lnet/minecraft/world/phys/Vec3;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;isCrouching()Z"))
|
||||
|
||||
@@ -89,6 +89,7 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
autoPokemonBehaviours.clear()
|
||||
autoNPCBehaviours.clear()
|
||||
val data = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
|
||||
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
@@ -88,35 +87,23 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
|
||||
BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
|
||||
)
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
|
||||
// BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
|
||||
// )
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE: BlockEntityType<CampfireBlockEntity> = this.create("campfire_pot",
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity,
|
||||
CobblemonBlocks.CAMPFIRE,
|
||||
CobblemonBlocks.SOUL_CAMPFIRE
|
||||
).build(null)
|
||||
)
|
||||
|
||||
/*@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
|
||||
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
|
||||
CobblemonBlocks.LURE_CAKE
|
||||
).build(null)*/
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
|
||||
"lure_cake",
|
||||
BlockEntityType.Builder.of(::LureCakeBlockEntity, CobblemonBlocks.LURE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val POKE_CAKE: BlockEntityType<PokeCakeBlockEntity> = this.create(
|
||||
"poke_cake",
|
||||
BlockEntityType.Builder.of(::PokeCakeBlockEntity, CobblemonBlocks.POKE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE: BlockEntityType<CandlePokeCakeBlockEntity> = this.create(
|
||||
"candle_poke_cake",
|
||||
BlockEntityType.Builder.of(::CandlePokeCakeBlockEntity, CobblemonBlocks.CANDLE_POKE_CAKE).build(null)
|
||||
val POKE_SNACK: BlockEntityType<PokeSnackBlockEntity> = this.create(
|
||||
"poke_snack",
|
||||
BlockEntityType.Builder.of(::PokeSnackBlockEntity, CobblemonBlocks.POKE_SNACK, CobblemonBlocks.POKE_CAKE).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockSetType
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
@@ -230,8 +231,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.CRIMSON_NYLIUM).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.DIRT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
@@ -240,11 +243,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = this.create("lure_cake", LureCakeBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY)))
|
||||
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, false))
|
||||
@JvmField
|
||||
val POKE_CAKE = this.create("poke_cake", PokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE)))
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
|
||||
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)
|
||||
@@ -449,7 +450,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel { if ((it.getValue(CampfireBlock.SOUL) as Boolean)) 9 else 14 }))
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{14}, false))
|
||||
|
||||
@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))
|
||||
@@ -492,8 +496,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@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()))
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
|
||||
@@ -517,7 +522,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.VIVICHOKE_SOUNDS)))
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
|
||||
@@ -540,11 +545,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(SoundType.GRASS)))
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(CobblemonSounds.HEARTY_GRAIN_BALE_SOUNDS)))
|
||||
@JvmField
|
||||
val TATAMI_BLOCK = this.create("tatami_block", HorizontalRotationalBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(SoundType.MOSS_CARPET)))
|
||||
val TATAMI_BLOCK = this.create("tatami_block", HorizontalRotationalBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_BLOCK_SOUNDS)))
|
||||
@JvmField
|
||||
val TATAMI_MAT = this.create("tatami_mat", HorizontalRotationCarpetBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(SoundType.MOSS_CARPET)))
|
||||
val TATAMI_MAT = this.create("tatami_mat", HorizontalRotationCarpetBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_MAT_SOUNDS)))
|
||||
|
||||
/**
|
||||
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
|
||||
|
||||
@@ -349,15 +349,15 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = compostableBlockItem("saccharine_sapling", CobblemonBlocks.SACCHARINE_SAPLING, 0.30f)
|
||||
|
||||
@JvmField
|
||||
val BUGWORT = compostableBlockItem("bugwort", CobblemonBlocks.BUGWORT)
|
||||
// @JvmField
|
||||
// val BUGWORT = compostableBlockItem("bugwort", CobblemonBlocks.BUGWORT) // TODO after 1.7
|
||||
@JvmField
|
||||
val POKE_BAIT = noSettingsItem("poke_bait")
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = blockItem("lure_cake", CobblemonBlocks.LURE_CAKE)
|
||||
val POKE_CAKE = this.create("poke_cake", BlockItem(CobblemonBlocks.POKE_CAKE, Item.Properties().stacksTo(1)))
|
||||
@JvmField
|
||||
val POKE_CAKE = blockItem("poke_cake", CobblemonBlocks.POKE_CAKE)
|
||||
val POKE_SNACK = this.create("poke_snack", BlockItem(CobblemonBlocks.POKE_SNACK, Item.Properties().stacksTo(16)))
|
||||
|
||||
val aprijuices = mutableListOf<AprijuiceItem>()
|
||||
@JvmField
|
||||
@@ -386,31 +386,31 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
// FOODS
|
||||
@JvmField
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // TODO make a SweetHeartItem class for breeding purposes
|
||||
|
||||
@JvmField
|
||||
val TASTY_TAIL = create("tasty_tail", foodItem(3, 0.3f))
|
||||
|
||||
@JvmField
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 7, 0.343f, false)
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 64, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 8, 0.2f, false)
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 64, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 7, 0.343f, false)
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 64, 2, 0.3f, false)
|
||||
@JvmField
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 9, 0.1335f, false)
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
|
||||
@JvmField
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 5, .28f, false)
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 8, 0.2f, false)
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
|
||||
@JvmField
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 7, 0.343f, false)
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 8, 0.2f, false)
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 13, 0.5f, false)
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 64, 13, 0.5f, false)
|
||||
|
||||
// todo we might need to wait on these for later? These impact battles and may be harder to do
|
||||
/*@JvmField
|
||||
@@ -421,20 +421,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for battle purposes
|
||||
*/
|
||||
@JvmField
|
||||
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(16)
|
||||
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(8)
|
||||
.saturationModifier(0.2375F)
|
||||
.nutrition(4)
|
||||
.saturationModifier(0.4F)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(16)
|
||||
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(6)
|
||||
.saturationModifier(0.2335F)
|
||||
.usingConvertsTo(Items.STICK)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(16)
|
||||
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(5)
|
||||
.saturationModifier(0.22F)
|
||||
@@ -460,8 +460,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET)
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET) // TODO after 1.7
|
||||
|
||||
|
||||
// Evolution items
|
||||
@@ -695,7 +695,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 = noSettingsItem("moomoo_milk")
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
||||
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
|
||||
@@ -78,6 +79,7 @@ import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidP
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
@@ -409,6 +411,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler))
|
||||
list.add(PacketRegisterInfo(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler))
|
||||
list.add(PacketRegisterInfo(SaccharineLogBlockParticlesPacket.ID, SaccharineLogBlockParticlesPacket::decode, SaccharineLogBlockParticlesHandler))
|
||||
list.add(PacketRegisterInfo(PokeSnackBlockParticlesPacket.ID, PokeSnackBlockParticlesPacket::decode, PokeSnackBlockParticlesHandler))
|
||||
|
||||
// Hax
|
||||
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
|
||||
|
||||
@@ -26,11 +26,12 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
@JvmField
|
||||
val NURSE_KEY = createKey("nurse", CobblemonBlocks.HEALING_MACHINE, 1, 1)
|
||||
@JvmField
|
||||
val LURE_CAKE_KEY = createKey("lure_cake", CobblemonBlocks.LURE_CAKE, 0, 1)
|
||||
val POKE_SNACK_KEY = createKey("poke_snack", CobblemonBlocks.POKE_SNACK, 0, 1)
|
||||
@JvmField
|
||||
val SACCHARINE_LOG_SLATHERED_KEY = createKey("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED, 0, 1)
|
||||
@JvmField
|
||||
val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
|
||||
private fun createKey(string: String, block: Block, maxTickets: Int, validRange: Int ): ResourceKey<PoiType> =
|
||||
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string)).also { resourceKey ->
|
||||
|
||||
@@ -204,6 +204,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_STEP = this.create("block.evolution_stone_block.step")
|
||||
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_BREAK = this.create("block.tatami.break")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_HIT = this.create("block.tatami.hit")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_PLACE = this.create("block.tatami.place")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_STEP = this.create("block.tatami.step")
|
||||
@JvmField
|
||||
val TATAMI_MAT_BREAK = this.create("block.tatami_mat.break")
|
||||
@JvmField
|
||||
val TATAMI_MAT_PLACE = this.create("block.tatami_mat.place")
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_GIVE_ITEM = this.create("pokemon.gimmighoul.give_item")
|
||||
@JvmField
|
||||
@@ -230,6 +243,24 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val VIVICHOKE_PLACE = this.create("block.vivichoke.place")
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_BREAK = this.create("block.hearty_grain_bale.break")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_PLACE = this.create("block.hearty_grain_bale.place")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_HIT = this.create("block.hearty_grain_bale.hit")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_STEP = this.create("block.hearty_grain_bale.step")
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_BREAK = this.create("block.hearty_grains.break")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_PLACE = this.create("block.hearty_grains.place")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_BREAK_WATER = this.create("block.hearty_grains.break_water")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_PLACE_WATER = this.create("block.hearty_grains.place_water")
|
||||
|
||||
@JvmField
|
||||
val MINT_BREAK = this.create("block.mint.break")
|
||||
@JvmField
|
||||
@@ -324,8 +355,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val IMPACT_WATER = this.create("impact.water")
|
||||
|
||||
|
||||
|
||||
// Note to self or whoever's peeking, block sounds have to be at 1.1f pitch
|
||||
// For some reason, the game pitches them down and compensating for it here returns them to normal
|
||||
@JvmField
|
||||
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
|
||||
TUMBLESTONE_BREAK,
|
||||
@@ -343,6 +374,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
TUMBLESTONE_HIT,
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
EVOLUTION_STONE_BLOCK_BREAK,
|
||||
@@ -352,6 +384,23 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
EVOLUTION_STONE_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
TATAMI_BLOCK_BREAK,
|
||||
TATAMI_BLOCK_STEP,
|
||||
TATAMI_BLOCK_PLACE,
|
||||
TATAMI_BLOCK_HIT,
|
||||
TATAMI_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TATAMI_MAT_SOUNDS = SoundType(1f, 1.1f,
|
||||
TATAMI_MAT_BREAK,
|
||||
TATAMI_BLOCK_STEP,
|
||||
TATAMI_MAT_PLACE,
|
||||
TATAMI_BLOCK_HIT,
|
||||
TATAMI_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_SOUNDS = SoundType(0.8f, 1.1f,
|
||||
@@ -398,6 +447,33 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
SoundEvents.GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAIN_BALE_BREAK,
|
||||
HEARTY_GRAIN_BALE_STEP,
|
||||
HEARTY_GRAIN_BALE_PLACE,
|
||||
HEARTY_GRAIN_BALE_HIT,
|
||||
HEARTY_GRAIN_BALE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAINS_BREAK,
|
||||
HEARTY_GRAIN_BALE_STEP,
|
||||
HEARTY_GRAINS_PLACE,
|
||||
HEARTY_GRAIN_BALE_HIT,
|
||||
HEARTY_GRAIN_BALE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_WATER_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAINS_BREAK_WATER,
|
||||
SoundEvents.GRASS_STEP,
|
||||
HEARTY_GRAINS_PLACE_WATER,
|
||||
SoundEvents.GRASS_HIT,
|
||||
SoundEvents.GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MINT_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
MINT_BREAK,
|
||||
@@ -437,7 +513,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val DISPLAY_CASE_PLACE = this.create("block.display_case.place")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_STEP = this.create("block.display_case.step")
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_SOUNDS = SoundType(1f, 1f,
|
||||
DISPLAY_CASE_BREAK,
|
||||
@@ -448,7 +523,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE_CAMPFIRE = this.create("block.campfire_pot.place_campfire")
|
||||
val CAMPFIRE_POT_SET = this.create("block.campfire_pot.set")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RETRIEVE = this.create("block.campfire_pot.retrieve")
|
||||
@JvmField
|
||||
@@ -456,12 +531,14 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CLOSE = this.create("block.campfire_pot.close")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_USE = this.create("block.campfire_pot.use")
|
||||
val CAMPFIRE_POT_TAKE_ITEM = this.create("block.campfire_pot.take_item")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_ACTIVE = this.create("block.campfire_pot.active")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_AMBIENT = this.create("block.campfire_pot.ambient")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_COOK = this.create("block.campfire_pot.cook")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CRAFT = this.create("block.campfire_pot.craft")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE = this.create("block.campfire_pot.place")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BREAK = this.create("block.campfire_pot.break")
|
||||
|
||||
@@ -15,9 +15,12 @@ import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* Registry for all known Abilities
|
||||
@@ -31,15 +34,31 @@ object Abilities : DataRegistry {
|
||||
val DUMMY = AbilityTemplate(name = "dummy")
|
||||
|
||||
private val abilityMap = mutableMapOf<String, AbilityTemplate>()
|
||||
internal val abilityScripts = mutableMapOf<String, String>() // abilityId to JavaScript
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
PotentialAbility.types.clear()
|
||||
PotentialAbility.types.add(CommonAbilityType)
|
||||
PotentialAbility.types.add(HiddenAbilityType)
|
||||
this.abilityMap.clear()
|
||||
val abilitiesJson = ShowdownService.service.getAbilityIds()
|
||||
this.abilityScripts.clear()
|
||||
|
||||
ShowdownService.service.resetRegistryData("ability")
|
||||
manager.listResources("abilities") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
val js = reader.readText()
|
||||
abilityScripts[resolvedIdentifier.path] = js
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(abilityScripts, "ability")
|
||||
|
||||
val abilitiesJson = ShowdownService.service.getRegistryData("ability")
|
||||
for (i in 0 until abilitiesJson.size()) {
|
||||
val id = abilitiesJson[i].asString
|
||||
val jsonAbility = abilitiesJson[i].asJsonObject
|
||||
val id = jsonAbility.get("id").asString
|
||||
val ability = AbilityTemplate(id)
|
||||
this.register(ability)
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ class Berry(
|
||||
val fruitModelIdentifier: ResourceLocation,
|
||||
val fruitTexture: ResourceLocation,
|
||||
val stageOnePositioning: GrowthPoint,
|
||||
val pokeSnackPositionings: Array<GrowthPoint>,
|
||||
val weight: Float,
|
||||
val boneMealChance: Float
|
||||
) {
|
||||
@@ -293,6 +294,14 @@ class Berry(
|
||||
buffer.writeDouble(stageOnePositioning.rotation.x)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.y)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.z)
|
||||
buffer.writeCollection(this.pokeSnackPositionings.toList()) { writer, value ->
|
||||
writer.writeDouble(value.position.x)
|
||||
writer.writeDouble(value.position.y)
|
||||
writer.writeDouble(value.position.z)
|
||||
writer.writeDouble(value.rotation.x)
|
||||
writer.writeDouble(value.rotation.y)
|
||||
writer.writeDouble(value.rotation.z)
|
||||
}
|
||||
buffer.writeFloat(boneMealChance)
|
||||
}
|
||||
|
||||
@@ -360,9 +369,12 @@ class Berry(
|
||||
val flowerTexture = buffer.readIdentifier()
|
||||
val fruitModelIdentifier = buffer.readIdentifier()
|
||||
val fruitTexture = buffer.readIdentifier()
|
||||
val stageOneYPos = GrowthPoint(Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
|
||||
val stageOnePos = GrowthPoint(Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
|
||||
val pokeSnackPos = buffer.readList { reader ->
|
||||
GrowthPoint(Vec3(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3(reader.readDouble(), reader.readDouble(), reader.readDouble()))
|
||||
}.toTypedArray()
|
||||
val boneMealChance = buffer.readFloat()
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, colour, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOneYPos, 0F, boneMealChance)
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, colour, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOnePos, pokeSnackPos, 0F, boneMealChance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
@@ -157,6 +158,17 @@ object MoLangFunctions {
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
Cobblemon.LOGGER.info(message)
|
||||
},
|
||||
"delete_variable" to java.util.function.Function { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map.remove(variable)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"delete_variables" to java.util.function.Function { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
struct.map.clear()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"set_query" to java.util.function.Function { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
|
||||
@@ -21,9 +21,12 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* Registry for all known Moves
|
||||
@@ -36,11 +39,26 @@ object Moves : DataRegistry {
|
||||
|
||||
private val allMoves = mutableMapOf<String, MoveTemplate>()
|
||||
private val idMapping = mutableMapOf<Int, MoveTemplate>()
|
||||
internal val moveScripts = mutableMapOf<String, String>() // moveId to JavaScript
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.allMoves.clear()
|
||||
this.idMapping.clear()
|
||||
val movesJson = ShowdownService.service.getMoves()
|
||||
this.moveScripts.clear()
|
||||
|
||||
ShowdownService.service.resetRegistryData("move")
|
||||
manager.listResources("moves") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
val js = reader.readText()
|
||||
moveScripts[resolvedIdentifier.path] = js
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(moveScripts, "move")
|
||||
|
||||
val movesJson = ShowdownService.service.getRegistryData("move")
|
||||
for (i in 0 until movesJson.size()) {
|
||||
val jsMove = movesJson[i].asJsonObject
|
||||
val id = jsMove.get("id").asString
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -76,6 +77,7 @@ import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
@@ -123,6 +125,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
@@ -157,7 +160,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
}
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.registerSpecies()
|
||||
it.resetRegistryData("species")
|
||||
it.sendRegistryData(allShowdownSpecies(), "species")
|
||||
it.indicateSpeciesInitialized()
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
@@ -312,4 +316,18 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
return "${species.resourceIdentifier.namespace}:${species.name}"
|
||||
}
|
||||
|
||||
internal fun allShowdownSpecies(): Map<String, String> {
|
||||
val result = mutableMapOf<String, String>()
|
||||
this.species.forEach {species ->
|
||||
val baseSpecies = ShowdownSpecies(species, null)
|
||||
result[baseSpecies.name] = this.gson.toJson(baseSpecies)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
val formSpecies = ShowdownSpecies(species, form)
|
||||
result[formSpecies.name] = this.gson.toJson(formSpecies)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -8,8 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LurePokeSnackDetector
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import net.minecraft.core.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleOptions
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -18,7 +21,12 @@ import net.minecraft.resources.ResourceLocation
|
||||
* Used by [PokemonClientDelegate.spawnAspectParticle] to spawn associated particles for aspects
|
||||
*/
|
||||
val aspectParticleMap: Map<String, ParticleData> = mapOf(
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1)
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1),
|
||||
LurePokeSnackDetector.POKE_SNACK_CRUMBED_ASPECT to ParticleData.MinecraftParticle(
|
||||
BlockParticleOption(
|
||||
ParticleTypes.BLOCK,
|
||||
CobblemonBlocks.POKE_SNACK.defaultBlockState()
|
||||
), 0.05, 3),
|
||||
)
|
||||
|
||||
sealed class ParticleData {
|
||||
|
||||
@@ -18,11 +18,4 @@ import net.minecraft.world.entity.PlayerRideableJumping
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
interface Rideable : PlayerRideableJumping {
|
||||
|
||||
/**
|
||||
* Denotes the manager responsible for handling any instance of riding taking place on an entity
|
||||
*/
|
||||
val riding: RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>?
|
||||
|
||||
}
|
||||
interface Rideable : PlayerRideableJumping
|
||||
@@ -18,58 +18,41 @@ import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RidingProperties(
|
||||
val stats: MutableMap<RidingStat, RidingStatDefinition> = mutableMapOf(),
|
||||
val seats: List<Seat> = listOf(),
|
||||
val conditions: List<Expression> = listOf(),
|
||||
val behaviour: RidingBehaviourSettings? = null
|
||||
val behaviours: Map<RidingStyle, RidingBehaviourSettings>? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
|
||||
val stats: MutableMap<RidingStat, RidingStatDefinition> = buffer.readMap(
|
||||
{ buffer.readEnum(RidingStat::class.java) },
|
||||
{ RidingStatDefinition.decode(buffer) }
|
||||
).toMutableMap()
|
||||
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
|
||||
val conditions = buffer.readList { buffer.readString().asExpression() }
|
||||
val behaviour = buffer.readNullable { _ ->
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
return@readNullable settings
|
||||
val behaviours = buffer.readNullable { _ ->
|
||||
buffer.readMap(
|
||||
{ buffer.readEnumConstant(RidingStyle::class.java) },
|
||||
{
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
|
||||
?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
settings
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return RidingProperties(stats = stats, seats = seats, conditions = conditions, behaviour = behaviour)
|
||||
return RidingProperties(seats = seats, conditions = conditions, behaviours = behaviours)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun updateStatRange(stat: RidingStat, style: RidingStyle, min: Int, max: Int) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
if (stats[stat] == null) {
|
||||
stats[stat] = RidingStatDefinition()
|
||||
}
|
||||
stats[stat]!!.ranges[style] = min..max
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeMap(
|
||||
stats,
|
||||
{ _, stat -> buffer.writeEnum(stat) },
|
||||
{ _, stat -> stat.encode(buffer) }
|
||||
)
|
||||
buffer.writeCollection(seats) { _, seat -> seat.encode(buffer) }
|
||||
buffer.writeCollection(conditions) { _, condition -> buffer.writeString(condition.getString()) }
|
||||
buffer.writeNullable(behaviour) { _, behaviour ->
|
||||
behaviour.encode(buffer)
|
||||
buffer.writeNullable(behaviours) { _, v ->
|
||||
buffer.writeMap(
|
||||
v,
|
||||
{ _, style -> buffer.writeEnumConstant(style) },
|
||||
{ _, behaviour -> behaviour.encode(buffer) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculate(stat: RidingStat, style: RidingStyle, boosts: Int): Float {
|
||||
val definitions = stats[stat] ?: return 0F
|
||||
return definitions.calculate(style, boosts)
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat, style: RidingStyle): Boolean {
|
||||
return stats[stat]?.ranges?.containsKey(style) == true
|
||||
}
|
||||
}
|
||||
+15
@@ -101,4 +101,19 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
* Calculate the damage a horizontal collision will do to a ridden Pokémon. This is only relevant to fast flying behaviours, fall damage is separate!
|
||||
*/
|
||||
fun damageOnCollision(settings: Settings, state: State, vehicle: PokemonEntity, impactVec: Vec3): Boolean = false
|
||||
|
||||
|
||||
/**
|
||||
* Internal helpers to help behaviour specific calculations
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* Scales a given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
internal fun scaleToRange(x: Double, min: Double, max: Double): Double {
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
@@ -23,4 +25,13 @@ import net.minecraft.world.phys.Vec3
|
||||
*/
|
||||
interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
val key: ResourceLocation
|
||||
val stats: MutableMap<RidingStat, IntRange>
|
||||
|
||||
fun calculate(stat: RidingStat, boosts: Int): Float {
|
||||
val range = stats[stat] ?: return 0F
|
||||
val boostQuotient = boosts / 255F
|
||||
return range.first + (range.last - range.first) * boostQuotient
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ object RidingBehaviours {
|
||||
|
||||
fun register(key: ResourceLocation, behaviour: RidingBehaviour<out RidingBehaviourSettings, out RidingBehaviourState>) {
|
||||
if (behaviours.contains(key)) error("Behaviour already registered to key $key")
|
||||
behaviours[key] = RidingController(behaviour) as RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>
|
||||
behaviours[key] = behaviour as RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>
|
||||
}
|
||||
|
||||
fun get(key: ResourceLocation): RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState> {
|
||||
|
||||
+89
-176
@@ -8,206 +8,119 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.DoubleJump
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.collections.get
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
|
||||
class RidingController(
|
||||
val entity: PokemonEntity,
|
||||
val behaviours: Map<RidingStyle, RidingBehaviourSettings>) {
|
||||
|
||||
override val key = behaviour.key
|
||||
var lastTransitionAge: Int = 0
|
||||
private set
|
||||
|
||||
override fun getRidingStyle(settings: Settings, state: State): RidingStyle {
|
||||
return behaviour.getRidingStyle(settings, state)
|
||||
var context: ActiveRidingContext? = null
|
||||
private set
|
||||
|
||||
fun changeBehaviour(behaviour: ResourceLocation) {
|
||||
if (entity.form.riding.behaviours == null) return
|
||||
val style = entity.form.riding.behaviours!!.filter { it.value.key == behaviour }.keys.firstOrNull() ?: return
|
||||
val behaviourSettings = entity.pokemon.riding.behaviours?.get(style) ?: return
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, style)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
}
|
||||
|
||||
override fun isActive(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.isActive(settings, state, vehicle)
|
||||
fun tick() {
|
||||
if (entity.ticksLived - lastTransitionAge < 10) return
|
||||
val newTransition = checkForNewTransition(entity.controllingPassenger)
|
||||
val behaviourSettings = entity.pokemon.riding.behaviours?.get(newTransition)
|
||||
if (newTransition != context?.style) {
|
||||
if (newTransition != null && behaviourSettings != null) {
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
} else {
|
||||
context = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3) =
|
||||
behaviour.tick(settings, state, vehicle, driver, input)
|
||||
|
||||
override fun pose(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.pose(settings, state, vehicle)
|
||||
|
||||
override fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.speed(settings, state, vehicle, driver)
|
||||
if (context == null && !this.entity.level().isClientSide) {
|
||||
entity.ejectPassengers()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.updatePassengerRotation(settings, state, vehicle, driver)
|
||||
fun getBehaviour(): RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>? {
|
||||
if (context == null) return null
|
||||
return RidingBehaviours.get(context!!.behaviour)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.clampPassengerRotation(settings, state, vehicle, driver)
|
||||
private fun checkForNewTransition(driver: LivingEntity?): RidingStyle? {
|
||||
when (context?.style) {
|
||||
RidingStyle.AIR -> {
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
RidingStyle.LIQUID -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
RidingStyle.LAND -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
null -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
if (!isActive(settings, state, vehicle)) return driver.rotationVector
|
||||
return behaviour.rotation(settings, state, vehicle, driver)
|
||||
private fun canTransitionToLand(): Boolean {
|
||||
if (entity.isInLiquid || entity.isUnderWater) return false
|
||||
return entity.onGround()
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.velocity(settings, state, vehicle, driver, input)
|
||||
private fun canTransitionToLiquid(): Boolean {
|
||||
return entity.isInLiquid || entity.isUnderWater
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.angRollVel(settings, state, vehicle, driver, deltaTime)
|
||||
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
|
||||
if (driver != null) {
|
||||
if (driver !is DoubleJump) return false
|
||||
// check if style is water and player is submerged
|
||||
val isEyeInLiquid = entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
|
||||
if (context?.style == RidingStyle.LIQUID && isEyeInLiquid) return false
|
||||
return (driver as DoubleJump).isDoubleJumping
|
||||
}
|
||||
else {
|
||||
return !entity.onGround() && !(entity.isInLiquid || entity.isUnderWater)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.rotationOnMouseXY(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
|
||||
override fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.canJump(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotateRiderHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotatePokemonHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.dismountOnShift(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.turnOffOnGround(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRoll(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun inertia(settings: Settings, state: State, vehicle: PokemonEntity): Double {
|
||||
if (!isActive(settings, state, vehicle)) return 0.5
|
||||
return behaviour.inertia(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): ResourceLocation {
|
||||
if (!isActive(settings, state, vehicle)) return cobblemonResource("no_pose")
|
||||
return behaviour.useRidingAltPose(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return true
|
||||
return behaviour.useAngVelSmoothing(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 1.0F
|
||||
return behaviour.rideFovMultiplier(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun gravity(settings: Settings, state: State, vehicle: PokemonEntity, regularGravity: Double): Double {
|
||||
if (!isActive(settings, state, vehicle)) return regularGravity
|
||||
return behaviour.gravity(settings, state, vehicle, regularGravity)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.jumpForce(settings, state, vehicle, driver, jumpStrength)
|
||||
}
|
||||
|
||||
override fun setRideBar(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.setRideBar(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? {
|
||||
if (!isActive(settings, state, vehicle)) return null
|
||||
return behaviour.maxUpStep(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
if (!isActive(settings, state, vehicle)) return RideSoundSettingsList()
|
||||
return behaviour.getRideSounds(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
return behaviour.damageOnCollision(settings, state, vehicle, impactVec)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class ActiveRidingContext(
|
||||
val behaviour: ResourceLocation,
|
||||
val settings: RidingBehaviourSettings,
|
||||
val state: RidingBehaviourState,
|
||||
val style: RidingStyle
|
||||
)
|
||||
+8
-14
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -242,7 +243,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
@@ -298,7 +299,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
val rotMin = 15.0
|
||||
var rollForce = xInput
|
||||
val rotLimit = max(90 * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val rotLimit = max(90 * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
@@ -314,7 +315,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
val yawForce = xInput * ( 1.0 - sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
|
||||
//Yaw locally a bit when up or down so that its more intuitive to make it out of a dive or a straight vertical
|
||||
//climb
|
||||
@@ -342,16 +343,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return Vec3(0.0, 0.0, rollForce)
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -399,7 +390,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
@@ -470,6 +461,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
class BirdSettings : RidingBehaviourSettings {
|
||||
override val key = BirdBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
@@ -491,6 +483,7 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
@@ -503,6 +496,7 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
|
||||
+7
-13
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -228,7 +229,7 @@ class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState>
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
@@ -282,7 +283,7 @@ class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState>
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
val yawForce = xInput * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
|
||||
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
|
||||
|
||||
@@ -302,16 +303,6 @@ class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState>
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
@@ -359,7 +350,7 @@ class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState>
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
@@ -430,6 +421,7 @@ class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState>
|
||||
|
||||
class BirdNoRollSettings : RidingBehaviourSettings {
|
||||
override val key = BirdNoRollBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
@@ -451,6 +443,7 @@ class BirdNoRollSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
@@ -463,6 +456,7 @@ class BirdNoRollSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
|
||||
+4
-2
@@ -75,8 +75,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
|
||||
val zVector = driver.zza.toDouble()
|
||||
val statSpeed = vehicle.rideProp.calculate(RidingStat.SPEED, RidingStyle.AIR, 0 )
|
||||
|
||||
val speedStat = settings.calculate(RidingStat.SPEED, 0)
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
@@ -197,6 +196,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
|
||||
class GliderSettings : RidingBehaviourSettings {
|
||||
override val key = GliderBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
private set
|
||||
@@ -211,6 +211,7 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
@@ -218,6 +219,7 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
|
||||
+4
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
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
|
||||
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.*
|
||||
@@ -287,6 +288,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
|
||||
class HelicopterSettings : RidingBehaviourSettings {
|
||||
override val key = HelicopterBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "1.0".asExpression()
|
||||
private set
|
||||
@@ -304,6 +306,7 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
@@ -312,6 +315,7 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
|
||||
+6
-11
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -103,7 +104,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
val normSpeed = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
@@ -163,7 +164,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
val turnRate = (handling.toFloat() / 20.0f)
|
||||
|
||||
@@ -307,15 +308,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
@@ -472,6 +464,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
|
||||
class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
|
||||
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
|
||||
@@ -503,6 +496,7 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
@@ -512,6 +506,7 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
|
||||
+8
-14
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -79,7 +80,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
// //Calculate stamina loss due to speed
|
||||
// //At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
// //There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
// var staminaRate = (normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
// var staminaRate = (RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
//
|
||||
// //interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
|
||||
// staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
|
||||
@@ -94,16 +95,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
//TODO: Move these functions to a riding util class.
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
@@ -159,7 +150,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
@@ -177,7 +168,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
)
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
@@ -323,7 +314,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
@@ -398,6 +389,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
|
||||
class JetSettings : RidingBehaviourSettings {
|
||||
override val key = JetBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
@@ -428,6 +420,7 @@ class JetSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
@@ -441,6 +434,7 @@ class JetSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
|
||||
+7
-13
@@ -23,6 +23,7 @@ import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -79,16 +80,6 @@ class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
//TODO: Move these functions to a riding util class.
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
@@ -145,7 +136,7 @@ class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
@@ -163,7 +154,7 @@ class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
)
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
@@ -307,7 +298,7 @@ class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
@@ -382,6 +373,7 @@ class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
|
||||
class JetNoRollSettings : RidingBehaviourSettings {
|
||||
override val key = JetNoRollBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
@@ -412,6 +404,7 @@ class JetNoRollSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
@@ -425,6 +418,7 @@ class JetNoRollSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
|
||||
+5
-10
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -284,15 +285,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
@@ -373,7 +365,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
|
||||
|
||||
//TODO: Remove this magic number and get a better comparison for boost top speed
|
||||
val normalizedBoostSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
return 1.0f
|
||||
@@ -458,6 +450,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
|
||||
class RocketSettings : RidingBehaviourSettings {
|
||||
override val key = RocketBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".asExpression()
|
||||
private set
|
||||
@@ -485,6 +478,7 @@ class RocketSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
@@ -494,6 +488,7 @@ class RocketSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
|
||||
+6
@@ -12,10 +12,13 @@ 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.composite.strategies.CompositeRidingStrategies
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
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.adapters.RidingBehaviourSettingsAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -380,8 +383,10 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
private set
|
||||
|
||||
override val key: ResourceLocation = CompositeBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeResourceLocation(transitionStrategy)
|
||||
defaultBehaviour.encode(buffer)
|
||||
@@ -389,6 +394,7 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
transitionStrategy = buffer.readResourceLocation()
|
||||
val defaultBehaviourKey = buffer.readResourceLocation()
|
||||
defaultBehaviour = RidingBehaviourSettingsAdapter.types[defaultBehaviourKey]?.getConstructor()?.newInstance()
|
||||
|
||||
+12
-25
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -173,7 +174,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val normSpeed = 1.0f
|
||||
|
||||
// TurnRate should always be quick if not sprinting
|
||||
val invRelSpeed = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 1.0f) * -1.0f
|
||||
val invRelSpeed = (RidingBehaviour.scaleToRange(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 1.0f) * -1.0f
|
||||
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * invRelSpeed, 1.0)).toFloat()
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
@@ -201,19 +202,17 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 1.3
|
||||
val topSpeed = if(state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 2
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.length())
|
||||
//var newVelocity = vehicle.deltaMovement.normalize()
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
@@ -236,9 +235,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
|
||||
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
|
||||
//to do some testing and get other's opinions
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.8
|
||||
val terminalVel = 2.0
|
||||
|
||||
@@ -252,18 +248,12 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
val jumpForce = 1.0
|
||||
val velMag = newVelocity.length()
|
||||
|
||||
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
|
||||
|
||||
val test = 0.0
|
||||
//Ensure this doesn't add unwanted forward velocity
|
||||
//val mag = if(newVelocity.length() < rideTopSpeed) newVelocity.length() else rideTopSpeed
|
||||
//newVelocity = newVelocity.normalize().scale(mag)
|
||||
}
|
||||
|
||||
//Zero out lateral velocity possibly picked up from a controller transition
|
||||
@@ -272,15 +262,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* lerps the current val between minVal and maxVal.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
|
||||
*/
|
||||
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
|
||||
require(maxVal > minVal) { "minVal must be greater than maxVal" }
|
||||
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
|
||||
val walkspeed = vehicle.runtime.resolveDouble(vehicle.behaviour.moving.walk.walkSpeed)
|
||||
val movementSpeed = vehicle.attributes.getValue(Attributes.MOVEMENT_SPEED)
|
||||
@@ -449,10 +430,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
class HorseSettings : RidingBehaviourSettings {
|
||||
override val key = HorseBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var canSprint = "true".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
@@ -476,6 +461,7 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
@@ -485,6 +471,7 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
|
||||
+5
-10
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -146,7 +147,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
val driftInput = turnInput * 0.25f
|
||||
|
||||
val lowSpeedTurnBoost = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 2.0f) * -1.0f
|
||||
val lowSpeedTurnBoost = (RidingBehaviour.scaleToRange(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 2.0f) * -1.0f
|
||||
val forcedDriftTurnMomentum = (80.0f / 20.0f) * lowSpeedTurnBoost
|
||||
val maxTurnMomentum = (60.0f / 20.0f) * lowSpeedTurnBoost
|
||||
val maxDriftMomentum = forcedDriftTurnMomentum * 0.6f
|
||||
@@ -292,15 +293,6 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* lerps the current val between minVal and maxVal.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
|
||||
*/
|
||||
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
|
||||
require(maxVal > minVal) { "minVal must be greater than maxVal" }
|
||||
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
@@ -455,6 +447,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
|
||||
class MinekartSettings : RidingBehaviourSettings {
|
||||
override val key = MinekartBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -484,12 +477,14 @@ class MinekartSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(boostLimit)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
boostLimit = buffer.readExpression()
|
||||
|
||||
+4
-3
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -220,9 +221,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
|
||||
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
|
||||
//to do some testing and get other's opinions
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
|
||||
val terminalVel = 2.0
|
||||
|
||||
@@ -414,6 +412,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
class VehicleSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -456,6 +455,7 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(speed)
|
||||
@@ -466,6 +466,7 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
|
||||
+3
@@ -354,6 +354,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
class BoatSettings : RidingBehaviourSettings {
|
||||
override val key = BoatBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
@@ -364,11 +365,13 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
}
|
||||
|
||||
+6
@@ -15,10 +15,13 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -236,6 +239,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
|
||||
class BurstSettings : RidingBehaviourSettings {
|
||||
override val key = BurstBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var dashSpeed = 1F
|
||||
private set
|
||||
@@ -244,11 +248,13 @@ class BurstSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
dashSpeed = buffer.readFloat()
|
||||
}
|
||||
|
||||
+5
-11
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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
|
||||
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.*
|
||||
@@ -194,7 +195,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Yaw
|
||||
// TODO:move all magic numbers to handling expression
|
||||
val yawSpeed = handling * deltaTime * 0.25
|
||||
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
|
||||
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
|
||||
controller.applyGlobalYaw(yawForce.toFloat())
|
||||
|
||||
@@ -210,16 +211,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
@@ -328,6 +319,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
|
||||
class DolphinSettings : RidingBehaviourSettings {
|
||||
override val key = DolphinBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -357,6 +349,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
@@ -369,6 +362,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
|
||||
+2
-2
@@ -63,9 +63,9 @@ class RideSoundManager(
|
||||
}
|
||||
|
||||
fun getActiveRideKey(): ResourceLocation {
|
||||
val key = ride.ridingBehaviourSettings?.key ?: cobblemonResource("no_key")
|
||||
val key = ride.ridingController?.context?.settings?.key ?: cobblemonResource("no_key")
|
||||
if (key == CompositeBehaviour.KEY) {
|
||||
return (ride.ridingState as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
|
||||
return (ride.ridingController?.context?.state as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
|
||||
} else {
|
||||
return key
|
||||
}
|
||||
|
||||
+6
-4
@@ -44,10 +44,12 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawnablePosition>() {
|
||||
|
||||
if (minLureLevel != null) { // check for the lureLevel of the rod
|
||||
val pokerodStack = spawnablePosition.rodStack
|
||||
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(
|
||||
spawnablePosition.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
|
||||
pokerodStack
|
||||
)
|
||||
val lureLevel = spawnablePosition.enchantmentRegistry.getHolder(Enchantments.LURE).map {
|
||||
EnchantmentHelper.getItemEnchantmentLevel(
|
||||
it,
|
||||
pokerodStack
|
||||
)
|
||||
}.orElse(0)
|
||||
if (lureLevel < minLureLevel!!) {
|
||||
return false
|
||||
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
|
||||
|
||||
+4
-4
@@ -29,13 +29,13 @@ import net.minecraft.world.entity.Entity
|
||||
* @author Hiroku, Plastered_Crab
|
||||
* @since March 18th, 2025
|
||||
*/
|
||||
class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time: Int) -> Unit = {}) : SpawningInfluence {
|
||||
open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time: Int, entity: PokemonEntity?) -> Unit = { _, _ -> }) : SpawningInfluence {
|
||||
|
||||
var usedTimes = 0
|
||||
|
||||
private fun markUsed() {
|
||||
private fun markUsed(entity: PokemonEntity? = null) {
|
||||
usedTimes++
|
||||
onUsed(usedTimes)
|
||||
onUsed(usedTimes, entity)
|
||||
}
|
||||
|
||||
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
|
||||
@@ -43,7 +43,7 @@ class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time:
|
||||
val merged = SpawnBaitUtils.mergeEffects(effects)
|
||||
merged.forEach { effect ->
|
||||
if (Math.random() <= effect.chance) {
|
||||
markUsed()
|
||||
markUsed(entity)
|
||||
Effects.getEffectFunction(effect.type)?.invoke(entity, effect)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.IncenseSweetDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LureCakeDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LurePokeSnackDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SaccharineLogSlatheredDetector
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ interface SpawningInfluenceDetector {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val detectors = mutableSetOf<SpawningInfluenceDetector>(
|
||||
LureCakeDetector,
|
||||
LurePokeSnackDetector,
|
||||
SaccharineLogSlatheredDetector,
|
||||
IncenseSweetDetector
|
||||
)
|
||||
|
||||
+13
-12
@@ -37,18 +37,19 @@ object IncenseSweetDetector : SpawningInfluenceDetector {
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val centerPos = input.getCenter().toBlockPos()
|
||||
|
||||
val sweetIncensePositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.INCENSE_SWEET_KEY) },
|
||||
{ true },
|
||||
centerPos,
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
for (pos in sweetIncensePositions) {
|
||||
val influence = SpatialSpawningZoneInfluence(pos, radius = RANGE.toFloat(), IncenseSweetInfluence(pos))
|
||||
listOfInfluences.add(influence)
|
||||
}
|
||||
// TODO after 1.7
|
||||
// val sweetIncensePositions = world.poiManager.findAll(
|
||||
// { holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.INCENSE_SWEET_KEY) },
|
||||
// { true },
|
||||
// centerPos,
|
||||
// searchRange,
|
||||
// PoiManager.Occupancy.ANY
|
||||
// ).toList()
|
||||
//
|
||||
// for (pos in sweetIncensePositions) {
|
||||
// val influence = SpatialSpawningZoneInfluence(pos, radius = RANGE.toFloat(), IncenseSweetInfluence(pos))
|
||||
// listOfInfluences.add(influence)
|
||||
// }
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
-104
@@ -1,104 +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.api.spawning.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpatialSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.detector.SpawningInfluenceDetector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.block.LureCakeBlock
|
||||
import com.cobblemon.mod.common.block.entity.LureCakeBlockEntity
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
object LureCakeDetector : SpawningInfluenceDetector {
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): MutableList<SpawningZoneInfluence> {
|
||||
val world = input.world
|
||||
val listOfInfluences = mutableListOf<SpawningZoneInfluence>()
|
||||
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val lureCakePositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.LURE_CAKE_KEY) },
|
||||
{ true },
|
||||
input.getCenter().toBlockPos(),
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
var highestLureTier = 0
|
||||
|
||||
for (lureCakePos in lureCakePositions) {
|
||||
val blockState = world.getBlockState(lureCakePos)
|
||||
if (blockState.block !is LureCakeBlock) continue
|
||||
|
||||
val blockEntity = world.getBlockEntity(lureCakePos) as? LureCakeBlockEntity ?: continue
|
||||
val baitEffects = blockEntity.getBaitEffectsFromLureCake()
|
||||
|
||||
highestLureTier = maxOf(highestLureTier, baitEffects.filter { it.type == SpawnBait.Effects.RARITY_BUCKET }.maxOfOrNull { it.value }?.toInt() ?: 0)
|
||||
|
||||
listOfInfluences.add(
|
||||
SpatialSpawningZoneInfluence(
|
||||
pos = lureCakePos,
|
||||
radius = RANGE.toFloat(),
|
||||
influence = SpawnBaitInfluence(
|
||||
effects = baitEffects,
|
||||
onUsed = { time ->
|
||||
if (time == 1) {
|
||||
tryBiteCake(world, lureCakePos)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (highestLureTier > 0) {
|
||||
listOfInfluences.add(UnconditionalSpawningZoneInfluence(influence = BucketNormalizingInfluence(tier = highestLureTier)))
|
||||
}
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
private fun tryBiteCake(world: ServerLevel, lureCakePos: BlockPos) {
|
||||
if (world.isLoaded(lureCakePos)) {
|
||||
val blockEntity = world.getBlockEntity(lureCakePos) as? LureCakeBlockEntity ?: return
|
||||
blockEntity.bites++
|
||||
|
||||
if (blockEntity.bites >= blockEntity.maxBites) {
|
||||
world.removeBlock(lureCakePos, false)
|
||||
} else {
|
||||
blockEntity.setChanged()
|
||||
world.sendBlockUpdated(lureCakePos, blockEntity.blockState, blockEntity.blockState, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun detectFromBlock(
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
blockState: BlockState
|
||||
) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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.spawning.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpatialSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.detector.SpawningInfluenceDetector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.abs
|
||||
|
||||
object LurePokeSnackDetector : SpawningInfluenceDetector {
|
||||
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
@JvmField
|
||||
val POKE_SNACK_CRUMBED_ASPECT = "poke_snack_crumbed"
|
||||
|
||||
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): MutableList<SpawningZoneInfluence> {
|
||||
val world = input.world
|
||||
val listOfInfluences = mutableListOf<SpawningZoneInfluence>()
|
||||
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val lurePokeSnackPositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.POKE_SNACK_KEY) },
|
||||
{ true },
|
||||
input.getCenter().toBlockPos(),
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
var highestLureTier = 0
|
||||
|
||||
for (lurePokeSnackPos in lurePokeSnackPositions) {
|
||||
val blockState = world.getBlockState(lurePokeSnackPos)
|
||||
if (blockState.block !is PokeSnackBlock) continue
|
||||
|
||||
val blockEntity = world.getBlockEntity(lurePokeSnackPos) as? PokeSnackBlockEntity ?: continue
|
||||
val baitEffects = blockEntity.getBaitEffects()
|
||||
|
||||
highestLureTier = maxOf(highestLureTier, baitEffects.filter { it.type == SpawnBait.Effects.RARITY_BUCKET }.maxOfOrNull { it.value }?.toInt() ?: 0)
|
||||
|
||||
listOfInfluences.add(
|
||||
SpatialSpawningZoneInfluence(
|
||||
pos = lurePokeSnackPos,
|
||||
radius = RANGE.toFloat(),
|
||||
influence = SpawnBaitInfluence(
|
||||
effects = baitEffects,
|
||||
onUsed = { usedAmount, entity ->
|
||||
entity?.let {
|
||||
// Prevent triggering twice on same entity
|
||||
if (usedAmount % 2 == 0) {
|
||||
tryBite(world, lurePokeSnackPos)
|
||||
moveAndApplyAspect(world, lurePokeSnackPos, entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (highestLureTier > 0) {
|
||||
listOfInfluences.add(UnconditionalSpawningZoneInfluence(influence = BucketNormalizingInfluence(tier = highestLureTier)))
|
||||
}
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
private fun tryBite(level: ServerLevel, blockPos: BlockPos) {
|
||||
if (level.isLoaded(blockPos)) {
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
val block = blockState.block as? PokeSnackBlock ?: return
|
||||
|
||||
block.eat(level, blockPos, blockState, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveAndApplyAspect(level: ServerLevel, blockPos: BlockPos, entity: PokemonEntity?) {
|
||||
entity?.let {
|
||||
entity.pokemon.forcedAspects += POKE_SNACK_CRUMBED_ASPECT
|
||||
attemptSafeMove(level, blockPos, entity)
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove2(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity, ) {
|
||||
val inWater = entity.isInWater
|
||||
|
||||
// Random offset in a cardinal direction (north, south, east, west)
|
||||
val randomDirection = listOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).random()
|
||||
val initialOffsetPos = startPos.relative(randomDirection)
|
||||
|
||||
// Variables to track the best (closest) safe position
|
||||
var safePos: BlockPos? = null
|
||||
var bestDistanceSq = Double.MAX_VALUE
|
||||
|
||||
val maxRadius = 10
|
||||
|
||||
// Search within a sphere around the initial offset position
|
||||
for (dx in -maxRadius..maxRadius) {
|
||||
for (dy in -maxRadius..maxRadius) {
|
||||
for (dz in -maxRadius..maxRadius) {
|
||||
val candidatePos = initialOffsetPos.offset(dx, dy, dz)
|
||||
val distanceSq = startPos.distSqr(candidatePos)
|
||||
|
||||
// Check if within sphere radius
|
||||
if (dx*dx + dy*dy + dz*dz <= maxRadius*maxRadius) {
|
||||
// Check collision at candidate position
|
||||
val currentBox = entity.boundingBox.move(
|
||||
candidatePos.x - entity.x,
|
||||
candidatePos.y - entity.y,
|
||||
candidatePos.z - entity.z
|
||||
)
|
||||
|
||||
if (level.noCollision(entity, currentBox)) {
|
||||
val blockBelowPos = candidatePos.below()
|
||||
val blockState = level.getBlockState(blockBelowPos)
|
||||
|
||||
val isWater = blockState.block == Blocks.WATER
|
||||
if (inWater) {
|
||||
if (isWater) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!blockState.isAir) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (safePos != null) {
|
||||
entity.moveTo(safePos.toVec3d())
|
||||
PokeSnackBlockParticlesPacket(startPos, safePos).sendToPlayersAround(
|
||||
startPos.x.toDouble(),
|
||||
startPos.y.toDouble(),
|
||||
startPos.z.toDouble(),
|
||||
64.0,
|
||||
level.dimension()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity) {
|
||||
val inWater = entity.isInWater
|
||||
val maxDistance = 10 // maximum search radius
|
||||
val maxPositions = 10 // maximum number of positions at the closest distance
|
||||
|
||||
var foundPositions: List<BlockPos> = emptyList()
|
||||
|
||||
// Search layer by layer, starting from the closest
|
||||
for (distance in 1..maxDistance) {
|
||||
val positionsAtThisDistance = mutableListOf<BlockPos>()
|
||||
|
||||
for (dx in -distance..distance) {
|
||||
for (dy in -distance..distance) {
|
||||
for (dz in -distance..distance) {
|
||||
// Consider only positions on the surface of the cube at current distance
|
||||
if (listOf(dx, dy, dz).count { abs(it) != 0 } == distance) {
|
||||
val candidatePos = startPos.offset(dx, dy, dz)
|
||||
if (!candidatePos.equals(startPos.relative(Direction.UP))) {
|
||||
// Check collision
|
||||
val currentBox = entity.boundingBox.move(
|
||||
candidatePos.x - entity.x,
|
||||
candidatePos.y - entity.y,
|
||||
candidatePos.z - entity.z
|
||||
)
|
||||
|
||||
if (level.noCollision(entity, currentBox)) {
|
||||
val blockBelowPos = candidatePos.below()
|
||||
val blockState = level.getBlockState(blockBelowPos)
|
||||
|
||||
val isWater = blockState.block == Blocks.WATER
|
||||
if (inWater) {
|
||||
if (isWater) {
|
||||
positionsAtThisDistance.add(candidatePos)
|
||||
}
|
||||
} else {
|
||||
if (!blockState.isAir) {
|
||||
positionsAtThisDistance.add(candidatePos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (positionsAtThisDistance.isNotEmpty()) {
|
||||
// Limit to 10 nearest positions (all at this distance)
|
||||
foundPositions = if (positionsAtThisDistance.size > maxPositions) {
|
||||
positionsAtThisDistance.shuffled().take(maxPositions)
|
||||
} else {
|
||||
positionsAtThisDistance
|
||||
}
|
||||
break // Found the nearest layer with safe positions, stop expanding
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPositions.isNotEmpty()) {
|
||||
val chosenPos = foundPositions.random()
|
||||
val centerX = chosenPos.x + 0.5
|
||||
val centerZ = chosenPos.z + 0.5
|
||||
entity.moveTo(Vec3(centerX, chosenPos.y.toDouble(), centerZ))
|
||||
|
||||
PokeSnackBlockParticlesPacket(startPos, chosenPos).sendToPlayersAround(
|
||||
startPos.x.toDouble(),
|
||||
startPos.y.toDouble(),
|
||||
startPos.z.toDouble(),
|
||||
64.0,
|
||||
level.dimension()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun detectFromBlock(level: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.Collections
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
@@ -259,9 +258,15 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
BattlePokemon.playerOwned(it)
|
||||
}.also { if (healPokemon) it.effectedPokemon.heal() }
|
||||
}.toMutableList()
|
||||
if (leadingPokemon != null) {
|
||||
Collections.rotate(result, result.size - this.indexOfFirst { it.uuid == leadingPokemon })
|
||||
|
||||
// reposition lead to front of the party
|
||||
if (leadingPokemon != null && result.first().uuid != leadingPokemon) {
|
||||
result.find { it.uuid == leadingPokemon }?.let { lead ->
|
||||
result.remove(lead)
|
||||
result.add(0, lead)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
fun clearParty() {
|
||||
|
||||
@@ -38,6 +38,8 @@ class Search(
|
||||
filter = filter.substring(1)
|
||||
}
|
||||
|
||||
filter = filter.lowercase(Locale.ROOT)
|
||||
|
||||
val pokemonFilter: PokemonFilter = when (filter) {
|
||||
"holding", "helditem", "held_item" -> PokemonFilter { pokemon -> !pokemon.heldItem().isEmpty }
|
||||
"fainted" -> PokemonFilter { pokemon -> pokemon.isFainted() }
|
||||
@@ -45,14 +47,16 @@ class Search(
|
||||
"mythical" -> PokemonFilter { pokemon -> pokemon.isMythical() }
|
||||
"ultrabeast", "ultra_beast" -> PokemonFilter { pokemon -> pokemon.isUltraBeast() }
|
||||
else -> {
|
||||
val nameFilter = filter.lowercase(Locale.ROOT)
|
||||
PokemonFilter { pokemon ->
|
||||
// search by partial name
|
||||
if (pokemon.species.resourceIdentifier.path.contains(nameFilter)) {
|
||||
return@PokemonFilter true
|
||||
if (filter.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
val species = pokemon.species.translatedName.string.lowercase(Locale.ROOT)
|
||||
val name = pokemon.getDisplayName().string.lowercase(Locale.ROOT)
|
||||
val props = PokemonProperties.parse(filter)
|
||||
|
||||
species.contains(filter) || name.contains(filter) || (props.matches(pokemon) && props.asString().isNotEmpty())
|
||||
}
|
||||
// search by exactly match in different props
|
||||
return@PokemonFilter PokemonProperties.parse(filter).matches(pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
@@ -40,17 +39,13 @@ object BagItems : DataRegistry {
|
||||
val bagItems = PrioritizedList<BagItemLike>()
|
||||
internal val bagItemsScripts = mutableMapOf<String, String>() // itemId to JavaScript
|
||||
|
||||
init {
|
||||
observable.subscribe {
|
||||
Cobblemon.showdownThread.queue(ShowdownService::registerBagItems)
|
||||
}
|
||||
}
|
||||
|
||||
fun getConvertibleForStack(stack: ItemStack): BagItemLike? {
|
||||
return bagItems.firstOrNull { it.getBagItem(stack) != null }
|
||||
}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.bagItemsScripts.clear()
|
||||
ShowdownService.service.resetRegistryData("bagItem")
|
||||
manager.listResources("bag_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
@@ -60,6 +55,8 @@ object BagItems : DataRegistry {
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(bagItemsScripts, "bagItem")
|
||||
|
||||
this.observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,8 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
|
||||
// No PP or disabled or something
|
||||
return false
|
||||
}
|
||||
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
val availableTargets = (if (gimmickID != null && validGimmickMove) gimmickMove.target else move.target)
|
||||
.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
|
||||
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
|
||||
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
|
||||
@@ -16,6 +16,9 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
* @author Segfault Guy
|
||||
* @since April 10th, 2023
|
||||
*/
|
||||
@Deprecated(message =
|
||||
"This was designed before Doubles and Multi battle formats were introduce and has not been maintained since." +
|
||||
"Accurate context for formats other than Singles is not guaranteed. This will be replaced in the near future.")
|
||||
class ContextManager {
|
||||
|
||||
/** [BattleContext.Type] buckets containing [BattleContexts] of that type. */
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.battles.runner
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.cobblemon.mod.common.battles.runner.socket.SocketShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import java.util.UUID
|
||||
|
||||
@@ -27,14 +28,13 @@ interface ShowdownService {
|
||||
fun closeConnection()
|
||||
fun startBattle(battle: PokemonBattle, messages: Array<String>)
|
||||
fun send(battleId: UUID, messages: Array<String>)
|
||||
fun getAbilityIds(): JsonArray
|
||||
fun getMoves(): JsonArray
|
||||
fun getItemIds(): JsonArray
|
||||
fun registerSpecies()
|
||||
fun registerBagItems()
|
||||
fun getRegistryData(type: String): JsonArray
|
||||
fun sendRegistryData(data: Map<String, String>, type: String)
|
||||
fun sendRegistryEntry(data: String, type: String)
|
||||
fun resetRegistryData(type: String)
|
||||
fun resetAllRegistries()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
|
||||
|
||||
companion object {
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
|
||||
+27
-39
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.gson.Gson
|
||||
@@ -33,6 +31,7 @@ import org.graalvm.polyglot.HostAccess.Export
|
||||
import org.graalvm.polyglot.PolyglotAccess
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.graalvm.polyglot.io.FileSystem
|
||||
import kotlin.text.replace
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
@@ -134,49 +133,38 @@ class GraalShowdownService : ShowdownService {
|
||||
sendToShowdown(battleId, messages)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
val getCobbledAbilityIdsFn = context.getBindings("js").getMember("getCobbledAbilityIds")
|
||||
val arrayResult = getCobbledAbilityIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
override fun getRegistryData(type: String): JsonArray {
|
||||
val func = context.getBindings("js").getMember("getData")
|
||||
val result = func.execute(type).asString()
|
||||
return gson.fromJson(result, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
val getCobbledMovesFn = context.getBindings("js").getMember("getCobbledMoves")
|
||||
val arrayResult = getCobbledMovesFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getCobbledItemIds")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
var index = 0L
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, null)))
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, form)))
|
||||
}
|
||||
}
|
||||
override fun sendRegistryData(data: Map<String, String>, type: String) {
|
||||
val payload = data.entries.joinToString(prefix = "{", postfix = "}") { (k, v) ->
|
||||
val newV = v.replace(Regex("[\r\n]+"), " ")
|
||||
"\"$k\": $newV"
|
||||
}
|
||||
receiveSpeciesDataFn.execute(jsArray)
|
||||
val func = this.context.getBindings("js").getMember("receiveData")
|
||||
func.execute(payload, type)
|
||||
}
|
||||
|
||||
override fun sendRegistryEntry(data: String, type: String) {
|
||||
val payload = data.replace(Regex("[\r\n]+"), " ")
|
||||
val func = this.context.getBindings("js").getMember("receiveData")
|
||||
func.execute(payload, type)
|
||||
}
|
||||
|
||||
override fun resetRegistryData(type: String) {
|
||||
this.context.getBindings("js").getMember("resetData").execute(type)
|
||||
}
|
||||
|
||||
override fun resetAllRegistries() {
|
||||
this.context.getBindings("js").getMember("resetAll").execute()
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
val afterCobbledSpeciesInitFn = this.context.getBindings("js").getMember("afterCobbledSpeciesInit")
|
||||
afterCobbledSpeciesInitFn.execute()
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
val receiveBagItemDataFn = this.context.getBindings("js").getMember("receiveBagItemData")
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
receiveBagItemDataFn.execute(itemId, js.replace("\n", " "))
|
||||
}
|
||||
val func = this.context.getBindings("js").getMember("afterSpeciesInit")
|
||||
func.execute()
|
||||
}
|
||||
|
||||
private fun sendToShowdown(battleId: UUID, messages: Array<String>) {
|
||||
|
||||
+37
-48
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.battles.runner.socket
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
@@ -102,48 +101,44 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
ShowdownInterpreter.interpretMessage(battleId, message)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
writer.write(">getCobbledAbilityIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
override fun sendRegistryData(data: Map<String, String>, type: String) {
|
||||
data.forEach { (_, value) -> sendRegistryEntry(value, type) }
|
||||
// The code for sending bulk data is commented out below, because:
|
||||
// 1) while debugging it's useful to have individual entries, and socket is only used for debug atm
|
||||
// 2) the species JSONs are way too big to be handled as one line on the JS side (maybe both sides?)
|
||||
// this can eventually be remedied if socket is intended to be used for any actual servers/etc.
|
||||
// alternatively, you could get around this limitation by just having ONLY PokemonSpecies send individually,
|
||||
// but it's likely that with enough custom data for moves/abilities/etc. you'd run into the same issue
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
writer.write(">getCobbledMoves\n")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
writer.write(">getCobbledItemIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
private fun sendBagItem(itemId: String, js: String) {
|
||||
writer.write(">receiveBagItemData $itemId $js")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send bag item to Showdown: $itemId") }
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
writer.write(">resetSpeciesData\n")
|
||||
acknowledge()
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
sendSpeciesData(species, null)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
sendSpeciesData(species, form)
|
||||
}
|
||||
}
|
||||
/*val payload = data.entries.joinToString(prefix = "{", postfix = "}") { (k, v) ->
|
||||
val newV = v.replace(Regex("[\r\n]+"), " ")
|
||||
"\"$k\": $newV"
|
||||
}
|
||||
writer.write(">receiveData $type payload")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send $type data to Showdown: $data") }*/
|
||||
}
|
||||
|
||||
override fun sendRegistryEntry(data: String, type: String) {
|
||||
val payload = data.replace(Regex("[\r\n]+"), " ")
|
||||
writer.write(">receiveEntry $type $payload")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send $type data to Showdown: $payload") }
|
||||
}
|
||||
|
||||
override fun getRegistryData(type: String): JsonArray {
|
||||
writer.write(">getData $type")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun resetRegistryData(type: String) {
|
||||
writer.write(">resetData $type")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
override fun resetAllRegistries() {
|
||||
writer.write(">resetAll")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
fun acknowledge(ifFails: () -> Unit = {}) {
|
||||
@@ -155,14 +150,8 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
sendBagItem(itemId, js.replace("\n", " "))
|
||||
}
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
writer.write(">afterCobbledSpeciesInit")
|
||||
writer.write(">afterSpeciesInit")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
|
||||
@@ -226,115 +226,99 @@ class ApricornBlock(settings: Properties, val apricorn: Apricorn) : HorizontalDi
|
||||
const val MIN_AGE = 0
|
||||
|
||||
// North
|
||||
// Stage 0
|
||||
private val NORTH_STAGE_0 = Shapes.box(0.40625, 0.5, 0.0, 0.59375, 0.6875, 0.1875)
|
||||
private val NORTH_CAP_STAGE_0 = Shapes.box(0.4375, 0.65625, 0.03125, 0.5625, 0.71875, 0.15625)
|
||||
|
||||
// Stage 1
|
||||
private val NORTH_TOP_STAGE_1 = Block.box(7.0, 11.0, 0.5, 9.0, 11.5, 2.5)
|
||||
private val NORTH_BODY_STAGE_1 = Block.box(6.5, 9.0, 0.0, 9.5, 11.0, 3.0)
|
||||
private val NORTH_BOTTOM_STAGE_1 = Block.box(7.0, 8.5, 0.5, 9.0, 9.0, 2.5)
|
||||
private val NORTH_STAGE_1 = Shapes.box(0.375, 0.40625, 0.0, 0.625, 0.65625, 0.25)
|
||||
private val NORTH_CAP_STAGE_1 = Shapes.box(0.40625, 0.625, 0.03125, 0.59375, 0.6875, 0.21875)
|
||||
|
||||
// Stage 2
|
||||
private val NORTH_TOP_STAGE_2 = Block.box(6.5, 10.5, 0.5, 9.5, 11.0, 3.5)
|
||||
private val NORTH_BODY_STAGE_2 = Block.box(6.0, 7.5, 0.0, 10.0, 10.5, 4.0)
|
||||
private val NORTH_BOTTOM_STAGE_2 = Block.box(6.5, 7.0, 0.5, 9.5, 7.5, 3.5)
|
||||
private val NORTH_STAGE_2 = Shapes.box(0.34375, 0.296875, 0.0, 0.65625, 0.609375, 0.3125)
|
||||
private val NORTH_CAP_STAGE_2 = Shapes.box(0.375, 0.59375, 0.03125, 0.625, 0.65625, 0.28125)
|
||||
|
||||
// Stage 3
|
||||
private val NORTH_TOP_STAGE_3 = Block.box(6.0, 9.75, 0.5, 10.0, 10.5, 4.5)
|
||||
private val NORTH_BODY_STAGE_3 = Block.box(5.5, 5.75, 0.0, 10.5, 9.75, 5.0)
|
||||
private val NORTH_BOTTOM_STAGE_3 = Block.box(6.0, 5.0, 0.5, 10.0, 5.75, 4.5)
|
||||
|
||||
// Full fruit
|
||||
private val NORTH_TOP_FRUIT = Block.box(6.0, 9.0, 1.0, 10.0, 10.0, 5.0)
|
||||
private val NORTH_BODY_FRUIT = Block.box(5.0, 4.0, 0.0, 11.0, 9.0, 6.0)
|
||||
private val NORTH_BOTTOM_FRUIT = Block.box(5.5, 3.0, 0.5, 10.5, 4.0, 5.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val NORTH_STAGE_3 = Shapes.box(0.3125, 0.1875, 0.0, 0.6875, 0.5625, 0.375)
|
||||
private val NORTH_CAP_STAGE_3 = Shapes.box(0.375, 0.5625, 0.0625, 0.625, 0.625, 0.3125)
|
||||
|
||||
private val NORTH_AABB = arrayOf(
|
||||
Shapes.or(NORTH_BODY_STAGE_1, NORTH_TOP_STAGE_1, NORTH_BOTTOM_STAGE_1),
|
||||
Shapes.or(NORTH_BODY_STAGE_2, NORTH_TOP_STAGE_2, NORTH_BOTTOM_STAGE_2),
|
||||
Shapes.or(NORTH_BODY_STAGE_3, NORTH_TOP_STAGE_3, NORTH_BOTTOM_STAGE_3),
|
||||
Shapes.or(NORTH_BODY_FRUIT, NORTH_TOP_FRUIT, NORTH_BOTTOM_FRUIT)
|
||||
Shapes.or(NORTH_STAGE_0, NORTH_CAP_STAGE_0),
|
||||
Shapes.or(NORTH_STAGE_1, NORTH_CAP_STAGE_1),
|
||||
Shapes.or(NORTH_STAGE_2, NORTH_CAP_STAGE_2),
|
||||
Shapes.or(NORTH_STAGE_3, NORTH_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// South
|
||||
// Stage 0
|
||||
private val SOUTH_STAGE_0 = Shapes.box(0.40625, 0.5, 0.8125, 0.59375, 0.6875, 1.0)
|
||||
private val SOUTH_CAP_STAGE_0 = Shapes.box(0.4375, 0.65625, 0.84375, 0.5625, 0.71875, 0.96875)
|
||||
|
||||
// Stage 1
|
||||
private val SOUTH_TOP_STAGE_1 = Block.box(7.0, 11.0, 13.5, 9.0, 11.5, 15.5)
|
||||
private val SOUTH_BODY_STAGE_1 = Block.box(6.5, 9.0, 13.0, 9.5, 11.0, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_1 = Block.box(7.0, 8.5, 13.5, 9.0, 9.0, 15.5)
|
||||
private val SOUTH_STAGE_1 = Shapes.box(0.375, 0.40625, 0.75, 0.625, 0.65625, 1.0)
|
||||
private val SOUTH_CAP_STAGE_1 = Shapes.box(0.40625, 0.625, 0.78125, 0.59375, 0.6875, 0.96875)
|
||||
|
||||
// Stage 2
|
||||
private val SOUTH_TOP_STAGE_2 = Block.box(6.5, 10.5, 12.5, 9.5, 11.0, 15.5)
|
||||
private val SOUTH_BODY_STAGE_2 = Block.box(6.0, 7.5, 12.0, 10.0, 10.5, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_2 = Block.box(6.5, 7.0, 12.5, 9.5, 7.5, 15.5)
|
||||
private val SOUTH_STAGE_2 = Shapes.box(0.34375, 0.296875, 0.6875, 0.65625, 0.609375, 1.0)
|
||||
private val SOUTH_CAP_STAGE_2 = Shapes.box(0.375, 0.59375, 0.71875, 0.625, 0.65625, 0.96875)
|
||||
|
||||
// Stage 3
|
||||
private val SOUTH_TOP_STAGE_3 = Block.box(6.0, 9.75, 11.5, 10.0, 10.5, 15.5)
|
||||
private val SOUTH_BODY_STAGE_3 = Block.box(5.5, 5.75, 11.0, 10.5, 9.75, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_3 = Block.box(6.0, 5.0, 11.5, 10.0, 5.75, 15.5)
|
||||
|
||||
// Full fruit
|
||||
private val SOUTH_TOP_FRUIT = Block.box(6.0, 9.0, 11.0, 10.0, 10.0, 15.0)
|
||||
private val SOUTH_BODY_FRUIT = Block.box(5.0, 4.0, 10.0, 11.0, 9.0, 16.0)
|
||||
private val SOUTH_BOTTOM_FRUIT = Block.box(5.5, 3.0, 10.5, 10.5, 4.0, 15.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val SOUTH_STAGE_3 = Shapes.box(0.3125, 0.1875, 0.625, 0.6875, 0.5625, 1.0)
|
||||
private val SOUTH_CAP_STAGE_3 = Shapes.box(0.375, 0.5625, 0.6875, 0.625, 0.625, 0.9375)
|
||||
|
||||
private val SOUTH_AABB = arrayOf(
|
||||
Shapes.or(SOUTH_BODY_STAGE_1, SOUTH_TOP_STAGE_1, SOUTH_BOTTOM_STAGE_1),
|
||||
Shapes.or(SOUTH_BODY_STAGE_2, SOUTH_TOP_STAGE_2, SOUTH_BOTTOM_STAGE_2),
|
||||
Shapes.or(SOUTH_BODY_STAGE_3, SOUTH_TOP_STAGE_3, SOUTH_BOTTOM_STAGE_3),
|
||||
Shapes.or(SOUTH_BODY_FRUIT, SOUTH_TOP_FRUIT, SOUTH_BOTTOM_FRUIT)
|
||||
Shapes.or(SOUTH_STAGE_0, SOUTH_CAP_STAGE_0),
|
||||
Shapes.or(SOUTH_STAGE_1, SOUTH_CAP_STAGE_1),
|
||||
Shapes.or(SOUTH_STAGE_2, SOUTH_CAP_STAGE_2),
|
||||
Shapes.or(SOUTH_STAGE_3, SOUTH_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// East
|
||||
// Stage 0
|
||||
private val EAST_STAGE_0 = Shapes.box(0.8125, 0.5, 0.40625, 1.0, 0.6875, 0.59375)
|
||||
private val EAST_CAP_STAGE_0 = Shapes.box(0.84375, 0.65625, 0.4375, 0.96875, 0.71875, 0.5625)
|
||||
|
||||
// Stage 1
|
||||
private val EAST_TOP_STAGE_1 = Block.box(13.5, 11.0, 7.0, 15.5, 11.5, 9.0)
|
||||
private val EAST_BODY_STAGE_1 = Block.box(13.0, 9.0, 6.5, 16.0, 11.0, 9.5)
|
||||
private val EAST_BOTTOM_STAGE_1 = Block.box(13.5, 8.5, 7.0, 15.5, 9.0, 9.0)
|
||||
private val EAST_STAGE_1 = Shapes.box(0.75, 0.40625, 0.375, 1.0, 0.65625, 0.625)
|
||||
private val EAST_CAP_STAGE_1 = Shapes.box(0.78125, 0.625, 0.40625, 0.96875, 0.6875, 0.59375)
|
||||
|
||||
// Stage 2
|
||||
private val EAST_TOP_STAGE_2 = Block.box(12.5, 10.5, 6.5, 15.5, 11.0, 9.5)
|
||||
private val EAST_BODY_STAGE_2 = Block.box(12.0, 7.5, 6.0, 16.0, 10.5, 10.0)
|
||||
private val EAST_BOTTOM_STAGE_2 = Block.box(12.5, 7.0, 6.5, 15.5, 7.5, 9.5)
|
||||
private val EAST_STAGE_2 = Shapes.box(0.6875, 0.296875, 0.34375, 1.0, 0.609375, 0.65625)
|
||||
private val EAST_CAP_STAGE_2 = Shapes.box(0.71875, 0.59375, 0.375, 0.96875, 0.65625, 0.625)
|
||||
|
||||
// Stage 3
|
||||
private val EAST_TOP_STAGE_3 = Block.box(11.5, 9.75, 6.0, 15.5, 10.5, 10.0)
|
||||
private val EAST_BODY_STAGE_3 = Block.box(11.0, 5.75, 5.5, 16.0, 9.75, 10.5)
|
||||
private val EAST_BOTTOM_STAGE_3 = Block.box(11.5, 5.0, 6.0, 15.5, 5.75, 10.0)
|
||||
|
||||
// Full fruit
|
||||
private val EAST_TOP_FRUIT = Block.box(11.0, 9.0, 6.0, 15.0, 10.0, 10.0)
|
||||
private val EAST_BODY_FRUIT = Block.box(10.0, 4.0, 5.0, 16.0, 9.0, 11.0)
|
||||
private val EAST_BOTTOM_FRUIT = Block.box(10.5, 3.0, 5.5, 15.5, 4.0, 10.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val EAST_STAGE_3 = Shapes.box(0.625, 0.1875, 0.3125, 1.0, 0.5625, 0.6875)
|
||||
private val EAST_CAP_STAGE_3 = Shapes.box(0.6875, 0.5625, 0.375, 0.9375, 0.625, 0.625)
|
||||
|
||||
private val EAST_AABB = arrayOf(
|
||||
Shapes.or(EAST_BODY_STAGE_1, EAST_TOP_STAGE_1, EAST_BOTTOM_STAGE_1),
|
||||
Shapes.or(EAST_BODY_STAGE_2, EAST_TOP_STAGE_2, EAST_BOTTOM_STAGE_2),
|
||||
Shapes.or(EAST_BODY_STAGE_3, EAST_TOP_STAGE_3, EAST_BOTTOM_STAGE_3),
|
||||
Shapes.or(EAST_BODY_FRUIT, EAST_TOP_FRUIT, EAST_BOTTOM_FRUIT)
|
||||
Shapes.or(EAST_STAGE_0, EAST_CAP_STAGE_0),
|
||||
Shapes.or(EAST_STAGE_1, EAST_CAP_STAGE_1),
|
||||
Shapes.or(EAST_STAGE_2, EAST_CAP_STAGE_2),
|
||||
Shapes.or(EAST_STAGE_3, EAST_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// West
|
||||
// Stage 0
|
||||
private val WEST_STAGE_0 = Shapes.box(0.0, 0.5, 0.40625, 0.1875, 0.6875, 0.59375)
|
||||
private val WEST_CAP_STAGE_0 = Shapes.box(0.03125, 0.65625, 0.4375, 0.15625, 0.71875, 0.5625)
|
||||
|
||||
// Stage 1
|
||||
private val WEST_TOP_STAGE_1 = Block.box(0.5, 11.0, 7.0, 2.5, 11.5, 9.0)
|
||||
private val WEST_BODY_STAGE_1 = Block.box(0.0, 9.0, 6.5, 3.0, 11.0, 9.5)
|
||||
private val WEST_BOTTOM_STAGE_1 = Block.box(0.5, 8.5, 7.0, 2.5, 9.0, 9.0)
|
||||
private val WEST_STAGE_1 = Shapes.box(0.0, 0.40625, 0.375, 0.25, 0.65625, 0.625)
|
||||
private val WEST_CAP_STAGE_1 = Shapes.box(0.03125, 0.625, 0.40625, 0.21875, 0.6875, 0.59375)
|
||||
|
||||
// Stage 2
|
||||
private val WEST_TOP_STAGE_2 = Block.box(0.5, 10.5, 6.5, 3.5, 11.0, 9.5)
|
||||
private val WEST_BODY_STAGE_2 = Block.box(0.0, 7.5, 6.0, 4.0, 10.5, 10.0)
|
||||
private val WEST_BOTTOM_STAGE_2 = Block.box(0.5, 7.0, 6.5, 3.5, 7.5, 9.5)
|
||||
private val WEST_STAGE_2 = Shapes.box(0.0, 0.296875, 0.34375, 0.3125, 0.609375, 0.65625)
|
||||
private val WEST_CAP_STAGE_2 = Shapes.box(0.03125, 0.59375, 0.375, 0.28125, 0.65625, 0.625)
|
||||
|
||||
// Stage 3
|
||||
private val WEST_TOP_STAGE_3 = Block.box(0.5, 9.75, 6.0, 4.5, 10.5, 10.0)
|
||||
private val WEST_BODY_STAGE_3 = Block.box(0.0, 5.75, 5.5, 5.0, 9.75, 10.5)
|
||||
private val WEST_BOTTOM_STAGE_3 = Block.box(0.5, 5.0, 6.0, 4.5, 5.75, 10.0)
|
||||
|
||||
// Full fruit
|
||||
private val WEST_TOP_FRUIT = Block.box(1.0, 9.0, 6.0, 5.0, 10.0, 10.0)
|
||||
private val WEST_BODY_FRUIT = Block.box(0.0, 4.0, 5.0, 6.0, 9.0, 11.0)
|
||||
private val WEST_BOTTOM_FRUIT = Block.box(0.5, 3.0, 5.5, 5.5, 4.0, 10.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val WEST_STAGE_3 = Shapes.box(0.0, 0.1875, 0.3125, 0.375, 0.5625, 0.6875)
|
||||
private val WEST_CAP_STAGE_3 = Shapes.box(0.0625, 0.5625, 0.375, 0.3125, 0.625, 0.625)
|
||||
|
||||
private val WEST_AABB = arrayOf(
|
||||
Shapes.or(WEST_BODY_STAGE_1, WEST_TOP_STAGE_1, WEST_BOTTOM_STAGE_1),
|
||||
Shapes.or(WEST_BODY_STAGE_2, WEST_TOP_STAGE_2, WEST_BOTTOM_STAGE_2),
|
||||
Shapes.or(WEST_BODY_STAGE_3, WEST_TOP_STAGE_3, WEST_BOTTOM_STAGE_3),
|
||||
Shapes.or(WEST_BODY_FRUIT, WEST_TOP_FRUIT, WEST_BOTTOM_FRUIT)
|
||||
Shapes.or(WEST_STAGE_0, WEST_CAP_STAGE_0),
|
||||
Shapes.or(WEST_STAGE_1, WEST_CAP_STAGE_1),
|
||||
Shapes.or(WEST_STAGE_2, WEST_CAP_STAGE_2),
|
||||
Shapes.or(WEST_STAGE_3, WEST_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ class BugwortBlock(settings: Properties) : CropBlock(settings), BonemealableBloc
|
||||
builder.add(IS_WILD)
|
||||
}
|
||||
|
||||
override fun getBaseSeedId(): ItemLike = CobblemonItems.BUGWORT
|
||||
// TODO after 1.7
|
||||
// override fun getBaseSeedId(): ItemLike = CobblemonItems.BUGWORT
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = AGE_TO_SHAPE[this.getAge(state)]
|
||||
|
||||
@@ -93,26 +94,27 @@ class BugwortBlock(settings: Properties) : CropBlock(settings), BonemealableBloc
|
||||
return floor.`is`(BlockTags.DIRT) || floor.`is`(Blocks.FARMLAND)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (state != null && world != null && pos != null) {
|
||||
// if bugwort is at max age then revert age to 0 and drop items
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
return InteractionResult.PASS
|
||||
} else if (this.isMaxAge(state)) {
|
||||
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
|
||||
world.setBlock(pos, state.setValue(this.ageProperty, (0).coerceAtMost(this.maxAge)), UPDATE_CLIENTS)
|
||||
Block.popResource(world, pos, ItemStack(CobblemonItems.BUGWORT, Random.nextInt(2, 4)))
|
||||
return InteractionResult.sidedSuccess(world.isClientSide)
|
||||
}
|
||||
}
|
||||
return super.useWithoutItem(state, world, pos, player, hit)
|
||||
}
|
||||
// TODO after 1.7
|
||||
// override fun useWithoutItem(
|
||||
// state: BlockState,
|
||||
// world: Level,
|
||||
// pos: BlockPos,
|
||||
// player: Player,
|
||||
// hit: BlockHitResult
|
||||
// ): InteractionResult {
|
||||
// if (state != null && world != null && pos != null) {
|
||||
// // if bugwort is at max age then revert age to 0 and drop items
|
||||
// if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
// return InteractionResult.PASS
|
||||
// } else if (this.isMaxAge(state)) {
|
||||
// world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
|
||||
// world.setBlock(pos, state.setValue(this.ageProperty, (0).coerceAtMost(this.maxAge)), UPDATE_CLIENTS)
|
||||
// Block.popResource(world, pos, ItemStack(CobblemonItems.BUGWORT, Random.nextInt(2, 4)))
|
||||
// return InteractionResult.sidedSuccess(world.isClientSide)
|
||||
// }
|
||||
// }
|
||||
// return super.useWithoutItem(state, world, pos, player, hit)
|
||||
// }
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
|
||||
|
||||
@@ -1,168 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.Rotation
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
|
||||
companion object {
|
||||
val FACING: DirectionProperty = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL)
|
||||
|
||||
val SHAPE_BY_BITE = arrayOf(
|
||||
box(1.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(3.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(5.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(7.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(9.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(11.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(13.0, 0.0, 1.0, 15.0, 9.0, 15.0)
|
||||
)
|
||||
|
||||
enum class Rotation {
|
||||
NONE,
|
||||
CLOCKWISE_90,
|
||||
CLOCKWISE_180,
|
||||
COUNTERCLOCKWISE_90
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH))
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
if (placer != null) {
|
||||
level.setBlock(pos, state.setValue(FACING, placer.direction.opposite), 2)
|
||||
}
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity ?: return ItemStack.EMPTY
|
||||
return blockEntity.toItemStack()
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
val bites = getBites(level, pos)
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
val maxBites = blockEntity?.maxBites ?: 6
|
||||
|
||||
val visualStage = ((bites * SHAPE_BY_BITE.size) / (maxBites + 1))
|
||||
.coerceIn(0, SHAPE_BY_BITE.lastIndex)
|
||||
|
||||
val baseShape = SHAPE_BY_BITE[visualStage]
|
||||
|
||||
return when (state.getValue(FACING)) {
|
||||
Direction.NORTH -> baseShape
|
||||
Direction.SOUTH -> rotateShape(baseShape, Rotation.CLOCKWISE_180)
|
||||
Direction.WEST -> rotateShape(baseShape, Rotation.COUNTERCLOCKWISE_90)
|
||||
Direction.EAST -> rotateShape(baseShape, Rotation.CLOCKWISE_90)
|
||||
else -> baseShape
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotateShape(shape: VoxelShape, rotation: Rotation): VoxelShape {
|
||||
var newShape = net.minecraft.world.phys.shapes.Shapes.empty()
|
||||
|
||||
for (box in shape.toAabbs()) {
|
||||
val minX = box.minX
|
||||
val minY = box.minY
|
||||
val minZ = box.minZ
|
||||
val maxX = box.maxX
|
||||
val maxY = box.maxY
|
||||
val maxZ = box.maxZ
|
||||
|
||||
val (rMinX, rMinZ, rMaxX, rMaxZ) = when (rotation) {
|
||||
Rotation.NONE -> arrayOf(minX, minZ, maxX, maxZ)
|
||||
Rotation.CLOCKWISE_90 -> arrayOf(minZ, 1.0 - maxX, maxZ, 1.0 - minX)
|
||||
Rotation.CLOCKWISE_180 -> arrayOf(1.0 - maxX, 1.0 - maxZ, 1.0 - minX, 1.0 - minZ)
|
||||
Rotation.COUNTERCLOCKWISE_90 -> arrayOf(1.0 - maxZ, minX, 1.0 - minZ, maxX)
|
||||
}
|
||||
|
||||
val rotatedVoxel = net.minecraft.world.phys.shapes.Shapes.box(rMinX, minY, rMinZ, rMaxX, maxY, rMaxZ)
|
||||
|
||||
newShape = net.minecraft.world.phys.shapes.Shapes.or(newShape, rotatedVoxel)
|
||||
}
|
||||
|
||||
return newShape
|
||||
}
|
||||
|
||||
override fun spawnDestroyParticles(
|
||||
level: Level,
|
||||
player: Player,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
val color = blockEntity?.foodColourComponent?.colours?.lastOrNull()
|
||||
val woolBlock = when (color) {
|
||||
DyeColor.RED -> Blocks.RED_WOOL
|
||||
DyeColor.ORANGE -> Blocks.ORANGE_WOOL
|
||||
DyeColor.YELLOW -> Blocks.YELLOW_WOOL
|
||||
DyeColor.LIME -> Blocks.LIME_WOOL
|
||||
DyeColor.GREEN -> Blocks.GREEN_WOOL
|
||||
DyeColor.CYAN -> Blocks.CYAN_WOOL
|
||||
DyeColor.LIGHT_BLUE -> Blocks.LIGHT_BLUE_WOOL
|
||||
DyeColor.BLUE -> Blocks.BLUE_WOOL
|
||||
DyeColor.PURPLE -> Blocks.PURPLE_WOOL
|
||||
DyeColor.MAGENTA -> Blocks.MAGENTA_WOOL
|
||||
DyeColor.PINK -> Blocks.PINK_WOOL
|
||||
else -> Blocks.WHITE_WOOL
|
||||
}
|
||||
|
||||
level.levelEvent(player, 2001, pos, getId(woolBlock.defaultBlockState()))
|
||||
}
|
||||
|
||||
fun getFoodColourComponent(level: BlockGetter, pos: BlockPos): FoodColourComponent? {
|
||||
return (level.getBlockEntity(pos) as? CakeBlockEntity)?.foodColourComponent
|
||||
}
|
||||
|
||||
fun setFoodColourComponent(level: BlockGetter, pos: BlockPos, foodColourComponent: FoodColourComponent) {
|
||||
(level.getBlockEntity(pos) as? CakeBlockEntity)?.foodColourComponent = foodColourComponent
|
||||
}
|
||||
|
||||
fun getBites(level: BlockGetter, pos: BlockPos): Int = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites ?: 0
|
||||
fun setBites(level: BlockGetter, pos: BlockPos, bites: Int) {
|
||||
(level.getBlockEntity(pos) as? CakeBlockEntity)?.bites = bites
|
||||
}
|
||||
}
|
||||
@@ -1,178 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.CandlePokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.block.CakeBlockEntityRenderer.Companion.TOP_LAYER_HEIGHT
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
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.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.AbstractCandleBlock
|
||||
import net.minecraft.world.level.block.AbstractCandleBlock.extinguish
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks.*
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
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.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val LIT = BlockStateProperties.LIT
|
||||
val CANDLE_COLOR = IntegerProperty.create("candle_color", 0, MapColor.MATERIAL_COLORS.size - 1)
|
||||
|
||||
fun getCandleByMapColorId(id: Int): CandleBlock {
|
||||
return when (id) {
|
||||
MapColor.SAND.id -> CANDLE
|
||||
MapColor.WOOL.id -> WHITE_CANDLE
|
||||
MapColor.COLOR_ORANGE.id -> ORANGE_CANDLE
|
||||
MapColor.COLOR_MAGENTA.id -> MAGENTA_CANDLE
|
||||
MapColor.COLOR_LIGHT_BLUE.id -> LIGHT_BLUE_CANDLE
|
||||
MapColor.COLOR_YELLOW.id -> YELLOW_CANDLE
|
||||
MapColor.COLOR_LIGHT_GREEN.id -> LIME_CANDLE
|
||||
MapColor.COLOR_PINK.id -> PINK_CANDLE
|
||||
MapColor.COLOR_GRAY.id -> GRAY_CANDLE
|
||||
MapColor.COLOR_LIGHT_GRAY.id -> LIGHT_GRAY_CANDLE
|
||||
MapColor.COLOR_CYAN.id -> CYAN_CANDLE
|
||||
MapColor.COLOR_PURPLE.id -> PURPLE_CANDLE
|
||||
MapColor.COLOR_BLUE.id -> BLUE_CANDLE
|
||||
MapColor.COLOR_BROWN.id -> BROWN_CANDLE
|
||||
MapColor.COLOR_GREEN.id -> GREEN_CANDLE
|
||||
MapColor.COLOR_RED.id -> RED_CANDLE
|
||||
MapColor.COLOR_BLACK.id -> BLACK_CANDLE
|
||||
else -> CANDLE
|
||||
} as CandleBlock
|
||||
}
|
||||
|
||||
val CANDLE_SHAPE: VoxelShape = box(7.0, 8.0, 7.0, 9.0, 15.0, 9.0)
|
||||
val SHAPE: VoxelShape = Shapes.or(SHAPE_BY_BITE[0], CANDLE_SHAPE)
|
||||
val PARTICLE_OFFSET = Vec3(0.5, 0.5 + TOP_LAYER_HEIGHT, 0.5)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(LIT, false)
|
||||
.setValue(CANDLE_COLOR, MapColor.SAND.id)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?> {
|
||||
return simpleCodec(::CandlePokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return CandlePokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(LIT, CANDLE_COLOR)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
return if (!stack.`is`(Items.FLINT_AND_STEEL) && !stack.`is`(Items.FIRE_CHARGE)) {
|
||||
if (candleHit(hitResult) && stack.isEmpty && state.getValue(LIT) == true) {
|
||||
extinguish(player, state, level, pos)
|
||||
level.addParticle(ParticleTypes.SMOKE, PARTICLE_OFFSET.x + pos.x, pos.y + PARTICLE_OFFSET.y, pos.z + PARTICLE_OFFSET.z, 0.0, 0.1, 0.0)
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||
}
|
||||
} else if (state.getValue(LIT) == false) {
|
||||
level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0f, level.random.nextFloat() * 0.4f + 0.8f)
|
||||
level.setBlock(pos, state.setValue(BlockStateProperties.LIT, true), UPDATE_ALL_IMMEDIATE)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
player.getItemInHand(hand).hurtAndBreak(1, player, hand.toEquipmentSlot())
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
val oldCookingComponent = getFoodColourComponent(level, pos)
|
||||
val candle = getCandleByMapColorId(state.getValue(CANDLE_COLOR))
|
||||
|
||||
val newBlockState = CobblemonBlocks.POKE_CAKE.defaultBlockState()
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
val pokeCakeBlock = newBlockState.block as PokeCakeBlock
|
||||
oldCookingComponent?.let {
|
||||
pokeCakeBlock.setFoodColourComponent(level, pos, it)
|
||||
}
|
||||
|
||||
val interactionResult = pokeCakeBlock.eat(level, pos, player)
|
||||
if (interactionResult.consumesAction()) {
|
||||
popResource(level, pos, ItemStack(candle.asItem()))
|
||||
}
|
||||
|
||||
return interactionResult
|
||||
}
|
||||
|
||||
private fun candleHit(hit: BlockHitResult): Boolean {
|
||||
return hit.location.y - hit.blockPos.y.toDouble() > TOP_LAYER_HEIGHT
|
||||
}
|
||||
|
||||
override fun animateTick(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
random: RandomSource
|
||||
) {
|
||||
if (state.getValue(LIT)) {
|
||||
AbstractCandleBlock.addParticlesAndSound(level, Vec3(PARTICLE_OFFSET.x + pos.x, PARTICLE_OFFSET.y + pos.y, PARTICLE_OFFSET.z + pos.z), random)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape = SHAPE
|
||||
}
|
||||
@@ -10,24 +10,23 @@ package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.RandomSource
|
||||
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.ItemLike
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.*
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.SoundType
|
||||
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
|
||||
@@ -116,7 +115,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
}
|
||||
|
||||
private fun canGrow(world: LevelReader, pos: BlockPos, state: BlockState): Boolean {
|
||||
return !this.isMaxAge(state) && canSurvive(state, world, pos)
|
||||
return !this.isMaxAge(state) && canSurvive(state, world, pos) && !(!canGrowInto(world, pos.above()) && state.getValue(AGE) >= AGE_AFTER_HARVEST)
|
||||
}
|
||||
|
||||
private fun canGrowInto(world: LevelReader, pos: BlockPos): Boolean {
|
||||
@@ -124,28 +123,6 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
return blockState.isAir || blockState.`is`(CobblemonBlocks.HEARTY_GRAINS)
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
if (!world.isClientSide) {
|
||||
if (state.getValue(HALF) == DoubleBlockHalf.UPPER) {
|
||||
val belowPos = pos.below()
|
||||
val belowState = world.getBlockState(belowPos)
|
||||
world.destroyBlock(pos, true) // Break the lower block without drops
|
||||
world.setBlock(
|
||||
belowPos,
|
||||
belowState.setValue(AGE, AGE_AFTER_HARVEST), // Reset age to simulate regrowth preparation
|
||||
UPDATE_CLIENTS
|
||||
)
|
||||
} else if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
|
||||
val abovePos = pos.above()
|
||||
val aboveState = world.getBlockState(abovePos)
|
||||
if (aboveState.block is HeartyGrainsBlock && aboveState.getValue(HALF) == DoubleBlockHalf.UPPER) {
|
||||
world.destroyBlock(abovePos, true) // Break the upper block without drops
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
@@ -157,15 +134,12 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
if (!state.canSurvive(world, pos)) return Blocks.AIR.defaultBlockState()
|
||||
val doubleBlockHalf = state.getValue(HALF)
|
||||
return if (direction.axis !== Direction.Axis.Y || doubleBlockHalf == DoubleBlockHalf.LOWER != (direction == Direction.UP) ||
|
||||
neighborState.`is`(this) && neighborState.getValue(HALF) != doubleBlockHalf) {
|
||||
if (doubleBlockHalf == DoubleBlockHalf.LOWER && direction == Direction.DOWN && !state.canSurvive(world, pos))
|
||||
Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
} else {
|
||||
Blocks.AIR.defaultBlockState()
|
||||
if (doubleBlockHalf == DoubleBlockHalf.LOWER && direction == Direction.UP) {
|
||||
if (state.getValue(AGE) > AGE_AFTER_HARVEST && !neighborState.`is`(CobblemonBlocks.HEARTY_GRAINS)) return state.setValue(AGE, AGE_AFTER_HARVEST)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
override fun isValidBonemealTarget(level: LevelReader, pos: BlockPos, state: BlockState): Boolean {
|
||||
@@ -211,7 +185,9 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
override fun getBonemealAgeIncrease(level: Level): Int {
|
||||
return Mth.nextInt(level.random, 1, 3)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
|
||||
return when (state.getValue(HALF)) {
|
||||
@@ -222,9 +198,9 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
// Check if bottom part is submerged in water
|
||||
val fluidState = world.getFluidState(pos)
|
||||
if (fluidState.`is`(FluidTags.WATER) && fluidState.isSource)
|
||||
return floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_WATER_PLANTABLE) && canGrowInto(world, pos.above())
|
||||
return floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_WATER_PLANTABLE)
|
||||
|
||||
floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_LAND_PLANTABLE) && canGrowInto(world, pos.above())
|
||||
floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_LAND_PLANTABLE)
|
||||
}
|
||||
DoubleBlockHalf.UPPER -> {
|
||||
getLowerHalf(world, pos, state) != null
|
||||
@@ -248,4 +224,8 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
|
||||
private fun isLower(state: BlockState): Boolean =
|
||||
state.`is`(CobblemonBlocks.HEARTY_GRAINS) && state.getValue(HALF) == DoubleBlockHalf.LOWER
|
||||
|
||||
override fun getSoundType(state: BlockState): SoundType? {
|
||||
return if (state.getValue(WATERLOGGED)) CobblemonSounds.HEARTY_GRAINS_WATER_SOUNDS else CobblemonSounds.HEARTY_GRAINS_SOUNDS
|
||||
}
|
||||
}
|
||||
@@ -1,25 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.LureCakeBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
override fun codec(): MapCodec<LureCakeBlock> {
|
||||
return simpleCodec(::LureCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return LureCakeBlockEntity(pos, state)
|
||||
}
|
||||
}
|
||||
@@ -1,134 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionHand.MAIN_HAND
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
||||
class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?> {
|
||||
return simpleCodec(::PokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
|
||||
return PokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeCakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (level.isClientSide) {
|
||||
if (eat(level, pos, player).consumesAction()) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getItemInHand(MAIN_HAND).isEmpty) {
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
|
||||
return eat(level, pos, player)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val item = stack.item
|
||||
val block = state.block as? PokeCakeBlock
|
||||
|
||||
if (stack.`is`(ItemTags.CANDLES) && block?.getBites(level, pos) == 0) {
|
||||
val candleBlock = byItem(item)
|
||||
if (candleBlock is CandleBlock) {
|
||||
stack.consume(1, player)
|
||||
level.playSound(null, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
|
||||
val oldCookingComponent = block.getFoodColourComponent(level, pos)
|
||||
|
||||
val newBlockState = CobblemonBlocks.CANDLE_POKE_CAKE.defaultBlockState()
|
||||
.setValue(CANDLE_COLOR, (item as BlockItem).block.defaultMapColor().id)
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
oldCookingComponent?.let {
|
||||
(newBlockState.block as CakeBlock).setFoodColourComponent(level, pos, it)
|
||||
}
|
||||
|
||||
player.awardStat(Stats.ITEM_USED[item])
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
|
||||
fun eat(level: LevelAccessor, pos: BlockPos, player: Player): InteractionResult {
|
||||
if (!player.canEat(false)) {
|
||||
return InteractionResult.PASS
|
||||
}
|
||||
|
||||
player.getFoodData().eat(2, 0.1F)
|
||||
level.gameEvent(player, GameEvent.EAT, pos)
|
||||
|
||||
var bites = getBites(level, pos)
|
||||
if (bites < 6) {
|
||||
setBites(level, pos, bites + 1)
|
||||
} else {
|
||||
level.removeBlock(pos, false)
|
||||
level.gameEvent(player, GameEvent.BLOCK_DESTROY, pos)
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* 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.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
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.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.EquipmentSlot
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.projectile.Projectile
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
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.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class PokeSnackBlock(settings: Properties, val isLure: Boolean): BaseEntityBlock(settings) {
|
||||
companion object {
|
||||
val MAX_BITES = 8
|
||||
val CAKE_HEIGHT = 0.4375
|
||||
|
||||
val CANDLE_PARTICLE_POSITION = Vec3(0.5, CAKE_HEIGHT + 0.5, 0.5)
|
||||
|
||||
val BITES: IntegerProperty = IntegerProperty.create("bites", 0, MAX_BITES)
|
||||
val CANDLE: IntegerProperty = IntegerProperty.create("candle", 0, MapColor.MATERIAL_COLORS.size - 1)
|
||||
|
||||
val idToCandleBlock: Map<Int, Block> = mapOf(
|
||||
MapColor.WOOL.id to Blocks.WHITE_CANDLE,
|
||||
MapColor.COLOR_ORANGE.id to Blocks.ORANGE_CANDLE,
|
||||
MapColor.COLOR_MAGENTA.id to Blocks.MAGENTA_CANDLE,
|
||||
MapColor.COLOR_LIGHT_BLUE.id to Blocks.LIGHT_BLUE_CANDLE,
|
||||
MapColor.COLOR_YELLOW.id to Blocks.YELLOW_CANDLE,
|
||||
MapColor.COLOR_LIGHT_GREEN.id to Blocks.LIME_CANDLE,
|
||||
MapColor.COLOR_PINK.id to Blocks.PINK_CANDLE,
|
||||
MapColor.COLOR_GRAY.id to Blocks.GRAY_CANDLE,
|
||||
MapColor.COLOR_LIGHT_GRAY.id to Blocks.LIGHT_GRAY_CANDLE,
|
||||
MapColor.COLOR_CYAN.id to Blocks.CYAN_CANDLE,
|
||||
MapColor.COLOR_PURPLE.id to Blocks.PURPLE_CANDLE,
|
||||
MapColor.COLOR_BLUE.id to Blocks.BLUE_CANDLE,
|
||||
MapColor.COLOR_BROWN.id to Blocks.BROWN_CANDLE,
|
||||
MapColor.COLOR_GREEN.id to Blocks.GREEN_CANDLE,
|
||||
MapColor.COLOR_RED.id to Blocks.RED_CANDLE,
|
||||
MapColor.COLOR_BLACK.id to Blocks.BLACK_CANDLE
|
||||
)
|
||||
|
||||
fun getCandleById(id: Int): Block = idToCandleBlock[id] ?: Blocks.CANDLE
|
||||
fun getIdByCandle(candle: CandleBlock): Int = (idToCandleBlock.entries.associate { (id, block) -> block to id })[candle] ?: MapColor.SAND.id
|
||||
|
||||
private val CANDLE_SHAPE = Shapes.box(0.4375, CAKE_HEIGHT, 0.4375, 0.5625, 0.8125, 0.5625)
|
||||
|
||||
private val SHAPES = listOf(
|
||||
Shapes.box(0.0625, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.3125, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.3125),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.6875, 0.0, 0.625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.0625, 0.0, 0.6875, 0.375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875),
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875)
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(BITES, 0)
|
||||
.setValue(CANDLE, 0)
|
||||
.setValue(BlockStateProperties.LIT, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<PokeSnackBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
PrimitiveCodec.BOOL.fieldOf("isLure").forGetter { it.isLure }
|
||||
).apply(it, ::PokeSnackBlock) }
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(BITES, CANDLE, BlockStateProperties.LIT)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = PokeSnackBlockEntity(pos, state)
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
|
||||
if (hasCandle(state)) {
|
||||
return Shapes.or(SHAPES[state.getValue(BITES)], CANDLE_SHAPE)
|
||||
}
|
||||
return SHAPES[state.getValue(BITES)]
|
||||
}
|
||||
|
||||
override fun getRenderShape(blockState: BlockState?) = RenderShape.MODEL
|
||||
|
||||
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeSnackBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeSnackBlockEntity ?: return ItemStack.EMPTY
|
||||
return blockEntity.toItemStack()
|
||||
}
|
||||
|
||||
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
|
||||
if (!isLure) {
|
||||
if (!candleHit(hitResult)) {
|
||||
if (level.isClientSide) {
|
||||
if (playerEat(level, pos, state, player).consumesAction()) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty) {
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
return playerEat(level, pos, state, player)
|
||||
} else if (canBeLit(state) && player.mainHandItem.isEmpty && player.isCrouching()) {
|
||||
player.setItemSlot(EquipmentSlot.MAINHAND, ItemStack(getCandleById(state.getValue(CANDLE))))
|
||||
level.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.5F, level.random.nextFloat() * 0.4F + 0.8F)
|
||||
level.setBlockAndUpdate(pos, state.setValue(CANDLE, 0))
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
}
|
||||
}
|
||||
return super.useWithoutItem(state, level, pos, player, hitResult)
|
||||
}
|
||||
|
||||
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hitResult: BlockHitResult): ItemInteractionResult {
|
||||
if (!isLure) {
|
||||
if (stack.`is`(ItemTags.CANDLES) && !hasCandle(state)) {
|
||||
val item = stack.getItem()
|
||||
val candle = byItem(item)
|
||||
if (candle is CandleBlock) {
|
||||
stack.consume(1, player)
|
||||
level.playSound(null as Player?, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0F, 1.0F)
|
||||
level.setBlockAndUpdate(pos, state.setValue(CANDLE, getIdByCandle(candle)))
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
player.awardStat(Stats.ITEM_USED.get(item))
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
if (candleHit(hitResult) && hasCandle(state)) {
|
||||
val isFlintAndSteel = stack.`is`(Items.FLINT_AND_STEEL)
|
||||
if (canBeLit(state) && (isFlintAndSteel || stack.`is`(Items.FIRE_CHARGE))) {
|
||||
setCandleLit(level, state, pos, true)
|
||||
level.playSound(
|
||||
player,
|
||||
pos,
|
||||
if (isFlintAndSteel) SoundEvents.FLINTANDSTEEL_USE else SoundEvents.FIRECHARGE_USE,
|
||||
SoundSource.BLOCKS,
|
||||
1.0F,
|
||||
level.random.nextFloat() * 0.4F + 0.8F
|
||||
)
|
||||
|
||||
if (isFlintAndSteel) stack.hurtAndBreak(1, player, hand.toEquipmentSlot())
|
||||
else stack.consume(1, player)
|
||||
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else if (stack.isEmpty() && isCandleLit(state)) {
|
||||
extinguishCandle(player, state, level, pos)
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||
}
|
||||
|
||||
override fun animateTick(state: BlockState, level: Level, pos: BlockPos, random: RandomSource) {
|
||||
if (isCandleLit(state)) {
|
||||
AbstractCandleBlock.addParticlesAndSound(
|
||||
level,
|
||||
Vec3(
|
||||
pos.x + CANDLE_PARTICLE_POSITION.x,
|
||||
pos.y + CANDLE_PARTICLE_POSITION.y,
|
||||
pos.z + CANDLE_PARTICLE_POSITION.z
|
||||
),
|
||||
random
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProjectileHit(level: Level, state: BlockState, hit: BlockHitResult, projectile: Projectile) {
|
||||
if (!level.isClientSide && projectile.isOnFire() && canBeLit(state)) {
|
||||
setCandleLit(level, state, hit.getBlockPos(), true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player): BlockState {
|
||||
dropCandle(level, blockPos, blockState, player)
|
||||
return super.playerWillDestroy(level, blockPos, blockState, player)
|
||||
}
|
||||
|
||||
private fun dropCandle(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player?) {
|
||||
if (hasCandle(blockState) && !level.isClientSide && (!(player?.isCreative ?: false))) {
|
||||
val itemEntity = ItemEntity(
|
||||
level,
|
||||
blockPos.x + 0.5,
|
||||
blockPos.y + CAKE_HEIGHT,
|
||||
blockPos.z + 0.5,
|
||||
ItemStack(getCandleById(blockState.getValue(CANDLE)))
|
||||
)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playerEat(level: Level, pos: BlockPos, state: BlockState, player: Player): InteractionResult {
|
||||
if (!player.canEat(false)) {
|
||||
return InteractionResult.PASS
|
||||
} else {
|
||||
player.awardStat(Stats.EAT_CAKE_SLICE)
|
||||
player.getFoodData().eat(2, 0.1F)
|
||||
level.gameEvent(player, GameEvent.EAT, pos)
|
||||
eat(level, pos, state, player)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
fun eat(level: Level, pos: BlockPos, state: BlockState, player: Player?) {
|
||||
val bites = state.getValue(BITES) as Int
|
||||
if (bites < MAX_BITES) {
|
||||
level.setBlock(pos, state.setValue(BITES, bites + 1) as BlockState, UPDATE_ALL)
|
||||
} else {
|
||||
dropCandle(level, pos, state, player)
|
||||
level.removeBlock(pos, false)
|
||||
level.removeBlockEntity(pos)
|
||||
}
|
||||
level.playSound(null, player?.blockPosition() ?: pos, SoundEvents.GENERIC_EAT, if (player != null) SoundSource.PLAYERS else SoundSource.NEUTRAL)
|
||||
spawnEatParticles(level, pos)
|
||||
player?.let {
|
||||
level.gameEvent(it, GameEvent.BLOCK_DESTROY, pos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasCandle(state: BlockState) = state.getValue(CANDLE) > 0
|
||||
private fun isCandleLit(state: BlockState) = state.getValue(BlockStateProperties.LIT)
|
||||
private fun canBeLit(state: BlockState) = hasCandle(state) && !isCandleLit(state)
|
||||
|
||||
private fun setCandleLit(level: LevelAccessor, state: BlockState, pos: BlockPos, lit: Boolean) {
|
||||
level.setBlock(pos, state.setValue(BlockStateProperties.LIT, lit) as BlockState, UPDATE_ALL_IMMEDIATE)
|
||||
}
|
||||
|
||||
private fun extinguishCandle(player: Player?, state: BlockState, level: LevelAccessor, pos: BlockPos) {
|
||||
setCandleLit(level, state, pos, false)
|
||||
level.addParticle(
|
||||
ParticleTypes.SMOKE,
|
||||
pos.getX().toDouble() + CANDLE_PARTICLE_POSITION.x(),
|
||||
pos.getY().toDouble() + CANDLE_PARTICLE_POSITION.y(),
|
||||
pos.getZ().toDouble() + CANDLE_PARTICLE_POSITION.z(),
|
||||
0.0,
|
||||
0.1,
|
||||
0.0
|
||||
)
|
||||
|
||||
level.playSound(null as Player?, pos, SoundEvents.CANDLE_EXTINGUISH, SoundSource.BLOCKS, 1.0F, 1.0F)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
}
|
||||
|
||||
private fun candleHit(hit: BlockHitResult): Boolean {
|
||||
return hit.getLocation().y - hit.getBlockPos().getY().toDouble() > CAKE_HEIGHT
|
||||
}
|
||||
|
||||
fun spawnEatParticles(level: Level, pos: BlockPos) {
|
||||
val random = level.random
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
BlockParticleOption(ParticleTypes.BLOCK, CobblemonBlocks.POKE_SNACK.defaultBlockState()),
|
||||
pos.x + random.nextDouble(),
|
||||
pos.y + random.nextDouble() * CAKE_HEIGHT,
|
||||
pos.z + random.nextDouble(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,12 @@ package com.cobblemon.mod.common.block.campfirepot
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.item.CampfirePotItem
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -55,12 +56,16 @@ import org.jetbrains.annotations.Nullable
|
||||
import net.minecraft.world.level.block.CampfireBlock as MCCampfireBlock
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
|
||||
class CampfireBlock(settings: Properties, val isSoul: Boolean) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::CampfireBlock)
|
||||
val CODEC: MapCodec<CampfireBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
propertiesCodec(),
|
||||
Codec.BOOL.fieldOf("isSoul").forGetter(CampfireBlock::isSoul)
|
||||
).apply(it, ::CampfireBlock)
|
||||
}
|
||||
val ITEM_DIRECTION = DirectionProperty.create("item_facing")
|
||||
val LIT = BlockStateProperties.LIT
|
||||
var SOUL = BooleanProperty.create("soul")
|
||||
val POWERED = BlockStateProperties.POWERED
|
||||
|
||||
private val campfireAABB = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0)
|
||||
@@ -78,7 +83,6 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(LIT, true)
|
||||
.setValue(SOUL, false)
|
||||
.setValue(ITEM_DIRECTION, Direction.NORTH)
|
||||
.setValue(POWERED, false))
|
||||
}
|
||||
@@ -182,11 +186,9 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
blockEntity.setPotItem(ItemStack.EMPTY)
|
||||
level.playSoundServer(blockPos.bottomCenter, CobblemonSounds.CAMPFIRE_POT_RETRIEVE)
|
||||
|
||||
blockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
Containers.dropContents(level, blockPos, blockEntity)
|
||||
|
||||
val facing = blockState.getValue(FACING)
|
||||
val isSoul = blockState.getValue(SOUL)
|
||||
blockEntity.setRemoved()
|
||||
|
||||
val newBlockState = if (isSoul) Blocks.SOUL_CAMPFIRE.defaultBlockState().setValue(FACING, facing)
|
||||
@@ -201,7 +203,7 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, ITEM_DIRECTION, LIT, SOUL, POWERED)
|
||||
builder.add(FACING, ITEM_DIRECTION, LIT, POWERED)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
@@ -271,7 +273,7 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
return if (state.getValue(SOUL)) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
return if (isSoul) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
}
|
||||
|
||||
override fun neighborChanged(
|
||||
|
||||
+35
-7
@@ -36,6 +36,7 @@ import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import java.util.*
|
||||
|
||||
class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, ContainerListener {
|
||||
private val player: Player
|
||||
@@ -46,6 +47,8 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
val containerData: ContainerData
|
||||
private val recipeType: RecipeType<CookingPotRecipe> = CobblemonRecipeTypes.COOKING_POT_COOKING
|
||||
private val quickCheck = RecipeManager.createCheck(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
var currentActiveRecipe: RecipeHolder<CookingPotRecipeBase>? = null
|
||||
var previewItem = ItemStack.EMPTY
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory) : super(CobblemonMenuType.COOKING_POT, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
@@ -59,6 +62,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
this.player = playerInventory.player
|
||||
this.level = playerInventory.player.level()
|
||||
initializeSlots(playerInventory)
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory, container: CraftingContainer, containerData: ContainerData) : super(
|
||||
@@ -91,9 +95,6 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
addSlot(SeasoningSlot(this.container, slotIndex, 110 + index * 18, 18))
|
||||
}
|
||||
|
||||
addSlot(CookingPotPreviewSlot(this.container,
|
||||
CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT, resultSlotX, resultSlotY))
|
||||
|
||||
for ((index, _) in CampfireBlockEntity.Companion.PLAYER_INVENTORY_SLOTS.withIndex()) {
|
||||
val i = index / CampfireBlockEntity.Companion.PLAYER_INVENTORY_WIDTH
|
||||
val j = index % CampfireBlockEntity.Companion.PLAYER_INVENTORY_WIDTH
|
||||
@@ -148,14 +149,40 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun recipeMatches(recipe: RecipeHolder<CookingPotRecipeBase>): Boolean {
|
||||
val craftInput = CraftingInput.of(3,3, container.items.subList(1,10))
|
||||
val recipeValue = recipe.value()
|
||||
return if (recipeValue is CookingPotRecipeBase) {
|
||||
recipeValue.matches(container.asCraftInput(), level)
|
||||
recipeValue.matches(craftInput, level)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun recalculateRecipe() {
|
||||
val craftInput = CraftingInput.of(3,3, container.items.subList(1,10))
|
||||
fun <T : CookingPotRecipeBase> fetchRecipe(
|
||||
recipeType: RecipeType<T>
|
||||
): Optional<RecipeHolder<CookingPotRecipeBase>> {
|
||||
val optional = level.recipeManager.getRecipeFor(recipeType, craftInput, level)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return optional.map { it as RecipeHolder<CookingPotRecipeBase> }
|
||||
}
|
||||
|
||||
val recipe = fetchRecipe(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
.orElseGet { fetchRecipe(CobblemonRecipeTypes.COOKING_POT_SHAPELESS).orElse(null) }
|
||||
|
||||
currentActiveRecipe = recipe
|
||||
if (recipe != null) {
|
||||
previewItem = recipe.value.assemble(craftInput, level.registryAccess())
|
||||
recipe.value.applySeasoning(
|
||||
previewItem,
|
||||
container.items.subList(CampfireBlockEntity.SEASONING_SLOTS.first,
|
||||
CampfireBlockEntity.SEASONING_SLOTS.last + 1)
|
||||
.filterNotNull()
|
||||
.filter { !it.isEmpty })
|
||||
} else previewItem = ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun getResultSlotIndex(): Int {
|
||||
return CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
}
|
||||
@@ -187,7 +214,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun shouldMoveToInventory(slotIndex: Int): Boolean {
|
||||
return slotIndex != CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT && !CampfireBlockEntity.SEASONING_SLOTS.contains(slotIndex)
|
||||
return !CampfireBlockEntity.SEASONING_SLOTS.contains(slotIndex)
|
||||
}
|
||||
|
||||
override fun quickMoveStack(
|
||||
@@ -209,8 +236,8 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
slot.onQuickCraft(slotItemStack, itemStack);
|
||||
} else if (index in CampfireBlockEntity.Companion.PLAYER_INVENTORY_SLOTS || index in CampfireBlockEntity.Companion.PLAYER_HOTBAR_SLOTS) {
|
||||
if (Seasonings.isSeasoning(slotItemStack)) {
|
||||
if (!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.SEASONING_SLOTS.first, CampfireBlockEntity.Companion.SEASONING_SLOTS.last + 1, false) &&
|
||||
!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.first, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.last + 1, false)
|
||||
if (!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.first, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.last + 1, false) &&
|
||||
!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.SEASONING_SLOTS.first, CampfireBlockEntity.Companion.SEASONING_SLOTS.last + 1, false)
|
||||
) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
@@ -244,6 +271,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
|
||||
if (CampfireBlockEntity.CRAFTING_GRID_SLOTS.contains(dataSlotIndex) || CampfireBlockEntity.SEASONING_SLOTS.contains(dataSlotIndex)) recalculateRecipe()
|
||||
broadcastChanges()
|
||||
}
|
||||
|
||||
|
||||
-31
@@ -1,31 +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.block.campfirepot
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class CookingPotPreviewSlot(
|
||||
container: CraftingContainer,
|
||||
index: Int,
|
||||
x: Int,
|
||||
y: Int
|
||||
) : Slot(container, index, x, y) {
|
||||
override fun isActive(): Boolean {
|
||||
return container.getItem(RESULT_SLOT).isEmpty
|
||||
}
|
||||
|
||||
override fun isFake(): Boolean = true
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun mayPickup(player: Player): Boolean = false
|
||||
override fun isHighlightable() = false
|
||||
}
|
||||
+1
-2
@@ -31,12 +31,11 @@ class CookingPotResultSlot(
|
||||
val menu = player.containerMenu
|
||||
if (menu is CookingPotMenu) {
|
||||
menu.broadcastChanges()
|
||||
player.playNotifySound(CobblemonSounds.CAMPFIRE_POT_USE, SoundSource.MASTER, 1.0f, 1.0f)
|
||||
player.playNotifySound(CobblemonSounds.CAMPFIRE_POT_TAKE_ITEM, SoundSource.MASTER, 1.0f, 1.0f)
|
||||
}
|
||||
|
||||
super.onTake(player, stack)
|
||||
}
|
||||
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun isActive(): Boolean = hasItem()
|
||||
}
|
||||
+51
-41
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromSeasonings
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.item.components.PotComponent
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
@@ -39,11 +40,12 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.WorldlyContainer
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.StackedContents
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
@@ -51,6 +53,7 @@ import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
@@ -67,11 +70,10 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
const val RESULT_SLOT = 0
|
||||
val CRAFTING_GRID_SLOTS = 1..9
|
||||
val SEASONING_SLOTS = 10..12
|
||||
const val PREVIEW_ITEM_SLOT = 13
|
||||
const val ITEMS_SIZE = 14
|
||||
const val ITEMS_SIZE = 13
|
||||
|
||||
val PLAYER_INVENTORY_SLOTS = 14..40
|
||||
val PLAYER_HOTBAR_SLOTS = 41..49
|
||||
val PLAYER_INVENTORY_SLOTS = 13..39
|
||||
val PLAYER_HOTBAR_SLOTS = 40..48
|
||||
|
||||
const val CRAFTING_GRID_WIDTH = 3
|
||||
const val PLAYER_INVENTORY_WIDTH = 9
|
||||
@@ -89,15 +91,21 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
if (!level.isClientSide) return
|
||||
|
||||
val isLit = campfireBlockEntity.dataAccess.get(COOKING_PROGRESS_INDEX) > 0
|
||||
val isSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.runningSound.location)
|
||||
val isRunningSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.runningSound.location)
|
||||
val isAmbientSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.ambientSound.location)
|
||||
val containsItems = campfireBlockEntity.getSeasonings().isNotEmpty() || campfireBlockEntity.getIngredients().isNotEmpty()
|
||||
|
||||
if (isLit && !isSoundActive) {
|
||||
BlockEntitySoundTracker.play(
|
||||
pos,
|
||||
CancellableSoundInstance(campfireBlockEntity.runningSound, pos, true, 0.8f, 1.0f)
|
||||
)
|
||||
} else if (!isLit && isSoundActive) {
|
||||
if (containsItems) {
|
||||
if (isLit) {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.ambientSound.location)
|
||||
if (!isRunningSoundActive) BlockEntitySoundTracker.play(pos, CancellableSoundInstance(campfireBlockEntity.runningSound, pos, true, 1.0f, 1.0f))
|
||||
} else {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.runningSound.location)
|
||||
if (!isAmbientSoundActive) BlockEntitySoundTracker.play(pos, CancellableSoundInstance(campfireBlockEntity.ambientSound, pos, true, 1.0f, 1.0f))
|
||||
}
|
||||
} else {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.runningSound.location)
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.ambientSound.location)
|
||||
}
|
||||
|
||||
campfireBlockEntity.brothColor =
|
||||
@@ -149,7 +157,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
if (optionalRecipe == null) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -160,8 +167,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
recipe.applySeasoning(cookedItem, campfireBlockEntity.getSeasonings())
|
||||
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, cookedItem)
|
||||
|
||||
if (campfireBlockEntity.isLidOpen) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
@@ -171,7 +176,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
if (!ItemStack.isSameItemSameComponents(
|
||||
resultSlotItem,
|
||||
cookedItem
|
||||
) || resultSlotItem.count >= resultSlotItem.maxStackSize
|
||||
) || resultSlotItem.count + cookedItem.count > resultSlotItem.maxStackSize
|
||||
) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
@@ -191,11 +196,11 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
resultSlotItem.grow(cookedItem.count)
|
||||
}
|
||||
|
||||
campfireBlockEntity.consumeCraftingIngredients(recipe)
|
||||
campfireBlockEntity.consumeCraftingIngredients(recipe, level, pos, state, campfireBlockEntity)
|
||||
|
||||
level.playSoundServer(
|
||||
position = pos.bottomCenter,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_CRAFT,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_COOK,
|
||||
)
|
||||
|
||||
setChanged(level, pos, state);
|
||||
@@ -204,7 +209,8 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
}
|
||||
}
|
||||
|
||||
private val runningSound = CobblemonSounds.CAMPFIRE_POT_COOK
|
||||
private val runningSound = CobblemonSounds.CAMPFIRE_POT_ACTIVE
|
||||
private val ambientSound = CobblemonSounds.CAMPFIRE_POT_AMBIENT
|
||||
private var cookingProgress: Int = 0
|
||||
private var cookingTotalTime: Int = COOKING_TOTAL_TIME
|
||||
private var isLidOpen: Boolean = true
|
||||
@@ -270,32 +276,17 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
return particleStorm
|
||||
}
|
||||
|
||||
fun consumeCraftingIngredients(recipe: CookingPotRecipeBase) {
|
||||
fun consumeCraftingIngredients(recipe: CookingPotRecipeBase, level: Level, pos: BlockPos, state: BlockState, campfireBlockEntity: CampfireBlockEntity) {
|
||||
val remainderItems = mutableMapOf<Item, Int>() //This is so we don't spawn multiple entities for buckets
|
||||
|
||||
fun consumeItem(slot: Int) {
|
||||
val itemInSlot = getItem(slot)
|
||||
if (!itemInSlot.isEmpty) {
|
||||
when (itemInSlot.item) {
|
||||
Items.LAVA_BUCKET, Items.WATER_BUCKET, Items.MILK_BUCKET -> {
|
||||
// Replace with empty bucket
|
||||
setItem(slot, ItemStack(Items.BUCKET))
|
||||
}
|
||||
|
||||
Items.HONEY_BOTTLE -> {
|
||||
// TODO: Currently eats the empty bottles until the honey bottle stack is empty, replace with better system later.
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(slot, ItemStack.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Decrease the stack size by 1
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(slot, ItemStack.EMPTY) // Clear the slot if empty
|
||||
}
|
||||
}
|
||||
if (itemInSlot.item.hasCraftingRemainingItem()) {
|
||||
remainderItems[itemInSlot.item.craftingRemainingItem!!] = (remainderItems[itemInSlot.item.craftingRemainingItem!!] ?: 0) + 1
|
||||
}
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) setItem(slot, ItemStack.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +296,24 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
for (i in SEASONING_SLOTS.first..SEASONING_SLOTS.last) {
|
||||
if (recipe.seasoningProcessors.any { it.consumesItem(getItem(i)) }) consumeItem(i)
|
||||
}
|
||||
|
||||
val direction = state.getValue(CampfireBlock.ITEM_DIRECTION)
|
||||
val container = HopperBlockEntity.getContainerAt(level, pos.relative(direction))
|
||||
|
||||
for (remainder in remainderItems) {
|
||||
var remainderItem = ItemStack(remainder.key, remainder.value)
|
||||
|
||||
if (container != null) {
|
||||
remainderItem = HopperBlockEntity.addItem(campfireBlockEntity, container, remainderItem, direction.opposite)
|
||||
}
|
||||
|
||||
if (!remainderItem.isEmpty) {
|
||||
val spawnPos = Vec3.atCenterOf(pos).relative(direction, 0.7)
|
||||
val itemEntity = ItemEntity(level, spawnPos.x, spawnPos.y, spawnPos.z, remainderItem)
|
||||
itemEntity.setDeltaMovement(direction.stepX * 0.05, 0.0, direction.stepZ * 0.05)
|
||||
level.addFreshEntity(itemEntity) //Wanted to use default dispenser behavior but its too much speed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSeasonings(): List<ItemStack> =
|
||||
@@ -429,6 +438,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
if (level?.isClientSide == true) {
|
||||
BlockEntitySoundTracker.stop(blockPos, runningSound.location)
|
||||
BlockEntitySoundTracker.stop(blockPos, ambientSound.location)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-18
@@ -1,18 +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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class CandlePokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.CANDLE_POKE_CAKE, pos, state)
|
||||
@@ -1,30 +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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.LURE_CAKE, pos, state) {
|
||||
override val maxBites: Int
|
||||
get() = 20
|
||||
|
||||
/**
|
||||
* Combine all the [SpawnBait.Effect] values from the [baitEffectsComponent] data.
|
||||
*/
|
||||
fun getBaitEffectsFromLureCake(): List<SpawnBait.Effect> {
|
||||
return baitEffectsComponent?.effects?.mapNotNull(SpawnBaitEffects::getFromIdentifier)?.flatMap { it.effects }.orEmpty()
|
||||
}
|
||||
}
|
||||
@@ -1,18 +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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class PokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.POKE_CAKE, pos, state)
|
||||
+93
-96
@@ -8,130 +8,127 @@
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromFlavours
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait.Effect
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.item.components.BaitEffectsComponent
|
||||
import com.cobblemon.mod.common.item.components.FlavourComponent
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
import com.cobblemon.mod.common.item.components.IngredientComponent
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import kotlin.collections.orEmpty
|
||||
|
||||
open class CakeBlockEntity(
|
||||
type: BlockEntityType<*>,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : BlockEntity(type, pos, state) {
|
||||
open val maxBites: Int
|
||||
get() = 6
|
||||
open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) : TintBlockEntity(CobblemonBlockEntities.POKE_SNACK, pos, state) {
|
||||
|
||||
var flavourComponent: FlavourComponent? = null
|
||||
var baitEffectsComponent: BaitEffectsComponent? = null
|
||||
var foodColourComponent: FoodColourComponent? = null
|
||||
var bites: Int = 0
|
||||
var ingredientComponent: IngredientComponent? = null
|
||||
|
||||
fun initializeFromItemStack(itemStack: ItemStack) {
|
||||
flavourComponent = itemStack.get(CobblemonItemComponents.FLAVOUR)
|
||||
baitEffectsComponent = itemStack.get(CobblemonItemComponents.BAIT_EFFECTS)
|
||||
foodColourComponent = itemStack.get(CobblemonItemComponents.FOOD_COLOUR)
|
||||
}
|
||||
ingredientComponent = itemStack.get(CobblemonItemComponents.INGREDIENT)
|
||||
if (isLure()) baitEffectsComponent = itemStack.get(CobblemonItemComponents.BAIT_EFFECTS)
|
||||
|
||||
override fun saveAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
tag.putInt(DataKeys.CAKE_BITES, bites)
|
||||
flavourComponent?.let { component ->
|
||||
CobblemonItemComponents.FLAVOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_FLAVOUR, encodedTag)
|
||||
}
|
||||
}
|
||||
baitEffectsComponent?.let { component ->
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_BAIT_EFFECTS, encodedTag)
|
||||
}
|
||||
}
|
||||
foodColourComponent?.let { component ->
|
||||
CobblemonItemComponents.FOOD_COLOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_FOOD_COLOUR, encodedTag)
|
||||
}
|
||||
flavourComponent?.let {
|
||||
getColourMixFromFlavours(it.getDominantFlavours())?.let {
|
||||
setTint(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
bites = tag.getInt(DataKeys.CAKE_BITES)
|
||||
if (tag.contains(DataKeys.CAKE_FLAVOUR)) {
|
||||
CobblemonItemComponents.FLAVOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_FLAVOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
flavourComponent = component
|
||||
}
|
||||
fun isLure(): Boolean {
|
||||
val block = blockState.block
|
||||
if (block is PokeSnackBlock) {
|
||||
return block.isLure
|
||||
}
|
||||
if (tag.contains(DataKeys.CAKE_BAIT_EFFECTS)) {
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_BAIT_EFFECTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
baitEffectsComponent = component
|
||||
}
|
||||
}
|
||||
if (tag.contains(DataKeys.CAKE_FOOD_COLOUR)) {
|
||||
CobblemonItemComponents.FOOD_COLOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_FOOD_COLOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
foodColourComponent = component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener?>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
|
||||
val tag = CompoundTag()
|
||||
saveAdditional(tag, registries)
|
||||
return tag
|
||||
return false
|
||||
}
|
||||
|
||||
fun toItemStack(): ItemStack {
|
||||
val stack = ItemStack(this.blockState.block)
|
||||
if (stack.item == CobblemonItems.LURE_CAKE && baitEffectsComponent != null) {
|
||||
if (isLure() && baitEffectsComponent != null) {
|
||||
stack.set(CobblemonItemComponents.BAIT_EFFECTS, baitEffectsComponent)
|
||||
}
|
||||
if (flavourComponent != null) {
|
||||
stack.set(CobblemonItemComponents.FLAVOUR, flavourComponent)
|
||||
}
|
||||
if (foodColourComponent != null) {
|
||||
stack.set(CobblemonItemComponents.FOOD_COLOUR, foodColourComponent)
|
||||
}
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all the [SpawnBait.Effect] values from the [baitEffectsComponent] data.
|
||||
*/
|
||||
fun getBaitEffects(): List<Effect> {
|
||||
return baitEffectsComponent?.effects?.mapNotNull(SpawnBaitEffects::getFromIdentifier)?.flatMap { it.effects }.orEmpty()
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
flavourComponent?.let { component ->
|
||||
CobblemonItemComponents.FLAVOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.FLAVOUR, encodedTag)
|
||||
}
|
||||
}
|
||||
|
||||
baitEffectsComponent?.let { component ->
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.BAIT_EFFECTS, encodedTag)
|
||||
}
|
||||
}
|
||||
|
||||
ingredientComponent?.let { component ->
|
||||
CobblemonItemComponents.INGREDIENT.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.INGREDIENTS, encodedTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
if (tag.contains(DataKeys.FLAVOUR)) {
|
||||
CobblemonItemComponents.FLAVOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.FLAVOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
flavourComponent = component
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(DataKeys.BAIT_EFFECTS)) {
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.BAIT_EFFECTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
baitEffectsComponent = component
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(DataKeys.INGREDIENTS)) {
|
||||
CobblemonItemComponents.INGREDIENT.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.INGREDIENTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
ingredientComponent = component
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -13,4 +13,6 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class SweetIncenseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.INCENSE_SWEET, pos, state)
|
||||
class SweetIncenseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
|
||||
null // CobblemonBlockEntities.INCENSE_SWEET // TODO after 1.7
|
||||
, pos, state)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.IntTag
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
open class TintBlockEntity(type: BlockEntityType<*>?, blockPos: BlockPos, blockState: BlockState) : BlockEntity(type, blockPos, blockState) {
|
||||
companion object {
|
||||
const val TINT = "tint"
|
||||
}
|
||||
|
||||
var tint: Int? = null
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
tint?.let { tag.put(TINT, IntTag.valueOf(it)) }
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
if (tag.contains(TINT)) tint = tag.getInt(TINT)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
return this.saveWithoutMetadata(registryLookup)
|
||||
}
|
||||
|
||||
fun getTint() = tint ?: 0xFFFFFF
|
||||
|
||||
fun setTint(tintValue: Int) {
|
||||
level?.let {
|
||||
tint = tintValue
|
||||
setChanged()
|
||||
it.blockEntityChanged(blockPos)
|
||||
it.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_ALL)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,19 +20,20 @@ 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.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.entity.TintBlockEntity
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
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.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.block.CandlePokeCakeBlockEntityRenderer
|
||||
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.AprijuiceItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeSnackItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PonigiriItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.SinisterTeaItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
@@ -59,6 +60,8 @@ import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.color.block.BlockColor
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.MenuScreens
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
@@ -124,7 +127,7 @@ object CobblemonClient {
|
||||
|
||||
this.registerBlockEntityRenderers()
|
||||
registerBlockRenderTypes()
|
||||
//registerColors()
|
||||
registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
this.registerItemColors()
|
||||
@@ -196,16 +199,16 @@ object CobblemonClient {
|
||||
// .apply()
|
||||
}
|
||||
|
||||
/*
|
||||
fun registerColors() {
|
||||
this.implementation.registerBlockColors(BlockColorProvider { _, _, _, _ ->
|
||||
return@BlockColorProvider 0xE0A33A
|
||||
}, CobblemonBlocks.APRICORN_LEAVES)
|
||||
this.implementation.registerItemColors(ItemColorProvider { _, _ ->
|
||||
return@ItemColorProvider 0xE0A33A
|
||||
}, CobblemonItems.APRICORN_LEAVES)
|
||||
this.implementation.registerBlockColors(BlockColor { blockState, view, blockPos, tintIndex ->
|
||||
blockPos?.let { pos ->
|
||||
view?.getBlockEntity(pos)?.let { blockEntity ->
|
||||
if (blockEntity is TintBlockEntity) return@BlockColor blockEntity.getTint()
|
||||
}
|
||||
}
|
||||
return@BlockColor 0xFFFFFF
|
||||
}, CobblemonBlocks.POKE_SNACK, CobblemonBlocks.POKE_CAKE)
|
||||
}
|
||||
*/
|
||||
|
||||
private fun registerBlockRenderTypes() {
|
||||
|
||||
@@ -289,10 +292,10 @@ object CobblemonClient {
|
||||
CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.POTTED_SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.LURE_CAKE,
|
||||
CobblemonBlocks.POKE_CAKE,
|
||||
CobblemonBlocks.POKE_SNACK,
|
||||
CobblemonBlocks.LECTERN,
|
||||
CobblemonBlocks.CAMPFIRE,
|
||||
CobblemonBlocks.SOUL_CAMPFIRE,
|
||||
CobblemonBlocks.BLACK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.BLUE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.GREEN_CAMPFIRE_POT,
|
||||
@@ -349,12 +352,7 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LURE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(
|
||||
CobblemonBlockEntities.CANDLE_POKE_CAKE,
|
||||
::CandlePokeCakeBlockEntityRenderer
|
||||
)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -383,9 +381,8 @@ object CobblemonClient {
|
||||
|
||||
private fun registerItemColors() {
|
||||
implementation.registerItemColors(AprijuiceItemColorProvider, *CobblemonItems.aprijuices.toTypedArray())
|
||||
implementation.registerItemColors(PokeSnackItemColorProvider, CobblemonItems.POKE_SNACK, CobblemonItems.POKE_CAKE)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.LURE_CAKE)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_CAKE)
|
||||
implementation.registerItemColors(PonigiriItemColorProvider, CobblemonItems.PONIGIRI)
|
||||
implementation.registerItemColors(SinisterTeaItemColorProvider, CobblemonItems.SINISTER_TEA)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.aspectParticleMap
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.ParticleData
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnClient
|
||||
|
||||
@@ -100,13 +100,14 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
// Don't render if duration config set to 0 or less
|
||||
if (maxDurationFrames <= 0F) return
|
||||
|
||||
val rideBehaviourSettings = riddenEntity.ridingBehaviourSettings
|
||||
val rideBehaviourKey = rideBehaviourSettings?.key
|
||||
val rideBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
|
||||
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
|
||||
val rideBehaviourKey = rideBehaviourSettings.key
|
||||
|
||||
// Update current ride behaviour if changed
|
||||
// If composite, get active behaviour instead
|
||||
if (rideBehaviourKey == CompositeBehaviour.KEY) {
|
||||
val activeBehaviour = (riddenEntity.ridingState as CompositeState).activeBehaviour.get()
|
||||
val activeBehaviour = (ridingBehaviourState as CompositeState).activeBehaviour.get()
|
||||
if (currentBehaviourKey != activeBehaviour) {
|
||||
currentBehaviourKey = activeBehaviour
|
||||
resetOverlayState()
|
||||
|
||||
+2
-2
@@ -201,10 +201,10 @@ class BattleMoveSelection(
|
||||
val move = moveTiles.find { it.isHovered(mouseX, mouseY) }
|
||||
val gimmick = gimmickButtons.find { it.isHovered(mouseX, mouseY) }
|
||||
if (move != null) {
|
||||
if(this.request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
if(request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
move.onClick()
|
||||
} else {
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move))
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move, move.response.gimmickID, move.move.gimmickMove))
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
return true
|
||||
|
||||
+7
-3
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.InBattleGimmickMove
|
||||
import com.cobblemon.mod.common.battles.InBattleMove
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
@@ -43,7 +44,9 @@ import kotlin.math.sin
|
||||
class BattleTargetSelection(
|
||||
battleGUI: BattleGUI,
|
||||
request: SingleActionRequest,
|
||||
val move: InBattleMove
|
||||
val move: InBattleMove,
|
||||
val gimmickID: String?,
|
||||
gimmickMove: InBattleGimmickMove?
|
||||
) : BattleActionSelection(
|
||||
battleGUI = battleGUI,
|
||||
request = request,
|
||||
@@ -81,9 +84,10 @@ class BattleTargetSelection(
|
||||
|
||||
val targets = request.activePokemon.getAllActivePokemon()
|
||||
|
||||
val targetType = if (gimmickID != null && gimmickMove != null) gimmickMove.target else move.target
|
||||
val backButton = BattleBackButton(x + 9F, Minecraft.getInstance().window.guiScaledHeight - 22F)
|
||||
val selectableTargetList = move.target.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(move.target) else null
|
||||
val selectableTargetList = targetType.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(targetType) else null
|
||||
|
||||
val baseTiles = targets.mapIndexed { index, target ->
|
||||
val isAlly = target.isAllied(request.activePokemon)
|
||||
|
||||
+26
-13
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.client.gui.cookingpot
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.campfirepot.SeasoningSlot
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.integration.jei.CobblemonJeiPlugin
|
||||
import com.cobblemon.mod.common.integration.jei.cooking.CampfirePotRecipeCategory
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
|
||||
@@ -39,6 +39,7 @@ import net.minecraft.world.inventory.ClickType
|
||||
import net.minecraft.world.inventory.RecipeBookMenu
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@@ -174,24 +175,36 @@ class CookingPotScreen(
|
||||
this.recipeBookComponent.renderGhostRecipe(context, this.leftPos, this.topPos, true, partialTicks)
|
||||
}
|
||||
|
||||
val resultSlot = menu.slots[menu.resultSlotIndex]
|
||||
val optionalRecipe = menu.currentActiveRecipe
|
||||
if (optionalRecipe != null && !optionalRecipe.value.result.isEmpty && !resultSlot.hasItem()) {
|
||||
val resultItem = menu.previewItem
|
||||
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 0.5F)
|
||||
|
||||
context.renderFakeItem(resultItem, leftPos + resultSlot.x, topPos + resultSlot.y);
|
||||
context.renderItemDecorations(font, resultItem, leftPos + resultSlot.x, topPos + resultSlot.y)
|
||||
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F)
|
||||
RenderSystem.disableBlend()
|
||||
|
||||
if (isHovering(resultSlot.x, resultSlot.y, 16, 16, mouseX.toDouble(), mouseY.toDouble()) && menu.carried.isEmpty) {
|
||||
context.renderTooltip(font, getTooltipFromContainerItem(resultItem), resultItem.tooltipImage, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
this.renderTooltip(context, mouseX, mouseY)
|
||||
this.recipeBookComponent.renderTooltip(context, this.leftPos, this.topPos, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun renderSlot(guiGraphics: GuiGraphics, slot: Slot) {
|
||||
if (slot.index != PREVIEW_ITEM_SLOT) {
|
||||
super.renderSlot(guiGraphics, slot)
|
||||
return
|
||||
if (slot is SeasoningSlot && !menu.carried.isEmpty && !slot.mayPlace(menu.carried)) {
|
||||
val x = slot.x
|
||||
val y = slot.y
|
||||
guiGraphics.fill(x,y,x+16,y+16,822018048)
|
||||
}
|
||||
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 0.5F)
|
||||
|
||||
guiGraphics.renderFakeItem(slot.item, slot.x, slot.y);
|
||||
guiGraphics.renderItemDecorations(minecraft!!.font, slot.item, slot.x, slot.y)
|
||||
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F)
|
||||
RenderSystem.disableBlend()
|
||||
super.renderSlot(guiGraphics, slot)
|
||||
}
|
||||
|
||||
override fun recipesUpdated() {
|
||||
|
||||
+8
-6
@@ -146,19 +146,19 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
val maxStamina = maxStaminaInput.value.toIntOrNull() ?: getRidingStatRange(RidingStat.STAMINA).last
|
||||
|
||||
if (minSpeed < maxSpeed) {
|
||||
vehicle.rideProp.stats[RidingStat.SPEED]?.ranges?.put(ridingStyle, minSpeed..maxSpeed)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.SPEED, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minAcceleration < maxAcceleration) {
|
||||
vehicle.rideProp.stats[RidingStat.ACCELERATION]?.ranges?.put(ridingStyle, minAcceleration..maxAcceleration)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.ACCELERATION, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minSkill < maxSkill) {
|
||||
vehicle.rideProp.stats[RidingStat.SKILL]?.ranges?.put(ridingStyle, minSkill..maxSkill)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.SKILL, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minJump < maxJump) {
|
||||
vehicle.rideProp.stats[RidingStat.JUMP]?.ranges?.put(ridingStyle, minJump..maxJump)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.JUMP, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minStamina < maxStamina) {
|
||||
vehicle.rideProp.stats[RidingStat.STAMINA]?.ranges?.put(ridingStyle, minStamina..maxStamina)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.STAMINA, minSpeed..maxSpeed)
|
||||
}
|
||||
|
||||
CobblemonNetwork.sendToServer(
|
||||
@@ -198,7 +198,9 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
}
|
||||
|
||||
fun getRidingStatRange(ridingStat: RidingStat): IntRange {
|
||||
return vehicle.rideProp.stats[ridingStat]?.ranges?.get(ridingStyle) ?: 0..0
|
||||
// TODO
|
||||
return 0..0
|
||||
// return vehicle.rideProp.stats[ridingStat]?.ranges?.get(ridingStyle) ?: 0..0
|
||||
}
|
||||
|
||||
fun getScaledWidth() = Minecraft.getInstance().window.guiScaledWidth
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
@@ -25,7 +26,7 @@ class SummaryTab(
|
||||
val label: MutableComponent? = null,
|
||||
val icon: ResourceLocation? = null,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, WIDTH, HEIGHT, label, onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
): Button(pX, pY, WIDTH, HEIGHT, label ?: Component.empty(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
companion object {
|
||||
private const val WIDTH = 39
|
||||
private const val HEIGHT = 13
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.effect
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
|
||||
object PokeSnackBlockParticlesHandler : ClientNetworkPacketHandler<PokeSnackBlockParticlesPacket> {
|
||||
override fun handle(packet: PokeSnackBlockParticlesPacket, client: Minecraft) {
|
||||
val level = Minecraft.getInstance().level ?: return
|
||||
|
||||
val blockState = level.getBlockState(packet.blockPos)
|
||||
val block = blockState.block
|
||||
if (block is PokeSnackBlock) {
|
||||
block.spawnEatParticles(level, packet.blockPos)
|
||||
}
|
||||
|
||||
val entityPos = packet.entityPos
|
||||
if (entityPos != null) {
|
||||
val random = level.random
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
ParticleTypes.POOF,
|
||||
entityPos.x + random.nextDouble(),
|
||||
entityPos.y + random.nextDouble(),
|
||||
entityPos.z + random.nextDouble(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ object SaccharineLogBlockParticlesHandler : ClientNetworkPacketHandler<Saccharin
|
||||
val entityPos = packet.entityPos
|
||||
if (entityPos != null) {
|
||||
val random = level.random
|
||||
repeat(10) {
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
ParticleTypes.POOF,
|
||||
entityPos.x + random.nextDouble(),
|
||||
|
||||
+4
-2
@@ -21,8 +21,10 @@ object ClientboundUpdateRidingStateHandler : ClientNetworkPacketHandler<Clientbo
|
||||
if (entity !is PokemonEntity) return@executeIfPossible
|
||||
if (entity.controllingPassenger == player) return@executeIfPossible
|
||||
val buffer = packet.data ?: return@executeIfPossible
|
||||
if (entity.riding?.key != packet.behaviour) return@executeIfPossible
|
||||
entity.ridingState?.decode(buffer)
|
||||
if (entity.ridingController?.context?.settings?.key != packet.behaviour) {
|
||||
entity.ridingController?.changeBehaviour(packet.behaviour)
|
||||
}
|
||||
entity.ridingController?.context?.state?.decode(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-295
@@ -1,295 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.block.CakeBlock
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
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 kotlin.random.Random
|
||||
import net.minecraft.client.Minecraft
|
||||
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.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.inventory.InventoryMenu.BLOCK_ATLAS
|
||||
import org.joml.Vector3d
|
||||
|
||||
open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<CakeBlockEntity> {
|
||||
|
||||
companion object {
|
||||
const val PIXEL_SHIFT = 1.0 / 16.0
|
||||
const val WHITE_OPAQUE_COLOR = 0xFFFFFFFF.toInt()
|
||||
|
||||
const val ATLAS_START = 0.0f
|
||||
const val ATLAS_END = 1.0f
|
||||
const val HEIGHT = 0.5f
|
||||
const val TOP_LAYER_HEIGHT = 0.5625f
|
||||
const val BOTTOM_LAYER_HEIGHT = 0.0f
|
||||
|
||||
object DefaultCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
object HeartCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer4", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer5", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
val CAKE_LAYOUTS = listOf(DefaultCakeLayout, HeartCakeLayout)
|
||||
|
||||
fun loadTexture(textureName: String): TextureAtlasSprite {
|
||||
return Minecraft.getInstance().getTextureAtlas(BLOCK_ATLAS).apply(cobblemonResource(textureName))
|
||||
}
|
||||
}
|
||||
|
||||
interface CakeLayout {
|
||||
val topLayers: List<CakeTexture>
|
||||
val sideLayers: List<CakeTexture>
|
||||
val bottomLayers: List<CakeTexture>
|
||||
val innerLayers: List<CakeTexture>
|
||||
}
|
||||
|
||||
class CakeTexture(textureName: String, val color: LayerColor) {
|
||||
|
||||
val texture = loadTexture("block/food/${textureName}")
|
||||
|
||||
fun getColor(primaryColor: Int, secondaryColor: Int, tertiaryColor: Int): Int {
|
||||
return when (color) {
|
||||
LayerColor.PRIMARY -> primaryColor
|
||||
LayerColor.SECONDARY -> secondaryColor
|
||||
LayerColor.TERTIARY -> tertiaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class LayerColor {
|
||||
PRIMARY, SECONDARY, TERTIARY
|
||||
}
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
val foodColourComponent = blockEntity.foodColourComponent
|
||||
renderCake(blockEntity, foodColourComponent, poseStack, bufferSource, packedLight, blockEntity.bites)
|
||||
}
|
||||
|
||||
fun renderCake(
|
||||
blockEntity: CakeBlockEntity,
|
||||
foodColourComponent: FoodColourComponent?,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
bites: Int,
|
||||
) {
|
||||
val seedString = foodColourComponent?.colours?.joinToString()
|
||||
val random = Random(seedString.hashCode())
|
||||
val cakeLayout: CakeLayout = CAKE_LAYOUTS[random.nextInt(CAKE_LAYOUTS.size)]
|
||||
|
||||
val visualStages = 7f
|
||||
val maxBites = blockEntity.maxBites.toFloat()
|
||||
val bitesPerStage = ((maxBites + 1) / visualStages).coerceAtLeast(1f)
|
||||
val visualStage = (bites / bitesPerStage).toInt().coerceIn(0, visualStages.toInt() - 1)
|
||||
val biteOffset = (15f - 2f * visualStage) / 16f
|
||||
val clippedWidth = (ATLAS_END - ATLAS_START) * biteOffset
|
||||
val topLayer = cakeLayout.topLayers.first().texture
|
||||
val uClipped = topLayer.u0 + (topLayer.u1 - topLayer.u0) * biteOffset
|
||||
|
||||
val vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout())
|
||||
val primaryColor = foodColourComponent?.colours?.getOrNull(0)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
val secondaryColor = foodColourComponent?.colours?.getOrNull(1)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
val tertiaryColor = foodColourComponent?.colours?.getOrNull(2)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.5, 0.5)
|
||||
val facing = blockEntity.blockState.getValue(CakeBlock.FACING)
|
||||
val angle = when (facing) {
|
||||
Direction.NORTH -> 180f
|
||||
Direction.SOUTH -> 0f
|
||||
Direction.WEST -> 90f
|
||||
Direction.EAST -> -90f
|
||||
else -> 0f
|
||||
}
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(angle))
|
||||
|
||||
poseStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
poseStack.pushPose()
|
||||
for (layer in cakeLayout.topLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, TOP_LAYER_HEIGHT, ATLAS_START, clippedWidth, TOP_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
poseStack.popPose()
|
||||
|
||||
val translations = arrayOf(
|
||||
Vector3d(-1.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
Vector3d(-1.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
)
|
||||
|
||||
val rotationsY = arrayOf(
|
||||
Axis.YP.rotationDegrees(90f),
|
||||
Axis.YP.rotationDegrees(0f),
|
||||
Axis.YP.rotationDegrees(-90f),
|
||||
Axis.YP.rotationDegrees(-180f)
|
||||
)
|
||||
|
||||
val cakeIsFull = bites == 0
|
||||
|
||||
for (i in translations.indices) {
|
||||
poseStack.pushPose()
|
||||
poseStack.mulPose(rotationsY[i])
|
||||
poseStack.translate(translations[i].x, translations[i].y, translations[i].z)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
|
||||
|
||||
if (i == 0 && !cakeIsFull) {
|
||||
poseStack.translate(0.0, -visualStage * PIXEL_SHIFT * 2, 0.0)
|
||||
for (layer in cakeLayout.innerLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 0 || i == 1) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, clippedWidth, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 2) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 3) {
|
||||
val clippedStartX = ATLAS_START + (ATLAS_END - ATLAS_START) * (1 - biteOffset)
|
||||
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
val clippedUStart = texture.u0 + (texture.u1 - texture.u0) * (1 - biteOffset)
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
clippedStartX, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
clippedUStart, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.0, 0.5)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(180f))
|
||||
poseStack.translate(-0.5, 0.0, -0.5)
|
||||
|
||||
for (layer in cakeLayout.bottomLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, BOTTOM_LAYER_HEIGHT, ATLAS_START, clippedWidth, BOTTOM_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun drawQuad(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x0: Float, y0: Float, z0: Float,
|
||||
x1: Float, y1: Float, z1: Float,
|
||||
u0: Float, v0: Float, u1: Float, v1: Float,
|
||||
packedLight: Int, color: Int,
|
||||
) {
|
||||
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color)
|
||||
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y1, z1, u1, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y0, z0, u1, v0, packedLight, color)
|
||||
}
|
||||
|
||||
private fun drawVertex(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x: Float, y: Float, z: Float,
|
||||
u: Float, v: Float,
|
||||
packedLight: Int, color: Int
|
||||
) {
|
||||
builder.addVertex(poseStack.last().pose(), x, y, z)
|
||||
.setColor(color)
|
||||
.setUv(u, v)
|
||||
.setLight(packedLight)
|
||||
.setNormal(0f, 1f, 0f)
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +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.block
|
||||
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.getCandleByMapColorId
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
|
||||
class CandlePokeCakeBlockEntityRenderer(
|
||||
ctx: BlockEntityRendererProvider.Context,
|
||||
) : CakeBlockEntityRenderer(ctx) {
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
super.render(blockEntity, partialTick, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, TOP_LAYER_HEIGHT.toDouble(), 0.0)
|
||||
|
||||
val candleBlock = getCandleByMapColorId(blockEntity.blockState.getValue(CANDLE_COLOR))
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
candleBlock.defaultBlockState().setValue(LIT, blockEntity.blockState.getValue(LIT)),
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
packedOverlay
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.api.berry.Berries
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock.Companion.CANDLE
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock.Companion.getCandleById
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.atlas.CobblemonAtlases.BERRY_SPRITE_ATLAS
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.setPosition
|
||||
import com.cobblemon.mod.common.util.math.geometry.Axis
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
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.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
|
||||
open class PokeSnackBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<PokeSnackBlockEntity> {
|
||||
override fun render(blockEntity: PokeSnackBlockEntity, partialTick: Float, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
renderCandle(blockEntity, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
renderBerries(blockEntity, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
}
|
||||
|
||||
fun renderCandle(blockEntity: PokeSnackBlockEntity, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
val candleId = blockEntity.blockState.getValue(CANDLE)
|
||||
|
||||
if (candleId > 0) {
|
||||
val candleBlock = getCandleById(blockEntity.blockState.getValue(CANDLE))
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, PokeSnackBlock.CAKE_HEIGHT, 0.0)
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
candleBlock.defaultBlockState().setValue(LIT, blockEntity.blockState.getValue(LIT)),
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
packedOverlay
|
||||
)
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
fun renderBerries(blockEntity: PokeSnackBlockEntity, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
// RenderType.entityCutoutNoCull for shading, as shading is absent in RenderType CobblemonRenderLayers.BERRY_LAYER
|
||||
val buffer = bufferSource.getBuffer(
|
||||
RenderType.entityCutoutNoCull(BERRY_SPRITE_ATLAS.textureAtlas.location())
|
||||
)
|
||||
|
||||
val berryIngredients = (blockEntity.ingredientComponent?.ingredientIds?.take(3) ?: listOf())
|
||||
.mapNotNull { Berries.getByIdentifier(it) }
|
||||
|
||||
berryIngredients.forEachIndexed { index, berry ->
|
||||
val model = BerryModelRepository.modelOf(berry.fruitModelIdentifier) ?: return
|
||||
|
||||
when (berryIngredients.size) {
|
||||
1 -> {
|
||||
val pos = berry.pokeSnackPositionings[0].position
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, 8F)
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, 8F)
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(180.0).toFloat(),
|
||||
Math.toRadians(0.0).toFloat(),
|
||||
Math.toRadians(0.0).toFloat()
|
||||
)
|
||||
}
|
||||
2 -> {
|
||||
val pos = berry.pokeSnackPositionings[0].position
|
||||
val rot = berry.pokeSnackPositionings[0].rotation
|
||||
val isFirst = index == 0
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, if (isFirst) pos.z.toFloat() else (16F - pos.z.toFloat()))
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(
|
||||
if (isFirst) rot.x else (180.0 + (180.0 - rot.x))
|
||||
).toFloat(),
|
||||
Math.toRadians(rot.y).toFloat(),
|
||||
Math.toRadians(rot.z).toFloat()
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val pos = berry.pokeSnackPositionings[index].position
|
||||
val rot = berry.pokeSnackPositionings[index].rotation
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, pos.z.toFloat())
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(rot.x).toFloat(),
|
||||
Math.toRadians(rot.y).toFloat(),
|
||||
Math.toRadians(rot.z).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
model.render(poseStack, buffer, packedLight, packedOverlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromFlavours
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object PokeSnackItemColorProvider : ItemColor {
|
||||
private const val BASE_LAYER_INDEX = 0
|
||||
|
||||
override fun getColor(stack: ItemStack, layer: Int): Int {
|
||||
val flavourComponent = stack.get(CobblemonItemComponents.FLAVOUR) ?: return -1
|
||||
if (layer == BASE_LAYER_INDEX) {
|
||||
val colorMix = getColourMixFromFlavours(flavourComponent.getDominantFlavours())
|
||||
if (colorMix != null) return colorMix
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -32,13 +32,10 @@ object MountedLivingEntityRenderer {
|
||||
) {
|
||||
if (entity.vehicle !is Rideable) return
|
||||
val matrix = stack.last().pose()
|
||||
|
||||
val seatIndex = pokemon.passengers.indexOf(entity)
|
||||
val seat = pokemon.seats[seatIndex]
|
||||
val delegate = pokemon.delegate as PokemonClientDelegate
|
||||
val locator = delegate.locatorStates[seat.locator]
|
||||
val locator = delegate.locatorStates[delegate.getSeatLocator(entity)]
|
||||
|
||||
//Positions player
|
||||
//Positions entity
|
||||
if (locator != null) {
|
||||
MountedPokemonAnimationRenderController.setup(pokemon, partialTicks)
|
||||
|
||||
|
||||
+7
-7
@@ -113,13 +113,13 @@ object CobblemonModelPredicateRegistry {
|
||||
"minecraft:brown_mushroom" -> 0.07f
|
||||
"minecraft:pumpkin" -> 0.08f
|
||||
"cobblemon:rice" -> 0.09f
|
||||
"minecraft:raw_chicken" -> 0.10f
|
||||
"minecraft:raw_cod" -> 0.11f
|
||||
"minecraft:raw_mutton" -> 0.12f
|
||||
"minecraft:raw_porkchop" -> 0.13f
|
||||
"minecraft:raw_rabbit" -> 0.14f
|
||||
"minecraft:raw_salmon" -> 0.15f
|
||||
"minecraft:raw_beef" -> 0.16f
|
||||
"minecraft:chicken" -> 0.10f
|
||||
"minecraft:cod" -> 0.11f
|
||||
"minecraft:mutton" -> 0.12f
|
||||
"minecraft:porkchop" -> 0.13f
|
||||
"minecraft:rabbit" -> 0.14f
|
||||
"minecraft:salmon" -> 0.15f
|
||||
"minecraft:beef" -> 0.16f
|
||||
"minecraft:egg" -> 0.17f
|
||||
"minecraft:poisonous_potato" -> 0.18f
|
||||
"minecraft:rotten_flesh" -> 0.19f
|
||||
|
||||
+44
-1
@@ -8,17 +8,26 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel.Companion.getOrMakeShard
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import net.minecraft.client.model.EntityModel
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.apache.commons.lang3.tuple.Triple
|
||||
|
||||
/**
|
||||
* A wrapping around a [PosableModel] that presents as an [EntityModel]. This is used to continue using
|
||||
@@ -29,13 +38,46 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since January 5th, 2024
|
||||
*/
|
||||
abstract class PosableEntityModel<T : Entity>(
|
||||
renderTypeFunc: (ResourceLocation) -> RenderType = RenderType::entityCutout
|
||||
renderTypeFunc: (ResourceLocation) -> RenderType = {
|
||||
val textures = ImmutableList.of(
|
||||
Triple.of(it, false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
)
|
||||
makeLayer(RenderStateShard.MultiTextureStateShard(textures), false, false, false)
|
||||
}
|
||||
) : EntityModel<T>(renderTypeFunc) {
|
||||
val context: RenderContext = RenderContext().also {
|
||||
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
|
||||
}
|
||||
lateinit var posableModel: PosableModel
|
||||
|
||||
companion object {
|
||||
fun makeLayer(texture: RenderStateShard.EmptyTextureStateShard, emissive: Boolean, translucent: Boolean, translucentCull: Boolean): RenderType {
|
||||
val multiPhaseParameters: RenderType.CompositeState = RenderType.CompositeState.builder()
|
||||
.setShaderState(getOrMakeShard() as RenderStateShard.ShaderStateShard)
|
||||
.setTextureState(texture)
|
||||
.setTransparencyState(if (translucent) RenderStateShard.TRANSLUCENT_TRANSPARENCY else RenderStateShard.NO_TRANSPARENCY)
|
||||
.setLightmapState(if (!emissive) RenderStateShard.LIGHTMAP else RenderStateShard.LightmapStateShard(false))
|
||||
.setCullState(RenderStateShard.CULL)
|
||||
.setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE)
|
||||
.setOverlayState(RenderStateShard.OVERLAY)
|
||||
.createCompositeState(false)
|
||||
|
||||
return RenderType.create(
|
||||
"cobblemon_entity_layer",
|
||||
DefaultVertexFormat.NEW_ENTITY,
|
||||
VertexFormat.Mode.QUADS,
|
||||
256,
|
||||
true,
|
||||
translucent,
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderToBuffer(
|
||||
stack: PoseStack,
|
||||
buffer: VertexConsumer,
|
||||
@@ -49,6 +91,7 @@ abstract class PosableEntityModel<T : Entity>(
|
||||
// We're using the living entity renderer, ergo we need to apply a Y offset of 24 model units (1.5 blocks) for reasons only God can tell us.
|
||||
stack.translate(0.0, 1.5, 0.0)
|
||||
}
|
||||
// RenderSystem.setShader { CobblemonShaders.OVERLAY }
|
||||
posableModel.render(context, stack, buffer, packedLight, overlay, color)
|
||||
}
|
||||
|
||||
|
||||
+32
-11
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.ModelLayer
|
||||
@@ -33,6 +34,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
@@ -40,26 +42,33 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.plus
|
||||
import com.cobblemon.mod.common.util.toRGBA
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.client.renderer.GameRenderer
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.ShaderInstance
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.apache.commons.lang3.tuple.Triple
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Vector3f
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
/**
|
||||
* A model that can be posed and animated using [PoseAnimation]s and [ActiveAnimation]s. This
|
||||
@@ -414,6 +423,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
|
||||
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
|
||||
RenderSystem.setShaderTexture(0, texture.id)
|
||||
RenderSystem.setShaderTexture(1, cobblemonResource("textures/tera_layer.png"))
|
||||
}, Runnable {
|
||||
texture.close() // Cleanup
|
||||
})
|
||||
@@ -453,13 +463,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
if (provider != null) {
|
||||
for (layer in currentLayers) {
|
||||
var renderLayer : RenderType
|
||||
var texture: ResourceLocation? = null
|
||||
if (layer.texture is AnimatedModelTextureSupplier && layer.texture.interpolation) {
|
||||
//Handle Interpolation
|
||||
val texture = layer.texture.interpolatedTexture(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = makeLayer(DynamicStateShard(texture), layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
else {
|
||||
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = getLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
val consumer = provider.getBuffer(renderLayer)
|
||||
@@ -490,18 +501,21 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
return ItemDisplayContext.valueOf(displayContextString)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var test: Any? = null
|
||||
|
||||
fun getOrMakeShard(): Any {
|
||||
if (test == null) {
|
||||
test = RenderStateShard.ShaderStateShard({ CobblemonShaders.OVERLAY })
|
||||
}
|
||||
return test!!
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates a [RenderType] by the power of god and anime. Only possible thanks to 100 access wideners. */
|
||||
fun makeLayer(texture: RenderStateShard.EmptyTextureStateShard, emissive: Boolean, translucent: Boolean, translucentCull: Boolean): RenderType {
|
||||
val multiPhaseParameters: RenderType.CompositeState = RenderType.CompositeState.builder()
|
||||
.setShaderState(
|
||||
when {
|
||||
emissive && translucent -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_EMISSIVE_SHADER
|
||||
!emissive && translucent && translucentCull -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER
|
||||
!emissive && translucent -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_SHADER
|
||||
!emissive && !translucent -> RenderStateShard.RENDERTYPE_ENTITY_CUTOUT_SHADER
|
||||
else -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_EMISSIVE_SHADER // This one should be changed to maybe a custom shader? Translucent stuffs with things
|
||||
}
|
||||
)
|
||||
.setShaderState(getOrMakeShard() as RenderStateShard.ShaderStateShard)
|
||||
.setTextureState(texture)
|
||||
.setTransparencyState(if (translucent) RenderStateShard.TRANSLUCENT_TRANSPARENCY else RenderStateShard.NO_TRANSPARENCY)
|
||||
.setLightmapState(if (!emissive) RenderStateShard.LIGHTMAP else RenderStateShard.LightmapStateShard(false))
|
||||
@@ -528,7 +542,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
} else if (!emissive && !translucentCull) {
|
||||
RenderType.entityTranslucent(texture)
|
||||
} else {
|
||||
makeLayer(RenderStateShard.TextureStateShard(texture, false, false), emissive = emissive, translucent = translucent, translucentCull = translucentCull)
|
||||
val texture = RenderStateShard.MultiTextureStateShard(ImmutableList.of(
|
||||
Triple.of(texture, false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
))
|
||||
makeLayer(texture, emissive = emissive, translucent = translucent, translucentCull = translucentCull)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -271,7 +271,7 @@ abstract class PosableState : Schedulable {
|
||||
|
||||
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
|
||||
//?: return@addFunction DoubleValue(0.0)
|
||||
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0) * -1.0)
|
||||
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
|
||||
}
|
||||
.addFunction("input_up") { params ->
|
||||
val lookBackTick = params.getInt(0)
|
||||
|
||||
+12
-12
@@ -33,6 +33,7 @@ class PitchTiltAnimation(
|
||||
) : PoseAnimation() {
|
||||
companion object {
|
||||
const val PREVIOUS_ANGLE = "previous_pitch_tilt_angle"
|
||||
const val CORRECTED_ANGLE = "corrected_pitch_tilt_angle"
|
||||
const val PITCHED_TILT = "has_pitched_tilt"
|
||||
}
|
||||
|
||||
@@ -52,10 +53,9 @@ class PitchTiltAnimation(
|
||||
return
|
||||
}
|
||||
val delta = entity.deltaMovement
|
||||
// The angleOfMotion should be 0 in the case that horizontal movement is 1 and vertical movement is 0.
|
||||
// It should be the angle in degrees that becomes higher as the entity's motion becomes more upwards, and negative as the entity's motion becomes more downwards
|
||||
val vertical = delta.y
|
||||
val horizontal = delta.horizontalDistance()
|
||||
// It should be the angle in degrees that becomes higher as the entity's motion becomes more upwards, and negative as the entity's motion becomes more downwards
|
||||
val angleOfMotion = if (horizontal == 0.0) {
|
||||
when {
|
||||
vertical > 0 -> maxPitch
|
||||
@@ -64,20 +64,20 @@ class PitchTiltAnimation(
|
||||
}
|
||||
} else {
|
||||
Mth.atan2(vertical, horizontal).toDegrees()
|
||||
}.coerceIn(minPitch, maxPitch)
|
||||
|
||||
val maxChangePerTick = this.maxChangePerTick * intensity
|
||||
}.coerceIn(minPitch, maxPitch) * -1
|
||||
|
||||
val lastAngle = state.numbers[PREVIOUS_ANGLE] ?: 0F
|
||||
val interpolatedAngle = if (angleOfMotion - lastAngle > maxChangePerTick) {
|
||||
lastAngle + maxChangePerTick
|
||||
} else if (angleOfMotion - lastAngle < -maxChangePerTick) {
|
||||
lastAngle - maxChangePerTick
|
||||
val change = (angleOfMotion - lastAngle).coerceIn(-maxChangePerTick, maxChangePerTick)
|
||||
if (PITCHED_TILT !in state.renderMarkers) {
|
||||
state.numbers.remove(CORRECTED_ANGLE)
|
||||
val correctedAngle = (lastAngle + change) * intensity
|
||||
bone.addRotation(0, correctedAngle.toRadians())
|
||||
state.numbers[PREVIOUS_ANGLE] = correctedAngle
|
||||
} else {
|
||||
angleOfMotion
|
||||
// It was already pitched, which means the body bone already has a rotation, so only rotate it a pinch more.
|
||||
bone.addRotation(0, change.toRadians() * intensity)
|
||||
state.numbers[PREVIOUS_ANGLE] = lastAngle + change * intensity
|
||||
}
|
||||
bone.addRotation(0, -interpolatedAngle.toRadians())
|
||||
state.numbers[PREVIOUS_ANGLE] = interpolatedAngle
|
||||
state.renderMarkers += PITCHED_TILT
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench.animation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.addRotation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation.Companion.CORRECTED_ANGLE
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation.Companion.PITCHED_TILT
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation.Companion.PREVIOUS_ANGLE
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
@@ -59,7 +60,6 @@ class SingleBoneLookAnimation(
|
||||
minYaw = minYaw ?: -45F,
|
||||
)
|
||||
|
||||
|
||||
override var labels = setOf("look")
|
||||
override fun setupAnim(context: RenderContext, model: PosableModel, state: PosableState, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float, intensity: Float) {
|
||||
val head = bone ?: return
|
||||
@@ -67,8 +67,14 @@ class SingleBoneLookAnimation(
|
||||
val yaw = yawMultiplier * headYaw.coerceIn(minYaw, maxYaw)
|
||||
// If PitchTiltAnimation was applied then we should counteract that tilt. It does kinda
|
||||
// assume that the tilt was on the body bone, though.
|
||||
val counterTiltDegrees = if (PITCHED_TILT in state.renderMarkers) { state.numbers[PREVIOUS_ANGLE] ?: 0 } else 0F
|
||||
head.addRotation(X_AXIS, (pitch.toRadians() * intensity) + counterTiltDegrees.toRadians())
|
||||
var counterTiltDegrees = if (PITCHED_TILT in state.renderMarkers) { state.numbers[PREVIOUS_ANGLE] ?: 0F } else 0F
|
||||
// If the corrected angle is present then we're probably in a pose transition so some amount of the tilt
|
||||
// has already been corrected for, so we counter a bit less.
|
||||
if (CORRECTED_ANGLE in state.numbers && PITCHED_TILT in state.renderMarkers) {
|
||||
counterTiltDegrees -= state.numbers[CORRECTED_ANGLE] ?: 0F
|
||||
}
|
||||
state.numbers[CORRECTED_ANGLE] = counterTiltDegrees
|
||||
head.addRotation(X_AXIS, pitch.toRadians() * intensity - counterTiltDegrees.toRadians())
|
||||
head.addRotation(Y_AXIS, yaw.toRadians() * intensity)
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +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.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.MOVING_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class DrowzeeModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("drowzee")
|
||||
override val head = getPart("head")
|
||||
|
||||
override val leftLeg = getPart("left_leg")
|
||||
override val rightLeg = getPart("right_leg")
|
||||
|
||||
override var portraitScale = 2.4F
|
||||
override var portraitTranslation = Vec3(0.0, -0.1, 0.0)
|
||||
|
||||
override var profileScale = 0.9F
|
||||
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var sleep: Pose
|
||||
lateinit var battleidle: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("drowzee", "blink")}
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("drowzee", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = STATIONARY_POSES + UI_POSES,
|
||||
transformTicks = 10,
|
||||
condition = { !it.isBattling },
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("drowzee", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = MOVING_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("drowzee", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
battleidle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("drowzee", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("drowzee", "faint") else null
|
||||
}
|
||||
-86
@@ -1,86 +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.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.MOVING_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
|
||||
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class HypnoModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("hypno")
|
||||
override val head = getPart("head")
|
||||
|
||||
override val leftLeg = getPart("left_upper_leg")
|
||||
override val rightLeg = getPart("right_upper_leg")
|
||||
|
||||
override var portraitScale = 2.1F
|
||||
override var portraitTranslation = Vec3(-0.3, 0.6, 0.0)
|
||||
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(-0.05, 0.5, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var sleep: Pose
|
||||
lateinit var battleidle: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("hypno", "blink")}
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("hypno", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = STATIONARY_POSES + UI_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { !it.isBattling },
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("hypno", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("hypno", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
battleidle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("hypno", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("hypno", "faint") else null
|
||||
}
|
||||
-2
@@ -262,7 +262,6 @@ object VaryingModelRepository {
|
||||
inbuilt("ditto", ::DittoModel)
|
||||
inbuilt("dodrio", ::DodrioModel)
|
||||
inbuilt("doduo", ::DoduoModel)
|
||||
inbuilt("drowzee", ::DrowzeeModel)
|
||||
inbuilt("electabuzz", ::ElectabuzzModel)
|
||||
inbuilt("exeggcute", ::ExeggcuteModel)
|
||||
inbuilt("exeggutor", ::ExeggutorModel)
|
||||
@@ -279,7 +278,6 @@ object VaryingModelRepository {
|
||||
inbuilt("growlithe", ::GrowlitheModel)
|
||||
inbuilt("hitmonchan", ::HitmonchanModel)
|
||||
inbuilt("hitmonlee", ::HitmonleeModel)
|
||||
inbuilt("hypno", ::HypnoModel)
|
||||
inbuilt("jynx", ::JynxModel)
|
||||
inbuilt("kabuto", ::KabutoModel)
|
||||
inbuilt("kabutops", ::KabutopsModel)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user