mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-22 07:06:23 -04:00
Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33e0f433a7 | ||
|
|
411a02933b | ||
|
|
854d7827bb | ||
|
|
8e3228419a | ||
|
|
cb8a80f68d | ||
|
|
71342623c6 | ||
|
|
b0c37ac216 | ||
|
|
86ab0e5615 | ||
|
|
2783d29552 | ||
|
|
05323d952f | ||
|
|
a014334f21 | ||
|
|
2e80924c68 | ||
|
|
6749b39142 | ||
|
|
ff25416b07 | ||
|
|
bb81a44a11 | ||
|
|
40dfca2294 | ||
|
|
5a40ced96b | ||
|
|
3eb97b29a1 | ||
|
|
d88de0b072 | ||
|
|
460c44248c | ||
|
|
a16668695c | ||
|
|
fe5ebb5444 | ||
|
|
c34e4c8bbc | ||
|
|
84e76a0a6d | ||
|
|
be70c1e8de | ||
|
|
fba40387bb | ||
|
|
1217e6d101 | ||
|
|
905c410aee | ||
|
|
cea0c826c6 | ||
|
|
dc3b17df3b | ||
|
|
5602dee9ac | ||
|
|
9e4137cc20 | ||
|
|
c92248f6e9 |
@@ -32,6 +32,6 @@ final class ParticleManagerMixin {
|
||||
@Inject(at = @At("RETURN"), method = "<clinit>")
|
||||
private static void lodestone$addTypes(CallbackInfo ci) {
|
||||
RENDER_ORDER = ImmutableList.<ParticleRenderType>builder().addAll(RENDER_ORDER)
|
||||
.add(ParticleMaterials.INSTANCE.getADD(), ParticleMaterials.INSTANCE.getALPHA(), ParticleMaterials.INSTANCE.getBLEND(), ParticleMaterials.INSTANCE.getOPAQUE()).build();
|
||||
.add(ParticleMaterials.INSTANCE.getXRAY(), ParticleMaterials.INSTANCE.getADD(), ParticleMaterials.INSTANCE.getALPHA(), ParticleMaterials.INSTANCE.getBLEND(), ParticleMaterials.INSTANCE.getOPAQUE()).build();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.gui.interact.wheel.InteractWheelGuiFactoryKt;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.RidingFreelookBinding;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
@@ -67,6 +68,39 @@ public abstract class MouseHandlerMixin {
|
||||
|
||||
@Unique private double cobblemon$timeDelta;
|
||||
|
||||
@Inject(
|
||||
method = "onPress",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
public void cobblemon$pokeskillGuiControl(long windowPointer, int button, int action, int modifiers, CallbackInfo ci) {
|
||||
if (button == 0 && action == 1) {
|
||||
|
||||
// Make sure no menus are open already
|
||||
if (this.minecraft.screen == null && this.minecraft.getOverlay() == null) {
|
||||
// Check to see if party key is held
|
||||
if (PartySendBinding.INSTANCE.getWasDown()) {
|
||||
|
||||
// Don't want to send out a pokemon if we let go of the key
|
||||
PartySendBinding.INSTANCE.actioned();
|
||||
|
||||
//Open the actual gui
|
||||
var player = Minecraft.getInstance().player;
|
||||
var storage = CobblemonClient.INSTANCE.getStorage();
|
||||
var selectedPokemon = storage.getParty().get(storage.getSelectedSlot());
|
||||
if (selectedPokemon != null && player != null) {
|
||||
//canApplyChange = false
|
||||
var gui = InteractWheelGuiFactoryKt.createPokeSkillGui(player, selectedPokemon);
|
||||
Minecraft.getInstance().setScreen(gui);
|
||||
}
|
||||
|
||||
// Stop the rest of minecraft click logic like punching and using items
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "onScroll",
|
||||
at = @At(
|
||||
|
||||
@@ -24,27 +24,13 @@ import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
|
||||
import com.cobblemon.mod.common.client.net.debug.OpenRidingStatsDebugGUIHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.ExpGainedDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetActiveTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SetTMMachineContainerDataPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SynchronizeActiveTMHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.*
|
||||
import com.cobblemon.mod.common.client.net.gui.*
|
||||
import com.cobblemon.mod.common.client.net.habitat.OpenHabitatBlockEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.*
|
||||
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundSeatAssignmentPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
|
||||
@@ -63,14 +49,7 @@ import com.cobblemon.mod.common.client.net.storage.party.SetPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.party.SetPartyReferenceHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.pc.*
|
||||
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeAcceptanceChangedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCancelledHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCompletedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferExpiredHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.*
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
@@ -86,21 +65,13 @@ import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.RequestOpenRidingStatsDebugGUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.*
|
||||
import com.cobblemon.mod.common.net.messages.client.habitat.OpenHabitatBlockEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
|
||||
@@ -121,47 +92,17 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePart
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.tms.TechnicalMachineRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCompletedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.ExpGainedDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SetTMMachineContainerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SynchronizeActiveTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.*
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.behaviour.SetEntityBehaviourPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
@@ -188,11 +129,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
@@ -205,21 +142,12 @@ import com.cobblemon.mod.common.net.messages.server.storage.party.PartyTutorialP
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.UsePokeSkillHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.DamageOnCollisionPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.behaviour.SetEntityBehaviourHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
@@ -247,11 +175,7 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.ServerboundUpdatePassengerPositionHandler
|
||||
@@ -267,12 +191,7 @@ import com.cobblemon.mod.common.net.serverhandling.storage.party.PartyTutorialPa
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -342,6 +261,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(SkillCooldownsUpdatePacket.ID, SkillCooldownsUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(AlphaUpdatePacket.ID, AlphaUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
list.add(PacketRegisterInfo(ScaleModifierUpdatePacket.ID, ScaleModifierUpdatePacket::decode, PokemonUpdatePacketHandler()))
|
||||
|
||||
@@ -443,6 +363,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
|
||||
list.add(PacketRegisterInfo(PokeSkillsRegistrySyncPacket.ID, PokeSkillsRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
@@ -535,6 +456,7 @@ object CobblemonNetwork {
|
||||
// Interaction Packets
|
||||
list.add(PacketRegisterInfo(InteractPokemonPacket.ID, InteractPokemonPacket::decode, InteractPokemonHandler))
|
||||
list.add(PacketRegisterInfo(RequestPlayerInteractionsPacket.ID, RequestPlayerInteractionsPacket::decode, RequestInteractionsHandler))
|
||||
list.add(PacketRegisterInfo(UsePokeSkillPacket.ID, UsePokeSkillPacket::decode, UsePokeSkillHandler))
|
||||
|
||||
// Storage Packets
|
||||
list.add(PacketRegisterInfo(SendOutPokemonPacket.ID, SendOutPokemonPacket::decode, SendOutPokemonHandler))
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.entity.pokemon.pokeskills.PokeSkill
|
||||
import com.cobblemon.mod.common.entity.pokemon.pokeskills.PokeSkillTier
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PokeSkillsRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.adapters.ActionEffectTimelineAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TextAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
/**
|
||||
* The JSON data registry for all the possible [PokeSkill]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 31st, 2025
|
||||
*/
|
||||
object CobblemonPokeSkills : JsonDataRegistry<PokeSkill> {
|
||||
override val id = cobblemonResource("poke_skills")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<CobblemonPokeSkills>()
|
||||
override val typeToken = TypeToken.get(PokeSkill::class.java)
|
||||
override val resourcePath = "poke_skills"
|
||||
override val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(ActionEffectTimeline::class.java, ActionEffectTimelineAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.create()
|
||||
|
||||
var skills = mapOf<ResourceLocation, PokeSkill>()
|
||||
|
||||
fun getAccessiblePokeSkillTiers(player: Player, pokemon: Pokemon): List<PokeSkillTier> {
|
||||
return skills.values.mapNotNull { skill ->
|
||||
skill.tiers.lastOrNull { tier ->
|
||||
tier.canAccess(player, pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PokeSkill>) {
|
||||
data.forEach { (id, skill) -> skill.id = id }
|
||||
|
||||
skills = data
|
||||
skills.values.forEach { skill ->
|
||||
skill.tiers.forEachIndexed { index, tier ->
|
||||
tier.initialize(skill, index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
observable.emit(this)
|
||||
|
||||
val tiersCount = skills.values.sumOf { it.tiers.size }
|
||||
Cobblemon.LOGGER.info("Loaded ${skills.size} PokéSkills with $tiersCount tiers")
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(PokeSkillsRegistrySyncPacket(skills.values.toList()))
|
||||
}
|
||||
}
|
||||
@@ -286,6 +286,15 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
fun Any.asMostSpecificMoLangValue(): MoValue {
|
||||
return when (this) {
|
||||
is Number -> DoubleValue(this.toDouble())
|
||||
is String -> StringValue(this)
|
||||
is Boolean -> DoubleValue(if (this) 1.0 else 0.0)
|
||||
else -> StringValue(toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -293,11 +302,11 @@ object MoLangFunctions {
|
||||
)
|
||||
value.functions["is_in"] = Function put@{ params: MoParams ->
|
||||
val tag = TagKey.create(key, ResourceLocation.parse(params.getString(0).replace("#", "")))
|
||||
return@put DoubleValue(if (value.obj.`is`(tag)) 1.0 else 0.0)
|
||||
return@put DoubleValue(value.obj.`is`(tag))
|
||||
}
|
||||
value.functions["is_of"] = Function put@{ params: MoParams ->
|
||||
val identifier = ResourceLocation.parse(params.getString(0))
|
||||
return@put DoubleValue(if (value.obj.`is`(identifier)) 1.0 else 0.0)
|
||||
return@put DoubleValue(value.obj.`is`(identifier))
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ object DropEntryMoLangFunctions : AbstractMoLangFunctionHolder<DropEntry>() {
|
||||
map["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
|
||||
map["can_drop"] = { params ->
|
||||
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
|
||||
DoubleValue(if (drop.canDrop(pokemon)) 1.0 else 0.0)
|
||||
DoubleValue(drop.canDrop(pokemon))
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
+83
-40
@@ -25,22 +25,8 @@ import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asResource
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.cloneFrom
|
||||
import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.getBlockStatesWithPos
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIsSubmerged
|
||||
import com.cobblemon.mod.common.util.getOrNull
|
||||
import com.cobblemon.mod.common.util.getStringOrNull
|
||||
import com.cobblemon.mod.common.util.isStandingOn
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.worldRegistry
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -48,10 +34,13 @@ import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.TamableAnimal
|
||||
import net.minecraft.world.level.block.BonemealableBlock
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
@@ -78,8 +67,13 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
"vertical_velocity" to { DoubleValue(thisEntity.deltaMovement.y) },
|
||||
"is_on_ground" to { _ -> DoubleValue(thisEntity.onGround()) },
|
||||
"world" to { _ -> thisEntity.level().worldRegistry.wrapAsHolder(thisEntity.level()).asWorldMoLangValue() },
|
||||
"dimension" to { StringValue(thisEntity.level().dimension().location().toString()) },
|
||||
"biome" to { _ -> thisEntity.level().getBiome(thisEntity.blockPosition()).asBiomeMoLangValue() },
|
||||
"is_passenger" to { DoubleValue(thisEntity.isPassenger) },
|
||||
"is_passenger" to isPassenger@{ params ->
|
||||
val passengerEntity = (params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? LivingEntity ?: return@isPassenger DoubleValue.ZERO
|
||||
DoubleValue(passengerEntity in thisEntity.passengers)
|
||||
},
|
||||
"discard" to { thisEntity.discard() },
|
||||
"is_sneaking" to { _ -> DoubleValue(thisEntity.isShiftKeyDown) },
|
||||
"is_sprinting" to { _ -> DoubleValue(thisEntity.isSprinting) },
|
||||
@@ -121,6 +115,62 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
listOf(thisEntity.deltaMovement.x, thisEntity.deltaMovement.y, thisEntity.deltaMovement.z)
|
||||
.asArrayValue(::DoubleValue)
|
||||
},
|
||||
"set_position" to { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
thisEntity.setPos(x, y, z)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"fertilize_nearby_crops" to { params -> //TODO: pretty bespoke. Maybe should be moved or generalized?
|
||||
val radius = params.getIntOrNull(0) ?: 8
|
||||
val maxCrops = params.getIntOrNull(1) ?: 8
|
||||
|
||||
var fertilizedCount = 0
|
||||
val level = thisEntity.level()
|
||||
val centerPos = thisEntity.blockPosition()
|
||||
|
||||
// Find all bonemealable blocks within radius
|
||||
val bonemealableBlocks = mutableListOf<BlockPos>()
|
||||
|
||||
for (x in -radius..radius) {
|
||||
for (y in -3..3) { // Limited vertical range
|
||||
for (z in -radius..radius) {
|
||||
val pos = centerPos.offset(x, y, z)
|
||||
val blockState = level.getBlockState(pos)
|
||||
val block = blockState.block
|
||||
|
||||
// Check if block can be bonemealed
|
||||
if (block is BonemealableBlock && block is CropBlock && block.isValidBonemealTarget(level, pos, blockState)) {
|
||||
bonemealableBlocks.add(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle and take up to maxCrops blocks to fertilize
|
||||
bonemealableBlocks.shuffle()
|
||||
val blocksToFertilize = bonemealableBlocks.take(maxCrops)
|
||||
|
||||
for (pos in blocksToFertilize) {
|
||||
val blockState = level.getBlockState(pos)
|
||||
val block = blockState.block
|
||||
|
||||
if (block is BonemealableBlock && block.isValidBonemealTarget(level, pos, blockState)) {
|
||||
if (block.isBonemealSuccess(level, level.random, pos, blockState)) {
|
||||
block.performBonemeal(level as ServerLevel, level.random, pos, blockState)
|
||||
|
||||
// Add bonemeal particle
|
||||
val particlePos = block.getParticlePos(pos)
|
||||
level.levelEvent(1505, particlePos, 15) // Same event used for bonemealing in vanilla
|
||||
|
||||
fertilizedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DoubleValue(fertilizedCount.toDouble())
|
||||
},
|
||||
"tags" to {
|
||||
val tags = thisEntity.tags
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
@@ -152,12 +202,14 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
StringValue(it)
|
||||
} ?: DoubleValue.ZERO
|
||||
},
|
||||
"find_nearby_block" to findNearbyBlock@{ params ->
|
||||
"find_nearby_blocks" to { params ->
|
||||
val input = params.getString(0)
|
||||
val isTag = input.contains("#")
|
||||
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
|
||||
val range = params.getDoubleOrNull(1) ?: 10
|
||||
val blockPos = thisEntity.level().getBlockStatesWithPos(
|
||||
|
||||
val blockPosList = thisEntity.level().getBlockStatesWithPos(
|
||||
AABB.ofSize(
|
||||
thisEntity.position(),
|
||||
range.toDouble(),
|
||||
@@ -165,28 +217,19 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
range.toDouble()
|
||||
)
|
||||
)
|
||||
.filter { blockPosPair ->
|
||||
blockPosPair.first.blockHolder.let {
|
||||
if (isTag) it.`is`(
|
||||
TagKey.create(
|
||||
Registries.BLOCK,
|
||||
type
|
||||
)
|
||||
) else it.`is`(type)
|
||||
}
|
||||
.filter { blockPosPair ->
|
||||
blockPosPair.first.blockHolder.let {
|
||||
if (isTag) it.`is`(
|
||||
TagKey.create(
|
||||
Registries.BLOCK,
|
||||
type
|
||||
)
|
||||
) else it.`is`(type)
|
||||
}
|
||||
.minByOrNull { it.second.distSqr(thisEntity.blockPosition()) }
|
||||
?.second
|
||||
if (blockPos != null) {
|
||||
return@findNearbyBlock ArrayStruct(
|
||||
mapOf(
|
||||
"0" to DoubleValue(blockPos.x),
|
||||
"1" to DoubleValue(blockPos.y),
|
||||
"2" to DoubleValue(blockPos.z)
|
||||
)
|
||||
)
|
||||
}
|
||||
return@findNearbyBlock DoubleValue.ZERO
|
||||
.map { blockPosPair -> blockPosPair.second }
|
||||
|
||||
blockPosList.map { it.toArrayStruct() }.asArrayValue()
|
||||
},
|
||||
"get_nearby_entities" to { params ->
|
||||
val distance = params.getDouble(0)
|
||||
@@ -233,7 +276,7 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
val intangible = params.getBooleanOrNull(0) ?: true
|
||||
thisEntity.noPhysics = intangible
|
||||
DoubleValue.ONE
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
+18
@@ -39,6 +39,7 @@ import net.minecraft.world.entity.Mob
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.TamableAnimal
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.EntityTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
@@ -123,6 +124,23 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
|
||||
duration.toLong()
|
||||
)
|
||||
}
|
||||
map["look_at_entity"] = put@{ params ->
|
||||
val targetEntity = params.get<MoValue>(0)
|
||||
val duration = params.getDoubleOrNull(1)?.toInt() ?: 20
|
||||
|
||||
val lookEntity = if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
|
||||
targetEntity.obj as Entity
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
// Set the current entity to track the looked for entity
|
||||
val brain = entity.brain
|
||||
val tracker = EntityTracker(lookEntity, true)
|
||||
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, tracker, duration.toLong())
|
||||
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["get_wander_control_memory"] = put@{
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
|
||||
+62
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
@@ -34,6 +35,7 @@ import com.cobblemon.mod.common.util.activeDialogue
|
||||
import com.cobblemon.mod.common.util.asBlockPos
|
||||
import com.cobblemon.mod.common.util.asResource
|
||||
import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.blockRegistry
|
||||
import com.cobblemon.mod.common.util.getBattleState
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
@@ -46,15 +48,20 @@ import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.pc
|
||||
import com.cobblemon.mod.common.util.pokedex
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.toArrayStruct
|
||||
import com.cobblemon.mod.common.util.tmList
|
||||
import net.minecraft.client.player.AbstractClientPlayer
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.level.GameType
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import kotlin.collections.iterator
|
||||
|
||||
object PlayerMoLangFunctions : AbstractMoLangFunctionHolder<Player>() {
|
||||
@@ -399,6 +406,61 @@ object PlayerMoLangFunctions : AbstractMoLangFunctionHolder<Player>() {
|
||||
} ?: DoubleValue.ZERO
|
||||
}
|
||||
map["has_chosen_starter"] = { _ -> DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected) }
|
||||
map["ride"] = put@{ params ->
|
||||
val entity = (params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? Entity ?: return@put DoubleValue.ZERO
|
||||
player.startRiding(entity)
|
||||
}
|
||||
map["has_respawn_position"] = {
|
||||
DoubleValue(player.respawnPosition != null)
|
||||
}
|
||||
map["respawn_position"] = {
|
||||
player.respawnPosition?.toArrayStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map["respawn_dimension"] = {
|
||||
StringValue(player.respawnDimension.location().toString())
|
||||
}
|
||||
map["teleport_to_respawn"] = put@{
|
||||
val respawnPosition = player.respawnPosition?.center ?: return@put DoubleValue.ZERO
|
||||
player.teleportTo(respawnPosition.x, respawnPosition.y, respawnPosition.z)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["get_looked_block"] = put@{ params ->
|
||||
// Get the block the player is looking at 'distance' units away
|
||||
val distance = params.getDoubleOrNull(0) ?: 8.0
|
||||
|
||||
// perform clip
|
||||
val level = player.level()
|
||||
val eye = player.eyePosition
|
||||
val view = player.getViewVector(1.0f)
|
||||
val lookPos = eye.add(view.scale(distance))
|
||||
val hitResult = level.clip(ClipContext(eye, lookPos, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player))
|
||||
|
||||
if (hitResult.type == HitResult.Type.BLOCK) {
|
||||
// Get base block info
|
||||
val blockPos = hitResult.blockPos
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
val blockHolder = level.blockRegistry.wrapAsHolder(blockState.block)
|
||||
val blockValue = blockHolder.asMoLangValue(Registries.BLOCK)
|
||||
|
||||
// Attach the position to the blockValue return
|
||||
blockValue.addFunction("x") { DoubleValue(blockPos.x.toDouble()) }
|
||||
blockValue.addFunction("y") { DoubleValue(blockPos.y.toDouble()) }
|
||||
blockValue.addFunction("z") { DoubleValue(blockPos.z.toDouble()) }
|
||||
|
||||
return@put blockValue
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_battling"] = {
|
||||
DoubleValue(player.isInBattle())
|
||||
}
|
||||
}
|
||||
|
||||
if (player is AbstractClientPlayer) {
|
||||
map["is_battling"] = {
|
||||
DoubleValue(CobblemonClient.battle?.spectating == false) // Checks not-null and not-spectating at the same time, trust
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
|
||||
+28
@@ -15,6 +15,7 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMovesetBuilders
|
||||
import com.cobblemon.mod.common.CobblemonPokeSkills
|
||||
import com.cobblemon.mod.common.api.mark.Marks
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
@@ -26,6 +27,9 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
@@ -447,6 +451,30 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
evolution?.forceEvolve(pokemon)
|
||||
StringValue(evolution.toString())
|
||||
}
|
||||
map["is_sent_out"] = {
|
||||
DoubleValue(pokemon.state is SentOutState)
|
||||
}
|
||||
map["is_shouldered"] = {
|
||||
DoubleValue(pokemon.state is ShoulderedState)
|
||||
}
|
||||
map["entity"] = {
|
||||
(pokemon.state as? ActivePokemonState)?.entity?.struct ?: DoubleValue.ZERO
|
||||
}
|
||||
map["is_rideable"] = put@{
|
||||
return@put DoubleValue(!pokemon.riding.behaviours.isNullOrEmpty())
|
||||
}
|
||||
map["has_status"] = put@{ params ->
|
||||
val status = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
return@put DoubleValue(pokemon.status?.status?.name == status)
|
||||
}
|
||||
// TODO remove this or figure a way to determine which tier of the pokeskill is being used
|
||||
map["add_skill_cooldown"] = put@{ params ->
|
||||
val skillId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val cooldownTicks = params.getIntOrNull(1)
|
||||
val skill = CobblemonPokeSkills.skills[skillId] ?: return@put DoubleValue.ZERO
|
||||
pokemon.addSkillCooldown(skillId, cooldownTicks ?: skill.cooldownTicks ?: 0)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
map["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
|
||||
|
||||
+23
-2
@@ -40,6 +40,7 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.Level.ExplosionInteraction
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
|
||||
@@ -61,6 +62,26 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
}
|
||||
map["add_furnace_fuel"] = put@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val fuelTicks = params.getInt(3)
|
||||
|
||||
val blockEntity = world.getBlockEntity(BlockPos(x, y, z))
|
||||
if (blockEntity is AbstractFurnaceBlockEntity) {
|
||||
// Add fuel time to the furnace
|
||||
blockEntity.litTime += fuelTicks
|
||||
|
||||
// If this exceeds the litDuration then bump it up
|
||||
if (blockEntity.litTime >= blockEntity.litDuration) {
|
||||
blockEntity.litDuration = blockEntity.litTime
|
||||
}
|
||||
blockEntity.setChanged()
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["game_time"] = { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map["time_of_day"] = put@{
|
||||
val time = world.dayTime % 24000
|
||||
@@ -128,13 +149,13 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3).toFloat()
|
||||
val range = params.getDoubleOrNull(3) ?: 8.0
|
||||
world.explode(
|
||||
null,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
range,
|
||||
range.toFloat(),
|
||||
ExplosionInteraction.valueOf(
|
||||
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name
|
||||
)
|
||||
|
||||
+2
-1
@@ -50,7 +50,7 @@ class ActionEffectTimeline(
|
||||
}
|
||||
|
||||
fun chainKeyframes(context: ActionEffectContext, iterator: Iterator<ActionEffectKeyframe>, finalFuture: CompletableFuture<Unit>) {
|
||||
if (!iterator.hasNext()) {
|
||||
if (!iterator.hasNext() || context.cancelled) {
|
||||
finalFuture.complete(Unit)
|
||||
} else {
|
||||
val keyframe = iterator.next()
|
||||
@@ -70,6 +70,7 @@ class ActionEffectContext(
|
||||
val runtime: MoLangRuntime,
|
||||
var canBeInterrupted: Boolean = false,
|
||||
var interrupted: Boolean = false,
|
||||
var cancelled: Boolean = false,
|
||||
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf(),
|
||||
var level: Level? = null
|
||||
) {
|
||||
|
||||
@@ -48,6 +48,8 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
|
||||
ActionEffectKeyframe.register<CannotInterruptActionEffectKeyframe>("cannot_interrupt")
|
||||
ActionEffectKeyframe.register<RemoveHoldsActionEffectKeyframe>("remove_holds")
|
||||
ActionEffectKeyframe.register<AddHoldsActionEffectKeyframe>("add_holds")
|
||||
ActionEffectKeyframe.register<PauseTasksActionEffectKeyframe>("pause_tasks")
|
||||
ActionEffectKeyframe.register<ResumeTasksActionEffectKeyframe>("resume_tasks")
|
||||
ActionEffectKeyframe.register<MoveToTargetActionEffectKeyframe>("move_to_target")
|
||||
ActionEffectKeyframe.register<ReturnToPositionActionEffectKeyframe>("return_to_position")
|
||||
ActionEffectKeyframe.register<PauseActionEffectKeyframe>("pause")
|
||||
@@ -57,6 +59,7 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
|
||||
ActionEffectKeyframe.register<RunActionEffectKeyframe>("run_action_effect")
|
||||
ActionEffectKeyframe.register<EntityParticlesActionEffectKeyframe>("entity_particles")
|
||||
ActionEffectKeyframe.register<EntitySoundActionEffectKeyframe>("entity_sound")
|
||||
ActionEffectKeyframe.register<CancelActionEffectKeyframe>("cancel")
|
||||
}
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class CancelActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
context.cancelled = true
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+28
-6
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.commands.arguments.EntityArgument.getPlayers
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Spawns particles on the entity based on the given particle effect and locator.
|
||||
@@ -65,15 +66,36 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
|
||||
allEntities.any { player.distanceTo(it) <= visibilityRange }
|
||||
}
|
||||
|
||||
// Try to resolve a world-position target from runtime variables
|
||||
val runtimeTargetPosition: Vec3? = try {
|
||||
val x = "v.target_x".asExpressionLike().resolveDouble(context.runtime)
|
||||
val y = "v.target_y".asExpressionLike().resolveDouble(context.runtime)
|
||||
val z = "v.target_z".asExpressionLike().resolveDouble(context.runtime)
|
||||
if (x != 0.0 || y != 0.0 || z != 0.0) Vec3(x, y, z) else null
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
sourceEntities.filter { it is PosableEntity }.forEach { entity ->
|
||||
if (targetLocators == null) {
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators)
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
else {
|
||||
// If target entities are specified, send the particle using entity locators/position.
|
||||
// If there are no target entities then attempt to send to v.target world position or send without target.
|
||||
if (targetEntities.isNotEmpty()) {
|
||||
targetEntities.forEach { targetEntity ->
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators, targetEntity.id, targetLocators!!.toList())
|
||||
if (targetLocators != null) {
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators, targetEntity.id, targetLocators!!.toList())
|
||||
packet.sendToPlayers(players)
|
||||
} else {
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators, targetEntity.id)
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (runtimeTargetPosition != null) {
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators, targetPosition = runtimeTargetPosition)
|
||||
packet.sendToPlayers(players)
|
||||
} else {
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locators)
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -8,8 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
class PauseTasksActionEffectKeyframe : ActionEffectKeyframe {
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.filter { it is UsersProvider || it is NPCProvider }
|
||||
.flatMap { it.entities }
|
||||
|
||||
for (entity in entities) {
|
||||
if (entity is LivingEntity) {
|
||||
entity.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
class ResumeTasksActionEffectKeyframe : ActionEffectKeyframe {
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.filter { it is UsersProvider || it is NPCProvider }
|
||||
.flatMap { it.entities }
|
||||
|
||||
for (entity in entities) {
|
||||
if (entity is LivingEntity) {
|
||||
val level = entity.level()
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+25
-2
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@@ -18,16 +20,37 @@ import net.minecraft.resources.ResourceLocation
|
||||
* effect is marked complete only when the action effect it runs is complete unless [waitForActionEffect] is
|
||||
* false.
|
||||
*
|
||||
* If [expression] is set, it will be resolved at runtime to determine the action effect identifier.
|
||||
* This allows dynamic delegation based on MoLang variables (e.g. v.granting_move).
|
||||
* If [expression] is not set, the static [actionEffect] identifier is used instead.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
class RunActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
val actionEffect: ResourceLocation? = null
|
||||
val expression: ExpressionLike? = null
|
||||
val waitForActionEffect = true
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return if (actionEffect != null) {
|
||||
ActionEffects.actionEffects[actionEffect]?.run(context)?.takeIf { waitForActionEffect } ?: skip()
|
||||
val effectId = if (expression != null) {
|
||||
val resolved = expression.resolveString(context.runtime)
|
||||
if (resolved == "0" || resolved.isBlank()) {
|
||||
Cobblemon.LOGGER.warn("RunActionEffectKeyframe expression resolved to empty/zero string: $resolved")
|
||||
return skip()
|
||||
}
|
||||
try {
|
||||
ResourceLocation.parse(resolved)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.warn("RunActionEffectKeyframe expression resolved to invalid resource location: $resolved with exception: $e")
|
||||
return skip()
|
||||
}
|
||||
} else {
|
||||
actionEffect
|
||||
}
|
||||
return if (effectId != null) {
|
||||
ActionEffects.actionEffects[effectId]?.run(context)?.takeIf { waitForActionEffect } ?: skip()
|
||||
} else {
|
||||
Cobblemon.LOGGER.warn("RunActionEffectKeyframe has no action effect identifier or expression to run")
|
||||
skip()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ enum class ParticleMaterial {
|
||||
ALPHA,
|
||||
OPAQUE,
|
||||
BLEND,
|
||||
ADD
|
||||
ADD,
|
||||
XRAY,
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ object ParticleMaterials {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
@@ -38,6 +39,7 @@ object ParticleMaterials {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.disableCull()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
@@ -52,6 +54,7 @@ object ParticleMaterials {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.disableCull()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
@@ -66,6 +69,7 @@ object ParticleMaterials {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.enableCull()
|
||||
RenderSystem.enableDepthTest()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
@@ -73,6 +77,21 @@ object ParticleMaterials {
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
}
|
||||
|
||||
override fun toString() = "OPAQUE"
|
||||
override fun toString() = "OPAQUE"
|
||||
}
|
||||
|
||||
val XRAY = object : ParticleRenderType {
|
||||
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.disableCull()
|
||||
RenderSystem.disableDepthTest()
|
||||
RenderSystem.depthMask(false)
|
||||
RenderSystem.setShader { GameRenderer.getParticleShader() }
|
||||
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
|
||||
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
|
||||
}
|
||||
|
||||
override fun toString() = "XRAY"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -102,7 +102,7 @@ open class PlayerPartyStore(
|
||||
for (pokemon in this) {
|
||||
// Awake from fainted
|
||||
if (pokemon.isFainted()) {
|
||||
//Skip awaken logic check if config value is 0
|
||||
// Skip awaken logic check if config value is 0
|
||||
if (Cobblemon.config.faintAwakenHealthPercent > 0) {
|
||||
pokemon.faintedTimer -= 1
|
||||
if (pokemon.faintedTimer <= -1) {
|
||||
@@ -119,7 +119,7 @@ open class PlayerPartyStore(
|
||||
}
|
||||
// Passive healing while less than full health
|
||||
else if (pokemon.currentHealth < pokemon.maxHealth) {
|
||||
//Skip passive healing logic check if config value is 0
|
||||
// Skip passive healing logic check if config value is 0
|
||||
if (Cobblemon.config.healPercent > 0) {
|
||||
pokemon.healTimer--
|
||||
if (pokemon.healTimer <= -1) {
|
||||
|
||||
@@ -179,6 +179,8 @@ object CobblemonClient {
|
||||
return@subscribe
|
||||
}
|
||||
|
||||
storage.party.forEach { it?.tickSkillCooldowns() }
|
||||
|
||||
val nearbyPokemon = player.level().getEntities(
|
||||
player,
|
||||
AABB.ofSize(player.position(), 16.0, 16.0, 16.0)
|
||||
|
||||
+6
-6
@@ -28,7 +28,7 @@ class InteractWheelButton(
|
||||
private val tooltipText: String?,
|
||||
x: Int,
|
||||
y: Int,
|
||||
private val isEnabled: Boolean,
|
||||
private val isEnabled: () -> Boolean,
|
||||
private val colour: () -> Vector3f?,
|
||||
onPress: OnPress,
|
||||
private val canHover: (Double, Double) -> Boolean
|
||||
@@ -74,12 +74,12 @@ class InteractWheelButton(
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = if (isHovered(mouseX.toFloat(), mouseY.toFloat()) && isEnabled) height else 0,
|
||||
vOffset = if (isHovered(mouseX.toFloat(), mouseY.toFloat()) && isEnabled()) height else 0,
|
||||
textureHeight = height * 2,
|
||||
alpha = if (isEnabled) 1f else 0.4F
|
||||
alpha = if (isEnabled()) 1f else 0.4F
|
||||
)
|
||||
|
||||
if(isEnabled && isHovered(mouseX.toFloat(), mouseY.toFloat())){
|
||||
if (isEnabled() && isHovered(mouseX.toFloat(), mouseY.toFloat())){
|
||||
tooltipText?.let {
|
||||
context.renderTooltip(Minecraft.getInstance().font, Component.translatable(it), mouseX, mouseY)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class InteractWheelButton(
|
||||
y = iconY,
|
||||
width = ICON_SIZE,
|
||||
height = ICON_SIZE,
|
||||
alpha = if (isEnabled) 1F else 0.2F,
|
||||
alpha = if (isEnabled()) 1F else 0.2F,
|
||||
red = colour.x,
|
||||
green = colour.y,
|
||||
blue = colour.z,
|
||||
@@ -114,7 +114,7 @@ class InteractWheelButton(
|
||||
y = iconY - ICON_SIZE,
|
||||
width = ICON_SIZE,
|
||||
height = ICON_SIZE,
|
||||
alpha = if (isEnabled) 1F else 0.4F,
|
||||
alpha = if (isEnabled()) 1F else 0.4F,
|
||||
red = colour.x,
|
||||
green = colour.y,
|
||||
blue = colour.z,
|
||||
|
||||
+3
-3
@@ -18,7 +18,7 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.chat.Component
|
||||
import kotlin.math.max
|
||||
|
||||
class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title), CobblemonRenderable {
|
||||
open class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title), CobblemonRenderable {
|
||||
companion object {
|
||||
const val SIZE = 170
|
||||
private val backgroundResource = cobblemonResource("textures/gui/interact/interact_wheel_base.png")
|
||||
@@ -32,7 +32,7 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
private val buttons = mutableListOf<InteractWheelButton>()
|
||||
protected val buttons = mutableListOf<InteractWheelButton>()
|
||||
private var maxPage = 1
|
||||
private var currentPage = 0
|
||||
override fun renderBlurredBackground(delta: Float) { }
|
||||
@@ -104,7 +104,7 @@ class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelO
|
||||
tooltipText = option?.tooltipText,
|
||||
x = x,
|
||||
y = y,
|
||||
isEnabled = option != null && option.enabled,
|
||||
isEnabled = { option != null && option.enabled() },
|
||||
colour = option?.colour ?: { null },
|
||||
onPress = { option?.onPress?.invoke() },
|
||||
canHover = { a: Double, b: Double -> !isMouseInCenter(a, b)}
|
||||
|
||||
+71
-4
@@ -9,20 +9,25 @@
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonPokeSkills
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.interact.battleRequest.BattleConfigureGUI
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.UsePokeSkillPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.common.collect.ArrayListMultimap
|
||||
import com.google.common.collect.Multimap
|
||||
import java.util.UUID
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.player.AbstractClientPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import org.joml.Vector3f
|
||||
|
||||
@@ -30,7 +35,7 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canGive
|
||||
val mountShoulder = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/interact_wheel_icon_shoulder.png"),
|
||||
tooltipText = "cobblemon.ui.interact.mount.shoulder",
|
||||
enabled = canMountShoulder,
|
||||
enabled = { canMountShoulder },
|
||||
onPress = {
|
||||
if (canMountShoulder) {
|
||||
InteractPokemonPacket(pokemonID, InteractTypePokemon.SHOULDER).sendToServer()
|
||||
@@ -41,7 +46,7 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canGive
|
||||
val giveHeldItem = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/interact_wheel_icon_held_item.png"),
|
||||
tooltipText = "cobblemon.ui.interact.give.item",
|
||||
enabled = canGiveHeld,
|
||||
enabled = { canGiveHeld },
|
||||
onPress = {
|
||||
if (canGiveHeld) {
|
||||
InteractPokemonPacket(pokemonID, InteractTypePokemon.HELD_ITEM).sendToServer()
|
||||
@@ -52,7 +57,7 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canGive
|
||||
val giveCosmeticItem = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/interact_wheel_icon_cosmetic_item.png"),
|
||||
tooltipText = "cobblemon.ui.interact.give.cosmetic_item",
|
||||
enabled = canGiveCosmetic,
|
||||
enabled = { canGiveCosmetic },
|
||||
onPress = {
|
||||
if (canGiveCosmetic) {
|
||||
InteractPokemonPacket(pokemonID, InteractTypePokemon.COSMETIC_ITEM).sendToServer()
|
||||
@@ -64,7 +69,7 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canGive
|
||||
val ride = InteractWheelOption(
|
||||
iconResource = cobblemonResource("textures/gui/interact/interact_wheel_icon_ride.png"),
|
||||
tooltipText = "cobblemon.ui.interact.ride",
|
||||
enabled = canRide,
|
||||
enabled = { canRide },
|
||||
onPress = {
|
||||
if (canRide) {
|
||||
InteractPokemonPacket(pokemonID, InteractTypePokemon.RIDE).sendToServer()
|
||||
@@ -177,6 +182,68 @@ fun createPlayerInteractGui(optionsPacket: PlayerInteractOptionsPacket): Interac
|
||||
return InteractWheelGUI(options, Component.translatable("cobblemon.ui.interact.player"))
|
||||
}
|
||||
|
||||
fun createPokeSkillGui(player: AbstractClientPlayer, pokemon: Pokemon): InteractWheelGUI {
|
||||
val accessibleSkills = CobblemonPokeSkills.getAccessiblePokeSkillTiers(player, pokemon)
|
||||
val options: Multimap<Orientation, InteractWheelOption> = ArrayListMultimap.create()
|
||||
for (skill in accessibleSkills) {
|
||||
val skillOption = InteractWheelOption(
|
||||
iconResource = skill.icon,
|
||||
enabled = { skill.id !in pokemon.skillCooldownsTicks },
|
||||
tooltipText = "${skill.name.asTranslated().string} ${getRomanNumeral(skill.tier)}",
|
||||
onPress = {
|
||||
CobblemonNetwork.sendToServer(UsePokeSkillPacket(skillId = skill.id, pokemonId = pokemon.uuid))
|
||||
closeGUI()
|
||||
}
|
||||
)
|
||||
|
||||
when (options.size()) {
|
||||
0 -> options.put(Orientation.NORTH, skillOption)
|
||||
1 -> options.put(Orientation.NORTHEAST, skillOption)
|
||||
2 -> options.put(Orientation.SOUTHEAST, skillOption)
|
||||
3 -> options.put(Orientation.SOUTH, skillOption)
|
||||
4 -> options.put(Orientation.SOUTHWEST, skillOption)
|
||||
5 -> options.put(Orientation.NORTHWEST, skillOption)
|
||||
6 -> options.put(Orientation.WEST, skillOption)
|
||||
7 -> options.put(Orientation.EAST, skillOption)
|
||||
else -> break // We only have 8 slots
|
||||
}
|
||||
}
|
||||
|
||||
return PokeSkillWheelGUI(options, Component.translatable("cobblemon.ui.interact.poke_skill"))
|
||||
}
|
||||
|
||||
private fun getRomanNumeral(number: Int): String {
|
||||
if (number <= 0) return number.toString()
|
||||
|
||||
val numerals = listOf(
|
||||
1000 to "M",
|
||||
900 to "CM",
|
||||
500 to "D",
|
||||
400 to "CD",
|
||||
100 to "C",
|
||||
90 to "XC",
|
||||
50 to "L",
|
||||
40 to "XL",
|
||||
10 to "X",
|
||||
9 to "IX",
|
||||
5 to "V",
|
||||
4 to "IV",
|
||||
1 to "I"
|
||||
)
|
||||
|
||||
var remaining = number
|
||||
val result = StringBuilder()
|
||||
|
||||
for ((value, numeral) in numerals) {
|
||||
while (remaining >= value) {
|
||||
result.append(numeral)
|
||||
remaining -= value
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
private fun getLangKey(status: PlayerInteractOptionsPacket.OptionStatus) : String {
|
||||
return when (status) {
|
||||
PlayerInteractOptionsPacket.OptionStatus.TOO_FAR -> "cobblemon.ui.interact.too_far"
|
||||
|
||||
+2
-2
@@ -12,9 +12,9 @@ import net.minecraft.resources.ResourceLocation
|
||||
import org.joml.Vector3f
|
||||
|
||||
data class InteractWheelOption(
|
||||
val iconResource: ResourceLocation,
|
||||
val iconResource: ResourceLocation? = null,
|
||||
val secondaryIconResource: ResourceLocation? = null,
|
||||
val enabled: Boolean = true,
|
||||
val enabled: () -> Boolean = { true },
|
||||
val tooltipText: String?,
|
||||
val colour: () -> Vector3f? = { null },
|
||||
val onPress: () -> Unit
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.google.common.collect.Multimap
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
class PokeSkillWheelGUI(
|
||||
private val options: Multimap<Orientation, InteractWheelOption>,
|
||||
title: Component
|
||||
) : InteractWheelGUI(options, title) {
|
||||
|
||||
/**
|
||||
* Due to the button being held swallow the event
|
||||
*/
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (button == 0) return false
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
/**
|
||||
* On release will close the wheel and trigger the button press of the hovered button
|
||||
* TODO: make this wheel choose more like pizza slices.
|
||||
*/
|
||||
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (button == 0) {
|
||||
// Manually trigger the hovered button's press
|
||||
buttons.forEach { btn ->
|
||||
if (btn.isMouseOver(mouseX, mouseY)) {
|
||||
btn.onClick(mouseX, mouseY)
|
||||
return true
|
||||
}
|
||||
}
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
return true
|
||||
}
|
||||
return super.mouseReleased(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
}
|
||||
+11
-21
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.interact.wheel.createPokeSkillGui
|
||||
import com.cobblemon.mod.common.client.keybind.CobblemonBlockingKeyBinding
|
||||
import com.cobblemon.mod.common.client.keybind.KeybindCategories
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -26,12 +27,12 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.isUsingPokedex
|
||||
import com.cobblemon.mod.common.util.traceFirstEntityCollision
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import kotlin.math.pow
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import kotlin.math.pow
|
||||
|
||||
object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
"key.cobblemon.throwpartypokemon",
|
||||
@@ -39,32 +40,17 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
InputConstants.KEY_R,
|
||||
KeybindCategories.COBBLEMON_CATEGORY
|
||||
) {
|
||||
const val POKE_SKILL_TIME = 0.7F
|
||||
var canApplyChange = true
|
||||
var heldDownSeconds = 0F
|
||||
|
||||
fun actioned() {
|
||||
canApplyChange = false
|
||||
wasDown = true
|
||||
heldDownSeconds = 0F
|
||||
}
|
||||
|
||||
fun canAction() = canApplyChange && Minecraft.getInstance().player?.isUsingPokedex() == false
|
||||
|
||||
override fun onTick() {
|
||||
if (wasDown) {
|
||||
if (heldDownSeconds < 100) {
|
||||
heldDownSeconds += Minecraft.getInstance().timer.getGameTimeDeltaPartialTick(false)
|
||||
}
|
||||
} else {
|
||||
heldDownSeconds = 0F
|
||||
}
|
||||
|
||||
super.onTick()
|
||||
}
|
||||
|
||||
override fun onRelease() {
|
||||
val canAction = canAction()
|
||||
wasDown = false
|
||||
canApplyChange = true
|
||||
if (!canAction) {
|
||||
return
|
||||
@@ -105,7 +91,8 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
entityClass = LivingEntity::class.java,
|
||||
ignoreEntity = player,
|
||||
maxDistance = Cobblemon.config.battleSpectateMaxDistance,
|
||||
collideBlock = ClipContext.Fluid.NONE)
|
||||
collideBlock = ClipContext.Fluid.NONE
|
||||
)
|
||||
if (canSendOutPokemon(player, targetEntity)) {
|
||||
sendToServer(SendOutPokemonPacket(CobblemonClient.storage.selectedSlot))
|
||||
} else {
|
||||
@@ -127,10 +114,13 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
|
||||
//The server sends a packet back that opens the player interaction menu with the proper options
|
||||
sendToServer(RequestPlayerInteractionsPacket(entity.uuid, entity.id, pokemon.uuid))
|
||||
}
|
||||
|
||||
is PokemonEntity -> {
|
||||
if (!entity.canBattle(player) || entity.position().distanceToSqr(player.position()) > Cobblemon.config.battleWildMaxDistance.pow(2)) return
|
||||
sendToServer(BattleChallengePacket(entity.id, pokemon.uuid, BattleFormat.GEN_9_SINGLES))
|
||||
}
|
||||
if (!entity.canBattle(player) || entity.position()
|
||||
.distanceToSqr(player.position()) > Cobblemon.config.battleWildMaxDistance.pow(2)
|
||||
) return
|
||||
sendToServer(BattleChallengePacket(entity.id, pokemon.uuid, BattleFormat.GEN_9_SINGLES))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormEntityParticlePacket> {
|
||||
override fun handle(packet: SpawnSnowstormEntityParticlePacket, client: Minecraft) {
|
||||
@@ -31,6 +32,7 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
|
||||
val targetState = (targetedEntity as? PosableEntity)?.delegate as? PosableState
|
||||
val sourceLocators = packet.sourceLocators.firstNotNullOfOrNull { sourceState.getMatchingLocators(it).takeIf { it.isNotEmpty() } } ?: return
|
||||
val targetedLocator = packet.targetLocators?.firstOrNull { targetState?.getMatchingLocators(it)?.isNotEmpty() == true }
|
||||
val targetPosition = packet.targetPosition
|
||||
val rootMatrix = sourceState.locatorStates["root"]!!
|
||||
|
||||
sourceLocators.forEach { locator ->
|
||||
@@ -40,6 +42,16 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
|
||||
val particleRuntime = MoLangRuntime().setup().setupClient()
|
||||
particleRuntime.environment.query.addFunction("entity") { sourceState.runtime.environment.query }
|
||||
|
||||
// Target resolution priority: entity+locator > world position > none
|
||||
val targetPos: (() -> Vec3)? = if (targetedEntity != null) {
|
||||
{ targetedLocator?.let { targetState?.locatorStates?.get(it) }?.getOrigin() ?: targetedEntity.position() }
|
||||
} else if (targetPosition != null) {
|
||||
val pos = packet.targetPosition
|
||||
{ pos }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val storm = ParticleStorm(
|
||||
effect = effect,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
@@ -49,7 +61,7 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
|
||||
sourceVelocity = { sourceEntity.deltaMovement },
|
||||
sourceAlive = { !sourceEntity.isRemoved },
|
||||
sourceVisible = { !sourceEntity.isInvisible },
|
||||
targetPos = if (targetedEntity != null) { { targetedLocator?.let { targetState?.locatorStates?.get(it) }?.getOrigin() ?: targetedEntity.position() } } else null,
|
||||
targetPos = targetPos,
|
||||
entity = sourceEntity
|
||||
)
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ class SnowstormParticle(
|
||||
ParticleMaterial.OPAQUE -> ParticleMaterials.OPAQUE
|
||||
ParticleMaterial.BLEND -> ParticleMaterials.BLEND
|
||||
ParticleMaterial.ADD -> ParticleMaterials.ADD
|
||||
ParticleMaterial.XRAY -> ParticleMaterials.XRAY
|
||||
}
|
||||
}
|
||||
storm.effect.particle.creationEvents.forEach { it.trigger(storm, this) }
|
||||
|
||||
-32
@@ -219,36 +219,4 @@ fun generateAdditionalRideBoostsTooltip(stack: ItemStack): MutableList<Component
|
||||
}
|
||||
|
||||
return resultLines
|
||||
}
|
||||
|
||||
private fun getRomanNumeral(number: Int): String {
|
||||
if (number <= 0) return number.toString()
|
||||
|
||||
val numerals = listOf(
|
||||
1000 to "M",
|
||||
900 to "CM",
|
||||
500 to "D",
|
||||
400 to "CD",
|
||||
100 to "C",
|
||||
90 to "XC",
|
||||
50 to "L",
|
||||
40 to "XL",
|
||||
10 to "X",
|
||||
9 to "IX",
|
||||
5 to "V",
|
||||
4 to "IV",
|
||||
1 to "I"
|
||||
)
|
||||
|
||||
var remaining = number
|
||||
val result = StringBuilder()
|
||||
|
||||
for ((value, numeral) in numerals) {
|
||||
while (remaining >= value) {
|
||||
result.append(numeral)
|
||||
remaining -= value
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
@@ -313,6 +313,9 @@ class CobblemonConfig {
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "infinite_tm_uses", SERVER)
|
||||
var infiniteTmUses = false
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "infinite_poke_skill_cooldown", SERVER)
|
||||
var infinitePokeSkillCooldown = false
|
||||
|
||||
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex", CLIENT)
|
||||
var hideUnimplementedPokemonInThePokedex = false
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ package com.cobblemon.mod.common.data
|
||||
|
||||
import com.cobblemon.mod.common.*
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonBehaviours
|
||||
import com.cobblemon.mod.common.CobblemonCallbacks
|
||||
import com.cobblemon.mod.common.CobblemonCosmeticItems
|
||||
import com.cobblemon.mod.common.CobblemonMechanics
|
||||
import com.cobblemon.mod.common.CobblemonPokeSkills
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
@@ -73,6 +80,7 @@ object CobblemonDataProvider : DataProvider {
|
||||
this.register(GlobalSpeciesFeatures, reloadable = false)
|
||||
this.register(SpeciesFeatureAssignments, reloadable = false)
|
||||
this.register(ActionEffects, reloadable = true)
|
||||
this.register(CobblemonPokeSkills, reloadable = true)
|
||||
this.register(Moves, reloadable = false)
|
||||
this.register(CobblemonMovesetBuilders, reloadable = false)
|
||||
this.register(Abilities, reloadable = false)
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.pokeskills
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.blockRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_LIKE_CODEC
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.projectile.ProjectileUtil
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.HitResult
|
||||
|
||||
/**
|
||||
* The reference object for a skill that a Pokémon can use in the overworld.
|
||||
*
|
||||
* Most skills are "wrapper" skills that play a move's action effect at a target and run
|
||||
* some MoLang on impact. Custom skills can define a full action effect timeline.
|
||||
*
|
||||
* Access is auto-derived from requiredMoves/Abilities/Types/Species lists. If none are
|
||||
* populated, accessCondition is used as a fallback.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 31st, 2025
|
||||
*/
|
||||
class PokeSkill(
|
||||
val name: String,
|
||||
val enabled: Boolean? = null,
|
||||
val actionEffect: ActionEffectTimeline? = null,
|
||||
val icon: ResourceLocation? = null,
|
||||
val cooldownTicks: Int? = null,
|
||||
val accessCondition: ExpressionLike? = null,
|
||||
val useCondition: ExpressionLike? = null,
|
||||
|
||||
val targetType: PokeSkillTargetType? = null,
|
||||
val range: Double? = null,
|
||||
val targetCondition: ExpressionLike? = null,
|
||||
|
||||
val requiredMoves: List<String>? = null,
|
||||
val requiredAbilities: List<String>? = null,
|
||||
val requiredTypes: List<String>? = null,
|
||||
val requiredSpecies: List<String>? = null,
|
||||
|
||||
val hitDelay: ExpressionLike? = null,
|
||||
val hitEffect: ResourceLocation? = null,
|
||||
val fallbackActionEffect: ResourceLocation? = null,
|
||||
|
||||
val variables: Map<String, Any>? = null,
|
||||
|
||||
val tiers: List<PokeSkillTier>,
|
||||
) {
|
||||
companion object {
|
||||
val runtime = MoLangRuntime().setup()
|
||||
// TODO add all fields to codec
|
||||
val CODEC = RecordCodecBuilder.create<PokeSkill> { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.fieldOf("name").forGetter { it.name },
|
||||
Codec.BOOL.fieldOf("enabled").forGetter { it.enabled },
|
||||
Codec.unit<ActionEffectTimeline> { ActionEffectTimeline.NONE }.fieldOf("actionEffect").forGetter { it.actionEffect },
|
||||
ResourceLocation.CODEC.fieldOf("icon").forGetter { it.icon },
|
||||
Codec.INT.fieldOf("cooldownTicks").forGetter { it.cooldownTicks },
|
||||
EXPRESSION_LIKE_CODEC.fieldOf("accessCondition").forGetter { it.accessCondition },
|
||||
EXPRESSION_LIKE_CODEC.fieldOf("useCondition").forGetter { it.useCondition }
|
||||
).apply(instance) { name, enabled, actionEffect, icon, cooldownTicks, accessCondition, useCondition ->
|
||||
PokeSkill(
|
||||
name = name,
|
||||
enabled = enabled,
|
||||
actionEffect = actionEffect,
|
||||
icon = icon,
|
||||
cooldownTicks = cooldownTicks,
|
||||
accessCondition = accessCondition,
|
||||
useCondition = useCondition,
|
||||
targetType = null,
|
||||
range = null,
|
||||
targetCondition = null,
|
||||
requiredMoves = null,
|
||||
requiredAbilities = null,
|
||||
requiredTypes = null,
|
||||
requiredSpecies = null,
|
||||
hitDelay = null,
|
||||
hitEffect = null,
|
||||
fallbackActionEffect = null,
|
||||
tiers = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val PACKET_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC)
|
||||
}
|
||||
|
||||
@Transient
|
||||
lateinit var id: ResourceLocation
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.pokeskills
|
||||
|
||||
enum class PokeSkillTargetType {
|
||||
NONE, SELF, BLOCK, ENTITY, BOTH
|
||||
}
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.pokeskills
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.entity.pokemon.pokeskills.PokeSkill.Companion.runtime
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.blockRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.projectile.ProjectileUtil
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import kotlin.collections.set
|
||||
|
||||
class PokeSkillTier(
|
||||
@SerializedName("enabled")
|
||||
private val _enabled: Boolean? = null,
|
||||
@SerializedName("actionEffect")
|
||||
private val _actionEffect: ActionEffectTimeline? = null,
|
||||
@SerializedName("icon")
|
||||
private val _icon: ResourceLocation? = null,
|
||||
@SerializedName("cooldownTicks")
|
||||
private val _cooldownTicks: Int? = null,
|
||||
@SerializedName("accessCondition")
|
||||
private val _accessCondition: ExpressionLike? = null,
|
||||
@SerializedName("useCondition")
|
||||
private val _useCondition: ExpressionLike? = null,
|
||||
|
||||
@SerializedName("targetType")
|
||||
private val _targetType: PokeSkillTargetType? = null,
|
||||
@SerializedName("range")
|
||||
private val _range: Double? = null,
|
||||
@SerializedName("targetCondition")
|
||||
private val _targetCondition: ExpressionLike? = null,
|
||||
|
||||
@SerializedName("requiredMoves")
|
||||
private val _requiredMoves: List<String>? = null,
|
||||
@SerializedName("requiredAbilities")
|
||||
private val _requiredAbilities: List<String>? = null,
|
||||
@SerializedName("requiredTypes")
|
||||
private val _requiredTypes: List<String>? = null,
|
||||
@SerializedName("requiredSpecies")
|
||||
private val _requiredSpecies: List<String>? = null,
|
||||
|
||||
@SerializedName("hitDelay")
|
||||
private val _hitDelay: ExpressionLike? = null,
|
||||
@SerializedName("hitEffect")
|
||||
private val _hitEffect: ResourceLocation? = null,
|
||||
@SerializedName("fallbackActionEffect")
|
||||
private val _fallbackActionEffect: ResourceLocation? = null,
|
||||
|
||||
@SerializedName("variables")
|
||||
private val _variables: Map<String, Any>? = null,
|
||||
) {
|
||||
|
||||
@Transient
|
||||
lateinit var pokeSkill: PokeSkill
|
||||
|
||||
@Transient
|
||||
var tier: Int = -1
|
||||
|
||||
fun initialize(skill: PokeSkill, tier: Int): PokeSkillTier {
|
||||
this.pokeSkill = skill
|
||||
this.tier = tier
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_ACTION_EFFECT = cobblemonResource("default_poke_skill")
|
||||
}
|
||||
|
||||
val id: ResourceLocation
|
||||
get() = pokeSkill.id
|
||||
val name: String
|
||||
get() = pokeSkill.name
|
||||
|
||||
val enabled: Boolean
|
||||
get() = _enabled ?: pokeSkill.enabled ?: true
|
||||
val actionEffect: ActionEffectTimeline?
|
||||
get() = _actionEffect ?: pokeSkill.actionEffect
|
||||
val icon: ResourceLocation?
|
||||
get() = _icon ?: pokeSkill.icon
|
||||
val cooldownTicks: Int
|
||||
get() = _cooldownTicks ?: pokeSkill.cooldownTicks ?: 0
|
||||
val accessCondition: ExpressionLike
|
||||
get() = _accessCondition ?: pokeSkill.accessCondition ?: "true".asExpressionLike()
|
||||
val useCondition: ExpressionLike
|
||||
get() = _useCondition ?: pokeSkill.useCondition ?: "true".asExpressionLike()
|
||||
|
||||
val targetType: PokeSkillTargetType?
|
||||
get() = _targetType ?: pokeSkill.targetType
|
||||
val range: Double?
|
||||
get() = _range ?: pokeSkill.range
|
||||
val targetCondition: ExpressionLike?
|
||||
get() = _targetCondition ?: pokeSkill.targetCondition
|
||||
|
||||
val requiredMoves: List<String>?
|
||||
get() = _requiredMoves ?: pokeSkill.requiredMoves
|
||||
val requiredAbilities: List<String>?
|
||||
get() = _requiredAbilities ?: pokeSkill.requiredAbilities
|
||||
val requiredTypes: List<String>?
|
||||
get() = _requiredTypes ?: pokeSkill.requiredTypes
|
||||
val requiredSpecies: List<String>?
|
||||
get() = _requiredSpecies ?: pokeSkill.requiredSpecies
|
||||
|
||||
val hitDelay: ExpressionLike?
|
||||
get() = _hitDelay ?: pokeSkill.hitDelay
|
||||
val hitEffect: ResourceLocation?
|
||||
get() = _hitEffect ?: pokeSkill.hitEffect
|
||||
val fallbackActionEffect: ResourceLocation?
|
||||
get() = _fallbackActionEffect ?: pokeSkill.fallbackActionEffect
|
||||
|
||||
val variables: Map<String, Any>?
|
||||
get() = _variables ?: pokeSkill.variables
|
||||
|
||||
/**
|
||||
* Determines access by checking the pokemons data against the granting lists.
|
||||
* If no granting lists are populated, accessCondition is used as a fallback.
|
||||
*/
|
||||
fun canAccess(player: Player, pokemon: Pokemon): Boolean {
|
||||
if (!enabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
val requiredMoves = requiredMoves
|
||||
val requiredAbilities = requiredAbilities
|
||||
val requiredTypes = requiredTypes
|
||||
val requiredSpecies = requiredSpecies
|
||||
|
||||
// Check granting lists — if any are populated, check if the player has any of the listed moves/abilities/types/species
|
||||
val hasGrantingLists = !requiredMoves.isNullOrEmpty() || !requiredAbilities.isNullOrEmpty() ||
|
||||
!requiredTypes.isNullOrEmpty() || !requiredSpecies.isNullOrEmpty()
|
||||
|
||||
if (hasGrantingLists) {
|
||||
val moveMatch = !requiredMoves.isNullOrEmpty() && requiredMoves.any { moveName ->
|
||||
pokemon.moveSet.any { it.template.name == moveName }
|
||||
}
|
||||
|
||||
val abilityMatch = !requiredAbilities.isNullOrEmpty() && requiredAbilities.any { abilityName ->
|
||||
pokemon.ability.name == abilityName
|
||||
}
|
||||
|
||||
val typeMatch = !requiredTypes.isNullOrEmpty() && requiredTypes.any { typeName ->
|
||||
pokemon.types.any { it.name.equals(typeName, ignoreCase = true) }
|
||||
}
|
||||
|
||||
val speciesMatch = !requiredSpecies.isNullOrEmpty() && requiredSpecies.any { speciesName ->
|
||||
pokemon.species.name.equals(speciesName, ignoreCase = true)
|
||||
}
|
||||
|
||||
if (!(moveMatch || abilityMatch || typeMatch || speciesMatch)) return false
|
||||
}
|
||||
|
||||
// Always evaluate accessCondition — acts as additional gate when granting lists are present,
|
||||
// or as the sole gate when no granting lists are populated
|
||||
runtime.withQueryValue("player", player.asMostSpecificMoLangValue())
|
||||
runtime.withQueryValue("pokemon", pokemon.struct)
|
||||
pokemon.entity?.let { runtime.withQueryValue("entity", it.asMostSpecificMoLangValue()) }
|
||||
return runtime.resolveBoolean(accessCondition)
|
||||
}
|
||||
|
||||
fun canUse(player: Player, pokemon: Pokemon): Boolean {
|
||||
val status = pokemon.status?.status
|
||||
if ((!Cobblemon.config.infinitePokeSkillCooldown && id in pokemon.skillCooldownsTicks) || player.isInBattle() || status == Statuses.SLEEP || status == Statuses.FROZEN) {
|
||||
return false
|
||||
}
|
||||
runtime.withQueryValue("player", player.asMostSpecificMoLangValue())
|
||||
runtime.withQueryValue("pokemon", pokemon.struct)
|
||||
val canUse = runtime.resolveBoolean(useCondition)
|
||||
return canUse
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves which move from the pokemon's moveset grants this skill.
|
||||
* Returns the first matching move name from requiredMoves, or null if none match.
|
||||
*/
|
||||
fun resolveGrantingMove(pokemon: Pokemon): String? {
|
||||
val requiredMoves = requiredMoves
|
||||
if (requiredMoves.isNullOrEmpty()) return null // need this guard
|
||||
return requiredMoves.firstOrNull { moveName ->
|
||||
pokemon.moveSet.any { it.template.name == moveName }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the action effect to use for the move visual.
|
||||
* Priority: granting move's action effect > fallbackActionEffect > null
|
||||
*/
|
||||
fun resolveVisualEffect(pokemon: Pokemon): ResourceLocation? {
|
||||
val grantingMove = resolveGrantingMove(pokemon)
|
||||
if (grantingMove != null) {
|
||||
val effectId = cobblemonResource(grantingMove)
|
||||
if (ActionEffects.actionEffects.containsKey(effectId)) return effectId
|
||||
Cobblemon.LOGGER.warn("PokeSkill '$id': granting move '$grantingMove' has no action effect at '$effectId'")
|
||||
}
|
||||
return fallbackActionEffect
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the skill on the given pokemon.
|
||||
* Populates context and runs targeting logic.
|
||||
*/
|
||||
fun use(
|
||||
player: ServerPlayer,
|
||||
pokemon: Pokemon,
|
||||
// context information!!!
|
||||
): Boolean {
|
||||
if (!canUse(player, pokemon)) return false
|
||||
val entity = pokemon.entity ?: return false
|
||||
val level = player.level() as? ServerLevel ?: return false
|
||||
|
||||
// I think these should be defaulted but making them nullable means I don't have to adjust the
|
||||
// other jsons that don't populate them yet...
|
||||
val effectiveTargetType = targetType ?: PokeSkillTargetType.NONE
|
||||
val effectiveRange = range ?: 8.0
|
||||
|
||||
// Resolve the action effect to run
|
||||
val actionEffect = actionEffect
|
||||
val effectTimeline = if (actionEffect != null && actionEffect != ActionEffectTimeline.NONE) {
|
||||
actionEffect
|
||||
} else {
|
||||
ActionEffects.actionEffects[DEFAULT_ACTION_EFFECT] ?: run {
|
||||
Cobblemon.LOGGER.warn("PokeSkill '$id': default_poke_skill action effect not found")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Set up runtime
|
||||
val skillRuntime = MoLangRuntime().setup()
|
||||
skillRuntime.withQueryValue("player", player.asMostSpecificMoLangValue())
|
||||
skillRuntime.withQueryValue("pokemon", pokemon.struct)
|
||||
skillRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
skillRuntime.environment.setSimpleVariable("skill_id", StringValue(id.toString()))
|
||||
skillRuntime.environment.setSimpleVariable("target_type", StringValue(effectiveTargetType.name.lowercase()))
|
||||
skillRuntime.environment.query.addFunction("missed") { DoubleValue(0.0) } // move action effects use this to determine if the target particles should play. We want them to always play for skills I believe
|
||||
injectVariables(skillRuntime)
|
||||
|
||||
// Targeting — raycast based on targetType
|
||||
when (effectiveTargetType) {
|
||||
PokeSkillTargetType.SELF -> {
|
||||
injectEntityTarget(skillRuntime, player)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(1.0))
|
||||
}
|
||||
PokeSkillTargetType.BLOCK -> {
|
||||
val blockHit = getPlayerLookedBlock(player, effectiveRange) ?: return false
|
||||
injectBlockTarget(skillRuntime, blockHit, level)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(0.0))
|
||||
}
|
||||
PokeSkillTargetType.ENTITY -> {
|
||||
val entityHit = getPlayerLookedEntity(player, effectiveRange) ?: return false
|
||||
injectEntityTarget(skillRuntime, entityHit)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(1.0))
|
||||
}
|
||||
PokeSkillTargetType.BOTH -> {
|
||||
val entityHit = getPlayerLookedEntity(player, effectiveRange)
|
||||
if (entityHit != null) {
|
||||
injectEntityTarget(skillRuntime, entityHit)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(1.0))
|
||||
} else {
|
||||
val blockHit = getPlayerLookedBlock(player, effectiveRange) ?: return false
|
||||
injectBlockTarget(skillRuntime, blockHit, level)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(0.0))
|
||||
}
|
||||
}
|
||||
PokeSkillTargetType.NONE -> {}
|
||||
}
|
||||
|
||||
// Evaluate target condition
|
||||
val targetCondition = targetCondition
|
||||
if (targetCondition != null && !skillRuntime.resolveBoolean(targetCondition)) return false
|
||||
|
||||
// Inject wrapper skill variables
|
||||
val visualEffect = resolveVisualEffect(pokemon)
|
||||
if (visualEffect != null) {
|
||||
skillRuntime.environment.setSimpleVariable("granting_move", StringValue(visualEffect.toString()))
|
||||
}
|
||||
|
||||
if (hitEffect != null) {
|
||||
skillRuntime.environment.setSimpleVariable("hit_effect", StringValue(hitEffect.toString()))
|
||||
}
|
||||
|
||||
val hitDelay = hitDelay
|
||||
if (hitDelay != null) {
|
||||
skillRuntime.environment.setSimpleVariable("hit_delay", hitDelay.resolve(skillRuntime))
|
||||
}
|
||||
|
||||
// Build providers
|
||||
val providers = mutableListOf<Any>(
|
||||
UsersProvider(entity)
|
||||
)
|
||||
|
||||
// Build and run context
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = effectTimeline,
|
||||
runtime = skillRuntime,
|
||||
providers = providers,
|
||||
level = level
|
||||
)
|
||||
|
||||
effectTimeline.run(context).thenRun {
|
||||
// Auto-apply cooldown after effect completes
|
||||
if (!Cobblemon.config.infinitePokeSkillCooldown) {
|
||||
pokemon.skillCooldownsTicks[id] = cooldownTicks
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility functions for setting up the runtime for a skill.
|
||||
*/
|
||||
private fun injectBlockTarget(runtime: MoLangRuntime, blockHit: BlockHitResult, level: ServerLevel) {
|
||||
val pos = blockHit.blockPos
|
||||
runtime.environment.setSimpleVariable("target_x", DoubleValue(pos.x + 0.5))
|
||||
runtime.environment.setSimpleVariable("target_y", DoubleValue(pos.y + 0.5))
|
||||
runtime.environment.setSimpleVariable("target_z", DoubleValue(pos.z + 0.5))
|
||||
runtime.environment.setSimpleVariable("target_block_x", DoubleValue(pos.x.toDouble()))
|
||||
runtime.environment.setSimpleVariable("target_block_y", DoubleValue(pos.y.toDouble()))
|
||||
runtime.environment.setSimpleVariable("target_block_z", DoubleValue(pos.z.toDouble()))
|
||||
|
||||
val blockState = level.getBlockState(pos)
|
||||
val blockHolder = level.blockRegistry.wrapAsHolder(blockState.block)
|
||||
val blockValue = blockHolder.asMoLangValue(Registries.BLOCK)
|
||||
|
||||
blockValue.addFunction("x") { DoubleValue(pos.x.toDouble()) }
|
||||
blockValue.addFunction("y") { DoubleValue(pos.y.toDouble()) }
|
||||
blockValue.addFunction("z") { DoubleValue(pos.z.toDouble()) }
|
||||
|
||||
runtime.environment.setSimpleVariable("target_block", blockValue)
|
||||
}
|
||||
|
||||
private fun injectEntityTarget(runtime: MoLangRuntime, entityHit: Entity) {
|
||||
runtime.environment.setSimpleVariable("target_x", DoubleValue(entityHit.x))
|
||||
runtime.environment.setSimpleVariable("target_y", DoubleValue(entityHit.y + entityHit.bbHeight / 2.0))
|
||||
runtime.environment.setSimpleVariable("target_z", DoubleValue(entityHit.z))
|
||||
runtime.environment.setSimpleVariable("target_entity", entityHit.asMostSpecificMoLangValue())
|
||||
}
|
||||
|
||||
private fun injectVariables(runtime: MoLangRuntime) {
|
||||
variables?.forEach { (key, value) ->
|
||||
runtime.environment.setSimpleVariable(
|
||||
key,
|
||||
value.asMostSpecificMoLangValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* simple raycast to get the block the player is looking at.
|
||||
*/
|
||||
private fun getPlayerLookedBlock(player: ServerPlayer, range: Double): BlockHitResult? {
|
||||
val hitResult = player.pick(range, 0f, true)
|
||||
return if (hitResult.type == HitResult.Type.BLOCK) hitResult as BlockHitResult else null
|
||||
}
|
||||
|
||||
/**
|
||||
* Raycast to determine the entity the player is looking at
|
||||
*/
|
||||
private fun getPlayerLookedEntity(player: ServerPlayer, range: Double): Entity? {
|
||||
val eyePosition = player.getEyePosition(1.0f)
|
||||
val lookVector = player.getViewVector(1.0f)
|
||||
val reachVector = eyePosition.add(lookVector.x * range, lookVector.y * range, lookVector.z * range)
|
||||
|
||||
// Create an outer bounding box to find potential candidates
|
||||
val searchBox = player.boundingBox.expandTowards(lookVector.scale(range)).inflate(1.0, 1.0, 1.0)
|
||||
|
||||
// Filter entities within the search box
|
||||
val entityHitResult = ProjectileUtil.getEntityHitResult(
|
||||
player,
|
||||
eyePosition,
|
||||
reachVector,
|
||||
searchBox,
|
||||
{ entity -> !entity.isSpectator && entity.isPickable },
|
||||
range * range
|
||||
)
|
||||
|
||||
return entityHitResult?.entity
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,13 @@ object ServerTickHandler {
|
||||
|
||||
secondsTick++
|
||||
|
||||
if (secondsTick == 20) {
|
||||
secondsTick = 0
|
||||
|
||||
// Party tick
|
||||
for (player in server.playerList.players) {
|
||||
player.party().onSecondPassed(player)
|
||||
// Party tick
|
||||
for (player in server.playerList.players) {
|
||||
val party = player.party()
|
||||
party.forEach { it.tickSkillCooldowns() }
|
||||
if (secondsTick == 20) {
|
||||
secondsTick = 0
|
||||
party.onSecondPassed(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.data
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPokeSkills
|
||||
import com.cobblemon.mod.common.entity.pokemon.pokeskills.PokeSkill
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class PokeSkillsRegistrySyncPacket(
|
||||
pokeSkills: Collection<PokeSkill>
|
||||
) : DataRegistrySyncPacket<PokeSkill, PokeSkillsRegistrySyncPacket>(pokeSkills) {
|
||||
|
||||
override val id = ID
|
||||
|
||||
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: PokeSkill) {
|
||||
PokeSkill.PACKET_CODEC.encode(buffer, entry)
|
||||
}
|
||||
|
||||
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): PokeSkill? {
|
||||
return PokeSkill.PACKET_CODEC.decode(buffer)
|
||||
}
|
||||
|
||||
override fun synchronizeDecoded(entries: Collection<PokeSkill>) {
|
||||
CobblemonPokeSkills.reload(entries.associateBy { it.id })
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("poke_skill_sync")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = PokeSkillsRegistrySyncPacket(emptyList()).apply { decodeBuffer(buffer) }
|
||||
}
|
||||
}
|
||||
+14
-2
@@ -13,10 +13,15 @@ import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHa
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Packet that spawns a snowstorm particle effect on a specified entity and specified locator.
|
||||
* Can specify an optional target+locator to pass through to the spawned particle
|
||||
* Can alternatively specify a [targetPosition] for entity-to-world-position targeting
|
||||
* (e.g. a Pokémon firing particles at a block).
|
||||
*
|
||||
* Target resolution priority: targetedEntityId + targetLocators > targetPosition > no target
|
||||
*
|
||||
* Handled by [SpawnSnowstormEntityParticleHandler]
|
||||
*
|
||||
@@ -28,7 +33,8 @@ class SpawnSnowstormEntityParticlePacket(
|
||||
val sourceEntityId: Int,
|
||||
val sourceLocators: List<String> = listOf("root"),
|
||||
val targetedEntityId: Int? = null,
|
||||
val targetLocators: List<String>? = null
|
||||
val targetLocators: List<String>? = null,
|
||||
val targetPosition: Vec3? = null
|
||||
) : NetworkPacket<SpawnSnowstormEntityParticlePacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("spawn_snowstorm_entity_particle")
|
||||
@@ -39,7 +45,8 @@ class SpawnSnowstormEntityParticlePacket(
|
||||
val sourceLocators = buffer.readList { buffer.readString() }
|
||||
val targetedEntityId = buffer.readNullable { it.readInt() }
|
||||
val targetLocators = buffer.readNullable { buffer.readList { buffer.readString() }}
|
||||
return SpawnSnowstormEntityParticlePacket(effectId, sourceEntityId, sourceLocators, targetedEntityId, targetLocators)
|
||||
val targetPosition = buffer.readNullable { Vec3(it.readDouble(), it.readDouble(), it.readDouble()) }
|
||||
return SpawnSnowstormEntityParticlePacket(effectId, sourceEntityId, sourceLocators, targetedEntityId, targetLocators, targetPosition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,5 +62,10 @@ class SpawnSnowstormEntityParticlePacket(
|
||||
buffer.writeString(locator)
|
||||
}
|
||||
}
|
||||
buffer.writeNullable(targetPosition) { _, value ->
|
||||
buffer.writeDouble(value.x)
|
||||
buffer.writeDouble(value.y)
|
||||
buffer.writeDouble(value.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.client.pokemon.update
|
||||
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readSizedInt
|
||||
import com.cobblemon.mod.common.util.writeSizedInt
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Packet that adds a single skill cooldown to a Pokémon's skill cooldowns map.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 31st, 2025
|
||||
*/
|
||||
class SkillCooldownsUpdatePacket(
|
||||
pokemon: () -> Pokemon?,
|
||||
val skillId: ResourceLocation,
|
||||
val cooldownTicks: Int
|
||||
) : SingleUpdatePacket<Pair<ResourceLocation, Int>, SkillCooldownsUpdatePacket>(pokemon, skillId to cooldownTicks) {
|
||||
override val id: ResourceLocation = ID
|
||||
|
||||
override fun encodeValue(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(skillId)
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, cooldownTicks)
|
||||
}
|
||||
|
||||
override fun set(pokemon: Pokemon, value: Pair<ResourceLocation, Int>) {
|
||||
val (skillId, cooldownTicks) = value
|
||||
pokemon.skillCooldownsTicks[skillId] = cooldownTicks
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("skill_cooldowns_update")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): SkillCooldownsUpdatePacket {
|
||||
val pokemon = decodePokemon(buffer)
|
||||
val skillId = buffer.readResourceLocation()
|
||||
val cooldownTicks = buffer.readSizedInt(IntSize.U_SHORT)
|
||||
return SkillCooldownsUpdatePacket(pokemon, skillId, cooldownTicks)
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.net.messages.server
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import java.util.UUID
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Packet sent by the client to the server to indicate that it wants to use a Poké Skill.
|
||||
*
|
||||
* The server will validate if the skill can be used, and if so, will execute it.
|
||||
*
|
||||
* @param skillId The ID of the skill to use.
|
||||
* @param pokemonId The UUID of the Pokémon using the skill.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 31st, 2025
|
||||
*/
|
||||
class UsePokeSkillPacket(
|
||||
val skillId: ResourceLocation,
|
||||
val pokemonId: UUID,
|
||||
) : NetworkPacket<UsePokeSkillPacket> {
|
||||
override val id: ResourceLocation = ID
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(skillId)
|
||||
buffer.writeUUID(pokemonId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = ResourceLocation("cobblemon", "use_poke_skill")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): UsePokeSkillPacket {
|
||||
return UsePokeSkillPacket(
|
||||
buffer.readResourceLocation(),
|
||||
buffer.readUUID()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.net.serverhandling
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPokeSkills
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.UsePokeSkillPacket
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object UsePokeSkillHandler : ServerNetworkPacketHandler<UsePokeSkillPacket> {
|
||||
override fun handle(packet: UsePokeSkillPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
val party = player.party()
|
||||
val pokemon = party[packet.pokemonId] ?: return // Who?
|
||||
val skill = CobblemonPokeSkills.getAccessiblePokeSkillTiers(player, pokemon).firstOrNull { it.id == packet.skillId } ?: return // What skill?
|
||||
if (skill.canUse(player, pokemon)) {
|
||||
skill.use(player, pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -766,6 +766,8 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
val riding: RidingProperties
|
||||
get() = this.form.riding
|
||||
|
||||
var skillCooldownsTicks: MutableMap<ResourceLocation, Int> = mutableMapOf()
|
||||
|
||||
init {
|
||||
storeCoordinates.subscribe { if (it != null && it.store !is PCStore && this.tetheringId != null) afterOnServer(seconds = 0.05F) { this.tetheringId = null } }
|
||||
storeCoordinates.subscribe {
|
||||
@@ -1140,6 +1142,21 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
}
|
||||
|
||||
fun tickSkillCooldowns() {
|
||||
val iterator = skillCooldownsTicks.entries.iterator()
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
val entry = iterator.next()
|
||||
val newValue = entry.value - 1
|
||||
|
||||
if (newValue <= 0) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
entry.setValue(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isOnInteractionCooldown(group: ResourceLocation): Boolean {
|
||||
return this.interactionCooldowns.getOrDefault(group, 0) > 0
|
||||
}
|
||||
@@ -2228,6 +2245,11 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
return true
|
||||
}
|
||||
|
||||
fun addSkillCooldown(skillId: ResourceLocation, cooldownTicks: Int) {
|
||||
skillCooldownsTicks[skillId] = cooldownTicks
|
||||
onChange(SkillCooldownsUpdatePacket({ this }, skillId, cooldownTicks))
|
||||
}
|
||||
|
||||
fun notify(packet: PokemonUpdatePacket<*>) {
|
||||
storeCoordinates.get()?.run { sendPacketToPlayers(store.getObservingPlayers(), packet) }
|
||||
}
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.pokemon.status.statuses.persistent
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
class FrozenStatus : PersistentStatus(
|
||||
name = cobblemonResource("frozen"),
|
||||
showdownName = "frz",
|
||||
|
||||
@@ -20,6 +20,7 @@ object DataKeys {
|
||||
const val POKEMON_FRIENDSHIP = "Friendship"
|
||||
const val POKEMON_FULLNESS = "Fullness"
|
||||
const val POKEMON_INTERACTION_COOLDOWN = "InteractionCooldown"
|
||||
const val POKEMON_SKILL_COOLDOWNS = "SkillCooldowns"
|
||||
const val POKEMON_ALPHA = "Alpha"
|
||||
|
||||
const val POKEMON_IVS = "IVs"
|
||||
|
||||
@@ -100,10 +100,16 @@ fun ServerPlayer.didSleep(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun ServerPlayer.isInBattle() = BattleRegistry.getBattleByParticipatingPlayer(this) != null
|
||||
fun Player.isInBattle() = if (this is ServerPlayer) {
|
||||
BattleRegistry.getBattleByParticipatingPlayer(this) != null
|
||||
} else {
|
||||
CobblemonClient.battle?.spectating == false
|
||||
}
|
||||
|
||||
var ServerPlayer.spawner: PlayerSpawner
|
||||
get() = (this as PlayerSpawnerAccessor).getPlayerSpawner()
|
||||
set(value) = (this as PlayerSpawnerAccessor).setPlayerSpawner(value)
|
||||
|
||||
fun ServerPlayer.getBattleState(): Pair<PokemonBattle, BattleActor>? {
|
||||
val battle = BattleRegistry.getBattleByParticipatingPlayer(this)
|
||||
if (battle != null) {
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Adapter for deserializing a reference to an [ActionEffectTimeline] from its identifier.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 31st, 2025
|
||||
*/
|
||||
object ActionEffectTimelineAdapter : JsonDeserializer<ActionEffectTimeline> {
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): ActionEffectTimeline {
|
||||
val identifier = json.asString.asIdentifierDefaultingNamespace()
|
||||
val timeline = ActionEffects.actionEffects[identifier]
|
||||
return timeline ?: throw IllegalArgumentException("No ActionEffectTimeline found for id $identifier")
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.util.codec
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.MoLang
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.Codec.optionalField
|
||||
@@ -29,6 +31,17 @@ val EXPRESSION_CODEC = object : PrimitiveCodec<Expression> {
|
||||
}
|
||||
}
|
||||
|
||||
val EXPRESSION_LIKE_CODEC = object : PrimitiveCodec<ExpressionLike> {
|
||||
override fun <T> read(ops: DynamicOps<T>, input: T): DataResult<ExpressionLike> {
|
||||
return ops.getStringValue(input).map { it.asExpressionLike() }
|
||||
}
|
||||
|
||||
override fun <T> write(ops: DynamicOps<T>, value: ExpressionLike): T {
|
||||
return ops.createString(value.getString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// special codec that always writes default value
|
||||
// vanilla changed the behaviour of Codec#optionalFieldOf(String, A) so that it OMITS default values when encoding
|
||||
// later on decoding it assumes no value -> default, but we want some default being encoded anyways so it can be targeted by commands
|
||||
|
||||
+7
-3
@@ -22,10 +22,12 @@ import net.minecraft.world.item.ItemStack
|
||||
import java.util.*
|
||||
|
||||
internal data class ClientPokemonP4(
|
||||
val originalTrainerFriendship: Optional<Int>
|
||||
val skillCooldownTicks: Map<ResourceLocation, Int>,
|
||||
val originalTrainerFriendship: Optional<Int>,
|
||||
) : Partial<Pokemon> {
|
||||
|
||||
override fun into(other: Pokemon): Pokemon {
|
||||
other.skillCooldownsTicks = this.skillCooldownTicks.toMutableMap()
|
||||
this.originalTrainerFriendship.ifPresent { other.originalTrainerFriendship = it }
|
||||
return other
|
||||
}
|
||||
@@ -36,11 +38,13 @@ internal data class ClientPokemonP4(
|
||||
*/
|
||||
internal val CODEC: MapCodec<ClientPokemonP4> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(ClientPokemonP4::originalTrainerFriendship)
|
||||
).apply(instance, ::ClientPokemonP4)
|
||||
Codec.unboundedMap(ResourceLocation.CODEC, Codec.INT).fieldOf(DataKeys.POKEMON_SKILL_COOLDOWNS).forGetter(ClientPokemonP4::skillCooldownTicks),
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(ClientPokemonP4::originalTrainerFriendship),
|
||||
).apply(instance, ::ClientPokemonP4)
|
||||
}
|
||||
|
||||
internal fun from(pokemon: Pokemon): ClientPokemonP4 = ClientPokemonP4(
|
||||
pokemon.skillCooldownsTicks,
|
||||
Optional.ofNullable(pokemon.originalTrainerFriendship)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,13 +13,16 @@ import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
internal data class PokemonP4(
|
||||
val originalTrainerFriendship: Optional<Int>
|
||||
val skillCooldownTicks: Map<ResourceLocation, Int>,
|
||||
val originalTrainerFriendship: Optional<Int>,
|
||||
) : Partial<Pokemon> {
|
||||
|
||||
override fun into(other: Pokemon): Pokemon {
|
||||
other.skillCooldownsTicks = this.skillCooldownTicks.toMutableMap()
|
||||
this.originalTrainerFriendship.ifPresent { other.originalTrainerFriendship = it }
|
||||
other.refreshOriginalTrainer()
|
||||
return other
|
||||
@@ -28,16 +31,20 @@ internal data class PokemonP4(
|
||||
companion object {
|
||||
internal val CODEC: MapCodec<PokemonP4> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(PokemonP4::originalTrainerFriendship)
|
||||
Codec.unboundedMap(ResourceLocation.CODEC, Codec.INT).optionalFieldOf(DataKeys.POKEMON_SKILL_COOLDOWNS, emptyMap<ResourceLocation, Int>()).forGetter { it.skillCooldownTicks },
|
||||
Codec.INT.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER_FRIENDSHIP).forGetter(PokemonP4::originalTrainerFriendship),
|
||||
).apply(instance) {
|
||||
skillCooldownTicks,
|
||||
originalTrainerFriendship -> PokemonP4(
|
||||
originalTrainerFriendship
|
||||
skillCooldownTicks,
|
||||
originalTrainerFriendship,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun from(pokemon: Pokemon): PokemonP4 = PokemonP4(
|
||||
Optional.ofNullable(pokemon.originalTrainerFriendship)
|
||||
pokemon.skillCooldownsTicks,
|
||||
Optional.ofNullable(pokemon.originalTrainerFriendship),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:skill_sense_sparkle",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_xray",
|
||||
"texture": "textures/particles/generic/sparkle/glowingsparkle"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 8.5,
|
||||
"max_particles": 100
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 5
|
||||
},
|
||||
"minecraft:emitter_shape_box": {
|
||||
"half_dimensions": [0.35, 0.35, 0.35],
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.35
|
||||
},
|
||||
"minecraft:particle_initial_speed": 0,
|
||||
"minecraft:particle_motion_dynamic": {},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": [0.08, 0.08],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 8,
|
||||
"texture_height": 32,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [7, 8],
|
||||
"step_UV": [0, 8],
|
||||
"frames_per_second": 10,
|
||||
"max_frame": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": {
|
||||
"interpolant": "v.particle_random_1",
|
||||
"gradient": {
|
||||
"0.0": "#FFFFF0B6",
|
||||
"0.5": "#FFFFE45A",
|
||||
"1.0": "#FFAE521A"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -848,6 +848,11 @@
|
||||
"cobblemon.gender.male": "Male",
|
||||
"cobblemon.gender.genderless": "Genderless",
|
||||
"cobblemon.fossilmachine.protected": "This fossil belongs to %s!",
|
||||
|
||||
"cobblemon.poke_skill.quick_ride.name": "Quick Ride",
|
||||
"cobblemon.poke_skill.teleport.name": "Teleport",
|
||||
"cobblemon.poke_skill.iron_sense.name": "Iron Sense",
|
||||
|
||||
"itemGroup.cobblemon.blocks": "Cobblemon: Blocks",
|
||||
"itemGroup.cobblemon.utility_item": "Cobblemon: Utility Items",
|
||||
"itemGroup.cobblemon.agriculture": "Cobblemon: Agriculture",
|
||||
@@ -2532,6 +2537,8 @@
|
||||
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default.tooltip": "If enabled, TM and level-up moves are unlocked and discovered in the TM Machine and Pokédex Move Dex.",
|
||||
"cobblemon.config.ui.infinite_tm_uses": "Infinite TM Uses",
|
||||
"cobblemon.config.ui.infinite_tm_uses.tooltip": "TMs are not consumed when teaching moves.",
|
||||
"cobblemon.config.ui.infinite_poke_skill_cooldown": "Infinite Poké Skill Cooldown",
|
||||
"cobblemon.config.ui.infinite_poke_skill_cooldown.tooltip": "Poké Skills do not go on cooldown after use.",
|
||||
"cobblemon.config.ui.hide_unimplemented_pokemon_in_the_pokedex": "Hide Unimplemented Pokémon in the Pokédex",
|
||||
"cobblemon.config.ui.hide_unimplemented_pokemon_in_the_pokedex.tooltip": "Whether to hide unimplemented Pokémon from the Pokédex.",
|
||||
"cobblemon.config.ui.summary_pokemon_follow_cursor": "Make Pokémon Follow Cursor",
|
||||
|
||||
@@ -143,6 +143,9 @@ transitive-accessible class net/minecraft/stats/RecipeBookSettings$TypeSettings
|
||||
transitive-accessible field net/minecraft/client/gui/screens/recipebook/RecipeBookComponent tabButtons Ljava/util/List;
|
||||
accessible field net/minecraft/client/gui/screens/recipebook/RecipeBookComponent menu Lnet/minecraft/world/inventory/RecipeBookMenu;
|
||||
accessible method net/minecraft/world/level/block/AbstractCandleBlock addParticlesAndSound (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/util/RandomSource;)V
|
||||
accessible field net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity litTime I
|
||||
accessible field net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity litDuration I
|
||||
|
||||
|
||||
transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$IngredientsSlot
|
||||
transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$FuelSlot
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.pokemon.entity.set_position(q.player.x, q.player.y, q.player.z);",
|
||||
"q.player.ride(q.pokemon.entity);",
|
||||
"q.pokemon.add_skill_cooldown(q.skill_id);"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"timeline": [
|
||||
{ "type": "pause_tasks" },
|
||||
{ "type": "save_position" },
|
||||
{
|
||||
"type": "sequence",
|
||||
"condition": "!v.target_is_entity'",
|
||||
"keyframes": [
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": ["q.entity.look_at_position(v.target_x, v.target_y, v.target_z, 60)"]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "sequence",
|
||||
"condition": "v.target_is_entity",
|
||||
"keyframes": [
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": ["q.entity.look_at_entity(v.target_entity, 120)"]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "parallel",
|
||||
"keyframes": [
|
||||
{
|
||||
"type": "run_action_effect",
|
||||
"expression": "v.granting_move"
|
||||
},
|
||||
{
|
||||
"type": "sequence",
|
||||
"condition": "v.hit_effect != 0",
|
||||
"keyframes": [
|
||||
{ "type": "pause", "pause": "v.hit_delay" },
|
||||
{
|
||||
"type": "run_action_effect",
|
||||
"expression": "v.hit_effect"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ "type": "return_to_position" },
|
||||
{ "type": "resume_tasks" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["status"],
|
||||
"delay": 0.2
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "minecraft:happy_villager",
|
||||
"locators": ["special", "target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.8
|
||||
},
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"v.fertilized_count = q.entity.fertilize_nearby_crops(8, 8);",
|
||||
"v.fertilized_count > 0 ? q.pokemon.add_skill_cooldown(q.skill_id) : 0;",
|
||||
"v.fertilized_count > 0 ? q.entity.play_sound('minecraft:item.bone_meal.use') : 0;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "remove_holds",
|
||||
"holds": ["effects"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_explosion(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 0.5 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 2.0 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
]
|
||||
},
|
||||
{ "type": "pause", "pause": 4.0 },
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())",
|
||||
"q.entity.world.spawn_lightning(v.target_entity.x(), v.target_entity.y() + (v.target_entity.height() / 2), v.target_entity.z())"
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["status"],
|
||||
"delay": 0.2
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "cobblemon:explosion_actorboom",
|
||||
"locators": ["special", "target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
},
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"v.found_gem = (q.entity.find_nearby_block('cobblemon:gemstones') != 0);",
|
||||
"v.found_gem ? q.entity.play_sound('cobblemon:shiny/shiny_appear_chime') : 0;",
|
||||
"q.pokemon.add_skill_cooldown(q.skill_id);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "remove_holds",
|
||||
"holds": ["effects"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "pause_tasks"
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["status"],
|
||||
"delay": 0.2
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "cobblemon:explosion_actorboom",
|
||||
"locators": ["special", "target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
},
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"v.found_gold = q.entity.find_nearby_blocks('#GOLD_ORES', 32);",
|
||||
"for_each(t.block_pos, v.found_gold, { q.entity.world.spawn_bedrock_particles('cobblemon:skill_sense_sparkle', t.block_pos[0] + 0.5, t.block_pos[1] + 0.5, t.block_pos[2] + 0.5, q.player); } )",
|
||||
"v.found_gold > 0 ? q.entity.play_sound('cobblemon:shiny/shiny_appear_chime') : 0;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "resume_tasks"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["status"],
|
||||
"delay": 0.2
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "cobblemon:explosion_actorboom",
|
||||
"locators": ["special", "target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
},
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"v.found_iron = (q.entity.find_nearby_block('#IRON_ORES') != 0);",
|
||||
"v.found_iron ? q.entity.world.play_sound_on_server('minecraft:entity.wither.spawn', 'neutral', q.entity.x, q.entity.y, q.entity.z) : 0;",
|
||||
"q.pokemon.add_skill_cooldown(q.skill_id);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "remove_holds",
|
||||
"holds": ["effects"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.entity.world.add_furnace_fuel(v.target_block_x, v.target_block_y, v.target_block_z, v.fuel_quantity);"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["psybeam", "beam", "special"],
|
||||
"delay": 0.8
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "cobblemon:psybeam_charge",
|
||||
"locators": ["special", "target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.53
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"effect": "cobblemon:psybeam_actorpilot",
|
||||
"locators": ["special", "target"],
|
||||
"targetLocators": ["target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.1
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"entityCondition": "q.entity.is_user == false",
|
||||
"effect": "cobblemon:psybeam_target",
|
||||
"locators": ["target"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
},
|
||||
{
|
||||
"type": "molang",
|
||||
"expressions": [
|
||||
"q.player.teleport_to_respawn();",
|
||||
"q.pokemon.add_skill_cooldown(q.skill_id);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
"entityCondition": "q.entity.is_user == false",
|
||||
"effect": "cobblemon:psybeam_targetburst",
|
||||
"locators": ["target"]
|
||||
},
|
||||
{
|
||||
"type": "remove_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 1.7
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,6 +19,15 @@
|
||||
"look_at_target"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "cobblemon:action_effect",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
"look_at_target"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.fertilize.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:fertilize_action",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 3600,
|
||||
"accessCondition": "q.pokemon.has_learned('grassterrain') || q.pokemon.has_ability('grassysurge') || q.pokemon.species.primary_type == 'grass'",
|
||||
"useCondition": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.freeze.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_stoke.png",
|
||||
"cooldownTicks": 20,
|
||||
"targetType": "ENTITY",
|
||||
"range": 16,
|
||||
"requiredMoves": [
|
||||
"powderglow",
|
||||
"iceshard",
|
||||
"icebeam",
|
||||
"blizzard",
|
||||
"frostbreath",
|
||||
"freezedry",
|
||||
"icywind"
|
||||
],
|
||||
"requiredTypes": [
|
||||
"ice"
|
||||
],
|
||||
"fallbackActionEffect": "cobblemon:icebeam",
|
||||
"hitDelay": "2.0",
|
||||
"hitEffect": "cobblemon:freeze_entity_hit",
|
||||
"useCondition": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.gem_sense.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:gem_sense_action",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 20,
|
||||
"accessCondition": "q.pokemon.species.name == 'Sableye'",
|
||||
"useCondition": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.gold_sense.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:gold_sense_action",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 20,
|
||||
"accessCondition": "q.pokemon.species.name == 'Gimmighoul'",
|
||||
"useCondition": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.iron_sense.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:iron_sense_action",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 1200,
|
||||
"accessCondition": "q.pokemon.has_learned('magnetrise') || q.pokemon.species.name == 'Magnemite'",
|
||||
"useCondition": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.quick_ride.name",
|
||||
"tiers": [
|
||||
{
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:begin_riding",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 40,
|
||||
"accessCondition": "q.pokemon.is_rideable",
|
||||
"useCondition": "q.pokemon.is_sent_out && !q.pokemon.entity.is_passenger(q.player)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.stoke.name",
|
||||
"enabled": true,
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_stoke.png",
|
||||
"cooldownTicks": 20,
|
||||
"targetType": "BLOCK",
|
||||
"range": 8,
|
||||
"targetCondition": "1",
|
||||
"fallbackActionEffect": "cobblemon:flamethrower",
|
||||
"hitDelay": "1.25",
|
||||
"hitEffect": "cobblemon:stoke_1_hit",
|
||||
"useCondition": "1",
|
||||
"tiers": [
|
||||
{
|
||||
"requiredMoves": [
|
||||
"ember"
|
||||
],
|
||||
"variables": {
|
||||
"fuel_quantity": 2400
|
||||
}
|
||||
},
|
||||
{
|
||||
"requiredMoves": [
|
||||
"flamethrower"
|
||||
],
|
||||
"variables": {
|
||||
"fuel_quantity": 4800
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.teleport.name",
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:teleport_to_spawn",
|
||||
"accessCondition": "q.pokemon.has_learned('teleport')",
|
||||
"useCondition": "q.player.has_respawn_position && q.player.dimension == q.player.respawn_dimension",
|
||||
"tiers": [
|
||||
{
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"requiredSpecies": [
|
||||
"Abra"
|
||||
],
|
||||
"cooldownTicks": 18000
|
||||
},
|
||||
{
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_shoulder.png",
|
||||
"requiredSpecies": [
|
||||
"Alakazam"
|
||||
],
|
||||
"cooldownTicks": 20
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user