mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 22:56:23 -04:00
Compare commits
177
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eeeab340c | ||
|
|
9163046b45 | ||
|
|
325d1af484 | ||
|
|
6ae71f28e6 | ||
|
|
5687a71de4 | ||
|
|
8199f8d7c2 | ||
|
|
6b39251d65 | ||
|
|
cd03e6f8c4 | ||
|
|
a1b53f90ad | ||
|
|
c0677c6353 | ||
|
|
756989b0c0 | ||
|
|
58a4267f42 | ||
|
|
e8826a3d0e | ||
|
|
0606d57ef1 | ||
|
|
ef2578c1a7 | ||
|
|
3812de7558 | ||
|
|
ef558981c2 | ||
|
|
e4f928bce1 | ||
|
|
6090fce254 | ||
|
|
5a5d43404f | ||
|
|
931c3a7e6c | ||
|
|
d5a6e0dffd | ||
|
|
f56c357e4e | ||
|
|
4bfca0da43 | ||
|
|
52cff2e99d | ||
|
|
52f21ffc82 | ||
|
|
9c31899f3c | ||
|
|
9a5172f1cc | ||
|
|
11051f2b54 | ||
|
|
8b48cfaa50 | ||
|
|
50c3ffc774 | ||
|
|
daf32a5cf3 | ||
|
|
af4d2077ae | ||
|
|
3f090ce691 | ||
|
|
b1a3cbf29d | ||
|
|
130c50a2d6 | ||
|
|
0b65259ee7 | ||
|
|
53897a2613 | ||
|
|
8e6681b3aa | ||
|
|
50427965ab | ||
|
|
3318ee801a | ||
|
|
0b97d139af | ||
|
|
3966607373 | ||
|
|
b7c5f489fd | ||
|
|
39ec6187b5 | ||
|
|
b7681b43f9 | ||
|
|
ae594a0790 | ||
|
|
6af299406d | ||
|
|
29833df50e | ||
|
|
01d42abdb2 | ||
|
|
baa3f47769 | ||
|
|
16e716411a | ||
|
|
3bbc551fd7 | ||
|
|
74381d8e3c | ||
|
|
6688c32a1d | ||
|
|
45dcf6f23b | ||
|
|
e4d92ead6a | ||
|
|
9631f3c9d5 | ||
|
|
0787f2772d | ||
|
|
4011502e54 | ||
|
|
06855f6404 | ||
|
|
815955b904 | ||
|
|
df9eed0da3 | ||
|
|
99b73aa8dd | ||
|
|
f2d5073c7c | ||
|
|
3fda5f7c7a | ||
|
|
9b81666437 | ||
|
|
b43f969c02 | ||
|
|
02c4947513 | ||
|
|
6158b36103 | ||
|
|
2e14f8f8e8 | ||
|
|
bc80d6a171 | ||
|
|
65a7fc6fd7 | ||
|
|
ce019631ce | ||
|
|
0534e0d201 | ||
|
|
895e3ca378 | ||
|
|
3048e3d81d | ||
|
|
77fb94d9ef | ||
|
|
9078ee2191 | ||
|
|
06edc2810e | ||
|
|
81b83aabc1 | ||
|
|
6e7895c3b3 | ||
|
|
c69de804b9 | ||
|
|
fbbed7b20a | ||
|
|
a1b5f0a567 | ||
|
|
e42e06781d | ||
|
|
6a7b71d5a7 | ||
|
|
0a48014931 | ||
|
|
a70fed25c5 | ||
|
|
c2daed803f | ||
|
|
658fed638d | ||
|
|
3faa2a0467 | ||
|
|
77788a91b7 | ||
|
|
745b48e77b | ||
|
|
9a4815f453 | ||
|
|
2ff41cdf9b | ||
|
|
d5268e8751 | ||
|
|
518799ee25 | ||
|
|
b597bb0818 | ||
|
|
1b39a64927 | ||
|
|
dda128e9e3 | ||
|
|
599603501a | ||
|
|
891ed6045d | ||
|
|
a15aa86c66 | ||
|
|
1984990a7e | ||
|
|
49e2e635db | ||
|
|
d795dca4fa | ||
|
|
503511e0ae | ||
|
|
a7f8a281a8 | ||
|
|
5ba6679a84 | ||
|
|
4ecd64bdf6 | ||
|
|
6863cd0602 | ||
|
|
b9e6d63049 | ||
|
|
cb002ff530 | ||
|
|
19da349e78 | ||
|
|
30c3b605a3 | ||
|
|
b8e79d2d3e | ||
|
|
60c2721b53 | ||
|
|
b6f2ab25e1 | ||
|
|
6417f8cfa8 | ||
|
|
1d473437de | ||
|
|
c24b60785f | ||
|
|
08df2fef6c | ||
|
|
bae3da9411 | ||
|
|
db7745589a | ||
|
|
65f0f69726 | ||
|
|
19263ddec6 | ||
|
|
dde4680ce0 | ||
|
|
a2939e34a0 | ||
|
|
6a937492dd | ||
|
|
d13e456d17 | ||
|
|
e36de84927 | ||
|
|
54cbaa9a10 | ||
|
|
dbe743b50a | ||
|
|
7d26870a13 | ||
|
|
a418cbd118 | ||
|
|
e45af26af3 | ||
|
|
bdc260abca | ||
|
|
d9e5e52d9f | ||
|
|
c12f991b18 | ||
|
|
977d66dafd | ||
|
|
f0b1843b71 | ||
|
|
785e63c7d4 | ||
|
|
61c3c995aa | ||
|
|
5dc56bd4fc | ||
|
|
f9afd1d3e6 | ||
|
|
ad3cfe3708 | ||
|
|
109895c0ff | ||
|
|
96bcffcc84 | ||
|
|
dc4f204db6 | ||
|
|
c07c78c331 | ||
|
|
e902e034ad | ||
|
|
77218f5250 | ||
|
|
d5b5e1055b | ||
|
|
06732e89fd | ||
|
|
304f368ae1 | ||
|
|
9e8d6a4f39 | ||
|
|
e22eeafdea | ||
|
|
662efa55c6 | ||
|
|
efb9e538f3 | ||
|
|
47e9491e4e | ||
|
|
c4713b522b | ||
|
|
42c711cfc7 | ||
|
|
1296acd151 | ||
|
|
1a5ef95879 | ||
|
|
681d5f8bcc | ||
|
|
a52d8452b4 | ||
|
|
e65298b587 | ||
|
|
f8c09b8e26 | ||
|
|
5d1019afd5 | ||
|
|
f36f126a2b | ||
|
|
eb9bd116b2 | ||
|
|
da40245fa6 | ||
|
|
70bf4d6654 | ||
|
|
080a5b9c74 | ||
|
|
b0be0b2dca | ||
|
|
d8300436c9 |
@@ -12,9 +12,11 @@
|
||||
|
||||
### Developer
|
||||
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
|
||||
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
|
||||
|
||||
### MoLang & Datapacks
|
||||
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
|
||||
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
|
||||
- Added very many behaviours to choose from.
|
||||
- Removed many NPC presets that now exist as behaviours. This is a breaking change if you are using NPCs with these presets.
|
||||
- NPCs with these presets should be modified to instead list under `"behaviours"` all the equivalent behaviours.
|
||||
@@ -8,7 +8,8 @@
|
||||
- Lure Cakes can be used to modify the spawn rate of Pokémon in the wild.
|
||||
- Added the Saccharine Tree, which can be found in the wild and is used to obtain Honey Bottles.
|
||||
- The leaves of the tree can be harvested to obtain honey, which can be used in the Campfire Pot to make Lure Cakes.
|
||||
- The Log of the tree can be clicked on with honey to create a Honey Log, which increases the change of spawning a Pokemon with an Hidden Ability.
|
||||
- The Log of the tree can be interacted with using a honey bottle to create a Honey-Slathered Saccharine Log, which increases the change of spawning a Pokémon with a Hidden Ability.
|
||||
- Added Hearty Grains
|
||||
- Clicking on the Progress Arrow will show the JEI Recipe Book for the Campfire Pot, which will show all recipes that can be made with it.
|
||||
- Added a Ton of Redstone Compatibility.
|
||||
- Dispenser can now Honey and Unhoney:
|
||||
|
||||
+52
-7
@@ -32,17 +32,22 @@
|
||||
- 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)
|
||||
- Added Galarica Nut Bushes
|
||||
- Many Pokémon (mostly cats) are now feared by phantoms
|
||||
- Lightning is now affected by a Pokemon's ability/typing
|
||||
- Pokémon with the ability Lightning Rod draw in lightning similar to a lightning rod block albeit with a lower priority and range, gain immunity to lightning damage, and receive a temporary damage buff.
|
||||
- Pokémon with the ability Motor Drive are immune to lightning damage and receive a temporary speed buff when struck by lightning
|
||||
- Pokémon with the ability Volt Absorb are immune to lightning damage and receive Regeneration II for a short duration
|
||||
- Ground type Pokémon are immune to lightning damage
|
||||
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
|
||||
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
|
||||
- Galarica Nut bushes now generate on beaches
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
#### Gen 2
|
||||
- Marill
|
||||
- Azumarill
|
||||
- Dunsparce
|
||||
|
||||
#### Gen 3
|
||||
- Azurill
|
||||
- Spoink
|
||||
- Grumpig
|
||||
- Snorunt
|
||||
@@ -189,6 +194,19 @@
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
|
||||
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
|
||||
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
|
||||
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
|
||||
- Added molang particle queries for getting distance to targeted entities
|
||||
- Added debug renderer for poseable entity locators
|
||||
- Added time zone as a config option
|
||||
- Added optional boolean to use time zone for MoLang function "date_local_time" and "date_of"
|
||||
- Added "irlDateRange" and "irlTimeRange" as a condition for spawning
|
||||
- Added an optional MoValue to either use system timezone when set as true, or use the supplied string as a timezone for MoLang functions `date_local_time` and `date_of`. If the timezone supplied fails to parse this function now returns `0.0`.
|
||||
|
||||
### Additions
|
||||
- Added time zone as a config option
|
||||
- Added optional boolean to use time zone for MoLang function "date_local_time" and "date_of"
|
||||
- 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
|
||||
@@ -229,6 +247,12 @@
|
||||
- Updated interaction interface to include 4 more option spaces
|
||||
- Made lecterns that hold a Pokédex emit light.
|
||||
- Updated light levels for active PC, Pasture, Healing Machine, and Data Monitor.
|
||||
- Vivichokes now always drop one seed when harvested, and converting a fully grown Vivichoke to seeds via crafting results in 1 seed.
|
||||
- Healing Machine recipe rebalanced.
|
||||
- Reorganised the `block` texture folder to be more organised, in line with the `item` texture folder.
|
||||
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
|
||||
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
|
||||
- Removed Braised Vivichoke
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -294,9 +318,10 @@
|
||||
- Fixed Pokédex Scanner not respecting the "Invert Mouse" option.
|
||||
- Fixed a crash due to a ConcurrentModificationException that could occur during world generation.
|
||||
- Fixed Moon Ball moon phase logic to actually work correctly
|
||||
- Fixed recoil eyes on Shellder & Cloyster.
|
||||
- Fixed Shroomish spinning at lightning speed when fainting (no longer allowed to let it rip.)
|
||||
- Fixed Quaxly's animations showing hidden wing sets
|
||||
- Fixed `/pokedex printcalculations` to now show the correct percentage completed of the Pokedex
|
||||
- Fixed mod incompatibility with the `Raised` mod
|
||||
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
|
||||
- Fixed a rare edge case where sorting your PC could be rolled back later.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -331,10 +356,15 @@
|
||||
- The IVs class has now been extended to include Hyper Trained values.
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`.
|
||||
- Added `HyperTrainedIvEvent.Pre` and `HyperTrainedIvEvent.Post`.
|
||||
- Added `Pokemon#validateMoveSet()` to validate an existing Pokemon's moveset, clearing illegal moves.
|
||||
- Added a `hoverText` option to PartySelectCallback, to display a tooltip on hovering over a Pokémon in the selection screen.
|
||||
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
|
||||
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
|
||||
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
- Pokemon no longer have a change observable
|
||||
|
||||
### MoLang & Datapacks
|
||||
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
|
||||
- The `requiredContext` for an item interaction evolution
|
||||
@@ -364,9 +394,24 @@
|
||||
- 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
|
||||
- Added Molang functions for Party and PC: `set_pokemon`
|
||||
- Added Molang functions for Pokémon: `pokeball`, `held_item`, `remove_held_item`, `hyper_train_iv`, `validate_moveset`, `initialize_moveset`, and `add_exp`.
|
||||
- Added Molang functions for Pokémon: `aspects`, `form_aspects`, `unlearn_move`, `teach_learnable_moves`, `cosmetic_item`, and `remove_cosmetic_item`.
|
||||
- Added Molang functions for Pokémon: `ability`, `set_iv`, `set_ev`, `teach_move`, and `can_learn_move`.
|
||||
- Adds Flows for `STARTER_CHOSEN`, `SHOULDER_MOUNTED`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
|
||||
- Adds Flows for `POKEMON_HEALED`, `POKEMON_SCANNED`, `BERRY_HARVEST`, `LOOT_DROPPED`, `POKEMON_SEEN`, `COLLECT_EGG`, `HATCH_EGG`, and `EXPERIENCE_GAINED`.
|
||||
- Adds Flows for `POKEMON_CATCH_RATE`, `BAIT_SET`, `BAIT_SET_PRE`, `BAIT_CONSUMED`, `POKEROD_CAST_PRE`, `POKEROD_CAST_POST`, `POKEROD_REEL`, and `BOBBER_SPAWN_POKEMON_PRE`.
|
||||
- Adds Flows for `POKEMON_ASPECTS_CHANGED`, `FRIENDSHIP_UPDATED`, `CHANGE_PC_BOX_WALLPAPER_EVENT_PRE`, `CHANGE_PC_BOX_WALLPAPER_EVENT_POST`, and `FULLNESS_UPDATED`.
|
||||
- MoLang triggered battles may now set the battle format, whether to clone the player's party, set level, or heal prior.
|
||||
- Added Molang function for Player: `inventory`
|
||||
- Adds Flows for `STARTER_CHOSEN`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
|
||||
- Adds Pokemon functions for `pokeball`, `held_item` and `remove_held_item`
|
||||
- Adds Pokemon functions for `pokeball`, `held_item`, `remove_held_item`, `add_aspects`, and `remove_aspects`
|
||||
- Added `pokemon.hyper_train_iv` as an available Molang function.
|
||||
- Added `prepare_{effect}` and `damage_{effect}` action effect hooks in battles for more battle particle effects.
|
||||
- Added `q.has_argument(<argument_name>, [argument_value])` MoLang function to several battle-related action effect contexts.
|
||||
- Added `q.has_argument_at(<index>, [argument_value])` MoLang function to several battle-related action effect contexts.
|
||||
- Added `q.hit_count` MoLang function to move action effect contexts.
|
||||
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.duck;
|
||||
|
||||
public interface ChannelDuck {
|
||||
void cobblemon$applyLowPassFilter(float gain, float hfGain);
|
||||
void cobblemon$inverseAttenuation(float rolloffFactor);
|
||||
void cobblemon$clearFilters();
|
||||
|
||||
// void cobblemon$exponentialAttenuation(float rolloff, float refDistance, float maxDistance);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.duck.ChannelDuck;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.lwjgl.openal.EXTEfx;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(Channel.class)
|
||||
public abstract class ChannelMixin implements ChannelDuck {
|
||||
|
||||
@Shadow
|
||||
private int source;
|
||||
|
||||
// Id of filter applied when sound is obstructed
|
||||
private int cobblemon$lowPassFilterId = 0;
|
||||
|
||||
@Override
|
||||
public void cobblemon$applyLowPassFilter(float gain, float hfGain) {
|
||||
// Delete old filter if it exists
|
||||
if (cobblemon$lowPassFilterId != 0) {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0); // Detach filter from source
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId); // Delete old filter
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
|
||||
// Generate a fresh filter
|
||||
int filterId = EXTEfx.alGenFilters();
|
||||
if (!EXTEfx.alIsFilter(filterId)) {
|
||||
System.err.println("Failed to create OpenAL low-pass filter");
|
||||
return;
|
||||
}
|
||||
|
||||
EXTEfx.alFilteri(filterId, EXTEfx.AL_FILTER_TYPE, EXTEfx.AL_FILTER_LOWPASS);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAIN, gain);
|
||||
EXTEfx.alFilterf(filterId, EXTEfx.AL_LOWPASS_GAINHF, hfGain);
|
||||
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, filterId);
|
||||
|
||||
// Save the new filter ID
|
||||
cobblemon$lowPassFilterId = filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$inverseAttenuation(float rolloffFactor) {
|
||||
AL10.alSourcei(source, AL10.AL_DISTANCE_MODEL, AL10.AL_INVERSE_DISTANCE);
|
||||
AL10.alSourcef(source, AL10.AL_ROLLOFF_FACTOR, rolloffFactor);
|
||||
AL10.alSourcef(source, AL10.AL_REFERENCE_DISTANCE, 1.0f);
|
||||
AL10.alSourcef(source, AL10.AL_MAX_DISTANCE, 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$clearFilters() {
|
||||
AL10.alSourcei(source, EXTEfx.AL_DIRECT_FILTER, 0);
|
||||
|
||||
if (EXTEfx.alIsFilter(cobblemon$lowPassFilterId)) {
|
||||
EXTEfx.alDeleteFilters(cobblemon$lowPassFilterId);
|
||||
cobblemon$lowPassFilterId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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 net.minecraft.world.entity.monster.Phantom;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(targets = "net/minecraft/world/entity/monster/Phantom$PhantomSweepAttackGoal")
|
||||
public class EntityPhantomMixin {
|
||||
// TODO: Figure out how to access the outer class that wraps SwoopMovementGoal
|
||||
// Need to find the cat mons that are near the phantom
|
||||
|
||||
// @Inject(method = "shouldContinue", at = @At(value = "RETURN"), cancellable = true)
|
||||
// public void cobblemon$shouldContinue(CallbackInfoReturnable<Boolean> ci) {
|
||||
//// var goalSelectorField = PhantomEntity.class.getClasses();// PhantomEntity.class.getFields();// .class.getDeclaredField("goalSelector");
|
||||
//
|
||||
//// goalSelectorField.setAccessible(true);
|
||||
//// GoalSelector goalSelector = (GoalSelector) goalSelectorField.get(mobEntity);
|
||||
//// final SwoopMovementGoal phantom = (SwoopMovementGoal) (Object) this;
|
||||
//// if(ci.getReturnValue() == false) {
|
||||
//// var result = this$0.getWorld().getEntitiesByClass(
|
||||
//// PokemonEntity.class,
|
||||
//// this$0.getBoundingBox().expand(16.0),
|
||||
//// entity -> {
|
||||
//// return ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom();
|
||||
//// }
|
||||
//// ).isEmpty();
|
||||
////
|
||||
//// ci.setReturnValue(result);
|
||||
//// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LightningBolt;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Mixin(LightningBolt.class)
|
||||
public abstract class LightningBoltMixin {
|
||||
|
||||
@Inject(method = "spawnFire", at = @At(value = "Head"), cancellable = true)
|
||||
private void cobblemon$spawnFire(int extraIgnitions, CallbackInfo ci) {
|
||||
// This prevents fire from spawning around Pokemon who otherwise should be immune to lightning and effectively absorb the hit
|
||||
final LightningBolt lightningBolt = (LightningBolt) (Object) this;
|
||||
List<Entity> list = lightningBolt.level().getEntities(
|
||||
lightningBolt,
|
||||
new AABB(lightningBolt.getX() - 2.0, lightningBolt.getY() - 2.0, lightningBolt.getZ() - 2.0, lightningBolt.getX() + 2.0, lightningBolt.getY() + 2.0, lightningBolt.getZ() + 2.0),
|
||||
it -> {
|
||||
if (!it.isAlive() || !(it instanceof PokemonEntity)) {
|
||||
return false;
|
||||
}
|
||||
String abilityName = ((PokemonEntity) it).getPokemon().getAbility().getName();
|
||||
return abilityName.equals("lightningrod") || abilityName.equals("voltabsorb") || abilityName.equals("motordrive");
|
||||
});
|
||||
if (!list.isEmpty()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokemon.ai.EntityBehaviour;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.monster.Phantom;
|
||||
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.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.entity.monster.Phantom$PhantomSweepAttackGoal")
|
||||
public abstract class PhantomSweepAttackGoalMixin extends Goal {
|
||||
|
||||
@Unique
|
||||
private Phantom phantomSelf; // Allows access to the outer class
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void onInit(Phantom phantom, CallbackInfo ci) {
|
||||
this.phantomSelf = phantom;
|
||||
}
|
||||
|
||||
@Inject(method = "canContinueToUse", at = @At("RETURN"), cancellable = true)
|
||||
private void modifyCanContinueToUse(CallbackInfoReturnable<Boolean> cir) {
|
||||
Phantom phantom = this.phantomSelf; // Outer class instance
|
||||
if(phantom == null) return;
|
||||
|
||||
if (cir.getReturnValue()) {
|
||||
var nearbyFearedEntities = phantom.level().getEntities(phantom, phantom.getBoundingBox().inflate(16.0),
|
||||
(entity -> ((entity instanceof PokemonEntity && ((PokemonEntity) entity).getBeamMode() == 0 && ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom())
|
||||
|| (entity instanceof ServerPlayer && EntityBehaviour.Companion.hasPhantomFearedShoulderMount((ServerPlayer)entity)))));
|
||||
if (!nearbyFearedEntities.isEmpty()) {
|
||||
nearbyFearedEntities.forEach(entity -> {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
((PokemonEntity) entity).cry();
|
||||
}
|
||||
//TODO: Find a way to make shoulder mounted Pokemon cry
|
||||
});
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelMixin {
|
||||
|
||||
@Inject(method = "findLightningRod", at = @At(value = "RETURN"), cancellable = true)
|
||||
private void cobblemon$findLightningRod(BlockPos blockPos, CallbackInfoReturnable<Optional<BlockPos>> ci) {
|
||||
final ServerLevel serverLevel = (ServerLevel) (Object) this;
|
||||
if (ci.getReturnValue().isEmpty()) {
|
||||
// Search for Pokemon with the ability Lightning Rod and redirect to them
|
||||
AABB aABB = AABB.encapsulatingFullBlocks(blockPos, new BlockPos(blockPos.atY(serverLevel.getMaxBuildHeight()))).inflate(64.0);
|
||||
List<LivingEntity> list = serverLevel.getEntitiesOfClass(LivingEntity.class, aABB, (livingEntity) -> livingEntity != null
|
||||
&& livingEntity.isAlive()
|
||||
&& livingEntity instanceof PokemonEntity
|
||||
&& ((PokemonEntity) livingEntity).getBeamMode() == 0
|
||||
&& ((PokemonEntity) livingEntity).getPokemon().getAbility().getName().equals("lightningrod"));
|
||||
if (!list.isEmpty()) {
|
||||
ci.setReturnValue(Optional.of((list.get(serverLevel.random.nextInt(list.size()))).blockPosition()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.block.HeartyGrainsBlock;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.DoublePlantBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.feature.SimpleBlockFeature;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.SimpleBlockConfiguration;
|
||||
import org.spongepowered.asm.mixin.Debug;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(SimpleBlockFeature.class)
|
||||
@Debug(export = true, print = true)
|
||||
public class SimpleBlockFeatureMixin {
|
||||
//this mixin inserts the logic needed to ensure that when hearty grains are of a certain age, they place with the upper block too
|
||||
//similarly to what the doubleblock logic is here
|
||||
@Inject(method = "place", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;canSurvive(Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z", shift = At.Shift.AFTER), cancellable = true)
|
||||
public void place(FeaturePlaceContext<SimpleBlockConfiguration> context, CallbackInfoReturnable<Boolean> cir,
|
||||
@Local BlockState blockState, @Local BlockPos blockPos, @Local WorldGenLevel worldGenLevel) {
|
||||
if (!(blockState.getBlock() instanceof HeartyGrainsBlock)) {
|
||||
return;
|
||||
}
|
||||
if (blockState.getValue(HeartyGrainsBlock.Companion.getAGE()) <= HeartyGrainsBlock.AGE_AFTER_HARVEST) {
|
||||
return; //block is too young to be double
|
||||
}
|
||||
if (!worldGenLevel.isEmptyBlock(blockPos.above())) {
|
||||
cir.setReturnValue(false); //no space for the 2nd block above
|
||||
return;
|
||||
}
|
||||
DoublePlantBlock.placeAt(worldGenLevel, blockState, blockPos, Block.UPDATE_CLIENTS);
|
||||
cir.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
@@ -8,23 +8,33 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideAttenuationModel;
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound;
|
||||
import com.cobblemon.mod.common.client.sound.BattleMusicController;
|
||||
import com.cobblemon.mod.common.duck.ChannelDuck;
|
||||
import com.cobblemon.mod.common.duck.SoundEngineDuck;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import net.minecraft.client.resources.sounds.Sound;
|
||||
import net.minecraft.client.resources.sounds.SoundInstance;
|
||||
import net.minecraft.client.resources.sounds.TickableSoundInstance;
|
||||
import net.minecraft.client.sounds.ChannelAccess;
|
||||
import net.minecraft.client.sounds.SoundEngine;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(SoundEngine.class)
|
||||
@@ -45,6 +55,86 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
|
||||
@Shadow
|
||||
public abstract boolean isActive(SoundInstance sound);
|
||||
|
||||
@Inject(method = "play",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V"
|
||||
),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$overrideChannelExecute(
|
||||
SoundInstance sound,
|
||||
CallbackInfo ci,
|
||||
@Local(name = "h") float h,
|
||||
@Local(name = "i") float i,
|
||||
@Local(name = "g") float g,
|
||||
@Local(name = "bl2") boolean bl2,
|
||||
@Local(name = "bl3") boolean bl3,
|
||||
@Local(name = "bl") boolean bl,
|
||||
@Local(name = "vec3") Vec3 vec3,
|
||||
@Local(name = "channelHandle") ChannelAccess.ChannelHandle handle
|
||||
) {
|
||||
if (!(sound instanceof RideLoopSound rideSound)) return;
|
||||
|
||||
if (handle == null) return;
|
||||
|
||||
handle.execute((channel) -> {
|
||||
channel.setPitch(i);
|
||||
channel.setVolume(h);
|
||||
|
||||
if (rideSound.getRideAttenuation() == RideAttenuationModel.LINEAR) {
|
||||
channel.linearAttenuation(g);
|
||||
} else if (rideSound.getRideAttenuation() == RideAttenuationModel.EXPONENTIAL){
|
||||
((ChannelDuck) channel).cobblemon$inverseAttenuation(g);
|
||||
} else {
|
||||
channel.disableAttenuation();
|
||||
}
|
||||
|
||||
// Apply low pass filter if the rideSound dictate that it should be muffled
|
||||
if (rideSound.getShouldMuffle()) {
|
||||
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
|
||||
} else {
|
||||
((ChannelDuck) channel).cobblemon$clearFilters();
|
||||
}
|
||||
|
||||
channel.setLooping(bl2 && !bl3);
|
||||
channel.setSelfPosition(vec3);
|
||||
channel.setRelative(bl);
|
||||
});
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "tickNonPaused",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/sounds/ChannelAccess$ChannelHandle;execute(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private void cobblemon$tickableSoundTick(
|
||||
CallbackInfo ci,
|
||||
@Local(name = "tickableSoundInstance") TickableSoundInstance instance,
|
||||
@Local(name = "channelHandle", ordinal = 0) ChannelAccess.ChannelHandle handle,
|
||||
@Local(name = "f", ordinal = 0) float volume,
|
||||
@Local(name = "g", ordinal = 0) float pitch,
|
||||
@Local(name = "vec3", ordinal = 0) Vec3 vec3
|
||||
) {
|
||||
if (!(instance instanceof RideLoopSound rideSound)) return;
|
||||
|
||||
handle.execute(channel -> {
|
||||
channel.setVolume(volume);
|
||||
channel.setPitch(pitch);
|
||||
channel.setSelfPosition(vec3);
|
||||
|
||||
// Apply low pass filter if the rideSound dictate that it should be muffled
|
||||
if (rideSound.getShouldMuffle()) {
|
||||
((ChannelDuck) channel).cobblemon$applyLowPassFilter(1.0f, rideSound.getMuffleAmount());
|
||||
} else {
|
||||
((ChannelDuck) channel).cobblemon$clearFilters();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Resumes the Source belonging to the queried SoundInstance. */
|
||||
private void resume(SoundInstance sound) {
|
||||
ChannelAccess.ChannelHandle sourceManager = this.instanceToChannel.get(sound);
|
||||
|
||||
+3
-4
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
import com.cobblemon.mod.common.CobblemonRecipeCategories;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotBookCategory;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -48,10 +49,8 @@ public abstract class ClientRecipeBookMixin {
|
||||
@Inject(method = "getCategory", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategories> cir) {
|
||||
Recipe<?> recipe2 = recipe.value();
|
||||
if (recipe2 instanceof CookingPotRecipe || recipe2 instanceof CookingPotShapelessRecipe) {
|
||||
CookingPotBookCategory category = recipe2 instanceof CookingPotRecipe
|
||||
? ((CookingPotRecipe) recipe2).getCategory()
|
||||
: ((CookingPotShapelessRecipe) recipe2).category();
|
||||
if (recipe2 instanceof CookingPotRecipeBase) {
|
||||
CookingPotBookCategory category = ((CookingPotRecipeBase) recipe2).getCategory();
|
||||
RecipeBookCategories var7;
|
||||
switch (category) {
|
||||
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
|
||||
|
||||
+5
-6
@@ -9,22 +9,21 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import net.minecraft.client.gui.components.AbstractSelectionList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(AbstractSelectionList.class)
|
||||
public abstract class EntryListWidgetMixin {
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
@ModifyConstant(method = "getEntryAtPosition", constant = @Constant(intValue = 4))
|
||||
public int cobblemon$adjustOnlyIfNecessary(int input) {
|
||||
@ModifyExpressionValue(method = "getEntryAtPosition", at = @At(value = "CONSTANT", args = {"intValue=4"}))
|
||||
public int cobblemon$adjustOnlyIfNecessary(int original) {
|
||||
if(ScrollingWidget.class.isInstance(this)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return input;
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.EntityCallbackHandler
|
||||
import com.cobblemon.mod.common.events.FlowHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
@@ -313,6 +314,7 @@ object Cobblemon {
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
FlowHandler.setup()
|
||||
EntityCallbackHandler.setup()
|
||||
|
||||
ifDedicatedServer {
|
||||
isDedicatedServer = true
|
||||
|
||||
@@ -19,6 +19,7 @@ object CobblemonActivities {
|
||||
val POKEMON_GROW_CROP = register(Activity("pokemon_grow_crop"))
|
||||
val POKEMON_SLEEP_ON_TRAINER_BED = register(Activity("pokemon_sleep_on_trainer_bed"))
|
||||
val POKEMON_HERD = register(Activity("pokemon_herd"))
|
||||
val POKEMON_POLLINATION = register(Activity("pokemon_pollination"))
|
||||
|
||||
fun register(activity: Activity): Activity {
|
||||
activities.add(activity)
|
||||
|
||||
@@ -78,6 +78,9 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
|
||||
*/
|
||||
val autoPokemonBehaviours = mutableListOf<CobblemonBehaviour>()
|
||||
|
||||
/** See [autoPokemonBehaviours] but think npc instead of pokemon*/
|
||||
val autoNPCBehaviours = mutableListOf<CobblemonBehaviour>()
|
||||
|
||||
val behaviours = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
@@ -94,6 +97,8 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
|
||||
val behaviour = parse(reader, resolvedIdentifier)
|
||||
if ("pokemon/auto/" in identifier.path) {
|
||||
autoPokemonBehaviours.add(behaviour)
|
||||
} else if ("npc/auto/" in identifier.path) {
|
||||
autoNPCBehaviours.add(behaviour)
|
||||
}
|
||||
data[resolvedIdentifier] = behaviour
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ object CobblemonFlows : DataRegistry {
|
||||
functions: Map<String, (MoParams) -> Any> = emptyMap(),
|
||||
cancelable: Cancelable? = null
|
||||
) {
|
||||
if (eventResourceLocation !in flows) return
|
||||
|
||||
if (cancelable == null) {
|
||||
runtime.environment.query.functions.remove("cancel")
|
||||
} else {
|
||||
|
||||
@@ -389,40 +389,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
|
||||
|
||||
@JvmField
|
||||
val TASTY_TAIL = noSettingsItem("tasty_tail")
|
||||
val TASTY_TAIL = create("tasty_tail", foodItem(3, 0.3f))
|
||||
|
||||
@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 // 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 // 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 // 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 // todo get final values for this from vera
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@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 // 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 get final values for this from vera
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@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 get final values for this from vera
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 10, 1.2f, MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1f)
|
||||
|
||||
@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)
|
||||
@JvmField
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 9, 0.1335f, false)
|
||||
@JvmField
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 5, .28f, false)
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 13, 0.5f, false)
|
||||
|
||||
// todo we might need to wait on these for later? These impact battles and may be harder to do
|
||||
/*@JvmField
|
||||
@@ -435,30 +423,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.build())))
|
||||
@JvmField
|
||||
val FRIED_RICE = create("fried_rice", CobblemonItem(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.nutrition(8)
|
||||
.saturationModifier(0.2375F)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.nutrition(6)
|
||||
.saturationModifier(0.2335F)
|
||||
.usingConvertsTo(Items.STICK)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10) // todo get final values for this from vera
|
||||
.saturationModifier(1.2F) // todo get final values for this from vera
|
||||
.alwaysEdible()
|
||||
.nutrition(5)
|
||||
.saturationModifier(0.22F)
|
||||
.usingConvertsTo(Items.STICK)
|
||||
.build())))
|
||||
|
||||
//@JvmField
|
||||
@@ -685,14 +665,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = heldItem("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Properties().food(FoodProperties.Builder().fast().nutrition(1).saturationModifier(0.2f).build())), "leek")
|
||||
@JvmField
|
||||
val ROASTED_LEEK = compostableItem("roasted_leek", CobblemonItem(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.3f).build())), 0.85f)
|
||||
@JvmField
|
||||
val BRAISED_VIVICHOKE = compostableItem("braised_vivichoke", foodItem(6, 0.6F), 0.85f)
|
||||
val ROASTED_LEEK = compostableItem("roasted_leek", CobblemonItem(Item.Properties().food(FoodProperties.Builder().fast().nutrition(3).saturationModifier(0.2f).build())), 0.85f)
|
||||
@JvmField
|
||||
val VIVICHOKE_DIP = create("vivichoke_dip", object : CobblemonItem(Properties().stacksTo(1)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.saturationModifier(0.6F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.usingConvertsTo(Items.BOWL)
|
||||
@@ -703,7 +681,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Properties().food(FoodProperties.Builder().nutrition(1).fast().saturationModifier(0.2f).build())), 0.60f)
|
||||
val ENERGY_ROOT = compostableItem("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Properties()), 0.60f)
|
||||
@JvmField
|
||||
val REVIVAL_HERB = compostableItem("revival_herb", RevivalHerbItem(CobblemonBlocks.REVIVAL_HERB))
|
||||
@JvmField
|
||||
@@ -733,7 +711,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val HEAL_POWDER = create("heal_powder", HealPowderItem())
|
||||
@JvmField
|
||||
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(8).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
|
||||
val LEEK_AND_POTATO_STEW = create("leek_and_potato_stew", CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(6).saturationModifier(0.6f).usingConvertsTo(Items.BOWL).build()).stacksTo(1)))
|
||||
@JvmField
|
||||
val REVIVE = create("revive", ReviveItem(max = false))
|
||||
@JvmField
|
||||
@@ -1544,8 +1522,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
stacksTo: Int,
|
||||
nutrition: Int,
|
||||
saturationModifier: Float,
|
||||
effect: MobEffectInstance? = null,
|
||||
effectChance: Float? = null,
|
||||
alwaysEdible: Boolean = false,
|
||||
convertsToOnUse: ItemStack? = null
|
||||
): RegionalFoodItem {
|
||||
@@ -1553,10 +1529,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
.nutrition(nutrition)
|
||||
.saturationModifier(saturationModifier)
|
||||
|
||||
if (effect != null && effectChance != null) {
|
||||
foodPropertiesBuilder.effect(effect, effectChance)
|
||||
}
|
||||
|
||||
if (alwaysEdible == true) {
|
||||
foodPropertiesBuilder.alwaysEdible()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -43,11 +44,16 @@ object CobblemonMemories {
|
||||
val NEAREST_VISIBLE_ATTACKER = register<LivingEntity>("nearest_visible_attacker")
|
||||
val NEARBY_GROWABLE_CROPS = register<BlockPos>("nearby_growable_crops")
|
||||
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
|
||||
val HIVE_LOCATION = register<BlockPos>("hive_location")
|
||||
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
|
||||
val NEARBY_FLOWERS = register<List<BlockPos>>("nearby_flower")
|
||||
val POLLINATED = register<Boolean>("pollinated", PrimitiveCodec.BOOL)
|
||||
/** who am i following rn? */
|
||||
val HERD_LEADER = register<String>("herd_leader", PrimitiveCodec.STRING)
|
||||
/** how many are following me rn? */
|
||||
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
|
||||
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
|
||||
val WANDER_CONTROL = register<CobblemonWanderControl>("wander_control")
|
||||
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
val memoryModule = MemoryModuleType(Optional.of(codec))
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
@@ -77,6 +78,7 @@ import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
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.npc.CloseNPCEditorPacket
|
||||
@@ -402,6 +404,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler))
|
||||
list.add(PacketRegisterInfo(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler))
|
||||
list.add(PacketRegisterInfo(SaccharineLogBlockParticlesPacket.ID, SaccharineLogBlockParticlesPacket::decode, SaccharineLogBlockParticlesHandler))
|
||||
|
||||
// Hax
|
||||
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.*
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
|
||||
@@ -36,6 +33,10 @@ object CobblemonSensors {
|
||||
|
||||
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
|
||||
|
||||
val NEARBY_BEE_HIVE = register("nearby_bee_hive", ::BeeHiveSensor)
|
||||
|
||||
val NEARBY_FLOWER = register("nearby_flower", ::FlowerSensor)
|
||||
|
||||
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
|
||||
val sensor = SensorType(supplier)
|
||||
sensors[id] = sensor
|
||||
|
||||
@@ -275,6 +275,11 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
RELIC_COIN_SACK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val RIDE_LOOP_LEATHER = create("ride.loop.leather")
|
||||
@JvmField
|
||||
val RIDE_LOOP_PLUMAGE = create("ride.loop.plumage")
|
||||
|
||||
@JvmField
|
||||
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
|
||||
@JvmField
|
||||
@@ -319,6 +324,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val IMPACT_WATER = this.create("impact.water")
|
||||
|
||||
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
|
||||
TUMBLESTONE_BREAK,
|
||||
|
||||
+23
@@ -8,8 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.serialization.Dynamic
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -28,6 +35,9 @@ class BehaviourConfigurationContext {
|
||||
val memories = mutableSetOf<MemoryModuleType<*>>()
|
||||
val sensors = mutableSetOf<SensorType<*>>()
|
||||
|
||||
val onAddScripts = mutableListOf<ResourceLocation>()
|
||||
val onAdd = mutableListOf<ExpressionLike>()
|
||||
|
||||
fun getOrCreateActivity(activity: Activity): ActivityConfigurationContext {
|
||||
return activities.firstOrNull { it.activity == activity } ?: ActivityConfigurationContext(activity).also(activities::add)
|
||||
}
|
||||
@@ -48,6 +58,14 @@ class BehaviourConfigurationContext {
|
||||
this.sensors.addAll(sensors)
|
||||
}
|
||||
|
||||
fun addOnAddScript(script: ResourceLocation) {
|
||||
onAddScripts.add(script)
|
||||
}
|
||||
|
||||
fun addOnAddScript(script: ExpressionLike) {
|
||||
onAdd.add(script)
|
||||
}
|
||||
|
||||
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>, dynamic: Dynamic<*>) {
|
||||
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
@@ -62,6 +80,11 @@ class BehaviourConfigurationContext {
|
||||
// Apply the brain config
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
brain = entity.assignNewBrainWithMemoriesAndSensors(dynamic, memories, sensors)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
onAddScripts.forEach { CobblemonScripts.run(it, runtime) }
|
||||
onAdd.forEach { runtime.resolve(it) }
|
||||
}
|
||||
activities.forEach { it.apply(entity) }
|
||||
brain.setCoreActivities(coreActivities)
|
||||
|
||||
+39
-2
@@ -9,7 +9,17 @@
|
||||
package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* This is used in the AI system to store optional additional information about the attack target,
|
||||
@@ -30,5 +40,32 @@ import com.cobblemon.mod.common.util.asExpression
|
||||
*/
|
||||
class CobblemonAttackTargetData(
|
||||
/** An expression that returns false when the target is no longer worth attacking. */
|
||||
val shouldContinue: Expression = "true".asExpression()
|
||||
)
|
||||
var shouldContinue: Expression = "true".asExpression(),
|
||||
var hitsUntilDisengage: Int = -1, // -1 means no limit
|
||||
var disengageAction: (entity: Entity) -> Unit = {}
|
||||
) {
|
||||
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
|
||||
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
|
||||
it.addFunction("set_hits_until_disengage") { params ->
|
||||
val value = params.getDouble(0)
|
||||
hitsUntilDisengage = value.toInt()
|
||||
DoubleValue(hitsUntilDisengage.toDouble())
|
||||
}
|
||||
it.addFunction("set_disengage_action") { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val function = CobblemonScripts.scripts[script] ?: run {
|
||||
Cobblemon.LOGGER.warn("set_disengage_action was given script '$script' which does not exist.")
|
||||
return@addFunction DoubleValue.ZERO
|
||||
}
|
||||
disengageAction = { entity ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.query.addFunction("entity") { entity.asMostSpecificMoLangValue() }
|
||||
runtime.resolve(function)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
it.addFunction("unset_disengage_action") { params ->
|
||||
disengageAction = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -28,23 +29,35 @@ class CobblemonBehaviour(
|
||||
val visible: Boolean = true,
|
||||
val entityType: ResourceLocation? = null,
|
||||
val configurations: List<BehaviourConfig> = mutableListOf(),
|
||||
val undo: ExpressionLike? = null,
|
||||
val undoScript: ResourceLocation? = null,
|
||||
// I feel like the onAdd+onAddScript etc could be merged using some interface and a clever deserializer,
|
||||
// detect if it's a ResourceLocation and failing that, Expression. The on[..]Script fields are kinda fringe though.
|
||||
@SerializedName("onRemove", alternate = ["undo"])
|
||||
val onRemove: ExpressionLike? = null,
|
||||
@SerializedName("onRemoveScript", alternate = ["undoScript"])
|
||||
val onRemoveScript: ResourceLocation? = null,
|
||||
val onAdd: ExpressionLike? = null,
|
||||
val onAddScript: ResourceLocation? = null,
|
||||
) {
|
||||
fun canBeApplied(entity: LivingEntity) = entityType?.let { entityType == entity.type.builtInRegistryHolder().unwrapKey().get().location() } != false
|
||||
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
|
||||
if (onAdd != null) {
|
||||
behaviourConfigurationContext.addOnAddScript(onAdd)
|
||||
}
|
||||
if (onAddScript != null) {
|
||||
behaviourConfigurationContext.addOnAddScript(onAddScript)
|
||||
}
|
||||
configurations.forEach { it.configure(entity, behaviourConfigurationContext) }
|
||||
}
|
||||
|
||||
/** Undoes anything that needs undoing once this configuration is being removed from an entity that had it before. */
|
||||
fun undo(entity: LivingEntity) {
|
||||
fun onRemove(entity: LivingEntity) {
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (undoScript != null) {
|
||||
CobblemonScripts.run(undoScript, runtime)
|
||||
if (onRemoveScript != null) {
|
||||
CobblemonScripts.run(onRemoveScript, runtime)
|
||||
}
|
||||
if (undo != null) {
|
||||
runtime.resolve(undo)
|
||||
if (onRemove != null) {
|
||||
runtime.resolve(onRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.ai
|
||||
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Wraps around [BlockPosTracker] to add additional flags for Cobblemon-specific behavior.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 4th, 2025
|
||||
*/
|
||||
class CobblemonBlockPosTracker(
|
||||
pos: Vec3,
|
||||
val flags: Set<String> = emptySet(),
|
||||
) : BlockPosTracker(pos) {
|
||||
companion object {
|
||||
const val BATTLE_LOOK_BYPASS_FLAG = "battle_look_bypass"
|
||||
}
|
||||
|
||||
fun isBattleLookBypass() = flags.contains(BATTLE_LOOK_BYPASS_FLAG)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
|
||||
/**
|
||||
* Used to maintain some settings around how the entity should wander this tick. The idea is you
|
||||
* reset this with some script task at priority 0 then some number of steps before it gets to the wander
|
||||
* tasks you can have other conditional tasks jump in to configure parts of this.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 18, 2025
|
||||
*/
|
||||
class CobblemonWanderControl(
|
||||
var center: WanderCenter? = null,
|
||||
var maxAttempts: Int = 4, // How many times to try to find a suitable point before giving up on that tick.
|
||||
var allowLand: Boolean = true,
|
||||
var allowWater: Boolean = false,
|
||||
var allowAir: Boolean = false,
|
||||
var pathCooldownTicks: Int = 20 * 5, // Used to mitigate the cost of pathfinding.
|
||||
var wanderSpeed: Float = 0.35F,
|
||||
) {
|
||||
class WanderCenter(val x: Number, val y: Number, val z: Number, val minRange: Float, val maxRange: Float)
|
||||
|
||||
@Transient
|
||||
val struct: QueryStruct = QueryStruct(hashMapOf())
|
||||
.addFunction("center_x") { center?.x ?: 0.0 }
|
||||
.addFunction("center_y") { center?.y ?: 0.0 }
|
||||
.addFunction("center_z") { center?.z ?: 0.0 }
|
||||
.addFunction("max_attempts") { maxAttempts.toDouble() }
|
||||
.addFunction("min_range") { center?.minRange ?: 0.0 }
|
||||
.addFunction("max_range") { center?.maxRange ?: 0.0 }
|
||||
.addFunction("allow_land") { if (allowLand) 1.0 else 0.0 }
|
||||
.addFunction("allow_water") { if (allowWater) 1.0 else 0.0 }
|
||||
.addFunction("allow_air") { if (allowAir) 1.0 else 0.0 }
|
||||
.addFunction("path_cooldown_ticks") { pathCooldownTicks.toDouble() }
|
||||
.addFunction("wander_speed") { wanderSpeed }
|
||||
.addFunction("set_center") { params ->
|
||||
if (params.params.size < 3) {
|
||||
throw IllegalArgumentException("set_center requires at least 3 parameters: x, y, z")
|
||||
}
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val minRange = params.getDoubleOrNull(3) ?: 0.0
|
||||
val maxRange = params.getDoubleOrNull(4) ?: Float.MAX_VALUE.toDouble()
|
||||
center = WanderCenter(
|
||||
x = x,
|
||||
y = y,
|
||||
z = z,
|
||||
minRange = minRange.toFloat(),
|
||||
maxRange = maxRange.toFloat(),
|
||||
)
|
||||
}
|
||||
.addFunction("set_max_attempts") { params -> maxAttempts = params.getDoubleOrNull(0)?.toInt() ?: 4 }
|
||||
.addFunction("set_allow_land") { params -> allowLand = params.getBooleanOrNull(0) ?: true }
|
||||
.addFunction("set_allow_water") { params -> allowWater = params.getBooleanOrNull(0) ?: false }
|
||||
.addFunction("set_allow_air") { params -> allowAir = params.getBooleanOrNull(0) ?: false }
|
||||
.addFunction("set_path_cooldown_ticks") { params -> pathCooldownTicks = params.getDoubleOrNull(0)?.toInt() ?: (20 * 5) }
|
||||
.addFunction("set_wander_speed") { params -> wanderSpeed = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
|
||||
.addFunction("reset") {
|
||||
center = null
|
||||
allowLand = false
|
||||
allowWater = false
|
||||
allowAir = false
|
||||
pathCooldownTicks = 20 * 5
|
||||
wanderSpeed = 0.35F
|
||||
maxAttempts = 4
|
||||
}
|
||||
|
||||
fun isSuitable(position: BlockPos): Boolean {
|
||||
val center = center ?: return true
|
||||
val centerPos = BlockPos(center.x.toInt(), center.y.toInt(), center.z.toInt())
|
||||
val distance = sqrt(centerPos.distSqr(position))
|
||||
val suitable = distance >= center.minRange && distance <= center.maxRange
|
||||
return suitable
|
||||
}
|
||||
}
|
||||
|
||||
+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.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class ActivityChangeTaskConfig : SingleTaskConfig {
|
||||
val activity: Activity = Activity.IDLE
|
||||
val condition: ExpressionLike = "false".asExpressionLike()
|
||||
|
||||
override fun getVariables(entity: LivingEntity): List<MoLangConfigVariable> = emptyList()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return object : OneShot<LivingEntity>() {
|
||||
override fun trigger(serverLevel: ServerLevel, livingEntity: LivingEntity, l: Long): Boolean {
|
||||
runtime.withQueryValue("entity", livingEntity.asMostSpecificMoLangValue())
|
||||
if (condition.resolveBoolean(runtime)) {
|
||||
livingEntity.brain.setActiveActivityIfPossible(activity)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -21,6 +21,7 @@ import net.minecraft.world.entity.Mob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.LookAtTargetSink
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
class LookAtTargetTaskConfig : SingleTaskConfig {
|
||||
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
|
||||
@@ -39,7 +40,8 @@ class LookAtTargetTaskConfig : SingleTaskConfig {
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (!condition.resolveBoolean()) return null
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET)
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)
|
||||
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
|
||||
return WrapperLivingEntityTask(LookAtTargetSink(minDurationTicks.resolveInt(), maxDurationTicks.resolveInt()), Mob::class.java)
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.ast.StringExpression
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
|
||||
/**
|
||||
* A task that modifies the aspects of a Pokémon or NPC based on a memory module.
|
||||
* If the memory is registered, the aspect is added; if it is absent, the aspect is removed.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 6th, 2025
|
||||
*/
|
||||
class MemoryAspectTaskConfig : SingleTaskConfig {
|
||||
var memory: MemoryModuleType<*> = MemoryModuleType.DUMMY
|
||||
var aspect: ExpressionOrEntityVariable = Either.left(StringExpression(StringValue("")))
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(aspect).asVariables()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity && entity !is NPCEntity) {
|
||||
return null
|
||||
} else if (memory == MemoryModuleType.DUMMY) {
|
||||
Cobblemon.LOGGER.error("MemoryAspectTaskConfig: Memory module type is not set for ${entity.name.string}.")
|
||||
return null
|
||||
} else if (aspect.left().orElse(null)?.originalString == "") {
|
||||
Cobblemon.LOGGER.error("MemoryAspectTaskConfig: Aspect is not set for ${entity.name.string}.")
|
||||
return null
|
||||
}
|
||||
|
||||
val aspect = aspect.resolveString()
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(memory to MemoryStatus.REGISTERED),
|
||||
Int.MAX_VALUE,
|
||||
Int.MAX_VALUE
|
||||
) {
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
val hasMemory = !owner.brain.hasMemoryValue(memory)
|
||||
if (owner is PokemonEntity) {
|
||||
if (!hasMemory && aspect in owner.pokemon.forcedAspects) {
|
||||
owner.pokemon.forcedAspects -= aspect
|
||||
} else if (hasMemory && aspect !in owner.pokemon.forcedAspects) {
|
||||
owner.pokemon.forcedAspects += aspect
|
||||
} else return
|
||||
owner.pokemon.updateAspects()
|
||||
} else if (owner is NPCEntity) {
|
||||
if (!hasMemory && aspect in owner.appliedAspects) {
|
||||
owner.appliedAspects -= aspect
|
||||
} else if (hasMemory && aspect !in owner.appliedAspects) {
|
||||
owner.appliedAspects += aspect
|
||||
} else return
|
||||
owner.updateAspects()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToBeeHiveTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class PathToBeeHiveTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val HONEY = "honey"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition,
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!checkCondition(entity, condition)) {
|
||||
return null
|
||||
}
|
||||
return PathToBeeHiveTask.create()
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToFlowerTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class PathToFlowerTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!checkCondition(entity, condition)) {
|
||||
return null
|
||||
}
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.NEARBY_FLOWERS,
|
||||
CobblemonMemories.HIVE_COOLDOWN,
|
||||
CobblemonMemories.HIVE_LOCATION,
|
||||
CobblemonMemories.POLLINATED
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE, CobblemonSensors.NEARBY_FLOWER)
|
||||
return PathToFlowerTask.create()
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class PlaceHoneyInHiveTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val HONEY = "honey"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!checkCondition(entity, condition)) {
|
||||
return null
|
||||
}
|
||||
return PlaceHoneyInHiveTask.create()
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.sendParticlesServer
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
val durationTicks: ExpressionOrEntityVariable = Either.left("30".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!checkCondition(entity, condition)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
CobblemonMemories.NEARBY_FLOWERS to MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.POLLINATED to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
|
||||
),
|
||||
15,
|
||||
45
|
||||
) {
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
return entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWERS).orElse(emptyList()).any {
|
||||
Vec3.atCenterOf(it).distanceTo(entity.position()) <= 1.0
|
||||
}
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
return checkExtraStartConditions(level, entity)
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
super.start(level, entity, gameTime)
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
level.sendParticlesServer(
|
||||
particleType = ParticleTypes.DRIPPING_HONEY,
|
||||
position = owner.eyePosition,
|
||||
particles = 3,
|
||||
offset = Vec3.ZERO,
|
||||
speed = 0.0
|
||||
)
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
val flowerStillThere = canStillUse(level, entity, gameTime)
|
||||
if (flowerStillThere) {
|
||||
entity.brain.setMemory(CobblemonMemories.POLLINATED, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,11 @@ interface TaskConfig {
|
||||
cobblemonResource("eat_grass") to EatGrassTaskConfig::class.java,
|
||||
cobblemonResource("find_air") to FindAirTaskConfig::class.java,
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("path_to_hive") to PathToBeeHiveTaskConfig::class.java,
|
||||
cobblemonResource("place_honey_in_hive") to PlaceHoneyInHiveTaskConfig::class.java,
|
||||
cobblemonResource("path_to_flower") to PathToFlowerTaskConfig::class.java,
|
||||
cobblemonResource("pollinate_flower") to PollinateFlowerTaskConfig::class.java,
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("manage_flight_in_battle") to ManageFlightInBattleTaskConfig::class.java,
|
||||
cobblemonResource("attack_hostile_mobs") to AttackHostileMobsTaskConfig::class.java,
|
||||
cobblemonResource("move_into_fluid") to MoveIntoFluidTaskConfig::class.java,
|
||||
@@ -110,6 +115,8 @@ interface TaskConfig {
|
||||
cobblemonResource("count_followers") to CountFollowersTaskConfig::class.java,
|
||||
cobblemonResource("hate_entity") to HateEntityTaskConfig::class.java,
|
||||
cobblemonResource("target_entity") to TargetEntityTaskConfig::class.java,
|
||||
cobblemonResource("memory_aspect") to MemoryAspectTaskConfig::class.java,
|
||||
cobblemonResource("activity_change") to ActivityChangeTaskConfig::class.java,
|
||||
)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
|
||||
+50
-21
@@ -10,12 +10,14 @@ package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.BlockPos
|
||||
@@ -23,9 +25,11 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.util.HoverRandomPos
|
||||
import net.minecraft.world.entity.ai.util.LandRandomPos
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
|
||||
@@ -101,45 +105,70 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
CobblemonMemories.PATH_COOLDOWN
|
||||
CobblemonMemories.PATH_COOLDOWN,
|
||||
CobblemonMemories.WANDER_CONTROL
|
||||
)
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.registered(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(CobblemonMemories.PATH_COOLDOWN)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown ->
|
||||
it.absent(CobblemonMemories.PATH_COOLDOWN),
|
||||
it.registered(CobblemonMemories.WANDER_CONTROL)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || entity.isUnderWater) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
val wanderControl = it.tryGet(wanderControl).orElse(null) ?: CobblemonWanderControl()
|
||||
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean()
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
pathCooldown.setWithExpiry(true, 40L)
|
||||
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean()
|
||||
|
||||
// val targetVec = getLandTarget(entity) ?: return@Trigger true
|
||||
val targetVec = LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(),
|
||||
verticalRange.resolveInt()
|
||||
) ?: return@Trigger false
|
||||
|
||||
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
|
||||
val minimumHeight = minimumHeight.resolveInt()
|
||||
val maximumHeight = maximumHeight.resolveInt()
|
||||
|
||||
val pos = applyHeightConstraints(
|
||||
pos = BlockPos.containing(targetVec),
|
||||
minimumHeight = minimumHeight,
|
||||
maximumHeight = maximumHeight,
|
||||
world = world
|
||||
)
|
||||
var attempts = 0
|
||||
var pos: BlockPos? = null
|
||||
while (attempts++ < wanderControl.maxAttempts && pos == null) {
|
||||
// val targetVec = getLandTarget(entity) ?: return@Trigger true
|
||||
val targetVec = if (maximumHeight != -1) {
|
||||
HoverRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(),
|
||||
maxOf(verticalRange.resolveInt(), maximumHeight), // In case they fly up pretty high and need to find a way down
|
||||
entity.random.nextFloat() - 0.5,
|
||||
entity.random.nextFloat() - 0.5,
|
||||
Math.PI.toFloat(),
|
||||
maximumHeight,
|
||||
minimumHeight
|
||||
)
|
||||
} else {
|
||||
LandRandomPos.getPos(
|
||||
entity,
|
||||
horizontalRange.resolveInt(),
|
||||
verticalRange.resolveInt()
|
||||
)
|
||||
} ?: continue
|
||||
|
||||
pos = applyHeightConstraints(
|
||||
pos = BlockPos.containing(targetVec),
|
||||
minimumHeight = minimumHeight,
|
||||
maximumHeight = maximumHeight,
|
||||
world = world
|
||||
).takeIf(wanderControl::isSuitable)
|
||||
}
|
||||
|
||||
if (pos == null) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
walkTarget.set(
|
||||
CobblemonWalkTarget(
|
||||
pos = pos,
|
||||
@@ -149,7 +178,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
|
||||
)
|
||||
)
|
||||
lookTarget.erase()
|
||||
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
+30
-7
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WANDER
|
||||
@@ -30,6 +31,7 @@ import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
|
||||
@@ -54,35 +56,56 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
CobblemonMemories.PATH_COOLDOWN
|
||||
CobblemonMemories.PATH_COOLDOWN,
|
||||
CobblemonMemories.WANDER_CONTROL
|
||||
)
|
||||
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.registered(MemoryModuleType.LOOK_TARGET),
|
||||
it.registered(CobblemonMemories.PATH_COOLDOWN)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown ->
|
||||
it.registered(CobblemonMemories.PATH_COOLDOWN),
|
||||
it.registered(CobblemonMemories.WANDER_CONTROL)
|
||||
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControlData ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isInWater) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
val wanderControl = it.tryGet(wanderControlData).orElse(null) ?: CobblemonWanderControl()
|
||||
if (!wanderControl.allowWater) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val wanderChance = runtime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
pathCooldown.setWithExpiry(true, 60L)
|
||||
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
|
||||
|
||||
var pos: BlockPos? = null
|
||||
var target: Vec3? = null
|
||||
var attempts = 0
|
||||
|
||||
while (attempts < wanderControl.maxAttempts && pos == null) {
|
||||
attempts++
|
||||
target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt())
|
||||
?: continue
|
||||
pos = BlockPos.containing(target).takeIf(wanderControl::isSuitable)
|
||||
}
|
||||
|
||||
if (pos == null || target == null) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
val target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(), verticalRange.resolveInt()) ?: return@Trigger false
|
||||
walkTarget.set(
|
||||
CobblemonWalkTarget(
|
||||
pos = BlockPos.containing(target),
|
||||
pos = pos,
|
||||
nodeTypeFilter = { it == PathType.WATER || it == PathType.WATER_BORDER },
|
||||
speedModifier = speedMultiplier.resolveFloat(),
|
||||
completionRange = 1
|
||||
completionRange = 0
|
||||
)
|
||||
)
|
||||
lookTarget.set(BlockPosTracker(target))
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
@@ -77,7 +78,7 @@ open class PokemonBattle(
|
||||
val side2: BattleSide
|
||||
) {
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
var mute = !CobblemonBuildDetails.SNAPSHOT
|
||||
val struct = this.asMoLangValue()
|
||||
val runtime = MoLangRuntime().also { it.environment.query = struct }
|
||||
|
||||
|
||||
+44
-5
@@ -12,9 +12,17 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleFaintPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleReplacePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwapPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleTransformPokemonPacket
|
||||
import java.util.UUID
|
||||
|
||||
abstract class AIBattleActor(
|
||||
@@ -25,8 +33,15 @@ abstract class AIBattleActor(
|
||||
override fun sendUpdate(packet: NetworkPacket<*>) {
|
||||
super.sendUpdate(packet)
|
||||
|
||||
if (packet is BattleMakeChoicePacket) {
|
||||
this.onChoiceRequested()
|
||||
when (packet) {
|
||||
is BattleMakeChoicePacket -> this.onChoiceRequested()
|
||||
is BattlePersistentStatusPacket -> {}
|
||||
is BattleFaintPacket -> {}
|
||||
is BattleSwitchPokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
|
||||
is BattleSwapPokemonPacket -> { /* TODO CONSIDER MULTI SHIFTING */ }
|
||||
is BattleHealthChangePacket -> battleAI.onHealthChange(packet)
|
||||
is BattleTransformPokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
|
||||
is BattleReplacePokemonPacket -> if (!packet.isAlly) { /* TODO this might be redundant; switch on opposing site should be visible during turn */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +50,35 @@ abstract class AIBattleActor(
|
||||
*/
|
||||
open fun onChoiceRequested() {
|
||||
try {
|
||||
setActionResponses(request!!.iterate(this.activePokemon, battleAI::choose))
|
||||
request?.let {
|
||||
setActionResponses(it.iterate(this.activePokemon) { battleMon, moveset, forceSwitch ->
|
||||
battleAI.choose(battleMon, battle, getSide(), moveset, forceSwitch)
|
||||
})
|
||||
pokemonList.forEach { pokemon ->
|
||||
pokemon.willBeSwitchedIn = false
|
||||
}
|
||||
} ?: {
|
||||
val response = mutableListOf<ShowdownActionResponse>()
|
||||
repeat(activePokemon.size) {
|
||||
response.add(PassActionResponse)
|
||||
}
|
||||
setActionResponses(response)
|
||||
LOGGER.warn("AI requested choice, but no request was set. Returning PassActionResponses.")
|
||||
}
|
||||
} catch (exception: IllegalActionChoiceException) {
|
||||
LOGGER.error("AI was unable to choose a move, we're going to need to pass!")
|
||||
LOGGER.error("AI was unable to choose an action, we're going to need to pass!")
|
||||
exception.printStackTrace()
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
request?.let {
|
||||
setActionResponses(it.iterate(this.activePokemon) { _, _ , _->
|
||||
PassActionResponse
|
||||
})
|
||||
} ?: {
|
||||
val response = mutableListOf<ShowdownActionResponse>()
|
||||
repeat(activePokemon.size) {
|
||||
response.add(PassActionResponse)
|
||||
}
|
||||
setActionResponses(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,23 +8,36 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model.ai
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
|
||||
/**
|
||||
* Interface for an actors battle AI
|
||||
* Interface for an actor battle AI
|
||||
*
|
||||
* @since January 16th, 2022
|
||||
* @author Deltric
|
||||
* @author Deltric, Polymeta
|
||||
*/
|
||||
interface BattleAI {
|
||||
/**
|
||||
* Requests that the AI choose an action for the given Pokémon
|
||||
* @param activeBattlePokemon The Pokémon slot that is choosing an action
|
||||
* @param moveset The [ShowdownMoveset] for this slot. This can be null if [forceSwitch] is true. Otherwise it is the available move information from Showdown.
|
||||
* @param forceSwitch Whether or not this is a force switch situation.
|
||||
* @param battle The ongoing [PokemonBattle]
|
||||
* @param aiSide The [BattleSide] the AI is fighting on, useful for allies and opponent determination
|
||||
* @param moveset The [ShowdownMoveset] for this slot. This can be null if [forceSwitch] is true. Otherwise, it is the available move information from Showdown.
|
||||
* @param forceSwitch Whether this is a force switch situation.
|
||||
* @return the action response
|
||||
*/
|
||||
fun choose(activeBattlePokemon: ActiveBattlePokemon, moveset: ShowdownMoveset?, forceSwitch: Boolean): ShowdownActionResponse
|
||||
fun choose(
|
||||
activeBattlePokemon: ActiveBattlePokemon,
|
||||
battle: PokemonBattle,
|
||||
aiSide: BattleSide,
|
||||
moveset: ShowdownMoveset?,
|
||||
forceSwitch: Boolean
|
||||
): ShowdownActionResponse
|
||||
|
||||
fun onHealthChange(packet: BattleHealthChangePacket) {}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
|
||||
fun updatePostDeath() {}
|
||||
fun handleStatus(status: Byte) {}
|
||||
fun spawnShinyParticle(player: Player) {}
|
||||
fun spawnAspectParticle() {}
|
||||
fun positionRider(passenger: Entity, positionUpdater: MoveFunction)
|
||||
fun updateAge(age: Int) {}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
|
||||
val POKEMON_SEEN = CancelableObservable<PokemonSeenEvent>()
|
||||
@JvmField
|
||||
val POKEMON_ASPECTS_CHANGED = EventObservable<PokemonAspectsChangedEvent>()
|
||||
@JvmField
|
||||
|
||||
+13
-1
@@ -8,9 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.berry
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.util.toArrayStruct
|
||||
import com.cobblemon.mod.common.util.worldRegistry
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
@@ -38,4 +43,11 @@ data class BerryHarvestEvent(
|
||||
val state: BlockState,
|
||||
val blockEntity: BerryBlockEntity,
|
||||
val drops: MutableList<ItemStack>
|
||||
) : BerryEvent
|
||||
) : BerryEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"berry" to StringValue(berry.identifier.toString()),
|
||||
"world" to world.worldRegistry.wrapAsHolder(world).asMoLangValue(Registries.DIMENSION),
|
||||
"pos" to pos.toArrayStruct()
|
||||
)
|
||||
}
|
||||
+17
-1
@@ -8,10 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.drops
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
@@ -38,4 +44,14 @@ class LootDroppedEvent(
|
||||
val player: ServerPlayer?,
|
||||
val entity: LivingEntity?,
|
||||
val drops: MutableList<DropEntry>
|
||||
) : Cancelable()
|
||||
) : Cancelable() {
|
||||
val context = mapOf<String, MoValue>(
|
||||
"player" to (player?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"entity" to (entity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO),
|
||||
"drops" to drops.asArrayValue { it.asMoLangValue() }
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+12
-1
@@ -9,10 +9,21 @@
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
* Event that is fired when a bait is consumed.
|
||||
* @param rod The ItemStack of the rod that consumed the bait.
|
||||
*/
|
||||
class BaitConsumedEvent(val rod: ItemStack) : Cancelable()
|
||||
class BaitConsumedEvent(val rod: ItemStack) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
@@ -8,8 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.reactive.CancelableObservable
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
@@ -17,4 +21,12 @@ import net.minecraft.world.item.ItemStack
|
||||
* @param rod The ItemStack of the rod that the bait is set on.
|
||||
* @param bait The ItemStack of the bait that is set on the rod.
|
||||
*/
|
||||
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable()
|
||||
class BaitSetEvent(val rod: ItemStack, val bait: ItemStack) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+12
-1
@@ -9,14 +9,18 @@
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
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
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -35,7 +39,14 @@ interface BobberSpawnPokemonEvent {
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val spawnAction: SpawnAction<*>,
|
||||
val rod: ItemStack
|
||||
) : Cancelable(), BobberSpawnPokemonEvent
|
||||
) : Cancelable(), BobberSpawnPokemonEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that is fired when a Pokemon is modified before it is spawned by a bobber.
|
||||
|
||||
+19
-2
@@ -8,8 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
@@ -28,7 +32,15 @@ interface PokerodCastEvent {
|
||||
override var rod: ItemStack,
|
||||
var bobber: PokeRodFishingBobberEntity,
|
||||
var bait: ItemStack
|
||||
) : PokerodCastEvent, Cancelable()
|
||||
) : PokerodCastEvent, Cancelable() {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that is fired after a fishing rod is cast.
|
||||
@@ -40,5 +52,10 @@ interface PokerodCastEvent {
|
||||
override var rod: ItemStack,
|
||||
var bobber: PokeRodFishingBobberEntity,
|
||||
var bait: ItemStack
|
||||
) : PokerodCastEvent
|
||||
) : PokerodCastEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess()),
|
||||
"bait" to bait.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -9,6 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -16,4 +19,13 @@ import net.minecraft.world.item.ItemStack
|
||||
* Event that is fired when a fishing rod is reeled.
|
||||
* @param rod The ItemStack of the rod that is being reeled.
|
||||
*/
|
||||
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable()
|
||||
class PokerodReelEvent(val player: Player, val rod: ItemStack) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"rod" to rod.asMoLangValue(server()!!.registryAccess())
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+17
-1
@@ -8,6 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokeball
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -27,4 +30,17 @@ class PokemonCatchRateEvent(
|
||||
val pokeBallEntity: EmptyPokeBallEntity,
|
||||
val pokemonEntity: PokemonEntity,
|
||||
var catchRate: Float
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"thrower" to thrower.asMostSpecificMoLangValue(),
|
||||
"poke_ball_entity" to pokeBallEntity.struct,
|
||||
"pokemon_entity" to pokemonEntity.struct,
|
||||
"catch_rate" to DoubleValue(catchRate.toDouble())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
"set_catch_rate" to {
|
||||
catchRate = it.getDouble(0).toFloat()
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
+5
-3
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.events.pokeball
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
@@ -20,8 +21,9 @@ class ThrownPokeballHitEvent(
|
||||
val pokeBall : EmptyPokeBallEntity,
|
||||
val pokemon : PokemonEntity
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokeball" to pokeBall.struct,
|
||||
"pokemon" to pokemon.asMoLangValue()
|
||||
val functions = moLangFunctionMap(
|
||||
"pokeball" to { pokeBall.struct },
|
||||
"pokemon" to { pokemon.asMoLangValue() },
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+6
-6
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.events.pokedex.scanning
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -21,15 +22,14 @@ data class PokemonScannedEvent(val player: ServerPlayer, val scannedPokemonEntit
|
||||
val isOwned: Boolean
|
||||
get() = scannedPokemonEntityData.pokemon.getOwnerUUID() == player.uuid
|
||||
|
||||
val context = mutableMapOf(
|
||||
val context: MutableMap<String, MoValue> = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"pokemon" to scannedPokemonEntityData.pokemon.struct,
|
||||
"disguise" to (scannedPokemonEntityData.disguise?.struct ?: DoubleValue.ZERO),
|
||||
"entity" to {
|
||||
when (scannedEntity) {
|
||||
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
|
||||
else -> DoubleValue.ZERO
|
||||
}
|
||||
"entity" to when (scannedEntity) {
|
||||
is LivingEntity -> scannedEntity.asMostSpecificMoLangValue()
|
||||
else -> DoubleValue.ZERO
|
||||
},
|
||||
"is_owned" to DoubleValue(if (isOwned) 1.0 else 0.0)
|
||||
)
|
||||
}
|
||||
+14
-1
@@ -8,9 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
class CollectEggEvent (
|
||||
@@ -18,4 +22,13 @@ class CollectEggEvent (
|
||||
val maleParent : Pokemon,
|
||||
val femaleParent : Pokemon,
|
||||
val player : ServerPlayer
|
||||
) : Cancelable()
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"male_parent" to maleParent.struct,
|
||||
"female_parent" to femaleParent.struct,
|
||||
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
+19
-5
@@ -14,9 +14,7 @@ import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvSource
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.*
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.ifIsType
|
||||
@@ -56,7 +54,15 @@ interface EvGainedEvent {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to source.pokemon.struct,
|
||||
"stat" to StringValue(stat.identifier.toString()),
|
||||
"amount" to DoubleValue(amount.toDouble())
|
||||
"amount" to DoubleValue(amount.toDouble()),
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleEvSource -> "battle"
|
||||
is ItemEvSource -> "interaction"
|
||||
is SidemodEvSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
)
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc,
|
||||
@@ -78,7 +84,15 @@ interface EvGainedEvent {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to source.pokemon.struct,
|
||||
"stat" to StringValue(stat.identifier.toString()),
|
||||
"amount" to DoubleValue(amount.toDouble())
|
||||
"amount" to DoubleValue(amount.toDouble()),
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleEvSource -> "battle"
|
||||
is ItemEvSource -> "interaction"
|
||||
is SidemodEvSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+43
-3
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.*
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
@@ -24,7 +27,28 @@ class ExperienceGainedPreEvent(
|
||||
val pokemon: Pokemon,
|
||||
val source: ExperienceSource,
|
||||
var experience: Int
|
||||
) : Cancelable()
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc,
|
||||
"set_amount" to {
|
||||
experience = it.getInt(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired when experience has been gained. Information about whether it leveled up or not is
|
||||
@@ -40,4 +64,20 @@ class ExperienceGainedPostEvent(
|
||||
val previousLevel: Int,
|
||||
val currentLevel: Int,
|
||||
val learnedMoves: MutableSet<MoveTemplate>
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"source" to StringValue(
|
||||
when (source) {
|
||||
is BattleExperienceSource -> "battle"
|
||||
is CandyExperienceSource -> "interaction"
|
||||
is CommandExperienceSource -> "command"
|
||||
is SidemodExperienceSource -> source.sidemodId
|
||||
else -> "unknown"
|
||||
}
|
||||
),
|
||||
"experience" to DoubleValue(experience.toDouble()),
|
||||
"previous_level" to DoubleValue(previousLevel.toDouble()),
|
||||
"current_level" to DoubleValue(currentLevel.toDouble())
|
||||
)
|
||||
}
|
||||
+14
@@ -8,7 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
@@ -25,4 +27,16 @@ data class FriendshipUpdatedEvent(
|
||||
set(value) {
|
||||
field = value.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
|
||||
}
|
||||
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"new_friendship" to DoubleValue(newFriendship.toDouble())
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
"set_new_friendship" to {
|
||||
newFriendship = it.getInt(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
+14
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
|
||||
/**
|
||||
* Event that is fired when a player owned Pokémon has its fullness changed
|
||||
@@ -22,4 +24,16 @@ data class FullnessUpdatedEvent(
|
||||
set(value) {
|
||||
field = value.coerceIn(0, pokemon.getMaxFullness())
|
||||
}
|
||||
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"new_fullness" to DoubleValue(newFullness.toDouble())
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
"set_new_fullness" to {
|
||||
newFullness = it.getInt(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
)
|
||||
}
|
||||
+17
-2
@@ -8,15 +8,30 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
interface HatchEggEvent {
|
||||
val egg: PokemonProperties
|
||||
val player: ServerPlayer
|
||||
|
||||
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable()
|
||||
data class Pre(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent
|
||||
data class Post(override var egg : PokemonProperties, override var player: ServerPlayer) : HatchEggEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to (player.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ interface HeldItemEvent {
|
||||
"pokemon" to pokemon.struct,
|
||||
"receiving" to receiving.asMoLangValue(server()!!.registryAccess()),
|
||||
"returning" to returning.asMoLangValue(server()!!.registryAccess()),
|
||||
"decrement" to DoubleValue(if (decrement) 1.0 else 0.0),
|
||||
"decrement" to DoubleValue(if (decrement) 1.0 else 0.0)
|
||||
)
|
||||
}
|
||||
val functions = moLangFunctionMap(
|
||||
@@ -74,7 +74,7 @@ interface HeldItemEvent {
|
||||
"pokemon" to pokemon.struct,
|
||||
"received" to received.asMoLangValue(server()!!.registryAccess()),
|
||||
"returned" to returned.asMoLangValue(server()!!.registryAccess()),
|
||||
"decremented" to DoubleValue(if (decremented) 1.0 else 0.0),
|
||||
"decremented" to DoubleValue(if (decremented) 1.0 else 0.0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -8,10 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.util.*
|
||||
|
||||
data class PokemonAspectsChangedEvent(
|
||||
val ownerId: UUID?,
|
||||
val pokemon: Pokemon,
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"player" to (ownerId?.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
}
|
||||
+14
-1
@@ -8,10 +8,23 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.util.UUID
|
||||
|
||||
data class PokemonSeenEvent(
|
||||
val playerId: UUID,
|
||||
val pokemon: Pokemon
|
||||
)
|
||||
) : Cancelable() {
|
||||
val context = mapOf<String, MoValue>(
|
||||
"player" to playerId.getPlayer()!!.asMoLangValue(),
|
||||
"pokemon" to pokemon.struct
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+17
-2
@@ -8,7 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -25,7 +27,15 @@ data class PokemonSentPreEvent(
|
||||
val pokemon: Pokemon,
|
||||
val level: ServerLevel,
|
||||
val position: Vec3
|
||||
) : Cancelable()
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"position" to StringValue(position.toString())
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired after a [PokemonEntity] is spawned from a player's party and after its animations are finished.
|
||||
@@ -37,4 +47,9 @@ data class PokemonSentPreEvent(
|
||||
data class PokemonSentPostEvent(
|
||||
val pokemon: Pokemon,
|
||||
val pokemonEntity: PokemonEntity
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"pokemon_entity" to pokemonEntity.struct
|
||||
)
|
||||
}
|
||||
+13
-1
@@ -8,7 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -22,4 +25,13 @@ data class ShoulderMountEvent(
|
||||
val player: ServerPlayer,
|
||||
val pokemon: Pokemon,
|
||||
val isLeft: Boolean
|
||||
) : Cancelable()
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to (player.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"pokemon" to pokemon.struct,
|
||||
"is_left" to DoubleValue(if (isLeft) 1.0 else 0.0),
|
||||
)
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+13
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.healing
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.item.HealingSource
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.item.berry.HealingBerryItem
|
||||
import com.cobblemon.mod.common.item.interactive.PotionItem
|
||||
import com.cobblemon.mod.common.item.interactive.PotionType
|
||||
@@ -31,4 +34,14 @@ class PokemonHealedEvent(
|
||||
) : Cancelable() {
|
||||
fun isFullHeal() = amount == -1
|
||||
fun isHealed() = amount > 0
|
||||
|
||||
val context = mutableMapOf(
|
||||
"pokemon" to pokemon.struct,
|
||||
"amount" to DoubleValue(amount),
|
||||
"source" to StringValue(source.toString())
|
||||
)
|
||||
|
||||
val functions = moLangFunctionMap(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
+20
-2
@@ -8,7 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.storage
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCBox
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -42,11 +45,26 @@ interface ChangePCBoxWallpaperEvent {
|
||||
override val player: ServerPlayer,
|
||||
override val box: PCBox,
|
||||
override var wallpaper: ResourceLocation
|
||||
) : ChangePCBoxWallpaperEvent, Cancelable()
|
||||
) : ChangePCBoxWallpaperEvent, Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"box" to StringValue(box.toString()),
|
||||
"wallpaper" to StringValue(wallpaper.toString())
|
||||
)
|
||||
val functions = mapOf(
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
|
||||
class Post(
|
||||
override val player: ServerPlayer,
|
||||
override val box: PCBox,
|
||||
override val wallpaper: ResourceLocation
|
||||
) : ChangePCBoxWallpaperEvent
|
||||
) : ChangePCBoxWallpaperEvent {
|
||||
val context = mutableMapOf(
|
||||
"player" to player.asMoLangValue(),
|
||||
"box" to StringValue(box.toString()),
|
||||
"wallpaper" to StringValue(wallpaper.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,18 @@ 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.ai.CobblemonBlockPosTracker
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
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
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.mark.Marks
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
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
|
||||
@@ -45,11 +51,14 @@ import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
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.experience.SidemodExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.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.TimeRange
|
||||
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
|
||||
@@ -59,10 +68,13 @@ import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
@@ -91,6 +103,7 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
@@ -130,6 +143,12 @@ import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Holds a bunch of useful MoLang trickery that can be used or extended in API
|
||||
@@ -155,6 +174,20 @@ object MoLangFunctions {
|
||||
val replace = params.getString(2)
|
||||
return@Function StringValue(text.replace(search, replace))
|
||||
},
|
||||
"is_included" to java.util.function.Function { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
return@Function DoubleValue(text.contains(search))
|
||||
},
|
||||
"to_lower" to java.util.function.Function { params ->
|
||||
return@Function StringValue(params.getString(0).lowercase())
|
||||
},
|
||||
"to_upper" to java.util.function.Function { params ->
|
||||
return@Function StringValue(params.getString(0).uppercase())
|
||||
},
|
||||
"string_length" to java.util.function.Function { params ->
|
||||
return@Function DoubleValue(params.getString(0).length)
|
||||
},
|
||||
"is_blank" to java.util.function.Function { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
@@ -192,6 +225,48 @@ object MoLangFunctions {
|
||||
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
|
||||
return@Function array
|
||||
},
|
||||
"length" to java.util.function.Function { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
return@Function DoubleValue(array.map.size.toDouble())
|
||||
},
|
||||
"append" to java.util.function.Function { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
val nextIndex = array.map.size
|
||||
array.setDirectly("$nextIndex", value)
|
||||
return@Function array
|
||||
},
|
||||
"insert" to java.util.function.Function { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val size = array.map.size
|
||||
|
||||
// Shift elements at and after index up by 1
|
||||
for (i in (size - 1) downTo index) {
|
||||
val current = array.map[i.toString()]
|
||||
if (current != null) {
|
||||
array.map[(i + 1).toString()] = current
|
||||
}
|
||||
}
|
||||
array.map[index.toString()] = value
|
||||
return@Function array
|
||||
},
|
||||
"delete" to java.util.function.Function { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
if (index in 0 until array.map.size) {
|
||||
array.map.remove(index.toString())
|
||||
// Re-index the array to keep keys sequential in numerical order
|
||||
val newMap = hashMapOf<String, MoValue>()
|
||||
array.map.keys.mapNotNull { it.toIntOrNull() }
|
||||
.sorted()
|
||||
.forEachIndexed { i, k -> newMap[i.toString()] = array.map[k.toString()]!! }
|
||||
array.map.clear()
|
||||
array.map.putAll(newMap)
|
||||
}
|
||||
return@Function array
|
||||
},
|
||||
"run_script" to java.util.function.Function { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
@@ -220,16 +295,44 @@ object MoLangFunctions {
|
||||
DoubleValue(System.currentTimeMillis())
|
||||
},
|
||||
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
|
||||
"date_local_time" to java.util.function.Function { _ ->
|
||||
val time = System.currentTimeMillis()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
|
||||
"date_local_time" to java.util.function.Function { params ->
|
||||
val useTimeZone = params.get<MoValue>(0)
|
||||
var timezone: ZoneId? = null
|
||||
if (useTimeZone is DoubleValue){
|
||||
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
|
||||
} else if (useTimeZone is StringValue){
|
||||
try {
|
||||
timezone = ZoneId.of(useTimeZone.value)
|
||||
} catch (_: Exception) {
|
||||
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
|
||||
return@Function DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
val date: LocalDateTime = if (timezone != null) LocalDateTime.now(timezone) else LocalDateTime.now()
|
||||
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_of" to java.util.function.Function { params ->
|
||||
val time = params.getDouble(0).toLong()
|
||||
val useTimeZone = params.get<MoValue>(1)
|
||||
var timezone: ZoneId? = null
|
||||
if (useTimeZone is DoubleValue){
|
||||
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
|
||||
} else if (useTimeZone is StringValue){
|
||||
try {
|
||||
timezone = ZoneId.of(useTimeZone.value)
|
||||
} catch (_: Exception) {
|
||||
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
|
||||
return@Function DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
|
||||
val instant: Instant = date.toInstant()
|
||||
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(LocalDateTime.ofInstant(instant,
|
||||
timezone ?: ZoneOffset.systemDefault()
|
||||
))
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_is_after" to java.util.function.Function { params ->
|
||||
@@ -250,6 +353,12 @@ object MoLangFunctions {
|
||||
{ worldHolder ->
|
||||
val world = worldHolder.value()
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_time_of_day") { params ->
|
||||
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@put DoubleValue.ZERO
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
}
|
||||
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map.put("time_of_day") {
|
||||
val time = world.dayTime % 24000
|
||||
@@ -418,6 +527,14 @@ object MoLangFunctions {
|
||||
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map.put("main_held_item") { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
|
||||
map.put("off_held_item") { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
|
||||
map.put("inventory") { _ ->
|
||||
val inventory = player.inventory
|
||||
val items = ArrayStruct(hashMapOf())
|
||||
for (i in 0 until inventory.containerSize) {
|
||||
items.setDirectly("$i", inventory.getItem(i).asMoLangValue(player.registryAccess()))
|
||||
}
|
||||
return@put items
|
||||
}
|
||||
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
|
||||
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
@@ -529,11 +646,55 @@ object MoLangFunctions {
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("start_battle") { params ->
|
||||
val opponentValue = params.get<MoValue>(0)
|
||||
val opponent = if (opponentValue is ObjectValue<*>) {
|
||||
opponentValue.obj as ServerPlayer
|
||||
} else {
|
||||
val paramString = opponentValue.asString()
|
||||
val playerUUID = paramString.asUUID
|
||||
if (playerUUID != null) {
|
||||
server()?.playerList?.getPlayer(playerUUID) ?: return@put DoubleValue.ZERO
|
||||
} else {
|
||||
server()?.playerList?.getPlayerByName(paramString) ?: return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
val format = params.getStringOrNull(1)
|
||||
?.let(BattleFormat::fromFormatIdentifier)
|
||||
?: BattleFormat.GEN_9_SINGLES
|
||||
|
||||
val setLevel = params.getIntOrNull(2) ?: -1
|
||||
format.adjustLevel = setLevel
|
||||
|
||||
val rules = params.getStringOrNull(5)
|
||||
?.split(",")
|
||||
?.toSet()
|
||||
?: emptySet()
|
||||
|
||||
val modifiedBattleFormat = BattleFormat.setBattleRules(
|
||||
battleFormat = format,
|
||||
rules = rules
|
||||
)
|
||||
|
||||
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
|
||||
val healFirst = params.getBooleanOrNull(4) ?: false
|
||||
|
||||
val battleStartResult = BattleBuilder.pvp1v1(
|
||||
player1 = player,
|
||||
player2 = opponent,
|
||||
battleFormat = modifiedBattleFormat,
|
||||
cloneParties = cloneParties,
|
||||
healFirst = healFirst
|
||||
)
|
||||
var returnValue: MoValue = DoubleValue.ZERO
|
||||
battleStartResult.ifSuccessful { returnValue = it.struct }
|
||||
return@put returnValue
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ stack, registryAccess ->
|
||||
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
|
||||
val holder = itemRegistry.wrapAsHolder(stack.item)
|
||||
@@ -552,7 +713,7 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val entityFunctions: MutableList<(Entity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
val entityFunctions: MutableList<(Entity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
|
||||
@@ -581,6 +742,34 @@ object MoLangFunctions {
|
||||
return@put owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
|
||||
}
|
||||
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
map.put("add_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
val allowDuplicates = params.getBooleanOrNull(2) ?: false
|
||||
entity.callbacks.addCallback(type, callback, allowDuplicates)
|
||||
}
|
||||
map.put("remove_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
entity.callbacks.removeCallback(type, callback)
|
||||
}
|
||||
map.put("has_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
return@put DoubleValue(callback in (entity.callbacks[type] ?: emptySet()))
|
||||
}
|
||||
map.put("clear_callbacks") { params ->
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
if (type != null) {
|
||||
entity.callbacks[type]?.clear()
|
||||
} else {
|
||||
entity.callbacks.clear()
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
if (entity is PathfinderMob) {
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
@@ -762,7 +951,7 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_living_entity") { DoubleValue.ONE }
|
||||
@@ -770,6 +959,80 @@ object MoLangFunctions {
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
map.put("look_at_position") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val duration = params.getIntOrNull(3) ?: 20
|
||||
val flags = params.params.subList(4, params.params.size).map { it.asString() }
|
||||
val brain = entity.brain
|
||||
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()), duration.toLong())
|
||||
}
|
||||
map.put("get_wander_control_memory") {
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
if (
|
||||
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT)
|
||||
&& entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
|
||||
) {
|
||||
entity.brain.setMemory(CobblemonMemories.WANDER_CONTROL, value)
|
||||
}
|
||||
return@put value.struct
|
||||
}
|
||||
map.put("get_position_memory") { params ->
|
||||
val id = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
|
||||
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
|
||||
val memory = entity.brain.getMemory(memoryType).get()
|
||||
return@put when (memory) {
|
||||
is Vec3i -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x),
|
||||
"y" to DoubleValue(memory.y),
|
||||
"z" to DoubleValue(memory.z)
|
||||
)
|
||||
)
|
||||
is net.minecraft.core.Position -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x()),
|
||||
"y" to DoubleValue(memory.y()),
|
||||
"z" to DoubleValue(memory.z())
|
||||
)
|
||||
)
|
||||
else -> DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_uuid_memory") { params ->
|
||||
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
|
||||
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
|
||||
val expiry = params.getIntOrNull(2) ?: -1
|
||||
if (expiry != -1) {
|
||||
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
|
||||
} else {
|
||||
entity.brain.setMemory(memory, uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("erase_memory") { params ->
|
||||
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
memories.forEach { entity.brain.eraseMemory(it) }
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("has_memory_value") { params ->
|
||||
val memoryTypes = params.params.map {
|
||||
it.asString().asIdentifierDefaultingNamespace()
|
||||
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
return@put DoubleValue(memoryTypes.all { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
|
||||
}
|
||||
map.put("lacks_memory_value") { params ->
|
||||
val memoryTypes = params.params.map {
|
||||
it.asString().asIdentifierDefaultingNamespace()
|
||||
}.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
return@put DoubleValue(memoryTypes.none { entity.brain.checkMemory(it, MemoryStatus.VALUE_PRESENT) })
|
||||
}
|
||||
map.put("can_see") { params ->
|
||||
val target = params.get<MoValue>(0)
|
||||
val range = params.getDoubleOrNull(1) ?: 32.0
|
||||
@@ -1076,6 +1339,23 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val battleMessageFunctions = mutableListOf<(BattleMessage) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ message ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("has_argument_at") { params ->
|
||||
val index = params.getInt(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
return@put DoubleValue(value == message.argumentAt(index))
|
||||
}
|
||||
map.put("has_argument") { params ->
|
||||
val argumentName = params.getString(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
return@put DoubleValue(value == message.optionalArgument(argumentName))
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -1085,9 +1365,37 @@ object MoLangFunctions {
|
||||
map.put("max_hp") { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
map.put("current_hp") { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
map.put("friendship") { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
map.put("max_fullness") { DoubleValue(pokemon.getMaxFullness().toDouble()) }
|
||||
map.put("fullness") { DoubleValue(pokemon.currentFullness.toDouble()) }
|
||||
map.put("lose_fullness") { params ->
|
||||
val amount = params.getDouble(0)
|
||||
pokemon.loseFullness(amount.toInt())
|
||||
}
|
||||
map.put("feed_pokemon") { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val playSound = params.getBooleanOrNull(1) ?: true
|
||||
pokemon.feedPokemon(amount.toInt(), playSound)
|
||||
}
|
||||
map.put("behaviour") { pokemon.form.behaviour.struct }
|
||||
map.put("behavior") { pokemon.form.behaviour.struct } // Inferior
|
||||
map.put("pokeball") { StringValue(pokemon.caughtBall.toString()) }
|
||||
map.put("ability") { StringValue(pokemon.ability.name) }
|
||||
map.put("has_learned") { params ->
|
||||
val moveName = params.getString(0)
|
||||
val move = pokemon.allAccessibleMoves.find { it.name == moveName }
|
||||
if(move != null) {
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("moveset") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.moveSet.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("evs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
@@ -1102,6 +1410,13 @@ object MoLangFunctions {
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("hyper_trained_ivs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.hyperTrainedIVs[stat] ?: -1.0) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("natdex_number") {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
@@ -1190,6 +1505,17 @@ object MoLangFunctions {
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("aspects") {
|
||||
val aspects = pokemon.aspects
|
||||
return@put aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map.put("form_aspects") {
|
||||
val aspects = pokemon.form.aspects
|
||||
return@put aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map.put("form_name") {
|
||||
StringValue(pokemon.form.name.toString())
|
||||
}
|
||||
// Yes, this is the lazy call for a single hardcoded pre-evolution.
|
||||
// Lol. Lmao, even.
|
||||
// TO-DO: Subscribe to [PokemonSpecies.observable].
|
||||
@@ -1214,6 +1540,18 @@ object MoLangFunctions {
|
||||
map.put("remove_held_item") { _ ->
|
||||
pokemon.removeHeldItem()
|
||||
}
|
||||
map.put("add_aspects") { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects += aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map.put("remove_aspects") { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects -= aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map.put("cosmetic_item") { pokemon.cosmeticItem().asMoLangValue(server()!!.registryAccess()) ?: DoubleValue.ZERO }
|
||||
map.put("remove_cosmetic_item") { _ ->
|
||||
pokemon.removeCosmeticItem()
|
||||
}
|
||||
map.put("add_marks") { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
@@ -1255,6 +1593,92 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("add_exp") { params ->
|
||||
val exp = params.getDouble(0).toInt()
|
||||
pokemon.addExperience(SidemodExperienceSource("molang"), exp)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("set_iv") { params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = params.getIntOrNull(1)?.coerceIn(0, IVs.MAX_VALUE) ?: IVs.MAX_VALUE
|
||||
|
||||
if (Stats.PERMANENT.contains(stat)) {
|
||||
pokemon.setIV(stat, value)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_ev") { params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = (params.getIntOrNull(1) ?: 0).coerceIn(0, 255)
|
||||
|
||||
if (Stats.PERMANENT.contains(stat)) {
|
||||
pokemon.setEV(stat, value)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("initialize_moveset") { params ->
|
||||
val preferLatest = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.initializeMoveset(preferLatest)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("validate_moveset") { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.validateMoveset(includeLegacy)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("teach_learnable_moves") { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.teachLearnableMoves(includeLegacy)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("teach_move") { params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
|
||||
val bypass = params.getBooleanOrNull(1) ?: false
|
||||
|
||||
val canLearn = bypass || LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
if (!canLearn) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val alreadyKnows = pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
|
||||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
|
||||
if (alreadyKnows) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
if (pokemon.moveSet.hasSpace()) {
|
||||
pokemon.moveSet.add(moveTemplate.create())
|
||||
} else {
|
||||
pokemon.benchedMoves.add(BenchedMove(moveTemplate, 0))
|
||||
}
|
||||
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map.put("can_learn_move") { params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@put DoubleValue.ZERO
|
||||
val includeLegacy = params.getBooleanOrNull(1) ?: true
|
||||
|
||||
val canLearn = LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
return@put DoubleValue(if (canLearn) 1.0 else 0.0)
|
||||
}
|
||||
map.put("unlearn_move") { params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName)
|
||||
if (moveTemplate != null) {
|
||||
pokemon.unlearnMove(moveTemplate)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -1378,6 +1802,12 @@ object MoLangFunctions {
|
||||
val pokemon = party.get(index) ?: return@put DoubleValue.ZERO
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("set_pokemon") { params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(1).obj
|
||||
party.set(index, pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@mutableListOf map
|
||||
})
|
||||
|
||||
@@ -1390,6 +1820,13 @@ object MoLangFunctions {
|
||||
val pokemon = pc[PCPosition(box, slot)] ?: return@put DoubleValue.ZERO
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("set_pokemon") { params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(2).obj
|
||||
pc.set(PCPosition(box, slot), pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("resize") { params ->
|
||||
val newSize = params.getInt(0)
|
||||
val lockNewSize = params.getBooleanOrNull(1) == true
|
||||
@@ -1552,8 +1989,8 @@ object MoLangFunctions {
|
||||
|
||||
map.put("identifier") { StringValue(species.resourceIdentifier.toString()) }
|
||||
map.put("name") { StringValue(species.name) }
|
||||
map.put("primary_type") { StringValue(species.primaryType.name) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.name ?: "null") }
|
||||
map.put("primary_type") { StringValue(species.primaryType.showdownId) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.showdownId ?: "null") }
|
||||
map.put("experience_group") { StringValue(species.experienceGroup.name) }
|
||||
map.put("height") { DoubleValue(species.height) }
|
||||
map.put("weight") { DoubleValue(species.weight) }
|
||||
@@ -1568,7 +2005,22 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
val dropEntryFunctions: MutableList<(DropEntry) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ dropEntry ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
map.put("percentage") { DoubleValue(dropEntry.percentage.toDouble()) }
|
||||
map.put("quantity") { DoubleValue(dropEntry.quantity.toDouble()) }
|
||||
map.put("max_selectable_times") { DoubleValue(dropEntry.maxSelectableTimes.toDouble()) }
|
||||
map.put("can_drop") { params ->
|
||||
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
|
||||
DoubleValue(if (dropEntry.canDrop(pokemon)) 1.0 else 0.0)
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ props ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
@@ -1626,7 +2078,7 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
val evolutionFunctions: MutableList<(Evolution) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ evolution ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
@@ -1696,6 +2148,15 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun DropEntry.asMoLangValue(): ObjectValue<DropEntry> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(dropEntryFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -1853,6 +2314,14 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addBattleMessageFunctions(battleMessage: BattleMessage): QueryStruct {
|
||||
val addedFunctions = battleMessageFunctions
|
||||
.flatMap { it.invoke(battleMessage).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonEntityFunctions(pokemonEntity: PokemonEntity): QueryStruct {
|
||||
val addedFunctions = pokemonEntityFunctions
|
||||
.flatMap { it.invoke(pokemonEntity).entries }
|
||||
|
||||
@@ -103,7 +103,7 @@ open class Move(
|
||||
"name" to Function { StringValue(name) },
|
||||
"display_name" to Function { StringValue(displayName.string) },
|
||||
"description" to Function { StringValue(description.string) },
|
||||
"type" to Function { StringValue(type.name) },
|
||||
"type" to Function { StringValue(type.showdownId) },
|
||||
"damage_category" to Function { StringValue(damageCategory.name) },
|
||||
"power" to Function { DoubleValue(power) },
|
||||
"accuracy" to Function { DoubleValue(accuracy) },
|
||||
|
||||
@@ -59,7 +59,7 @@ open class MoveTemplate(
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
.addFunction("name") { StringValue(name) }
|
||||
.addFunction("type") { StringValue(elementalType.name) }
|
||||
.addFunction("type") { StringValue(elementalType.showdownId) }
|
||||
.addFunction("damage_category") { StringValue(damageCategory.name) }
|
||||
.addFunction("power") { DoubleValue(power) }
|
||||
.addFunction("target") { StringValue(target.name) }
|
||||
|
||||
+12
-4
@@ -17,6 +17,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
* As opposed to the [GlobalPokedexValueCalculator] this interface is always called on a specific dex definition (e.g. galar, kanto)
|
||||
*/
|
||||
interface PokedexValueCalculator<T> {
|
||||
val outputIsPercentage: Boolean
|
||||
fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): T
|
||||
}
|
||||
|
||||
@@ -25,6 +26,7 @@ interface PokedexValueCalculator<T> {
|
||||
* The calculation of this interface is aimed to consider all available Pokédex entries and is not limited or filtered to a particular dex
|
||||
*/
|
||||
interface GlobalPokedexValueCalculator<T> {
|
||||
val outputIsPercentage: Boolean
|
||||
fun calculate(dexManager: AbstractPokedexManager): T
|
||||
}
|
||||
|
||||
@@ -32,6 +34,7 @@ interface GlobalPokedexValueCalculator<T> {
|
||||
* Calculates the amount of caught Pokémon (globally or per dex)
|
||||
*/
|
||||
object CaughtCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
|
||||
override val outputIsPercentage = false
|
||||
override fun calculate(dexManager: AbstractPokedexManager): Int {
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }
|
||||
}
|
||||
@@ -45,6 +48,7 @@ object CaughtCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<I
|
||||
* Calculates the amount of seen Pokémon (globally or per dex)
|
||||
*/
|
||||
object SeenCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int> {
|
||||
override val outputIsPercentage = false
|
||||
override fun calculate(dexManager: AbstractPokedexManager): Int {
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }
|
||||
}
|
||||
@@ -58,12 +62,14 @@ object SeenCount : PokedexValueCalculator<Int>, GlobalPokedexValueCalculator<Int
|
||||
* Calculates the seen percentage globally or relative to a particular dex
|
||||
*/
|
||||
object SeenPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator<Float> {
|
||||
override val outputIsPercentage = true
|
||||
|
||||
override fun calculate(dexManager: AbstractPokedexManager): Float {
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.count().toFloat() * 100F
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.values.map { it.speciesId }.toSet().size * 100F
|
||||
}
|
||||
|
||||
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Float {
|
||||
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }.toFloat() / DexEntries.entries.count().toFloat() * 100F
|
||||
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) != PokedexEntryProgress.NONE }.toFloat() / dex.entries.size * 100F
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +77,13 @@ object SeenPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator
|
||||
* Calculates the caught percentage globally or relative to a particular dex
|
||||
*/
|
||||
object CaughtPercent : PokedexValueCalculator<Float>, GlobalPokedexValueCalculator<Float> {
|
||||
override val outputIsPercentage = true
|
||||
|
||||
override fun calculate(dexManager: AbstractPokedexManager): Float {
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.count().toFloat().toFloat() * 100F
|
||||
return dexManager.speciesRecords.values.count { it.getKnowledge() == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.values.map { it.speciesId }.toSet().size * 100F
|
||||
}
|
||||
|
||||
override fun calculate(dexManager: AbstractPokedexManager, dex: Map<ResourceLocation, PokedexEntry>): Float {
|
||||
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }.toFloat() / DexEntries.entries.count().toFloat().toFloat() * 100F
|
||||
return dex.entries.map { it.value }.count { dexManager.getKnowledgeForSpecies(it.speciesId) == PokedexEntryProgress.CAUGHT }.toFloat() / dex.entries.size * 100F
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ open class PokemonProperties {
|
||||
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
|
||||
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
|
||||
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
|
||||
props.type = parseStringOfRegistry(keyPairs, listOf("type", "elemental_type")) { ElementalTypes.get(it)?.name }
|
||||
props.type = parseStringOfRegistry(keyPairs, listOf("type", "elemental_type")) { ElementalTypes.get(it)?.showdownId }
|
||||
props.teraType = parseIdentifierOfRegistry(keyPairs, listOf("tera_type", "tera")) { TeraTypes.get(it)?.id?.simplify() }
|
||||
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
|
||||
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
|
||||
@@ -340,6 +340,7 @@ open class PokemonProperties {
|
||||
var originalTrainer: String? = null // Original Trainer by Username or UUID
|
||||
var moves: List<String>? = null
|
||||
var heldItem: String? = null
|
||||
var cosmeticItem: String? = null
|
||||
|
||||
var ivs: IVs? = null
|
||||
var evs: EVs? = null
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
enum class PokemonSortMode(val comparator: Comparator<Pokemon?>, val reverseComparator: Comparator<Pokemon?>) {
|
||||
NAME({ it?.getDisplayName()?.string }),
|
||||
LEVEL({ it?.level }),
|
||||
TYPE({ it?.primaryType?.name }),
|
||||
TYPE({ it?.primaryType?.showdownId }),
|
||||
POKEDEX_NUMBER({ it?.species?.nationalPokedexNumber }),
|
||||
GENDER({ it?.gender });
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
@@ -127,6 +128,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
|
||||
.registerTypeAdapter(RidingStatDefinition::class.java, RidingStatDefinitionAdapter)
|
||||
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
@@ -265,7 +267,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
"H" to "No Ability",
|
||||
"S" to "No Ability"
|
||||
)
|
||||
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
|
||||
val types = (form?.types ?: species.types).map { it.name }
|
||||
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
|
||||
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
|
||||
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
|
||||
|
||||
+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.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import net.minecraft.core.particles.ParticleOptions
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Used by [PokemonClientDelegate.spawnAspectParticle] to spawn associated particles for aspects
|
||||
*/
|
||||
val aspectParticleMap: Map<String, ParticleData> = mapOf(
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1)
|
||||
)
|
||||
|
||||
sealed class ParticleData {
|
||||
data class SnowstormParticle(val particle: ResourceLocation, val chance: Double, val amount: Int, val locators: List<String>): ParticleData()
|
||||
data class MinecraftParticle(val particle: ParticleOptions, val chance: Double, val amount: Int): ParticleData()
|
||||
}
|
||||
@@ -99,6 +99,15 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
|
||||
fun getAllLegalMoves(): Set<MoveTemplate> {
|
||||
return levelUpMoves.values.flatten().toSet() +
|
||||
eggMoves +
|
||||
tutorMoves +
|
||||
tmMoves +
|
||||
formChangeMoves +
|
||||
evolutionMoves
|
||||
}
|
||||
|
||||
// We only sync level up moves atm
|
||||
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|
||||
|
||||
+3
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -88,6 +89,8 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList
|
||||
|
||||
fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
|
||||
|
||||
fun canStopRiding(settings: Settings, state: State, vehicle: PokemonEntity, passenger: Player): Boolean = true
|
||||
|
||||
+6
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -190,6 +191,11 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
|
||||
return behaviour.maxUpStep(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
if (!isActive(settings, state, vehicle)) return RideSoundSettingsList()
|
||||
return behaviour.getRideSounds(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
+15
@@ -16,6 +16,8 @@ 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.sound.RideLoopSound
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -23,6 +25,7 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -469,6 +472,14 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BirdSettings) = BirdState()
|
||||
}
|
||||
|
||||
@@ -491,8 +502,11 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(glidespeedExpr)
|
||||
@@ -504,6 +518,7 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
glidespeedExpr = buffer.readExpression()
|
||||
|
||||
+9
@@ -17,6 +17,7 @@ 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.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -187,6 +188,10 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: GliderSettings) = RidingBehaviourState()
|
||||
}
|
||||
|
||||
@@ -202,14 +207,18 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
var canStrafe: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(canStrafe)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
canStrafe = buffer.readExpression()
|
||||
|
||||
+9
@@ -17,6 +17,7 @@ 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.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -272,6 +273,10 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: HelicopterSettings) = RidingBehaviourState()
|
||||
}
|
||||
|
||||
@@ -291,8 +296,11 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
var speed: Expression = "1.0".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
buffer.writeExpression(verticalVelocity)
|
||||
@@ -300,6 +308,7 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
verticalVelocity = buffer.readExpression()
|
||||
|
||||
+18
@@ -13,6 +13,8 @@ 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.sound.RideLoopSound
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -458,12 +460,24 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: HoverSettings) = HoverState()
|
||||
}
|
||||
|
||||
class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
|
||||
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
|
||||
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
|
||||
var pitchExpr: Expression = "math.pow(math.min(1.0 + 0.2*(q.ride_velocity() / 0.8), 1.2),2)".asExpression()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
@@ -486,8 +500,11 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
@@ -496,6 +513,7 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
|
||||
+19
-1
@@ -21,6 +21,8 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -380,10 +382,22 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: JetSettings) = JetState()
|
||||
|
||||
override fun damageOnCollision(
|
||||
@@ -428,8 +442,11 @@ class JetSettings : RidingBehaviourSettings {
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
@@ -443,6 +460,7 @@ class JetSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
|
||||
+11
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.composite
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.CompositeRidingStrategies
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
@@ -327,6 +328,16 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.getRideSounds(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isActive(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
|
||||
+13
@@ -13,6 +13,7 @@ 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.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -439,6 +440,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: HorseSettings) = HorseState()
|
||||
}
|
||||
|
||||
@@ -467,8 +476,11 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
@@ -477,6 +489,7 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
|
||||
+13
@@ -15,6 +15,7 @@ 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.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -269,6 +270,14 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: VehicleSettings) = VehicleState()
|
||||
}
|
||||
|
||||
@@ -299,8 +308,11 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
var lookYawLimit = "101".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
buffer.writeExpression(jumpVector[1])
|
||||
@@ -314,6 +326,7 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
buffer.readExpression(),
|
||||
|
||||
+12
@@ -14,6 +14,7 @@ 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.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -337,6 +338,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun maxUpStep(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity) = 0f
|
||||
|
||||
override fun createDefaultState(settings: BoatSettings) = BoatState()
|
||||
@@ -345,6 +354,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
class BoatSettings : RidingBehaviourSettings {
|
||||
override val key = BoatBehaviour.KEY
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
private set
|
||||
@@ -354,10 +364,12 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -14,6 +14,7 @@ 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.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -222,6 +223,14 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BurstSettings) = BurstState()
|
||||
}
|
||||
|
||||
@@ -231,12 +240,16 @@ class BurstSettings : RidingBehaviourSettings {
|
||||
var dashSpeed = 1F
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
dashSpeed = buffer.readFloat()
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -15,6 +15,7 @@ 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.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -261,6 +262,14 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: DolphinSettings) = DolphinState()
|
||||
|
||||
}
|
||||
@@ -286,8 +295,11 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
buffer.writeExpression(jumpVector[1])
|
||||
@@ -299,6 +311,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
buffer.readExpression(),
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.sound;
|
||||
|
||||
|
||||
/**
|
||||
* Enum of attenuations models for ride sounds
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since May 11th, 2025
|
||||
*/
|
||||
|
||||
enum class RideAttenuationModel {
|
||||
NONE,
|
||||
LINEAR,
|
||||
EXPONENTIAL
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.sound
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.Mth.lerp
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
* Class to handle looping ride sounds for clients.
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since April 21st, 2025
|
||||
*/
|
||||
class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSettings) :
|
||||
AbstractTickableSoundInstance(SoundEvent.createVariableRangeEvent(soundSettings.soundLocation), SoundSource.NEUTRAL, SoundInstance.createUnseededRandom()) {
|
||||
|
||||
var shouldStop: Boolean = false
|
||||
var fade: Float = 0.0f
|
||||
var shouldMuffle: Boolean = soundSettings.muffleEnabled
|
||||
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
|
||||
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
|
||||
// TODO: revisit below flag once multi passenger support is enabled
|
||||
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
|
||||
|
||||
init {
|
||||
this.looping = true
|
||||
this.delay = 0
|
||||
this.volume = 0.1f
|
||||
this.attenuation = SoundInstance.Attenuation.NONE // This class performs its own attenuation
|
||||
if (this.isPassenger) this.relative = true
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
// Check if the sound should fade in/out or stop
|
||||
this.runFade()
|
||||
|
||||
// If the ride is out of attenuation distance then don't play
|
||||
if (this.outOfRange()) {
|
||||
this.volume = 0.0f
|
||||
return
|
||||
}
|
||||
|
||||
// Perform data driven pitch and volume calculation
|
||||
this.volume = ((soundSettings.volumeExpr.let { ride.runtime.resolveDouble(it) })).toFloat()
|
||||
this.volume *= this.fade
|
||||
this.pitch = ((soundSettings.pitchExpr.let { ride.runtime.resolveDouble(it) })).toFloat()
|
||||
|
||||
if (!this.isPassenger && this.soundSettings.playForNonPassengers) {
|
||||
this.setPos()
|
||||
|
||||
// This should now be done fully through openal. Add to RideAttneuationModel and the
|
||||
// ChannelMixin to expand on the attenuation models.
|
||||
// Attenuate due to distance
|
||||
// this.volume *= attenuation()
|
||||
|
||||
// Calculate influence due to the doppler effect
|
||||
this.pitch *= calcDopplerInfluence().toFloat()
|
||||
|
||||
// Calculate muffling due to occlusion
|
||||
val soundOc = soundOcclusion()
|
||||
if (soundOc != 0.0f) {
|
||||
val newMuffle = lerp(soundOc,1.0f, 0.2f).coerceIn(0.2f,1.0f)
|
||||
// lerp muffle smoothly
|
||||
this.muffleAmount = lerp(0.1f, this.muffleAmount, newMuffle)
|
||||
this.shouldMuffle = true
|
||||
this.volume *= muffleAmount
|
||||
} else {
|
||||
this.shouldMuffle = true
|
||||
this.muffleAmount = lerp(0.1f, this.muffleAmount, 1.0f)
|
||||
this.volume *= muffleAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun runFade() {
|
||||
if (this.shouldStop && this.fade == 0.0f) {
|
||||
this.stop()
|
||||
} else if(this.shouldStop) {
|
||||
this.fade = max(0.0f, this.fade - 0.05f)
|
||||
} else if(!this.shouldStop && this.fade != 1.0f){
|
||||
this.fade = min(1.0f, this.fade + 0.05f)
|
||||
}
|
||||
}
|
||||
|
||||
fun outOfRange(): Boolean {
|
||||
val attenDist = this.sound.attenuationDistance
|
||||
val listenerPosition = Minecraft.getInstance().player?.eyePosition ?: return false
|
||||
return listenerPosition.distanceTo(ride.position()) > attenDist
|
||||
}
|
||||
|
||||
fun calcDopplerInfluence(): Double {
|
||||
val rideVel = ride.getRideVelocity()
|
||||
|
||||
// TODO: Expand this to multiple players
|
||||
val listener = Minecraft.getInstance().player ?: return 1.0
|
||||
val listenerVel = listener.deltaMovement//listener.position().subtract(Vec3(listener.xOld, listener.yOld, listener.zOld))
|
||||
|
||||
// Tweaked constant based off speed of sound in room temp air. Exaggerated for a better effect in minecraft
|
||||
val waveSpeed = 343.0f / 50.0f
|
||||
|
||||
// listener->ride los velocity
|
||||
val lineOfSightToRide = ride.position().subtract(listener.position()).normalize()
|
||||
//val lineOfSightToPlayer = listener.position().subtract(ride.position()).normalize()
|
||||
|
||||
// Speed of the observer towards the sound source
|
||||
val velObserver = listenerVel.dot(lineOfSightToRide)
|
||||
// Speed of the sound source towards the observer
|
||||
val velSrc = rideVel.dot(lineOfSightToRide.scale(-1.0))
|
||||
|
||||
// Use the doppler effect equation to determine the pitch change
|
||||
val pitchFactor = (waveSpeed + velObserver) / (waveSpeed - velSrc)
|
||||
|
||||
return pitchFactor
|
||||
}
|
||||
|
||||
fun soundOcclusion(): Float {
|
||||
val listener = Minecraft.getInstance().player ?: return 0.0f
|
||||
val level = listener.level()
|
||||
val maxAttenDist = this.sound.attenuationDistance
|
||||
|
||||
var totalMuffle = 0.0
|
||||
|
||||
val center = ride.boundingBox.center
|
||||
|
||||
val hit = level.clip(
|
||||
ClipContext(
|
||||
listener.eyePosition,
|
||||
center,
|
||||
ClipContext.Block.COLLIDER,
|
||||
ClipContext.Fluid.NONE,
|
||||
listener
|
||||
)
|
||||
)
|
||||
|
||||
if(hit.type != HitResult.Type.MISS) {
|
||||
//Calculate the distance the hit was away from the listener
|
||||
val hitDist = hit.location.distanceTo(listener.eyePosition)
|
||||
|
||||
//Calculate the influence based on distance of this particular ray hit
|
||||
val hitMuffle = lerp((1 - (hitDist / maxAttenDist)), 0.3, 1.0)
|
||||
totalMuffle += hitMuffle
|
||||
}
|
||||
|
||||
|
||||
return totalMuffle.toFloat()
|
||||
}
|
||||
|
||||
// fun attenuation(): Float {
|
||||
// val listener = Minecraft.getInstance().player ?: return 1.0f
|
||||
// val soundDistance = (ride.position().subtract(listener.eyePosition)).length()
|
||||
// val maxAttenDistance = this.sound.attenuationDistance
|
||||
//
|
||||
// return (1 - (soundDistance / maxAttenDistance).pow(2)).toFloat()
|
||||
// }
|
||||
|
||||
fun setPos() {
|
||||
this.x = ride.x
|
||||
this.y = ride.y
|
||||
this.z = ride.z
|
||||
}
|
||||
|
||||
fun stopSound() {
|
||||
this.shouldStop = true
|
||||
//Minecraft.getInstance().soundManager.stop(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.sound
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Class to control playing all ride sounds
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since April 26th, 2025
|
||||
*/
|
||||
class RideSoundManager(
|
||||
private val ride: PokemonEntity
|
||||
) {
|
||||
private val activeSounds = mutableListOf<RideLoopSound>()
|
||||
private var rideActive: Boolean = false
|
||||
private var currKey: ResourceLocation = cobblemonResource("no_key")
|
||||
|
||||
fun tick() {
|
||||
val currActive = ride.hasControllingPassenger()
|
||||
|
||||
if( !currActive && !this.rideActive) return
|
||||
|
||||
// Remove stopped sounds
|
||||
activeSounds.removeIf { it.isStopped}
|
||||
|
||||
if (!currActive && this.rideActive) {
|
||||
this.rideActive = false
|
||||
this.currKey = cobblemonResource("no_key")
|
||||
this.stop()
|
||||
} else if (currActive && !this.rideActive) {
|
||||
this.rideActive = true
|
||||
this.currKey = getActiveRideKey()
|
||||
this.getBehaviourSounds()
|
||||
} else if (currActive && this.rideActive && this.currKey != getActiveRideKey()) {
|
||||
this.currKey = getActiveRideKey()
|
||||
this.stop()
|
||||
this.getBehaviourSounds()
|
||||
}
|
||||
}
|
||||
|
||||
fun getBehaviourSounds() {
|
||||
val rideSounds = ride.ifRidingAvailableSupply(null) { behaviour, settings, state ->
|
||||
behaviour.getRideSounds(settings, state, ride)
|
||||
}
|
||||
rideSounds?.sounds?.forEach {
|
||||
val loopingRideSound = RideLoopSound(ride,it)
|
||||
activeSounds.add(loopingRideSound)
|
||||
Minecraft.getInstance().soundManager.play(loopingRideSound)
|
||||
}
|
||||
}
|
||||
|
||||
fun getActiveRideKey(): ResourceLocation {
|
||||
val key = ride.ridingBehaviourSettings?.key ?: cobblemonResource("no_key")
|
||||
if (key == CompositeBehaviour.KEY) {
|
||||
return (ride.ridingState as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
|
||||
} else {
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
activeSounds.forEach { it.stopSound() }
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.sound;
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readExpression
|
||||
import com.cobblemon.mod.common.util.writeExpression
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Class to store data for looping sounds played during riding.
|
||||
*
|
||||
* @author Jackowes
|
||||
* @since April 26th, 2025
|
||||
*/
|
||||
data class RideSoundSettings(
|
||||
val soundLocation: ResourceLocation,
|
||||
val volumeExpr: Expression = "1.0".asExpression(),
|
||||
val pitchExpr: Expression = "1.0".asExpression(),
|
||||
val playForNonPassengers: Boolean = false,
|
||||
val muffleEnabled: Boolean = false,
|
||||
val attenuationModel: RideAttenuationModel = RideAttenuationModel.NONE
|
||||
): Encodable {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(soundLocation)
|
||||
buffer.writeExpression(volumeExpr)
|
||||
buffer.writeExpression(pitchExpr)
|
||||
buffer.writeBoolean(playForNonPassengers)
|
||||
buffer.writeBoolean(muffleEnabled)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettings {
|
||||
|
||||
// Try to read if available, fallback to default otherwise
|
||||
val soundLocation = buffer.readResourceLocation()
|
||||
val volumeExpr = buffer.readExpression()
|
||||
val pitchExpr = buffer.readExpression()
|
||||
val playForNonPassengers = buffer.readBoolean()
|
||||
val muffleEnabled = buffer.readBoolean()
|
||||
val attenuationModel = RideAttenuationModel.EXPONENTIAL
|
||||
|
||||
return RideSoundSettings(
|
||||
soundLocation,
|
||||
volumeExpr,
|
||||
pitchExpr,
|
||||
playForNonPassengers,
|
||||
muffleEnabled,
|
||||
attenuationModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RideSoundSettingsList(var sounds: List<RideSoundSettings> = mutableListOf()) {
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeCollection(sounds) { _, sound -> sound.encode(buffer) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RideSoundSettingsList {
|
||||
val list = RideSoundSettingsList()
|
||||
list.sounds = buffer.readList { RideSoundSettings.decode(buffer) }.toMutableList()
|
||||
return list
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-13
@@ -25,7 +25,7 @@ import org.joml.Vector3f
|
||||
*/
|
||||
|
||||
|
||||
class RidingAnimationData
|
||||
class RidingAnimationData(val ride: PokemonEntity)
|
||||
{
|
||||
var prevOrientation: Matrix3f = Matrix3f()
|
||||
var prevRot: Vec3 = Vec3.ZERO
|
||||
@@ -34,29 +34,30 @@ class RidingAnimationData
|
||||
var rotDeltaSpring: Vec3Spring = Vec3Spring()
|
||||
var localVelocitySpring: Vec3Spring = Vec3Spring()
|
||||
|
||||
fun update(entity: PokemonEntity) {
|
||||
fun update() {
|
||||
val activeRide = ride.hasControllingPassenger()
|
||||
|
||||
if (!activeRide) return
|
||||
|
||||
var currRot = Vec3.ZERO
|
||||
var angDelta = Vec3.ZERO
|
||||
|
||||
val stiffness = 90.0
|
||||
val damping = 18.0
|
||||
// val stiffness = 16.0
|
||||
// val damping = 8.0
|
||||
|
||||
// Update velocity first
|
||||
val currentVelocity = Vec3(
|
||||
entity.x - entity.xOld,
|
||||
entity.y - entity.yOld,
|
||||
entity.z - entity.zOld
|
||||
ride.x - ride.xOld,
|
||||
ride.y - ride.yOld,
|
||||
ride.z - ride.zOld
|
||||
)
|
||||
|
||||
val r = entity.ifRidingAvailableSupply(false) { behaviour, settings, state ->
|
||||
behaviour.shouldRoll(settings, state, entity);
|
||||
val r = ride.ifRidingAvailableSupply(false) { behaviour, settings, state ->
|
||||
behaviour.shouldRoll(settings, state, ride);
|
||||
}
|
||||
if( r ) {
|
||||
//TODO: should this return?
|
||||
val controller = (entity.firstPassenger as? OrientationControllable)?.orientationController ?: return
|
||||
val controller = (ride.firstPassenger as? OrientationControllable)?.orientationController ?: return
|
||||
val orientation = controller.orientation ?: Matrix3f()
|
||||
|
||||
val currPitch = controller.pitch
|
||||
@@ -105,8 +106,8 @@ class RidingAnimationData
|
||||
// Update previous values
|
||||
prevOrientation.set(currOrientation)
|
||||
} else {
|
||||
val currPitch = entity.xRot
|
||||
val currYaw = entity.yRot
|
||||
val currPitch = ride.xRot
|
||||
val currYaw = ride.yRot
|
||||
val currRoll = 0.0f
|
||||
currRot = Vec3(currPitch.toDouble(), currYaw.toDouble(), currRoll.toDouble())
|
||||
|
||||
@@ -120,7 +121,7 @@ class RidingAnimationData
|
||||
/******************************************************
|
||||
* Calculate ride local velocity
|
||||
*****************************************************/
|
||||
val yaw360 = -1.0 * (entity.yRot + 360.0f) % 360.0f
|
||||
val yaw360 = -1.0 * (ride.yRot + 360.0f) % 360.0f
|
||||
val orientationNonRollable = Matrix3f().rotateY(yaw360.toRadians())
|
||||
|
||||
//Convert to Vector3f
|
||||
|
||||
@@ -41,11 +41,15 @@ class EmitterSpace(
|
||||
|
||||
if (scaling == ScalingMode.ENTITY) {
|
||||
locatorMatrix.matrix.getScale(scale)
|
||||
|
||||
// If any value is zero we get NaNs, so default to a large enough value to be safe
|
||||
scale.x.coerceAtLeast(.01f)
|
||||
scale.y.coerceAtLeast(.01f)
|
||||
scale.z.coerceAtLeast(.01f)
|
||||
}
|
||||
|
||||
val particleScale = Vector3f(scale.x, scale.y, scale.z)
|
||||
//Presumably we will want to make the initial rotation configurable instead of always using root
|
||||
val particleRawMatrix = Matrix4f().scale(particleScale).rotate(rootRotation)
|
||||
val particleRawMatrix = Matrix4f().scale(scale).rotate(rootRotation)
|
||||
return MatrixWrapper().updateMatrix(particleRawMatrix).updatePosition(locatorMatrix.getOrigin())
|
||||
}
|
||||
|
||||
|
||||
+62
-10
@@ -53,9 +53,12 @@ interface ParticleEmitterShape : CodecMapped {
|
||||
}
|
||||
|
||||
val type: ParticleEmitterShapeType
|
||||
val defaultAttachedOptions: MutableMap<AttachedType, Boolean>
|
||||
get() = mutableMapOf(AttachedType.POSITION to true, AttachedType.ROTATION to false, AttachedType.SCALE to false)
|
||||
|
||||
fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3
|
||||
fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3
|
||||
fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> = defaultAttachedOptions // Default Values
|
||||
}
|
||||
|
||||
enum class ParticleEmitterShapeType {
|
||||
@@ -66,6 +69,12 @@ enum class ParticleEmitterShapeType {
|
||||
ENTITY_BOUNDING_BOX
|
||||
}
|
||||
|
||||
enum class AttachedType {
|
||||
ROTATION,
|
||||
SCALE,
|
||||
POSITION
|
||||
}
|
||||
|
||||
class SphereParticleEmitterShape(
|
||||
var offset: Triple<Expression, Expression, Expression> = Triple(
|
||||
NumberExpression(0.0),
|
||||
@@ -73,7 +82,8 @@ class SphereParticleEmitterShape(
|
||||
NumberExpression(0.0)
|
||||
),
|
||||
var radius: Expression = NumberExpression(0.0),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
) : ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<SphereParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -114,9 +124,7 @@ class SphereParticleEmitterShape(
|
||||
buffer.writeBoolean(surfaceOnly)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
return runtime.resolveVec3d(offset)
|
||||
}
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
val radius = runtime.resolveDouble(radius) * if (surfaceOnly) 1.0 else Random.Default.nextDouble()
|
||||
@@ -124,6 +132,16 @@ class SphereParticleEmitterShape(
|
||||
val psi = Math.PI * 2 * Random.Default.nextDouble()
|
||||
return getCenter(runtime, entity).add(convertSphericalToCartesian(radius = radius, theta = theta, psi = psi))
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
class PointParticleEmitterShape(
|
||||
@@ -131,7 +149,8 @@ class PointParticleEmitterShape(
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0)
|
||||
)
|
||||
),
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
): ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<PointParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -145,11 +164,21 @@ class PointParticleEmitterShape(
|
||||
}
|
||||
|
||||
override val type = ParticleEmitterShapeType.POINT
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
offset = Triple(
|
||||
@@ -177,7 +206,8 @@ class BoxParticleEmitterShape(
|
||||
NumberExpression(1.0),
|
||||
NumberExpression(1.0)
|
||||
),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
) : ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<BoxParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -226,7 +256,7 @@ class BoxParticleEmitterShape(
|
||||
buffer.writeBoolean(surfaceOnly)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
val center = getCenter(runtime, entity)
|
||||
@@ -274,6 +304,16 @@ class BoxParticleEmitterShape(
|
||||
|
||||
return center.add(disposition)
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
class DiscParticleEmitterShape(
|
||||
@@ -288,7 +328,8 @@ class DiscParticleEmitterShape(
|
||||
NumberExpression(1.0),
|
||||
NumberExpression(0.0)
|
||||
),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
): ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<DiscParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -333,7 +374,18 @@ class DiscParticleEmitterShape(
|
||||
return center.add(displacement)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
offset = Triple(
|
||||
|
||||
@@ -100,7 +100,7 @@ class ParticleEvent(
|
||||
|
||||
val locatorMatrix = when (effect.type) {
|
||||
EventParticleOptions.EventParticleType.EMITTER -> particleMatrix
|
||||
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.locatorSpaceMatrix
|
||||
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.attachedMatrix
|
||||
EventParticleOptions.EventParticleType.PARTICLE,
|
||||
EventParticleOptions.EventParticleType.PARTICLE_WITH_VELOCITY -> particleMatrix
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class ParticleEvent(
|
||||
val newStorm = ParticleStorm(
|
||||
effect = bedrockParticleOptions,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
locatorSpaceMatrix = locatorMatrix,
|
||||
attachedMatrix = locatorMatrix,
|
||||
world = storm.world,
|
||||
sourceVelocity = sourceVelocity,
|
||||
sourceAlive = storm.sourceAlive,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import java.time.LocalDate
|
||||
|
||||
open class IrlDateRange (
|
||||
val startDate: LocalDate,
|
||||
val stopDate: LocalDate? = null
|
||||
) {
|
||||
operator fun contains(date: LocalDate): Boolean {
|
||||
return if(stopDate != null) {
|
||||
(startDate.isEqual(date) || startDate.isBefore(date)) && (stopDate.isAfter(date) || stopDate.isEqual(date))
|
||||
} else {
|
||||
startDate.isEqual(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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 java.time.LocalTime
|
||||
|
||||
open class IrlTimeRange(
|
||||
val startTime: LocalTime,
|
||||
val stopTime: LocalTime
|
||||
) {
|
||||
operator fun contains(time: LocalTime): Boolean {
|
||||
return (time.isAfter(startTime) || time == startTime) && time.isBefore(stopTime)
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,8 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(IrlTimeRange::class.java, IrlTimeRangeAdapter)
|
||||
.registerTypeAdapter(IrlDateRange::class.java, IrlDateRangeAdapter)
|
||||
.create()
|
||||
|
||||
var deserializingConditionClass: Class<out SpawningCondition<*>>? = null
|
||||
|
||||
+15
@@ -8,7 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.IrlDateRange
|
||||
import com.cobblemon.mod.common.api.spawning.IrlTimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -21,6 +24,8 @@ import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.levelgen.WorldgenRandom
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
/**
|
||||
* The root of spawning conditions that can be applied to a spawnable position. What type
|
||||
@@ -58,6 +63,8 @@ abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
var timeRange: TimeRange? = null
|
||||
var structures: MutableList<Either<ResourceLocation, TagKey<Structure>>>? = null
|
||||
var isSlimeChunk: Boolean? = null
|
||||
var irlTimeRange: IrlTimeRange? = null
|
||||
var irlDateRange: IrlDateRange? = null
|
||||
var markers: MutableList<String>? = null
|
||||
|
||||
@Transient
|
||||
@@ -129,6 +136,10 @@ abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
|
||||
val random = Random(seed)
|
||||
return random.nextInt(10) == 0*/
|
||||
} else if (irlTimeRange != null && !irlTimeRange!!.contains(LocalTime.now(Cobblemon.config.timeZoneId))){
|
||||
return false
|
||||
} else if (irlDateRange != null && !irlDateRange!!.contains(LocalDate.now(Cobblemon.config.timeZoneId))){
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -160,6 +171,10 @@ abstract class SpawningCondition<T : SpawnablePosition> {
|
||||
return false
|
||||
if (structures != null && structures!!.any { it == null })
|
||||
return false
|
||||
if(irlTimeRange != null && irlTimeRange!!.stopTime.isBefore(irlTimeRange!!.startTime))
|
||||
return false
|
||||
if(irlDateRange != null && irlDateRange!!.stopDate != null && irlDateRange!!.stopDate!!.isBefore(irlDateRange!!.startDate))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
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())
|
||||
species.secondaryType?.let { listOf(species.primaryType.showdownId, it.showdownId) }
|
||||
?: listOf(species.primaryType.showdownId)
|
||||
)
|
||||
|
||||
if (height == -1) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user