forked from mirrors/cobblemon
Compare commits
22
Commits
main
...
jack-pokeskills
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e80924c68 | ||
|
|
6749b39142 | ||
|
|
ff25416b07 | ||
|
|
bb81a44a11 | ||
|
|
40dfca2294 | ||
|
|
5a40ced96b | ||
|
|
3eb97b29a1 | ||
|
|
d88de0b072 | ||
|
|
460c44248c | ||
|
|
a16668695c | ||
|
|
fe5ebb5444 | ||
|
|
c34e4c8bbc | ||
|
|
84e76a0a6d | ||
|
|
be70c1e8de | ||
|
|
fba40387bb | ||
|
|
1217e6d101 | ||
|
|
905c410aee | ||
|
|
cea0c826c6 | ||
|
|
dc3b17df3b | ||
|
|
5602dee9ac | ||
|
|
9e4137cc20 | ||
|
|
c92248f6e9 |
@@ -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,26 +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.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
|
||||
@@ -62,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
|
||||
@@ -85,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
|
||||
@@ -120,46 +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.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
|
||||
@@ -186,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
|
||||
@@ -203,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
|
||||
@@ -245,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
|
||||
@@ -265,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
|
||||
|
||||
@@ -340,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()))
|
||||
|
||||
@@ -440,6 +362,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
|
||||
@@ -532,6 +455,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,68 @@
|
||||
/*
|
||||
* 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.PokeSkill
|
||||
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 getAccessibleSkills(player: Player, pokemon: Pokemon): List<PokeSkill> {
|
||||
return skills.values.filter { it.canAccess(player, pokemon) }
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PokeSkill>) {
|
||||
data.forEach { (id, skill) -> skill.id = id }
|
||||
skills = data
|
||||
observable.emit(this)
|
||||
Cobblemon.LOGGER.info("Loaded ${skills.size} PokéSkills")
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(PokeSkillsRegistrySyncPacket(skills.values.toList()))
|
||||
}
|
||||
}
|
||||
@@ -293,11 +293,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
|
||||
}
|
||||
|
||||
+67
-1
@@ -35,12 +35,14 @@ 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.getIntOrNull
|
||||
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 net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -48,7 +50,10 @@ 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
|
||||
@@ -78,8 +83,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 +131,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())
|
||||
@@ -233,7 +299,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
@@ -23,6 +23,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
|
||||
@@ -32,6 +33,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
|
||||
@@ -44,14 +46,19 @@ 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 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>() {
|
||||
@@ -379,6 +386,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
|
||||
|
||||
+27
@@ -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,29 @@ 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)
|
||||
}
|
||||
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)
|
||||
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
|
||||
) {
|
||||
|
||||
@@ -57,6 +57,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
|
||||
|
||||
+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()
|
||||
}
|
||||
}
|
||||
|
||||
+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)}
|
||||
|
||||
+37
-4
@@ -9,20 +9,24 @@
|
||||
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.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 +34,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 +45,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 +56,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 +68,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 +181,35 @@ fun createPlayerInteractGui(optionsPacket: PlayerInteractOptionsPacket): Interac
|
||||
return InteractWheelGUI(options, Component.translatable("cobblemon.ui.interact.player"))
|
||||
}
|
||||
|
||||
fun createPokeSkillGui(player: AbstractClientPlayer, pokemon: Pokemon): InteractWheelGUI {
|
||||
val accessibleSkills = CobblemonPokeSkills.getAccessibleSkills(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,
|
||||
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 getLangKey(status: PlayerInteractOptionsPacket.OptionStatus) : String {
|
||||
return when (status) {
|
||||
PlayerInteractOptionsPacket.OptionStatus.TOO_FAR -> "cobblemon.ui.interact.too_far"
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import org.joml.Vector3f
|
||||
data class InteractWheelOption(
|
||||
val iconResource: ResourceLocation,
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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.entity.pokemon
|
||||
|
||||
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.molang.ObjectValue
|
||||
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.TargetsProvider
|
||||
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.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 grantingMoves/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 = true,
|
||||
val actionEffect: ActionEffectTimeline,
|
||||
val icon: ResourceLocation,
|
||||
val cooldownTicks: Int,
|
||||
val accessCondition: ExpressionLike = "true".asExpressionLike(),
|
||||
val useCondition: ExpressionLike = "true".asExpressionLike(),
|
||||
// Targeting
|
||||
val targetType: TargetType? = null,
|
||||
val range: Double? = null,
|
||||
val targetCondition: ExpressionLike? = null,
|
||||
// Granting
|
||||
val grantingMoves: List<String>? = null,
|
||||
val grantingAbilities: List<String>? = null,
|
||||
val grantingTypes: List<String>? = null,
|
||||
val grantingSpecies: List<String>? = null,
|
||||
// Wrapper skill fields
|
||||
val hitDelay: ExpressionLike? = null,
|
||||
val hitEffect: ResourceLocation? = null,
|
||||
val fallbackActionEffect: ResourceLocation? = null
|
||||
) {
|
||||
// Enum to determine what this skill will target.
|
||||
enum class TargetType {
|
||||
NONE, SELF, BLOCK, ENTITY, BOTH
|
||||
}
|
||||
|
||||
companion object {
|
||||
val runtime = MoLangRuntime().setup()
|
||||
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, ::PokeSkill)
|
||||
}
|
||||
val PACKET_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC)
|
||||
private val DEFAULT_ACTION_EFFECT = cobblemonResource("default_poke_skill")
|
||||
}
|
||||
|
||||
@Transient
|
||||
lateinit var id: ResourceLocation
|
||||
|
||||
|
||||
// fun canAccess(player: Player, pokemon: Pokemon): Boolean {
|
||||
// if (!enabled) {
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// runtime.withQueryValue("player", player.asMostSpecificMoLangValue())
|
||||
// runtime.withQueryValue("pokemon", pokemon.struct)
|
||||
// pokemon.entity?.let { runtime.withQueryValue("entity", it.asMostSpecificMoLangValue()) }
|
||||
// val hasAccess = runtime.resolveBoolean(accessCondition)
|
||||
// return hasAccess
|
||||
// }
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
// Check granting lists — if any are populated, check if the player has any of the listed moves/abilities/types/species
|
||||
val hasGrantingLists = !grantingMoves.isNullOrEmpty() || !grantingAbilities.isNullOrEmpty() ||
|
||||
!grantingTypes.isNullOrEmpty() || !grantingSpecies.isNullOrEmpty()
|
||||
|
||||
if (hasGrantingLists) {
|
||||
val moveMatch = !grantingMoves.isNullOrEmpty() && grantingMoves.any { moveName ->
|
||||
pokemon.moveSet.any { it.template.name == moveName }
|
||||
}
|
||||
val abilityMatch = !grantingAbilities.isNullOrEmpty() && grantingAbilities.any { abilityName ->
|
||||
pokemon.ability.name == abilityName
|
||||
}
|
||||
val typeMatch = !grantingTypes.isNullOrEmpty() && grantingTypes.any { typeName ->
|
||||
pokemon.types.any { it.name.equals(typeName, ignoreCase = true) }
|
||||
}
|
||||
val speciesMatch = !grantingSpecies.isNullOrEmpty() && grantingSpecies.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 ?: "true".asExpressionLike())
|
||||
}
|
||||
|
||||
fun canUse(player: Player, pokemon: Pokemon): Boolean {
|
||||
val status = pokemon.status?.status
|
||||
if (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 grantingMoves, or null if none match.
|
||||
*/
|
||||
fun resolveGrantingMove(pokemon: Pokemon): String? {
|
||||
if (grantingMoves.isNullOrEmpty()) return null // need this guard
|
||||
return grantingMoves.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 ?: TargetType.NONE
|
||||
val effectiveRange = range ?: 8.0
|
||||
|
||||
// Resolve the action effect to run
|
||||
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
|
||||
|
||||
|
||||
// Targeting — raycast based on targetType
|
||||
when (effectiveTargetType) {
|
||||
TargetType.SELF -> {
|
||||
injectEntityTarget(skillRuntime, player)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(1.0))
|
||||
}
|
||||
TargetType.BLOCK -> {
|
||||
val blockHit = getPlayerLookedBlock(player, effectiveRange)
|
||||
if (blockHit == null) return false
|
||||
injectBlockTarget(skillRuntime, blockHit, level)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(0.0))
|
||||
}
|
||||
TargetType.ENTITY -> {
|
||||
val entityHit = getPlayerLookedEntity(player, effectiveRange)
|
||||
if (entityHit == null) return false
|
||||
injectEntityTarget(skillRuntime, entityHit)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(1.0))
|
||||
}
|
||||
TargetType.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)
|
||||
if (blockHit == null) return false
|
||||
injectBlockTarget(skillRuntime, blockHit, level)
|
||||
skillRuntime.environment.setSimpleVariable("target_is_entity", DoubleValue(0.0))
|
||||
}
|
||||
}
|
||||
TargetType.NONE -> {}
|
||||
}
|
||||
|
||||
// Evaluate target condition
|
||||
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()))
|
||||
}
|
||||
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
|
||||
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())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = net.minecraft.world.entity.projectile.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.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.getAccessibleSkills(player, pokemon).firstOrNull { it.id == packet.skillId } ?: return // What skill?
|
||||
if (skill.canUse(player, pokemon)) {
|
||||
skill.use(player, pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,6 +767,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 {
|
||||
@@ -1141,6 +1143,16 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
}
|
||||
|
||||
fun tickSkillCooldowns() {
|
||||
this.skillCooldownsTicks.entries.forEach { (key, value) ->
|
||||
val newValue = value - 1
|
||||
if (newValue <= 0)
|
||||
this.skillCooldownsTicks -= key
|
||||
else
|
||||
this.skillCooldownsTicks[key] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
fun isOnInteractionCooldown(group: ResourceLocation): Boolean {
|
||||
return this.interactionCooldowns.getOrDefault(group, 0) > 0
|
||||
}
|
||||
@@ -2229,6 +2241,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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "add_holds", "holds": ["effects"] },
|
||||
{ "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": "remove_holds" }
|
||||
]
|
||||
}
|
||||
@@ -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,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_gold = (q.entity.find_nearby_block('#GOLD_ORES') != 0);",
|
||||
"v.found_gold ? 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,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, 2400);"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.fertilize.name",
|
||||
"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,24 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.freeze.name",
|
||||
"enabled": true,
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_stoke.png",
|
||||
"cooldownTicks": 20,
|
||||
"targetType": "ENTITY",
|
||||
"range": 16,
|
||||
"grantingMoves": [
|
||||
"powderglow",
|
||||
"iceshard",
|
||||
"icebeam",
|
||||
"blizzard",
|
||||
"frostbreath",
|
||||
"freezedry",
|
||||
"icywind"
|
||||
],
|
||||
"grantingTypes": [
|
||||
"ice"
|
||||
],
|
||||
"fallbackActionEffect": "cobblemon:icebeam",
|
||||
"hitDelay": "2.0",
|
||||
"hitEffect": "cobblemon:freeze_entity_hit",
|
||||
"useCondition": "1"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.gem_sense.name",
|
||||
"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,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.gold_sense.name",
|
||||
"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,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.iron_sense.name",
|
||||
"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,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.quick_ride.name",
|
||||
"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,24 @@
|
||||
{
|
||||
"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",
|
||||
"grantingMoves": [
|
||||
"ember",
|
||||
"flamethrower",
|
||||
"fireblast",
|
||||
"heatwave",
|
||||
"flamecharge",
|
||||
"lavaplume"
|
||||
],
|
||||
"grantingTypes": [
|
||||
"fire"
|
||||
],
|
||||
"fallbackActionEffect": "cobblemon:flamethrower",
|
||||
"hitDelay": "1.25",
|
||||
"hitEffect": "cobblemon:stoke_1_hit",
|
||||
"useCondition": "1"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "cobblemon.poke_skill.teleport.name",
|
||||
"enabled": true,
|
||||
"actionEffect": "cobblemon:teleport_to_spawn",
|
||||
"icon": "cobblemon:textures/gui/interact/interact_wheel_icon_ride.png",
|
||||
"cooldownTicks": 18000,
|
||||
"accessCondition": "q.pokemon.has_learned('teleport')",
|
||||
"useCondition": "q.player.has_respawn_position && q.player.dimension == q.player.respawn_dimension"
|
||||
}
|
||||
Reference in New Issue
Block a user