forked from mirrors/cobblemon
Compare commits
220
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5799e5df | ||
|
|
54ea4903ba | ||
|
|
28ad0cce83 | ||
|
|
7fe1929609 | ||
|
|
e03082a75e | ||
|
|
8d0c27f0e4 | ||
|
|
91fc2dc896 | ||
|
|
ab1ca295a3 | ||
|
|
5cbe937e23 | ||
|
|
4fa196da94 | ||
|
|
d35db46c1b | ||
|
|
b8a6c94fcb | ||
|
|
b069e448c1 | ||
|
|
18f41407c8 | ||
|
|
b659a662a2 | ||
|
|
dbb2e45e14 | ||
|
|
da491015a6 | ||
|
|
55db0d8af1 | ||
|
|
211821bc0f | ||
|
|
16a21f9d2e | ||
|
|
e69060bde6 | ||
|
|
ebdbbd5176 | ||
|
|
6e163d08f0 | ||
|
|
13c7b75b19 | ||
|
|
1ad36e39c5 | ||
|
|
f4d67b9b64 | ||
|
|
3c84afb95d | ||
|
|
0fc1239c77 | ||
|
|
38a0d149c5 | ||
|
|
8fee0e7f91 | ||
|
|
7985aed6bf | ||
|
|
5543b8a660 | ||
|
|
d7d855adc2 | ||
|
|
4fd50b8d2b | ||
|
|
53749e014b | ||
|
|
b4cdf371fe | ||
|
|
e0e30e7e11 | ||
|
|
cfec4e10cb | ||
|
|
614799cfcb | ||
|
|
ec367fd4df | ||
|
|
e415b281ee | ||
|
|
265aa6e124 | ||
|
|
2765a5584f | ||
|
|
d77cb36149 | ||
|
|
63f8e6251e | ||
|
|
5a3e9bb236 | ||
|
|
2fce916f05 | ||
|
|
52c4c35c0d | ||
|
|
40f4f80d6e | ||
|
|
721025281f | ||
|
|
08e6f68693 | ||
|
|
1e3fcf3e84 | ||
|
|
92a67ca0ca | ||
|
|
42600fc840 | ||
|
|
9917b57e1a | ||
|
|
b5e2f69207 | ||
|
|
c6c83c065b | ||
|
|
687381dc1b | ||
|
|
f088dd62bc | ||
|
|
3d1f9458f6 | ||
|
|
470d3807f1 | ||
|
|
1c74d498e6 | ||
|
|
4d3c46acc4 | ||
|
|
fbec87d2e0 | ||
|
|
397f8db5a0 | ||
|
|
026897719d | ||
|
|
c4451ffb4f | ||
|
|
710b6404e7 | ||
|
|
b8f59110f0 | ||
|
|
0a0c1edffc | ||
|
|
18403c833d | ||
|
|
b9824e5708 | ||
|
|
57c1201ad0 | ||
|
|
4637bef5d2 | ||
|
|
37e0030d08 | ||
|
|
6207a01ae3 | ||
|
|
40aaef68f9 | ||
|
|
2dd0d3dcf1 | ||
|
|
cc5b65a1dd | ||
|
|
f4e242a270 | ||
|
|
da3a513905 | ||
|
|
33af6a1557 | ||
|
|
62b07f7f8a | ||
|
|
7bddbcfc67 | ||
|
|
993c7a5cf0 | ||
|
|
44da19f3e0 | ||
|
|
6d97c6ea34 | ||
|
|
2a55ef114d | ||
|
|
c236d8147a | ||
|
|
cf90bf0b0f | ||
|
|
b109ff6edf | ||
|
|
4ea288cc34 | ||
|
|
98540294b1 | ||
|
|
5535a6a7cb | ||
|
|
dd426423a1 | ||
|
|
6237e44e7c | ||
|
|
09561f736a | ||
|
|
14e4fe66ac | ||
|
|
975c68cea0 | ||
|
|
6b77adbcef | ||
|
|
d12a8704a9 | ||
|
|
52fc6b2575 | ||
|
|
68ffe0bdbc | ||
|
|
4734c0a3cb | ||
|
|
e16ae2544c | ||
|
|
cff65ab22e | ||
|
|
1d550b2cac | ||
|
|
c859cbae76 | ||
|
|
767f820b7a | ||
|
|
06e4783f16 | ||
|
|
fe83b9d58e | ||
|
|
709e6f4a63 | ||
|
|
17f686488e | ||
|
|
db574332e5 | ||
|
|
73b72ff19d | ||
|
|
15aac5e20a | ||
|
|
b88f4335ee | ||
|
|
6916cd51f7 | ||
|
|
b2ebfcc3b0 | ||
|
|
3b73493801 | ||
|
|
bfb48e8583 | ||
|
|
1bfe5e2044 | ||
|
|
ace65eda66 | ||
|
|
01c44537d0 | ||
|
|
e414dd94c3 | ||
|
|
2d4800a733 | ||
|
|
90e193e5a0 | ||
|
|
c7e86cf767 | ||
|
|
d8906992a3 | ||
|
|
972c259f6d | ||
|
|
8228424191 | ||
|
|
9bb67087b3 | ||
|
|
0f08446732 | ||
|
|
af09a6a0c7 | ||
|
|
b4d2f39c48 | ||
|
|
1a12bbd493 | ||
|
|
8a24f635cf | ||
|
|
d6e066ddcd | ||
|
|
50335135c9 | ||
|
|
1ee6f83a5b | ||
|
|
e8b747a432 | ||
|
|
8f8804f3f4 | ||
|
|
ef826158ce | ||
|
|
d32314742d | ||
|
|
e8e0ca47dd | ||
|
|
ebcac6e782 | ||
|
|
63bfa634ae | ||
|
|
7ea85dbd1e | ||
|
|
6cdb14d2f8 | ||
|
|
f041049576 | ||
|
|
e1aa6445fa | ||
|
|
f1b7ee5b9c | ||
|
|
9ea0f7e4f8 | ||
|
|
d0edd6ecbc | ||
|
|
1372505486 | ||
|
|
a4ce4bf99b | ||
|
|
d72dad355c | ||
|
|
40b5726c5b | ||
|
|
d32aa46832 | ||
|
|
5068e66fa7 | ||
|
|
482b682595 | ||
|
|
ffecc58979 | ||
|
|
eaaf2ca05c | ||
|
|
e91651553b | ||
|
|
874a5d2e8f | ||
|
|
70e49bbbdb | ||
|
|
39fce8ca06 | ||
|
|
d288fe46da | ||
|
|
cefc97320a | ||
|
|
a2094677cd | ||
|
|
eae4eb5121 | ||
|
|
ed3a008515 | ||
|
|
462074ce43 | ||
|
|
d54a85aa68 | ||
|
|
bf8433f6e9 | ||
|
|
374fc44089 | ||
|
|
f7664a04db | ||
|
|
51b0f768a0 | ||
|
|
106e487035 | ||
|
|
2d772f5bf7 | ||
|
|
63af1713b9 | ||
|
|
8fecb12829 | ||
|
|
d0b50a1ee0 | ||
|
|
2ecbe28c87 | ||
|
|
80112e0b70 | ||
|
|
f776542365 | ||
|
|
045ca3a07f | ||
|
|
a71bad07bd | ||
|
|
2085f12fef | ||
|
|
863b43f902 | ||
|
|
1769af7385 | ||
|
|
88e38bfa1a | ||
|
|
507a9a54b5 | ||
|
|
0b72a072bd | ||
|
|
c927cae609 | ||
|
|
474918e414 | ||
|
|
0e93383c90 | ||
|
|
abb44cf696 | ||
|
|
e6bd7cad72 | ||
|
|
e2db59c837 | ||
|
|
10bf69a79b | ||
|
|
172293c813 | ||
|
|
9bb653b8b7 | ||
|
|
7d06f1e2cd | ||
|
|
a7b55e5d03 | ||
|
|
28db655376 | ||
|
|
006736cd71 | ||
|
|
f240dc1963 | ||
|
|
535c5f2e67 | ||
|
|
8b209e081b | ||
|
|
f7f276ecfb | ||
|
|
fc0cd0dac4 | ||
|
|
019245472f | ||
|
|
cefb502f10 | ||
|
|
ee5e4d33b2 | ||
|
|
0fd954ffa5 | ||
|
|
60d3d737f9 | ||
|
|
1caf963323 | ||
|
|
bb94f872a9 | ||
|
|
65ab0f48e4 |
@@ -7,6 +7,7 @@
|
||||
- Added `/changewallpaper <player> <boxNumber> <wallpaper>` command to change a box wallpaper through commands.
|
||||
- Added `/renamebox <player> <boxNumber> <name>` command to rename a PC box through commands.
|
||||
- Added IVs and EVs stat displays in PC.
|
||||
- improved filter by Name, now is available search by exact names or partial matches, for example "cha" will show Charmander, Charmeleon and Charizard among others that match.
|
||||
|
||||
### Developer
|
||||
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
### Additions
|
||||
- Pokémon can now spawn in herds, either as distinct herd groupings or simply several Pokémon spawning at once.
|
||||
|
||||
### Fixes
|
||||
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
|
||||
|
||||
### Developer
|
||||
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
|
||||
- Renamed heaps of things in the spawning system to make more sense.
|
||||
- SpawningContext is now SpawnablePosition
|
||||
- WorldSlice is SpawningZone
|
||||
- SpawningProspector is now SpawningZoneGenerator
|
||||
- Renamed things in Spawn Rules to go with the other renames:
|
||||
- contextSelector is now spawnablePositionSelector
|
||||
- context is now spawnable_position
|
||||
+41
-3
@@ -21,6 +21,15 @@
|
||||
- Fixed Moon Ball moon phase logic to actually work correctly
|
||||
- Added `translucent_cull` boolean option into resolver's layer to allow for translucent textures with culling
|
||||
- Added [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) support for items held by Pokémon.
|
||||
- Added the Clear Amulet, Grip Claw, Lagging Tail, Luminous Moss, Metal Alloy, Scroll of Darkness, Scroll of Waters
|
||||
- Added Recipes for Masterpiece Cup, Eject Pack
|
||||
- Added modification to Minecraft Creative Inventory search to account for item names that contain `poké` when input contains `poke`.
|
||||
- Added Campfire Pot as well as loads of new food items (Poke Puffs, Ponigiri, Sinister Tea, etc)
|
||||
- Added Hearty Grains, a new crop used in the new cooking mechanic
|
||||
- Added Tatami blocks and Tatami Mat blocks, made from Hearty Grain, for decorating builds
|
||||
- Added `/pcsearch <player> <pokemonProperties>` command that searches for a specific Pokémon within a player's PC.
|
||||
- Added `/pctake <player> <box> <slot>` command that takes a specific Pokémon from a player's PC. Removes the pokemon if target is self or ran from the server.
|
||||
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
@@ -114,12 +123,13 @@
|
||||
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
|
||||
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
|
||||
- Updated Sweet and Tart Apple sprites
|
||||
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc
|
||||
- Added alternate ingredient options to the following recipes: Cell Battery, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
|
||||
- Updated the following recipes: Air Balloon, Assault Vest, Binding Band, Black Belt, Blunder Policy, Choice Band, Choice Scarf, Cleanse Tag, Covert Cloak, Destiny Knot, Eject Button, Expert Belt, Focus Band, Focus Sash, Magnet, Metronome, Muscle Band, Power Anklet, Power Band, Power Belt, Power Bracer, Power Lens, Power Weight, Protective Pads, Protein, Punching Glove, Reaper Cloth, Rocky Helmet, Room Service, Sachet, Safety Goggles, Silk Scarf, Spell Tag, Utility Umbrella, Weakness Policy, Zinc, Chipped Pot, Cracked Pot, Unremarkable Cup, Loaded Dice, Charcoal Stick, Dragon Fang, Miracle Seed, Mystic Water, Never Melt Ice, Twisted Spoon, Black Glasses, Fairy Feather, Hard Stone, Silver Powder, Soft Sand
|
||||
- Added alternate ingredient options to the following recipes: Cell Battery, Damp Rock, Heat Rock, Icy Rock, Smooth Rock
|
||||
- Grouped together some recipes within the Recipe Book. Groups include: the seven basic Poké Balls, the seven basic Ancient Poké Balls, Gilded Chests, Pokedexes, and the Weather Rocks.
|
||||
- Updated some item tags to better integrate behaviours between Cobblemon, Vanilla Minecraft, and other mods
|
||||
- Removed Cooked Meat, Raw Meat, Protein Ingredients, and Zinc Ingredients, the first two are now using `c` namespace tags, the latter have better integrated use of tags within their recipes which removes need for custom tags.
|
||||
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
|
||||
- Added our seeds tag into `#c:seeds`, which is now made use of for the Miracle Seed recipe.
|
||||
- Added herbs and snowballs to the consumable in PvE and Wild battle tags
|
||||
- Tweaked the Natural Materials Vanilla file to fit with the changes to tags
|
||||
- Substantially optimised spawning checks mainly by front-loading biome filtering.
|
||||
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
|
||||
@@ -134,8 +144,15 @@
|
||||
- Improved parity with vanilla mobs' drop behavior; loot and XP drop on death instead of after the entire death sequence finishes.
|
||||
- Quirk animations no longer play in the battle GUI since they were pretty distracting.
|
||||
- A number of Pokemon that float above the ground visually (Gastly, Klingklang, etc.) are no longer considered to be touching the ground
|
||||
- Changed the recipes for Level, Lure, and Moon Balls to be cleaner
|
||||
- Renamed `pokemonId` and `type` to `species` in relevant Advancement triggers for conformity, this is a breaking change.
|
||||
- Added a separate `species` argument to the `pick_starter`, `pasture_use` and `resurrect_pokemon` Advancement triggers.
|
||||
- Made `CobblemonAgingDespawner` thresholds configurable via the config file.
|
||||
- Tweaked Berry flavor data to balance cooking pot mechanics
|
||||
- Made Red, Yellow, Green, Blue, Pink, Black, and White Apricorn Sprout and Saccharine Sapling able to be placed into flower pots.
|
||||
- Added all Potted Apricorn Sprouts, Potted Saccharine Saplings, and Potted Pep-Up Flowers to the `#minecraft:flower_pots` block tag
|
||||
- Corrected the healing values of the remedies and energy root, and reintroduced the friendship lowering mechanics for them, revivl herb, and heal powder
|
||||
- Updated battle language keys for side effects to use position-specific formats, improving grammatical consistency
|
||||
|
||||
### Fixes
|
||||
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
|
||||
@@ -169,6 +186,17 @@
|
||||
- Fixed dragon's breath not being usable on the restoration tank when it should be
|
||||
- Fixed Moon Stones not interacting properly with dripstone blocks.
|
||||
- Fixed some effects like particles from fishing rods appearing for players in the same coordinates in another world.
|
||||
- Fixed an issue with Sketch where the Pokémon using Sketch would not properly learn moves with special characters in their name (e.g. King's Shield, Baby-Doll Eyes, etc.)
|
||||
- Fixed wild Pokémon sometimes spawning with incorrect friendship values
|
||||
- Fixed typo while saving/loading NPCEntity causes data loss
|
||||
- Fixed an issue where catching a Pokémon while it was leashed to a fence would not update the fence.
|
||||
- Fixed the `dimensions` spawning condition using the incorrect ResourceLocation, causing it to not function properly.
|
||||
- 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)
|
||||
- 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
|
||||
- Fixed energy root always spreading into more energy roots instead of by chance (same as big root)
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -195,6 +223,8 @@
|
||||
}
|
||||
}
|
||||
`
|
||||
- The IVs class has now been extended to include Hyper Trained values.
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
|
||||
### 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)
|
||||
@@ -214,6 +244,14 @@
|
||||
- Fixed `heldItem` property inside spawn files not working and causing crashes
|
||||
- Fixed `spawn_bedrock_particles` MoLang causing crashes when used in a server environment
|
||||
- The Pokédex form lang key definition now follows `cobblemon.ui.pokedex.info.form.{species}-{formname}` instead of `cobblemon.ui.pokedex.info.form.{formname}`.
|
||||
- Added `play_sound_on_server` as an available Molang function for the `worldHolder` & `player` functions.
|
||||
- Added `run_molang_after` as an available Molang function for the `entity` functions when schedulable.
|
||||
- Added an optional parameter for `run_molang` to schedule the function.
|
||||
- Added `labels` & `has_label` as available Molang functions for the `speciesFunctions`
|
||||
- Added datapack-defined starter categories via `data/<namespace>/starters/*.json`, with built-in fallback and `useConfigStarters` merge option.
|
||||
- The format of the `remedies.json` file has changed to allow for individual friendshipDrop amounts per remedy
|
||||
- Fixed `entity.find_nearby_block` causing crashes when attempting to use a block tag
|
||||
- Spawn Filters can now access `v.spawn.class` to get the identifier of an NPC class for when trying to influence NPC spawns
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
|
||||
@@ -1,119 +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.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
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.LocalPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable {
|
||||
@Shadow
|
||||
private float jumpRidingScale;
|
||||
|
||||
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
|
||||
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
|
||||
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
|
||||
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
|
||||
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
|
||||
return behaviour.setRideBar(settings, state, pokemonEntity, player);
|
||||
});
|
||||
if (rideValue != null) {
|
||||
cir.setReturnValue(rideValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
|
||||
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
|
||||
if (locator == null) {
|
||||
super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
OrientationController controller = this.getOrientationController();
|
||||
|
||||
float currEyeHeight = this.getEyeHeight();
|
||||
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
|
||||
|
||||
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
|
||||
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
|
||||
offset.add(cobblemon$getFirstPersonOffset(model, seat));
|
||||
}
|
||||
|
||||
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
|
||||
|
||||
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
|
||||
|
||||
Vec3 viewVector = this.getViewVector(partialTicks);
|
||||
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
|
||||
return this.level()
|
||||
.clip(
|
||||
new ClipContext(
|
||||
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, Seat seat) {
|
||||
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
|
||||
|
||||
if (cameraOffsets.containsKey(seat.getLocator())) {
|
||||
return cameraOffsets.get(seat.getLocator()).toVector3f();
|
||||
} else {
|
||||
return new Vector3f(0f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Structure.class)
|
||||
public abstract class StructureMixin {
|
||||
// Define a set of structures that should not spawn below Sea Level
|
||||
@Unique
|
||||
private static final Set<ResourceLocation> RESTRICTED_STRUCTURES = new HashSet<>(Arrays.asList(
|
||||
CobblemonStructureIDs.STONJOURNER_HENGE,
|
||||
CobblemonStructureIDs.LUNA_HENGE,
|
||||
CobblemonStructureIDs.SOL_HENGE
|
||||
));
|
||||
|
||||
@Inject(method = "generate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;isValid()Z"),
|
||||
cancellable = true)
|
||||
public void cobblemon$isValid(RegistryAccess registryAccess, ChunkGenerator chunkGenerator, BiomeSource biomeSource,
|
||||
RandomState randomState, StructureTemplateManager structureTemplateManager, long seed,
|
||||
ChunkPos chunkPos, int references, LevelHeightAccessor heightAccessor,
|
||||
Predicate<Holder<Biome>> validBiome,
|
||||
CallbackInfoReturnable<StructureStart> cir,
|
||||
@Local StructureStart structureStart) {
|
||||
ResourceLocation structureKey = registryAccess.registryOrThrow(Registries.STRUCTURE).getKey((Structure) (Object) this);
|
||||
if(!RESTRICTED_STRUCTURES.contains(structureKey)) {
|
||||
return;
|
||||
}
|
||||
for (StructurePiece piece : structureStart.getPieces()) {
|
||||
if (piece.getBoundingBox().minY() < chunkGenerator.getSeaLevel()) {
|
||||
cir.setReturnValue(StructureStart.INVALID_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -56,7 +56,7 @@ public abstract class ClientRecipeBookMixin {
|
||||
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
|
||||
case FOODS -> var7 = CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory();
|
||||
case MEDICINES -> var7 = CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory();
|
||||
case BAITS -> var7 = CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory();
|
||||
case COMPLEX_DISHES -> var7 = CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory();
|
||||
default -> throw new MatchException(null, null);
|
||||
}
|
||||
cir.setReturnValue(var7);
|
||||
|
||||
@@ -10,27 +10,51 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
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.LocalPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin extends LivingEntity {
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable{
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
|
||||
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
@Shadow
|
||||
private float jumpRidingScale;
|
||||
|
||||
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
|
||||
// @Inject(method = "sendPosition", at = @At("TAIL"))
|
||||
@@ -76,4 +100,73 @@ public abstract class LocalPlayerMixin extends LivingEntity {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
|
||||
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity pokemonEntity) {
|
||||
var rideValue = pokemonEntity.<Float>ifRidingAvailableSupply(null, (behaviour, settings, state) -> {
|
||||
if (behaviour.canJump(settings, state, pokemonEntity, player)) return null;
|
||||
return behaviour.setRideBar(settings, state, pokemonEntity, player);
|
||||
});
|
||||
if (rideValue != null) {
|
||||
cir.setReturnValue(rideValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
|
||||
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
|
||||
if (locator == null) {
|
||||
super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
OrientationController controller = this.getOrientationController();
|
||||
|
||||
float currEyeHeight = this.getEyeHeight();
|
||||
Vector3f offset = new Vector3f(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f));
|
||||
|
||||
if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) {
|
||||
PosableModel model = VaryingModelRepository.INSTANCE.getPoser(pokemonEntity.getPokemon().getSpecies().getResourceIdentifier(), new FloatingState());
|
||||
offset.add(cobblemon$getFirstPersonOffset(model, seat));
|
||||
}
|
||||
|
||||
Matrix3f orientation = controller.isActive() && controller.getOrientation() != null ? controller.getOrientation() : new Matrix3f();
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(offset));
|
||||
|
||||
Vec3 eyePosition = locatorOffset.add(pokemonEntity.position()).add(rotatedEyeHeight);
|
||||
|
||||
Vec3 viewVector = this.getViewVector(partialTicks);
|
||||
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
|
||||
return this.level()
|
||||
.clip(
|
||||
new ClipContext(
|
||||
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static @NotNull Vector3f cobblemon$getFirstPersonOffset(PosableModel model, Seat seat) {
|
||||
Map<String, Vec3> cameraOffsets = model.getFirstPersonCameraOffset();
|
||||
|
||||
if (cameraOffsets.containsKey(seat.getLocator())) {
|
||||
return cameraOffsets.get(seat.getLocator()).toVector3f();
|
||||
} else {
|
||||
return new Vector3f(0f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -50,10 +50,10 @@ public class RecipeBookCategoriesMixin {
|
||||
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
|
||||
ArrayList<RecipeBookCategories> types = new ArrayList<>(List.of($VALUES));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_SEARCH", $VALUES.length, new ItemStack(Items.COMPASS)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.ROASTED_LEEK)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.HP_UP)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_BAITS", $VALUES.length + 3, new ItemStack(CobblemonItems.PERSIM_BERRY)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.MIRROR_HERB)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.LEEK_AND_POTATO_STEW)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.POTION)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_COMPLEX_DISHES", $VALUES.length + 3, new ItemStack(CobblemonItems.POKE_PUFF)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.PROTEIN)));
|
||||
$VALUES = types.toArray(RecipeBookCategories[]::new);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class RecipeBookCategoriesMixin {
|
||||
CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_COMPLEX_DISHES.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory());
|
||||
cir.setReturnValue(var10000);
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import net.minecraft.client.searchtree.IdSearchTree;
|
||||
import net.minecraft.client.searchtree.SearchTree;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(IdSearchTree.class)
|
||||
public abstract class SearchTreeMixin {
|
||||
@Unique
|
||||
private static final ThreadLocal<Boolean> reentryFlag = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
/**
|
||||
* Runs an additional search using the original search term where every instance of 'e' is replaced with 'é'
|
||||
* Appends additional search results to original search results.
|
||||
*/
|
||||
@Inject(method = "search(Ljava/lang/String;)Ljava/util/List;", at = @At("RETURN"), cancellable = true)
|
||||
private void onSearch(String string, CallbackInfoReturnable<List> cir) {
|
||||
// If flag is true, skip to avoid infinite loop
|
||||
if (reentryFlag.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
reentryFlag.set(true);
|
||||
List originalResults = cir.getReturnValue();
|
||||
|
||||
if (!string.contains("poke")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String modifiedString = string.replace("poke", "poké");
|
||||
List additionalResults = ((SearchTree)this).search(modifiedString);
|
||||
|
||||
Set combinedSet = new LinkedHashSet<>();
|
||||
combinedSet.addAll(originalResults);
|
||||
combinedSet.addAll(additionalResults);
|
||||
|
||||
cir.setReturnValue(new ArrayList<>(combinedSet));
|
||||
} finally {
|
||||
reentryFlag.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,9 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
|
||||
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.starter.StarterHandler
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
|
||||
@@ -160,8 +160,8 @@ object Cobblemon {
|
||||
var isDedicatedServer = false
|
||||
val showdownThread = ShowdownThread()
|
||||
lateinit var config: CobblemonConfig
|
||||
var prospector: SpawningProspector = CobblemonSpawningProspector
|
||||
var areaContextResolver: AreaContextResolver = object : AreaContextResolver {}
|
||||
var spawningZoneGenerator: SpawningZoneGenerator = CobblemonSpawningZoneGenerator
|
||||
var areaSpawnablePositionResolver: AreaSpawnablePositionResolver = object : AreaSpawnablePositionResolver {}
|
||||
val bestSpawner = BestSpawner
|
||||
val battleRegistry = BattleRegistry
|
||||
var storage = PokemonStoreManager()
|
||||
|
||||
@@ -515,6 +515,30 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@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)))
|
||||
|
||||
@JvmField
|
||||
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", HeartyGrainBaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(SoundType.GRASS)))
|
||||
@JvmField
|
||||
val TATAMI_BLOCK = this.create("tatami_block", TatamiBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).sound(SoundType.GRASS)))
|
||||
@JvmField
|
||||
val TATAMI_MAT = this.create("tatami_mat", TatamiMatBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).sound(SoundType.GRASS)))
|
||||
|
||||
/**
|
||||
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
|
||||
*
|
||||
|
||||
@@ -68,6 +68,8 @@ object CobblemonCommands {
|
||||
MarkGiveCommand.register(dispatcher)
|
||||
MarkTakeCommand.register(dispatcher)
|
||||
MarkGiveAllCommand.register(dispatcher)
|
||||
PcSearchCommand.register(dispatcher)
|
||||
PcTakeCommand.register(dispatcher)
|
||||
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
|
||||
@@ -9,17 +9,14 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.PotComponent
|
||||
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.HeldItemCapableComponent
|
||||
import com.cobblemon.mod.common.item.components.PokemonItemComponent
|
||||
import com.cobblemon.mod.common.item.components.RodBaitComponent
|
||||
import com.cobblemon.mod.common.item.components.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.component.DataComponentType
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
@@ -60,6 +57,25 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(FoodColourComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
|
||||
.persistent(IngredientComponent.CODEC)
|
||||
.networkSynchronized(IngredientComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val FOOD: DataComponentType<FoodComponent> = create("food", DataComponentType.builder<FoodComponent>()
|
||||
.persistent(FoodComponent.CODEC)
|
||||
.networkSynchronized(FoodComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val MOB_EFFECTS: DataComponentType<MobEffectsComponent> = create("mob_effects", DataComponentType.builder<MobEffectsComponent>()
|
||||
.persistent(MobEffectsComponent.CODEC)
|
||||
.networkSynchronized(MobEffectsComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val CRAFTED: DataComponentType<Boolean> = create("crafted", DataComponentType.builder<Boolean>()
|
||||
.persistent(Codec.BOOL)
|
||||
.networkSynchronized(ByteBufCodecs.BOOL)
|
||||
.build())
|
||||
|
||||
fun register() {
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
|
||||
@@ -68,6 +84,9 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait_effects"), BAIT_EFFECTS)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:flavour"), FLAVOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food_colour"), FOOD_COLOUR)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.blue
|
||||
@@ -34,10 +35,13 @@ import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.food.PonigiriItem
|
||||
import com.cobblemon.mod.common.item.food.SinisterTeaItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.Registry
|
||||
@@ -53,6 +57,7 @@ import net.minecraft.world.food.FoodProperties
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.HangingSignItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.Item.Properties
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.Rarity
|
||||
@@ -201,7 +206,14 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
|
||||
|
||||
@JvmField
|
||||
val Hearty_Grains = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
|
||||
val HEARTY_GRAINS = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE = compostableBlockItem("hearty_grain_bale", CobblemonBlocks.HEARTY_GRAIN_BALE, 0.85f)
|
||||
|
||||
@JvmField
|
||||
val TATAMI_BLOCK = blockItem("tatami_block", CobblemonBlocks.TATAMI_BLOCK)
|
||||
@JvmField
|
||||
val TATAMI_MAT = blockItem("tatami_mat", CobblemonBlocks.TATAMI_MAT)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE = compostableItem("vivichoke")
|
||||
@@ -348,260 +360,105 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
val aprijuices = mutableListOf<AprijuiceItem>()
|
||||
@JvmField
|
||||
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
|
||||
@JvmField
|
||||
val APRIJUICE_RED = aprijuiceItem(Apricorn.RED)
|
||||
@JvmField
|
||||
val APRIJUICE_BLUE = aprijuiceItem(Apricorn.BLUE)
|
||||
@JvmField
|
||||
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
|
||||
@JvmField
|
||||
val APRIJUICE_YELLOW = aprijuiceItem(Apricorn.YELLOW)
|
||||
@JvmField
|
||||
val APRIJUICE_WHITE = aprijuiceItem(Apricorn.WHITE)
|
||||
val APRIJUICE_GREEN = aprijuiceItem(Apricorn.GREEN)
|
||||
@JvmField
|
||||
val APRIJUICE_BLUE = aprijuiceItem(Apricorn.BLUE)
|
||||
@JvmField
|
||||
val APRIJUICE_PINK = aprijuiceItem(Apricorn.PINK)
|
||||
@JvmField
|
||||
val APRIJUICE_BLACK = aprijuiceItem(Apricorn.BLACK)
|
||||
@JvmField
|
||||
val APRIJUICE_WHITE = aprijuiceItem(Apricorn.WHITE)
|
||||
|
||||
@JvmField
|
||||
val POKE_PUFF = noSettingsItem("poke_puff") // todo make a PokePuffItem class for friendship boosting purposes
|
||||
val PONIGIRI = create("ponigiri", PonigiriItem())
|
||||
|
||||
@JvmField
|
||||
val SINISTER_TEA = create("sinister_tea", SinisterTeaItem())
|
||||
|
||||
@JvmField
|
||||
val POKE_PUFF = pokepuffItem("poke_puff")
|
||||
|
||||
// FOODS
|
||||
@JvmField
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
|
||||
|
||||
@JvmField
|
||||
val PEWTER_CRUNCHIES = create("pewter_crunchies", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
val TASTY_TAIL = noSettingsItem("tasty_tail")
|
||||
|
||||
@JvmField
|
||||
val RAGE_CANDY_BAR = create("rage_candy_bar", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
|
||||
|
||||
@JvmField
|
||||
val LAVA_COOKIE = create("lava_cookie", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false)
|
||||
|
||||
@JvmField
|
||||
val OLD_GATEAU = create("old_gateau", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@JvmField
|
||||
val CASTELIACONE = create("casteliacone", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@JvmField
|
||||
val SHALOR_SABLE = create("shalor_sable", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = create("lumiose_galette", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val SHALOUR_SABLE = regionalFoodItem("shalour_sable", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@JvmField
|
||||
val BIG_MALASADA = create("big_malasada", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
/*@JvmField // todo see what we are doing for these (are there different kinds?)
|
||||
val CURRY = create("curry", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.usingConvertsTo(Items.BOWL)
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})*/
|
||||
@JvmField // todo get final values for this from vera
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = create("jubilife_muffin", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField // todo get final values for this from vera
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f, false, ItemStack(Items.BOWL, 1))
|
||||
|
||||
/*@JvmField // todo see what we are doing for these (are there different kinds?)
|
||||
val SANDWICH = create("sandwich", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.usingConvertsTo(Items.BOWL)
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})*/
|
||||
@JvmField // todo get final values for this from vera
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
// todo we might need to wait on these for later?
|
||||
@JvmField // todo get final values for this from vera
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
// todo we might need to wait on these for later? These impact battles and may be harder to do
|
||||
/*@JvmField
|
||||
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for breeding purposes
|
||||
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for battle purposes
|
||||
@JvmField
|
||||
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for breeding purposes
|
||||
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for battle purposes
|
||||
@JvmField
|
||||
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for breeding purposes
|
||||
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for battle purposes
|
||||
*/
|
||||
@JvmField
|
||||
val POTATO_MOCHI = create("potato_mochi", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
.build())) {})
|
||||
@JvmField
|
||||
val FRIED_RICE = create("fried_rice", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
.build())) {})
|
||||
@JvmField
|
||||
val CANDIED_APPLE = create("candied_apple", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
.build())) {})
|
||||
@JvmField
|
||||
val CANDIED_BERRY = create("candied_berry", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val JELLY_DOUGHNUT = create("jelly_doughnut", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
// todo etc?
|
||||
.build())) {})
|
||||
|
||||
//@JvmField
|
||||
//val SCATTER_BANG = this.create("scatter_bang", ScatterBangItem(Item.Settings()))
|
||||
@@ -665,6 +522,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
|
||||
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
|
||||
@JvmField val METAL_ALLOY = noSettingsItem("metal_alloy")
|
||||
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
|
||||
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
|
||||
|
||||
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
|
||||
// Plants
|
||||
@@ -743,6 +603,33 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val GALARICA_NUTS = create("galarica_nuts", GalaricaNutItem())
|
||||
|
||||
// Hyper Training Items
|
||||
val hyperTrainingItems = mutableListOf<HyperTrainingItem>()
|
||||
@JvmField
|
||||
val HEALTH_CANDY = hyperTrainingItem("health_candy", 1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val MIGHTY_CANDY = hyperTrainingItem("mighty_candy", 1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val TOUGH_CANDY = hyperTrainingItem("tough_candy", 1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val SMART_CANDY = hyperTrainingItem("smart_candy", 1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val COURAGE_CANDY = hyperTrainingItem("courage_candy", 1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val QUICK_CANDY = hyperTrainingItem("quick_candy", 1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val SICKLY_CANDY = hyperTrainingItem("sickly_candy", -1, setOf(Stats.HP), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val WEAK_CANDY = hyperTrainingItem("weak_candy", -1, setOf(Stats.ATTACK), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val BRITTLE_CANDY = hyperTrainingItem("brittle_candy", -1, setOf(Stats.DEFENCE), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val NUMB_CANDY = hyperTrainingItem("numb_candy", -1, setOf(Stats.SPECIAL_ATTACK), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val COWARD_CANDY = hyperTrainingItem("coward_candy", -1, setOf(Stats.SPECIAL_DEFENCE), 0..IVs.MAX_VALUE)
|
||||
@JvmField
|
||||
val SLOW_CANDY = hyperTrainingItem("slow_candy", -1, setOf(Stats.SPEED), 0..IVs.MAX_VALUE)
|
||||
|
||||
// Medicine
|
||||
@JvmField
|
||||
val RARE_CANDY = candyItem("rare_candy", Rarity.RARE) { _, pokemon -> pokemon.getExperienceToNextLevel() }
|
||||
@@ -828,6 +715,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val FINE_REMEDY = create("fine_remedy", RemedyItem(RemedyItem.FINE))
|
||||
@JvmField
|
||||
val SUPERB_REMEDY = create("superb_remedy", RemedyItem(RemedyItem.SUPERB))
|
||||
@JvmField
|
||||
val MOOMOO_MILK = create("moomoo_milk", Item(Item.Properties()))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
@@ -1062,8 +951,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val CHOICE_SCARF = heldItem("choice_scarf")
|
||||
@JvmField
|
||||
val CLEANSE_TAG = heldItem("cleanse_tag")
|
||||
// @JvmField
|
||||
// val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
@@ -1092,16 +981,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val FOCUS_SASH = heldItem("focus_sash")
|
||||
@JvmField
|
||||
val GRASSY_SEED = heldItem("grassy_seed")
|
||||
// @JvmField
|
||||
// val GRIP_CLAW = heldItem("grip_claw")
|
||||
@JvmField
|
||||
val GRIP_CLAW = heldItem("grip_claw")
|
||||
@JvmField
|
||||
val HARD_STONE = heldItem("hard_stone")
|
||||
@JvmField
|
||||
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
|
||||
@JvmField
|
||||
val IRON_BALL = heldItem("iron_ball")
|
||||
// @JvmField
|
||||
// val LAGGING_TAIL = heldItem("lagging_tail")
|
||||
@JvmField
|
||||
val LAGGING_TAIL = heldItem("lagging_tail")
|
||||
@JvmField
|
||||
val LEFTOVERS = heldItem("leftovers")
|
||||
@JvmField
|
||||
@@ -1114,8 +1003,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val LOADED_DICE = heldItem("loaded_dice")
|
||||
@JvmField
|
||||
val LUCKY_EGG = heldItem("lucky_egg")
|
||||
// @JvmField
|
||||
// val LUMINOUS_MOSS = heldItem("luminous_moss")
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = heldItem("luminous_moss")
|
||||
@JvmField
|
||||
val MAGNET = heldItem("magnet")
|
||||
@JvmField
|
||||
@@ -1642,6 +1531,44 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun pokepuffItem(name: String): PokePuffItem {
|
||||
val item = create("poke_puff", PokePuffItem())
|
||||
return item
|
||||
}
|
||||
|
||||
private fun regionalFoodItem(
|
||||
name: String,
|
||||
stacksTo: Int,
|
||||
nutrition: Int,
|
||||
saturationModifier: Float,
|
||||
effect: MobEffectInstance? = null,
|
||||
effectChance: Float? = null,
|
||||
alwaysEdible: Boolean = false,
|
||||
convertsToOnUse: ItemStack? = null
|
||||
): RegionalFoodItem {
|
||||
val foodPropertiesBuilder = FoodProperties.Builder()
|
||||
.nutrition(nutrition)
|
||||
.saturationModifier(saturationModifier)
|
||||
|
||||
if (effect != null && effectChance != null) {
|
||||
foodPropertiesBuilder.effect(effect, effectChance)
|
||||
}
|
||||
|
||||
if (alwaysEdible == true) {
|
||||
foodPropertiesBuilder.alwaysEdible()
|
||||
}
|
||||
|
||||
if (convertsToOnUse != null && !convertsToOnUse.isEmpty) {
|
||||
foodPropertiesBuilder.usingConvertsTo(convertsToOnUse.item)
|
||||
}
|
||||
|
||||
val properties = Item.Properties()
|
||||
.stacksTo(stacksTo)
|
||||
.food(foodPropertiesBuilder.build())
|
||||
|
||||
return create(name, RegionalFoodItem(properties))
|
||||
}
|
||||
|
||||
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
CobblemonItem(Item.Properties()).also {
|
||||
@@ -1687,6 +1614,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun hyperTrainingItem(name: String, increaseAmount: Int, targetStats: Set<Stat>, validRange: IntRange): HyperTrainingItem {
|
||||
val item = this.create(name, HyperTrainingItem(increaseAmount, targetStats, validRange))
|
||||
hyperTrainingItems.add(item)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun apricornItem(name: String, apricornItem: ApricornItem): ApricornItem {
|
||||
val finalName = "${name}_apricorn"
|
||||
val item = this.create(finalName, apricornItem)
|
||||
|
||||
@@ -259,6 +259,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Pokemon Update Packets
|
||||
list.add(PacketRegisterInfo(FriendshipUpdatePacket.ID, FriendshipUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(FullnessUpdatePacket.ID, FullnessUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(MoveSetUpdatePacket.ID, MoveSetUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(NatureUpdatePacket.ID, NatureUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ShinyUpdatePacket.ID, ShinyUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
@@ -36,7 +36,7 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
@JvmField
|
||||
val SACCHARINE_HONEY_LOG = create(SACCHARINE_HONEY_LOG_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.SACCHARINE_HONEY_LOG), 0, 1))
|
||||
@JvmField
|
||||
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("saccharine_honey_log")
|
||||
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("incense_sweet")
|
||||
@JvmField
|
||||
val INCENSE_SWEET = create(INCENSE_SWEET_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.INCENSE_SWEET), 0, 1))
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ enum class CobblemonRecipeCategories {
|
||||
COOKING_POT_SEARCH("COOKING_POT_SEARCH"),
|
||||
COOKING_POT_FOODS("COOKING_POT_FOODS"),
|
||||
COOKING_POT_MEDICINES("COOKING_POT_MEDICINES"),
|
||||
COOKING_POT_BAITS("COOKING_POT_BAITS"),
|
||||
COOKING_POT_COMPLEX_DISHES("COOKING_POT_COMPLEX_DISHES"),
|
||||
COOKING_POT_MISC("COOKING_POT_MISC");
|
||||
|
||||
companion object {
|
||||
val customAggregateCategories: Map<RecipeBookCategories, List<RecipeBookCategories>> = mapOf(
|
||||
COOKING_POT_SEARCH.toVanillaCategory() to listOf(
|
||||
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_BAITS.toVanillaCategory()
|
||||
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_COMPLEX_DISHES.toVanillaCategory()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val MOCHI_USE = this.create("item.medicine.feather.use")
|
||||
|
||||
|
||||
@JvmField
|
||||
val MULCH_PLACE = this.create("block.mulch.place")
|
||||
@JvmField
|
||||
@@ -216,6 +217,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val BERRY_HARVEST = this.create("block.berry_bush.harvest")
|
||||
@JvmField
|
||||
val BERRY_EAT = this.create("item.berry.eat")
|
||||
@JvmField
|
||||
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_BREAK = this.create("block.big_root.break")
|
||||
|
||||
+2
-1
@@ -32,6 +32,7 @@ class PokemonCriterion(
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: Pokemon): Boolean {
|
||||
return (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier)) && properties.matches(context)
|
||||
var speciesCheck = (species.equals(context.species.resourceIdentifier.path) || species.equals(context.species.resourceIdentifier.toString())) || species.equals("any")
|
||||
return speciesCheck && properties.matches(context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Task Configuration
|
||||
|
||||
A task configuration is responsible for producing one or multiple tasks as part of a
|
||||
[BrainConfiguration](../README.md). Tasks are the smallest unit of behaviour in a brain and are executed
|
||||
[Behaviour Configuration](../README.md). Tasks are the smallest unit of behaviour in a brain and are executed
|
||||
as part of an activity.
|
||||
|
||||
A task configuration can be a simple string that represents the type of task to create, or a JSON object
|
||||
|
||||
@@ -23,7 +23,8 @@ enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringR
|
||||
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
|
||||
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
|
||||
BITTER(0xFFFF00, ChatFormatting.GREEN),
|
||||
SOUR(0xFF00FF, ChatFormatting.YELLOW);
|
||||
SOUR(0xFF00FF, ChatFormatting.YELLOW),
|
||||
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
|
||||
|
||||
override fun getSerializedName() = name
|
||||
companion object {
|
||||
|
||||
@@ -29,9 +29,12 @@ private val bubbleColourMap = mapOf(
|
||||
|
||||
fun getColourMixFromSeasonings(seasonings: List<ItemStack>, forBubbles: Boolean = false): Int? {
|
||||
val flavors = seasonings
|
||||
.flatMap { Seasonings.getFlavoursFromItemStack(it).entries }
|
||||
.groupingBy { it.key }
|
||||
.fold(0) { acc, entry -> acc + entry.value }
|
||||
.mapNotNull { Seasonings.getFlavoursFromItemStack(it) }
|
||||
.flatMap { it.entries }
|
||||
.groupingBy { it.key }
|
||||
.fold(0) { acc, entry -> acc + entry.value }
|
||||
|
||||
if (flavors.isEmpty()) return null
|
||||
|
||||
val maxFlavorValue = flavors.values.maxOrNull()
|
||||
val dominantFlavors = flavors.filter { it.value == maxFlavorValue }.map { it.key }
|
||||
@@ -54,6 +57,25 @@ fun getColourMixFromFlavours(dominantFlavours: List<Flavour>, forBubbles: Boolea
|
||||
acc
|
||||
}
|
||||
|
||||
return FastColor.ARGB32.color(
|
||||
alphaSum / colors.size,
|
||||
redSum / colors.size,
|
||||
greenSum / colors.size,
|
||||
blueSum / colors.size
|
||||
)
|
||||
}
|
||||
|
||||
fun getColourMixFromColors(colors: List<Int>): Int? {
|
||||
if (colors.isEmpty()) return null
|
||||
|
||||
val (alphaSum, redSum, greenSum, blueSum) = colors.fold(IntArray(4)) { acc, color ->
|
||||
acc[0] += FastColor.ARGB32.alpha(color)
|
||||
acc[1] += FastColor.ARGB32.red(color)
|
||||
acc[2] += FastColor.ARGB32.green(color)
|
||||
acc[3] += FastColor.ARGB32.blue(color)
|
||||
acc
|
||||
}
|
||||
|
||||
return FastColor.ARGB32.color(
|
||||
alphaSum / colors.size,
|
||||
redSum / colors.size,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.item.components.FoodComponent
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
|
||||
data class Food(
|
||||
val hunger: Int = 0,
|
||||
val saturation: Float = 0f,
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Food> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
Codec.INT.optionalFieldOf("hunger", 0).forGetter { it.hunger },
|
||||
Codec.FLOAT.optionalFieldOf("saturation", 0f).forGetter { it.saturation },
|
||||
).apply(builder, ::Food)
|
||||
}
|
||||
}
|
||||
|
||||
fun toComponent(): FoodComponent = FoodComponent(hunger, saturation)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.item.components.FoodComponent
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.round
|
||||
|
||||
object FoodUtils {
|
||||
fun merge(components: List<FoodComponent>, extraHunger: Int = 0, extraSaturation: Float = 0f): FoodComponent {
|
||||
if (components.isEmpty()) return FoodComponent(0, 0f)
|
||||
|
||||
val count = components.size
|
||||
val totalHunger = components.sumOf { it.hunger.toDouble() } + extraHunger
|
||||
val totalSaturation = components.sumOf { it.saturation.toDouble() } + extraSaturation
|
||||
|
||||
val multiplier = when (count) {
|
||||
1 -> 1.0
|
||||
2 -> 0.8
|
||||
3 -> 0.6
|
||||
else -> 0.4
|
||||
}
|
||||
|
||||
val adjustedHunger = ceil(totalHunger * multiplier)
|
||||
val adjustedSaturation = totalSaturation * multiplier
|
||||
|
||||
return FoodComponent(
|
||||
hunger = adjustedHunger.toInt(),
|
||||
saturation = (round(adjustedSaturation * 100) / 100f).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
|
||||
object MobEffectUtils {
|
||||
fun mergeEffects(effects: List<SerializableMobEffectInstance>): List<SerializableMobEffectInstance> {
|
||||
val grouped = effects.groupBy { it.effect }
|
||||
|
||||
return grouped.map { (effectId, group) ->
|
||||
val amplifier = mergeAmplifiers(group.map { it.amplifier })
|
||||
val duration = mergeDurations(group.map { it.duration })
|
||||
|
||||
val first = group.first()
|
||||
SerializableMobEffectInstance(
|
||||
effect = effectId,
|
||||
duration = duration,
|
||||
amplifier = amplifier,
|
||||
ambient = first.ambient,
|
||||
visible = first.visible,
|
||||
showIcon = first.showIcon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeDurations(durations: List<Int>): Int {
|
||||
if (durations.isEmpty()) return 0
|
||||
|
||||
val sorted = durations.sortedDescending()
|
||||
var result = 0.0
|
||||
|
||||
for ((index, value) in sorted.withIndex()) {
|
||||
val multiplier = when (index) {
|
||||
0 -> 1.0
|
||||
1 -> 0.75
|
||||
2 -> 0.5
|
||||
else -> 0.25
|
||||
}
|
||||
result += value * multiplier
|
||||
}
|
||||
|
||||
return ceil(result).toInt()
|
||||
}
|
||||
|
||||
private fun mergeAmplifiers(amplifiers: List<Int>): Int {
|
||||
return amplifiers.maxOrNull() ?: 0 // we do not want the power of the effects to combine, just take the highest one used of that type
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.item.PokePuffItem
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.DISLIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.item.PokePuffItem.Companion.LIKED_FLAVOR_MULTIPLIER
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
|
||||
object PokePuffUtils {
|
||||
|
||||
fun calculateFriendshipChange(stack: ItemStack, nature: Nature): Int {
|
||||
val dominantFlavour = getDominantFlavour(stack) ?: return 0
|
||||
val isNeutralNature = nature.favouriteFlavour == nature.dislikedFlavour
|
||||
|
||||
val (baseFriendship) = calculateBaseFriendship(stack)
|
||||
|
||||
return when {
|
||||
dominantFlavour == nature.favouriteFlavour -> (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
dominantFlavour == Flavour.MILD && isNeutralNature -> (baseFriendship / 2.0).coerceAtLeast(1.0).toInt()
|
||||
else -> (-baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateBaseFriendship(stack: ItemStack): Pair<Double, Double> {
|
||||
val ingredientIds = stack.get(CobblemonItemComponents.INGREDIENT)?.ingredientIds ?: emptyList()
|
||||
var baseFriendship = 0.0
|
||||
|
||||
for (id in ingredientIds) {
|
||||
val item = BuiltInRegistries.ITEM.get(id)
|
||||
val itemStack = ItemStack(item)
|
||||
val seasoning = Seasonings.getFromItemStack(itemStack) ?: continue
|
||||
val dominantValue = seasoning.flavours?.values?.maxOrNull() ?: 0
|
||||
baseFriendship += dominantValue
|
||||
}
|
||||
|
||||
val ingredientStrings = ingredientIds.map { it.toString() }
|
||||
val hasSugar = "minecraft:sugar" in ingredientStrings
|
||||
val hasSweet = ingredientStrings.any { it.startsWith("cobblemon:") && it.endsWith("_sweet") }
|
||||
|
||||
val multiplier = when {
|
||||
hasSugar && hasSweet -> 5.0
|
||||
hasSweet -> 2.25
|
||||
hasSugar -> 1.75
|
||||
else -> 1.0
|
||||
}
|
||||
|
||||
val finalBaseFriendship = baseFriendship * multiplier
|
||||
|
||||
return finalBaseFriendship to multiplier
|
||||
}
|
||||
|
||||
fun getDominantFlavour(stack: ItemStack): Flavour? {
|
||||
val flavours = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours ?: return null
|
||||
val max = flavours.values.maxOrNull() ?: return null
|
||||
val dominant = flavours.filterValues { it == max }.keys
|
||||
|
||||
return when {
|
||||
dominant.isEmpty() -> null
|
||||
dominant.size > 1 -> Flavour.MILD
|
||||
else -> dominant.first()
|
||||
}
|
||||
}
|
||||
|
||||
fun getFlavorNatureLabel(flavour: Flavour): String {
|
||||
return when (flavour) {
|
||||
Flavour.SPICY -> "attack"
|
||||
Flavour.DRY -> "special_attack"
|
||||
Flavour.SWEET -> "speed"
|
||||
Flavour.BITTER -> "special_defense"
|
||||
Flavour.SOUR -> "defense"
|
||||
Flavour.MILD -> "neutral"
|
||||
}
|
||||
}
|
||||
|
||||
fun getFriendshipChangeLiked(stack: ItemStack): Int {
|
||||
val (baseFriendship) = calculateBaseFriendship(stack)
|
||||
return (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
}
|
||||
|
||||
fun getFriendshipChangeDisliked(stack: ItemStack): Int {
|
||||
val (baseFriendship) = calculateBaseFriendship(stack)
|
||||
return (baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
|
||||
}
|
||||
}
|
||||
@@ -21,33 +21,43 @@ import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.Item
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
data class Seasoning(
|
||||
val ingredient: RegistryLikeCondition<Item>,
|
||||
@SerializedName("flavours", alternate = ["flavors"])
|
||||
val flavours: Map<Flavour, Int>,
|
||||
val flavours: Map<Flavour, Int>? = null,
|
||||
@SerializedName("colour", alternate = ["color"])
|
||||
val colour: DyeColor,
|
||||
@SerializedName("baitEffects")
|
||||
val baitEffects: List<SpawnBait.Effect> = emptyList()
|
||||
val baitEffects: List<SpawnBait.Effect>? = null,
|
||||
@SerializedName("food")
|
||||
val food: Food? = null,
|
||||
@SerializedName("mobEffects")
|
||||
val mobEffects: List<SerializableMobEffectInstance>? = null
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Seasoning> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
ITEM_REGISTRY_LIKE_CODEC.fieldOf("ingredient").forGetter { it.ingredient },
|
||||
Codec.unboundedMap(Flavour.CODEC, Codec.INT).fieldOf("flavours").forGetter { it.flavours }, // Use map codec
|
||||
Codec.unboundedMap(Flavour.CODEC, Codec.INT).optionalFieldOf("flavours").forGetter { Optional.ofNullable(it.flavours) },
|
||||
DyeColor.CODEC.fieldOf("colour").forGetter { it.colour },
|
||||
SpawnBait.Effect.CODEC.listOf().optionalFieldOf("baitEffects", emptyList()).forGetter { it.baitEffects }
|
||||
).apply(builder, ::Seasoning)
|
||||
SpawnBait.Effect.CODEC.listOf().optionalFieldOf("baitEffects").forGetter { Optional.ofNullable(it.baitEffects) },
|
||||
Food.CODEC.optionalFieldOf("food").forGetter { Optional.ofNullable(it.food) },
|
||||
SerializableMobEffectInstance.CODEC.listOf().optionalFieldOf("mobEffects").forGetter { Optional.ofNullable(it.mobEffects) }
|
||||
).apply(builder) { item, flavours, colour, baitEffects, food, mobEffects ->
|
||||
Seasoning(
|
||||
item,
|
||||
flavours.getOrNull(),
|
||||
colour,
|
||||
baitEffects.getOrNull(),
|
||||
food.getOrNull(),
|
||||
mobEffects.getOrNull()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val STREAM_CODEC: StreamCodec<ByteBuf, Seasoning> = ByteBufCodecs.fromCodec(CODEC)
|
||||
|
||||
val BLANK_SEASONING = Seasoning(
|
||||
ingredient = RegistryLikeIdentifierCondition<Item>(cobblemonResource("blank")),
|
||||
flavours = Flavour.entries.associate { it to 0 },
|
||||
colour = DyeColor.WHITE,
|
||||
baitEffects = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitUtils
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.item.components.FoodComponent
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.SeasoningRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
|
||||
@@ -43,12 +46,6 @@ object Seasonings : JsonDataRegistry<Seasoning> {
|
||||
|
||||
val seasonings = mutableListOf<Seasoning>()
|
||||
|
||||
val BLANK_SEASONING = Seasoning(
|
||||
ingredient = RegistryLikeIdentifierCondition<Item>(cobblemonResource("blank")),
|
||||
flavours = emptyMap(),
|
||||
colour = DyeColor.WHITE
|
||||
)
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
SeasoningRegistrySyncPacket(seasonings.toList()).sendToPlayer(player)
|
||||
}
|
||||
@@ -59,7 +56,10 @@ object Seasonings : JsonDataRegistry<Seasoning> {
|
||||
it.identifier to Seasoning(
|
||||
ingredient = RegistryLikeIdentifierCondition(it.identifier),
|
||||
flavours = it.flavours.toMap(),
|
||||
colour = it.colour
|
||||
colour = it.colour,
|
||||
baitEffects = emptyList(),
|
||||
food = Food(),
|
||||
mobEffects = emptyList()
|
||||
)
|
||||
}.toMutableMap()
|
||||
finalData.putAll(data)
|
||||
@@ -71,16 +71,54 @@ object Seasonings : JsonDataRegistry<Seasoning> {
|
||||
this.seasonings.addAll(seasonings)
|
||||
}
|
||||
|
||||
fun getFlavoursFromItemStack(stack: ItemStack): Map<Flavour, Int> {
|
||||
fun getFlavoursFromItemStack(stack: ItemStack): Map<Flavour, Int>? {
|
||||
val holder = stack.itemHolder
|
||||
val seasoning = seasonings.find { it.ingredient.fits(holder) } ?: BLANK_SEASONING
|
||||
val inherentFlavours = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours ?: emptyMap()
|
||||
val seasoning = seasonings.find { it.ingredient.fits(holder) }
|
||||
val inherentFlavours = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours
|
||||
val seasoningFlavours = seasoning?.flavours
|
||||
|
||||
return seasoning.flavours.mapValues { (flavour, value) -> inherentFlavours.getOrDefault(flavour, 0) + value }
|
||||
if (seasoningFlavours.isNullOrEmpty() && inherentFlavours.isNullOrEmpty())
|
||||
return null
|
||||
|
||||
return (seasoningFlavours ?: emptyMap()).mapValues { (flavour, value) ->
|
||||
value + (inherentFlavours?.getOrDefault(flavour, 0) ?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun hasFlavors(stack: ItemStack): Boolean {
|
||||
val flavors = getFlavoursFromItemStack(stack)
|
||||
return !flavors.isNullOrEmpty() && flavors.any { it.value != 0 }
|
||||
}
|
||||
|
||||
fun getFoodComponentFromItemStack(stack: ItemStack): FoodComponent? {
|
||||
return getFromItemStack(stack)?.food?.toComponent()
|
||||
}
|
||||
|
||||
fun hasFood(stack: ItemStack): Boolean {
|
||||
val effects = getFromItemStack(stack)?.food ?: return false
|
||||
return effects.hunger > 0 || effects.saturation > 0f
|
||||
}
|
||||
|
||||
fun getMobEffectsFromItemStack(stack: ItemStack): List<SerializableMobEffectInstance> {
|
||||
return getFromItemStack(stack)?.mobEffects ?: emptyList()
|
||||
}
|
||||
|
||||
fun hasMobEffect(stack: ItemStack): Boolean {
|
||||
return !getFromItemStack(stack)?.mobEffects.isNullOrEmpty()
|
||||
}
|
||||
|
||||
fun getBaitEffectsFromItemStack(stack: ItemStack): List<SpawnBait.Effect> {
|
||||
return getFromItemStack(stack)?.baitEffects ?: emptyList()
|
||||
val primaryEffects = SpawnBaitEffects.getEffectsFromItemStack(stack)
|
||||
return if (primaryEffects.isNotEmpty()) {
|
||||
primaryEffects
|
||||
} else {
|
||||
getFromItemStack(stack)?.baitEffects ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasBaitEffects(stack: ItemStack): Boolean {
|
||||
return SpawnBaitEffects.getEffectsFromItemStack(stack).isNotEmpty() ||
|
||||
!(getFromItemStack(stack)?.baitEffects.isNullOrEmpty())
|
||||
}
|
||||
|
||||
fun getFromItemStack(stack: ItemStack): Seasoning? {
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.effect.MobEffect
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
|
||||
data class SerializableMobEffectInstance(
|
||||
val effect: ResourceLocation,
|
||||
val duration: Int,
|
||||
val amplifier: Int = 0,
|
||||
val ambient: Boolean = false,
|
||||
val visible: Boolean = true,
|
||||
val showIcon: Boolean = true
|
||||
) {
|
||||
fun toInstance(): MobEffectInstance {
|
||||
val holder = BuiltInRegistries.MOB_EFFECT.getHolder(effect)
|
||||
.orElseThrow { IllegalArgumentException("Unknown MobEffect: $effect") }
|
||||
return MobEffectInstance(holder, duration, amplifier, ambient, visible, showIcon)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<SerializableMobEffectInstance> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
ResourceLocation.CODEC.fieldOf("effect").forGetter { it.effect },
|
||||
Codec.INT.fieldOf("duration").forGetter { it.duration },
|
||||
Codec.INT.fieldOf("amplifier").forGetter { it.amplifier },
|
||||
Codec.BOOL.optionalFieldOf("ambient", false).forGetter { it.ambient },
|
||||
Codec.BOOL.optionalFieldOf("visible", true).forGetter { it.visible },
|
||||
Codec.BOOL.optionalFieldOf("showIcon", true).forGetter { it.showIcon }
|
||||
).apply(builder, ::SerializableMobEffectInstance)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.*
|
||||
@@ -71,6 +72,8 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val FRIENDSHIP_UPDATED = EventObservable<FriendshipUpdatedEvent>()
|
||||
@JvmField
|
||||
val FULLNESS_UPDATED = EventObservable<FullnessUpdatedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_FAINTED = EventObservable<PokemonFaintedEvent>()
|
||||
@JvmField
|
||||
val EVOLUTION_ACCEPTED = CancelableObservable<EvolutionAcceptedEvent>()
|
||||
@@ -245,8 +248,6 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val POKEROD_REEL = CancelableObservable<PokerodReelEvent>()
|
||||
@JvmField
|
||||
val BOBBER_BUCKET_CHOSEN = EventObservable<BobberBucketChosenEvent>()
|
||||
@JvmField
|
||||
val BOBBER_SPAWN_POKEMON_PRE = CancelableObservable<BobberSpawnPokemonEvent.Pre>()
|
||||
@JvmField
|
||||
val BOBBER_SPAWN_POKEMON_MODIFY = EventObservable<BobberSpawnPokemonEvent.Modify>()
|
||||
@@ -255,6 +256,9 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val BAIT_EFFECT_REGISTRATION = EventObservable<BaitEffectFunctionRegistryEvent>()
|
||||
|
||||
@JvmField
|
||||
val SPAWN_BUCKET_CHOSEN = EventObservable<SpawnBucketChosenEvent>()
|
||||
|
||||
// Baits and Lures
|
||||
@JvmField
|
||||
val BAIT_SPAWN_POKEMON_MODIFY = EventObservable<BaitSpawnPokemonEvent.Modify>()
|
||||
|
||||
+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.api.events.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
|
||||
/**
|
||||
* Event fired when the spawning system makes a decision on which bucket to spawn from.
|
||||
*/
|
||||
class SpawnBucketChosenEvent(
|
||||
/** The spawner this is happening within. */
|
||||
val spawner: Spawner,
|
||||
/** The cause of the spawn. */
|
||||
val spawnCause: SpawnCause,
|
||||
/** The bucket that was chosen. You can alter this. */
|
||||
var bucket: SpawnBucket,
|
||||
/** The final weights that were used to make the choice. */
|
||||
val bucketWeights: Map<SpawnBucket, Float>
|
||||
)
|
||||
@@ -10,15 +10,15 @@ package com.cobblemon.mod.common.api.events.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* Event fired when an entity is spawned using the [BestSpawner]. The entity that was spawned
|
||||
* and the [SpawningContext] of the spawn are provided. Cancelling this event will prevent the
|
||||
* and the [SpawnablePosition] of the spawn are provided. Cancelling this event will prevent the
|
||||
* entity from spawning.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since April 22nd, 2023
|
||||
*/
|
||||
class SpawnEvent<T : Entity>(val entity: T, val ctx: SpawningContext) : Cancelable()
|
||||
class SpawnEvent<T : Entity>(val entity: T, val spawnablePosition: SpawnablePosition) : Cancelable()
|
||||
+7
-7
@@ -8,12 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
@@ -28,23 +28,23 @@ interface BobberSpawnPokemonEvent {
|
||||
/**
|
||||
* Event that is fired before a Pokemon is spawned by a bobber.
|
||||
* @param bobber The PokeRodFishingBobberEntity that is spawning the Pokemon.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param spawnAction The spawn that is planned.
|
||||
* @param rod The ItemStack of the rod that the bobber is attached to.
|
||||
*/
|
||||
data class Pre(
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val chosenBucket: SpawnBucket,
|
||||
val spawnAction: SpawnAction<*>,
|
||||
val rod: ItemStack
|
||||
) : Cancelable(), BobberSpawnPokemonEvent
|
||||
|
||||
/**
|
||||
* Event that is fired when a Pokemon is modified before it is spawned by a bobber.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param spawnAction The [SpawnAction] that is motivating the spawn.
|
||||
* @param rod The ItemStack of the rod that the bobber is attached to.
|
||||
* @param pokemon The Pokemon that is modified.
|
||||
*/
|
||||
data class Modify(
|
||||
val chosenBucket: SpawnBucket,
|
||||
val spawnAction: SpawnAction<*>,
|
||||
val rod: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
@@ -58,12 +58,12 @@ interface BobberSpawnPokemonEvent {
|
||||
*/
|
||||
data class Post(
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val chosenBucket: SpawnBucket,
|
||||
val spawnAction: SpawnAction<*>,
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"chosen_bucket" to ObjectValue(chosenBucket, { it.name }),
|
||||
"chosen_bucket" to ObjectValue(spawnAction.bucket, { it.name }),
|
||||
"bait" to pokemon.level().itemRegistry.wrapAsHolder(bait.item).asMoLangValue(Registries.ITEM),
|
||||
"pokemon_entity" to pokemon.asMoLangValue()
|
||||
)
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* Event that is fired when a player owned Pokémon has its fullness changed
|
||||
*/
|
||||
data class FullnessUpdatedEvent(
|
||||
val pokemon: Pokemon,
|
||||
val newFullnessInitial: Int
|
||||
) {
|
||||
var newFullness: Int = newFullnessInitial
|
||||
set(value) {
|
||||
field = value.coerceIn(0, pokemon.getMaxFullness())
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.fishing
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpawnBaitRegistrySyncPacket
|
||||
@@ -85,7 +86,7 @@ object SpawnBaitEffects : JsonDataRegistry<SpawnBait> {
|
||||
(it.ingredient as RegistryLikeIdentifierCondition<Item>).identifier.path == identifier.path
|
||||
}
|
||||
|
||||
if (seasoning != null && seasoning.baitEffects.isNotEmpty()) {
|
||||
if (seasoning != null && !seasoning.baitEffects.isNullOrEmpty()) {
|
||||
return SpawnBait(
|
||||
item = seasoning.ingredient,
|
||||
effects = seasoning.baitEffects
|
||||
@@ -96,8 +97,9 @@ object SpawnBaitEffects : JsonDataRegistry<SpawnBait> {
|
||||
return null
|
||||
}
|
||||
|
||||
// if it has bait effects and is not listed as a seasoning then it is a bait
|
||||
fun isFishingBait(stack: ItemStack): Boolean {
|
||||
val holder = stack.itemHolder
|
||||
return stack.has(CobblemonItemComponents.BAIT_EFFECTS) || effectsMap.values.any { it.item.fits(holder) }
|
||||
return effectsMap.values.any { it.item.fits(holder) }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.fishing
|
||||
|
||||
import kotlin.math.ceil
|
||||
|
||||
object SpawnBaitUtils {
|
||||
fun mergeEffects(effects: List<SpawnBait.Effect>): List<SpawnBait.Effect> {
|
||||
val grouped = effects.groupBy { Pair(it.type, it.subcategory) }
|
||||
@@ -15,25 +17,20 @@ object SpawnBaitUtils {
|
||||
return grouped.map { (key, groupEffects) ->
|
||||
val (type, subcategory) = key
|
||||
|
||||
if (type == SpawnBait.Effects.RARITY_BUCKET) {
|
||||
// we want to keep the highest rarity bucket effect instead of merging them
|
||||
groupEffects.maxByOrNull { it.value }!!
|
||||
} else {
|
||||
val count = groupEffects.size
|
||||
val totalChance = groupEffects.sumOf { it.chance }
|
||||
val totalValue = groupEffects.sumOf { it.value }
|
||||
val count = groupEffects.size
|
||||
val totalChance = groupEffects.sumOf { it.chance }
|
||||
val totalValue = groupEffects.sumOf { it.value }
|
||||
|
||||
val multiplier = when (count) {
|
||||
2 -> 0.8
|
||||
3 -> 0.9
|
||||
else -> 1.0
|
||||
}
|
||||
|
||||
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
|
||||
val adjustedValue = totalValue * multiplier
|
||||
|
||||
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
|
||||
val multiplier = when (count) {
|
||||
2 -> 0.8
|
||||
3 -> 0.6
|
||||
else -> 1.0
|
||||
}
|
||||
|
||||
val adjustedChance = (totalChance * multiplier).coerceAtMost(1.0)
|
||||
val adjustedValue = ceil(totalValue * multiplier)
|
||||
|
||||
SpawnBait.Effect(type, subcategory, adjustedChance, adjustedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.callback.MoveSelectCallbacks
|
||||
import com.cobblemon.mod.common.api.callback.PartyMoveSelectCallbacks
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BagItemActionResponse
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
@@ -92,7 +93,13 @@ interface PokemonAndMoveSelectingItem {
|
||||
}
|
||||
}
|
||||
|
||||
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean
|
||||
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
|
||||
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
|
||||
return !pokemon.isFull()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
|
||||
fun canUseOnMove(stack: ItemStack, pokemon: Pokemon, move: Move): Boolean = canUseOnMove(stack, move)
|
||||
fun canUseOnMove(stack: ItemStack, move: Move): Boolean
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BagItemActionResponse
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
@@ -105,7 +106,13 @@ interface PokemonSelectingItem {
|
||||
}
|
||||
}
|
||||
|
||||
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean
|
||||
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
|
||||
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
|
||||
return !pokemon.isFull()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
|
||||
|
||||
fun interactWithSpecificBattle(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon): InteractionResultHolder<ItemStack> {
|
||||
@@ -136,6 +143,7 @@ interface PokemonSelectingItem {
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -32,6 +33,7 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.DialogueNPCInteractionConfiguration
|
||||
import com.cobblemon.mod.common.api.npc.configuration.interaction.ScriptNPCInteractionConfiguration
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
@@ -44,8 +46,11 @@ import com.cobblemon.mod.common.api.pokedex.SeenPercent
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
@@ -68,6 +73,7 @@ import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimati
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -96,6 +102,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
@@ -197,7 +204,15 @@ object MoLangFunctions {
|
||||
runtime.environment.variable = params.environment.variable
|
||||
runtime.environment.context = params.environment.context
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
runtime.resolve(expression)
|
||||
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
|
||||
if (delayInSeconds > 0.0f) {
|
||||
val tracker = if (Cobblemon.implementation.environment() == Environment.SERVER) ServerTaskTracker else ClientTaskTracker
|
||||
tracker.after(delayInSeconds) {
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
} else {
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
},
|
||||
"system_time_millis" to java.util.function.Function { _ ->
|
||||
DoubleValue(System.currentTimeMillis())
|
||||
@@ -344,6 +359,27 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("play_sound_on_server") { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = params.getString(1).uppercase()
|
||||
val x = params.getDouble(2)
|
||||
val y = params.getDouble(3)
|
||||
val z = params.getDouble(4)
|
||||
val player = params.getOrNull<MoValue>(5)?.let {
|
||||
if (it is StringValue) world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
else if (it is ObjectValue<*>) it.obj
|
||||
else null
|
||||
} as? ServerPlayer
|
||||
val volume = params.getDoubleOrNull(6)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(7)?.toFloat() ?: 1.0f
|
||||
|
||||
val packet = UnvalidatedPlaySoundS2CPacket(sound, SoundSource.valueOf(soundSource), x, y, z, volume, pitch)
|
||||
if (player != null) {
|
||||
packet.sendToPlayer(player)
|
||||
} else {
|
||||
packet.sendToPlayersAround(x, y, z, 16.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map.put("get_entities_around") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
@@ -404,6 +440,15 @@ object MoLangFunctions {
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map.put("play_sound_on_server") { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
|
||||
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
|
||||
|
||||
val packet = UnvalidatedPlaySoundS2CPacket(sound, soundSource, player.x, player.y, player.z, volume, pitch)
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
map.put("is_party_at_full_health") { _ ->
|
||||
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
|
||||
map.put("can_heal_at_healer") { params ->
|
||||
@@ -618,8 +663,9 @@ object MoLangFunctions {
|
||||
map.put("biome") { _ -> entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
|
||||
map.put("is_passenger") { DoubleValue(entity.isPassenger) }
|
||||
map.put("find_nearby_block") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val isTag = type.path.startsWith("#")
|
||||
val input = params.getString(0)
|
||||
val isTag = input.contains("#")
|
||||
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val range = params.getDoubleOrNull(1) ?: 10
|
||||
val blockPos = entity.level().getBlockStatesWithPos(AABB.ofSize(entity.position(), range.toDouble(), range.toDouble(), range.toDouble()))
|
||||
.filter { it.first.blockHolder.let { if (isTag) it.`is`(TagKey.create(Registries.BLOCK, type)) else it.`is`(type) } }
|
||||
@@ -675,6 +721,17 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entity is Schedulable) {
|
||||
map.put("run_molang_after") { params ->
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDouble(1).toFloat()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
entity.after(delayInSeconds) {
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
// q.entity.spawn_bedrock_particles(effect, locator, [player]) - sends to everyone nearby or just to the player if they're set. Locator is necessary even if unused on non-posables.
|
||||
map.put("spawn_bedrock_particles") { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
@@ -811,6 +868,17 @@ object MoLangFunctions {
|
||||
npc.hideNameTag = !nameTagVisible
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("unset_interaction") {
|
||||
npc.interaction = null
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("set_dialogue_interaction") { params ->
|
||||
val dialogue = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = DialogueNPCInteractionConfiguration().also {
|
||||
it.dialogue = dialogue
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("set_script_interaction") { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = ScriptNPCInteractionConfiguration().also {
|
||||
@@ -1184,7 +1252,7 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val spawningContextFunctions = mutableListOf<(SpawningContext) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val spawningContextFunctions = mutableListOf<(SpawnablePosition) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ spawningContext ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
@@ -1198,7 +1266,6 @@ object MoLangFunctions {
|
||||
map.put("moon_phase") { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
map.put("can_see_sky") { _ -> DoubleValue(spawningContext.canSeeSky) }
|
||||
map.put("sky_light") { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
map.put("bucket") { _ -> StringValue(spawningContext.cause.bucket.name) }
|
||||
map.put("player") { _ ->
|
||||
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) {
|
||||
@@ -1335,6 +1402,8 @@ object MoLangFunctions {
|
||||
map.put("hitbox_height") { DoubleValue(species.hitbox.height) }
|
||||
map.put("hitbox_fixed") { DoubleValue(species.hitbox.fixed) }
|
||||
map.put("catch_rate") { DoubleValue(species.catchRate) }
|
||||
map.put("labels") { return@put species.labels.asArrayValue { StringValue(it) } }
|
||||
map.put("has_label") { params -> DoubleValue(species.labels.contains(params.getString(0))) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -1536,7 +1605,7 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun SpawningContext.asMoLangValue(): ObjectValue<SpawningContext> {
|
||||
fun SpawnablePosition.asMoLangValue(): ObjectValue<SpawnablePosition> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
object HiddenPowerUtil {
|
||||
val hiddenPowerTable = arrayOf(
|
||||
ElementalTypes.FIGHTING,
|
||||
ElementalTypes.FLYING,
|
||||
ElementalTypes.POISON,
|
||||
ElementalTypes.GROUND,
|
||||
ElementalTypes.ROCK,
|
||||
ElementalTypes.BUG,
|
||||
ElementalTypes.GHOST,
|
||||
ElementalTypes.STEEL,
|
||||
ElementalTypes.FIRE,
|
||||
ElementalTypes.WATER,
|
||||
ElementalTypes.GRASS,
|
||||
ElementalTypes.ELECTRIC,
|
||||
ElementalTypes.PSYCHIC,
|
||||
ElementalTypes.ICE,
|
||||
ElementalTypes.DRAGON,
|
||||
ElementalTypes.DARK
|
||||
)
|
||||
|
||||
fun getHiddenPowerType(pokemon: Pokemon?): ElementalType {
|
||||
if (pokemon == null) {
|
||||
return ElementalTypes.NORMAL
|
||||
}
|
||||
val ivs = pokemon.ivs
|
||||
val ivArray = arrayOf(
|
||||
ivs[Stats.HP],
|
||||
ivs[Stats.ATTACK],
|
||||
ivs[Stats.DEFENCE],
|
||||
ivs[Stats.SPEED],
|
||||
ivs[Stats.SPECIAL_ATTACK],
|
||||
ivs[Stats.SPECIAL_DEFENCE]
|
||||
).map { it ?: return@getHiddenPowerType ElementalTypes.NORMAL }
|
||||
var tableIndex = 0
|
||||
ivArray.forEachIndexed { index, it ->
|
||||
tableIndex += (it % 2) shl index
|
||||
}
|
||||
tableIndex = tableIndex * 15 / 63
|
||||
return hiddenPowerTable[tableIndex.coerceAtMost(hiddenPowerTable.size - 1)]
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
@@ -100,24 +99,6 @@ open class MoveTemplate(
|
||||
Moves::getByName,
|
||||
MoveTemplate::name
|
||||
) { id -> "No MoveTemplate for ID $id" }
|
||||
val hiddenPowerTable = arrayOf(
|
||||
ElementalTypes.FIGHTING,
|
||||
ElementalTypes.FLYING,
|
||||
ElementalTypes.POISON,
|
||||
ElementalTypes.GROUND,
|
||||
ElementalTypes.ROCK,
|
||||
ElementalTypes.BUG,
|
||||
ElementalTypes.GHOST,
|
||||
ElementalTypes.STEEL,
|
||||
ElementalTypes.FIRE,
|
||||
ElementalTypes.WATER,
|
||||
ElementalTypes.GRASS,
|
||||
ElementalTypes.ELECTRIC,
|
||||
ElementalTypes.PSYCHIC,
|
||||
ElementalTypes.ICE,
|
||||
ElementalTypes.DRAGON,
|
||||
ElementalTypes.DARK
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,21 +127,7 @@ open class MoveTemplate(
|
||||
return this.elementalType
|
||||
}
|
||||
if (name == "hiddenpower") {
|
||||
val ivs = pokemon.ivs
|
||||
val ivArray = arrayOf(
|
||||
ivs[Stats.HP],
|
||||
ivs[Stats.ATTACK],
|
||||
ivs[Stats.DEFENCE],
|
||||
ivs[Stats.SPEED],
|
||||
ivs[Stats.SPECIAL_ATTACK],
|
||||
ivs[Stats.SPECIAL_DEFENCE]
|
||||
).map { it ?: return@getEffectiveElementalType ElementalTypes.NORMAL }
|
||||
var tableIndex = 0
|
||||
ivArray.forEachIndexed { index, it ->
|
||||
tableIndex += (it % 2) shl index
|
||||
}
|
||||
tableIndex = tableIndex * 15 / 63
|
||||
return hiddenPowerTable[tableIndex.coerceAtMost(hiddenPowerTable.size - 1)]
|
||||
return HiddenPowerUtil.getHiddenPowerType(pokemon)
|
||||
}
|
||||
// TODO: Handle ability suppression: clientactivebattlepokemon needs data about volatiles
|
||||
// TODO: Handle Liquid Voice: need to know what moves have the sound flag
|
||||
|
||||
@@ -96,6 +96,8 @@ object CobblemonPermissions {
|
||||
@JvmStatic
|
||||
val PC = this.create("${COMMAND_PREFIX}pc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val PC_SEARCH = this.create("${COMMAND_PREFIX}pcsearch", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
@JvmStatic
|
||||
val POKEBOX = this.create("${COMMAND_PREFIX}pokebox", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
@@ -42,6 +43,7 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.brigadier.StringReader
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.JsonOps
|
||||
import java.util.UUID
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
@@ -50,6 +52,7 @@ import net.minecraft.commands.arguments.item.ItemParser
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.chat.Component
|
||||
@@ -58,6 +61,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
/**
|
||||
* A grouping of typical, selectable properties for a Pokémon. This is serializable
|
||||
@@ -385,7 +389,13 @@ open class PokemonProperties {
|
||||
shiny?.let { pokemon.shiny = it }
|
||||
gender?.let { pokemon.gender = it }
|
||||
level?.let { pokemon.level = it }
|
||||
friendship?.let { pokemon.setFriendship(it) }
|
||||
friendship.also {
|
||||
if (it != null) {
|
||||
pokemon.setFriendship(it)
|
||||
} else {
|
||||
pokemon.setFriendship(pokemon.form.baseFriendship)
|
||||
}
|
||||
}
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
|
||||
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
|
||||
@@ -395,6 +405,9 @@ open class PokemonProperties {
|
||||
ivs.forEach { stat ->
|
||||
pokemon.setIV(stat.key, stat.value)
|
||||
}
|
||||
ivs.hyperTrainedIVs.forEach { stat ->
|
||||
pokemon.hyperTrainIV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
evs?.let { evs ->
|
||||
evs.forEach { stat ->
|
||||
@@ -626,8 +639,8 @@ open class PokemonProperties {
|
||||
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
|
||||
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
|
||||
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(CompoundTag())) }
|
||||
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(CompoundTag())) }
|
||||
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().get()) }
|
||||
evs?.let { nbt.put(DataKeys.POKEMON_EVS, EVs.CODEC.encodeStart(NbtOps.INSTANCE, it).result().get()) }
|
||||
type?.let { nbt.putString(DataKeys.ELEMENTAL_TYPE, it) }
|
||||
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
|
||||
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
|
||||
@@ -657,8 +670,8 @@ open class PokemonProperties {
|
||||
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
|
||||
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
|
||||
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
|
||||
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) IVs().loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs else null
|
||||
evs = if (tag.contains(DataKeys.POKEMON_EVS)) EVs().loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs else null
|
||||
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) IVs.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(DataKeys.POKEMON_IVS)).result().getOrNull()?.first else null
|
||||
evs = if (tag.contains(DataKeys.POKEMON_EVS)) EVs.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(DataKeys.POKEMON_EVS)).result().getOrNull()?.first else null
|
||||
type = if (tag.contains(DataKeys.ELEMENTAL_TYPE)) tag.getString(DataKeys.ELEMENTAL_TYPE) else null
|
||||
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
|
||||
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
|
||||
@@ -690,8 +703,8 @@ open class PokemonProperties {
|
||||
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
|
||||
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
|
||||
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
|
||||
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
|
||||
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
|
||||
ivs?.let { json.add(DataKeys.POKEMON_IVS, IVs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()) }
|
||||
evs?.let { json.add(DataKeys.POKEMON_EVS, EVs.CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()) }
|
||||
type?.let { json.addProperty(DataKeys.ELEMENTAL_TYPE, it) }
|
||||
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
|
||||
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
|
||||
@@ -722,8 +735,8 @@ open class PokemonProperties {
|
||||
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
|
||||
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
|
||||
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
|
||||
ivs = json.getAsJsonObject(DataKeys.POKEMON_IVS)?.let { IVs().loadFromJSON(it) } as? IVs
|
||||
evs = json.getAsJsonObject(DataKeys.POKEMON_EVS)?.let { EVs().loadFromJSON(it) } as? EVs
|
||||
ivs = json.getAsJsonObject(DataKeys.POKEMON_IVS)?.let { IVs.CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first }
|
||||
evs = json.getAsJsonObject(DataKeys.POKEMON_EVS)?.let { EVs.CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first }
|
||||
type = json.get(DataKeys.ELEMENTAL_TYPE)?.asString
|
||||
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
|
||||
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
|
||||
@@ -798,4 +811,19 @@ open class PokemonProperties {
|
||||
return potentialAbility.template.create(false, potentialAbility.priority)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared logic for figuring out what range of levels is possible for a Pokémon, given that some
|
||||
* kind of optional level range has been requested. Used mainly as a thing for spawning to avoid
|
||||
* copy-pasting code.
|
||||
*/
|
||||
fun deriveLevelRange(levelRange: IntRange?): IntRange {
|
||||
return levelRange.let { levelRange ->
|
||||
val pokemonLevel = level
|
||||
levelRange
|
||||
?: pokemonLevel?.until(pokemonLevel)
|
||||
?: IntRange(1, config.maxPokemonLevel)
|
||||
}
|
||||
}
|
||||
|
||||
fun hasSpecies() = species?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) } != null
|
||||
}
|
||||
+4
-2
@@ -39,9 +39,9 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
fun clampPassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
|
||||
fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun updatePassengerRotation(settings: Settings, state: State, entity: PokemonEntity, driver: LivingEntity) {}
|
||||
fun updatePassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2
|
||||
|
||||
@@ -88,6 +88,8 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
|
||||
|
||||
fun createDefaultState(settings: Settings): State
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,8 +23,8 @@ import net.minecraft.world.phys.Vec3
|
||||
* @author landonjw
|
||||
*/
|
||||
open class RidingBehaviourState {
|
||||
open val rideVelocity: SidedRidingState<Vec3> = ridingState(Vec3.ZERO, Side.BOTH)
|
||||
open val stamina: SidedRidingState<Float> = ridingState(1.0F, Side.BOTH)
|
||||
open val rideVelocity: SidedRidingState<Vec3> = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
open val stamina: SidedRidingState<Float> = ridingState(1.0F, Side.CLIENT)
|
||||
|
||||
open fun reset() {
|
||||
rideVelocity.set(Vec3.ZERO, true)
|
||||
|
||||
+13
-8
@@ -8,23 +8,28 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object RidingBehaviours {
|
||||
val behaviours = mutableMapOf<ResourceLocation, RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>>()
|
||||
|
||||
init {
|
||||
register(BirdAirBehaviour.KEY, BirdAirBehaviour())
|
||||
register(BirdBehaviour.KEY, BirdBehaviour())
|
||||
register(DolphinBehaviour.KEY, DolphinBehaviour())
|
||||
register(GenericLandBehaviour.KEY, GenericLandBehaviour())
|
||||
register(GenericSwimBehaviour.KEY, GenericSwimBehaviour())
|
||||
register(GliderAirBehaviour.KEY, GliderAirBehaviour())
|
||||
register(HorseBehaviour.KEY, HorseBehaviour())
|
||||
register(BoatBehaviour.KEY, BoatBehaviour())
|
||||
register(GliderBehaviour.KEY, GliderBehaviour())
|
||||
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
|
||||
register(JetAirBehaviour.KEY, JetAirBehaviour())
|
||||
register(SwimDashBehaviour.KEY, SwimDashBehaviour())
|
||||
register(VehicleLandBehaviour.KEY, VehicleLandBehaviour())
|
||||
register(JetBehaviour.KEY, JetBehaviour())
|
||||
register(BurstBehaviour.KEY, BurstBehaviour())
|
||||
register(VehicleBehaviour.KEY, VehicleBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
+16
-1
@@ -26,7 +26,7 @@ import net.minecraft.world.phys.Vec3
|
||||
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
|
||||
|
||||
override val key = behaviour.key
|
||||
|
||||
|
||||
override fun getRidingStyle(settings: Settings, state: State): RidingStyle {
|
||||
return behaviour.getRidingStyle(settings, state)
|
||||
}
|
||||
@@ -45,6 +45,16 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
|
||||
return behaviour.speed(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.updatePassengerRotation(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.clampPassengerRotation(settings, state, vehicle, driver)
|
||||
@@ -175,6 +185,11 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
|
||||
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 createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
-267
@@ -1,267 +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.riding.behaviour.types
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
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 net.minecraft.world.phys.shapes.Shapes
|
||||
|
||||
class GenericSwimBehaviour : RidingBehaviour<GenericSwimSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("swim/generic")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: GenericSwimSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<GenericSwimSettings, RidingBehaviourState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
val driveFactor = runtime.resolveFloat(settings.driveFactor)
|
||||
val strafeFactor = runtime.resolveFloat(settings.strafeFactor)
|
||||
val f = driver.xxa * strafeFactor
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(settings.reverseDriveFactor)
|
||||
}
|
||||
|
||||
val velocity = Vec3(f.toDouble(), 0.0, g.toDouble())
|
||||
|
||||
return velocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
|
||||
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
|
||||
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return regularGravity
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: GenericSwimSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: GenericSwimSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GenericSwimSettings) = RidingBehaviourState()
|
||||
|
||||
}
|
||||
|
||||
class GenericSwimSettings : RidingBehaviourSettings {
|
||||
override val key = GenericSwimBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
|
||||
var speed = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var driveFactor = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
private set
|
||||
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
buffer.writeExpression(jumpVector[1])
|
||||
buffer.writeExpression(jumpVector[2])
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(driveFactor)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(strafeFactor)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression()
|
||||
)
|
||||
speed = buffer.readExpression()
|
||||
driveFactor = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
strafeFactor = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
+46
-46
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
@@ -33,31 +33,31 @@ import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/bird")
|
||||
}
|
||||
|
||||
override val key: ResourceLocation = KEY
|
||||
|
||||
override fun getRidingStyle(settings: BirdAirSettings, state: BirdAirState): RidingStyle {
|
||||
override fun getRidingStyle(settings: BirdSettings, state: BirdState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BirdAirSettings, BirdAirState>(PoseType.HOVER)
|
||||
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
override fun isActive(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): PoseType {
|
||||
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -65,8 +65,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
@@ -74,8 +74,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
@@ -122,7 +122,7 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
fun calculateRideSpaceVel(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity, driver: Player) {
|
||||
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
@@ -240,8 +240,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
@@ -282,8 +282,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
@@ -367,8 +367,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
@@ -376,8 +376,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -385,8 +385,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
@@ -395,8 +395,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
@@ -404,8 +404,8 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -420,16 +420,16 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
@@ -439,44 +439,44 @@ class BirdAirBehaviour : RidingBehaviour<BirdAirSettings, BirdAirState> {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Double {
|
||||
override fun inertia(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRoll(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun turnOffOnGround(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: BirdAirSettings, state: BirdAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun dismountOnShift(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: BirdAirSettings,
|
||||
state: BirdAirState,
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BirdAirSettings) = BirdAirState()
|
||||
override fun createDefaultState(settings: BirdSettings) = BirdState()
|
||||
|
||||
}
|
||||
|
||||
class BirdAirSettings : RidingBehaviourSettings {
|
||||
override val key = BirdAirBehaviour.KEY
|
||||
class BirdSettings : RidingBehaviourSettings {
|
||||
override val key = BirdBehaviour.KEY
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
@@ -518,7 +518,7 @@ class BirdAirSettings : RidingBehaviourSettings {
|
||||
}
|
||||
}
|
||||
|
||||
class BirdAirState : RidingBehaviourState() {
|
||||
class BirdState : RidingBehaviourState() {
|
||||
var gliding = ridingState(false, Side.CLIENT)
|
||||
var lastGlide = ridingState(-100L, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
@@ -542,10 +542,10 @@ class BirdAirState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BirdAirState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
|
||||
return "BirdState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = BirdAirState().also {
|
||||
override fun copy() = BirdState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.gliding.set(this.gliding.get(), forced = true)
|
||||
@@ -554,7 +554,7 @@ class BirdAirState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is BirdAirState) return false
|
||||
if (previous !is BirdState) return false
|
||||
if (previous.gliding.get() != gliding.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
+27
-27
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
@@ -29,34 +29,34 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourState> {
|
||||
class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/glider")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: GliderAirSettings, state: RidingBehaviourState): RidingStyle {
|
||||
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<GliderAirSettings, RidingBehaviourState>(PoseType.HOVER)
|
||||
val poseProvider = PoseProvider<GliderSettings, RidingBehaviourState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun isActive(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pose(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
|
||||
override fun pose(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return vehicle.runtime.resolveFloat(settings.speed)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
@@ -65,7 +65,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
@@ -80,7 +80,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
@@ -90,7 +90,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
@@ -109,12 +109,12 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
|
||||
}
|
||||
|
||||
override fun canJump(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
override fun canJump(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
@@ -123,7 +123,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
@@ -133,7 +133,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
@@ -142,7 +142,7 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
@@ -150,12 +150,12 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun useAngVelSmoothing(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: GliderAirSettings,
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
@@ -163,35 +163,35 @@ class GliderAirBehaviour : RidingBehaviour<GliderAirSettings, RidingBehaviourSta
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
override fun inertia(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRoll(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun turnOffOnGround(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun dismountOnShift(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRotatePokemonHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: GliderAirSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRotatePlayerHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GliderAirSettings) = RidingBehaviourState()
|
||||
override fun createDefaultState(settings: GliderSettings) = RidingBehaviourState()
|
||||
}
|
||||
|
||||
class GliderAirSettings : RidingBehaviourSettings {
|
||||
override val key = GliderAirBehaviour.KEY
|
||||
class GliderSettings : RidingBehaviourSettings {
|
||||
override val key = GliderBehaviour.KEY
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
private set
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
+43
-43
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
@@ -30,21 +30,21 @@ import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/jet")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: JetAirSettings, state: JetAirState): RidingStyle {
|
||||
override fun getRidingStyle(settings: JetSettings, state: JetState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<JetAirSettings, JetAirState>(PoseType.HOVER)
|
||||
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
|
||||
|
||||
override fun isActive(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun isActive(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
@@ -59,11 +59,11 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): PoseType {
|
||||
override fun pose(settings: JetSettings, state: JetState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
override fun speed(settings: JetSettings, state: JetState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
@@ -104,8 +104,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
@@ -113,8 +113,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
@@ -153,8 +153,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
@@ -186,8 +186,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
@@ -211,8 +211,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
@@ -273,8 +273,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
@@ -282,8 +282,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -291,8 +291,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
@@ -301,8 +301,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
@@ -310,8 +310,8 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -326,52 +326,52 @@ class JetAirBehaviour : RidingBehaviour<JetAirSettings, JetAirState> {
|
||||
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Double {
|
||||
override fun inertia(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRoll(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun turnOffOnGround(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun dismountOnShift(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: JetAirSettings,
|
||||
state: JetAirState,
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: JetAirSettings, state: JetAirState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRotatePlayerHead(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: JetAirSettings) = JetAirState()
|
||||
override fun createDefaultState(settings: JetSettings) = JetState()
|
||||
}
|
||||
|
||||
class JetAirSettings : RidingBehaviourSettings {
|
||||
override val key = JetAirBehaviour.KEY
|
||||
class JetSettings : RidingBehaviourSettings {
|
||||
override val key = JetBehaviour.KEY
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
@@ -428,7 +428,7 @@ class JetAirSettings : RidingBehaviourSettings {
|
||||
}
|
||||
}
|
||||
|
||||
class JetAirState : RidingBehaviourState() {
|
||||
class JetState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseXForce = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseYForce = ridingState(0.0, Side.CLIENT)
|
||||
@@ -451,7 +451,7 @@ class JetAirState : RidingBehaviourState() {
|
||||
currMouseYForce.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = JetAirState().also {
|
||||
override fun copy() = JetState().also {
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
@@ -460,7 +460,7 @@ class JetAirState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is JetAirState) return false
|
||||
if (previous !is JetState) return false
|
||||
if (previous.currSpeed != currSpeed) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
+59
-59
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
@@ -30,25 +30,25 @@ import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandState> {
|
||||
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/generic")
|
||||
val KEY = cobblemonResource("land/horse")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: GenericLandSettings, state: GenericLandState): RidingStyle {
|
||||
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<GenericLandSettings, GenericLandState>(PoseType.STAND)
|
||||
val poseProvider = PoseProvider<HorseSettings, HorseState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().z) > 0.0
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
@@ -66,16 +66,16 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -102,8 +102,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
@@ -128,8 +128,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
@@ -143,8 +143,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
@@ -161,8 +161,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
* without it being instant.
|
||||
*/
|
||||
fun calcRotAmount(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
@@ -198,8 +198,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
|
||||
|
||||
override fun velocity(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
@@ -209,8 +209,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
@@ -293,8 +293,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
@@ -303,8 +303,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
@@ -324,8 +324,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
@@ -333,8 +333,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -343,8 +343,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
@@ -353,8 +353,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
@@ -362,8 +362,8 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -371,16 +371,16 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
@@ -392,58 +392,58 @@ class GenericLandBehaviour : RidingBehaviour<GenericLandSettings, GenericLandSta
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: GenericLandSettings,
|
||||
state: GenericLandState,
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GenericLandSettings) = GenericLandState()
|
||||
override fun createDefaultState(settings: HorseSettings) = HorseState()
|
||||
}
|
||||
|
||||
class GenericLandSettings : RidingBehaviourSettings {
|
||||
override val key = GenericLandBehaviour.KEY
|
||||
class HorseSettings : RidingBehaviourSettings {
|
||||
override val key = HorseBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -486,7 +486,7 @@ class GenericLandSettings : RidingBehaviourSettings {
|
||||
|
||||
}
|
||||
|
||||
class GenericLandState : RidingBehaviourState() {
|
||||
class HorseState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
|
||||
@@ -508,7 +508,7 @@ class GenericLandState : RidingBehaviourState() {
|
||||
inAir.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = GenericLandState().also {
|
||||
override fun copy() = HorseState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
@@ -516,7 +516,7 @@ class GenericLandState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is GenericLandState) return false
|
||||
if (previous !is HorseState) return false
|
||||
if (previous.sprinting.get() != sprinting.get()) return true
|
||||
if (previous.inAir.get() != inAir.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
+46
-46
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
@@ -31,7 +31,7 @@ import kotlin.math.absoluteValue
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandState> {
|
||||
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/vehicle")
|
||||
|
||||
@@ -54,14 +54,14 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: VehicleLandSettings, state: VehicleLandState): RidingStyle {
|
||||
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<VehicleLandSettings, VehicleLandState>(PoseType.STAND)
|
||||
val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
|
||||
override fun isActive(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
@@ -76,13 +76,13 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): PoseType {
|
||||
override fun pose(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -101,8 +101,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
@@ -113,8 +113,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
@@ -134,8 +134,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
@@ -144,8 +144,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
@@ -164,8 +164,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
@@ -173,8 +173,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -182,8 +182,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
@@ -195,8 +195,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
@@ -204,8 +204,8 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -213,67 +213,67 @@ class VehicleLandBehaviour : RidingBehaviour<VehicleLandSettings, VehicleLandSta
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Double {
|
||||
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: VehicleLandSettings, state: VehicleLandState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRoll(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: VehicleLandSettings,
|
||||
state: VehicleLandState,
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: VehicleLandSettings) = VehicleLandState()
|
||||
override fun createDefaultState(settings: VehicleSettings) = VehicleState()
|
||||
}
|
||||
|
||||
class VehicleLandSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleLandBehaviour.KEY
|
||||
class VehicleSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleBehaviour.KEY
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -329,7 +329,7 @@ class VehicleLandSettings : RidingBehaviourSettings {
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleLandState : RidingBehaviourState() {
|
||||
class VehicleState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.BOTH)
|
||||
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
|
||||
|
||||
@@ -340,10 +340,10 @@ class VehicleLandState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "VehicleLandState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
|
||||
return "VehicleState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = VehicleLandState().also {
|
||||
override fun copy() = VehicleState().also {
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
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.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("liquid/boat")
|
||||
val TOP_SPEED = 2.0
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BoatSettings, BoatState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
if (state.jumpBuffer.get() != -1) {
|
||||
return true
|
||||
}
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any true
|
||||
}
|
||||
val blockState = vehicle.level().getBlockState(it)
|
||||
return@any !blockState.fluidState.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if (state.jumpBuffer.get() > 5) {
|
||||
if ((vehicle.isInWater || vehicle.isUnderWater || vehicle.onGround())) {
|
||||
state.jumpBuffer.set(-1)
|
||||
}
|
||||
}
|
||||
else if (state.jumpBuffer.get() > -1) {
|
||||
state.jumpBuffer.set(state.jumpBuffer.get() + 1)
|
||||
}
|
||||
|
||||
if (driver.zza == 0f && (vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
if (state.staminaBuffer.get() >= 20) {
|
||||
val staminaIncrease = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 2.0)
|
||||
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
|
||||
}
|
||||
}
|
||||
else if (abs(state.rideVelocity.get().z) > 0.1) {
|
||||
consumeStamina(vehicle, state, 0.01f)
|
||||
}
|
||||
state.staminaBuffer.set(state.staminaBuffer.get() + 1)
|
||||
super.tick(settings, state, vehicle, driver, input)
|
||||
}
|
||||
|
||||
override fun pose(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
state.deltaRotation.set(state.deltaRotation.get() * 0.8)
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + state.deltaRotation.get().toFloat())
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
applyStrafeRotation(vehicle, driver, state)
|
||||
|
||||
var velocity = state.rideVelocity.get()
|
||||
velocity = applyVelocityFromInput(velocity, vehicle, driver, state)
|
||||
velocity = applyGravity(velocity, vehicle, settings, state)
|
||||
velocity = applyJump(velocity, vehicle, driver, state)
|
||||
state.rideVelocity.set(velocity)
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
|
||||
val speed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.LIQUID, 0.8, TOP_SPEED)
|
||||
val acceleration = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.LIQUID, 0.05, 0.5)
|
||||
|
||||
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
return velocity
|
||||
}
|
||||
|
||||
val forwardInput = driver.zza.toDouble()
|
||||
val delta = when {
|
||||
!hasStamina(state) || forwardInput == 0.0 -> -velocity.z * 0.04
|
||||
forwardInput < 0.0 -> when {
|
||||
velocity.z < 0.0 -> forwardInput * acceleration * 0.5
|
||||
else -> min(forwardInput * acceleration * 0.5, -velocity.z * 0.04) // We never want it to be slower to reverse than no input
|
||||
}
|
||||
else -> forwardInput * acceleration
|
||||
}
|
||||
|
||||
return Vec3(
|
||||
velocity.x,
|
||||
velocity.y,
|
||||
Mth.clamp(velocity.z + delta, -speed * 0.25, speed)
|
||||
)
|
||||
}
|
||||
|
||||
private fun applyJump(velocity: Vec3, vehicle: PokemonEntity, driver: Player, state: BoatState): Vec3 {
|
||||
if (!driver.jumping) return velocity // Not jumping
|
||||
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
|
||||
if (!hasStamina(state)) return velocity // Has no stamina
|
||||
|
||||
val jumpStrength = vehicle.getRideStat(RidingStat.JUMP, RidingStyle.LIQUID, 1.0, 2.5)
|
||||
state.jumpBuffer.set(0)
|
||||
consumeStamina(vehicle, state, 0.05f)
|
||||
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
|
||||
}
|
||||
|
||||
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
|
||||
if (state.jumpBuffer.get() == -1 && vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return Vec3(velocity.x, -0.05, velocity.z)
|
||||
}
|
||||
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2
|
||||
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
|
||||
}
|
||||
|
||||
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, state: BoatState) {
|
||||
val skill = vehicle.getRideStat(RidingStat.SKILL, RidingStyle.LIQUID, 0.5, 1.5)
|
||||
val strafe = driver.xxa
|
||||
if (abs(strafe) > 0) {
|
||||
val increase = if (strafe > 0) -1 else 1
|
||||
state.deltaRotation.set(state.deltaRotation.get() + increase * skill)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasStamina(state: BoatState): Boolean {
|
||||
return state.stamina.get() != 0f
|
||||
}
|
||||
|
||||
private fun consumeStamina(vehicle: PokemonEntity, state: BoatState, drain: Float) {
|
||||
val stamina = vehicle.getRideStat(RidingStat.STAMINA, RidingStyle.LIQUID, 1.0, 5.0)
|
||||
state.stamina.set(max(0f, state.stamina.get() - drain / stamina.toFloat()))
|
||||
state.staminaBuffer.set(0)
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
driver.yRot += state.deltaRotation.get().toFloat() * 0.887f
|
||||
driver.yHeadRot += state.deltaRotation.get().toFloat() * 0.887f
|
||||
clampPassengerRotation(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
driver.setYBodyRot(vehicle.yRot)
|
||||
val f: Float = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val g = Mth.clamp(f, -105.0f, 105.0f)
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f
|
||||
driver.setYHeadRot(driver.yRot)
|
||||
vehicle.setYHeadRot(driver.yRot)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.rideVelocity.get().z > TOP_SPEED * 0.8) {
|
||||
return Mth.lerp((state.rideVelocity.get().z - (TOP_SPEED * 0.8)) / (TOP_SPEED * 0.2), 1.0, 1.2).toFloat()
|
||||
}
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun maxUpStep(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity) = 0f
|
||||
|
||||
override fun createDefaultState(settings: BoatSettings) = BoatState()
|
||||
|
||||
}
|
||||
|
||||
class BoatSettings : RidingBehaviourSettings {
|
||||
override val key = BoatBehaviour.KEY
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
private set
|
||||
|
||||
var rotatePokemonHead = "true".asExpression()
|
||||
private set
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
terminalVelocity = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class BoatState : RidingBehaviourState() {
|
||||
val deltaRotation = ridingState(0.0, Side.CLIENT)
|
||||
val jumpBuffer = ridingState(-1, Side.CLIENT)
|
||||
val staminaBuffer = ridingState(0, Side.CLIENT)
|
||||
|
||||
override fun copy() = BoatState().also {
|
||||
it.deltaRotation.set(deltaRotation.get(), true)
|
||||
it.rideVelocity.set(rideVelocity.get(), true)
|
||||
it.stamina.set(stamina.get(), true)
|
||||
it.jumpBuffer.set(jumpBuffer.get(), true)
|
||||
it.staminaBuffer.set(staminaBuffer.get(), true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is BoatState) return false
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
|
||||
}
|
||||
+44
-44
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
@@ -27,22 +27,22 @@ import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
|
||||
class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("swim/dash")
|
||||
val KEY = cobblemonResource("liquid/burst")
|
||||
const val DASH_TICKS: Int = 60
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: SwimDashSettings, state: SwimDashState): RidingStyle {
|
||||
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<SwimDashSettings, SwimDashState>(PoseType.FLOAT)
|
||||
val poseProvider = PoseProvider<BurstSettings, BurstState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.isSwimming && entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
override fun isActive(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
@@ -53,13 +53,13 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): PoseType {
|
||||
override fun pose(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -77,8 +77,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
@@ -86,8 +86,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
@@ -102,8 +102,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
@@ -112,8 +112,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
@@ -132,8 +132,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
@@ -141,8 +141,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
@@ -150,8 +150,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
@@ -160,8 +160,8 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
@@ -169,64 +169,64 @@ class SwimDashBehaviour : RidingBehaviour<SwimDashSettings, SwimDashState> {
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
override fun useAngVelSmoothing(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Double {
|
||||
override fun inertia(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRoll(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
override fun turnOffOnGround(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: SwimDashSettings, state: SwimDashState, vehicle: PokemonEntity): Boolean {
|
||||
override fun dismountOnShift(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: SwimDashSettings,
|
||||
state: SwimDashState,
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: SwimDashSettings) = SwimDashState()
|
||||
override fun createDefaultState(settings: BurstSettings) = BurstState()
|
||||
}
|
||||
|
||||
class SwimDashSettings : RidingBehaviourSettings {
|
||||
override val key = SwimDashBehaviour.KEY
|
||||
class BurstSettings : RidingBehaviourSettings {
|
||||
override val key = BurstBehaviour.KEY
|
||||
|
||||
var dashSpeed = 1F
|
||||
private set
|
||||
@@ -241,7 +241,7 @@ class SwimDashSettings : RidingBehaviourSettings {
|
||||
}
|
||||
}
|
||||
|
||||
class SwimDashState : RidingBehaviourState() {
|
||||
class BurstState : RidingBehaviourState() {
|
||||
var dashing = ridingState(false, Side.BOTH)
|
||||
var ticks = ridingState(0, Side.BOTH)
|
||||
|
||||
@@ -252,10 +252,10 @@ class SwimDashState : RidingBehaviourState() {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SwimDashState(dashing=${dashing.get()}, ticks=${ticks.get()})"
|
||||
return "BurstState(dashing=${dashing.get()}, ticks=${ticks.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = SwimDashState().also {
|
||||
override fun copy() = BurstState().also {
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.dashing.set(dashing.get(), forced = true)
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
@@ -32,7 +32,7 @@ import kotlin.math.sin
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("swim/dolphin")
|
||||
val KEY = cobblemonResource("liquid/dolphin")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.cobblemon.mod.common.util.codec.optionalFieldOfWithDefault
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
@@ -34,7 +32,11 @@ class EmitterSpace(
|
||||
}
|
||||
|
||||
fun initializeEmitterMatrix(rootMatrix: MatrixWrapper, locatorMatrix: MatrixWrapper): MatrixWrapper {
|
||||
val rootRotation = rootMatrix.matrix.getRotation(AxisAngle4f())
|
||||
var rootRotation = rootMatrix.matrix.getRotation(AxisAngle4f())
|
||||
//When the locator hasn't yet been initialized, we start getting NaNs, so default to no rotation instead
|
||||
if (rootRotation.x.isNaN() || rootRotation.y.isNaN() || rootRotation.z.isNaN() || rootRotation.angle.isNaN()) {
|
||||
rootRotation = AxisAngle4f()
|
||||
}
|
||||
val scale = Vector3f()
|
||||
|
||||
if (scaling == ScalingMode.ENTITY) {
|
||||
|
||||
+10
-5
@@ -14,11 +14,16 @@ import com.bedrockk.molang.ast.NumberExpression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.math.convertSphericalToCartesian
|
||||
import com.cobblemon.mod.common.util.math.getRotationMatrix
|
||||
import com.cobblemon.mod.common.util.math.times
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveVec3d
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.DynamicOps
|
||||
@@ -27,10 +32,10 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
interface ParticleEmitterShape : CodecMapped {
|
||||
companion object : ArbitrarilyMappedSerializableCompanion<ParticleEmitterShape, ParticleEmitterShapeType>(
|
||||
@@ -261,9 +266,9 @@ class BoxParticleEmitterShape(
|
||||
}
|
||||
} else {
|
||||
Vec3(
|
||||
Random.nextDouble(sizes.x) - sizes.x / 2,
|
||||
Random.nextDouble(sizes.y) - sizes.y / 2,
|
||||
Random.nextDouble(sizes.z) - sizes.z / 2
|
||||
Random.nextDouble(sizes.x) - sizes.x / 2.0,
|
||||
Random.nextDouble(sizes.y) - sizes.y / 2.0,
|
||||
Random.nextDouble(sizes.z) - sizes.z / 2.0
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,29 +18,30 @@ import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SubmergedSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.GroundedSpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.LavafloorSpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.SeafloorSpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.SpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.SubmergedSpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.position.calculators.SurfaceSpawnablePositionCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
|
||||
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.GroundedSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.LavafloorSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SeafloorSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SubmergedSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SurfaceSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
|
||||
import com.cobblemon.mod.common.api.spawning.preset.PokemonSpawnDetailPreset
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.AreaSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
@@ -57,12 +58,12 @@ import net.minecraft.server.MinecraftServer
|
||||
* the configuration and many other properties used by the spawner.
|
||||
*
|
||||
* The Best Spawner (in world spawning) works in distinct stages that are:
|
||||
* - Prospecting (see: [SpawningProspector])
|
||||
* - Context resolving (see: [AreaContextResolver])
|
||||
* - Spawning zone generation (see: [SpawningZoneGenerator])
|
||||
* - Spawnable position resolving (see: [AreaSpawnablePositionResolver])
|
||||
* - Spawn selection (see: [SpawningSelector])
|
||||
* - Spawn action (see: [SpawnAction])
|
||||
*
|
||||
* In the case of more specialized use, the creation of a [SpawningContext] that motivates most of the spawn
|
||||
* In the case of more specialized use, the creation of a [SpawnablePosition] that motivates most of the spawn
|
||||
* process can be created manually, skipping the first two steps.
|
||||
*
|
||||
* An individually spawnable entity is defined as a [SpawnDetail]. A processor handling this process is a [Spawner].
|
||||
@@ -74,10 +75,10 @@ import net.minecraft.server.MinecraftServer
|
||||
* Spawning is coordinated and ticked using a [SpawnerManager], and all the current managers are accessible from
|
||||
* [BestSpawner.spawnerManagers].
|
||||
*
|
||||
* Spawners and contexts are often put under the effects of [SpawningInfluence]s which can be used to make temporary
|
||||
* or lasting changes to spawning for whatever component they are attached to (whether that is a spawner or a context).
|
||||
* This pairs strongly with edits to the influence builders inside the [PlayerSpawnerFactory]. The range of effects
|
||||
* an influence can have is significant.
|
||||
* Spawners and spawnable positions are often put under the effects of [SpawningInfluence]s which can be used to make
|
||||
* temporary or lasting changes to spawning for whatever component they are attached to (whether that is a spawner or a
|
||||
* spawnable position). This pairs strongly with edits to the influence builders inside the [PlayerSpawnerFactory]. The
|
||||
* range of effects an influence can exert is very broad.
|
||||
*
|
||||
* Broad configuration of this spawning system is found in [BestSpawner.config].
|
||||
*
|
||||
@@ -87,7 +88,7 @@ import net.minecraft.server.MinecraftServer
|
||||
object BestSpawner {
|
||||
var config = BestSpawnerConfig()
|
||||
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
|
||||
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
|
||||
lateinit var defaultPokemonDespawner: Despawner<PokemonEntity>
|
||||
lateinit var fishingSpawner: FishingSpawner
|
||||
|
||||
fun init() {
|
||||
@@ -103,23 +104,24 @@ object BestSpawner {
|
||||
|
||||
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
|
||||
|
||||
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
|
||||
SpawningContextCalculator.register(SeafloorSpawningContextCalculator)
|
||||
SpawningContextCalculator.register(LavafloorSpawningContextCalculator)
|
||||
SpawningContextCalculator.register(SubmergedSpawningContextCalculator)
|
||||
SpawningContextCalculator.register(SurfaceSpawningContextCalculator)
|
||||
SpawnablePositionCalculator.register(GroundedSpawnablePositionCalculator)
|
||||
SpawnablePositionCalculator.register(SeafloorSpawnablePositionCalculator)
|
||||
SpawnablePositionCalculator.register(LavafloorSpawnablePositionCalculator)
|
||||
SpawnablePositionCalculator.register(SubmergedSpawnablePositionCalculator)
|
||||
SpawnablePositionCalculator.register(SurfaceSpawnablePositionCalculator)
|
||||
|
||||
SpawningContext.register(name = "grounded", clazz = GroundedSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = SeafloorSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "grounded", clazz = GroundedSpawnablePosition::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "seafloor", clazz = SeafloorSpawnablePosition::class.java, defaultCondition = SeafloorSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "lavafloor", clazz = LavafloorSpawnablePosition::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "submerged", clazz = SubmergedSpawnablePosition::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "surface", clazz = SurfaceSpawnablePosition::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
|
||||
SpawnablePosition.register(name = "fishing", clazz = FishingSpawnablePosition::class.java, defaultCondition = FishingSpawningCondition.NAME)
|
||||
|
||||
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
|
||||
LOGGER.info("Loaded ${SpawnablePosition.spawnablePositionTypes.size} spawnable position types.")
|
||||
|
||||
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = PokemonHerdSpawnDetail.TYPE, PokemonHerdSpawnDetail::class.java)
|
||||
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
|
||||
|
||||
loadConfig()
|
||||
@@ -129,6 +131,7 @@ object BestSpawner {
|
||||
}
|
||||
|
||||
fun loadConfig() {
|
||||
defaultPokemonDespawner = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
|
||||
config = BestSpawnerConfig.load()
|
||||
}
|
||||
|
||||
@@ -142,4 +145,4 @@ object BestSpawner {
|
||||
spawnerManagers.forEach(SpawnerManager::onServerStarted)
|
||||
fishingSpawner = FishingSpawner()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.api.spawning
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BiomePrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BucketPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.ContextPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawnablePositionTypePrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -31,7 +31,7 @@ object CobblemonSpawnPools {
|
||||
fun load() {
|
||||
WORLD_SPAWN_POOL = CobblemonDataProvider.register(SpawnPool("world")
|
||||
.addPrecalculators(
|
||||
ContextPrecalculation,
|
||||
SpawnablePositionTypePrecalculation,
|
||||
BucketPrecalculation,
|
||||
BiomePrecalculation
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(SpawnRuleComponent::class.java, SpawnRuleComponentAdapter)
|
||||
.registerTypeAdapter(SpawnDetailSelector::class.java, SpawnDetailSelectorAdapter)
|
||||
.registerTypeAdapter(SpawningContextSelector::class.java, SpawningContextSelectorAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionSelector::class.java, SpawnablePositionSelectorAdapter)
|
||||
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
@@ -56,8 +56,8 @@ object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
|
||||
|
||||
SpawnDetailSelector.register<ExpressionSpawnDetailSelector>("expression")
|
||||
|
||||
SpawningContextSelector.register<ExpressionSpawningContextSelector>("expression")
|
||||
SpawningContextSelector.register<ConditionalSpawningContextSelector>("conditional")
|
||||
SpawnablePositionSelector.register<ExpressionSpawnablePositionSelector>("expression")
|
||||
SpawnablePositionSelector.register<ConditionalSpawnablePositionSelector>("conditional")
|
||||
}
|
||||
|
||||
val rules = mutableMapOf<ResourceLocation, SpawnRule>()
|
||||
|
||||
+35
-36
@@ -10,11 +10,10 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningInfluenceProspector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
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.SpawningArea
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.SectionPos.blockToSectionCoord
|
||||
@@ -27,21 +26,21 @@ import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* A spawning prospector that takes a straightforward approach
|
||||
* in slicing out a [WorldSlice]. If you want to replace this,
|
||||
* change over the value of [Cobblemon.prospector].
|
||||
* A spawning zone generator that takes a straightforward approach
|
||||
* in slicing out a [SpawningZone]. If you want to replace this,
|
||||
* change over the value of [Cobblemon.spawningZoneGenerator].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 5th, 2022
|
||||
*/
|
||||
object CobblemonSpawningProspector : SpawningProspector {
|
||||
override fun prospect(
|
||||
object CobblemonSpawningZoneGenerator : SpawningZoneGenerator {
|
||||
override fun generate(
|
||||
spawner: Spawner,
|
||||
area: SpawningArea
|
||||
): WorldSlice {
|
||||
val world = area.world
|
||||
var baseY = area.baseY
|
||||
var height = area.height
|
||||
input: SpawningZoneInput
|
||||
): SpawningZone {
|
||||
val world = input.world
|
||||
var baseY = input.baseY
|
||||
var height = input.height
|
||||
if (baseY < world.minBuildHeight) {
|
||||
val difference = world.minBuildHeight - baseY
|
||||
baseY += difference
|
||||
@@ -60,34 +59,34 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
}
|
||||
|
||||
val minimumDistanceBetweenEntities = config.minimumDistanceBetweenEntities
|
||||
val nearbyEntityPositions = area.world.getEntities(
|
||||
area.cause.entity,
|
||||
val nearbyEntityPositions = input.world.getEntities(
|
||||
input.cause.entity,
|
||||
AABB.ofSize(
|
||||
Vec3(
|
||||
area.baseX + area.length / 2.0,
|
||||
input.baseX + input.length / 2.0,
|
||||
baseY + height / 2.0,
|
||||
area.baseZ + area.width / 2.0
|
||||
input.baseZ + input.width / 2.0
|
||||
),
|
||||
area.length + minimumDistanceBetweenEntities,
|
||||
input.length + minimumDistanceBetweenEntities,
|
||||
height + minimumDistanceBetweenEntities,
|
||||
area.width + minimumDistanceBetweenEntities
|
||||
input.width + minimumDistanceBetweenEntities
|
||||
)
|
||||
).filterIsInstance<LivingEntity>()
|
||||
.map { it.position() }
|
||||
|
||||
val defaultState = Blocks.STONE.defaultBlockState()
|
||||
val defaultBlockData = WorldSlice.BlockData(defaultState, 0, 0)
|
||||
val defaultBlockData = SpawningZone.BlockData(defaultState, 0, 0)
|
||||
|
||||
val blocks = Array(area.length) { Array(height) { Array(area.width) { defaultBlockData } } }
|
||||
val skyLevel = Array(area.length) { Array(area.width) { world.maxBuildHeight } }
|
||||
val blocks = Array(input.length) { Array(height) { Array(input.width) { defaultBlockData } } }
|
||||
val skyLevel = Array(input.length) { Array(input.width) { world.maxBuildHeight } }
|
||||
val pos = BlockPos.MutableBlockPos()
|
||||
val worldSlicedSpawningInfluences = mutableListOf<WorldSlicedSpawningInfluence>()
|
||||
val spawningZoneInfluences = mutableListOf<SpawningZoneInfluence>()
|
||||
|
||||
val chunks = mutableMapOf<Pair<Int, Int>, ChunkAccess?>()
|
||||
val yRange = (baseY until baseY + height).reversed()
|
||||
val lightingProvider = world.lightEngine
|
||||
for (x in area.baseX until area.baseX + area.length) {
|
||||
for (z in area.baseZ until area.baseZ + area.width) {
|
||||
for (x in input.baseX until input.baseX + input.length) {
|
||||
for (z in input.baseZ until input.baseZ + input.width) {
|
||||
val query = chunks.computeIfAbsent(Pair(blockToSectionCoord(x), blockToSectionCoord(z))) {
|
||||
world.getChunk(it.first, it.second, ChunkStatus.FULL, false)
|
||||
} ?: continue
|
||||
@@ -96,14 +95,14 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
for (y in yRange) {
|
||||
val skyLight = lightingProvider.getLayerListener(LightLayer.SKY).getLightValue(pos.set(x, y, z))
|
||||
val state = query.getBlockState(pos.set(x, y, z))
|
||||
blocks[x - area.baseX][y - baseY][z - area.baseZ] = WorldSlice.BlockData(
|
||||
blocks[x - input.baseX][y - baseY][z - input.baseZ] = SpawningZone.BlockData(
|
||||
state = state,
|
||||
light = world.getMaxLocalRawBrightness(pos),
|
||||
skyLight = skyLight
|
||||
)
|
||||
worldSlicedSpawningInfluences.addAll(SpawningInfluenceProspector.prospectors.mapNotNull { it.prospectBlock(world, pos, state) })
|
||||
spawningZoneInfluences.addAll(SpawningInfluenceDetector.detectors.flatMap { it.detectFromBlock(world, pos, state) })
|
||||
if (canSeeSky) {
|
||||
skyLevel[x - area.baseX][z - area.baseZ] = y
|
||||
skyLevel[x - input.baseX][z - input.baseZ] = y
|
||||
}
|
||||
if (state.fluidState.isEmpty && !state.`is`(CobblemonBlockTags.SEES_SKY)) {
|
||||
canSeeSky = false
|
||||
@@ -112,20 +111,20 @@ object CobblemonSpawningProspector : SpawningProspector {
|
||||
}
|
||||
}
|
||||
|
||||
for (prospector in SpawningInfluenceProspector.prospectors) {
|
||||
worldSlicedSpawningInfluences.addAll(prospector.prospect(spawner, area))
|
||||
for (spawningZoneGenerator in SpawningInfluenceDetector.detectors) {
|
||||
spawningZoneInfluences.addAll(spawningZoneGenerator.detectFromInput(spawner, input))
|
||||
}
|
||||
|
||||
return WorldSlice(
|
||||
cause = area.cause,
|
||||
return SpawningZone(
|
||||
cause = input.cause,
|
||||
world = world,
|
||||
baseX = area.baseX,
|
||||
baseX = input.baseX,
|
||||
baseY = baseY,
|
||||
baseZ = area.baseZ,
|
||||
baseZ = input.baseZ,
|
||||
blocks = blocks,
|
||||
skyLevel = skyLevel,
|
||||
nearbyEntityPositions = nearbyEntityPositions,
|
||||
influences = worldSlicedSpawningInfluences
|
||||
influences = spawningZoneInfluences
|
||||
)
|
||||
}
|
||||
}
|
||||
-1
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import java.util.UUID
|
||||
@@ -9,8 +9,8 @@
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
/**
|
||||
* A spawn bucket is a simple referenceable object by spawn files. The bucket is used early in the spawn
|
||||
* process to decide which set of spawns will be used for spawning on that attempt. Weights are used to
|
||||
* A spawn bucket is a simple referenceable object by spawn files. The bucket is used at the beginning of
|
||||
* the selection process to decide which set of spawns will be used for spawning on that attempt. Weights are used to
|
||||
* make entire buckets more or less likely, and then weighted selections will occur within whichever bucket
|
||||
* was chosen.
|
||||
*
|
||||
|
||||
@@ -1,32 +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
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
|
||||
object SpawnBucketUtils {
|
||||
fun chooseAdjustedSpawnBucket(buckets: List<SpawnBucket>, bucketLureStrength: Int): SpawnBucket {
|
||||
val baseValues = listOf(88.5F, 8.5F, 2.4F, 0.6F)
|
||||
val adjustments = listOf(-6.9F, 3.6F, 2.3F, 1F)
|
||||
|
||||
val adjustedWeights = buckets.mapIndexed { index, bucket ->
|
||||
if (index >= baseValues.size) {
|
||||
bucket to bucket.weight
|
||||
} else {
|
||||
val base = baseValues[index]
|
||||
val adjustment = adjustments[index]
|
||||
val adjusted = base + adjustment * bucketLureStrength
|
||||
bucket to adjusted.coerceAtLeast(0f) // we do not want the weights to be negative
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
return buckets.weightedSelection { adjustedWeights[it]!! }!!
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import net.minecraft.world.entity.EntityType
|
||||
|
||||
open class SpawnCause(
|
||||
val spawner: Spawner,
|
||||
val bucket: SpawnBucket,
|
||||
entity: Entity? = null
|
||||
): SpawningInfluence {
|
||||
val entityWorldId = entity?.level()?.dimension()
|
||||
@@ -25,5 +24,9 @@ open class SpawnCause(
|
||||
val entityType = entity?.type
|
||||
|
||||
val entity: Entity?
|
||||
get() = if (entityType == EntityType.PLAYER) server()?.playerList?.getPlayer(entityUUID) else server()?.getLevel(entityWorldId)?.getEntity(entityId!!)
|
||||
get() = if (entityType == EntityType.PLAYER) {
|
||||
server()?.playerList?.getPlayer(entityUUID!!)
|
||||
} else {
|
||||
server()?.getLevel(entityWorldId!!)?.getEntity(entityId!!)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PossibleHeldItem
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
@@ -50,7 +50,7 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
.setLenient()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PossibleHeldItem
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
@@ -58,7 +58,7 @@ object SpawnLoader {
|
||||
).type,
|
||||
EitherIdentifierOrTagAdapter(Registries.STRUCTURE)
|
||||
)
|
||||
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(SpawnDetail::class.java, SpawnDetailAdapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
|
||||
+19
-11
@@ -8,28 +8,29 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.influence.ConditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.WorldSlicedSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import kotlin.math.max
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* A slice of the world that can be accessed safely from an async thread. This includes all of the information
|
||||
* A zone of the world that can be accessed quickly from later steps. This includes all the information
|
||||
* that might be unsafe to access async from the world itself for the purposes of spawning.
|
||||
*
|
||||
* This is generated by a [SpawningProspector].
|
||||
* This is generated by a [SpawningZoneGenerator].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 31st, 2022
|
||||
*/
|
||||
class WorldSlice(
|
||||
class SpawningZone(
|
||||
val cause: SpawnCause,
|
||||
val world: ServerLevel,
|
||||
val baseX: Int,
|
||||
@@ -38,7 +39,7 @@ class WorldSlice(
|
||||
val blocks: Array<Array<Array<BlockData>>>,
|
||||
val skyLevel: Array<Array<Int>>,
|
||||
var nearbyEntityPositions: List<Vec3>,
|
||||
val influences: List<WorldSlicedSpawningInfluence>
|
||||
influences: List<SpawningZoneInfluence>
|
||||
) {
|
||||
class BlockData(
|
||||
val state: BlockState,
|
||||
@@ -46,14 +47,17 @@ class WorldSlice(
|
||||
val skyLight: Int
|
||||
)
|
||||
|
||||
val unconditionalInfluences = influences.filterIsInstance<UnconditionalSpawningZoneInfluence>().map { it.influence }
|
||||
val conditionalInfluences = influences.filterIsInstance<ConditionalSpawningZoneInfluence>()
|
||||
|
||||
val length = blocks.size
|
||||
val height = blocks[0].size
|
||||
val width = blocks[0][0].size
|
||||
|
||||
private val structureChunkCaches = mutableMapOf<ChunkPos, SpawningContext.StructureChunkCache>()
|
||||
private val structureChunkCaches = mutableMapOf<ChunkPos, SpawnablePosition.StructureChunkCache>()
|
||||
|
||||
fun getStructureCache(pos: BlockPos): SpawningContext.StructureChunkCache {
|
||||
return structureChunkCaches.getOrPut(ChunkPos(pos), SpawningContext::StructureChunkCache)
|
||||
fun getStructureCache(pos: BlockPos): SpawnablePosition.StructureChunkCache {
|
||||
return structureChunkCaches.getOrPut(ChunkPos(pos), SpawnablePosition::StructureChunkCache)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -228,4 +232,8 @@ class WorldSlice(
|
||||
|
||||
return space
|
||||
}
|
||||
|
||||
fun getInfluences(spawnablePosition: SpawnablePosition): List<SpawningInfluence> {
|
||||
return unconditionalInfluences + conditionalInfluences.filter { it.appliesTo(spawnablePosition) }.map { it.influence }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
|
||||
/**
|
||||
* Interface responsible for slicing out an async-save [SpawningZone] that can be used for generating
|
||||
* [SpawnablePosition]s, specifically [AreaSpawnablePosition]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 29th, 2022
|
||||
*/
|
||||
interface SpawningZoneGenerator {
|
||||
fun generate(spawner: Spawner, input: SpawningZoneInput): SpawningZone
|
||||
}
|
||||
+3
-4
@@ -8,8 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
|
||||
|
||||
/**
|
||||
@@ -17,7 +16,7 @@ import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
|
||||
* and is then stowed in [SpawningCondition.appendages].
|
||||
*
|
||||
* As an example, you could add an appendage which has a single field called 'weather'. The [fits]
|
||||
* implementation would check the [SpawningContext] using your own logic. Inside the JSON, you
|
||||
* implementation would check the [SpawnablePosition] using your own logic. Inside the JSON, you
|
||||
* would specify the 'weather' property as if it was a built-in field inside [SpawningCondition]. During
|
||||
* loading, it will deserialize the [SpawningCondition]'s JSON several times targeting the various
|
||||
* appendage classes, and then add those appendages to [SpawningCondition.appendages] so that when
|
||||
@@ -55,5 +54,5 @@ interface AppendageCondition {
|
||||
|
||||
fun getAppendages(spawningCondition: SpawningCondition<*>) = appendages.filter { it.spawningConditionFits(spawningCondition) }.map { it.clazz }
|
||||
}
|
||||
fun fits(ctx: SpawningContext): Boolean
|
||||
fun fits(spawnablePosition: SpawnablePosition): Boolean
|
||||
}
|
||||
+13
-13
@@ -9,30 +9,30 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [AreaSpawningContext]. This
|
||||
* can be extended for subclasses of [AreaSpawningContext].
|
||||
* Base type for a spawning condition that applies to some kind of [AreaSpawnablePosition]. This
|
||||
* can be extended for subclasses of [AreaSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
abstract class AreaTypeSpawningCondition<T : AreaSpawningContext> : SpawningCondition<T>() {
|
||||
abstract class AreaTypeSpawningCondition<T : AreaSpawnablePosition> : SpawningCondition<T>() {
|
||||
var minHeight: Int? = null
|
||||
var maxHeight: Int? = null
|
||||
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: T): Boolean {
|
||||
if (!super.fits(spawnablePosition)) {
|
||||
return false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
} else if (minHeight != null && spawnablePosition.height < minHeight!!) {
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
} else if (maxHeight != null && spawnablePosition.height > maxHeight!!) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockHolders.any { cond.fits(it) } }) {
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> spawnablePosition.nearbyBlockHolders.any { cond.fits(it) } }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
@@ -49,19 +49,19 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawningContext> : SpawningCond
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
val containsNullValues = neededNearbyBlocks != null && neededNearbyBlocks!!.any {it == null}
|
||||
val containsNullValues = neededNearbyBlocks != null && neededNearbyBlocks!!.any { it == null }
|
||||
return super.isValid() && !containsNullValues
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for an [AreaSpawningContext].
|
||||
* A spawning condition for an [AreaSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
class AreaSpawningCondition : AreaTypeSpawningCondition<AreaSpawningContext>() {
|
||||
override fun contextClass(): Class<out AreaSpawningContext> = AreaSpawningContext::class.java
|
||||
class AreaSpawningCondition : AreaTypeSpawningCondition<AreaSpawnablePosition>() {
|
||||
override fun spawnablePositionClass(): Class<out AreaSpawnablePosition> = AreaSpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "area"
|
||||
}
|
||||
|
||||
+4
-4
@@ -8,16 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
|
||||
/**
|
||||
* A basic spawning condition that works for any type of spawning context.
|
||||
* A basic spawning condition that works for any type of spawnable position.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
class BasicSpawningCondition : SpawningCondition<SpawningContext>() {
|
||||
override fun contextClass(): Class<out SpawningContext> = SpawningContext::class.java
|
||||
class BasicSpawningCondition : SpawningCondition<SpawnablePosition>() {
|
||||
override fun spawnablePositionClass(): Class<out SpawnablePosition> = SpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "basic"
|
||||
}
|
||||
|
||||
+4
-5
@@ -8,8 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
@@ -41,11 +40,11 @@ class CompositeSpawningCondition {
|
||||
return false
|
||||
}
|
||||
|
||||
fun satisfiedBy(ctx: SpawningContext): Boolean {
|
||||
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
|
||||
fun satisfiedBy(spawnablePosition: SpawnablePosition): Boolean {
|
||||
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(spawnablePosition) }) {
|
||||
false
|
||||
} else {
|
||||
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) })
|
||||
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(spawnablePosition) })
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-12
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.Item
|
||||
@@ -18,13 +18,13 @@ import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* A spawning condition that applies to [FishingSpawningContext]s.
|
||||
* A spawning condition that applies to [FishingSpawnablePosition]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
|
||||
override fun contextClass() = FishingSpawningContext::class.java
|
||||
class FishingSpawningCondition: SpawningCondition<FishingSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = FishingSpawnablePosition::class.java
|
||||
|
||||
var rod: RegistryLikeCondition<Item>? = null
|
||||
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
@@ -33,19 +33,19 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
|
||||
var bait: ResourceLocation? = null
|
||||
var rodType: ResourceLocation? = null
|
||||
|
||||
override fun fits(ctx: FishingSpawningContext): Boolean {
|
||||
if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: FishingSpawnablePosition): Boolean {
|
||||
if (!super.fits(spawnablePosition)) {
|
||||
return false
|
||||
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
|
||||
} else if (rod != null && !rod!!.fits(spawnablePosition.rodItem ?: return false, spawnablePosition.world.itemRegistry)) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> spawnablePosition.nearbyBlockTypes.any { cond.fits(it, spawnablePosition.blockRegistry) } }) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (minLureLevel != null) { // check for the lureLevel of the rod
|
||||
val pokerodStack = ctx.rodStack
|
||||
val pokerodStack = spawnablePosition.rodStack
|
||||
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(
|
||||
ctx.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
|
||||
spawnablePosition.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
|
||||
pokerodStack
|
||||
)
|
||||
if (lureLevel < minLureLevel!!) {
|
||||
@@ -55,13 +55,13 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
|
||||
}
|
||||
}
|
||||
if (bait != null) { // check for the bait on the bobber
|
||||
val pokerodBait = ctx.baitStack.itemHolder.unwrapKey().orElse(null)?.location()
|
||||
val pokerodBait = spawnablePosition.baitStack.itemHolder.unwrapKey().orElse(null)?.location()
|
||||
if (pokerodBait != bait) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (rodType != null) { // check for the type of pokerod being used
|
||||
val pokerodItem = ctx.rodItem
|
||||
val pokerodItem = spawnablePosition.rodItem
|
||||
if (pokerodItem?.pokeRodId != rodType) {
|
||||
return false
|
||||
}
|
||||
|
||||
+12
-12
@@ -9,28 +9,28 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.GroundedSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [GroundedSpawningContext]. This
|
||||
* can be extended for subclasses of [GroundedSpawningContext].
|
||||
* Base type for a spawning condition that applies to some kind of [GroundedSpawnablePosition]. This
|
||||
* can be extended for subclasses of [GroundedSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
abstract class GroundedTypeSpawningCondition<T : GroundedSpawningContext> : AreaTypeSpawningCondition<T>() {
|
||||
abstract class GroundedTypeSpawningCondition<T : GroundedSpawnablePosition> : AreaTypeSpawningCondition<T>() {
|
||||
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
return if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: T): Boolean {
|
||||
return if (!super.fits(spawnablePosition)) {
|
||||
false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
} else if (minHeight != null && spawnablePosition.height < minHeight!!) {
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
} else if (maxHeight != null && spawnablePosition.height > maxHeight!!) {
|
||||
return false
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlockHolder) }) {
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(spawnablePosition.baseBlockHolder) }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
@@ -51,13 +51,13 @@ abstract class GroundedTypeSpawningCondition<T : GroundedSpawningContext> : Area
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for a [GroundedSpawningContext].
|
||||
* A spawning condition for a [GroundedSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
open class GroundedSpawningCondition : GroundedTypeSpawningCondition<GroundedSpawningContext>() {
|
||||
override fun contextClass() = GroundedSpawningContext::class.java
|
||||
open class GroundedSpawningCondition : GroundedTypeSpawningCondition<GroundedSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = GroundedSpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "grounded"
|
||||
}
|
||||
|
||||
+13
-13
@@ -9,31 +9,31 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SeafloorSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [SeafloorSpawningContext]. This
|
||||
* can be extended for subclasses of [SeafloorSpawningContext].
|
||||
* Base type for a spawning condition that applies to some kind of [SeafloorSpawnablePosition]. This
|
||||
* can be extended for subclasses of [SeafloorSpawnablePosition].
|
||||
*
|
||||
* Borrowed the Grounded context code since it was unintentionally spawning Pokémon on the sea floor
|
||||
* Borrowed the Grounded spawnable position code since it was unintentionally spawning Pokémon on the sea floor
|
||||
* already. Seems to be working fine /shrug
|
||||
*
|
||||
* @author FrankTheFarmer
|
||||
* @since May 22nd, 2024
|
||||
*/
|
||||
abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : AreaTypeSpawningCondition<T>() {
|
||||
abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawnablePosition> : AreaTypeSpawningCondition<T>() {
|
||||
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
return if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: T): Boolean {
|
||||
return if (!super.fits(spawnablePosition)) {
|
||||
false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
} else if (minHeight != null && spawnablePosition.height < minHeight!!) {
|
||||
false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
} else if (maxHeight != null && spawnablePosition.height > maxHeight!!) {
|
||||
false
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlockHolder) }) {
|
||||
} else if (neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(spawnablePosition.baseBlockHolder) }) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
@@ -54,13 +54,13 @@ abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : Area
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for a [SeafloorSpawningContext].
|
||||
* A spawning condition for a [SeafloorSpawnablePosition].
|
||||
*
|
||||
* @author FrankTheFarmer
|
||||
* @since May 22nd, 2024
|
||||
*/
|
||||
open class SeafloorSpawningCondition : SeafloorTypeSpawningCondition<SeafloorSpawningContext>() {
|
||||
override fun contextClass() = SeafloorSpawningContext::class.java
|
||||
open class SeafloorSpawningCondition : SeafloorTypeSpawningCondition<SeafloorSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = SeafloorSpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "seafloor"
|
||||
}
|
||||
|
||||
+34
-34
@@ -11,32 +11,29 @@ package com.cobblemon.mod.common.api.spawning.condition
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import com.cobblemon.mod.common.util.math.orMax
|
||||
import com.cobblemon.mod.common.util.math.orMin
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.levelgen.WorldgenRandom
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
|
||||
/**
|
||||
* The root of spawning conditions that can be applied to a spawning context. What type
|
||||
* of spawning context it can be applied to is relevant for any subclasses.
|
||||
* The root of spawning conditions that can be applied to a spawnable position. What type
|
||||
* of spawnable position it can be applied to is relevant for any subclasses.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 24th, 2022
|
||||
*/
|
||||
abstract class SpawningCondition<T : SpawningContext> {
|
||||
abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
companion object {
|
||||
val conditionTypes = mutableMapOf<String, Class<out SpawningCondition<*>>>()
|
||||
fun getByName(name: String) = conditionTypes[name]
|
||||
fun <T : SpawningContext, C : SpawningCondition<T>> register(name: String, clazz: Class<C>) {
|
||||
fun <T : SpawnablePosition, C : SpawningCondition<T>> register(name: String, clazz: Class<C>) {
|
||||
conditionTypes[name] = clazz
|
||||
}
|
||||
}
|
||||
@@ -61,69 +58,72 @@ abstract class SpawningCondition<T : SpawningContext> {
|
||||
var timeRange: TimeRange? = null
|
||||
var structures: MutableList<Either<ResourceLocation, TagKey<Structure>>>? = null
|
||||
var isSlimeChunk: Boolean? = null
|
||||
var markers: MutableList<String>? = null
|
||||
|
||||
@Transient
|
||||
var appendages = mutableListOf<AppendageCondition>()
|
||||
|
||||
abstract fun contextClass(): Class<out T>
|
||||
fun contextMatches(ctx: SpawningContext) = contextClass().isAssignableFrom(ctx::class.java)
|
||||
abstract fun spawnablePositionClass(): Class<out T>
|
||||
fun spawnablePositionMatches(spawnablePosition: SpawnablePosition) = spawnablePositionClass().isAssignableFrom(spawnablePosition::class.java)
|
||||
|
||||
fun isSatisfiedBy(ctx: SpawningContext): Boolean {
|
||||
return if (contextMatches(ctx)) {
|
||||
fits(ctx as T)
|
||||
fun isSatisfiedBy(spawnablePosition: SpawnablePosition): Boolean {
|
||||
return if (spawnablePositionMatches(spawnablePosition)) {
|
||||
fits(spawnablePosition as T)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun fits(ctx: T): Boolean {
|
||||
if (ctx.position.x < minX.orMin() || ctx.position.x > maxX.orMax()) {
|
||||
protected open fun fits(spawnablePosition: T): Boolean {
|
||||
if (spawnablePosition.position.x < minX.orMin() || spawnablePosition.position.x > maxX.orMax()) {
|
||||
return false
|
||||
} else if (ctx.position.y < minY.orMin() || ctx.position.y > maxY.orMax()) {
|
||||
} else if (spawnablePosition.position.y < minY.orMin() || spawnablePosition.position.y > maxY.orMax()) {
|
||||
return false
|
||||
} else if (ctx.position.z < minZ.orMin() || ctx.position.z > maxZ.orMax()) {
|
||||
} else if (spawnablePosition.position.z < minZ.orMin() || spawnablePosition.position.z > maxZ.orMax()) {
|
||||
return false
|
||||
} else if (moonPhase != null && ctx.moonPhase !in moonPhase!!) {
|
||||
} else if (moonPhase != null && spawnablePosition.moonPhase !in moonPhase!!) {
|
||||
return false
|
||||
} else if (ctx.light > maxLight.orMax() || ctx.light < minLight.orMin()) {
|
||||
} else if (spawnablePosition.light > maxLight.orMax() || spawnablePosition.light < minLight.orMin()) {
|
||||
return false
|
||||
} else if (ctx.skyLight > maxSkyLight.orMax() || ctx.skyLight < minSkyLight.orMin()) {
|
||||
} else if (spawnablePosition.skyLight > maxSkyLight.orMax() || spawnablePosition.skyLight < minSkyLight.orMin()) {
|
||||
return false
|
||||
} else if (timeRange != null && !timeRange!!.contains((ctx.world.dayTime() % 24000).toInt())) {
|
||||
} else if (timeRange != null && !timeRange!!.contains((spawnablePosition.world.dayTime() % 24000).toInt())) {
|
||||
return false
|
||||
} else if (canSeeSky != null && canSeeSky != ctx.canSeeSky) {
|
||||
} else if (canSeeSky != null && canSeeSky != spawnablePosition.canSeeSky) {
|
||||
return false
|
||||
} else if (isRaining != null && ctx.world.isRaining != isRaining!!) {
|
||||
} else if (isRaining != null && spawnablePosition.world.isRaining != isRaining!!) {
|
||||
return false
|
||||
} else if (isThundering != null && ctx.world.isThundering != isThundering!!) {
|
||||
} else if (isThundering != null && spawnablePosition.world.isThundering != isThundering!!) {
|
||||
return false
|
||||
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimensionType().effectsLocation !in dimensions!!) {
|
||||
} else if (dimensions != null && dimensions!!.isNotEmpty() && spawnablePosition.world.dimension().location() !in dimensions!!) {
|
||||
return false
|
||||
} else if (biomes != null && biomes!!.isNotEmpty() && biomes!!.none { condition -> condition.fits(ctx.biomeHolder) }) {
|
||||
} else if (markers != null && markers!!.isNotEmpty() && markers!!.none { marker -> marker in spawnablePosition.markers }) {
|
||||
return false
|
||||
} else if (appendages.any { !it.fits(ctx) }) {
|
||||
} else if (biomes != null && biomes!!.isNotEmpty() && biomes!!.none { condition -> condition.fits(spawnablePosition.biomeHolder) }) {
|
||||
return false
|
||||
} else if (appendages.any { !it.fits(spawnablePosition) }) {
|
||||
return false
|
||||
} else if (structures != null && structures!!.isNotEmpty() &&
|
||||
structures!!.let { structures ->
|
||||
val structureAccess = ctx.world.structureManager()
|
||||
val cache = ctx.getStructureCache(ctx.position)
|
||||
val structureAccess = spawnablePosition.world.structureManager()
|
||||
val cache = spawnablePosition.getStructureCache(spawnablePosition.position)
|
||||
return@let structures.none {
|
||||
it.map({ cache.check(structureAccess, ctx.position, it) }, { cache.check(structureAccess, ctx.position, it) })
|
||||
it.map({ cache.check(structureAccess, spawnablePosition.position, it) }, { cache.check(structureAccess, spawnablePosition.position, it) })
|
||||
}
|
||||
}
|
||||
) {
|
||||
return false
|
||||
} else if (isSlimeChunk != null && isSlimeChunk != false) {
|
||||
val isSlimeChunk = WorldgenRandom.seedSlimeChunk(ctx.position.x shr 4, ctx.position.z shr 4, ctx.world.seed, 987234911L).nextInt(10) == 0
|
||||
val isSlimeChunk = WorldgenRandom.seedSlimeChunk(spawnablePosition.position.x shr 4, spawnablePosition.position.z shr 4, spawnablePosition.world.seed, 987234911L).nextInt(10) == 0
|
||||
|
||||
if (!isSlimeChunk) {
|
||||
return false
|
||||
}
|
||||
|
||||
/*val chunkX = ctx.position.x shr 4
|
||||
val chunkZ = ctx.position.z shr 4
|
||||
/*val chunkX = spawnablePosition.position.x shr 4
|
||||
val chunkZ = spawnablePosition.position.z shr 4
|
||||
|
||||
val seed = (ctx.world.seed +
|
||||
val seed = (spawnablePosition.world.seed +
|
||||
(chunkX * chunkX * 4987142L) + (chunkX * 5947611L) +
|
||||
(chunkZ * chunkZ * 4392871L) + (chunkZ * 389711L)) xor 987234911L
|
||||
|
||||
|
||||
+17
-17
@@ -9,18 +9,18 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A type of precalculation that can occur on a list of [SpawnDetail] to accelerate
|
||||
* checks on whether a spawn can occur at a particular [SpawningContext]. This process
|
||||
* checks on whether a spawn can occur at a particular [SpawnablePosition]. This process
|
||||
* works by creating a [HashMap] keying from some property of a [SpawnDetail] and
|
||||
* [SpawningContext] and mapping it to a list of spawns that were calculated ahead of
|
||||
* [SpawnablePosition] and mapping it to a list of spawns that were calculated ahead of
|
||||
* time as sharing that value. The requirement of implementations is only to get that
|
||||
* key from both a [SpawnDetail] and a [SpawningContext].
|
||||
* key from both a [SpawnDetail] and a [SpawnablePosition].
|
||||
*
|
||||
* When a precalculation has been registered with a [Spawner] by adding to [Spawner.precalculators],
|
||||
* the precalculation result will need to be updated by running [Spawner.precalculate]. That
|
||||
@@ -33,8 +33,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
interface SpawningPrecalculation<T : Any> {
|
||||
/** Retrieves the precalculation keys for the given spawn detail. */
|
||||
fun select(detail: SpawnDetail): Collection<T>
|
||||
/** Retrieves the precalculation key for the given context, if that key exists for this context. */
|
||||
fun select(ctx: SpawningContext): T?
|
||||
/** Retrieves the precalculation key for the given spawnable position, if that key exists for this position. */
|
||||
fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): T?
|
||||
|
||||
/**
|
||||
* Generates a precalculation result for the list of [SpawnDetail]s and the subsequent precalculations.
|
||||
@@ -74,23 +74,23 @@ interface SpawningPrecalculation<T : Any> {
|
||||
object RootPrecalculation : SpawningPrecalculation<Any> {
|
||||
val list = listOf(Unit)
|
||||
override fun select(detail: SpawnDetail): List<Any> = list
|
||||
override fun select(ctx: SpawningContext): Any = Unit
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): Any = Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* A precalculation based on what context a spawn can be. This is a pretty sweet precalc.
|
||||
* A precalculation based on what spawnable position type a spawn can be. This is a pretty sweet precalc.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 14th, 2022
|
||||
*/
|
||||
object ContextPrecalculation : SpawningPrecalculation<Class<out SpawningContext>> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.context.clazz)
|
||||
override fun select(ctx: SpawningContext) = ctx::class.java
|
||||
object SpawnablePositionTypePrecalculation : SpawningPrecalculation<Class<out SpawnablePosition>> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.spawnablePositionType.clazz)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
|
||||
}
|
||||
|
||||
object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.bucket)
|
||||
override fun select(ctx: SpawningContext) = ctx.cause.bucket
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = bucket
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +99,7 @@ object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
*/
|
||||
object BiomePrecalculation : SpawningPrecalculation<ResourceLocation> {
|
||||
override fun select(detail: SpawnDetail) = detail.validBiomes
|
||||
override fun select(ctx: SpawningContext) = ctx.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,8 +113,8 @@ object BiomePrecalculation : SpawningPrecalculation<ResourceLocation> {
|
||||
sealed class PrecalculationResult<T : Any>(
|
||||
val calculation: SpawningPrecalculation<*>
|
||||
) {
|
||||
/** Gets the list of spawn details that fit this context. */
|
||||
abstract fun retrieve(ctx: SpawningContext): List<SpawnDetail>
|
||||
/** Gets the list of spawn details that fit this spawnable position. */
|
||||
abstract fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +127,7 @@ class NestedPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<T>,
|
||||
val mapping: Map<T, PrecalculationResult<*>> = mutableMapOf()
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(ctx: SpawningContext) = mapping[calculation.select(ctx)]?.retrieve(ctx) ?: emptyList()
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,5 +141,5 @@ class FinalPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<*>,
|
||||
val mapping: Map<T, List<SpawnDetail>>
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(ctx: SpawningContext) = mapping[calculation.select(ctx)] ?: emptyList()
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
|
||||
}
|
||||
+15
-15
@@ -9,37 +9,37 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SubmergedSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [SubmergedSpawningContext]. This
|
||||
* can be extended for subclasses of [SubmergedSpawningContext].
|
||||
* Base type for a spawning condition that applies to some kind of [SubmergedSpawnablePosition]. This
|
||||
* can be extended for subclasses of [SubmergedSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : AreaTypeSpawningCondition<T>() {
|
||||
abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawnablePosition> : AreaTypeSpawningCondition<T>() {
|
||||
var minDepth: Int? = null
|
||||
var maxDepth: Int? = null
|
||||
var fluidIsSource: Boolean? = null
|
||||
var fluid: RegistryLikeCondition<Fluid>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
return if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: T): Boolean {
|
||||
return if (!super.fits(spawnablePosition)) {
|
||||
false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
} else if (minHeight != null && spawnablePosition.height < minHeight!!) {
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
} else if (maxHeight != null && spawnablePosition.height > maxHeight!!) {
|
||||
return false
|
||||
} else if (minDepth != null && ctx.depth < minDepth!!) {
|
||||
} else if (minDepth != null && spawnablePosition.depth < minDepth!!) {
|
||||
false
|
||||
} else if (maxDepth != null && ctx.depth > maxDepth!!) {
|
||||
} else if (maxDepth != null && spawnablePosition.depth > maxDepth!!) {
|
||||
false
|
||||
} else if (fluidIsSource != null && ctx.fluid.isSource != fluidIsSource!!) {
|
||||
} else if (fluidIsSource != null && spawnablePosition.fluid.isSource != fluidIsSource!!) {
|
||||
false
|
||||
} else !(ctx.fluid.isEmpty || (fluid != null && !fluid!!.fits(ctx.fluid.type, ctx.fluidRegistry)))
|
||||
} else !(spawnablePosition.fluid.isEmpty || (fluid != null && !fluid!!.fits(spawnablePosition.fluid.type, spawnablePosition.fluidRegistry)))
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
@@ -54,13 +54,13 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for an [SubmergedSpawningContext].
|
||||
* A spawning condition for an [SubmergedSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 7th, 2022
|
||||
*/
|
||||
open class SubmergedSpawningCondition : SubmergedTypeSpawningCondition<SubmergedSpawningContext>() {
|
||||
override fun contextClass() = SubmergedSpawningContext::class.java
|
||||
open class SubmergedSpawningCondition : SubmergedTypeSpawningCondition<SubmergedSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = SubmergedSpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "submerged"
|
||||
}
|
||||
|
||||
+12
-12
@@ -9,30 +9,30 @@
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SurfaceSpawnablePosition
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [SurfaceSpawningContext]. This
|
||||
* can be extended for subclasses of [SurfaceSpawningContext].
|
||||
* Base type for a spawning condition that applies to some kind of [SurfaceSpawnablePosition]. This
|
||||
* can be extended for subclasses of [SurfaceSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 15th, 2022
|
||||
*/
|
||||
abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawningContext> : AreaTypeSpawningCondition<T>() {
|
||||
abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawnablePosition> : AreaTypeSpawningCondition<T>() {
|
||||
var minDepth: Int? = null
|
||||
var maxDepth: Int? = null
|
||||
var fluid: RegistryLikeCondition<Fluid>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
return if (!super.fits(ctx)) {
|
||||
override fun fits(spawnablePosition: T): Boolean {
|
||||
return if (!super.fits(spawnablePosition)) {
|
||||
false
|
||||
} else if (minDepth != null && ctx.depth < minDepth!!) {
|
||||
} else if (minDepth != null && spawnablePosition.depth < minDepth!!) {
|
||||
false
|
||||
} else if (maxDepth != null && ctx.depth > maxDepth!!) {
|
||||
} else if (maxDepth != null && spawnablePosition.depth > maxDepth!!) {
|
||||
false
|
||||
} else !(ctx.baseBlock.fluidState.isEmpty || (fluid != null && !fluid!!.fits(ctx.baseBlock.fluidState.type, ctx.fluidRegistry)))
|
||||
} else !(spawnablePosition.baseBlock.fluidState.isEmpty || (fluid != null && !fluid!!.fits(spawnablePosition.baseBlock.fluidState.type, spawnablePosition.fluidRegistry)))
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
@@ -46,13 +46,13 @@ abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawningContext> : AreaTy
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for a [SurfaceSpawningContext].
|
||||
* A spawning condition for a [SurfaceSpawnablePosition].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 15th, 2022
|
||||
*/
|
||||
open class SurfaceSpawningCondition : SurfaceTypeSpawningCondition<SurfaceSpawningContext>() {
|
||||
override fun contextClass() = SurfaceSpawningContext::class.java
|
||||
open class SurfaceSpawningCondition : SurfaceTypeSpawningCondition<SurfaceSpawnablePosition>() {
|
||||
override fun spawnablePositionClass() = SurfaceSpawnablePosition::class.java
|
||||
companion object {
|
||||
const val NAME = "surface"
|
||||
}
|
||||
|
||||
-133
@@ -1,133 +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.context
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.WorldSlice
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.core.BlockPos
|
||||
|
||||
/**
|
||||
* Interface responsible for drawing a list of spawn contexts from a slice of the world,
|
||||
* given a list of all the context calculators that should be considered in order. As soon
|
||||
* as one of the context calculators returns true for [AreaSpawningContextCalculator.fits],
|
||||
* no other context calculator will be considered.
|
||||
*
|
||||
* The default method body of this interface checks every single block in the slice
|
||||
* and composes a single context per position, at most. This is almost certainly fine,
|
||||
* but this interface exists, so you can override it if you want.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 31st, 2022
|
||||
*/
|
||||
interface AreaContextResolver {
|
||||
fun resolve(
|
||||
spawner: Spawner,
|
||||
contextCalculators: List<AreaSpawningContextCalculator<*>>,
|
||||
slice: WorldSlice
|
||||
): List<AreaSpawningContext> {
|
||||
var pos = BlockPos.MutableBlockPos(1, 2, 3)
|
||||
val input = AreaSpawningInput(spawner, pos, slice)
|
||||
val contexts = mutableListOf<AreaSpawningContext>()
|
||||
|
||||
var x = slice.baseX
|
||||
var y = slice.baseY
|
||||
var z = slice.baseZ
|
||||
|
||||
while (x < slice.baseX + slice.length) {
|
||||
while (y < slice.baseY + slice.height) {
|
||||
while (z < slice.baseZ + slice.width) {
|
||||
pos.set(x, y, z)
|
||||
val vec = pos.toVec3d()
|
||||
if (slice.nearbyEntityPositions.none { it.closerThan(vec, config.minimumDistanceBetweenEntities) && it != slice.cause.entity }) {
|
||||
val fittedContextCalculator = contextCalculators
|
||||
.firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
|
||||
if (fittedContextCalculator != null) {
|
||||
val context = fittedContextCalculator.calculate(input)
|
||||
if (context != null) {
|
||||
slice.influences.forEach {
|
||||
if (it.appliesTo(context)) {
|
||||
context.influences.add(it.influence)
|
||||
}
|
||||
}
|
||||
|
||||
contexts.add(context)
|
||||
// The position BlockPos has been used in a context, editing the same one
|
||||
// will cause entities to spawn at the wrong location (buried in walls, usually).
|
||||
// I made it so that built-in context calculators explicitly take a copy of the
|
||||
// BlockPos but it'd still be exposed in custom contexts so fixing it here too so
|
||||
// custom context calculators don't have to remember to do it. - Hiroku
|
||||
pos = BlockPos.MutableBlockPos(1, 2, 3)
|
||||
input.position = pos
|
||||
}
|
||||
}
|
||||
}
|
||||
z++
|
||||
}
|
||||
y++
|
||||
z = slice.baseZ
|
||||
}
|
||||
x++
|
||||
y = slice.baseY
|
||||
z = slice.baseZ
|
||||
}
|
||||
|
||||
return contexts
|
||||
}
|
||||
|
||||
// fun resolveFishing(
|
||||
// spawner: Spawner,
|
||||
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
|
||||
// slice: WorldSlice
|
||||
// ): List<AreaSpawningContext> {
|
||||
// var pos = BlockPos.Mutable(1, 2, 3)
|
||||
// val input = AreaSpawningInput(spawner, pos, slice)
|
||||
// val contexts = mutableListOf<AreaSpawningContext>()
|
||||
//
|
||||
// var x = slice.baseX
|
||||
// var y = slice.baseY
|
||||
// var z = slice.baseZ
|
||||
//
|
||||
// while (x < slice.baseX + slice.length) {
|
||||
// while (y < slice.baseY + slice.height) {
|
||||
// while (z < slice.baseZ + slice.width) {
|
||||
// pos.set(x, y, z)
|
||||
// val vec = pos.toVec3d()
|
||||
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
|
||||
// val fittedContextCalculator = contextCalculators
|
||||
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
|
||||
// if (fittedContextCalculator != null) {
|
||||
// val context = fittedContextCalculator.calculate(input)
|
||||
// if (context != null) {
|
||||
// contexts.add(context)
|
||||
// // The position BlockPos has been used in a context, editing the same one
|
||||
// // will cause entities to spawn at the wrong location (buried in walls, usually)
|
||||
// // I made it so that our context calculators specifically take a copy of the
|
||||
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
|
||||
// pos = BlockPos.Mutable(1, 2, 3)
|
||||
// input.position = pos
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// z++
|
||||
// }
|
||||
// y++
|
||||
// z = slice.baseZ
|
||||
// }
|
||||
// x++
|
||||
// y = slice.baseY
|
||||
// z = slice.baseZ
|
||||
// }
|
||||
//
|
||||
// return contexts
|
||||
// }
|
||||
}
|
||||
-8
@@ -1,8 +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/.
|
||||
*/
|
||||
|
||||
-60
@@ -1,60 +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.context.calculators
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.WorldSlice
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
/**
|
||||
* A spawning context calculator that deals with [AreaSpawningContext]s. These work off
|
||||
* [AreaSpawningInput] instances, and must output some kind of [AreaSpawningContext].
|
||||
*
|
||||
* This is the interface to use for all of the contexts that will get used by world spawning
|
||||
* as that is the primary case that area spawning is done. These get registered when
|
||||
* creating the context type, in [SpawningContext.register]
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 31st, 2022
|
||||
*/
|
||||
interface AreaSpawningContextCalculator<O : AreaSpawningContext> : SpawningContextCalculator<AreaSpawningInput, O> {
|
||||
/**
|
||||
* Whether or not this context calculator is likely to provide a value for this location.
|
||||
*
|
||||
* This should be relatively final. See how the [AreaContextResolver] works to understand why
|
||||
* you should be pretty sure before returning true to this function.
|
||||
*/
|
||||
fun fits(input: AreaSpawningInput): Boolean
|
||||
|
||||
fun getDepth(input: AreaSpawningInput, condition: (BlockState) -> Boolean, maximum: Int): Int
|
||||
= input.slice.depthSpace(input.position.x, input.position.y, input.position.z, condition, maximum)
|
||||
fun getHeight(input: AreaSpawningInput, condition: (BlockState) -> Boolean, maximum: Int, offsetX: Int = 0, offsetY: Int = 0, offsetZ: Int = 0): Int
|
||||
= input.slice.heightSpace(input.position.x + offsetX, input.position.y + offsetY, input.position.z + offsetZ, condition, maximum)
|
||||
fun getHorizontalSpace(input: AreaSpawningInput, condition: (BlockState) -> Boolean, maximum: Int, offsetX: Int = 0, offsetY: Int = 0, offsetZ: Int = 0): Int
|
||||
= input.slice.horizontalSpace(input.position.x + offsetX, input.position.y + offsetY, input.position.z + offsetZ, condition, maximum)
|
||||
fun getLight(input: AreaSpawningInput, elseLight: Int = 0): Int
|
||||
= input.slice.getLight(input.position.x, input.position.y + 1, input.position.z, elseLight)
|
||||
fun getSkyLight(input: AreaSpawningInput, elseLight: Int = 0): Int
|
||||
= input.slice.getSkyLight(input.position.x, input.position.y + 1, input.position.z, elseLight)
|
||||
fun getCanSeeSky(input: AreaSpawningInput): Boolean = input.slice.canSeeSky(input.position.x, input.position.y + 1, input.position.z)
|
||||
fun getSkySpaceAbove(input: AreaSpawningInput): Int = input.slice.skySpaceAbove(input.position.x, input.position.y, input.position.z)
|
||||
fun getNearbyBlocks(
|
||||
input: AreaSpawningInput,
|
||||
horizontalRadius: Int = config.maxNearbyBlocksHorizontalRange,
|
||||
verticalRadius: Int = config.maxNearbyBlocksVerticalRange
|
||||
) = input.slice.nearbyBlocks(input.position, horizontalRadius, verticalRadius)
|
||||
|
||||
}
|
||||
|
||||
open class AreaSpawningInput(val spawner: Spawner, var position: BlockPos, val slice: WorldSlice) : SpawningContextInput(slice.cause, slice.world)
|
||||
+6
-5
@@ -9,7 +9,8 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
|
||||
@@ -19,13 +20,13 @@ import com.cobblemon.mod.common.util.resolveInt
|
||||
* @author Hiroku
|
||||
* @since October 8th, 2023
|
||||
*/
|
||||
class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(ctx, detail) {
|
||||
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: SpawnBucket, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
|
||||
override fun createEntity(): NPCEntity {
|
||||
val npc = NPCEntity(ctx.world)
|
||||
val npc = NPCEntity(spawnablePosition.world)
|
||||
npc.npc = detail.npcClass
|
||||
npc.appliedAspects.addAll(detail.aspects)
|
||||
val minLevel = ctx.runtime.resolveInt(detail.minLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val maxLevel = ctx.runtime.resolveInt(detail.maxLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val minLevel = spawnablePosition.runtime.resolveInt(detail.minLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val maxLevel = spawnablePosition.runtime.resolveInt(detail.maxLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val seedLevel = (minLevel..maxLevel).random()
|
||||
npc.initialize(seedLevel)
|
||||
return npc
|
||||
|
||||
+20
-2
@@ -9,11 +9,15 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* A [SpawnDetail] describing an [NPCEntity] spawn.
|
||||
@@ -35,5 +39,19 @@ class NPCSpawnDetail : SpawnDetail() {
|
||||
val minLevel: Expression = "1".asExpression()
|
||||
val maxLevel: Expression = "100".asExpression()
|
||||
|
||||
override fun doSpawn(ctx: SpawningContext) = NPCSpawnAction(ctx, this)
|
||||
|
||||
override fun autoLabel() {
|
||||
val npcClass = this.npcClass
|
||||
|
||||
struct.addFunction("class") { StringValue(npcClass.resourceIdentifier.toString()) }
|
||||
labels.add(npcClass.resourceIdentifier.toString())
|
||||
|
||||
super.autoLabel()
|
||||
}
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
selectionData: SpawnSelectionData
|
||||
) = NPCSpawnAction(spawnablePosition, bucket, this)
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
/**
|
||||
* A spawn detail that spawns a herd of Pokémon. This relies on the same [PokemonSpawnAction]s as
|
||||
* regular [PokemonSpawnDetail]s, but takes advantage of selection context to 'take over' the spawn
|
||||
* pass and spawn many for the price of one.
|
||||
*
|
||||
* Selection will treat this like a single spawn until it chooses this detail once, at which point
|
||||
* all the other spawns are removed, a level is calculated for the herd based on [levelRange], and
|
||||
* the selector will be left to fit in as many herd members as possible with the remaining spawnable
|
||||
* positions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since April 22nd, 2025
|
||||
*/
|
||||
class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
companion object {
|
||||
val TYPE = "pokemon-herd"
|
||||
const val HERD_LEADER = "leader"
|
||||
}
|
||||
|
||||
override val type: String = TYPE
|
||||
var herdablePokemon = mutableListOf<Herdable>()
|
||||
@SerializedName("level", alternate = ["levelRange"])
|
||||
var levelRange: IntRange = 1..100
|
||||
var maxHerdSize = 10
|
||||
var minDistanceBetweenSpawns = 1F
|
||||
|
||||
val herdLevelKey: String
|
||||
get() = "${id}__LEVEL"
|
||||
|
||||
class Herdable {
|
||||
var pokemon: PokemonProperties = PokemonProperties()
|
||||
val levelRange: IntRange? = null
|
||||
var levelRangeOffset: IntRange? = null
|
||||
var dropTable: DropTable? = null
|
||||
var isLeader: Boolean? = null
|
||||
var weight: Float = 1F
|
||||
var maxTimes = 10
|
||||
}
|
||||
|
||||
override fun isValid() = super.isValid() && herdablePokemon.all { it.pokemon.hasSpecies() && it.weight > 0F && it.maxTimes > 0 }
|
||||
override fun onSelection(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
spawnAction: SpawnAction<*>,
|
||||
selectionData: SpawnSelectionData
|
||||
) {
|
||||
selectionData.removeSpawnDetails { it != this }
|
||||
selectionData.removeSpawnablePositions { _, pos -> pos.distanceTo(spawnablePosition) < minDistanceBetweenSpawns }
|
||||
|
||||
val herdSpawnCount = selectionData.spawnActions.count { it.detail == this } + 1
|
||||
|
||||
if (herdSpawnCount >= maxHerdSize || getValidHerdMembers(selectionData).isEmpty()) {
|
||||
// We can't spawn any more in the herd so remove this spawn from the selection pool.
|
||||
selectionData.removeSpawnDetails { it == this }
|
||||
}
|
||||
}
|
||||
|
||||
fun lacksPossibleLeader(selectionData: SpawnSelectionData): Boolean {
|
||||
val level = getOrSetHerdLevel(selectionData)
|
||||
val herdable = getValidHerdMembers(selectionData)
|
||||
val herdCounts = getHerdMemberCounts(selectionData)
|
||||
|
||||
val leaderIsPossible = herdable.any { it.levelRange?.contains(level) != false && it.isLeader == true }
|
||||
val leaderIsSelected = herdCounts.any { it.key?.isLeader == true && it.value > 0 }
|
||||
return leaderIsPossible && !leaderIsSelected
|
||||
}
|
||||
|
||||
private fun getOrSetHerdLevel(selectionData: SpawnSelectionData): Int {
|
||||
return (selectionData.context[herdLevelKey] as? Int) ?: run {
|
||||
val level = levelRange.random()
|
||||
selectionData.context[herdLevelKey] = level
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
selectionData: SpawnSelectionData
|
||||
): PokemonSpawnAction {
|
||||
val validHerdMembers = getValidHerdMembers(selectionData)
|
||||
val herdable = if (lacksPossibleLeader(selectionData)) {
|
||||
validHerdMembers.filter { it.isLeader == true }
|
||||
} else {
|
||||
validHerdMembers
|
||||
}.weightedSelection { it.weight } ?: herdablePokemon[0]
|
||||
|
||||
val level = selectionData.context[herdLevelKey] as? Int ?: 1
|
||||
val levelRange = herdable.levelRangeOffset?.let { offset ->
|
||||
val min = level + offset.first
|
||||
val max = level + offset.last
|
||||
min..max
|
||||
} ?: level..level
|
||||
|
||||
return PokemonSpawnAction(
|
||||
spawnablePosition = spawnablePosition,
|
||||
bucket = bucket,
|
||||
detail = this,
|
||||
props = herdable.pokemon,
|
||||
drops = herdable.dropTable,
|
||||
heldItem = null,
|
||||
levelRange = levelRange
|
||||
).also {
|
||||
if (herdable.isLeader == true) {
|
||||
it.labels += HERD_LEADER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getHerdMemberCounts(selectionData: SpawnSelectionData): Map<Herdable?, Int> {
|
||||
val relevantActions = selectionData.spawnActions.filterIsInstance<PokemonSpawnAction>().filter { it.detail == this }
|
||||
return relevantActions.groupBy { action -> herdablePokemon.find { it.pokemon == action.props } }.mapValues { it.value.size }
|
||||
}
|
||||
|
||||
private fun getValidHerdMembers(selectionData: SpawnSelectionData): List<Herdable> {
|
||||
val level = getOrSetHerdLevel(selectionData)
|
||||
val relevantActions = selectionData.spawnActions.filterIsInstance<PokemonSpawnAction>().filter { it.detail == this }
|
||||
val counts = relevantActions.groupBy { action -> herdablePokemon.find { it.pokemon == action.props } }.mapValues { it.value.size }
|
||||
return herdablePokemon.filter { herdable ->
|
||||
val count = counts[herdable] ?: 0
|
||||
return@filter when {
|
||||
count >= herdable.maxTimes -> false
|
||||
herdable.levelRange != null && level !in herdable.levelRange -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-31
@@ -9,13 +9,14 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
* A [SpawnAction] that will spawn a single [PokemonEntity].
|
||||
@@ -24,38 +25,49 @@ import net.minecraft.server.level.ServerPlayer
|
||||
* @since February 13th, 2022
|
||||
*/
|
||||
class PokemonSpawnAction(
|
||||
ctx: SpawningContext,
|
||||
override val detail: PokemonSpawnDetail,
|
||||
/** The [PokemonProperties] that are about to be used. */
|
||||
var props: PokemonProperties = detail.pokemon.copy()
|
||||
) : SingleEntitySpawnAction<PokemonEntity>(ctx, detail) {
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
override val detail: SpawnDetail,
|
||||
/**
|
||||
* The [PokemonProperties] that are about to be used. When using it, a copy will be taken.
|
||||
* Don't modify the contents of this, what you want to do is [updatePokemon] with a copy.
|
||||
*/
|
||||
var props: PokemonProperties,
|
||||
var heldItem: ItemStack?,
|
||||
var drops: DropTable? = null,
|
||||
var levelRange: IntRange
|
||||
) : SingleEntitySpawnAction<PokemonEntity>(spawnablePosition, bucket, detail) {
|
||||
override fun createEntity(): PokemonEntity {
|
||||
if (props.species == null) LOGGER.error("PokemonSpawnAction run with null species - Spawn detail: ${detail.id}")
|
||||
if (props.level == null) {
|
||||
props.level = detail.getDerivedLevelRange().random()
|
||||
}
|
||||
val heldItems = detail.heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
|
||||
val heldItem = if (heldItems.isNotEmpty()) {
|
||||
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
|
||||
if (until100 > 0 && ctx.world.random.nextDouble() < until100) {
|
||||
null
|
||||
} else {
|
||||
heldItems.weightedSelection { it.percentage }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}?.createStack(ctx)
|
||||
val sourcePlayer = ctx.cause.entity as? ServerPlayer
|
||||
val entity = props.createEntity(ctx.world, sourcePlayer)
|
||||
entity.spawnCause = ctx.cause
|
||||
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(ctx.world, ctx.position))
|
||||
if (heldItem != null) {
|
||||
val props = props.copy()
|
||||
props.level = levelRange.random()
|
||||
val sourcePlayer = spawnablePosition.cause.entity as? ServerPlayer
|
||||
val entity = props.createEntity(spawnablePosition.world, sourcePlayer)
|
||||
entity.spawnCause = spawnablePosition.cause
|
||||
SeasonFeatureHandler.updateSeason(entity.pokemon, Cobblemon.seasonResolver(spawnablePosition.world, spawnablePosition.position))
|
||||
val heldItem = heldItem
|
||||
if (heldItem != ItemStack.EMPTY && heldItem != null) {
|
||||
entity.pokemon.swapHeldItem(heldItem)
|
||||
}
|
||||
entity.drops = detail.drops
|
||||
entity.drops = drops
|
||||
// Useful debug code in situations where you want to find spawns
|
||||
// val fireworkRocketEntity = FireworkRocketEntity(ctx.world, ctx.position.x.toDouble(), ctx.position.y.toDouble() + 2, ctx.position.z.toDouble(), ItemStack(Items.FIREWORK_ROCKET))
|
||||
// ctx.world.spawnEntity(fireworkRocketEntity)
|
||||
// val fireworkRocketEntity = FireworkRocketEntity(spawnablePosition.world, spawnablePosition.position.x.toDouble(), spawnablePosition.position.y.toDouble() + 2, spawnablePosition.position.z.toDouble(), ItemStack(Items.FIREWORK_ROCKET))
|
||||
// spawnablePosition.world.spawnEntity(fireworkRocketEntity)
|
||||
return entity
|
||||
}
|
||||
|
||||
fun updatePokemon(props: PokemonProperties) {
|
||||
this.props = props
|
||||
}
|
||||
|
||||
fun updateHeldItem(heldItem: ItemStack) {
|
||||
this.heldItem = heldItem
|
||||
}
|
||||
|
||||
fun updateDrops(drops: DropTable) {
|
||||
this.drops = drops
|
||||
}
|
||||
|
||||
fun updateLevelRange(levelRange: IntRange) {
|
||||
this.levelRange = levelRange
|
||||
}
|
||||
}
|
||||
+33
-17
@@ -10,17 +10,19 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlin.math.ceil
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
@@ -72,14 +74,14 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
|
||||
override fun autoLabel() {
|
||||
struct.addFunction("pokemon") { pokemon.asMoLangValue() }
|
||||
struct.addFunction("min_level") { DoubleValue(getDerivedLevelRange().start.toDouble()) }
|
||||
struct.addFunction("max_level") { DoubleValue(getDerivedLevelRange().endInclusive.toDouble()) }
|
||||
struct.addFunction("min_level") { DoubleValue(pokemon.deriveLevelRange(levelRange).start.toDouble()) }
|
||||
struct.addFunction("max_level") { DoubleValue(pokemon.deriveLevelRange(levelRange).endInclusive.toDouble()) }
|
||||
if (pokemon.species != null) {
|
||||
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
|
||||
if (species != null) {
|
||||
labels.addAll(
|
||||
species.secondaryType?.let { listOf(species.primaryType.name.lowercase(), it.name.lowercase()) }
|
||||
?: listOf(species.primaryType.name.lowercase())
|
||||
?: listOf(species.primaryType.name.lowercase())
|
||||
)
|
||||
|
||||
if (height == -1) {
|
||||
@@ -95,23 +97,37 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
super.autoLabel()
|
||||
}
|
||||
|
||||
fun getDerivedLevelRange() = levelRange.let { levelRange ->
|
||||
if (levelRange == null && pokemon.level == null) {
|
||||
IntRange(1, config.maxPokemonLevel)
|
||||
} else if (levelRange == null) {
|
||||
IntRange(pokemon.level!!, pokemon.level!!)
|
||||
} else {
|
||||
levelRange
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
val isValidSpecies = pokemon.species != null
|
||||
if (!isValidSpecies) LOGGER.error("Invalid species for spawn detail: $id")
|
||||
return super.isValid() && isValidSpecies
|
||||
}
|
||||
|
||||
override fun doSpawn(ctx: SpawningContext): SingleEntitySpawnAction<PokemonEntity> {
|
||||
return PokemonSpawnAction(ctx, this)
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition, bucket: SpawnBucket,
|
||||
selectionData: SpawnSelectionData
|
||||
): SingleEntitySpawnAction<PokemonEntity> {
|
||||
val levelRange = pokemon.deriveLevelRange(levelRange)
|
||||
val heldItems = heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
|
||||
val heldItem = if (heldItems.isNotEmpty()) {
|
||||
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
|
||||
if (until100 > 0 && spawnablePosition.world.random.nextDouble() < until100) {
|
||||
null
|
||||
} else {
|
||||
heldItems.weightedSelection { it.percentage }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}?.createStack(spawnablePosition)
|
||||
|
||||
return PokemonSpawnAction(
|
||||
spawnablePosition = spawnablePosition,
|
||||
bucket = bucket,
|
||||
detail = this,
|
||||
props = pokemon,
|
||||
heldItem = heldItem,
|
||||
drops = drops,
|
||||
levelRange = levelRange
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.core.component.DataComponentMap
|
||||
import net.minecraft.core.component.DataComponentPatch
|
||||
import net.minecraft.core.registries.Registries
|
||||
@@ -23,15 +23,15 @@ class PossibleHeldItem(
|
||||
val componentMap: DataComponentMap? = null,
|
||||
val percentage: Double = 100.0
|
||||
) {
|
||||
fun createStack(ctx: SpawningContext): ItemStack? {
|
||||
val itemRegistry = ctx.world.registryAccess().registryOrThrow(Registries.ITEM)
|
||||
fun createStack(spawnablePosition: SpawnablePosition): ItemStack? {
|
||||
val itemRegistry = spawnablePosition.world.registryAccess().registryOrThrow(Registries.ITEM)
|
||||
val item = if (item.startsWith("#")) {
|
||||
val tag = TagKey.create(Registries.ITEM, ResourceLocation.parse(item.substring(1)))
|
||||
|
||||
val opt = itemRegistry.getTag(tag)
|
||||
if (opt.isPresent && opt.get().size() > 0) {
|
||||
val entryList = opt.get()
|
||||
entryList.getRandomElement(ctx.world.random).get().value()
|
||||
entryList.getRandomElement(spawnablePosition.world.random).get().value()
|
||||
} else {
|
||||
LOGGER.error("Unable to find matching spawn held items for tag: $item")
|
||||
null
|
||||
|
||||
+1
-1
@@ -20,5 +20,5 @@ import com.google.gson.JsonElement
|
||||
class RegisteredSpawnDetail<T : SpawnDetail>(
|
||||
val detailClass: Class<T>
|
||||
) {
|
||||
fun deserializeDetail(element: JsonElement, ctx: JsonDeserializationContext): T = ctx.deserialize(element, detailClass)
|
||||
fun deserializeDetail(element: JsonElement, spawnablePosition: JsonDeserializationContext): T = spawnablePosition.deserialize(element, detailClass)
|
||||
}
|
||||
+9
-7
@@ -11,7 +11,8 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
@@ -22,18 +23,19 @@ import net.minecraft.world.entity.Entity
|
||||
* @since January 13th, 2024
|
||||
*/
|
||||
abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
ctx: SpawningContext,
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
detail: SpawnDetail
|
||||
) : SpawnAction<EntitySpawnResult>(ctx, detail) {
|
||||
) : SpawnAction<EntitySpawnResult>(spawnablePosition, bucket, detail) {
|
||||
abstract fun createEntity(): T?
|
||||
|
||||
override fun run(): EntitySpawnResult? {
|
||||
val e = createEntity() ?: return null
|
||||
e.setPos(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
|
||||
e.setPos(spawnablePosition.position.toVec3d().add(0.5, 1.0, 0.5))
|
||||
var shouldSpawn = false
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = {
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, spawnablePosition), ifSucceeded = {
|
||||
this.entity.emit(e)
|
||||
ctx.world.addFreshEntity(e)
|
||||
spawnablePosition.world.addFreshEntity(e)
|
||||
shouldSpawn = true
|
||||
})
|
||||
|
||||
@@ -51,6 +53,6 @@ abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
* where the entity spawn has been canceled, so that your action will not occur.
|
||||
*/
|
||||
val entity: SimpleObservable<T> = SimpleObservable<T>().apply {
|
||||
subscribe { entity -> ctx.afterSpawn(entity) }
|
||||
subscribe { entity -> spawnablePosition.afterSpawn(action = this@SingleEntitySpawnAction, entity = entity) }
|
||||
}
|
||||
}
|
||||
@@ -8,24 +8,27 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
|
||||
/**
|
||||
* A scheduled spawning action.
|
||||
* A planned, actionable spawn.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 4th, 2022
|
||||
*/
|
||||
abstract class SpawnAction<R>(
|
||||
val ctx: SpawningContext,
|
||||
val spawnablePosition: SpawnablePosition,
|
||||
val bucket: SpawnBucket,
|
||||
open val detail: SpawnDetail
|
||||
) {
|
||||
val future = CompletableFuture<R>().also { it.thenApply { result -> ctx.spawner.afterSpawn(this, result) } }
|
||||
val labels = mutableListOf<String>()
|
||||
val future = CompletableFuture<R>().also { it.thenApply { result -> spawnablePosition.spawner.afterSpawn(this, result) } }
|
||||
|
||||
/**
|
||||
* Does whatever action is required to spawn this detail into the context.
|
||||
* Does whatever action is required to spawn this detail into the spawnable position.
|
||||
*
|
||||
* @return The result of spawning
|
||||
*/
|
||||
@@ -39,7 +42,7 @@ abstract class SpawnAction<R>(
|
||||
return null
|
||||
}
|
||||
|
||||
ctx.applyInfluences { it.affectAction(this) }
|
||||
spawnablePosition.applyInfluences { it.affectAction(this) }
|
||||
val result = run()
|
||||
if (result != null) {
|
||||
future.complete(result)
|
||||
|
||||
+56
-11
@@ -11,18 +11,21 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.ModDependant
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -45,7 +48,8 @@ abstract class SpawnDetail : ModDependant {
|
||||
abstract val type: String
|
||||
var id = ""
|
||||
var displayName: String? = null
|
||||
lateinit var context: RegisteredSpawningContext<*>
|
||||
@SerializedName("spawnablePositionType", alternate = ["context"])
|
||||
lateinit var spawnablePositionType: SpawnablePositionType<*>
|
||||
var bucket = SpawnBucket("", 0F)
|
||||
var conditions = mutableListOf<SpawningCondition<*>>()
|
||||
var anticonditions = mutableListOf<SpawningCondition<*>>()
|
||||
@@ -74,7 +78,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
"bucket" to { StringValue(bucket.name) },
|
||||
"width" to { DoubleValue(width.toDouble()) },
|
||||
"height" to { DoubleValue(height.toDouble()) },
|
||||
"context" to { StringValue(context.name) },
|
||||
"spawnable_position_type" to { StringValue(spawnablePositionType.name) },
|
||||
"labels" to { labels.asArrayValue { StringValue(it) } }
|
||||
)
|
||||
|
||||
@@ -102,16 +106,16 @@ abstract class SpawnDetail : ModDependant {
|
||||
}
|
||||
}
|
||||
|
||||
open fun isSatisfiedBy(ctx: SpawningContext): Boolean {
|
||||
if (!ctx.preFilter(this)) {
|
||||
open fun isSatisfiedBy(spawnablePosition: SpawnablePosition): Boolean {
|
||||
if (!spawnablePosition.preFilter(this)) {
|
||||
return false
|
||||
} else if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
|
||||
} else if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(spawnablePosition) }) {
|
||||
return false
|
||||
} else if (anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) }) {
|
||||
} else if (anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(spawnablePosition) }) {
|
||||
return false
|
||||
} else if (compositeCondition?.satisfiedBy(ctx) == false) {
|
||||
} else if (compositeCondition?.satisfiedBy(spawnablePosition) == false) {
|
||||
return false
|
||||
} else if (!ctx.postFilter(this)) {
|
||||
} else if (!spawnablePosition.postFilter(this)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -131,5 +135,46 @@ abstract class SpawnDetail : ModDependant {
|
||||
return super.isModDependencySatisfied() && !containsNullValues
|
||||
}
|
||||
|
||||
abstract fun doSpawn(ctx: SpawningContext): SpawnAction<*>
|
||||
/**
|
||||
* Runs when selected by a selection algorithm. This handles removing any nearby spawns in the selection
|
||||
* data and whatever else.
|
||||
*
|
||||
* [selectionData] the algorithm data that is being used to choose spawns.
|
||||
* [spawnablePosition] where the spawn is taking place.
|
||||
* [spawnAction] the spawn action that was chosen based off this [SpawnDetail] to spawn at this position.
|
||||
*/
|
||||
protected open fun onSelection(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
spawnAction: SpawnAction<*>,
|
||||
selectionData: SpawnSelectionData
|
||||
) {
|
||||
selectionData.removeSpawnablePositions { _, pos ->
|
||||
pos.distanceTo(spawnablePosition) < Cobblemon.config.minimumDistanceBetweenEntities
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses this spawn detail from selection data, returning a functional
|
||||
* spawn action.
|
||||
*/
|
||||
fun choose(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*> {
|
||||
val action = createSpawnAction(spawnablePosition, bucket, selectionData)
|
||||
onSelection(
|
||||
spawnablePosition = spawnablePosition,
|
||||
spawnAction = action,
|
||||
selectionData = selectionData
|
||||
)
|
||||
return action
|
||||
}
|
||||
|
||||
/** Instantiates an action at the given [SpawnablePosition] for the given [SpawnBucket]. */
|
||||
protected abstract fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*>
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnSet
|
||||
import com.cobblemon.mod.common.api.spawning.condition.PrecalculationResult
|
||||
import com.cobblemon.mod.common.api.spawning.condition.RootPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.server
|
||||
@@ -47,7 +48,7 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
override fun reload(data: Map<ResourceLocation, SpawnSet>) {
|
||||
details.clear()
|
||||
for (set in data.values) {
|
||||
for (set in data.values.filter { it.isEnabled() }) {
|
||||
details.addAll(set.filter { it.isValid() })
|
||||
}
|
||||
val server = server()
|
||||
@@ -73,8 +74,8 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
|
||||
/**
|
||||
* Precalculates spawns into hash mappings using the [precalculators] included
|
||||
* in this pool as well as the range of contexts mentioned in the pool. This
|
||||
* will speed up retrieval later, and thins the herd of spawns that need to be
|
||||
* in this pool as well as the range of spawnable positions mentioned in the pool.
|
||||
* This will speed up retrieval later, and thins the herd of spawns that need to be
|
||||
* thoroughly examined when a spawn is occurring. This function will probably
|
||||
* be slow, especially if there are many precalculators and spawns.
|
||||
*/
|
||||
@@ -84,21 +85,18 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
} else {
|
||||
precalculation = precalculators.first().generate(details, precalculators.subList(1, precalculators.size))
|
||||
}
|
||||
|
||||
// contexts.clear()
|
||||
// details.forEach { contexts.add(it.context) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the spawns that are precalculated as being potentially spawns at
|
||||
* this context. This, at most, prunes some spawns that were definitely not
|
||||
* possible here. The returned list can and almost certainly will include more
|
||||
* spawns that are not possible for this context - this function is simple
|
||||
* this spawnable position. This, at most, prunes some spawns that were definitely
|
||||
* not possible here. The returned list can and almost certainly will include more
|
||||
* spawns that are not possible for this spawnable position - this function is simple
|
||||
* to leverage the precalculation to get a smaller list of spawns as quickly
|
||||
* as possible.
|
||||
*/
|
||||
fun retrieve(ctx: SpawningContext): List<SpawnDetail> {
|
||||
return precalculation.retrieve(ctx)
|
||||
fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
return precalculation.retrieve(bucket, spawnablePosition)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+18
-11
@@ -18,7 +18,8 @@ import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
@@ -40,10 +41,10 @@ import net.minecraft.world.item.ItemStack
|
||||
*/
|
||||
class FishingSpawnCause(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
entity: Entity?,
|
||||
val rodStack: ItemStack
|
||||
) : SpawnCause(spawner, bucket, entity) {
|
||||
val rodStack: ItemStack,
|
||||
val lureLevel: Int
|
||||
) : SpawnCause(spawner, entity) {
|
||||
companion object {
|
||||
const val FISHED_ASPECT = "fished"
|
||||
fun shinyReroll(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
|
||||
@@ -134,22 +135,28 @@ class FishingSpawnCause(
|
||||
// Determine the combined bait effects if any
|
||||
val baitEffects = SpawnBaitInfluence(SpawnBaitEffects.getEffectsFromRodItemStack(rodStack))
|
||||
|
||||
override fun affectSpawn(entity: Entity) {
|
||||
super.affectSpawn(entity)
|
||||
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
|
||||
super.affectSpawn(action, entity)
|
||||
if (entity is PokemonEntity) {
|
||||
entity.pokemon.forcedAspects += FISHED_ASPECT
|
||||
baitEffects.affectSpawn(entity)
|
||||
baitEffects.affectSpawn(action, entity)
|
||||
// Some of the bait actions might have changed the aspects and we need it to be
|
||||
// in the entityData IMMEDIATELY otherwise it will flash as what it would be
|
||||
// with the old aspects.
|
||||
// New aspects copy into the entity data only on the next tick.
|
||||
entity.entityData.set(PokemonEntity.ASPECTS, entity.pokemon.aspects)
|
||||
CobblemonEvents.BOBBER_SPAWN_POKEMON_MODIFY.post(BobberSpawnPokemonEvent.Modify(bucket, rodStack, entity))
|
||||
CobblemonEvents.BOBBER_SPAWN_POKEMON_MODIFY.post(
|
||||
BobberSpawnPokemonEvent.Modify(
|
||||
spawnAction = action,
|
||||
rod = rodStack,
|
||||
pokemon = entity
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
|
||||
val weight = baitEffects.affectWeight(detail, ctx, weight)
|
||||
return super.affectWeight(detail, ctx, weight)
|
||||
override fun affectWeight(detail: SpawnDetail, spawnablePosition: SpawnablePosition, weight: Float): Float {
|
||||
val weight = baitEffects.affectWeight(detail, spawnablePosition, weight)
|
||||
return super.affectWeight(detail, spawnablePosition, weight)
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -9,7 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.spawning.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -25,14 +25,14 @@ open class FishingSpawner(
|
||||
override val name: String = "fishing",
|
||||
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
|
||||
override fun parseContext(cause: FishingSpawnCause, world: ServerLevel, pos: BlockPos): FishingSpawningContext? {
|
||||
override fun parseSpawnablePosition(cause: FishingSpawnCause, world: ServerLevel, pos: BlockPos): FishingSpawnablePosition? {
|
||||
// Maybe confirm that it's water we're fishing in?
|
||||
val ctx = FishingSpawningContext(
|
||||
val spawnablePosition = FishingSpawnablePosition(
|
||||
cause = cause,
|
||||
world = world,
|
||||
pos = pos,
|
||||
influences = influences
|
||||
)
|
||||
return ctx
|
||||
return spawnablePosition
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
* A [SpawningInfluence] that normalizes the weights of buckets based on a tier system. The idea is that
|
||||
* normally the rarest buckets are MUCH rarer than the most common buckets, but in some cases we want
|
||||
* those distances to be closer to each other. This is usually for when we're doing fewer spawn actions
|
||||
* overall, meaning that the rarest buckets become very harsh, or we want to apply lure bonuses.
|
||||
*
|
||||
* The [tier] is an integer stepping up through different degrees of normalization. This is used with [gradient]
|
||||
* and [firstTier] to control the rate at which the tiers move the buckets. The equation for the new
|
||||
* weight of a bucket is `weight ^ (1 / normalizationFactor)`. This means that a normalization factor of 1
|
||||
* will not change the weights at all, and a normalization factor of 2 will make each weight turn into its
|
||||
* square root, and so on. As this number grows, the closer the bucket weights will be to each other.
|
||||
*
|
||||
* The normalizationFactor is calculated as `initial + (gradient * tier)`.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since April 20th, 2025
|
||||
*/
|
||||
open class BucketNormalizingInfluence(
|
||||
val tier: Int = 0,
|
||||
val gradient: Float = 0.2F,
|
||||
val firstTier: Float = 1.29F
|
||||
) : SpawningInfluence {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
if (tier == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val normalizationFactor = firstTier + (gradient * (tier - 1)).toDouble()
|
||||
bucketWeights.keys.toList().forEach {
|
||||
val weight = bucketWeights[it] ?: return@forEach
|
||||
bucketWeights[it] = weight.toDouble().pow(1 / normalizationFactor).toFloat()
|
||||
}
|
||||
|
||||
val sum = bucketWeights.values.sum()
|
||||
val to100 = 100F / sum
|
||||
bucketWeights.keys.toList().forEach {
|
||||
val weight = bucketWeights[it] ?: return@forEach
|
||||
bucketWeights[it] = weight * to100
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user