Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41cf6c9efb | ||
|
|
15b73cedb6 | ||
|
|
56e8ed5a92 | ||
|
|
99cfe4f0c3 | ||
|
|
557ace977a | ||
|
|
1ba55b9f90 | ||
|
|
e1960c2b0b | ||
|
|
4bee9412ce | ||
|
|
264c7659f6 | ||
|
|
0c320905e1 | ||
|
|
b92148d0f9 | ||
|
|
6f1ac0c10b | ||
|
|
692d42e626 | ||
|
|
f73e93a5a8 | ||
|
|
21ae3dc9e8 |
No files matched your search
+12
-47
@@ -4,6 +4,8 @@
|
||||
### Additions
|
||||
- Added riding statistics: `Distance by Pokémon on Land`, `Distance by Pokémon in Air` and `Distance by Pokémon in Liquid`.
|
||||
- Added the Partner Mark, which party Pokémon have a very small chance to earn as the player walks, provided they have enough friendship.
|
||||
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
|
||||
- Added brewing recipe for Throat Spray.
|
||||
- Added new effects
|
||||
- Mental Restoration: Reduces "Time Since Last Rest" statistic, which controls Insomnia, by half a minute per second per effect level
|
||||
- Negative Effect Cleanse: Removes all negative effects
|
||||
@@ -13,73 +15,36 @@
|
||||
- White Herb - Negative Effect Cleanse
|
||||
- Milk - Effect Cleanse
|
||||
- Moomoo Milk - Effect Cleanse
|
||||
- Added `label` as an alternate key to the `tag` property.
|
||||
- Added Saccharine wood furniture for Adorn.
|
||||
- Added block models for existing items: Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Ether, Elixir, Max Ether, Max Elixir, X Accuracy, X Attack, X Defence, X Special Attack, X Special Defence, X Speed, Dire Hit, Guard Spec, Weakness Policy, Blunder Policy, Cleanse Tag, and Spell Tag.
|
||||
- Added decorative sign blocks, Plaques.
|
||||
|
||||
### Changes
|
||||
- Ride sounds have been separated into stereo for passengers and mono for other players. Riding will now sound more spacious.
|
||||
- Added a new config option, `Enable In-Flight Dismounting` (default: off), which lets you dismount while riding a Pokémon in the air.
|
||||
- Battle AI now uses a smarter threshold for switching, reducing unnecessary switches.
|
||||
- AI will always use the most damaging move when at low HP and when it is not switching, improving endgame decision-making.
|
||||
- Added support for optional message variants in battle activate instructions for more context-specific battle text.
|
||||
- Example: `this.add('-activate', pokemon, 'ability: example', '[msg]message1');` will parse to lang key `cobblemon.battle.activate.example.message1`
|
||||
|
||||
### Fixes
|
||||
- Fixed an issue in the OmniPathNodeMaker/Navigation that prevented Combee from pathing into and out of Saccharine Leaves.
|
||||
- Fixed orientation not getting reset after crashing while flying and getting back on the Pokémon.
|
||||
- Fixed Furfrou not being trimmable on NeoForge.
|
||||
- Fixed shiny Pokémon not respecting the silent flag on spawn.
|
||||
- Fixed honey from Saccharine Leaves being harvestable at less than the max age.
|
||||
- Items given from interacting with Saccharine Leaves are placed in the active hotbar slot if possible.
|
||||
- Fix all Pokémon being saved to chunks and never despawning.
|
||||
- Fixed possible error on world generation when Combee are placed in naturally generated bee nests.
|
||||
- Fixed Poké Snacks crashing if there was no available spawn.
|
||||
- Fixed Pokémon spawn influences not being specific to regional forms.
|
||||
- Fixed crashing when viewing another player in spectator mode.
|
||||
- Fixed missing apricorn textures for Adorn blocks.
|
||||
- Fixed crashing when riding due to malformed json file instead of resetting it.
|
||||
- Fixed mochi items consuming two items at a time.
|
||||
- Fixed PokeSnacks crashing if there was no available spawn.
|
||||
- Fixed Pokémon spawn influences not being specific to regional forms
|
||||
- Fixed crashing when viewing another player in spectator mode
|
||||
- Fixed missing apricorn textures for Adorn blocks
|
||||
- Fixed crashing when riding due to malformed json file, instead resetting it
|
||||
- Fixed mochi items consuming two items at a time
|
||||
- Fix item interaction sometimes not working properly when playing on servers.
|
||||
- Fixed Soothe Bell not being properly tagged as a held item.
|
||||
- Fixed Mint Leaves not being usable for filling the Resurrection Machine.
|
||||
- Added Saccharine Boats and Saccharine Boat with Chests to relevant boat tags.
|
||||
- Fixed the `run_script` Molang function to not fail if the environment's context is null.
|
||||
- Fixed battle item duplication issue.
|
||||
- Fixed the movesets of certain Pokémon being incorrectly sanitized upon reload.
|
||||
- Fixed the ordering of Aprijuice's riding stat boosts to always have the same order.
|
||||
- Fixed the Molang functions `date_local_time`, `date_of`, `date_is_after` to use the correct date format.
|
||||
- Fixed the bottom half of pasture blocks having an incorrect hit-box when facing north or south.
|
||||
- Fixed Galarian Mr. Mime incorrectly being rideable.
|
||||
- Fixed gimmicks not being usable in double / triple battles.
|
||||
- Fixed gimmick buttons showing when already used in the same turn in double / triple battles.
|
||||
- Fixed Apricorn block model rotation for counters/sinks/cupboards/drawers to match that of most recent version of Adorn.
|
||||
- Fixed Fresh Start Mochi EV reset not syncing health changes properly.
|
||||
- Fixed requirements such as `biomeCondition` crashing the game when used to define Pokémon interactions.
|
||||
- Fixed Pokémon interactions not playing sound effects when the volume was not specified.
|
||||
- Fixed Pokémon interaction cooldowns taking longer than intended when the Pokémon was in the player’s party.
|
||||
- Fixed Pokémon interactions occasionally crashing the game when spamming an interaction with a 0 cooldown.
|
||||
- Fixed Pokémon fullness decreasing more than intended when the Pokémon was pastured.
|
||||
- Fixed crashing when sending a Pokémon out after editing its form.
|
||||
- Fixed Super Potion recipes incorrectly using Hondew Berry instead of Aguav Berry.
|
||||
- Fixed `/calculateseatpositions` expecting a locator format that even we don't use. It needs an underscore after "seat".
|
||||
- Fixed invulnerability-bypassed damage being resisted by invulnerable NPC entities.
|
||||
- Fixed selected leading Pokémon in battle not being used when levels are raised.
|
||||
- Fixed killer not being set early enough on a wild wild Pokémon entity from WinInstruction.
|
||||
|
||||
### Developer
|
||||
- Changed the `owner` parameter in the `OwnerQueryRequirement` interface from `ServerPlayer` to `Player`. This method is now also called on the client to verify whether a Pokémon interaction succeeded, so make sure to update your implementations to handle both server and client contexts.
|
||||
- Added `pnx` to the `BattleFaintedEvent` and `FormeChangeEvent`.
|
||||
- Added register methods for custom instructions to `ShowdownInterpreter`.
|
||||
- Changed callback operations in `BattlePokemon` to allow multiple callbacks and is now mutable.
|
||||
- Changed the `owner` parameter in the `OwnerQueryRequirement` interface from `ServerPlayer` to `Player`. This method is now also called on the client to verify whether a Pokémon interaction succeeded, so make sure to update your implementations to handle both server and client contexts.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Ride sounds can now be set as exclusive to passengers.
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting.
|
||||
- Exception handling has been added to `run_molang`, resolving some crashes caused by malformed MoLang expressions.
|
||||
- Ride sounds can now be set as exclusive to passengers
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting
|
||||
|
||||
### Particles
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Updated translations for:
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface SoundBufferLibraryDuck {
|
||||
|
||||
CompletableFuture<AudioStream> getStreamWithLoopPoints(ResourceLocation audioLocation, int loopStart, int loopEnd);
|
||||
|
||||
CompletableFuture<AudioStream> getStreamWithIntro(ResourceLocation introLocation, ResourceLocation endLocation, boolean isLoop);
|
||||
|
||||
CompletableFuture<AudioStream> getStreamWithIntro(ResourceLocation introLocation, ResourceLocation endLocation, int loopStart, int loopEnd);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface SoundDuck {
|
||||
|
||||
int getLoopStart();
|
||||
|
||||
int getLoopEnd();
|
||||
|
||||
ResourceLocation getIntro();
|
||||
|
||||
void setLoopStart(int startInSamples);
|
||||
|
||||
void setLoopEnd(int endInSamples);
|
||||
|
||||
void setIntro(ResourceLocation intro);
|
||||
|
||||
boolean hasLoopPoints();
|
||||
|
||||
boolean hasIntro();
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.bridges;
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
|
||||
|
||||
@@ -11,9 +11,7 @@ package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories;
|
||||
import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.world.feature.SaccharineTreeFeature;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
@@ -44,21 +42,7 @@ public abstract class BeeOccupantMixin {
|
||||
if (cir.getReturnValue() == null) {
|
||||
final BeehiveBlockEntity.Occupant occupant = (BeehiveBlockEntity.Occupant) (Object) this;
|
||||
CompoundTag compoundTag = occupant.entityData().copyTag();
|
||||
String id = compoundTag.getString("id");
|
||||
Entity entity = null;
|
||||
if (id.equals(SaccharineTreeFeature.Companion.getPOKEMON_PLACEHOLD_ID())) {
|
||||
// Special case for Pokemon stuffed into a beenest upon world gen.
|
||||
// For technical reasons we cannot create them as full entities during world gen.
|
||||
// So we look for this special id tag and complete the entity when it attempts to exit a hive.
|
||||
if (compoundTag.contains("pokemonProperties")) {
|
||||
String properties = compoundTag.getString("pokemonProperties");
|
||||
PokemonProperties pokemon = PokemonProperties.Companion.parse(properties);
|
||||
entity = pokemon.createEntity(level);
|
||||
}
|
||||
} else {
|
||||
entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
|
||||
}
|
||||
|
||||
Entity entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
|
||||
if (entity instanceof PokemonEntity pokemonEntity) {
|
||||
var state = level.getBlockState(pos);
|
||||
var newPos = Vec3.ZERO;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.sounds.Music;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
public class MinecraftMixin {
|
||||
|
||||
/** Used by MusicManager to dispatch the music to play. */
|
||||
@WrapOperation(
|
||||
method = "getSituationalMusic",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/biome/Biome;getBackgroundMusic()Ljava/util/Optional;")
|
||||
)
|
||||
public Optional<Music> getBackgroundMusic(Biome instance, Operation<Optional<Music>> original) {
|
||||
return original.call(instance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.client.sound.streams.CompoundAudioStream;
|
||||
import com.cobblemon.mod.common.client.sound.streams.LoopPointAudioStream;
|
||||
import com.cobblemon.mod.common.duck.SoundBufferLibraryDuck;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
import net.minecraft.client.sounds.JOrbisAudioStream;
|
||||
import net.minecraft.client.sounds.LoopingAudioStream;
|
||||
import net.minecraft.client.sounds.SoundBufferLibrary;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
@Mixin(SoundBufferLibrary.class)
|
||||
public class SoundBufferLibraryMixin implements SoundBufferLibraryDuck {
|
||||
|
||||
@Shadow
|
||||
private ResourceProvider resourceManager;
|
||||
|
||||
/**
|
||||
* Creates a LoopPointAudioStream with defined loop points.
|
||||
*
|
||||
* @param audioLocation The audio file to open for stream.
|
||||
* @param loopStart The starting point of the stream's loop.
|
||||
* @param loopEnd The ending point of the stream's loop.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<AudioStream> getStreamWithLoopPoints(ResourceLocation audioLocation, int loopStart, int loopEnd) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
InputStream introStream = this.resourceManager.open(audioLocation);
|
||||
return new LoopPointAudioStream(JOrbisAudioStream::new, introStream, loopStart, loopEnd);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, Util.nonCriticalIoPool());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CompoundAudioStream from a JOrbisAudioStream intro and LoopPointAudioStream end.
|
||||
*
|
||||
* @param introLocation The audio file to open for the intro stream.
|
||||
* @param endLocation The audio file to open for the end stream.
|
||||
* @param loopStart The starting point of the ending LoopPointAudioStream's loop.
|
||||
* @param loopEnd The ending point of the ending LoopPointAudioStream's loop.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<AudioStream> getStreamWithIntro(ResourceLocation introLocation, ResourceLocation endLocation, int loopStart, int loopEnd) {
|
||||
return createCompoundStream(
|
||||
introLocation,
|
||||
endLocation,
|
||||
(inputStream) -> new LoopPointAudioStream(JOrbisAudioStream::new, inputStream, loopStart, loopEnd)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CompoundAudioStream from a JOrbisAudioStream intro and a JOrbisAudioStream or LoopingAudioStream end.
|
||||
*
|
||||
* @param introLocation The audio file to open for the intro stream.
|
||||
* @param endLocation The audio file to open for the end stream.
|
||||
* @param isLoop Whether the end stream loops (LoopingAudioStream) or not (JOrbisAudioStream).
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<AudioStream> getStreamWithIntro(ResourceLocation introLocation, ResourceLocation endLocation, boolean isLoop) {
|
||||
return createCompoundStream(
|
||||
introLocation,
|
||||
endLocation,
|
||||
!isLoop ? JOrbisAudioStream::new : (inputStream) -> new LoopingAudioStream(JOrbisAudioStream::new, inputStream)
|
||||
);
|
||||
}
|
||||
|
||||
private CompletableFuture<AudioStream> createCompoundStream(ResourceLocation introLocation, ResourceLocation endLocation, LoopingAudioStream.AudioStreamProvider endProvider) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
InputStream introInputStream = this.resourceManager.open(introLocation);
|
||||
InputStream endInputStream = this.resourceManager.open(endLocation);
|
||||
|
||||
AudioStream introStream = new JOrbisAudioStream(introInputStream);
|
||||
AudioStream endStream = endProvider.create(endInputStream);
|
||||
return new CompoundAudioStream(introStream, endStream);
|
||||
} catch (Exception e) {
|
||||
Cobblemon.LOGGER.error(e.getMessage());
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, Util.nonCriticalIoPool());
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
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.SoundBufferLibraryDuck;
|
||||
import com.cobblemon.mod.common.duck.SoundDuck;
|
||||
import com.cobblemon.mod.common.duck.SoundEngineDuck;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
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.AudioStream;
|
||||
import net.minecraft.client.sounds.ChannelAccess;
|
||||
import net.minecraft.client.sounds.SoundBufferLibrary;
|
||||
import net.minecraft.client.sounds.SoundEngine;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
@@ -30,12 +36,10 @@ 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;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(SoundEngine.class)
|
||||
public abstract class SoundEngineMixin implements SoundEngineDuck {
|
||||
@@ -120,4 +124,23 @@ public abstract class SoundEngineMixin implements SoundEngineDuck {
|
||||
cb.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatch custom stream for sounds that have an intro. WrapOperation because the Fabric API has its own Redirect that we don't want to use. */
|
||||
@WrapOperation(method = "play", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/sounds/SoundBufferLibrary;getStream(Lnet/minecraft/resources/ResourceLocation;Z)Ljava/util/concurrent/CompletableFuture;"))
|
||||
public CompletableFuture<AudioStream> getStream(SoundBufferLibrary bufferLibrary, ResourceLocation id, boolean looping, Operation<CompletableFuture<AudioStream>> original, SoundInstance soundInstance) {
|
||||
SoundDuck soundDuck = (SoundDuck)soundInstance.getSound();
|
||||
SoundBufferLibraryDuck libraryDuck = (SoundBufferLibraryDuck)bufferLibrary;
|
||||
if (soundDuck.hasIntro() && soundDuck.hasLoopPoints() && looping) {
|
||||
return libraryDuck.getStreamWithIntro(soundDuck.getIntro(), id, soundDuck.getLoopStart(), soundDuck.getLoopEnd());
|
||||
}
|
||||
else if (soundDuck.hasIntro()) {
|
||||
return libraryDuck.getStreamWithIntro(soundDuck.getIntro(), id, looping);
|
||||
}
|
||||
else if (soundDuck.hasLoopPoints() && looping) {
|
||||
return libraryDuck.getStreamWithLoopPoints(id, soundDuck.getLoopStart(), soundDuck.getLoopEnd());
|
||||
}
|
||||
else {
|
||||
return original.call(bufferLibrary, id, looping);
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.SoundDuck;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import net.minecraft.client.resources.sounds.Sound;
|
||||
import net.minecraft.client.resources.sounds.SoundEventRegistrationSerializer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(SoundEventRegistrationSerializer.class)
|
||||
public class SoundEventRegistrationSerializerMixin {
|
||||
|
||||
/** Injects custom fields into Sound resource for loop behavior. */
|
||||
@ModifyReturnValue(method = "getSound", at = @At("RETURN"))
|
||||
private Sound getSound(Sound original, JsonObject json) {
|
||||
int loopStart = GsonHelper.getAsInt(json, "loopStart", 0);
|
||||
int loopEnd = GsonHelper.getAsInt(json, "loopEnd", 0);
|
||||
ResourceLocation intro = ResourceLocation.parse(GsonHelper.getAsString(json, "intro", ""));
|
||||
((SoundDuck)original).setLoopStart(loopStart);
|
||||
((SoundDuck)original).setLoopEnd(loopEnd);
|
||||
((SoundDuck)original).setIntro(intro);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.duck.SoundDuck;
|
||||
import net.minecraft.client.resources.sounds.Sound;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import static net.minecraft.client.resources.sounds.Sound.SOUND_LISTER;
|
||||
|
||||
@Mixin(Sound.class)
|
||||
public abstract class SoundMixin implements SoundDuck {
|
||||
|
||||
int loopStart = 0;
|
||||
|
||||
int loopEnd = 0;
|
||||
|
||||
ResourceLocation intro = null;
|
||||
|
||||
@Override
|
||||
public ResourceLocation getIntro() {
|
||||
return SOUND_LISTER.idToFile(this.intro);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoopStart() {
|
||||
return loopStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoopEnd() {
|
||||
return loopEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoopStart(int startInSamples) {
|
||||
this.loopStart = startInSamples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoopEnd(int endInSamples) {
|
||||
this.loopEnd = endInSamples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntro(ResourceLocation intro) {
|
||||
this.intro = intro;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLoopPoints() {
|
||||
return (this.loopEnd >= 0 && this.loopStart >= 0) && (this.loopEnd + this.loopStart != 0) && (loopStart < loopEnd || loopStart == 0 || loopEnd == 0);
|
||||
}
|
||||
// this is used to determine if the LoopPointAudioStream is necessary or if the legacy LoopingAudioStream can be used
|
||||
|
||||
@Override
|
||||
public boolean hasIntro() {
|
||||
return this.intro != null && !this.intro.getPath().isBlank();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.bridges.StructureProcessorListBridge;
|
||||
import com.cobblemon.mod.common.duck.StructureProcessorListBridge;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -91,7 +91,7 @@ import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatu
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.properties.*
|
||||
import com.cobblemon.mod.common.pokemon.properties.LabelProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -315,7 +315,7 @@ object Cobblemon {
|
||||
|
||||
CustomPokemonProperty.register(UncatchableProperty)
|
||||
CustomPokemonProperty.register(BattleCloneProperty)
|
||||
CustomPokemonProperty.register(LabelProperty)
|
||||
CustomPokemonProperty.register(PokemonFlagProperty)
|
||||
CustomPokemonProperty.register(HiddenAbilityPropertyType)
|
||||
CustomPokemonProperty.register(AspectPropertyType)
|
||||
CustomPokemonProperty.register(UnaspectPropertyType)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.block.*
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.LecternBlock
|
||||
@@ -42,11 +41,11 @@ import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockSetType
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.level.material.PushReaction
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
|
||||
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
|
||||
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
|
||||
@@ -244,9 +243,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, false))
|
||||
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, false))
|
||||
@JvmField
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@@ -478,99 +477,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = create("gimmighoul_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.FAKE))
|
||||
|
||||
@JvmField
|
||||
val WHITE_PLAQUE = create("white_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val LIGHT_GRAY_PLAQUE = create("light_gray_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val GRAY_PLAQUE = create("gray_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val BLACK_PLAQUE = create("black_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val BROWN_PLAQUE = create("brown_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val RED_PLAQUE = create("red_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val ORANGE_PLAQUE = create("orange_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val YELLOW_PLAQUE = create("yellow_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val LIME_PLAQUE = create("lime_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val GREEN_PLAQUE = create("green_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val CYAN_PLAQUE = create("cyan_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val LIGHT_BLUE_PLAQUE = create("light_blue_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val BLUE_PLAQUE = create("blue_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val PURPLE_PLAQUE = create("purple_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val MAGENTA_PLAQUE = create("magenta_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
@JvmField
|
||||
val PINK_PLAQUE = create("pink_plaque", PlaqueBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.METAL).noOcclusion().pushReaction(PushReaction.DESTROY).strength(5.0F)))
|
||||
|
||||
@JvmField
|
||||
val CLEANSE_TAG = create("cleanse_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
@JvmField
|
||||
val SPELL_TAG = create("spell_tag", WallAttachedStackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().noCollission(), WallAttachedStackableItemBlock.WallStackableItemBlockType.TAG))
|
||||
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = create("blunder_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
|
||||
@JvmField
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
|
||||
@JvmField
|
||||
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
@JvmField
|
||||
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
|
||||
@JvmField
|
||||
val DIRE_HIT = create("dire_hit", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val GUARD_SPEC = create("guard_spec", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@JvmField
|
||||
val X_SPEED = create("x_${Stats.SPEED.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
|
||||
@JvmField
|
||||
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
|
||||
@JvmField
|
||||
|
||||
@@ -507,23 +507,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
|
||||
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
|
||||
|
||||
@JvmField val WHITE_PLAQUE = blockItem("white_plaque", CobblemonBlocks.WHITE_PLAQUE)
|
||||
@JvmField val LIGHT_GRAY_PLAQUE = blockItem("light_gray_plaque", CobblemonBlocks.LIGHT_GRAY_PLAQUE)
|
||||
@JvmField val GRAY_PLAQUE = blockItem("gray_plaque", CobblemonBlocks.GRAY_PLAQUE)
|
||||
@JvmField val BLACK_PLAQUE = blockItem("black_plaque", CobblemonBlocks.BLACK_PLAQUE)
|
||||
@JvmField val BROWN_PLAQUE = blockItem("brown_plaque", CobblemonBlocks.BROWN_PLAQUE)
|
||||
@JvmField val RED_PLAQUE = blockItem("red_plaque", CobblemonBlocks.RED_PLAQUE)
|
||||
@JvmField val ORANGE_PLAQUE = blockItem("orange_plaque", CobblemonBlocks.ORANGE_PLAQUE)
|
||||
@JvmField val YELLOW_PLAQUE = blockItem("yellow_plaque", CobblemonBlocks.YELLOW_PLAQUE)
|
||||
@JvmField val LIME_PLAQUE = blockItem("lime_plaque", CobblemonBlocks.LIME_PLAQUE)
|
||||
@JvmField val GREEN_PLAQUE = blockItem("green_plaque", CobblemonBlocks.GREEN_PLAQUE)
|
||||
@JvmField val CYAN_PLAQUE = blockItem("cyan_plaque", CobblemonBlocks.CYAN_PLAQUE)
|
||||
@JvmField val LIGHT_BLUE_PLAQUE = blockItem("light_blue_plaque", CobblemonBlocks.LIGHT_BLUE_PLAQUE)
|
||||
@JvmField val BLUE_PLAQUE = blockItem("blue_plaque", CobblemonBlocks.BLUE_PLAQUE)
|
||||
@JvmField val PURPLE_PLAQUE = blockItem("purple_plaque", CobblemonBlocks.PURPLE_PLAQUE)
|
||||
@JvmField val MAGENTA_PLAQUE = blockItem("magenta_plaque", CobblemonBlocks.MAGENTA_PLAQUE)
|
||||
@JvmField val PINK_PLAQUE = blockItem("pink_plaque", CobblemonBlocks.PINK_PLAQUE)
|
||||
|
||||
private val berries = mutableMapOf<ResourceLocation, BerryItem>()
|
||||
// Plants
|
||||
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
|
||||
@@ -716,15 +699,15 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val MOOMOO_MILK = create("moomoo_milk", MoomooMilk(Item.Properties().craftRemainder(Items.GLASS_BOTTLE).stacksTo((16))))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION, CobblemonBlocks.POTION))
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
@JvmField
|
||||
val SUPER_POTION = create("super_potion", PotionItem(PotionType.SUPER_POTION, CobblemonBlocks.SUPER_POTION))
|
||||
val SUPER_POTION = create("super_potion", PotionItem(PotionType.SUPER_POTION))
|
||||
@JvmField
|
||||
val HYPER_POTION = create("hyper_potion", PotionItem(PotionType.HYPER_POTION, CobblemonBlocks.HYPER_POTION))
|
||||
val HYPER_POTION = create("hyper_potion", PotionItem(PotionType.HYPER_POTION))
|
||||
@JvmField
|
||||
val MAX_POTION = create("max_potion", PotionItem(PotionType.MAX_POTION, CobblemonBlocks.MAX_POTION))
|
||||
val MAX_POTION = create("max_potion", PotionItem(PotionType.MAX_POTION))
|
||||
@JvmField
|
||||
val FULL_RESTORE = create("full_restore", PotionItem(PotionType.FULL_RESTORE, CobblemonBlocks.FULL_RESTORE))
|
||||
val FULL_RESTORE = create("full_restore", PotionItem(PotionType.FULL_RESTORE))
|
||||
|
||||
@JvmField
|
||||
val HEAL_POWDER = create("heal_powder", HealPowderItem())
|
||||
@@ -809,27 +792,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SERIOUS_MINT = mintItem("serious_mint", MintItem(Natures.SERIOUS))
|
||||
|
||||
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY, block = CobblemonBlocks.X_ACCURACY))
|
||||
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK, block = CobblemonBlocks.X_ATTACK))
|
||||
@JvmField val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", XStatItem(Stats.DEFENCE, block = CobblemonBlocks.X_DEFENSE))
|
||||
@JvmField val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", XStatItem(Stats.SPECIAL_ATTACK, block = CobblemonBlocks.X_SP_ATK))
|
||||
@JvmField val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", XStatItem(Stats.SPECIAL_DEFENCE, block = CobblemonBlocks.X_SP_DEF))
|
||||
@JvmField val X_SPEED = create("x_${Stats.SPEED.identifier.path}", XStatItem(Stats.SPEED, block = CobblemonBlocks.X_SPEED))
|
||||
@JvmField val X_ACCURACY = create("x_${Stats.ACCURACY.identifier.path}", XStatItem(Stats.ACCURACY))
|
||||
@JvmField val X_ATTACK = create("x_${Stats.ATTACK.identifier.path}", XStatItem(Stats.ATTACK))
|
||||
@JvmField val X_DEFENSE = create("x_${Stats.DEFENCE.identifier.path}", XStatItem(Stats.DEFENCE))
|
||||
@JvmField val X_SP_ATK = create("x_${Stats.SPECIAL_ATTACK.identifier.path}", XStatItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField val X_SP_DEF = create("x_${Stats.SPECIAL_DEFENCE.identifier.path}", XStatItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField val X_SPEED = create("x_${Stats.SPEED.identifier.path}", XStatItem(Stats.SPEED))
|
||||
|
||||
@JvmField val DIRE_HIT = create("dire_hit", DireHitItem(CobblemonBlocks.DIRE_HIT))
|
||||
@JvmField val GUARD_SPEC = create("guard_spec", GuardSpecItem(CobblemonBlocks.GUARD_SPEC))
|
||||
@JvmField val DIRE_HIT = create("dire_hit", DireHitItem())
|
||||
@JvmField val GUARD_SPEC = create("guard_spec", GuardSpecItem())
|
||||
|
||||
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal", block = CobblemonBlocks.FULL_HEAL))
|
||||
@JvmField val ANTIDOTE = create("antidote", StatusCureItem("item.cobblemon.antidote", Statuses.POISON, Statuses.POISON_BADLY, block = CobblemonBlocks.ANTIDOTE))
|
||||
@JvmField val AWAKENING = create("awakening", StatusCureItem("item.cobblemon.awakening", Statuses.SLEEP, block = CobblemonBlocks.AWAKENING))
|
||||
@JvmField val BURN_HEAL = create("burn_heal", StatusCureItem("item.cobblemon.burn_heal", Statuses.BURN, block = CobblemonBlocks.BURN_HEAL))
|
||||
@JvmField val ICE_HEAL = create("ice_heal", StatusCureItem("item.cobblemon.ice_heal", Statuses.FROZEN, block = CobblemonBlocks.ICE_HEAL))
|
||||
@JvmField val PARALYZE_HEAL = create("paralyze_heal", StatusCureItem("item.cobblemon.paralyze_heal", Statuses.PARALYSIS, block = CobblemonBlocks.PARALYZE_HEAL))
|
||||
@JvmField val BURN_HEAL = create("burn_heal", StatusCureItem("item.cobblemon.burn_heal", Statuses.BURN))
|
||||
@JvmField val PARALYZE_HEAL = create("paralyze_heal", StatusCureItem("item.cobblemon.paralyze_heal", Statuses.PARALYSIS))
|
||||
@JvmField val ICE_HEAL = create("ice_heal", StatusCureItem("item.cobblemon.ice_heal", Statuses.FROZEN))
|
||||
@JvmField val ANTIDOTE = create("antidote", StatusCureItem("item.cobblemon.antidote", Statuses.POISON, Statuses.POISON_BADLY))
|
||||
@JvmField val AWAKENING = create("awakening", StatusCureItem("item.cobblemon.awakening", Statuses.SLEEP))
|
||||
|
||||
@JvmField val ETHER = create("ether", EtherItem(max = false, CobblemonBlocks.ETHER))
|
||||
@JvmField val MAX_ETHER = create("max_ether", EtherItem(max = true, CobblemonBlocks.MAX_ETHER))
|
||||
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false, CobblemonBlocks.ELIXIR))
|
||||
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true, CobblemonBlocks.MAX_ELIXIR))
|
||||
@JvmField val FULL_HEAL = create("full_heal", StatusCureItem("item.cobblemon.full_heal"))
|
||||
|
||||
@JvmField val ETHER = create("ether", EtherItem(max = false))
|
||||
@JvmField val MAX_ETHER = create("max_ether", EtherItem(max = true))
|
||||
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
|
||||
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
|
||||
|
||||
@JvmField
|
||||
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
|
||||
@@ -938,7 +922,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_SLUDGE = heldItem("black_sludge")
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = itemNameBlockItem("blunder_policy", CobblemonBlocks.BLUNDER_POLICY)
|
||||
val BLUNDER_POLICY = heldItem("blunder_policy")
|
||||
@JvmField
|
||||
val CELL_BATTERY = heldItem("cell_battery")
|
||||
@JvmField
|
||||
@@ -946,7 +930,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val CHOICE_SCARF = heldItem("choice_scarf")
|
||||
@JvmField
|
||||
val CLEANSE_TAG = itemNameBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
|
||||
val CLEANSE_TAG = heldItem("cleanse_tag")
|
||||
@JvmField
|
||||
val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
@@ -1054,7 +1038,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SOFT_SAND = heldItem("soft_sand")
|
||||
@JvmField
|
||||
val SPELL_TAG = itemNameBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
|
||||
val SPELL_TAG = heldItem("spell_tag")
|
||||
@JvmField
|
||||
val SMOKE_BALL = heldItem("smoke_ball")
|
||||
@JvmField
|
||||
@@ -1072,7 +1056,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = itemNameBlockItem("weakness_policy", CobblemonBlocks.WEAKNESS_POLICY)
|
||||
val WEAKNESS_POLICY = heldItem("weakness_policy")
|
||||
@JvmField
|
||||
val WIDE_LENS = heldItem("wide_lens")
|
||||
@JvmField
|
||||
@@ -1569,7 +1553,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun heldItem(name: String, item: Item, remappedName: String? = null) = create(
|
||||
name = name,
|
||||
entry = item.also {
|
||||
|
||||
@@ -304,8 +304,7 @@ open class PokemonBattle(
|
||||
.forEach{it.pokemon.heal()}
|
||||
actors.forEach { actor ->
|
||||
actor.pokemonList.forEach { battlePokemon ->
|
||||
battlePokemon.postBattlePokemonOperations.forEach { it(battlePokemon) }
|
||||
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperations.forEach { it(entity) } }
|
||||
battlePokemon.entity?.let { entity -> battlePokemon.postBattleEntityOperation(entity) }
|
||||
if (battlePokemon.effectedPokemon.entity != null
|
||||
&& battlePokemon.effectedPokemon.entity?.beamMode == 0
|
||||
&& battlePokemon.effectedPokemon.entity?.platform != PlatformType.NONE) {
|
||||
|
||||
+3
-6
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.events.battles
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
@@ -26,8 +25,7 @@ import com.cobblemon.mod.common.util.asArrayValue
|
||||
data class BattleFaintedEvent(
|
||||
override val battle: PokemonBattle,
|
||||
val killed: BattlePokemon,
|
||||
val context: BattleContext,
|
||||
val pnx: String
|
||||
val context: BattleContext
|
||||
) : BattleEvent {
|
||||
val structContext = mutableMapOf<String, MoValue>(
|
||||
"battle" to battle.struct,
|
||||
@@ -35,7 +33,6 @@ data class BattleFaintedEvent(
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
|
||||
"pokemon" to killed.struct,
|
||||
"context" to context.getStruct(),
|
||||
"pnx" to StringValue(pnx)
|
||||
"context" to context.getStruct()
|
||||
)
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -25,8 +25,7 @@ import com.cobblemon.mod.common.util.asArrayValue
|
||||
data class FormeChangeEvent(
|
||||
override val battle: PokemonBattle,
|
||||
val pokemon: BattlePokemon,
|
||||
val formeName: String,
|
||||
val pnx: String
|
||||
val formeName: String
|
||||
) : BattleEvent {
|
||||
val context = mutableMapOf(
|
||||
"battle" to battle.struct,
|
||||
@@ -34,7 +33,6 @@ data class FormeChangeEvent(
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
|
||||
"pokemon" to pokemon.struct,
|
||||
"forme" to StringValue(formeName),
|
||||
"pnx" to StringValue(pnx)
|
||||
"forme" to StringValue(formeName)
|
||||
)
|
||||
}
|
||||
}
|
||||
-11
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
@@ -30,11 +29,6 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.util.LowerCaseEnumTypeAdapterFactory
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
|
||||
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
|
||||
override val id = cobblemonResource("pokemon_interactions")
|
||||
@@ -49,11 +43,6 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
|
||||
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
|
||||
.setPrettyPrinting()
|
||||
|
||||
+4
-19
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.pokemon.adapters.CobblemonRequirementAdapter
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -144,12 +143,11 @@ class GiveItemEffect(val item: ResourceLocation, val amount: IntRange?) : Intera
|
||||
class PlaySoundEffect(
|
||||
val sound: ResourceLocation,
|
||||
val soundSource: SoundSource?,
|
||||
@SerializedName("playAround") private val _playAround: Boolean?,
|
||||
@SerializedName("distance") private val _distance: Double?,
|
||||
@SerializedName("volume") private val _volume: Float?,
|
||||
@SerializedName("pitch") private val _pitch: Float?,
|
||||
val playAround: Boolean = true,
|
||||
val distance: Double = 64.0,
|
||||
val volume: Float = 1.0F,
|
||||
val pitch: Float = 1.0F
|
||||
) : InteractionEffect {
|
||||
|
||||
override fun applyEffect(
|
||||
pokemon: PokemonEntity,
|
||||
player: ServerPlayer
|
||||
@@ -163,7 +161,6 @@ class PlaySoundEffect(
|
||||
volume,
|
||||
pitch
|
||||
)
|
||||
|
||||
if (playAround) {
|
||||
packet.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, distance, pokemon.level().dimension())
|
||||
} else {
|
||||
@@ -171,18 +168,6 @@ class PlaySoundEffect(
|
||||
}
|
||||
}
|
||||
|
||||
val playAround: Boolean
|
||||
get() = _playAround != false
|
||||
|
||||
val distance: Double
|
||||
get() = _distance ?: 64.0
|
||||
|
||||
val volume: Float
|
||||
get() = _volume ?: 1.0F
|
||||
|
||||
val pitch: Float
|
||||
get() = _pitch ?: 1.0F
|
||||
|
||||
companion object {
|
||||
val ID = "play_sound"
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ object MoLangFunctions {
|
||||
runtime.environment.variable = params.environment.variable
|
||||
val args = params.params.subList(1, params.params.size)
|
||||
runtime.environment.context = ContextStruct(
|
||||
(params.environment.context?.map ?: emptyMap()) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
|
||||
params.environment.context.map + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
|
||||
)
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
// store the args in the
|
||||
@@ -324,24 +324,13 @@ object MoLangFunctions {
|
||||
runtime.environment.context = params.environment.context
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
|
||||
|
||||
fun evaluate(): MoValue {
|
||||
return try {
|
||||
runtime.resolve(expression)
|
||||
} catch (ex: Exception) {
|
||||
Cobblemon.LOGGER.warn("Failed to evaluate MoLang expression${if (delayInSeconds > 0.0f) " (delayed)" else ""}: $expression", ex)
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
if (delayInSeconds > 0.0f) {
|
||||
val tracker = if (Cobblemon.implementation.environment() == Environment.SERVER) ServerTaskTracker else ClientTaskTracker
|
||||
tracker.after(delayInSeconds) {
|
||||
evaluate()
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
} else {
|
||||
evaluate()
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
},
|
||||
"system_time_millis" to java.util.function.Function { _ ->
|
||||
@@ -351,19 +340,19 @@ object MoLangFunctions {
|
||||
"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)
|
||||
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_of" to java.util.function.Function { params ->
|
||||
val time = params.getDouble(0).toLong()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
|
||||
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_is_after" to java.util.function.Function { params ->
|
||||
val dateA = params.getString(0)
|
||||
val dateB = params.getString(1)
|
||||
val format = java.text.SimpleDateFormat("dd/MM/yyyy")
|
||||
val format = java.text.SimpleDateFormat("DD/MM/YYYY")
|
||||
val a = format.parse(dateA)
|
||||
val b = format.parse(dateB)
|
||||
DoubleValue(a.after(b))
|
||||
|
||||
+8
-10
@@ -80,9 +80,7 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
internal constructor(): this(emptyList())
|
||||
|
||||
override fun get(pokemon: Pokemon): StringSpeciesFeature? {
|
||||
return pokemon.features.filterIsInstance<StringSpeciesFeature>().find { it.name in keys }
|
||||
}
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
|
||||
val existing = get(pokemon)
|
||||
@@ -103,15 +101,15 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
}
|
||||
|
||||
override fun invoke(nbt: CompoundTag): StringSpeciesFeature? {
|
||||
val key = keys.find { nbt.contains(it) }
|
||||
if (key == null) return null
|
||||
return StringSpeciesFeature(key, "").also { it.loadFromNBT(nbt) }
|
||||
return if (nbt.contains(keys.first())) {
|
||||
StringSpeciesFeature(keys.first(), "").also { it.loadFromNBT(nbt) }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun invoke(json: JsonObject): StringSpeciesFeature? {
|
||||
val key = keys.find { json.has(it) }
|
||||
if (key == null) return null
|
||||
return StringSpeciesFeature(key, "").also { it.loadFromJSON(json) }
|
||||
return if (json.has(keys.first())) {
|
||||
StringSpeciesFeature(keys.first(), "").also { it.loadFromJSON(json) }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun fromString(value: String?): StringSpeciesFeature? {
|
||||
@@ -133,7 +131,7 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return if (isAspect) {
|
||||
val feature = properties.customProperties.filterIsInstance<StringSpeciesFeature>().find { it.name in keys }
|
||||
val feature = properties.customProperties.filterIsInstance<StringSpeciesFeature>().find { it.name == keys.first() }
|
||||
if (feature != null) {
|
||||
setOf(getAspect(feature))
|
||||
} else {
|
||||
|
||||
+11
-23
@@ -132,9 +132,7 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
|
||||
|
||||
constructor(vararg keys: String) : this(keys.toList())
|
||||
|
||||
override fun get(pokemon: Pokemon): FlagSpeciesFeature? {
|
||||
return pokemon.features.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
|
||||
}
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
|
||||
return get(pokemon)
|
||||
@@ -146,15 +144,15 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
|
||||
}
|
||||
|
||||
override fun invoke(nbt: CompoundTag): FlagSpeciesFeature? {
|
||||
val key = keys.find { nbt.contains(it) }
|
||||
if (key == null) return null
|
||||
return FlagSpeciesFeature(key, false).also { it.loadFromNBT(nbt) }
|
||||
return if (nbt.contains(keys.first())) {
|
||||
FlagSpeciesFeature(keys.first(), false).also { it.loadFromNBT(nbt) }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun invoke(json: JsonObject): FlagSpeciesFeature? {
|
||||
val key = keys.find { json.has(it) }
|
||||
if (key == null) return null
|
||||
return FlagSpeciesFeature(key, false).also { it.loadFromJSON(json) }
|
||||
return if (json.has(keys.first())) {
|
||||
FlagSpeciesFeature(keys.first(), false).also { it.loadFromJSON(json) }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun fromString(value: String?): FlagSpeciesFeature? {
|
||||
@@ -172,26 +170,16 @@ class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpecie
|
||||
}
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect) {
|
||||
val feature = pokemon.features.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
|
||||
if (feature != null && feature.enabled) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.enabled == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return if (isAspect) {
|
||||
val feature = properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name in keys }
|
||||
if (feature != null && feature.enabled) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
+10
-6
@@ -142,15 +142,19 @@ class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesF
|
||||
}
|
||||
|
||||
override fun invoke(nbt: CompoundTag): IntSpeciesFeature? {
|
||||
val key = keys.find { nbt.contains(it) }
|
||||
if (key == null) return null
|
||||
return IntSpeciesFeature(keys.first(), nbt.getInt(key));
|
||||
return if (nbt.contains(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(json: JsonObject): IntSpeciesFeature? {
|
||||
val key = keys.find { json.has(it) }
|
||||
if (key == null) return null
|
||||
return IntSpeciesFeature(keys.first(), json.get(key).asInt)
|
||||
return if (json.has(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
|
||||
|
||||
@@ -102,12 +102,6 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
|
||||
@JvmStatic
|
||||
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
|
||||
|
||||
@JvmStatic
|
||||
fun registerType(key: String, clazz: Class<out SpeciesFeatureProvider<*>>) {
|
||||
types[key] = clazz
|
||||
}
|
||||
|
||||
private fun registerFromAssets(identifier: ResourceLocation, provider: SpeciesFeatureProvider<*>) = register(identifier.path, provider, isCoded = false)
|
||||
|
||||
@JvmStatic
|
||||
|
||||
+3
-4
@@ -24,15 +24,14 @@ import net.minecraft.world.entity.LivingEntity
|
||||
interface EntityQueryRequirement : Requirement {
|
||||
override fun check(pokemon: Pokemon): Boolean {
|
||||
val queriedEntity = pokemon.entity ?: pokemon.getOwnerPlayer() ?: return false
|
||||
return this.check(pokemon, queriedEntity)
|
||||
return this.check(queriedEntity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [Pokemon] & [LivingEntity] satisfies the requirement.
|
||||
* Checks if the given [LivingEntity] satisfies the requirement.
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @param queriedEntity The [LivingEntity] that can be compared in context of a [Level].
|
||||
* @return If the requirement was satisfied.
|
||||
*/
|
||||
fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean
|
||||
fun check(queriedEntity: LivingEntity): Boolean
|
||||
}
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.reactive
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
|
||||
open class EventObservable<T> : SimpleObservable<T>() {
|
||||
@JvmOverloads
|
||||
inline fun post(vararg events: T, then: (T) -> Unit = {}) {
|
||||
// Issuing generic-typed pass-throughs to varargs doesn't work
|
||||
// properly unless the source is a vararg as well, complicated story.
|
||||
|
||||
@@ -258,22 +258,17 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
|
||||
@JvmOverloads
|
||||
fun toBattleTeam(clone: Boolean = false, healPokemon: Boolean = false, leadingPokemon: UUID? = null) : List<BattlePokemon> {
|
||||
var leadingPokemonId = leadingPokemon
|
||||
val result = this.mapNotNull {
|
||||
return@mapNotNull if (clone) {
|
||||
BattlePokemon.safeCopyOf(it).also { copy ->
|
||||
if (leadingPokemon != null && it.uuid == leadingPokemonId) {
|
||||
leadingPokemonId = copy.uuid
|
||||
}
|
||||
}
|
||||
BattlePokemon.safeCopyOf(it)
|
||||
} else {
|
||||
BattlePokemon.playerOwned(it)
|
||||
}.also { if (healPokemon) it.effectedPokemon.heal() }
|
||||
}.toMutableList()
|
||||
|
||||
// reposition lead to front of the party
|
||||
if (leadingPokemonId != null && result.first().uuid != leadingPokemonId) {
|
||||
result.find { it.uuid == leadingPokemonId }?.let { lead ->
|
||||
if (leadingPokemon != null && result.first().uuid != leadingPokemon) {
|
||||
result.find { it.uuid == leadingPokemon }?.let { lead ->
|
||||
result.remove(lead)
|
||||
result.add(0, lead)
|
||||
}
|
||||
|
||||
+2
-2
@@ -154,12 +154,12 @@ open class PlayerPartyStore(
|
||||
|
||||
// Metabolism for Fullness
|
||||
if (pokemon.currentFullness > 0) {
|
||||
pokemon.tickMetabolism(20)
|
||||
pokemon.tickMetabolism()
|
||||
}
|
||||
|
||||
// Interaction Cooldown
|
||||
if (pokemon.interactionCooldowns.any()) {
|
||||
pokemon.tickInteractionCooldown(20)
|
||||
pokemon.tickInteractionCooldown()
|
||||
}
|
||||
|
||||
pokemon.features.filterIsInstance<TickingSpeciesFeature>().forEach { it.onSecondPassed(player.serverLevel(), pokemon, null) }
|
||||
|
||||
@@ -42,7 +42,6 @@ object CobblemonItemTags {
|
||||
@JvmField val DAWN_STONE_ORES = create("dawn_stone_ores")
|
||||
@JvmField val POTTERY_SHERDS = create("decorated_pot_sherds")
|
||||
@JvmField val DEEP_SEAS = create("deep_seas")
|
||||
@JvmField val DISPLAY_CASE_ITEMS = create("display_case_items")
|
||||
@JvmField val DUSK_STONE_ORES = create("dusk_stone_ores")
|
||||
@JvmField val ETHERS = create("ethers")
|
||||
@JvmField val EVOLUTION_ITEMS = create("evolution_items")
|
||||
@@ -58,7 +57,6 @@ object CobblemonItemTags {
|
||||
@JvmField val HANGING_SIGNS = create("hanging_signs")
|
||||
@JvmField val HERBS = create("herbs")
|
||||
@JvmField val ICE_STONE_ORES = create("ice_stone_ores")
|
||||
@JvmField val ITEM_BLOCKS = create("item_blocks")
|
||||
@JvmField val LEAF_STONE_ORES = create("leaf_stone_ores")
|
||||
@JvmField val MACHINES = create("machines")
|
||||
@JvmField val MINT_LEAF = create("mint_leaf")
|
||||
@@ -67,7 +65,6 @@ object CobblemonItemTags {
|
||||
@JvmField val MOCHIS = create("mochis")
|
||||
@JvmField val MOON_STONE_ORES = create("moon_stone_ores")
|
||||
@JvmField val MUTATED_BERRIES = create("mutated_berries")
|
||||
@JvmField val PLAQUES = create("plaques")
|
||||
@JvmField val PLANTS = create("plants")
|
||||
@JvmField val POKE_BALLS = create("poke_balls")
|
||||
@JvmField val POKE_RODS = create("poke_rods")
|
||||
|
||||
@@ -63,8 +63,8 @@ object BattleBuilder {
|
||||
): BattleStartResult {
|
||||
|
||||
val adjustLevel = battleFormat.adjustLevel
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer1)
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer2)
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer1).sortedBy { it.health <= 0 }
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties || adjustLevel > 0, healPokemon = healFirst, leadingPokemonPlayer2).sortedBy { it.health <= 0 }
|
||||
|
||||
val battlePartyStores = mutableListOf<PlayerPartyStore>()
|
||||
|
||||
@@ -207,6 +207,7 @@ object BattleBuilder {
|
||||
val side1Theme = players[0].getBattleTheme()
|
||||
val side2Theme = players[2].getBattleTheme()
|
||||
|
||||
// the music that's played for a side is driven by the opposite leader's theme
|
||||
side1Actors.forEach { it.battleTheme = side2Theme }
|
||||
side2Actors.forEach { it.battleTheme = side1Theme }
|
||||
|
||||
|
||||
@@ -105,18 +105,12 @@ data class BattleFormat(
|
||||
}
|
||||
|
||||
fun toFormatJSON(): String {
|
||||
/*
|
||||
Bag clause does not exist in showdown as a rule because showdown simply has no concept of a 'bag'.
|
||||
If we try sending it, the battle will error. So this simply omits that from ever getting sent.
|
||||
If we ever find a case where we are doing this for more than just bag clause, we should make a proper abstraction.
|
||||
*/
|
||||
val rulesToSendToShowdown = ruleSet.filter { it != BattleRules.BAG_CLAUSE }
|
||||
return """
|
||||
{
|
||||
"mod": "$mod",
|
||||
"gameType": "${battleType.name}",
|
||||
"gen": $gen,
|
||||
"ruleset": [${rulesToSendToShowdown.joinToString { "\"$it\"" }}],
|
||||
"ruleset": [${ruleSet.joinToString { "\"$it\"" }}],
|
||||
"effectType": "Format"
|
||||
}
|
||||
""".trimIndent().replace("\n", "")
|
||||
|
||||
@@ -12,7 +12,6 @@ object BattleRules {
|
||||
const val OBTAINABLE = "Obtainable"
|
||||
const val PAST = "+Past"
|
||||
const val UNOBTAINABLE = "+Unobtainable"
|
||||
const val BAG_CLAUSE = "Bag Clause"
|
||||
const val TEAM_PREVIEW = "Team Preview"
|
||||
const val ENDLESS_BATTLE_CLAUSE = "Endless Battle Clause"
|
||||
const val CANCEL_MOD = "Cancel Mod"
|
||||
|
||||
@@ -289,8 +289,6 @@ class ShowdownMoveset {
|
||||
var canDynamax = false
|
||||
var maxMoves: List<InBattleGimmickMove?>? = null
|
||||
var canTerastallize: String? = null
|
||||
/** This stores a list of gimmicks that have already been used this turn by a previous Pokemon, but the full turn has not been processed (ie. double battles). */
|
||||
val pendingGimmickUsedThisTurn = mutableListOf<Gimmick>()
|
||||
|
||||
fun saveToBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
|
||||
|
||||
@@ -300,35 +300,4 @@ object ShowdownInterpreter {
|
||||
} ?:
|
||||
MissingContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new update instruction parser.
|
||||
* @param id The ID of the instruction to register.
|
||||
* @param callback The callback function to create the instruction.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun registerUpdateInstructionParser(id: String, callback: (PokemonBattle, InstructionSet, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction) {
|
||||
updateInstructionParser[id] = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new split instruction parser.
|
||||
* @param id The ID of the instruction to register.
|
||||
* @param callback The callback function to create the instruction.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun registerSplitInstructionParser(id: String, callback: (PokemonBattle, BattleActor, InstructionSet, BattleMessage, BattleMessage, Iterator<BattleMessage>) -> InterpreterInstruction) {
|
||||
splitInstructionParser[id] = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new side instruction parser.
|
||||
* @param id The ID of the instruction to register.
|
||||
* @param callback The callback function to create the instruction.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun registerSideInstructionParser(id: String, callback: (PokemonBattle, BattleActor, InstructionSet, BattleMessage) -> InterpreterInstruction) {
|
||||
sideInstructionParser[id] = callback
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import kotlin.collections.associateWith
|
||||
import kotlin.collections.partition
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
|
||||
/**
|
||||
* AI that tries to choose the best move for the given situations. Based off of the Pokemon Trainer Tournament Simulator Github
|
||||
* https://github.com/cRz-Shadows/Pokemon_Trainer_Tournament_Simulator/blob/main/pokemon-showdown/sim/examples/Simulation-test-1.ts#L330
|
||||
@@ -272,11 +271,6 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
}
|
||||
activeTrackerPokemon.firstTurn = false
|
||||
|
||||
// If HP is below 30% and not switching out, always use the most damaging move
|
||||
if (activeTrackerPokemon.currentHpPercent < 0.3 && !shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
|
||||
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)?.let { return it }
|
||||
}
|
||||
|
||||
// Decision-making based on move availability and switch-out condition
|
||||
if (!shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
|
||||
val nRemainingMons = activeTracker.alliedSide.actors.sumOf { actor -> actor.party.filter { it.currentHpPercent > 0 }.size }
|
||||
@@ -527,19 +521,6 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
|
||||
val opponentActiveTracker = activeTracker.opponentSide.activePokemon
|
||||
val opponentSpeedEstimations = opponentActiveTracker.map { statEstimationActive(it, Stats.SPEED) }
|
||||
|
||||
|
||||
// Only switch if a switch-in is significantly better than the current Pokémon
|
||||
val currentScore = estimateMatchup(activeBattlePokemon, side, battle)
|
||||
val bestSwitchScore = availableSwitches.maxOfOrNull { estimateMatchup(activeBattlePokemon, side, battle, it) } ?: currentScore
|
||||
val improvementThreshold = currentScore.absoluteValue * 0.25 + 2
|
||||
if (bestSwitchScore <= currentScore + improvementThreshold) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(bestSwitchScore < 1) {
|
||||
return false
|
||||
}
|
||||
// todo add some way to keep track of the player's boosting to see if it needs to switch out to something that can stop it
|
||||
|
||||
// if slower speed stat than the opposing pokemon and HP is less than 20% don't switch out
|
||||
@@ -567,8 +548,6 @@ class StrongBattleAI(skill: Int) : BattleAI {
|
||||
if (availableSwitches.isEmpty() || availableSwitches.none { estimateMatchup(activeBattlePokemon, side, battle, it) > 0 })
|
||||
return false
|
||||
|
||||
|
||||
|
||||
// ...and a 'good' reason to switch out
|
||||
if (opponentActiveTracker.any { it.boosts.getOrDefault(Stats.ACCURACY, 0) <= accuracySwitchThreshold } ||
|
||||
opponentActiveTracker.any { it.boosts.getOrDefault(Stats.DEFENCE, 0) <= -3 } ||
|
||||
|
||||
+1
-8
@@ -114,14 +114,7 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
}
|
||||
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
|
||||
"shadowforce", "hyperspacefury", "hyperspacehole" -> battleLang("activate.phantomforce", pokemonName)
|
||||
else -> {
|
||||
val msgVariant = message.optionalArgument("msg")
|
||||
if (msgVariant != null) {
|
||||
battleLang("activate.${effect.id}.$msgVariant", pokemonName, sourceName, extraEffect)
|
||||
} else {
|
||||
battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
|
||||
}
|
||||
}
|
||||
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ class FaintInstruction(battle: PokemonBattle, val message: BattleMessage) : Inte
|
||||
preamble.thenAccept {
|
||||
faintingPokemon.effectedPokemon.currentHealth = 0
|
||||
faintingPokemon.sendUpdate()
|
||||
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context, pnx))
|
||||
CobblemonEvents.BATTLE_FAINTED.post(BattleFaintedEvent(battle, pokemon, context))
|
||||
battle.getActorAndActiveSlotFromPNX(pnx).second.battlePokemon = null
|
||||
faintingPokemon.contextManager.add(context)
|
||||
faintingPokemon.contextManager.clear(BattleContext.Type.STATUS, BattleContext.Type.VOLATILE, BattleContext.Type.BOOST, BattleContext.Type.UNBOOST)
|
||||
|
||||
+1
-2
@@ -26,7 +26,6 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val (pnx, _) = message.pnxAndUuid(0) ?: return
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
val details = message.argumentAt(1)?.split(',')?.get(0)?.lowercase() ?: return
|
||||
val speciesName = details.substringBefore('-')
|
||||
@@ -38,7 +37,7 @@ class FormeChangeInstruction(val message: BattleMessage): InterpreterInstruction
|
||||
battle.dispatchWaiting {
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName, pnx))
|
||||
CobblemonEvents.FORME_CHANGE.post(FormeChangeEvent(battle, battlePokemon, formName))
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val lang = when(formName) {
|
||||
"busted", "hero", "complete" -> return@dispatchWaiting
|
||||
|
||||
-5
@@ -166,11 +166,6 @@ class SwitchInstruction(val instructionSet: InstructionSet, val battleActor: Bat
|
||||
// If we can't find the entity for some reason then we're going to skip the recall animation
|
||||
val sendOutFuture = CompletableFuture<Unit>()
|
||||
(pokemonEntity?.recallWithAnimation() ?: CompletableFuture.completedFuture(Unit)).thenApply {
|
||||
if (battle.ended) {
|
||||
sendOutFuture.complete(Unit)
|
||||
return@thenApply
|
||||
}
|
||||
|
||||
// Queue actual swap and send-in after the animation has ended
|
||||
actor.pokemonList.swap(actor.activePokemon.indexOf(activePokemon), actor.pokemonList.indexOf(newPokemon))
|
||||
activePokemon.battlePokemon = newPokemon
|
||||
|
||||
+10
-10
@@ -43,17 +43,17 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
val losersText = losers.map { it.getName() }.reduce { acc, next -> acc + " & " + next }
|
||||
val wasCaught = battle.showdownMessages.any { "capture" in it }
|
||||
|
||||
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
|
||||
if (battle.isPvW) {
|
||||
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
|
||||
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
|
||||
|
||||
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
}
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
// If the battle was a PvW battle, we need to set the killer of the wild Pokémon to the player
|
||||
if (battle.isPvW) {
|
||||
val nonPlayerActor = battle.actors.first { it.type == ActorType.WILD }
|
||||
val wildPokemon: BattlePokemon = nonPlayerActor.pokemonList.first()
|
||||
|
||||
if (!wasCaught && losers.any { it.uuid == wildPokemon.uuid }) {
|
||||
wildPokemon.effectedPokemon.entity?.killer = (battle.actors.firstOrNull { it.type == ActorType.PLAYER } as? PlayerBattleActor)?.entity
|
||||
}
|
||||
}
|
||||
|
||||
// broadcast victory / defeat
|
||||
if (!wasCaught) {
|
||||
val blackedOut = battle.isPvW && losers.any { it is PlayerBattleActor }
|
||||
|
||||
@@ -36,18 +36,10 @@ import java.util.function.Function
|
||||
open class BattlePokemon(
|
||||
val originalPokemon: Pokemon,
|
||||
val effectedPokemon: Pokemon = originalPokemon,
|
||||
var postBattlePokemonOperations: MutableList<(BattlePokemon) -> Unit> = mutableListOf(),
|
||||
var postBattleEntityOperations: MutableList<(PokemonEntity) -> Unit> = mutableListOf()
|
||||
val postBattleEntityOperation: (PokemonEntity) -> Unit = {}
|
||||
) {
|
||||
lateinit var actor: BattleActor
|
||||
|
||||
constructor(originalPokemon: Pokemon, effectedPokemon: Pokemon, postBattleEntityOperation: (PokemonEntity) -> Unit) : this(
|
||||
originalPokemon = originalPokemon,
|
||||
effectedPokemon = effectedPokemon,
|
||||
postBattlePokemonOperations = mutableListOf<(BattlePokemon) -> Unit>(),
|
||||
postBattleEntityOperations = mutableListOf(postBattleEntityOperation)
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun safeCopyOf(pokemon: Pokemon): BattlePokemon {
|
||||
//TOOD figure out a closer registry access (might have to break some method signatures for this (1.7?)
|
||||
|
||||
@@ -19,7 +19,6 @@ import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
@@ -111,8 +110,6 @@ class DisplayCaseBlock(settings: Properties) : BaseEntityBlock(settings) {
|
||||
|
||||
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
|
||||
|
||||
override fun getShadeBrightness(state: BlockState, level: BlockGetter, pos: BlockPos): Float = 0.75F
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, world: Level, pos: BlockPos): Int {
|
||||
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import net.minecraft.world.level.block.SoundType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
@@ -44,6 +43,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
val CODEC = simpleCodec(::HeartyGrainsBlock)
|
||||
const val MATURE_AGE = 6
|
||||
const val AGE_AFTER_HARVEST = 3 // Last single block stage
|
||||
val WATERLOGGED = BlockStateProperties.WATERLOGGED
|
||||
val AGE: IntegerProperty = IntegerProperty.create("age", 0, MATURE_AGE)
|
||||
val HALF: EnumProperty<DoubleBlockHalf> = BlockStateProperties.DOUBLE_BLOCK_HALF
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.voxelShape
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
@@ -72,10 +71,10 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
|
||||
private val WEST_AABB_TOP = buildCollider(top = true, Direction.WEST)
|
||||
private val EAST_AABB_TOP = buildCollider(top = true, Direction.EAST)
|
||||
|
||||
private val SOUTH_AABB_BOTTOM = buildCollider(top = false, Direction.SOUTH)
|
||||
private val NORTH_AABB_BOTTOM = buildCollider(top = false, Direction.NORTH)
|
||||
private val WEST_AABB_BOTTOM = buildCollider(top = false, Direction.WEST)
|
||||
private val EAST_AABB_BOTTOM = buildCollider(top = false, Direction.EAST)
|
||||
private val SOUTH_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.SOUTH, EAST_AABB_BOTTOM)
|
||||
private val NORTH_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.NORTH, EAST_AABB_BOTTOM)
|
||||
private val WEST_AABB_BOTTOM = rotateShape(Direction.EAST, Direction.WEST, EAST_AABB_BOTTOM)
|
||||
|
||||
private fun buildCollider(top: Boolean, direction: Direction): VoxelShape {
|
||||
if (top) {
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class PlaqueBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBlock(properties), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC: MapCodec<PlaqueBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(propertiesCodec()).apply(it, ::PlaqueBlock)
|
||||
}
|
||||
|
||||
val FLOOR_NORTH = Shapes.or(
|
||||
Shapes.box(0.0625, 0.25, 0.4375, 0.9375, 1.0, 0.5625),
|
||||
Shapes.box(0.125, 0.0, 0.4375, 0.25, 0.25, 0.5625),
|
||||
Shapes.box(0.75, 0.0, 0.4375, 0.875, 0.25, 0.5625),
|
||||
)
|
||||
val WALL_NORTH = Shapes.box(0.0625, 0.125, 0.9375, 0.9375, 0.875, 1.0)
|
||||
val CEILING_NORTH = Shapes.box(0.0625, 0.0, 0.4375, 0.9375, 0.75, 0.5625)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(FACE, AttachFace.WALL)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, FACE, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
for (direction in context.nearestLookingDirections) {
|
||||
val blockState: BlockState
|
||||
if (direction.getAxis() === Direction.Axis.Y) {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, if (direction == Direction.UP) AttachFace.CEILING else AttachFace.FLOOR) as BlockState)
|
||||
.setValue(FACING, context.horizontalDirection.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
} else {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, AttachFace.WALL) as BlockState)
|
||||
.setValue(FACING, direction.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
|
||||
}
|
||||
|
||||
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<PlaqueBlock> = CODEC
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, FLOOR_NORTH)
|
||||
} else FLOOR_NORTH
|
||||
AttachFace.WALL ->
|
||||
when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_NORTH
|
||||
else -> rotateShape(Direction.NORTH, direction, WALL_NORTH)
|
||||
}
|
||||
AttachFace.CEILING ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, CEILING_NORTH)
|
||||
} else CEILING_NORTH
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (getConnectedDirection(state).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return if (state.getValue(FACE) == AttachFace.CEILING) {
|
||||
val direction = getConnectedDirection(state)
|
||||
canSupportCenter(level, pos.relative(direction.opposite), direction)
|
||||
} else {
|
||||
super.canSurvive(state, level, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import kotlin.math.min
|
||||
|
||||
class StackableItemBlock(settings: Properties, val type: StackableItemBlockType): HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
|
||||
enum class StackableItemBlockType: StringRepresentable {
|
||||
BATTLE_ITEM,
|
||||
ETHER,
|
||||
MAX_POTION,
|
||||
POTION,
|
||||
STATUS_RESTORE;
|
||||
|
||||
override fun getSerializedName(): String = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum(::values)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val MAX_AMOUNT: Int = 8
|
||||
val AMOUNT: IntegerProperty = IntegerProperty.create("amount", 1, MAX_AMOUNT)
|
||||
|
||||
val CODEC: MapCodec<StackableItemBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
StackableItemBlockType.CODEC.fieldOf("type").forGetter(StackableItemBlock::type)
|
||||
).apply(it, ::StackableItemBlock) }
|
||||
|
||||
val northShapeMap: Map<StackableItemBlockType, List<VoxelShape>> = mapOf(
|
||||
StackableItemBlockType.BATTLE_ITEM to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.34375, 0.65625, 0.4375, 0.65625),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.34375, 0.40625, 0.4375, 0.65625),
|
||||
Shapes.box(0.59375, 0.0, 0.40625, 0.90625, 0.4375, 0.71875)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.59375, 0.0, 0.59375, 0.90625, 0.4375, 0.90625),
|
||||
Shapes.box(0.09375, 0.0, 0.53125, 0.40625, 0.4375, 0.84375),
|
||||
Shapes.box(0.34375, 0.0, 0.09375, 0.65625, 0.4375, 0.40625)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.09375, 0.40625, 0.4375, 0.40625),
|
||||
Shapes.box(0.59375, 0.0, 0.15625, 0.90625, 0.4375, 0.46875),
|
||||
Shapes.box(0.65625, 0.0, 0.59375, 0.96875, 0.4375, 0.90625),
|
||||
Shapes.box(0.15625, 0.0, 0.53125, 0.46875, 0.4375, 0.84375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.ETHER to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5, 0.6875)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.375, 0.0, 0.125, 0.625, 0.5, 0.5),
|
||||
Shapes.box(0.125, 0.0, 0.5625, 0.375, 0.5, 0.9375),
|
||||
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.375, 0.5, 0.4375),
|
||||
Shapes.box(0.625, 0.0, 0.125, 0.875, 0.5, 0.5),
|
||||
Shapes.box(0.6875, 0.0, 0.625, 0.9375, 0.5, 1.0),
|
||||
Shapes.box(0.1875, 0.0, 0.5625, 0.4375, 0.5, 0.9375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.MAX_POTION to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.3125, 0.65625, 0.5, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.3125, 0.40625, 0.5, 0.6875),
|
||||
Shapes.box(0.59375, 0.0, 0.375, 0.90625, 0.5, 0.75)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.34375, 0.0, 0.0625, 0.65625, 0.5, 0.4375),
|
||||
Shapes.box(0.09375, 0.0, 0.5625, 0.40625, 0.5, 0.9375),
|
||||
Shapes.box(0.59375, 0.0, 0.625, 0.90625, 0.5, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.0625, 0.40625, 0.5, 0.4375),
|
||||
Shapes.box(0.15625, 0.0, 0.5625, 0.46875, 0.5, 0.9375),
|
||||
Shapes.box(0.65625, 0.0, 0.625, 0.96875, 0.5, 1.0),
|
||||
Shapes.box(0.59375, 0.0, 0.125, 0.90625, 0.5, 0.5)
|
||||
).singleEncompassing()
|
||||
),
|
||||
StackableItemBlockType.POTION to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5625, 0.75),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5625, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5625, 0.8125)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.5625, 0.5),
|
||||
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.5625, 1.0),
|
||||
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.5625, 0.9375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.5625, 0.4375),
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.5625, 0.5),
|
||||
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.5625, 1.0),
|
||||
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.5625, 0.9375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.STATUS_RESTORE to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.4375, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.4375, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.4375, 0.75)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.4375, 0.875),
|
||||
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.4375, 0.9375),
|
||||
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.4375, 0.4375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.4375, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.4375, 0.375),
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.4375, 0.4375),
|
||||
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.4375, 0.9375)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun getShape(type: StackableItemBlockType, count: Int): VoxelShape = northShapeMap[type]?.let { list -> list[(count - 1).coerceIn(0, list.lastIndex)] } ?: Shapes.block()
|
||||
fun getMaxAmount(type: StackableItemBlockType): Int = min(northShapeMap[type]?.size ?: 1 , MAX_AMOUNT)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.setValue(AMOUNT, 1)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<StackableItemBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(HORIZONTAL_FACING, AMOUNT, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(HORIZONTAL_FACING)
|
||||
val count = state.getValue(AMOUNT)
|
||||
return rotateShape(Direction.NORTH, direction, getShape(type, count))
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return getCollisionShape(state, level, pos, context).singleEncompassing()
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val blockState = context.level.getBlockState(context.clickedPos)
|
||||
return if (blockState.`is`(this)) blockState.setValue(AMOUNT, min(getMaxAmount(type), (blockState.getValue(AMOUNT) + 1)))
|
||||
else super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)
|
||||
}
|
||||
|
||||
override fun canBeReplaced(state: BlockState, useContext: BlockPlaceContext): Boolean {
|
||||
return if (
|
||||
!useContext.isSecondaryUseActive
|
||||
&& useContext.itemInHand.`is`(this.asItem())
|
||||
&& state.getValue(AMOUNT) <= getMaxAmount(type)
|
||||
) true else super.canBeReplaced(state, useContext)
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
|
||||
open class WallAttachedDirectionalBlock(settings: Properties): HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::WallAttachedDirectionalBlock)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out WallAttachedDirectionalBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(HORIZONTAL_FACING, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
var direction = context.clickedFace
|
||||
if (direction === null || direction.axis === Direction.Axis.Y) {
|
||||
direction = context.horizontalDirection.opposite
|
||||
}
|
||||
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
val blockState = this.defaultBlockState()
|
||||
.setValue(HORIZONTAL_FACING, direction)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
|
||||
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
|
||||
return blockState
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return canAttach(level, pos, state.getValue(HORIZONTAL_FACING))
|
||||
}
|
||||
|
||||
fun canAttach(reader: LevelReader, pos: BlockPos, direction: Direction): Boolean {
|
||||
val blockPos = pos.relative(direction.opposite)
|
||||
return reader.getBlockState(blockPos).isFaceSturdy(reader, blockPos, direction)
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class WallAttachedDirectionalShapeBlock(settings: Properties, val width: Int = 16, val height: Int = 16, val depth: Int = 16): WallAttachedDirectionalBlock(settings) {
|
||||
companion object {
|
||||
/**
|
||||
* Calculates a [VoxelShape] based on a center point of (8, 8, 16).
|
||||
*
|
||||
* The resulting shape is centered X horizontally and Y vertically
|
||||
* expanding inwards/North along the Z-axis from the South face (16).
|
||||
*
|
||||
* @param width The width of the shape from 0-16, along the X-axis.
|
||||
* @param height The height of the shape from 0-16 along the Y-axis.
|
||||
* @param depth The depth of the shape from 0-16 along the Z-axis, extending inwards from 16.
|
||||
* @return A VoxelShape.
|
||||
*/
|
||||
fun convertToShape(sizeX: Int, sizeY: Int, sizeZ: Int): VoxelShape {
|
||||
val x = sizeX.coerceIn(0, 16).toDouble()
|
||||
val y = sizeY.coerceIn(0, 16).toDouble()
|
||||
val z = sizeZ.coerceIn(0, 16).toDouble()
|
||||
|
||||
val minX = (8.0 - (x / 2.0)) / 16.0
|
||||
val maxX = (8.0 + (x / 2.0)) / 16.0
|
||||
|
||||
val minY = (8.0 - (y / 2.0)) / 16.0
|
||||
val maxY = (8.0 + (y / 2.0)) / 16.0
|
||||
|
||||
val minZ = (16.0 - z) / 16.0
|
||||
|
||||
return Shapes.box(minX, minY, minZ, maxX, maxY, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<WallAttachedDirectionalShapeBlock> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
propertiesCodec(),
|
||||
Codec.INT.fieldOf("width").forGetter { it.width },
|
||||
Codec.INT.fieldOf("height").forGetter { it.height },
|
||||
Codec.INT.fieldOf("depth").forGetter { it.depth }
|
||||
).apply(instance, { properties, width, height, depth ->
|
||||
WallAttachedDirectionalShapeBlock(properties, width, height, depth)
|
||||
})
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return rotateShape(Direction.NORTH, direction, convertToShape(width, height, depth))
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.StackableItemBlock.Companion.AMOUNT
|
||||
import com.cobblemon.mod.common.block.StackableItemBlock.Companion.MAX_AMOUNT
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import kotlin.math.min
|
||||
|
||||
class WallAttachedStackableItemBlock(settings: Properties, val type: WallStackableItemBlockType): WallAttachedDirectionalBlock(settings) {
|
||||
enum class WallStackableItemBlockType: StringRepresentable {
|
||||
TAG;
|
||||
|
||||
override fun getSerializedName(): String = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum(::values)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<WallAttachedStackableItemBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
WallStackableItemBlockType.CODEC.fieldOf("type").forGetter(WallAttachedStackableItemBlock::type)
|
||||
).apply(it, ::WallAttachedStackableItemBlock) }
|
||||
|
||||
val northShapeMap: Map<WallStackableItemBlockType, List<VoxelShape>> = mapOf(
|
||||
WallStackableItemBlockType.TAG to listOf(
|
||||
Shapes.box(0.375, 0.25, 0.9375, 0.625, 0.6875, 1.0),
|
||||
Shapes.box(0.375, 0.0625, 0.9375, 1.0, 0.6875, 1.0),
|
||||
Shapes.box(0.0, 0.0625, 0.9375, 1.0, 0.9375, 1.0),
|
||||
Shapes.box(0.0, 0.0625, 0.9375, 1.0, 1.0, 1.0),
|
||||
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
|
||||
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
|
||||
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0),
|
||||
Shapes.box(0.0, 0.0, 0.9375, 1.0, 1.0, 1.0)
|
||||
)
|
||||
)
|
||||
|
||||
fun getShape(type: WallStackableItemBlockType, count: Int): VoxelShape = northShapeMap[type]?.let { list -> list[(count - 1).coerceIn(0, list.lastIndex)] } ?: Shapes.block()
|
||||
fun getMaxAmount(type: WallStackableItemBlockType): Int = min(northShapeMap[type]?.size ?: 1 , MAX_AMOUNT)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.setValue(AMOUNT, 1)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<WallAttachedStackableItemBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(AMOUNT)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(HORIZONTAL_FACING)
|
||||
val count = state.getValue(AMOUNT)
|
||||
return rotateShape(Direction.NORTH, direction, getShape(type, count))
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val blockState = context.level.getBlockState(context.clickedPos)
|
||||
return if (blockState.`is`(this)) blockState.setValue(AMOUNT, min(getMaxAmount(type), (blockState.getValue(AMOUNT) + 1)))
|
||||
else super.getStateForPlacement(context)
|
||||
}
|
||||
|
||||
override fun canBeReplaced(state: BlockState, useContext: BlockPlaceContext): Boolean {
|
||||
return if (
|
||||
!useContext.isSecondaryUseActive
|
||||
&& useContext.itemInHand.`is`(this.asItem())
|
||||
&& state.getValue(AMOUNT) <= getMaxAmount(type)
|
||||
) true else super.canBeReplaced(state, useContext)
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.pasture.PastureLinkManager
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.text.italicise
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.textClickHandlers
|
||||
import com.cobblemon.mod.common.block.PastureBlock
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
@@ -109,10 +110,10 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
for (tetheredPokemon in tetheredPokemonList) {
|
||||
val pokemon = tetheredPokemon.getPokemon()!!
|
||||
if (pokemon.currentFullness > 0) {
|
||||
pokemon.tickMetabolism(1)
|
||||
pokemon.tickMetabolism()
|
||||
}
|
||||
if (pokemon.interactionCooldowns.any()) {
|
||||
pokemon.tickInteractionCooldown(1)
|
||||
pokemon.tickInteractionCooldown()
|
||||
}
|
||||
}
|
||||
blockEntity.togglePastureOn(blockEntity.getInRangeViewerCount(world, blockEntity.blockPos) > 0)
|
||||
|
||||
@@ -192,6 +192,7 @@ object CobblemonClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
BattleMusicController.onTick()
|
||||
ClientPlayerIcon.onTick()
|
||||
PartyOverlayDataControl.tick(event.client.isPaused)
|
||||
}
|
||||
@@ -319,48 +320,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.PINK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.RED_CAMPFIRE_POT,
|
||||
CobblemonBlocks.WHITE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.YELLOW_CAMPFIRE_POT,
|
||||
CobblemonBlocks.WHITE_PLAQUE,
|
||||
CobblemonBlocks.LIGHT_GRAY_PLAQUE,
|
||||
CobblemonBlocks.GRAY_PLAQUE,
|
||||
CobblemonBlocks.BLACK_PLAQUE,
|
||||
CobblemonBlocks.BROWN_PLAQUE,
|
||||
CobblemonBlocks.RED_PLAQUE,
|
||||
CobblemonBlocks.ORANGE_PLAQUE,
|
||||
CobblemonBlocks.YELLOW_PLAQUE,
|
||||
CobblemonBlocks.LIME_PLAQUE,
|
||||
CobblemonBlocks.GREEN_PLAQUE,
|
||||
CobblemonBlocks.CYAN_PLAQUE,
|
||||
CobblemonBlocks.LIGHT_BLUE_PLAQUE,
|
||||
CobblemonBlocks.BLUE_PLAQUE,
|
||||
CobblemonBlocks.PURPLE_PLAQUE,
|
||||
CobblemonBlocks.MAGENTA_PLAQUE,
|
||||
CobblemonBlocks.PINK_PLAQUE,
|
||||
CobblemonBlocks.BLUNDER_POLICY,
|
||||
CobblemonBlocks.WEAKNESS_POLICY,
|
||||
CobblemonBlocks.POTION,
|
||||
CobblemonBlocks.SUPER_POTION,
|
||||
CobblemonBlocks.HYPER_POTION,
|
||||
CobblemonBlocks.MAX_POTION,
|
||||
CobblemonBlocks.FULL_RESTORE,
|
||||
CobblemonBlocks.ANTIDOTE,
|
||||
CobblemonBlocks.AWAKENING,
|
||||
CobblemonBlocks.BURN_HEAL,
|
||||
CobblemonBlocks.ICE_HEAL,
|
||||
CobblemonBlocks.PARALYZE_HEAL,
|
||||
CobblemonBlocks.FULL_HEAL,
|
||||
CobblemonBlocks.ETHER,
|
||||
CobblemonBlocks.MAX_ETHER,
|
||||
CobblemonBlocks.ELIXIR,
|
||||
CobblemonBlocks.MAX_ELIXIR,
|
||||
CobblemonBlocks.DIRE_HIT,
|
||||
CobblemonBlocks.GUARD_SPEC,
|
||||
CobblemonBlocks.X_ACCURACY,
|
||||
CobblemonBlocks.X_ATTACK,
|
||||
CobblemonBlocks.X_DEFENSE,
|
||||
CobblemonBlocks.X_SP_ATK,
|
||||
CobblemonBlocks.X_SP_DEF,
|
||||
CobblemonBlocks.X_SPEED
|
||||
CobblemonBlocks.YELLOW_CAMPFIRE_POT
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
|
||||
@@ -11,11 +11,10 @@ package com.cobblemon.mod.common.client.battle
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.ForcePassActionResponse
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.battles.PassActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
@@ -62,15 +61,8 @@ class ClientBattle(
|
||||
if (index != -1) {
|
||||
while(index < pendingActionRequests.size) {
|
||||
val request = pendingActionRequests[index]
|
||||
if (request.response != PassActionResponse && request.response !is ForcePassActionResponse) {
|
||||
val response = request.response
|
||||
// If we're cancelling a turn that used a gimmick, we re-enable the ability for that gimmick to be used.
|
||||
if (response is MoveActionResponse && response.gimmickID != null) {
|
||||
val gimmick = ShowdownMoveset.Gimmick.entries.find { it.id == response.gimmickID }
|
||||
pendingActionRequests.forEach { it.moveSet?.pendingGimmickUsedThisTurn?.remove(gimmick) }
|
||||
}
|
||||
if (request.response != PassActionResponse && request.response !is ForcePassActionResponse)
|
||||
request.response = null
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class BattleMoveSelection(
|
||||
var moveTiles = baseTiles
|
||||
|
||||
val backButton = BattleBackButton(x - 11F, Minecraft.getInstance().window.guiScaledHeight - 22F)
|
||||
val gimmickButtons = moveSet.getGimmicks().filter { it !in moveSet.pendingGimmickUsedThisTurn }.mapIndexed { index, gimmick ->
|
||||
val gimmickButtons = moveSet.getGimmicks().mapIndexed { index, gimmick ->
|
||||
val initOff = BattleBackButton.WIDTH * 0.65F
|
||||
val xOff = initOff + BattleGimmickButton.SPACING * index
|
||||
BattleGimmickButton.create(gimmick, this, backButton.x + xOff, backButton.y)
|
||||
|
||||
+1
-9
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.InBattleGimmickMove
|
||||
import com.cobblemon.mod.common.battles.InBattleMove
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.battles.ShowdownMoveset
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
@@ -128,7 +127,7 @@ class BattleTargetSelection(
|
||||
private val isMultiTarget = multiTargetList?.firstOrNull { it.getPNX() == target.getPNX() } != null
|
||||
private val isCurrentPokemon = request.activePokemon.battlePokemon?.uuid == target.battlePokemon!!.uuid
|
||||
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget, gimmickID)
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(targetSelection.move.id, responseTarget)
|
||||
val state = FloatingState()
|
||||
|
||||
open val selectable: Boolean get() = isMultiTarget || (responseTarget != null)
|
||||
@@ -312,13 +311,6 @@ class BattleTargetSelection(
|
||||
fun onClick() {
|
||||
if (!selectable) return
|
||||
targetSelection.playDownSound(Minecraft.getInstance().soundManager)
|
||||
// If a gimmick is used, remove the ability to use it on any pending action requests ahead of it
|
||||
if (response.gimmickID != null) {
|
||||
val gimmick = ShowdownMoveset.Gimmick.entries.first { it.id == response.gimmickID }
|
||||
CobblemonClient.battle?.pendingActionRequests?.forEach {
|
||||
it.moveSet?.pendingGimmickUsedThisTurn?.add(gimmick)
|
||||
}
|
||||
}
|
||||
targetSelection.battleGUI.selectAction(targetSelection.request, response)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -143,18 +143,18 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
}
|
||||
|
||||
private fun canAttemptDismount(player: LocalPlayer, selectedPartyPokemon: Pokemon?): Boolean {
|
||||
val vehicle = player.vehicle as? PokemonEntity ?: return false
|
||||
|
||||
if (player != vehicle.controllingPassenger) return true
|
||||
|
||||
if (player.vehicle !is PokemonEntity) return false
|
||||
val vehicle = player.vehicle as PokemonEntity
|
||||
if (player != vehicle.controllingPassenger) {
|
||||
return true
|
||||
}
|
||||
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
|
||||
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
|
||||
|
||||
if (!Cobblemon.config.enableInFlightDismounting && isAirRide && hasLandRide) {
|
||||
return false
|
||||
return if (isAirRide && hasLandRide) {
|
||||
false
|
||||
} else {
|
||||
vehicle.pokemon.uuid == selectedPartyPokemon?.uuid
|
||||
}
|
||||
|
||||
return vehicle.pokemon.uuid == selectedPartyPokemon?.uuid
|
||||
}
|
||||
|
||||
private fun isRidingPokemon(player: LocalPlayer, ignoreControlling: Boolean = false): Boolean {
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import net.minecraft.world.phys.Vec3
|
||||
|
||||
object CalculateSeatPositionsHandler : ClientNetworkPacketHandler<CalculateSeatPositionsPacket> {
|
||||
private const val FORMAT = "%.2f"
|
||||
private const val SEAT_PREFIX = "seat_"
|
||||
private const val SEAT_PREFIX = "seat"
|
||||
private const val SIMULATION_PERIOD_TICKS = 15 * 20F
|
||||
|
||||
class SeatRanges(
|
||||
|
||||
+6
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.client.sound.BattleMusicController
|
||||
import com.cobblemon.mod.common.client.sound.instances.BattleMusicInstance
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.ost.BattleThemes
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
|
||||
@@ -26,8 +27,12 @@ object BattleMusicHandler : ClientNetworkPacketHandler<BattleMusicPacket> {
|
||||
override fun handle(packet: BattleMusicPacket, client: Minecraft) {
|
||||
val soundManager = client.soundManager
|
||||
val currMusic = BattleMusicController.music
|
||||
|
||||
// TODO to support legacy behavior, use ResourceLocation for BattleTrackConfig THEN SoundEvent
|
||||
|
||||
val newMusic = packet.music?.let {
|
||||
val event = SoundEvent.createVariableRangeEvent(it)
|
||||
val location = BattleThemes.getByIdentifier(it)?.dispatchTrack()? ?: it
|
||||
val event = SoundEvent.createVariableRangeEvent(location)
|
||||
BattleMusicInstance(event, packet.volume, packet.pitch)
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -160,7 +160,6 @@ class DisplayCaseRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntit
|
||||
stack.item is BedItem -> PositioningType.BED
|
||||
stack.item is BannerItem -> PositioningType.BANNER
|
||||
stack.`is`(CobblemonItemTags.POKE_BALLS) -> PositioningType.POKE_BALL
|
||||
stack.`is`(CobblemonItemTags.PLAQUES) -> PositioningType.ITEM_MODEL
|
||||
stack.item is PokedexItem -> PositioningType.POKEDEX
|
||||
stack.item == CobblemonItems.RELIC_COIN_POUCH -> PositioningType.COIN_POUCH
|
||||
stack.item == CobblemonItems.PASTURE -> PositioningType.PASTURE
|
||||
|
||||
+2
-6
@@ -39,11 +39,7 @@ class PoseAdapter(
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): Pose {
|
||||
val model = modelFinder()
|
||||
val obj = json as JsonObject
|
||||
val pose = try {
|
||||
JsonPose(model, obj)
|
||||
} catch (e: Exception) {
|
||||
throw IllegalArgumentException("Failed to deserialize pose from JSON: $json", e)
|
||||
}
|
||||
val pose = JsonPose(model, obj)
|
||||
|
||||
val conditionsList = mutableListOf<(PosableState) -> Boolean>()
|
||||
|
||||
@@ -104,4 +100,4 @@ class PoseAdapter(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
package com.cobblemon.mod.common.client.sound
|
||||
|
||||
import com.cobblemon.mod.common.client.sound.instances.BattleMusicInstance
|
||||
import com.cobblemon.mod.common.ost.BattleMusicCue
|
||||
import com.cobblemon.mod.common.ost.BattleThemeConfig
|
||||
import com.cobblemon.mod.common.util.pauseSounds
|
||||
import com.cobblemon.mod.common.util.resumeSounds
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -23,6 +25,15 @@ import net.minecraft.sounds.SoundEvents
|
||||
*/
|
||||
object BattleMusicController {
|
||||
|
||||
/** The active [BattleThemeConfig] for dispatching [BattleMusicInstance]s and subscribing to [BattleMusicCue]s. */
|
||||
private var config: BattleThemeConfig? = null
|
||||
|
||||
/** The queued [BattleMusicCue]. */
|
||||
private var queuedCue: BattleMusicCue? = null
|
||||
|
||||
/** The active [BattleMusicCue]. */
|
||||
private var activeCue: BattleMusicCue? = null
|
||||
|
||||
/** The last [BattleMusicInstance] played. */
|
||||
var music = BattleMusicInstance(SoundEvents.EMPTY, 0.0F, 0.0F)
|
||||
private set
|
||||
@@ -56,4 +67,13 @@ object BattleMusicController {
|
||||
/** Fade out and end the current [BattleMusicInstance]. */
|
||||
fun endMusic() = music.setFade()
|
||||
|
||||
/** Processes the [queuedCue]. */
|
||||
private fun processCue(cue: BattleMusicCue) {
|
||||
|
||||
}
|
||||
|
||||
/** */
|
||||
fun onTick() {
|
||||
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.cobblemon.mod.common.client.sound.streams
|
||||
|
||||
import net.minecraft.client.sounds.AudioStream
|
||||
import java.nio.ByteBuffer
|
||||
import javax.sound.sampled.AudioFormat
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
/**
|
||||
* An [AudioStream] composed of multiple streams stitched together.
|
||||
*
|
||||
* Bytes of audio are read according to the active stream's implementation of [AudioStream.read].
|
||||
*
|
||||
* @param streams The [AudioStream]s to chain sequentially. Each stream must share the same [AudioFormat].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 19th, 2025
|
||||
*/
|
||||
class CompoundAudioStream @Throws(IllegalArgumentException::class) constructor(vararg streams: AudioStream): AudioStream {
|
||||
|
||||
private val streams = streams.iterator()
|
||||
|
||||
/** The [AudioStream] currently playing. */
|
||||
private var runningStream: AudioStream = this.streams.next()
|
||||
|
||||
private val format = this.runningStream.format
|
||||
|
||||
override fun getFormat() = format
|
||||
|
||||
init {
|
||||
/* Enqueuing buffers of different formats to the same source is not permissible in OpenAL 1.1 standard. */
|
||||
streams.filterNot { runningStream.format.matches(it.format) }.takeIf { it.isNotEmpty() }?.let { incompatibleStreams ->
|
||||
val message = StringBuilder("Cannot compose CompoundAudioStream from streams of different formats:\n${runningStream.format}")
|
||||
incompatibleStreams.forEach {
|
||||
message.append("\n${it.format}")
|
||||
}
|
||||
throw IllegalArgumentException(message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads [size] bytes of PCM, from the current [runningStream], for 1 second worth of audio. */
|
||||
override fun read(size: Int): ByteBuffer? {
|
||||
var buffer = this.runningStream.read(size)
|
||||
if (!buffer.hasRemaining() && this.streams.hasNext()) {
|
||||
this.runningStream.close()
|
||||
this.runningStream = this.streams.next()
|
||||
buffer = this.runningStream.read(size)
|
||||
}
|
||||
return buffer // it's fine if the buffer is not completely full - the stream will sound seamless
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
this.runningStream.close()
|
||||
while (streams.hasNext()) streams.next().close()
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
package com.cobblemon.mod.common.client.sound.streams
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.mojang.blaze3d.audio.Channel
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.client.sounds.AudioStream
|
||||
import net.minecraft.client.sounds.LoopingAudioStream
|
||||
import org.lwjgl.BufferUtils
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.sound.sampled.AudioFormat
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* An [AudioStream] divided into an intro substream and a loop substream, determined by loop points provided with the Sound resource.
|
||||
*
|
||||
* Similar to [LoopingAudioStream], the loop will play indefinitely until stopped.
|
||||
*
|
||||
* @param provider Provider for creating the internal [AudioStream] that decodes the audio.
|
||||
* @param primaryInputStream The initial [InputStream] of the encoded audio to play.
|
||||
* @param loopStart The starting point of the main audio loop in SAMPLES. If 0, loop will run from start until the [loopEnd].
|
||||
* @param loopEnd The ending point of the main audio loop in SAMPLES. If 0, loop will run to completion.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since October 19th, 2025
|
||||
*/
|
||||
class LoopPointAudioStream @Throws(IllegalArgumentException::class) constructor(
|
||||
private val provider: LoopingAudioStream.AudioStreamProvider,
|
||||
primaryInputStream: InputStream,
|
||||
loopStart: Int = 0,
|
||||
loopEnd: Int = 0
|
||||
): AudioStream {
|
||||
|
||||
private val DEBUG_FLAG = true
|
||||
|
||||
/** Amount of buffers of decoded PCM, from the start of the loop, to cache. */
|
||||
private val CACHE_LIMIT = Channel.QUEUED_BUFFER_COUNT // CACHE_LIMIT matches the number of buffers pre-read before feeding to OpenAL
|
||||
|
||||
/** Buffers of PCM that start the loop. */
|
||||
private val bufferCache = mutableListOf<ByteBuffer>() // cache size: 4 buffers, each of size ~176400 (70kb of cache) which is fine
|
||||
|
||||
/** The total number of bytes saved in the [bufferCache]. */
|
||||
private var cacheSize = 0
|
||||
|
||||
/** The number of buffers read from the [bufferCache] */
|
||||
private var bufferCounter = 0
|
||||
|
||||
/** The bytes bytween the end of the cache and the start of the rewinded [decoderStream]. */
|
||||
private var preliminaryBuffer: ByteBuffer? = null
|
||||
|
||||
/** Whether the [preliminaryBuffer] has been read on this pass. */
|
||||
private var readPrem: Boolean = false
|
||||
|
||||
/** The OGG Vorbis. This is encoded audio. */
|
||||
private lateinit var encodedStream: BufferedInputStream
|
||||
|
||||
/** The Vorbis decoder. This decodes PCM from the [encodedStream] on calls to [AudioStream.read]. */
|
||||
private lateinit var decoderStream: AudioStream
|
||||
|
||||
/** The starting point of the loop in BYTES. */
|
||||
private var loopStart by Delegates.notNull<Int>()
|
||||
|
||||
/** The ending point of the loop in BYTES. */
|
||||
private var loopEnd by Delegates.notNull<Int>()
|
||||
|
||||
/** Whether this is the first pass of the [encodedStream] and we haven't looped yet. */
|
||||
private var firstPass = true
|
||||
|
||||
/** Whether [decoderStream] has been rewinded to [loopStart]. */
|
||||
private var loopPrepared = false
|
||||
|
||||
/** The total number of bytes that have been read from the decoded stream. */
|
||||
private var runningRead = 0
|
||||
|
||||
init {
|
||||
if (loopStart > 0 && loopEnd > 0 && loopEnd < loopStart) throw IllegalArgumentException("Invalid loop points: non-zero end precedes non-zero start")
|
||||
if (loopStart < 0 || loopEnd < 0) throw IllegalArgumentException("Invalid loop points: only non-negative values allowed")
|
||||
|
||||
this.encodedStream = BufferedInputStream(primaryInputStream)
|
||||
this.encodedStream.mark(Int.Companion.MAX_VALUE)
|
||||
this.buildDecoderStream()
|
||||
|
||||
this.loopStart = this.samplesToBytes(loopStart)
|
||||
this.loopEnd = this.samplesToBytes(loopEnd)
|
||||
}
|
||||
|
||||
override fun getFormat(): AudioFormat? {
|
||||
return this.decoderStream.format
|
||||
}
|
||||
|
||||
/** Reads 1 second worth of PCM (bytes) for an OpenAL buffer (this will be AT LEAST 176400 bytes for stereo audio formats) */
|
||||
override fun read(size: Int): ByteBuffer? {
|
||||
|
||||
// if decoderStream has not finished rewinding, there's a problem
|
||||
if (!this.firstPass && !this.loopPrepared) {
|
||||
Cobblemon.LOGGER.warn("Decoder not rewinded!")
|
||||
}
|
||||
|
||||
this.debug("read " + size + " | runningRead: " + this.runningRead + " | loopStart: " + this.loopStart + " | loopEnd: " + this.loopEnd)
|
||||
|
||||
val buffer = this.getCachedOrRawBuffer(size)
|
||||
val sizeAfterRead = this.runningRead + buffer.remaining()
|
||||
|
||||
// if this read reaches the end loop point
|
||||
if (loopEnd > 0 && sizeAfterRead >= this.loopEnd) {
|
||||
val remainderBeforeEnd = this.loopEnd - this.runningRead
|
||||
// limit buffer if it goes past the loop point (cached buffers don't apply as they are always within the loop points)
|
||||
if (buffer.remaining() > remainderBeforeEnd)
|
||||
{
|
||||
this.debug("limit and reset read")
|
||||
buffer.limit(remainderBeforeEnd)
|
||||
}
|
||||
else {
|
||||
this.debug("reset read")
|
||||
}
|
||||
this.resetLoop()
|
||||
}
|
||||
// if loopEnd is 0 and the entire stream is read, or exaggerated and the stream is exhausted before reaching the point
|
||||
else if (!buffer.hasRemaining()) {
|
||||
this.debug("exhaustive read")
|
||||
this.resetLoop()
|
||||
}
|
||||
// standard read
|
||||
else {
|
||||
this.debug("standard read")
|
||||
this.runningRead += buffer.remaining();
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a [ByteBuffer] of varying size from the [bufferCache] if this is not the [firstPass] and a cached buffer
|
||||
* is available, or reads approximately [size] bytes from the [decoderStream].
|
||||
*/
|
||||
private fun getCachedOrRawBuffer(size: Int): ByteBuffer {
|
||||
val cacheExists = CACHE_LIMIT > 0 && !this.bufferCache.isEmpty() && this.bufferCounter < this.bufferCache.size
|
||||
|
||||
// get buffer from cache
|
||||
if (cacheExists && !this.firstPass) {
|
||||
val toReturn = this.bufferCache.get(this.bufferCounter++)
|
||||
this.debug("get cached buffer: " + toReturn.remaining())
|
||||
return toReturn
|
||||
}
|
||||
// TODO coalesce this with bufferCache
|
||||
else if (!this.readPrem && this.preliminaryBuffer != null && !this.firstPass) {
|
||||
this.readPrem = true
|
||||
val toReturn = this.preliminaryBuffer!!
|
||||
this.debug("read prem buffer: " + toReturn.remaining())
|
||||
return toReturn
|
||||
}
|
||||
// decode a buffer of approximate size
|
||||
else {
|
||||
val toReturn = this.decoderStream.read(size)
|
||||
this.debug("read raw: " + toReturn.remaining())
|
||||
this.cacheBuffer(toReturn)
|
||||
return toReturn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches [buffer] in the [bufferCache] during the [firstPass] if the [CACHE_LIMIT] has not been reached and the buffer
|
||||
* contains bytes within the [loopStart] and the [loopEnd] boundaries.
|
||||
*/
|
||||
private fun cacheBuffer(buffer: ByteBuffer) {
|
||||
val canCacheBuffer = CACHE_LIMIT > 0 && loopStart > 0 && this.bufferCache.size < CACHE_LIMIT && this.firstPass
|
||||
val canStartCaching = this.runningRead + buffer.remaining() > this.loopStart
|
||||
if (!canCacheBuffer || !canStartCaching) return
|
||||
|
||||
// offset used - so that only the tail of a buffer that contains the loopStart point is cached
|
||||
val remainderBeforeStart = Math.max(this.loopStart - this.runningRead, 0)
|
||||
var remainderBeforeEnd = 0
|
||||
|
||||
// only cache the head of a buffer that goes past the loopEnd point
|
||||
if (loopEnd > 0 && buffer.remaining() + this.runningRead > this.loopEnd) {
|
||||
remainderBeforeEnd = this.loopEnd - this.runningRead - remainderBeforeStart
|
||||
// NOTE: read will resetLoop and stop caching if CACHE_LIMIT has not been reached but loopEnd has
|
||||
}
|
||||
// otherwise, cache the entire remainder of the buffer
|
||||
else {
|
||||
remainderBeforeEnd = buffer.remaining() - remainderBeforeStart
|
||||
}
|
||||
|
||||
if (remainderBeforeEnd <= 0) return
|
||||
|
||||
// copy to avoid mutating provided buffer
|
||||
val buffed = BufferUtils.createByteBuffer(remainderBeforeEnd)
|
||||
buffed.put(0, buffer, remainderBeforeStart, remainderBeforeEnd)
|
||||
buffed.rewind()
|
||||
|
||||
this.debug("cache buffer | remainderBeforeStart: " + remainderBeforeStart + " | remainderBeforeEnd: " + remainderBeforeEnd + " | size: " + buffed.remaining())
|
||||
|
||||
this.bufferCache.add(buffed)
|
||||
this.cacheSize += buffed.remaining()
|
||||
|
||||
this.debug("cacheSize: " + cacheSize + " bufferCacheSize: " + this.bufferCache.size)
|
||||
}
|
||||
|
||||
/** Converts PCM samples to bytes. */
|
||||
private fun samplesToBytes(sampleAmount: Int): Int {
|
||||
val format = this.format ?: return 0
|
||||
return sampleAmount * format.getSampleSizeInBits() / 8 * format.getChannels()
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind [decoderStream] to start from [loopStart].
|
||||
*
|
||||
* NOTE: Assumes that [bufferCache] is finalized.
|
||||
*/
|
||||
private fun resetLoop() {
|
||||
// reset state info
|
||||
this.loopPrepared = false
|
||||
this.runningRead = this.loopStart
|
||||
this.bufferCounter = 0
|
||||
this.firstPass = false
|
||||
|
||||
this.debug("reset")
|
||||
|
||||
// if the entire loop is small enough to fit the cache, skip rewinding the decoderStream
|
||||
if (this.bufferCache.size > 0 && this.bufferCache.size < CACHE_LIMIT) {
|
||||
this.debug("comprehensive cache")
|
||||
this.loopPrepared = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Seeking is implemented agnostic of the OGG Vorbis decoder. Seek to the loop start by decoding PCM in bulk from
|
||||
the beginning of the OGG Vorbis up until the desired sample offset is reached. Then for playing the loop, continue
|
||||
streaming decoded PCM into OpenAL buffers.
|
||||
*/
|
||||
this.decoderStream.close()
|
||||
this.encodedStream.reset()
|
||||
this.buildDecoderStream()
|
||||
|
||||
// if loopStart is 0, there's no cache, so loop starts by reading from the new decoderStream
|
||||
if (this.bufferCache.size == 0) {
|
||||
this.debug("nonexistent cache")
|
||||
this.loopPrepared = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
After the first pass, encodedStream will have the entire Vorbis OGG buffered and there won't be any subsequent reads
|
||||
from disk. However, there's still latency in decoding a lot of PCM. This needs to be done asynchronously, otherwise
|
||||
the client will momentarily freeze each time the loop resets. In the unlikely case that the decoderStream is not
|
||||
rewinded by the time the Channel needs to read a buffer, it will be read from bufferCache.
|
||||
*/
|
||||
CompletableFuture.supplyAsync<Any?>({
|
||||
try {
|
||||
val decoderStart = this.loopStart + this.cacheSize
|
||||
val buffer = decoderStream.read(decoderStart)
|
||||
|
||||
// because the deecoderStream will only read an approximation, need to account for additional bytes
|
||||
if (this.preliminaryBuffer == null && buffer.remaining() > decoderStart) {
|
||||
this.debug("rewind buffer size: " + buffer.remaining() + " overshot by: " + (buffer.remaining() - decoderStart))
|
||||
this.preliminaryBuffer = BufferUtils.createByteBuffer(buffer.remaining() - decoderStart)
|
||||
this.preliminaryBuffer!!.put(0, buffer, decoderStart, buffer.remaining() - decoderStart)
|
||||
this.preliminaryBuffer!!.rewind()
|
||||
// decoderStream.read is deterministic, so this should only be set once
|
||||
}
|
||||
this.readPrem = false
|
||||
this.loopPrepared = true
|
||||
this.debug("decoderStream rewinded")
|
||||
return@supplyAsync null
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}, Util.nonCriticalIoPool())
|
||||
}
|
||||
|
||||
private fun buildDecoderStream() {
|
||||
this.decoderStream = provider.create(LoopingAudioStream.NoCloseBuffer(this.encodedStream))
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
this.decoderStream.close()
|
||||
this.encodedStream.close() // closes the InputStream used
|
||||
}
|
||||
|
||||
private fun debug(log: String) {
|
||||
if (this.DEBUG_FLAG) Cobblemon.LOGGER.error(log)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -206,7 +206,7 @@ fun generateAdditionalRideBoostsTooltip(stack: ItemStack): MutableList<Component
|
||||
val resultLines = mutableListOf<Component>()
|
||||
resultLines.add(rideBoostSeasoningHeader)
|
||||
|
||||
for ((stat, value) in boosts.toSortedMap()) {
|
||||
for ((stat, value) in boosts) {
|
||||
val statName = stat.displayName.also { it.style = it.style.withColor(stat.flavour.colour) }
|
||||
val valueText = if (value < 0) {
|
||||
Component.literal("$value").red()
|
||||
|
||||
@@ -329,9 +329,6 @@ class CobblemonConfig {
|
||||
@CobblemonConfigField(Category.Riding, lang = "remember_riding_camera", CLIENT)
|
||||
var rememberRidingCamera = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "enable_in_flight_dismounting", CLIENT)
|
||||
var enableInFlightDismounting = false
|
||||
|
||||
@CobblemonConfigField(Category.Debug, lang = "enable_debug_keys", CLIENT)
|
||||
var enableDebugKeys = false
|
||||
|
||||
|
||||
@@ -269,6 +269,9 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
|
||||
// If we can fly and we're airborne, return the current Y position
|
||||
return vec.y
|
||||
}
|
||||
if (world.getBlockState(blockPos).block == CobblemonBlocks.SACCHARINE_LEAVES && pather.canPathThroughSaccLeaves()) {
|
||||
return vec.y + 0.5
|
||||
}
|
||||
return if ((canFloat()) && blockGetter.getFluidState(blockPos).`is`(FluidTags.WATER)) vec.y + 0.5 else super.getGroundY(vec)
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerEntity
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.tags.DamageTypeTags.BYPASSES_INVULNERABILITY
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
@@ -605,7 +604,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Posabl
|
||||
}
|
||||
|
||||
override fun isPushable() = isMovable ?: npc.isMovable
|
||||
override fun isInvulnerableTo(source: DamageSource) = (isInvulnerable ?: npc.isInvulnerable) && !source.`is`(BYPASSES_INVULNERABILITY)
|
||||
override fun isInvulnerableTo(source: DamageSource) = isInvulnerable ?: npc.isInvulnerable
|
||||
override fun canBeLeashed() = isLeashable ?: npc.isLeashable
|
||||
override fun canBeHitByProjectile() = allowProjectileHits ?: npc.allowProjectileHits
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import juuxel.adorn.block.variant.CompatBlockVariantSet
|
||||
|
||||
class AdornCompatibility : CompatBlockVariantSet() {
|
||||
override fun getWoodVariants(): List<BlockVariant?>? {
|
||||
return this.createVariants({ BlockVariant.Wood(it) }, "apricorn", "saccharine")
|
||||
return this.createVariants({ BlockVariant.Wood(it) }, "apricorn")
|
||||
}
|
||||
|
||||
override fun getModId(): String? {
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BagItemActionResponse
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleRules
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
@@ -42,7 +41,7 @@ interface BagItemLike {
|
||||
fun handleInteraction(player: ServerPlayer, battlePokemon: BattlePokemon, stack: ItemStack): Boolean {
|
||||
val battle = battlePokemon.actor.battle
|
||||
val bagItem = getBagItem(stack) ?: return false
|
||||
if (!battlePokemon.actor.canFitForcedAction() || battle.format.ruleSet.contains(BattleRules.BAG_CLAUSE)) {
|
||||
if (!battlePokemon.actor.canFitForcedAction()) {
|
||||
player.sendSystemMessage(battleLang("bagitem.cannot").red())
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -11,13 +11,12 @@ package com.cobblemon.mod.common.item.battle
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
|
||||
class DireHitItem(block: Block) : ItemNameBlockItem(block, Properties()), SimpleBagItemLike {
|
||||
class DireHitItem : CobblemonItem(Properties()), SimpleBagItemLike {
|
||||
override val bagItem = object : BagItem {
|
||||
override val itemName = "item.cobblemon.dire_hit"
|
||||
override val returnItem = Items.AIR
|
||||
|
||||
@@ -11,12 +11,11 @@ package com.cobblemon.mod.common.item.battle
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
class GuardSpecItem(block: Block) : ItemNameBlockItem(block, Properties()), SimpleBagItemLike {
|
||||
class GuardSpecItem : CobblemonItem(Properties()), SimpleBagItemLike {
|
||||
override val bagItem = object : BagItem {
|
||||
override val itemName = "item.cobblemon.guard_spec"
|
||||
override val returnItem = Items.AIR
|
||||
|
||||
@@ -12,12 +12,11 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
class XStatItem(val stat: Stat, stages: Int = 2, block: Block) : ItemNameBlockItem(block, Properties()), SimpleBagItemLike {
|
||||
class XStatItem(val stat: Stat, stages: Int = 2) : CobblemonItem(Properties()), SimpleBagItemLike {
|
||||
override val bagItem = object : BagItem {
|
||||
override val itemName = "item.cobblemon.x_${stat.identifier.path}"
|
||||
override val returnItem = Items.AIR
|
||||
|
||||
@@ -66,7 +66,6 @@ object CobblemonItemGroups {
|
||||
@JvmStatic val EVOLUTION_ITEMS get() = BuiltInRegistries.CREATIVE_MODE_TAB.get(EVOLUTION_ITEMS_KEY)
|
||||
|
||||
@JvmStatic val BUILDING_BLOCKS_INJECTIONS = this.inject(ResourceKey.create(BuiltInRegistries.CREATIVE_MODE_TAB.key(), ResourceLocation.parse("building_blocks")), this::blocksInjections)
|
||||
@JvmStatic val COLORED_BLOCKS_INJECTIONS = this.inject(ResourceKey.create(BuiltInRegistries.CREATIVE_MODE_TAB.key(), ResourceLocation.parse("colored_blocks")), this::coloredBlocksInjections)
|
||||
@JvmStatic val FOOD_INJECTIONS = this.inject(ResourceKey.create(BuiltInRegistries.CREATIVE_MODE_TAB.key(), ResourceLocation.parse("food_and_drinks")), this::foodInjections)
|
||||
@JvmStatic val TOOLS_AND_UTILITIES_INJECTIONS = this.inject(ResourceKey.create(BuiltInRegistries.CREATIVE_MODE_TAB.key(), ResourceLocation.parse("tools_and_utilities")), this::toolsAndUtilitiesInjections)
|
||||
@JvmStatic val INGREDIENTS_INJECTIONS = this.inject(ResourceKey.create(BuiltInRegistries.CREATIVE_MODE_TAB.key(), ResourceLocation.parse("ingredients")), this::ingredientsInjections)
|
||||
@@ -382,23 +381,6 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.THUNDER_STONE_BLOCK)
|
||||
entries.accept(CobblemonItems.WATER_STONE_BLOCK)
|
||||
|
||||
entries.accept(CobblemonItems.WHITE_PLAQUE)
|
||||
entries.accept(CobblemonItems.LIGHT_GRAY_PLAQUE)
|
||||
entries.accept(CobblemonItems.GRAY_PLAQUE)
|
||||
entries.accept(CobblemonItems.BLACK_PLAQUE)
|
||||
entries.accept(CobblemonItems.BROWN_PLAQUE)
|
||||
entries.accept(CobblemonItems.RED_PLAQUE)
|
||||
entries.accept(CobblemonItems.ORANGE_PLAQUE)
|
||||
entries.accept(CobblemonItems.YELLOW_PLAQUE)
|
||||
entries.accept(CobblemonItems.LIME_PLAQUE)
|
||||
entries.accept(CobblemonItems.GREEN_PLAQUE)
|
||||
entries.accept(CobblemonItems.CYAN_PLAQUE)
|
||||
entries.accept(CobblemonItems.LIGHT_BLUE_PLAQUE)
|
||||
entries.accept(CobblemonItems.BLUE_PLAQUE)
|
||||
entries.accept(CobblemonItems.PURPLE_PLAQUE)
|
||||
entries.accept(CobblemonItems.MAGENTA_PLAQUE)
|
||||
entries.accept(CobblemonItems.PINK_PLAQUE)
|
||||
|
||||
CobblemonItems.campfire_pots.forEach(entries::accept)
|
||||
}
|
||||
|
||||
@@ -783,25 +765,6 @@ object CobblemonItemGroups {
|
||||
}
|
||||
}
|
||||
|
||||
private fun coloredBlocksInjections(injector: Injector) {
|
||||
injector.putLast(CobblemonItems.WHITE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.LIGHT_GRAY_PLAQUE, CobblemonItems.WHITE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.GRAY_PLAQUE, CobblemonItems.LIGHT_GRAY_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.BLACK_PLAQUE, CobblemonItems.GRAY_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.BROWN_PLAQUE, CobblemonItems.BLACK_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.RED_PLAQUE, CobblemonItems.BROWN_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.ORANGE_PLAQUE, CobblemonItems.RED_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.YELLOW_PLAQUE, CobblemonItems.ORANGE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.LIME_PLAQUE, CobblemonItems.YELLOW_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.GREEN_PLAQUE, CobblemonItems.LIME_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.CYAN_PLAQUE, CobblemonItems.GREEN_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.LIGHT_BLUE_PLAQUE, CobblemonItems.CYAN_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.BLUE_PLAQUE, CobblemonItems.LIGHT_BLUE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.PURPLE_PLAQUE, CobblemonItems.BLUE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.MAGENTA_PLAQUE, CobblemonItems.PURPLE_PLAQUE)
|
||||
injector.putAfter(CobblemonItems.PINK_PLAQUE, CobblemonItems.MAGENTA_PLAQUE)
|
||||
}
|
||||
|
||||
private fun foodInjections(injector: Injector) {
|
||||
injector.putAfter(CobblemonItems.TART_APPLE, Items.ENCHANTED_GOLDEN_APPLE)
|
||||
injector.putAfter(CobblemonItems.SWEET_APPLE, CobblemonItems.TART_APPLE)
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
@@ -24,10 +25,8 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.Rarity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* Items for recovering PP in all moves at once.
|
||||
@@ -35,7 +34,9 @@ import net.minecraft.world.level.block.Block
|
||||
* @author Hiroku
|
||||
* @since June 30th, 2023
|
||||
*/
|
||||
class ElixirItem(val max: Boolean, block: Block) : ItemNameBlockItem(block, Properties().apply {
|
||||
class ElixirItem(
|
||||
val max: Boolean
|
||||
) : CobblemonItem(Properties().apply {
|
||||
if (max) rarity(Rarity.UNCOMMON)
|
||||
}), PokemonSelectingItem {
|
||||
override val bagItem = object : BagItem {
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.item.PokemonAndMoveSelectingItem
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
@@ -25,10 +26,8 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.Rarity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* Item for recovering PP for a specific move in a Pokémon's move list. Opens a move selection GUI.
|
||||
@@ -36,7 +35,9 @@ import net.minecraft.world.level.block.Block
|
||||
* @author Hiroku
|
||||
* @since June 30th, 2023
|
||||
*/
|
||||
class EtherItem(val max: Boolean, block: Block) : ItemNameBlockItem(block, Properties().apply {
|
||||
class EtherItem(
|
||||
val max: Boolean
|
||||
) : CobblemonItem(Properties().apply {
|
||||
if (max) rarity(Rarity.UNCOMMON)
|
||||
}), PokemonAndMoveSelectingItem {
|
||||
override val bagItem = object : BagItem {
|
||||
|
||||
+1
-2
@@ -34,9 +34,8 @@ class FreshStartMochiItem : CobblemonItem(Properties()), PokemonSelectingItem {
|
||||
}
|
||||
|
||||
pokemon.feedPokemon(1)
|
||||
|
||||
pokemon.evs.forEach {
|
||||
pokemon.setEV(it.key, 0)
|
||||
pokemon.evs[it.key] = 0
|
||||
}
|
||||
|
||||
//pokemon.entity?.playSound(CobblemonSounds.MOCHI_USE, 1F, 1F) todo use mochi sounds for fullness levels and replace above
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.item.HealingSource
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
@@ -30,12 +31,12 @@ import net.minecraft.world.item.Items
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.Rarity
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
class PotionItem(val type: PotionType, block: Block) : ItemNameBlockItem(block, Properties().apply {
|
||||
class PotionItem(
|
||||
val type: PotionType
|
||||
) : CobblemonItem(Properties().apply {
|
||||
when (type.name) {
|
||||
PotionType.MAX_POTION.name -> rarity(Rarity.UNCOMMON)
|
||||
PotionType.FULL_RESTORE.name -> rarity(Rarity.UNCOMMON)
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Status
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.battle.BagItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
@@ -23,9 +24,7 @@ import net.minecraft.world.item.Items
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* An item that cures statuses. Based on the [status] parameter it can be either specific [Status]es or all statuses (none specified).
|
||||
@@ -33,7 +32,7 @@ import net.minecraft.world.level.block.Block
|
||||
* @author Hiroku
|
||||
* @since June 30th, 2023
|
||||
*/
|
||||
class StatusCureItem(val itemName: String, vararg val status: Status, block: Block) : ItemNameBlockItem(block, Properties()), PokemonSelectingItem {
|
||||
class StatusCureItem(val itemName: String, vararg val status: Status) : CobblemonItem(Properties()), PokemonSelectingItem {
|
||||
override val bagItem = object : BagItem {
|
||||
override val itemName = this@StatusCureItem.itemName
|
||||
override val returnItem = Items.AIR
|
||||
|
||||
+4
-2
@@ -20,8 +20,10 @@ import net.minecraft.resources.ResourceLocation
|
||||
*
|
||||
* Handled by [com.cobblemon.mod.common.client.net.battle.BattleMusicHandler].
|
||||
*
|
||||
* @property music The location of the sound to play during a battle. If null, gracefully ends the currently playing sound.
|
||||
* @property restartExisting If true and the location of [music] matches the currently playing sound, will restart from the beginning.
|
||||
* @property music The location of a [SoundEvent], or the location of the []to dispatch a sound from, to play during a battle.
|
||||
* If null, gracefully ends the currently playing sound.
|
||||
*
|
||||
* @property restartExisting If true and [music] matches the location of the currently playing sound, will restart from the beginning.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since April 20th, 2023
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.data
|
||||
|
||||
class SoundTrackConfigRegistrySyncPacket {
|
||||
}
|
||||
+2
-1
@@ -29,7 +29,8 @@ class FormUpdatePacket(pokemon: () -> Pokemon?, form: FormData) : SingleUpdatePa
|
||||
val ID: ResourceLocation = cobblemonResource("packets/form-update")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): FormUpdatePacket {
|
||||
val pokemon = decodePokemon(buffer)
|
||||
val form = FormData().also { it.initialize(pokemon()!!.species) }
|
||||
val form = FormData()
|
||||
form.species = pokemon()!!.species
|
||||
form.decode(buffer)
|
||||
return FormUpdatePacket(pokemon, form)
|
||||
}
|
||||
|
||||
+2
-7
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling.riding
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -26,16 +25,12 @@ object DismountPokemonPacketHandler : ServerNetworkPacketHandler<DismountPokemon
|
||||
|
||||
val pokemon = player.vehicle as PokemonEntity
|
||||
if (!canPlayerStopRidingPokemon(pokemon, player)) return
|
||||
|
||||
val isRidingInAir = pokemon.ridingController?.context?.style == RidingStyle.AIR
|
||||
|
||||
if (pokemon.controllingPassenger == player) {
|
||||
pokemon.passengers.forEach { it.stopRiding() }
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
player.stopRiding()
|
||||
}
|
||||
|
||||
if (isRidingInAir) pokemon.setFlying(true)
|
||||
}
|
||||
|
||||
private fun canPlayerStopRidingPokemon(pokemon: PokemonEntity, player: ServerPlayer): Boolean {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
|
||||
/**
|
||||
* Event to signal a transition in battle music. When received by the client, will either replace the currently playing
|
||||
* battle music with a new instance, or pause and temporarily switches from the current instance to play a [soundEvent].
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since December 16, 2925
|
||||
*/
|
||||
data class BattleMusicCue(
|
||||
/** The identifier of this cue. */
|
||||
val id: ResourceLocation,
|
||||
/** The track or sound effect to play. */
|
||||
val soundEvent: SoundEvent,
|
||||
/** Whether the [soundEvent] should replace and become the main track. */
|
||||
val replace: Boolean,
|
||||
/** Determines which cue's [soundEvent] plays if multiple cues are scheduled at once. Priority is lost when cue becomes
|
||||
* the main track. */
|
||||
val priority: Int,
|
||||
/** Time in seconds before the [soundEvent] starts playing. */
|
||||
val delay: Int,
|
||||
/** If the currently playing track should fade out, and the [soundEvent] should fade in. */
|
||||
val fade: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A configuration that dispatches a [BattleTrack] to use when playing battle music.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since January 6th, 2026
|
||||
*/
|
||||
class BattleThemeConfig(
|
||||
id: ResourceLocation,
|
||||
/** */
|
||||
override val tracks: Collection<BattleTrack>,
|
||||
/** 2nd-priority [BattleMusicCue]s that are subscribed by every track in the configuration. */
|
||||
val cues: Collection<BattleMusicCue>,
|
||||
) : SoundTrackConfig(id, tracks)
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
object BattleThemes : JsonDataRegistry<BattleThemeConfig> {
|
||||
|
||||
override val resourcePath = "battle_themes"
|
||||
|
||||
|
||||
override val id: ResourceLocation = cobblemonResource(this.resourcePath)
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<BattleThemeConfig>()
|
||||
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
|
||||
override val typeToken = TypeToken.get(BattleThemeConfig::class.java)
|
||||
|
||||
/** */
|
||||
private val battleSoundTrackConfigs = mutableMapOf<ResourceLocation, BattleThemeConfig>()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, BattleThemeConfig>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getByIdentifier(identifier: ResourceLocation): BattleThemeConfig? = this.battleSoundTrackConfigs[identifier]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
class BattleTrack(
|
||||
val battle_conditions
|
||||
) : SoundTrack {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.EntityQueryRequirement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* r
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since December 16, 2025
|
||||
*/
|
||||
open class SoundTrack {
|
||||
|
||||
val ambient: ResourceLocation
|
||||
val wildLocation: ResourceLocation
|
||||
val trainerLocation: ResourceLocation
|
||||
val priority: Int = 1
|
||||
val requirements: MutableSet<EntityQueryRequirement>
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.cobblemon.mod.common.ost
|
||||
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A configuration that dispatches a [SoundTrack] to use when playing music.
|
||||
*
|
||||
* @author Segfault Guy
|
||||
* @since January 6th, 2026
|
||||
*/
|
||||
open class SoundTrackConfig(
|
||||
/** The identifier of this configuration. */
|
||||
val id: ResourceLocation,
|
||||
/** The */
|
||||
open val tracks: Collection<SoundTrack>,
|
||||
) {
|
||||
|
||||
fun dispatchTrack(): BattleTrack {
|
||||
// TODO based on requirements
|
||||
}
|
||||
}
|
||||
|
||||
// should this be templated instead?
|
||||
@@ -199,9 +199,6 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
|
||||
var form = species.standardForm
|
||||
set(value) {
|
||||
// If nothing actually changed, avoid running heavy logic (e.g. updateMovesOnFormChange when the form hasn't really "changed")
|
||||
if (field == value) return
|
||||
|
||||
// Species updates already update HP but just a form change may require it
|
||||
// Moved to before the field was set else it won't actually do the hp calc proper <3
|
||||
val quotient = clamp(currentHealth / maxHealth.toFloat(), 0F, 1F)
|
||||
@@ -999,7 +996,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
this.currentFullness = max(0, this.currentFullness - value)
|
||||
}
|
||||
|
||||
// Amount of ticks that need to pass for the pokemon to lose 1 fullness value
|
||||
// Amount of seconds that need to pass for the pokemon to lose 1 fullness value
|
||||
fun getMetabolismRate(): Int {
|
||||
val speed = this.species.baseStats.getOrDefault(Stats.SPEED,0)
|
||||
|
||||
@@ -1012,12 +1009,16 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
//base berry count
|
||||
val baseBerryCount = 20
|
||||
|
||||
var metabolismRateInSeconds = ((baseBerryCount.toDouble() - ((speed.toDouble() / BST.toDouble()) * baseBerryCount.toDouble()) * multiplier.toDouble()) * 60.0).toInt()
|
||||
if (metabolismRateInSeconds <= 0) {
|
||||
metabolismRateInSeconds = 1 * 60
|
||||
}
|
||||
//rate of metabolism in seconds
|
||||
val metabolismRate = ((baseBerryCount.toDouble() - ((speed.toDouble() / BST.toDouble()) * baseBerryCount.toDouble()) * multiplier.toDouble()) * 60.0).toInt()
|
||||
|
||||
return metabolismRateInSeconds * 20
|
||||
// returns value in seconds for the onSecondPassed function
|
||||
// check for below 0 value and set to minimum to 1 minute
|
||||
return if (metabolismRate <= 0) {
|
||||
1 * 60
|
||||
} else {
|
||||
metabolismRate
|
||||
}
|
||||
}
|
||||
|
||||
// Boolean function that checks if a Pokemon can eat food based on fedTimes
|
||||
@@ -1036,18 +1037,13 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
this.interactionCooldowns.remove(group)
|
||||
}
|
||||
|
||||
open fun tickInteractionCooldown(ticksPassed: Int) {
|
||||
val iterator = interactionCooldowns.entries.iterator()
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
val entry = iterator.next()
|
||||
val newValue = entry.value - ticksPassed.coerceAtLeast(1)
|
||||
|
||||
if (newValue <= 0) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
entry.setValue(newValue)
|
||||
}
|
||||
open fun tickInteractionCooldown() {
|
||||
this.interactionCooldowns.entries.forEach { (key, value) ->
|
||||
val newValue = value - 1
|
||||
if(newValue <= 0)
|
||||
this.interactionCooldowns.remove(key)
|
||||
else
|
||||
this.interactionCooldowns.put(key, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,8 +1054,9 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
/**
|
||||
* Called every second on Player owned and Pastured Pokémon for their fullness
|
||||
*/
|
||||
open fun tickMetabolism(ticksPassed: Int) {
|
||||
metabolismCycle += ticksPassed.coerceAtLeast(1)
|
||||
open fun tickMetabolism() {
|
||||
// have metabolism cycle increase each second
|
||||
metabolismCycle += 1
|
||||
|
||||
// if the metabolismCycle value equals the Pokemon's metabolism rate then decrease Fullness by 1
|
||||
if (metabolismCycle >= this.getMetabolismRate()) {
|
||||
@@ -2147,26 +2144,16 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
*/
|
||||
open fun self(): Pokemon = this
|
||||
|
||||
|
||||
/**
|
||||
* Function that sanitizes specific form-related moves when the form of this Pokémon changes.
|
||||
* We only want to sanitize `form_change` moves here so that intended illegalities from the user are preserved.
|
||||
*
|
||||
* E.g, Removing Overheat from Rotom when it is no longer Rotom-Heat and the target form is not compatible with Overheat.
|
||||
*/
|
||||
private fun updateMovesOnFormChange(newForm: FormData) {
|
||||
if (this.isClient) return
|
||||
|
||||
val oldFormChangeMoves = this.form.moves.formChangeMoves.toSet()
|
||||
|
||||
// Only remove moves that were provided by the old form's form-change moves AND are not learnable by the new form
|
||||
if (this.isClient) {
|
||||
return
|
||||
}
|
||||
for (i in 0 until MoveSet.MOVE_COUNT) {
|
||||
val move = this.moveSet[i]
|
||||
if (move != null && move.template in oldFormChangeMoves) {
|
||||
if (move != null && !LearnsetQuery.ANY.canLearn(move.template, newForm.moves)) {
|
||||
this.moveSet.setMove(i, null)
|
||||
}
|
||||
}
|
||||
|
||||
val benchedIterator = this.benchedMoves.iterator()
|
||||
while (benchedIterator.hasNext()) {
|
||||
val benchedMove = benchedIterator.next()
|
||||
@@ -2174,18 +2161,11 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
benchedIterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
// Add new form's form-change moves
|
||||
// Add form change moves
|
||||
newForm.moves.formChangeMoves.forEach { move ->
|
||||
if (this.moveSet.filterNotNull().none { it.template == move }) {
|
||||
if (this.moveSet.hasSpace()) {
|
||||
this.moveSet.add(move.create())
|
||||
} else {
|
||||
this.benchedMoves.add(BenchedMove(move, 0))
|
||||
}
|
||||
}
|
||||
// Under the hood these check if the moves already exist
|
||||
this.benchedMoves.add(BenchedMove(move, 0))
|
||||
}
|
||||
|
||||
// If moveset is empty try to find one valid move to fill it
|
||||
if (this.moveSet.filterNotNull().isEmpty()) {
|
||||
val benchedMove = this.benchedMoves.firstOrNull()
|
||||
|
||||
+2
@@ -67,6 +67,8 @@ object CobblemonRequirementAdapter : RequirementAdapter {
|
||||
this.registerType(PropertyRangeRequirement.ADAPTER_VARIANT, PropertyRangeRequirement::class)
|
||||
this.registerType(AdvancementRequirement.ADAPTER_VARIANT, AdvancementRequirement::class)
|
||||
this.registerType(OwnerHoldsItemRequirement.ADAPTER_VARIANT, OwnerHoldsItemRequirement::class)
|
||||
this.registerType(DepthRequirement.ADAPTER_VARIANT, DepthRequirement::class)
|
||||
this.registerType(SkyRequirement.ADAPTER_VARIANT, SkyRequirement::class)
|
||||
}
|
||||
|
||||
val gson: Gson by lazy {
|
||||
|
||||
@@ -562,8 +562,8 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
//// PathType.WALKABLE
|
||||
//// } else if (blockState.canPathfindThrough(world, pos, NavigationType.AIR) && !blockStateBelow.isSideSolid(world, below, Direction.UP, SideShapeType.FULL)) {
|
||||
//// PathType.OPEN
|
||||
if (canPathThroughLeaves() && blockState.block == CobblemonBlocks.SACCHARINE_LEAVES)
|
||||
return PathType.OPEN
|
||||
// } else if (blockState.`is`(BlockTags.LEAVES) && blockState.block == CobblemonBlocks.SACCHARINE_LEAVES && canPathThroughLeaves()) {
|
||||
// return PathType.OPEN
|
||||
// } else PathType.BLOCKED
|
||||
|
||||
// This is the 1.6.1 logic but with some optimizations
|
||||
@@ -633,6 +633,9 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
pfContext, x, y, z, sizeX, sizeY, sizeZ, canOpenDoors, canPassDoors, set, PathType.BLOCKED, BlockPos(x, y, z), returnedEarlyPathType
|
||||
)
|
||||
|
||||
if (PathType.LEAVES in set) {
|
||||
return PathType.OPEN
|
||||
}
|
||||
|
||||
if (PathType.FENCE in set) {
|
||||
return PathType.FENCE
|
||||
@@ -752,7 +755,7 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
} else if (type == PathType.RAIL && block !is BaseRailBlock && pfContext.getCachedBlockState(pos.below()).block !is BaseRailBlock) {
|
||||
PathType.UNPASSABLE_RAIL
|
||||
} else if (type == PathType.LEAVES) {
|
||||
if (canPathThroughLeaves() && blockState.block === CobblemonBlocks.SACCHARINE_LEAVES) {
|
||||
if (blockState.block === CobblemonBlocks.SACCHARINE_LEAVES && canPathThroughLeaves()) {
|
||||
PathType.OPEN
|
||||
} else {
|
||||
PathType.BLOCKED
|
||||
|
||||
+8
-4
@@ -6,15 +6,19 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.properties
|
||||
package com.cobblemon.mod.common.pokemon.properties.tags
|
||||
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.StringProperty
|
||||
|
||||
object LabelProperty : CustomPokemonPropertyType<StringProperty> {
|
||||
override val keys = setOf("label", "tag")
|
||||
object PokemonFlagProperty : CustomPokemonPropertyType<StringProperty> {
|
||||
|
||||
private const val KEY = "tag"
|
||||
|
||||
override val keys = setOf(KEY)
|
||||
override val needsKey = true
|
||||
|
||||
override fun fromString(value: String?) = if (value == null) null else StringProperty(keys.first(), value, { _, _ -> }, { pokemon, underlyingValue -> pokemon.hasLabels(underlyingValue) })
|
||||
override fun fromString(value: String?) = if (value == null) null else StringProperty(KEY, value, { _, _ -> }, { pokemon, underlyingValue -> pokemon.hasLabels(underlyingValue) })
|
||||
|
||||
override fun examples() = emptySet<String>()
|
||||
}
|
||||
+2
-3
@@ -9,13 +9,12 @@
|
||||
package com.cobblemon.mod.common.pokemon.requirements
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.EntityQueryRequirement
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* A [EntityQueryRequirement] for when a [Pokemon] is expected to be in a certain area.
|
||||
* A [EntityQueryRequirement] for when a [LivingEntity] is expected to be in a certain area.
|
||||
*
|
||||
* @property box The [AABB] expected to be in.
|
||||
* @author Licious
|
||||
@@ -27,5 +26,5 @@ class AreaRequirement : EntityQueryRequirement {
|
||||
}
|
||||
|
||||
val box: AABB = AABB.unitCubeFromLowerCorner(Vec3.ZERO)
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity) = box.contains(queriedEntity.position())
|
||||
override fun check(queriedEntity: LivingEntity) = box.contains(queriedEntity.position())
|
||||
}
|
||||
+2
-2
@@ -18,7 +18,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
/**
|
||||
* A [Requirement] for when a [Pokemon] is expected to be in a certain [Biome].
|
||||
* A [Requirement] for when a [LivingEntity] is expected to be in a certain [Biome].
|
||||
*
|
||||
* @property biomeCondition The [ResourceLocation] of the [Biome] the queried entity is expected to be in.
|
||||
* @author Licious
|
||||
@@ -27,7 +27,7 @@ import net.minecraft.world.level.biome.Biome
|
||||
class BiomeRequirement : EntityQueryRequirement {
|
||||
val biomeCondition: RegistryLikeCondition<Biome>? = null
|
||||
val biomeAnticondition: RegistryLikeCondition<Biome>? = null
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean {
|
||||
override fun check(queriedEntity: LivingEntity): Boolean {
|
||||
val biome = queriedEntity.level().getBiome(queriedEntity.blockPosition()).value()
|
||||
val registry = queriedEntity.level().registryAccess().registryOrThrow(Registries.BIOME)
|
||||
return (biomeCondition == null || biomeCondition.fits(biome, registry)) && (biomeAnticondition == null || !biomeAnticondition.fits(biome, registry))
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.cobblemon.mod.common.pokemon.requirements
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.EntityQueryRequirement
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
/**
|
||||
* An [EntityQueryRequirement] that evaluates whether a [LivingEntity] is at a certain block level.
|
||||
*
|
||||
* @property minY The minimum block level to satisfy this requirement. If null, defaults to the dimension's min height.
|
||||
* @property maxY The maximum block level to satisfy this requirement. If null, defaults to the dimension's max height.
|
||||
* @author Segfault Guy
|
||||
* @since December 16, 2025
|
||||
*/
|
||||
class DepthRequirement(val minY: Int?, val maxY: Int?) : EntityQueryRequirement {
|
||||
|
||||
override fun check(queriedEntity: LivingEntity): Boolean {
|
||||
val minLevelY = queriedEntity.level().minBuildHeight
|
||||
val maxLevelY = queriedEntity.level().maxBuildHeight
|
||||
val test = minY?.coerceIn(minLevelY, maxLevelY) ?: minLevelY
|
||||
val test2 = maxY?.coerceIn(minLevelY, maxLevelY) ?: maxLevelY
|
||||
|
||||
return queriedEntity.blockPosition().y in test..test2
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ADAPTER_VARIANT = "depth"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ class MoonPhaseRequirement(moonPhase: MoonPhase) : EntityQueryRequirement {
|
||||
|
||||
val moonPhase: MoonPhase = moonPhase
|
||||
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean {
|
||||
override fun check(queriedEntity: LivingEntity): Boolean {
|
||||
return try {
|
||||
val moonPhase = MoonPhase.ofWorld(queriedEntity.level())
|
||||
this.moonPhase == moonPhase
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.pokemon.requirements
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.EntityQueryRequirement
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
/**
|
||||
* An [EntityQueryRequirement] that evaluates whether a [LivingEntity] is exposed to a dimension's sky.
|
||||
*
|
||||
* @param invert If true, this requirement evaluates whether the entity CANNOT see a dimension's sky.
|
||||
* @author Segfault Guy
|
||||
* @since December 16, 2025
|
||||
*/
|
||||
class SkyRequirement(val invert: Boolean = false) : EntityQueryRequirement {
|
||||
|
||||
override fun check(queriedEntity: LivingEntity) = queriedEntity.level().canSeeSky(queriedEntity.blockPosition()).xor(invert)
|
||||
|
||||
companion object {
|
||||
const val ADAPTER_VARIANT = "sky"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -16,7 +16,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
|
||||
/**
|
||||
* A [EntityQueryRequirement] for when a [Pokemon] is expected to be in a certain structure.
|
||||
* A [EntityQueryRequirement] for when a [LivingEntity] is expected to be in a certain structure.
|
||||
*
|
||||
* @property structureCondition The [Identifier] of the structure the queried entity is expected to be in.
|
||||
* @author whatsy
|
||||
@@ -25,7 +25,7 @@ import net.minecraft.world.level.levelgen.structure.Structure
|
||||
class StructureRequirement : EntityQueryRequirement {
|
||||
val structureCondition: RegistryLikeCondition<Structure>? = null
|
||||
val structureAnticondition: RegistryLikeCondition<Structure>? = null
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean {
|
||||
override fun check(queriedEntity: LivingEntity): Boolean {
|
||||
val structures = queriedEntity.level().getChunk(queriedEntity.blockPosition()).allReferences
|
||||
val registry = queriedEntity.level().registryAccess().registryOrThrow(Registries.STRUCTURE)
|
||||
return (structureCondition == null || structures.any { structureCondition.fits(it.key, registry) }) && (structureAnticondition == null || !structures.any { structureAnticondition.fits(it.key, registry) })
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
*/
|
||||
class TimeRangeRequirement : EntityQueryRequirement {
|
||||
val range = TimeRange(0..23999)
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity) = this.range.contains((queriedEntity.level().dayTime() % DAY_DURATION).toInt())
|
||||
override fun check(queriedEntity: LivingEntity) = this.range.contains((queriedEntity.level().dayTime() % DAY_DURATION).toInt())
|
||||
companion object {
|
||||
const val ADAPTER_VARIANT = "time_range"
|
||||
private const val DAY_DURATION = 24000
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ class WeatherRequirement : EntityQueryRequirement {
|
||||
val isRaining: Boolean? = null
|
||||
val isThundering: Boolean? = null
|
||||
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity): Boolean {
|
||||
override fun check(queriedEntity: LivingEntity): Boolean {
|
||||
val world = queriedEntity.level()
|
||||
return when {
|
||||
isRaining == true && !world.isRaining -> false
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
/**
|
||||
* A [EntityQueryRequirement] for when a [Pokemon] is expected to be in a [Level].
|
||||
* A [EntityQueryRequirement] for when a [LivingEntity] is expected to be in a [Level].
|
||||
*
|
||||
* @property identifier The [ResourceLocation] of the [Level] the queried entity is expected to be in.
|
||||
* @author Licious
|
||||
@@ -26,5 +26,5 @@ class WorldRequirement : EntityQueryRequirement {
|
||||
const val ADAPTER_VARIANT = "world"
|
||||
}
|
||||
val identifier: ResourceLocation = ResourceLocation.parse("minecraft:the_overworld")
|
||||
override fun check(pokemon: Pokemon, queriedEntity: LivingEntity) = queriedEntity.level().dimension().location() == this.identifier
|
||||
override fun check(queriedEntity: LivingEntity) = queriedEntity.level().dimension().location() == this.identifier
|
||||
}
|
||||
Loaded 100 of 601 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user