mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
121
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1986cb480b | ||
|
|
c8b0f36049 | ||
|
|
be2a0be806 | ||
|
|
ba4d42ccf6 | ||
|
|
5545d3498f | ||
|
|
641bd7ebaa | ||
|
|
234c4fa0d4 | ||
|
|
3dfda71ed5 | ||
|
|
65ee6de93f | ||
|
|
be88b727bc | ||
|
|
a69fc462ee | ||
|
|
7d5c30d27c | ||
|
|
f7c9c9d4f4 | ||
|
|
41bc14168a | ||
|
|
9540c38a72 | ||
|
|
7a96ed7cd1 | ||
|
|
677adc8d2f | ||
|
|
a99b1dfc6c | ||
|
|
4a718ad0f6 | ||
|
|
ddd417d458 | ||
|
|
10a12a897e | ||
|
|
9fce99ed73 | ||
|
|
c35f60dd5d | ||
|
|
54e5a5e819 | ||
|
|
79ae35ea9e | ||
|
|
58f85e6d0d | ||
|
|
71bfdf41f4 | ||
|
|
9976965673 | ||
|
|
098df1a5a6 | ||
|
|
f49f34efa3 | ||
|
|
eac19929c4 | ||
|
|
59cced5b2c | ||
|
|
f07707221f | ||
|
|
89b89592ab | ||
|
|
cdeb977769 | ||
|
|
a3987b433c | ||
|
|
99de91bd7e | ||
|
|
4b292b1241 | ||
|
|
1e6e4d8c5f | ||
|
|
9a61c3471b | ||
|
|
77f7439853 | ||
|
|
e9718b7e8b | ||
|
|
4cadd4e20f | ||
|
|
17a3d86324 | ||
|
|
48653073f5 | ||
|
|
82c7c03e28 | ||
|
|
2d7f3257d1 | ||
|
|
bc7b4f94c2 | ||
|
|
7a9e9d7ced | ||
|
|
0f85ece16f | ||
|
|
c4656e1a4e | ||
|
|
0c48a318c5 | ||
|
|
dab89a9f8c | ||
|
|
53fd2dba2d | ||
|
|
a5b15c6127 | ||
|
|
174fb59bbb | ||
|
|
2f8bd44938 | ||
|
|
9d58c7126e | ||
|
|
2afd722125 | ||
|
|
c9cfe489d6 | ||
|
|
4ee029fa54 | ||
|
|
5b0a221ade | ||
|
|
af23b82ce5 | ||
|
|
d1a8171079 | ||
|
|
d4b673ea38 | ||
|
|
36e78a5af2 | ||
|
|
55621b01b7 | ||
|
|
b828fe2b3a | ||
|
|
b7b10151e1 | ||
|
|
de21b7c402 | ||
|
|
10fe3c4362 | ||
|
|
cac6ed843a | ||
|
|
8f5f40c360 | ||
|
|
0e586b4f38 | ||
|
|
62dd487448 | ||
|
|
04ecd9189c | ||
|
|
55be22e942 | ||
|
|
f2e1b3dab4 | ||
|
|
66f98ebf12 | ||
|
|
c1842e42b0 | ||
|
|
e6c2f241d4 | ||
|
|
0f9c282e1f | ||
|
|
7bd5d77b60 | ||
|
|
f7b4d4e65a | ||
|
|
e78f403198 | ||
|
|
4ca91cc8d8 | ||
|
|
1518181f6b | ||
|
|
b915237c8d | ||
|
|
bb41a72067 | ||
|
|
fbb8492c75 | ||
|
|
c07aef04b1 | ||
|
|
6ff4befb04 | ||
|
|
a858d4f532 | ||
|
|
28fc273b26 | ||
|
|
fc89757a09 | ||
|
|
243c9abfd1 | ||
|
|
b871645e9e | ||
|
|
c79916d8a0 | ||
|
|
d503d4a1eb | ||
|
|
a0f75dd090 | ||
|
|
5eceaa849c | ||
|
|
0e0233993a | ||
|
|
56ed5a6412 | ||
|
|
f2e5ecef66 | ||
|
|
ad9e85923a | ||
|
|
bf461297b2 | ||
|
|
5965d5a81b | ||
|
|
4e02ca310d | ||
|
|
3260271d3c | ||
|
|
a77863adab | ||
|
|
455cfeb894 | ||
|
|
fd3c614c2c | ||
|
|
4f3eb7e4ba | ||
|
|
f9a3d2dc0e | ||
|
|
f817c18f4f | ||
|
|
a7e6e638fe | ||
|
|
9ef3ea2932 | ||
|
|
38dcee3327 | ||
|
|
12b92d3f35 | ||
|
|
dacfb4f1ff | ||
|
|
4086ba73d3 |
@@ -52,6 +52,7 @@
|
||||
- Added a unique set of sounds for Ancient Poké Balls.
|
||||
- Added a sound for using Exp Candy and Rare Candy items.
|
||||
- Added raft platforms for non-swimming, non-flying Pokémon to stand on during battles that take place on the water's surface. (Flying Pokémon will fly over water in battle, and water breathing Pokémon will swim in water during battle.)
|
||||
- Added `hiddenability=false` option to `/spawnpokemon` and `/pokemonedit` commands, allowing the Pokémon's ability to be reverted to a normal ability.
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 1
|
||||
@@ -396,6 +397,9 @@
|
||||
- Fixed Alolan Exeggutor's tail not showing in the party UI. It will now also stand in front of all Pokémon in the party menu :)
|
||||
- Fixed Timburr duplicating its log while fainting.
|
||||
- Fixed hitbox sizes for Grotle and Torterra being set to default values.
|
||||
- Fixed Hisuian Sneasel using Johtonian Sneasel's cry
|
||||
- Fixed Trevenant t-posing after 8 seconds of sleep.
|
||||
- Fixed Bewear t-posing for a bit if a wild one fainted.
|
||||
- Fixed reviving items throwing an exception when used in battle.
|
||||
- Fixed messages for Focus Sash, Confusion, Mummy, Ice Face, Own Tempo, and Revive.
|
||||
- Improve error handling when loading spawn-sets to ensure invalid configurations don't crash the server.
|
||||
@@ -426,6 +430,7 @@
|
||||
- Prevent summary stats tab from making sounds when clicking on an already open tab.
|
||||
- Fixed display case not being able to be fed items from underneath.
|
||||
- Fixed Pokémon battling in water continuously sinking to the bottom.
|
||||
- Fixed passive healing and wake from faint not disabling when their config values are set to 0.
|
||||
|
||||
### Developer
|
||||
- `SpawnCause` is now an implementation of `SpawningInfluence`.
|
||||
@@ -457,6 +462,7 @@
|
||||
- Added HealingSource, an interface applied to all sources of healing from player actions, for easier tracking of healing sources.
|
||||
- Move Change
|
||||
- Shiny Chance Calculation Event (with player context)
|
||||
- Dex Information Changed; this event is fired when the Pokédex is updated with new information.
|
||||
- Rebuilt large swaths of the model animation code to simplify it.
|
||||
- Renamed a bunch of things from %Poseable% to %Posable% because spelling.
|
||||
- Renamed StatelessAnimation to PoseAnimation.
|
||||
@@ -485,6 +491,10 @@
|
||||
- Added support for conditional pose animations.
|
||||
- Added a new universal locator called "top".
|
||||
- Added `eggs_collected` and `eggs_hatched` Advancement triggers.
|
||||
- Added missing `minYaw` configuration to the `q.look()` function for JSON posers.
|
||||
- Some pose condition names have been changed:
|
||||
- `isTouchingWaterOrRain` has been changed to `isInWaterOrRain`
|
||||
- `isSubermegedInWater` has been changed to `isUnderWater`
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
@@ -1719,6 +1729,7 @@ Thank you so much to all of our community translators that bring the mod to the
|
||||
- Lowered spawn rate of Tauros.
|
||||
- Sableye now spawns near gem ores as well as amethyst.
|
||||
- Added evolution stones and items to item tags
|
||||
- Pokémon now play their cry animations when clicked on the starter selection screen.
|
||||
|
||||
### Fixes
|
||||
- Fixed catch rates being entirely too difficult.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public abstract class MinecraftDedicatedServerMixin implements ServerInterface {
|
||||
|
||||
@Inject(method = "shouldDiscardEntity", at = @At(value = "HEAD"), cancellable = true)
|
||||
public void cobblemon$allowPokemonSpawns(Entity entity, CallbackInfoReturnable<Boolean> callback) {
|
||||
if(entity instanceof PokemonEntity) {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
callback.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.FlowHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
@@ -288,6 +289,8 @@ object Cobblemon {
|
||||
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
FlowHandler.setup()
|
||||
|
||||
ifDedicatedServer {
|
||||
isDedicatedServer = true
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ object CobblemonCommands {
|
||||
AbandonMultiTeam.register(dispatcher)
|
||||
FreezePokemonCommand.register(dispatcher)
|
||||
ApplyPlayerTextureCommand.register(dispatcher)
|
||||
RunMolangScriptCommand.register(dispatcher)
|
||||
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.net.messages.client.data.FlowRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
/**
|
||||
* Holds all the flows that are loaded from the server's data packs. A flow is foldered under the event identifier,
|
||||
* which can be Cobblemon's namespace but can also be minecraft or any other namespace someone has added hooks for.
|
||||
* The value of the map is a list of MoLang scripts that will execute when that event occurs.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 24th, 2024
|
||||
*/
|
||||
object CobblemonFlows : DataRegistry {
|
||||
override val id = cobblemonResource("flows")
|
||||
override val observable = SimpleObservable<CobblemonFlows>()
|
||||
override val type = PackType.SERVER_DATA
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(FlowRegistrySyncPacket(clientFlows.entries))
|
||||
}
|
||||
|
||||
val runtime by lazy { MoLangRuntime().setup() } // Lazy for if someone adds to generalFunctions in MoLangFunctions
|
||||
|
||||
val clientFlows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
val flows = hashMapOf<ResourceLocation, MutableList<ExpressionLike>>()
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
clientFlows.clear()
|
||||
flows.clear()
|
||||
val folderBeforeNameRegex = ".*\\/([^\\/]+)\\/[^\\/]+\$".toRegex()
|
||||
manager.listResources("flows") { path -> path.endsWith(CobblemonScripts.MOLANG_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.openAsReader().use { stream ->
|
||||
stream.buffered().use { reader ->
|
||||
try {
|
||||
val expression = reader.readText().asExpressionLike()
|
||||
val event = folderBeforeNameRegex.find(identifier.path)?.groupValues?.get(1)
|
||||
?: throw IllegalArgumentException("Invalid flow path: $identifier. Should have a folder structure that includes the name of the event being flowed.")
|
||||
if (identifier.path.startsWith("flows/client/")) {
|
||||
clientFlows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
|
||||
} else {
|
||||
flows.getOrPut(ResourceLocation.fromNamespaceAndPath(identifier.namespace, event)) { mutableListOf() }.add(expression)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading MoLang script for flow: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cobblemon.LOGGER.info("Loaded ${CobblemonScripts.scripts.size} flows and ${CobblemonScripts.clientScripts.size} client flows")
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
fun run(eventResourceLocation: ResourceLocation, context: Map<String, MoValue>) {
|
||||
flows[eventResourceLocation]?.forEach { it.resolve(runtime, context) }
|
||||
}
|
||||
}
|
||||
@@ -171,14 +171,6 @@ interface CobblemonImplementation {
|
||||
*/
|
||||
fun server(): MinecraftServer?
|
||||
|
||||
/**
|
||||
* Handles platform independent reloading of a [JsonDataRegistry].
|
||||
*
|
||||
* @param registry The [JsonDataRegistry] to reload.
|
||||
* @param manager The [ResourceManager] to reload from.
|
||||
*/
|
||||
fun <T> reloadJsonRegistry(registry: JsonDataRegistry<T>, manager: ResourceManager): HashMap<ResourceLocation, T>
|
||||
|
||||
/**
|
||||
* Registers an item to the [ComposterBlock].
|
||||
*
|
||||
|
||||
@@ -54,6 +54,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
override val registry: Registry<Item> = BuiltInRegistries.ITEM
|
||||
override val resourceKey: ResourceKey<Registry<Item>> = Registries.ITEM
|
||||
|
||||
@JvmField
|
||||
val NPC_EDITOR = create("npc_editor", CobblemonItem(Item.Properties().stacksTo(1)))
|
||||
|
||||
@JvmField
|
||||
val pokeBalls = mutableListOf<PokeBallItem>()
|
||||
@JvmField
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.*
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
@@ -74,6 +75,7 @@ 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.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
|
||||
@@ -85,7 +87,6 @@ import com.cobblemon.mod.common.net.messages.client.spawn.SpawnNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.RemoveClientPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.SwapClientPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePartyPacket
|
||||
@@ -94,10 +95,13 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemo
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
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.SummaryUIPacket
|
||||
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.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -110,6 +114,7 @@ import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
|
||||
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.SetNicknamePacket
|
||||
@@ -125,7 +130,10 @@ import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonT
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSelectedHandler
|
||||
@@ -139,6 +147,8 @@ import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
|
||||
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.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
@@ -156,21 +166,6 @@ import com.cobblemon.mod.common.net.serverhandling.storage.pc.MovePartyPokemonTo
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.ReleasePartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.SwapPCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.UnlinkPlayerFromPCHandler
|
||||
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.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
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.trade.*
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -308,6 +303,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
|
||||
|
||||
@@ -260,6 +260,10 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
|
||||
@JvmField
|
||||
val MOVE_QUICKATTACK_TARGET = this.create("move.quickattack.target")
|
||||
@JvmField
|
||||
val MOVE_PURSUIT_TARGET = this.create("move.pursuit.target")
|
||||
@JvmField
|
||||
val MOVE_PSYCHIC_TARGET = this.create("move.psychic.target")
|
||||
|
||||
@JvmField
|
||||
val IMPACT_NORMAL = this.create("impact.normal")
|
||||
|
||||
@@ -55,6 +55,8 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
|
||||
|
||||
val CAST_POKE_ROD = this.create("cast_poke_rod", SimpleCriterionTrigger(CastPokeRodCriterionCondition.CODEC))
|
||||
|
||||
val REEL_IN_POKEMON = this.create("reel_in_pokemon", SimpleCriterionTrigger(ReelInPokemonCriterionCondition.CODEC))
|
||||
|
||||
// Advancement criteria for [grow_tumblestone.json]
|
||||
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
|
||||
|
||||
|
||||
+14
-10
@@ -8,26 +8,30 @@
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class CastPokeRodContext(val baitId: ResourceLocation)
|
||||
|
||||
class CastPokeRodCriterionCondition(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val hasBait: Boolean
|
||||
): SimpleCriterionCondition<Boolean>(playerCtx) {
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val baitId: String
|
||||
): SimpleCriterionCondition<CastPokeRodContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<CastPokeRodCriterionCondition> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
|
||||
PrimitiveCodec.BOOL.fieldOf("hasBait").forGetter(CastPokeRodCriterionCondition::hasBait)
|
||||
).apply(it, ::CastPokeRodCriterionCondition) }
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(CastPokeRodCriterionCondition::playerCtx),
|
||||
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter(CastPokeRodCriterionCondition::baitId)
|
||||
).apply(it, { playerCtx, baitId -> CastPokeRodCriterionCondition(playerCtx, baitId.ifEmpty { "empty_bait" }) }) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: Boolean): Boolean {
|
||||
return hasBait == context
|
||||
override fun matches(player: ServerPlayer, context: CastPokeRodContext): Boolean {
|
||||
return (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId.equals("empty_bait"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.advancements.critereon.ContextAwarePredicate
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class ReelInPokemonContext(val pokemonId : ResourceLocation, val baitId: ResourceLocation)
|
||||
|
||||
class ReelInPokemonCriterionCondition(
|
||||
playerCtx: Optional<ContextAwarePredicate>,
|
||||
val pokemonId: String,
|
||||
val baitId: String
|
||||
) : SimpleCriterionCondition<ReelInPokemonContext>(playerCtx) {
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<ReelInPokemonCriterionCondition> = RecordCodecBuilder.create { it.group(
|
||||
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter { it.playerCtx },
|
||||
Codec.STRING.optionalFieldOf("pokemonId", "any").forGetter { it.pokemonId },
|
||||
Codec.STRING.optionalFieldOf("baitId", "empty_bait").forGetter { it.baitId }
|
||||
).apply(it, { playerCtx, pokemonId, baitId -> ReelInPokemonCriterionCondition(playerCtx, pokemonId, baitId.ifEmpty { "empty_bait" }) }) }
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayer, context: ReelInPokemonContext): Boolean {
|
||||
return (context.pokemonId == this.pokemonId.asIdentifierDefaultingNamespace() || this.pokemonId == "any") && (context.baitId == this.baitId.asIdentifierDefaultingNamespace() || this.baitId == "empty_bait")
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.ai.LookInDirectionTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
class LookInDirectionTaskConfig : SingleTaskConfig {
|
||||
var yaw: Expression = "0".asExpression()
|
||||
var pitch: Expression = "0".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = LookInDirectionTask(yaw, pitch)
|
||||
}
|
||||
@@ -172,4 +172,15 @@ The 'flee_attacker' type creates a task that will make the entity flee from its
|
||||
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the nearest hostile. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### run_script
|
||||
The 'run_script' type creates a task that will run a MoLang script every tick on this entity. This has the risk of being very laggy
|
||||
if the script is doing expensive things. Remember to use `q.entity.world.game_time` and `math.mod` to do intermittent actions inside
|
||||
the script to avoid lagging the game.
|
||||
- `script`: A resource location identifying the script to run. This should be in the `molang` datapack folder.
|
||||
|
||||
### look_in_direction
|
||||
The 'look_in_direction' type creates a task that will make the entity look in a specific direction at all times where they aren't being told to look elsewhere.
|
||||
- `yaw`: A MoLang expression that determines the yaw angle (degrees) of the entity. Defaults to `0`.
|
||||
- `pitch`: A MoLang expression that determines the pitch angle (degrees) of the entity. Defaults to `0`.
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
/**
|
||||
* Runs a registered MoLang script (from the molang datapack folder) each tick.
|
||||
*
|
||||
* It's up to the user to not make expensive ass scripts. Remember you can access the game time
|
||||
* from q.entity.level.game_time which can be used with a modulo operator to make something intermittent.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 2nd, 2024
|
||||
*/
|
||||
class RunScript : SingleTaskConfig {
|
||||
val script = cobblemonResource("dummy")
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? = BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.registered(MemoryModuleType.LOOK_TARGET) // I think I need to have at least something here?
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: entity.asMostSpecificMoLangValue())
|
||||
return@Trigger CobblemonScripts.run(script, runtime) == DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,8 @@ fun interface TaskConfig {
|
||||
"calm_down" to CalmDownTaskConfig::class.java,
|
||||
"flee_attacker" to FleeAttackerTaskConfig::class.java,
|
||||
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
|
||||
"run_script" to RunScript::class.java,
|
||||
"look_in_direction" to LookInDirectionTaskConfig::class.java,
|
||||
)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
|
||||
@@ -94,7 +94,6 @@ open class PokemonBattle(
|
||||
.filterIsInstance<LastBattleCriticalHitsEvolutionProgress>()
|
||||
.forEach { it.reset() }
|
||||
}
|
||||
actor.setupStruct()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.battles.model.actor
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.ActiveBattlePokemon
|
||||
import com.cobblemon.mod.common.battles.ForcePassActionResponse
|
||||
@@ -36,7 +37,7 @@ abstract class BattleActor(
|
||||
pokemonList.forEach { it.actor = this }
|
||||
}
|
||||
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
val struct = asMoLangValue()
|
||||
|
||||
lateinit var showdownId: String
|
||||
lateinit var battle: PokemonBattle
|
||||
@@ -139,10 +140,6 @@ abstract class BattleActor(
|
||||
battle.writeShowdownAction(">$showdownId ${showdownMessages.joinToString()}")
|
||||
}
|
||||
|
||||
open fun setupStruct() {
|
||||
struct.addFunction("uuid") { StringValue(uuid.toString()) }
|
||||
}
|
||||
|
||||
abstract fun getName(): MutableComponent
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
package com.cobblemon.mod.common.api.data
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
@@ -42,7 +45,24 @@ interface JsonDataRegistry<T> : DataRegistry {
|
||||
val resourcePath: String
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.reload(Cobblemon.implementation.reloadJsonRegistry(this, manager))
|
||||
val data = hashMapOf<ResourceLocation, T>()
|
||||
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
if (identifier.namespace == "pixelmon") {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
try {
|
||||
data[resolvedIdentifier] = gson.fromJson(reader, typeToken.type)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading JSON for data: $identifier", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reload(data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,13 +21,13 @@ import com.cobblemon.mod.common.util.adapters.DialogueTextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
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.adapters.TranslatedTextAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ object Dialogues : JsonDataRegistry<Dialogue> {
|
||||
.registerTypeAdapter(DialogueText::class.java, DialogueTextAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(MutableComponent::class.java, TextAdapter)
|
||||
.registerTypeAdapter(MutableComponent::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.also { gsonObservable.emit(it) }
|
||||
.create()
|
||||
|
||||
@@ -192,6 +192,8 @@ object CobblemonEvents {
|
||||
val POKEMON_GAINED = EventObservable<PokemonGainedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_SEEN = EventObservable<PokemonSeenEvent>()
|
||||
@JvmField
|
||||
val POKEDEX_DATA_CHANGED = EventObservable<PokedexDataChangedEvent>()
|
||||
|
||||
// Fishing
|
||||
@JvmField
|
||||
|
||||
+4
-4
@@ -19,8 +19,8 @@ import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
* @author Segfault Guy
|
||||
* @since April 6th, 2023
|
||||
*/
|
||||
data class BattleFaintedEvent (
|
||||
override val battle: PokemonBattle,
|
||||
val killed: BattlePokemon,
|
||||
val context: BattleContext
|
||||
data class BattleFaintedEvent(
|
||||
override val battle: PokemonBattle,
|
||||
val killed: BattlePokemon,
|
||||
val context: BattleContext
|
||||
) : BattleEvent
|
||||
|
||||
@@ -18,8 +18,6 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
* @since March 25th 2023
|
||||
*/
|
||||
data class BattleFledEvent (
|
||||
|
||||
override val battle: PokemonBattle,
|
||||
val player: PlayerBattleActor
|
||||
|
||||
) : BattleEvent
|
||||
+23
-1
@@ -8,8 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.battles
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
|
||||
/**
|
||||
* Event fired when a battle is won by some number of [BattleActor]s.
|
||||
@@ -22,4 +28,20 @@ data class BattleVictoryEvent (
|
||||
val winners : List<BattleActor>,
|
||||
val losers : List<BattleActor>,
|
||||
val wasWildCapture : Boolean
|
||||
) : BattleEvent
|
||||
) : BattleEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"battle" to battle.struct,
|
||||
"winners" to winners.asArrayValue { it.struct },
|
||||
"losers" to losers.asArrayValue { it.struct },
|
||||
"was_wild_capture" to DoubleValue(wasWildCapture),
|
||||
"player_winners" to winners.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"player_losers" to losers.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npc_winners" to winners.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"npc_losers" to losers.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon_winners" to winners.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
|
||||
"wild_pokemon_losers" to losers.filter { it.type == ActorType.WILD }.asArrayValue { it.struct },
|
||||
"players" to battle.actors.filter { it.type == ActorType.PLAYER }.asArrayValue { it.struct },
|
||||
"npcs" to battle.actors.filter { it.type == ActorType.NPC }.asArrayValue { it.struct },
|
||||
"wild_pokemon" to battle.actors.filter { it.type == ActorType.WILD }.asArrayValue { it.struct }
|
||||
)
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokedex.FormDexRecord
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Event that fires when a Pokémon's information is gained or updated in the Pokédex.
|
||||
*/
|
||||
data class PokedexDataChangedEvent(
|
||||
val dataSource: Either<PokedexEntityData, Pokemon>,
|
||||
val knowledge: PokedexEntryProgress,
|
||||
val playerUUID: UUID,
|
||||
val record: FormDexRecord
|
||||
) {
|
||||
val pokedexManager: AbstractPokedexManager
|
||||
get() = record.speciesDexRecord.pokedexManager
|
||||
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"player" to (playerUUID.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
|
||||
"pokemon" to dataSource.map({ DoubleValue.ZERO }, { it.struct }),
|
||||
"data" to dataSource.map({ it.struct }, { DoubleValue.ZERO }),
|
||||
"knowledge" to StringValue(knowledge.name.lowercase()),
|
||||
"pokedex" to pokedexManager.struct
|
||||
)
|
||||
}
|
||||
+12
-1
@@ -8,12 +8,23 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
data class PokemonCapturedEvent (
|
||||
val pokemon: Pokemon,
|
||||
val player: ServerPlayer,
|
||||
val pokeBallEntity: EmptyPokeBallEntity
|
||||
)
|
||||
) {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"pokemon" to pokemon.struct,
|
||||
"player" to player.asMoLangValue(),
|
||||
"poke_ball" to pokeBallEntity.struct,
|
||||
"item" to player.level().itemRegistry.wrapAsHolder(pokeBallEntity.pokeBall.item).asMoLangValue(Registries.ITEM)
|
||||
)
|
||||
}
|
||||
+1
-2
@@ -14,5 +14,4 @@ import java.util.UUID
|
||||
data class PokemonGainedEvent(
|
||||
val playerId: UUID,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
}
|
||||
)
|
||||
+1
-3
@@ -9,11 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
data class PokemonSeenEvent(
|
||||
val playerId: UUID,
|
||||
val pokemon: Pokemon
|
||||
) {
|
||||
}
|
||||
)
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.gui
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.gui.components.AbstractWidget
|
||||
import net.minecraft.client.gui.components.Renderable
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
@@ -22,9 +23,9 @@ abstract class ParentWidget(
|
||||
pX: Int, pY: Int,
|
||||
pWidth: Int, pHeight: Int,
|
||||
component: Component
|
||||
): Renderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
|
||||
): CobblemonRenderable, AbstractWidget(pX, pY, pWidth, pHeight, component) {
|
||||
|
||||
private val children: MutableList<GuiEventListener> = mutableListOf()
|
||||
val children: MutableList<GuiEventListener> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Adds Widget to the children list
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.item.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -33,7 +34,7 @@ interface AbilityChanger<T : PotentialAbility> {
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return A set containing all their legal abilities associated with [type], can be empty.
|
||||
*/
|
||||
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
|
||||
fun queryPossible(pokemon: Pokemon): Set<Pair<AbilityTemplate, Priority>>
|
||||
|
||||
/**
|
||||
* Attempts to update the ability of the given [pokemon].
|
||||
|
||||
@@ -22,11 +22,20 @@ import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtCount
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenPercent
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
@@ -37,17 +46,24 @@ import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.mojang.datafixers.util.Either
|
||||
import java.util.UUID
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
@@ -59,14 +75,23 @@ import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.damagesource.DamageTypes
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.entity.LightningBolt
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.player.Player
|
||||
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.dimension.DimensionType
|
||||
@@ -81,6 +106,31 @@ import net.minecraft.world.phys.Vec3
|
||||
*/
|
||||
object MoLangFunctions {
|
||||
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>(
|
||||
"print" to java.util.function.Function { params ->
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
Cobblemon.LOGGER.info(message)
|
||||
},
|
||||
"set_query" to java.util.function.Function { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
params.environment.query.addFunction(variable) { value }
|
||||
return@Function value
|
||||
},
|
||||
"replace" to java.util.function.Function { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
val replace = params.getString(2)
|
||||
return@Function StringValue(text.replace(search, replace))
|
||||
},
|
||||
"is_blank" to java.util.function.Function { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
},
|
||||
"run_command" to java.util.function.Function { params ->
|
||||
val command = params.getString(0)
|
||||
val server = server() ?: return@Function DoubleValue.ZERO
|
||||
server.commands.performPrefixedCommand(server.createCommandSourceStack(), command)
|
||||
},
|
||||
"is_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
|
||||
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
|
||||
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
|
||||
@@ -115,19 +165,93 @@ object MoLangFunctions {
|
||||
runtime.environment.variable = params.environment.variable
|
||||
runtime.environment.context = params.environment.context
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
|
||||
},
|
||||
"run_command" to java.util.function.Function { params ->
|
||||
val command = params.getString(0)
|
||||
server()?.let {
|
||||
return@Function DoubleValue(it.commands.dispatcher.execute(command, it.createCommandSourceStack()))
|
||||
} ?: return@Function DoubleValue.ZERO
|
||||
"run_molang" to java.util.function.Function { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
runtime.environment.variable = params.environment.variable
|
||||
runtime.environment.context = params.environment.context
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
runtime.resolve(expression)
|
||||
},
|
||||
)
|
||||
val biomeFunctions = mutableListOf<(Holder<Biome>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val worldFunctions = mutableListOf<(Holder<Level>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ worldHolder ->
|
||||
val world = worldHolder.value()
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map.put("time_of_day") {
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(time.toDouble())
|
||||
}
|
||||
map.put("server") { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map.put("is_raining_at") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
|
||||
}
|
||||
map.put("is_chunk_loaded_at") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isLoaded(BlockPos(x, y, z)))
|
||||
}
|
||||
map.put("is_thundering") { _ -> DoubleValue(world.isThundering) }
|
||||
map.put("is_raining") { _ -> DoubleValue(world.isRaining) }
|
||||
map.put("set_block") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
world.setBlock(BlockPos(x, y, z), block.defaultBlockState(), Block.UPDATE_ALL)
|
||||
}
|
||||
map.put("get_block") { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.getBlockState(BlockPos(x, y, z)).block
|
||||
return@put world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
|
||||
}
|
||||
map.put("spawn_explosion") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3).toFloat()
|
||||
world.explode(null, x, y, z, range, ExplosionInteraction.valueOf(params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name))
|
||||
}
|
||||
map.put("spawn_lightning") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, world)
|
||||
lightning.setPos(x, y, z)
|
||||
world.addFreshEntity(lightning)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("get_entities_around") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3) * 2
|
||||
val entities = world.getEntities(null, AABB.ofSize(Vec3(x, y, z), range, range, range))
|
||||
return@put entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val dimensionTypeFunctions = mutableListOf<(Holder<DimensionType>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val blockFunctions = mutableListOf<(Holder<Block>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>()
|
||||
val playerFunctions = mutableListOf<(Player) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ player ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -141,7 +265,7 @@ object MoLangFunctions {
|
||||
map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) }
|
||||
map.put("tell") { params ->
|
||||
val message = params.getString(0).text()
|
||||
val overlay = params.getBooleanOrNull(1) ?: false
|
||||
val overlay = params.getBooleanOrNull(1) == true
|
||||
player.displayClientMessage(message, overlay)
|
||||
}
|
||||
map.put("teleport") { params ->
|
||||
@@ -160,7 +284,12 @@ object MoLangFunctions {
|
||||
environment.query = player.asMoLangValue()
|
||||
environment
|
||||
}
|
||||
map.put("is_player") { DoubleValue.ONE }
|
||||
if (player is ServerPlayer) {
|
||||
map.put("run_command") { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map.put("is_party_at_full_health") { _ ->
|
||||
DoubleValue(player.party().none(Pokemon::canBeHealed)) }
|
||||
map.put("can_heal_at_healer") { params ->
|
||||
@@ -188,11 +317,46 @@ object MoLangFunctions {
|
||||
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
|
||||
map.put("in_battle") { DoubleValue(player.isInBattle()) }
|
||||
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map.put("get_npc_data") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put data.map[npcId]!!
|
||||
} else {
|
||||
val vars = VariableStruct()
|
||||
data.map[npcId] = vars
|
||||
return@put vars
|
||||
}
|
||||
}
|
||||
map.put("get_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_npc_variable") { params ->
|
||||
val npcId = (params.get<MoValue>(0) as? ObjectValue<NPCEntity>)?.obj?.stringUUID ?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val data = Cobblemon.molangData.load(player.uuid)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("pokedex") { player.pokedex().struct }
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
val entityFunctions = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val entityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
|
||||
@@ -201,6 +365,28 @@ object MoLangFunctions {
|
||||
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
|
||||
entity.hurt(source, amount.toFloat())
|
||||
}
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
if (entity is PathfinderMob) {
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(MemoryModuleType.WALK_TARGET, WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1))
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, BlockPosTracker(Vec3(x, y + entity.eyeHeight, z)))
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
map.put("has_walk_target") { _ ->
|
||||
if (entity is PathfinderMob) {
|
||||
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
} else {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_sneaking") { _ -> DoubleValue(entity.isShiftKeyDown) }
|
||||
map.put("is_sprinting") { _ -> DoubleValue(entity.isSprinting) }
|
||||
map.put("is_flying") { _ -> DoubleValue(entity.isFallFlying) }
|
||||
@@ -212,8 +398,14 @@ object MoLangFunctions {
|
||||
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
|
||||
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger()) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("distance_to_pos") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
return@put DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
|
||||
}
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
map.put("name") { _ -> StringValue(entity.effectiveName().string) }
|
||||
map.put("yaw") { _ -> DoubleValue(entity.yRot.toDouble()) }
|
||||
@@ -255,6 +447,21 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("get_nearby_entities") { params ->
|
||||
val distance = params.getDouble(0)
|
||||
val entities = entity.level().getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
|
||||
return@put entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
if (entity is PosableEntity) {
|
||||
map.put("play_animation") { params ->
|
||||
val animation = params.getString(0)
|
||||
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
|
||||
packet.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -315,9 +522,9 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
return@put DoubleValue(1)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
return@put DoubleValue(0)
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map.put("put_pokemon_in_healer") { params ->
|
||||
val healer = npc.level().getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE).orElse(null) ?: return@put DoubleValue.ZERO
|
||||
@@ -336,6 +543,8 @@ object MoLangFunctions {
|
||||
map.put("environment") { _ -> npc.runtime.environment }
|
||||
map.put("party") { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
map.put("has_party") { DoubleValue(npc.party != null) }
|
||||
map.put("is_npc") { DoubleValue.ONE }
|
||||
map.put("can_battle") { DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -355,10 +564,26 @@ object MoLangFunctions {
|
||||
return@put actor.struct
|
||||
}
|
||||
map.put("stop") { _ -> battle.stop() }
|
||||
map.put("actors") { battle.actors.toList().asArrayValue { it.struct } }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val battleActorFunctions = mutableListOf<(BattleActor) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ battleActor ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_npc") { DoubleValue(battleActor.type == ActorType.NPC) }
|
||||
map.put("is_player") { DoubleValue(battleActor.type == ActorType.PLAYER) }
|
||||
map.put("is_wild") { DoubleValue(battleActor.type == ActorType.WILD) }
|
||||
if (battleActor is NPCBattleActor) {
|
||||
map.put("npc") { battleActor.entity.struct }
|
||||
} else if (battleActor is PlayerBattleActor) {
|
||||
map.put("player") { battleActor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -383,6 +608,7 @@ object MoLangFunctions {
|
||||
}
|
||||
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
|
||||
map.put("species") { pokemon.species.struct }
|
||||
map.put("form") { StringValue(pokemon.form.name) }
|
||||
map.put("weight") { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
map.put("matches") { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
|
||||
@@ -397,6 +623,7 @@ object MoLangFunctions {
|
||||
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map.put("is_pokemon") { DoubleValue.ONE }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -535,10 +762,140 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions)
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions)
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions)
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
|
||||
val serverFunctions: MutableList<(MinecraftServer) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ server ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
map.put("get_world") { params ->
|
||||
val world = server.getLevel(
|
||||
ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
)
|
||||
) ?: return@put DoubleValue.ZERO
|
||||
|
||||
return@put world
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(world)
|
||||
.asWorldMoLangValue()
|
||||
}
|
||||
|
||||
map.put("broadcast") { params ->
|
||||
val message = params.getString(0)
|
||||
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
map.put("get_player_by_uuid") { params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val player = server.playerList.getPlayer(uuid) ?: return@put DoubleValue.ZERO
|
||||
return@put player.asMoLangValue()
|
||||
}
|
||||
|
||||
map.put("get_player_by_username") { params ->
|
||||
val username = params.getString(0)
|
||||
val player = server.playerList.getPlayerByName(username) ?: return@put DoubleValue.ZERO
|
||||
return@put player.asMoLangValue()
|
||||
}
|
||||
|
||||
map.put("data") { params ->
|
||||
val data = Cobblemon.molangData.load(UUID(0L, 0L))
|
||||
return@put data
|
||||
}
|
||||
|
||||
map.put("save_data") {
|
||||
Cobblemon.molangData.save(UUID(0L, 0L))
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
|
||||
// Maybe later...
|
||||
// map.put("stop") { params ->
|
||||
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
|
||||
// server.stopServer()
|
||||
// }
|
||||
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokedexFunctions: MutableList<(AbstractPokedexManager) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ pokedex ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("get_species_record") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
|
||||
}
|
||||
|
||||
map.put("has_seen") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.ENCOUNTERED.ordinal)
|
||||
} else {
|
||||
return@put DoubleValue((pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal ?: 0) >= PokedexEntryProgress.ENCOUNTERED.ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
map.put("has_caught") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.CAUGHT)
|
||||
} else {
|
||||
return@put DoubleValue(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.CAUGHT)
|
||||
}
|
||||
}
|
||||
map.put("caught_count") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
|
||||
map.put("seen_count") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
|
||||
map.put("caught_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
|
||||
map.put("seen_percent") { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
|
||||
|
||||
if (pokedex is PokedexManager) {
|
||||
map.put("player_id") { StringValue(pokedex.uuid.toString()) }
|
||||
map.put("see") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.encounter(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("catch") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.catch(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val speciesFunctions: MutableList<(Species) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ species ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
|
||||
map.put("identifier") { StringValue(species.resourceIdentifier.toString()) }
|
||||
map.put("name") { StringValue(species.name) }
|
||||
map.put("primary_type") { StringValue(species.primaryType.name) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.name ?: "null") }
|
||||
map.put("experience_group") { StringValue(species.experienceGroup.name) }
|
||||
map.put("height") { DoubleValue(species.height) }
|
||||
map.put("weight") { DoubleValue(species.weight) }
|
||||
map.put("base_scale") { DoubleValue(species.baseScale) }
|
||||
map.put("hitbox_width") { DoubleValue(species.hitbox.width) }
|
||||
map.put("hitbox_height") { DoubleValue(species.hitbox.height) }
|
||||
map.put("hitbox_fixed") { DoubleValue(species.hitbox.fixed) }
|
||||
map.put("catch_rate") { DoubleValue(species.catchRate) }
|
||||
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun MinecraftServer.asMoLangValue() = ObjectValue(this).addStandardFunctions().addFunctions(serverFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
fun Player.asMoLangValue(): ObjectValue<Player> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -586,6 +943,17 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonEntity.asMoLangValue(): ObjectValue<PokemonEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.pokemon.uuid.toString() }
|
||||
)
|
||||
value.addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(pokemonFunctions.flatMap { it(this.pokemon).entries.map { it.key to it.value } }.toMap()) // Convenience
|
||||
value.addFunctions(pokemonEntityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -595,6 +963,15 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun BattleActor.asMoLangValue(): ObjectValue<BattleActor> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(battleActorFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun SpawningContext.asMoLangValue(): ObjectValue<SpawningContext> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -604,6 +981,18 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Different functions exist depending on
|
||||
*/
|
||||
fun LivingEntity.asMostSpecificMoLangValue(): ObjectValue<out LivingEntity> {
|
||||
return when (this) {
|
||||
is Player -> asMoLangValue()
|
||||
is PokemonEntity -> struct
|
||||
is NPCEntity -> struct
|
||||
else -> ObjectValue(this).also { it.addStandardFunctions().addFunctions(entityFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -633,6 +1022,22 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokedexFunctions(pokedexManager: AbstractPokedexManager): QueryStruct {
|
||||
val addedFunctions = pokedexFunctions
|
||||
.flatMap { it.invoke(pokedexManager).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addSpeciesFunctions(species: Species): QueryStruct {
|
||||
val addedFunctions = speciesFunctions
|
||||
.flatMap { it.invoke(species).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonFunctions(pokemon: Pokemon): QueryStruct {
|
||||
val addedFunctions = pokemonFunctions
|
||||
.flatMap { it.invoke(pokemon).entries }
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
enum class MoveEffectiveness(val showdownId: Int, val multiplier: Double) {
|
||||
NORMAL(0, 1.0),
|
||||
WEAKNESS(1, 2.0),
|
||||
RESISTANCE(2, 0.5),
|
||||
IMMUNITY(3, 0.0);
|
||||
|
||||
companion object {
|
||||
fun of(showdownId: Int): MoveEffectiveness {
|
||||
return entries.find { it.showdownId == showdownId }
|
||||
?: throw IllegalArgumentException("Unknown MoveEffectiveness: $showdownId")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
|
||||
object TypeChart {
|
||||
private val movesEffectiveness = mutableMapOf<ElementalType, MutableMap<ElementalType, MoveEffectiveness>>()
|
||||
|
||||
fun load() {
|
||||
movesEffectiveness.clear()
|
||||
|
||||
val typeChartJson = ShowdownService.service.getTypeChart()
|
||||
typeChartJson.keySet().forEach { type ->
|
||||
val defenderType = ElementalTypes.get(type) ?: return@forEach
|
||||
|
||||
val moveTypes = typeChartJson.get(type).asJsonObject.get("damageTaken").asJsonObject
|
||||
moveTypes.keySet().forEach innerLoop@ {
|
||||
val moveType = ElementalTypes.get(it) ?: return@innerLoop
|
||||
val moveEffectiveness = moveTypes.get(it).asInt
|
||||
|
||||
val defenderMap = movesEffectiveness.getOrPut(moveType) { mutableMapOf() }
|
||||
defenderMap[defenderType] = MoveEffectiveness.of(moveEffectiveness)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getMoveEffectiveness(moveType: ElementalType, defenderTypes: List<ElementalType?>): Double {
|
||||
var multiplier = 1.0
|
||||
defenderTypes.filterNotNull().forEach {
|
||||
multiplier *= getMoveEffectiveness(moveType, it).multiplier
|
||||
}
|
||||
|
||||
return multiplier
|
||||
}
|
||||
|
||||
private fun getMoveEffectiveness(moveType: ElementalType, defenderType: ElementalType): MoveEffectiveness {
|
||||
return movesEffectiveness[moveType]?.get(defenderType) ?: throw IllegalArgumentException("Unknown effectiveness type: $moveType $defenderType")
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@ class NPCClass {
|
||||
var resourceIdentifier: ResourceLocation = cobblemonResource("dummy")
|
||||
var names: MutableList<Component> = mutableListOf()
|
||||
var aspects: MutableSet<String> = mutableSetOf() // These only make sense when applied via presets
|
||||
var hitbox = EntityDimensions.scalable(0.6F, 1.8F)
|
||||
var hitbox = EntityDimensions.scalable(0.6F, 1.8F).withEyeHeight(1.62F)
|
||||
var modelScale: Float = 0.94F
|
||||
var battleConfiguration = NPCBattleConfiguration()
|
||||
var interaction: NPCInteractConfiguration? = null
|
||||
var canDespawn = true
|
||||
@@ -48,6 +49,7 @@ class NPCClass {
|
||||
var party: NPCPartyProvider? = null
|
||||
var skill: Int = 0
|
||||
var autoHealParty: Boolean = true
|
||||
var randomizePartyOrder: Boolean = false
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var ai: MutableList<BrainConfig> = mutableListOf()
|
||||
var isMovable: Boolean = true
|
||||
@@ -83,6 +85,7 @@ class NPCClass {
|
||||
}
|
||||
buffer.writeInt(skill)
|
||||
buffer.writeBoolean(autoHealParty)
|
||||
buffer.writeBoolean(randomizePartyOrder)
|
||||
buffer.writeMapK(size = IntSize.U_BYTE, map = variables) { (key, value) ->
|
||||
buffer.writeString(key)
|
||||
buffer.writeString(value.asString())
|
||||
@@ -127,6 +130,7 @@ class NPCClass {
|
||||
}.toMutableList()
|
||||
skill = buffer.readInt()
|
||||
autoHealParty = buffer.readBoolean()
|
||||
randomizePartyOrder = buffer.readBoolean()
|
||||
buffer.readMapK(size = IntSize.U_BYTE, map = variables) {
|
||||
val key = buffer.readString()
|
||||
val value = buffer.readString()
|
||||
|
||||
@@ -70,7 +70,7 @@ object NPCClasses : JsonDataRegistry<NPCClass> {
|
||||
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassAdapter)
|
||||
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(WeightedAspect::class.java, WeightedAspectAdapter)
|
||||
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
|
||||
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
package com.cobblemon.mod.common.api.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.PoolPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.ScriptPartyProvider
|
||||
import com.cobblemon.mod.common.api.npc.partyproviders.SimplePartyProvider
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. Completely custom party providers will only display
|
||||
@@ -25,13 +27,14 @@ interface NPCPartyProvider {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, (String) -> NPCPartyProvider>(
|
||||
SimplePartyProvider.TYPE to { SimplePartyProvider() },
|
||||
PoolPartyProvider.TYPE to { PoolPartyProvider() }
|
||||
PoolPartyProvider.TYPE to { PoolPartyProvider() },
|
||||
ScriptPartyProvider.TYPE to { ScriptPartyProvider() }
|
||||
)
|
||||
}
|
||||
|
||||
val type: String
|
||||
val isStatic: Boolean
|
||||
fun provide(npc: NPCEntity, level: Int): NPCPartyStore
|
||||
fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer> = emptyList()): NPCPartyStore
|
||||
// Why did I opt for manual JSON loading??? I must have had a reason but I can't remember. Maybe for S2C? Use a codec doofus
|
||||
fun loadFromJSON(json: JsonElement)
|
||||
}
|
||||
@@ -30,6 +30,7 @@ class NPCPreset {
|
||||
var ai: List<BrainConfig>? = null
|
||||
var skill: Int? = null
|
||||
var autoHealParty: Boolean? = null
|
||||
var randomizePartyOrder: Boolean? = null
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var isMovable: Boolean? = null
|
||||
var isInvulnerable: Boolean? = null
|
||||
@@ -53,6 +54,7 @@ class NPCPreset {
|
||||
hitbox?.let { npcClass.hitbox = it }
|
||||
skill?.let { npcClass.skill = it }
|
||||
autoHealParty?.let { npcClass.autoHealParty = it }
|
||||
randomizePartyOrder?.let { npcClass.randomizePartyOrder = it }
|
||||
battleTheme?.let { npcClass.battleTheme = it }
|
||||
ai?.let { npcClass.ai.addAll(it) }
|
||||
isMovable?.let { npcClass.isMovable = it }
|
||||
|
||||
@@ -67,7 +67,7 @@ object NPCPresets : JsonDataRegistry<NPCPreset> {
|
||||
.registerTypeAdapter(NPCVariationProvider::class.java, NPCVariationProviderAdapter)
|
||||
.registerTypeAdapter(Activity::class.java, ActivityAdapter)
|
||||
.registerTypeAdapter(MoValue::class.java, MoValueAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TextAdapter)
|
||||
.registerTypeAdapter(Component::class.java, TranslatedTextAdapter)
|
||||
.registerTypeAdapter(BrainConfig::class.java, BrainConfigAdapter)
|
||||
.registerTypeAdapter(TaskConfig::class.java, TaskConfigAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
|
||||
@@ -57,8 +57,8 @@ any number of presets and the properties of those presets will be merged into th
|
||||
"variableName": "challenge_distance",
|
||||
"displayName": "cobblemon.npc.challenge_distance",
|
||||
"description": "cobblemon.npc.challenge_distance.description",
|
||||
"type": "number",
|
||||
"default": "5"
|
||||
"type": "NUMBER",
|
||||
"defaultValue": "5"
|
||||
}
|
||||
],
|
||||
"names": [
|
||||
@@ -77,6 +77,7 @@ any number of presets and the properties of those presets will be merged into th
|
||||
"canDespawn": false,
|
||||
"skill": 5,
|
||||
"autoHealParty": true,
|
||||
"randomizePartyOrder": true,
|
||||
"party": {
|
||||
"type": "simple",
|
||||
"pokemon": [
|
||||
@@ -178,19 +179,19 @@ An example situation of using this is if there is an NPC class preset that defin
|
||||
challenging a player when they come within a certain range. Inside that class preset, the AI script may
|
||||
need to know at what distance the player will be engaged. This can be defined in the NPC class preset as
|
||||
a config variable, and therefore any NPC class that relies on this preset will have that config variable
|
||||
and present it to users when they setup an NPC of that class.
|
||||
and present it to users when they set up an NPC of that class.
|
||||
|
||||
"config": [
|
||||
{
|
||||
"variableName": "challenge_distance",
|
||||
"displayName": "cobblemon.npc.challenge_distance",
|
||||
"description": "cobblemon.npc.challenge_distance.description",
|
||||
"type": "number",
|
||||
"default": "5"
|
||||
"displayName": "cobblemon.npc.challenge_distance.name",
|
||||
"description": "cobblemon.npc.challenge_distance.desc",
|
||||
"type": "NUMBER",
|
||||
"defaultValue": "5"
|
||||
}
|
||||
]
|
||||
|
||||
The "type" of variable is one of: `string`, `number`, or `boolean`. The type is used to preset a more
|
||||
The "type" of variable is one of: `STRING`, `NUMBER`, or `BOOLEAN`. The type is used to preset a more
|
||||
appropriate input field for the user when they are setting up the NPC.
|
||||
|
||||
### names
|
||||
@@ -282,15 +283,20 @@ is not mentioned in the Pokémon properties will be set to the seed level.
|
||||
The "pool" party type is a way to define a party of Pokémon using a more complicated format that involves some
|
||||
randomization and weighting to different Pokémon options.
|
||||
|
||||
A pool party provider can be static or dynamic depending on the value of `isStatic`. It specifies the minimum
|
||||
and maximum number of Pokémon that can be in the party using `minPokemon` and `maxPokemon`. The `pool` property dictates all of the possible Pokémon, with
|
||||
each being an object with several properties.
|
||||
A pool party provider can be static or dynamic depending on the value of `isStatic`. If `useFixedRandom` is true, the Pokémon it selects
|
||||
from the pool will be constant for a single spawned NPC entity. This is slightly different to a static party because
|
||||
the level of the Pokémon can still be modified dynamically using MoLang values inside each entry's `level` field.
|
||||
|
||||
The pool specifies the minimum and maximum number of Pokémon that can be in the party using `minPokemon` and `maxPokemon`.
|
||||
|
||||
The `pool` property dictates all the possible Pokémon with each being an object with several properties.
|
||||
|
||||
- The `pokemon` property is the Pokémon properties as used in commands and spawn files that will generate the Pokémon.
|
||||
- The `weight` property is a decimal number that represents how likely that Pokémon is to be chosen. If the weight is -1, it will be guaranteed to be selected. Defaults to 1. The larger the value, the more likely it will be chosen.
|
||||
- The `selectableTimes` property is the maximum number of times that the entry can be selected in the party. Defaults to 1.
|
||||
- The `npcLevels` property is a range of seed levels that the Pokémon can be generated at. For example, if the NPC is spawned with seed level 10, and the range is 5-9, the Pokémon will not be selected. The actual level of the Pokémon will be the seed level. Defaults to "1-100".
|
||||
- The `levelVariation` property is the amount of variation the Pokémon's level can have from the NPC seed level. For example, a value of 2 will mean that for an NPC generated with seed level 10, the Pokémon will be generated somewhere between level 8 and 12 inclusive. Defaults to zero.
|
||||
- The `level` property can be used instead of `levelVariation` to force a specific level. This is a MoLang expression.
|
||||
|
||||
For `minPokemon`, `maxPokemon`, `weight`, `selectableTimes`, and `levelVariation`, the values can be a simple number or be full MoLang expressions. At the time
|
||||
of seeding an NPC, the cosmetic variations have already been applied. Therefore, it's possible to make these properties
|
||||
@@ -298,11 +304,16 @@ depend on visual aspects of the NPC. For example, if the NPC has a variation for
|
||||
certain Pokémon to be `"weight": "q.npc.has_aspect('dirty') ? -1 : 0"` which would make it a guaranteed party Pokémon for "dirty" NPCs
|
||||
and impossible for all other variations of the NPC.
|
||||
|
||||
The MoLang environment used for the pool party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
|
||||
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
|
||||
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
|
||||
|
||||
"party": {
|
||||
"type": "pool",
|
||||
"minPokemon": "3",
|
||||
"maxPokemon": "6",
|
||||
"isStatic": true,
|
||||
"isStatic": false,
|
||||
"useFixedRandom": true,
|
||||
"pool": [
|
||||
{
|
||||
"pokemon": "weedle",
|
||||
@@ -339,11 +350,40 @@ and impossible for all other variations of the NPC.
|
||||
"pokemon": "butterfree",
|
||||
"weight": "5",
|
||||
"npcLevels": "10-15",
|
||||
"selectableTimes": "1"
|
||||
"selectableTimes": "1",
|
||||
"level": "q.player.party.highest_level"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
#### script
|
||||
The "script" party type is a way to define a party of Pokémon using a MoLang script. The script can modify a q.party struct,
|
||||
primarily using function calls such as `q.party.add_by_properties('pikachu shiny)`.
|
||||
|
||||
There is an `isStatic` property to control whether the party is static or dynamic. There is a `script` property which is a
|
||||
resource location pointing to the script that should be located in the `molang` datapack folder.
|
||||
|
||||
The MoLang environment used for the script party provider contains q.level for the seed level, q.npc for the NPC, as well as q.players for
|
||||
an array of all players that are challenging the NPC. The players list is only present for dynamic parties. If it is specifically
|
||||
a single battle challenge and this is a dynamic party, there will also be a `q.player`, for convenience.
|
||||
|
||||
"party": {
|
||||
"type": "script",
|
||||
"isStatic": true,
|
||||
"script": "cobblemon:npc_party"
|
||||
}
|
||||
|
||||
An example script might be:
|
||||
|
||||
t.use_butterfree = math.random_integer(1, 2) == 1;
|
||||
t.use_butterfree ? {
|
||||
q.party.add_by_properties('butterfree level=' + (q.level + 3));
|
||||
} : {
|
||||
q.party.add_by_properties('beedrill level=' + (q.level + 3));
|
||||
};
|
||||
q.party.add_by_properties('pidgey');
|
||||
|
||||
### autoHealParty
|
||||
Whether or not the NPC's party will be healed between battles. If the NPC has a dynamic party, this option will
|
||||
do nothing. If the NPC has a static party and this option is false, their Pokémon will not be healed and the
|
||||
@@ -364,6 +404,11 @@ the NPC and move them using leads.
|
||||
Controls whether the NPC can be hit by projectiles. Disabling this option prevents unwanted
|
||||
interactions, such as players moving the NPC with fishing rods or other projectiles.
|
||||
|
||||
### randomizePartyOrder
|
||||
Whether the NPC's party order will be randomized between battles. When true, this NPC's party (regardless of
|
||||
whether it was dynamic or static) will choose its initial send-out Pokémon randomly instead of always choosing the first
|
||||
Pokémon in the party.
|
||||
|
||||
### ai
|
||||
The AI property is an array of brain configurations. This is used to configure the behaviours of the NPC. See
|
||||
the documentation for brain configurations at [AI Configuration](../ai/config/README.md).
|
||||
+36
-8
@@ -13,6 +13,11 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.readText
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.cobblemon.mod.common.util.writeText
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
/**
|
||||
@@ -30,10 +35,38 @@ class NPCConfigVariable(
|
||||
val type: NPCVariableType = NPCVariableType.NUMBER,
|
||||
val defaultValue: String = "0",
|
||||
) {
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): NPCConfigVariable {
|
||||
return NPCConfigVariable(
|
||||
buffer.readString(),
|
||||
buffer.readText(),
|
||||
buffer.readText(),
|
||||
buffer.readEnum(NPCVariableType::class.java),
|
||||
buffer.readString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class NPCVariableType {
|
||||
NUMBER,
|
||||
STRING,
|
||||
BOOLEAN
|
||||
TEXT,
|
||||
BOOLEAN;
|
||||
|
||||
fun toMoValue(value: String): MoValue {
|
||||
return when (this) {
|
||||
TEXT -> StringValue(value)
|
||||
BOOLEAN -> DoubleValue(value.let { it == "1" || it.toBooleanStrictOrNull() == true })
|
||||
else -> DoubleValue(value.toDouble())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(variableName)
|
||||
buffer.writeText(displayName)
|
||||
buffer.writeText(description)
|
||||
buffer.writeEnum(type)
|
||||
buffer.writeString(defaultValue)
|
||||
}
|
||||
|
||||
fun apply(npc: NPCEntity, value: MoValue) {
|
||||
@@ -41,11 +74,6 @@ class NPCConfigVariable(
|
||||
}
|
||||
|
||||
fun applyDefault(npc: NPCEntity) {
|
||||
val value = when (type) {
|
||||
NPCVariableType.STRING -> StringValue(defaultValue)
|
||||
NPCVariableType.BOOLEAN -> DoubleValue(defaultValue.toBoolean())
|
||||
else -> DoubleValue(defaultValue.toDouble())
|
||||
}
|
||||
npc.config.set(variableName.split(".").iterator(), value)
|
||||
npc.config.setDirectly(variableName, type.toMoValue(defaultValue))
|
||||
}
|
||||
}
|
||||
+34
-16
@@ -10,11 +10,14 @@ package com.cobblemon.mod.common.api.npc.partyproviders
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
@@ -23,6 +26,8 @@ import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* A provider of a party for battling the NPC. It generates a party using a more complex process
|
||||
@@ -46,6 +51,7 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
override val type = TYPE
|
||||
|
||||
override var isStatic: Boolean = false
|
||||
var useFixedRandom: Boolean = false
|
||||
var minPokemon: Expression = "1".asExpression()
|
||||
var maxPokemon: Expression = "6".asExpression()
|
||||
var pool = mutableListOf<DynamicPokemon>()
|
||||
@@ -59,6 +65,7 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
return@map DynamicPokemon(
|
||||
PokemonProperties.parse(it.asString),
|
||||
"0".asExpression(),
|
||||
null,
|
||||
1..100,
|
||||
"1".asExpression(),
|
||||
"1".asExpression()
|
||||
@@ -67,19 +74,22 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
|
||||
it as JsonObject
|
||||
DynamicPokemon(
|
||||
PokemonProperties.parse(it.getAsJsonPrimitive("pokemon").asString),
|
||||
it.getAsJsonPrimitive("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
|
||||
it.getAsJsonPrimitive("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
|
||||
it.getAsJsonPrimitive("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
|
||||
it.getAsJsonPrimitive("weight")?.asString?.asExpression() ?: "1".asExpression()
|
||||
PokemonProperties.parse(it.get("pokemon").asString),
|
||||
it.get("levelVariation")?.asString?.asExpression() ?: "0".asExpression(),
|
||||
it.get("level")?.asString?.asExpression(),
|
||||
it.get("npcLevels")?.asString?.split("-")?.let { it[0].toInt()..it[1].toInt() } ?: 1..100,
|
||||
it.get("selectableTimes")?.asString?.asExpression() ?: "1".asExpression(),
|
||||
it.get("weight")?.asString?.asExpression() ?: "1".asExpression()
|
||||
)
|
||||
}.toMutableList()
|
||||
isStatic = json.getAsJsonPrimitive("isStatic").asBoolean
|
||||
isStatic = json.get("isStatic").asBoolean
|
||||
json.get("useFixedRandom")?.asBoolean?.let { useFixedRandom = it }
|
||||
}
|
||||
|
||||
class DynamicPokemon(
|
||||
val pokemon: PokemonProperties,
|
||||
val levelVariation: Expression,
|
||||
val level: Expression? = null,
|
||||
val npcLevels: IntRange,
|
||||
val selectableTimes: Expression,
|
||||
val weight: Expression = "1".asExpression()
|
||||
@@ -93,18 +103,25 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fun formulateParty(npc: NPCEntity, level: Int, party: NPCPartyStore) {
|
||||
fun formulateParty(npc: NPCEntity, level: Int, players: List<ServerPlayer>, party: NPCPartyStore) {
|
||||
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
|
||||
val random = if (useFixedRandom) Random(npc.uuid.hashCode()) else Random.Default
|
||||
runtime.withQueryValue("level", DoubleValue(level))
|
||||
runtime.withQueryValue("players", players.asArrayValue { it.asMoLangValue() })
|
||||
if (players.size == 1) {
|
||||
// This is for the convenience, most cases will be pvn one player
|
||||
runtime.withQueryValue("player", players.first().asMoLangValue())
|
||||
}
|
||||
val minPokemon = runtime.resolveInt(this.minPokemon)
|
||||
val maxPokemon = runtime.resolveInt(this.maxPokemon)
|
||||
var desiredPokemonCount = (minPokemon..maxPokemon).random()
|
||||
var desiredPokemonCount = (minPokemon..maxPokemon).random(random)
|
||||
|
||||
val workingPool = pool.filter { level in it.npcLevels }.toMutableList()
|
||||
val workingPool = pool.filter { level in it.npcLevels && runtime.resolveInt(it.selectableTimes) > 0 }.toMutableList()
|
||||
val useCounts = mutableMapOf<DynamicPokemon, Int>()
|
||||
|
||||
while (desiredPokemonCount > 0 && workingPool.filter { it.hasWeight(runtime) }.isNotEmpty()) {
|
||||
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull()
|
||||
?: workingPool.weightedSelection { it.getWeight(runtime) }
|
||||
while (desiredPokemonCount > 0 && workingPool.any { it.hasWeight(runtime) }) {
|
||||
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull(random)
|
||||
?: workingPool.weightedSelection(random = random) { it.getWeight(runtime) }
|
||||
?: break
|
||||
useCounts[selected] = useCounts.getOrDefault(selected, 0) + 1
|
||||
val allowedSelections = runtime.resolveInt(selected.selectableTimes)
|
||||
@@ -114,16 +131,17 @@ class PoolPartyProvider : NPCPartyProvider {
|
||||
desiredPokemonCount--
|
||||
|
||||
// If the Pokémon's props specifies a level then use that, otherwise choose a random level within the range
|
||||
val levelVariation = (0..runtime.resolveInt(selected.levelVariation)).random()
|
||||
val levelVariation = (0..runtime.resolveInt(selected.levelVariation)).random(random)
|
||||
val randomLevel = level + levelVariation
|
||||
val instance = selected.pokemon.copy().also { it.level = it.level ?: randomLevel }.create()
|
||||
val dictatedLevel = selected.level?.let { runtime.resolveInt(it) }
|
||||
val instance = selected.pokemon.copy().also { it.level = it.level ?: dictatedLevel ?: randomLevel }.create()
|
||||
party.add(instance)
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
|
||||
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
|
||||
val party = NPCPartyStore(npc)
|
||||
formulateParty(npc, level, party)
|
||||
formulateParty(npc, level, players, party)
|
||||
return party
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.npc.partyproviders
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.npc.NPCPartyProvider
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
class ScriptPartyProvider : NPCPartyProvider {
|
||||
companion object {
|
||||
const val TYPE = "script"
|
||||
}
|
||||
|
||||
override val type = TYPE
|
||||
override var isStatic: Boolean = true
|
||||
var script: ResourceLocation = cobblemonResource("dummy")
|
||||
|
||||
override fun loadFromJSON(json: JsonElement) {
|
||||
isStatic = json.asJsonObject.get("isStatic")?.asBoolean != false
|
||||
script = json.asJsonObject.get("script").asString.asIdentifierDefaultingNamespace()
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
|
||||
val runtime = MoLangRuntime().setup().withQueryValue("npc", npc.struct)
|
||||
runtime.withQueryValue("level", DoubleValue(level))
|
||||
runtime.withQueryValue("players", players.asArrayValue { it.asMoLangValue() })
|
||||
if (players.size == 1) {
|
||||
// This is for the convenience, most cases will be pvn one player
|
||||
runtime.withQueryValue("player", players.first().asMoLangValue())
|
||||
}
|
||||
val npcParty = NPCPartyStore(npc)
|
||||
runtime.withQueryValue("party", npcParty.struct)
|
||||
|
||||
if (!CobblemonScripts.scripts.containsKey(script)) {
|
||||
Cobblemon.LOGGER.error("Script $script not found in script registry.")
|
||||
npcParty.add("magikarp".toProperties().create())
|
||||
return npcParty
|
||||
}
|
||||
|
||||
CobblemonScripts.run(identifier = script, runtime = runtime)
|
||||
|
||||
return npcParty
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import com.google.gson.JsonElement
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* A basic party provider that just produces a [NPCPartyStore] based on a simple list of [PokemonProperties].
|
||||
@@ -52,7 +53,7 @@ class SimplePartyProvider : NPCPartyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(npc: NPCEntity, level: Int): NPCPartyStore {
|
||||
override fun provide(npc: NPCEntity, level: Int, players: List<ServerPlayer>): NPCPartyStore {
|
||||
val pokemon = pokemon.map { it.copy().also { it.level = it.level ?: level }.create() }
|
||||
val party = NPCPartyStore(npc)
|
||||
pokemon.forEach(party::add)
|
||||
|
||||
+1
-1
@@ -32,5 +32,5 @@ class WeightedNPCVariationProvider : NPCVariationProvider {
|
||||
override val aspects: Set<String>
|
||||
get() = options.flatMap(WeightedAspect::aspects).toSet()
|
||||
|
||||
override fun provideAspects(npcEntity: NPCEntity) = options.weightedSelection(WeightedAspect::weight)?.aspects ?: emptySet()
|
||||
override fun provideAspects(npcEntity: NPCEntity) = options.weightedSelection { it.weight }?.aspects ?: emptySet()
|
||||
}
|
||||
@@ -82,6 +82,8 @@ object CobblemonPermissions {
|
||||
|
||||
val ABANDON_MULTITEAM = this.create("${COMMAND_PREFIX}abandonmultiteam", PermissionLevel.NONE)
|
||||
|
||||
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
fun all(): Iterable<Permission> = this.permissions
|
||||
|
||||
private fun create(node: String, level: PermissionLevel): Permission {
|
||||
|
||||
+5
-7
@@ -8,13 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokedex
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokedexFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
abstract class AbstractPokedexManager {
|
||||
@@ -23,11 +23,9 @@ abstract class AbstractPokedexManager {
|
||||
private val globalCalculatedValues = mutableMapOf<GlobalPokedexValueCalculator<*>, Any>()
|
||||
|
||||
@Transient
|
||||
val struct = QueryStruct(hashMapOf()).addStandardFunctions()
|
||||
.addFunction("get_species_record") { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
|
||||
}
|
||||
val struct = ObjectValue<AbstractPokedexManager>(this)
|
||||
.addStandardFunctions()
|
||||
.addPokedexFunctions(this)
|
||||
|
||||
fun deleteSpeciesRecord(speciesId: ResourceLocation) {
|
||||
speciesRecords.remove(speciesId)
|
||||
|
||||
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common.api.pokedex
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.PokedexDataChangedEvent
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -19,6 +21,7 @@ import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeEnumConstant
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import com.google.common.collect.Sets
|
||||
import com.mojang.datafixers.util.Either
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
@@ -123,6 +126,10 @@ class FormDexRecord {
|
||||
}
|
||||
speciesDexRecord.addInformation(pokemon, knowledge)
|
||||
speciesDexRecord.onFormRecordUpdated(this)
|
||||
|
||||
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
|
||||
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.right(pokemon), knowledge, pokedexManager.uuid, this))
|
||||
}
|
||||
}
|
||||
|
||||
private fun addInformation(pokedexEntityData: PokedexEntityData, knowledge: PokedexEntryProgress) {
|
||||
@@ -133,6 +140,10 @@ class FormDexRecord {
|
||||
}
|
||||
speciesDexRecord.addInformation(pokedexEntityData, knowledge)
|
||||
speciesDexRecord.onFormRecordUpdated(this)
|
||||
|
||||
(speciesDexRecord.pokedexManager as? PokedexManager)?.let { pokedexManager ->
|
||||
CobblemonEvents.POKEDEX_DATA_CHANGED.post(PokedexDataChangedEvent(Either.left(pokedexEntityData), knowledge, pokedexManager.uuid, this))
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the given [Pokemon] and [knowledge] would add new information to the Pokédex.*/
|
||||
|
||||
@@ -36,7 +36,7 @@ import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.isInt
|
||||
import com.cobblemon.mod.common.util.isDouble
|
||||
import com.cobblemon.mod.common.util.isUuid
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.simplify
|
||||
@@ -59,7 +59,6 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
@@ -187,10 +186,10 @@ open class PokemonProperties {
|
||||
private fun parseIntProperty(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): Int? {
|
||||
val matchingKeyPair = getMatchedKeyPair(keyPairs, labels) ?: return null
|
||||
val value = matchingKeyPair.second
|
||||
return if (value == null || !value.isInt()) {
|
||||
return if (value == null || !value.isDouble()) {
|
||||
null
|
||||
} else {
|
||||
value.toInt()
|
||||
value.toDouble().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,7 +789,7 @@ open class PokemonProperties {
|
||||
}
|
||||
|
||||
fun copy(): PokemonProperties {
|
||||
return PokemonProperties().loadFromJSON(saveToJSON())
|
||||
return PokemonProperties().loadFromJSON(this.saveToJSON())
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
|
||||
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.TypeChart
|
||||
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
|
||||
@@ -125,6 +126,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
it.indicateSpeciesInitialized()
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
TypeChart.load()
|
||||
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.ScriptRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.contextOrEmpty
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -64,6 +65,6 @@ object CobblemonScripts : DataRegistry {
|
||||
}
|
||||
|
||||
fun run(identifier: ResourceLocation, runtime: MoLangRuntime): MoValue? {
|
||||
return scripts[identifier]?.resolve(runtime)
|
||||
return scripts[identifier]?.resolve(runtime, runtime.contextOrEmpty)
|
||||
}
|
||||
}
|
||||
+38
-2
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
@@ -33,9 +35,43 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
|
||||
if (minLureLevel != null) { // check for the lureLevel of the rod
|
||||
val pokerodStack = ctx.rodStack
|
||||
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(
|
||||
ctx.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
|
||||
pokerodStack
|
||||
)
|
||||
if (lureLevel < minLureLevel!!) {
|
||||
return false
|
||||
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (bait != null) { // check for the bait on the bobber
|
||||
val pokerodBait = ctx.rodBait?.item
|
||||
if (pokerodBait != bait) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (rodType != null) { // check for the type of pokerod being used
|
||||
val pokerodItem = ctx.rodItem
|
||||
if (pokerodItem?.pokeRodId != rodType) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
|
||||
val pokerodItem = (ctx as FishingSpawningContext).rodItem
|
||||
|
||||
// todo check if the EV yield of the berry matches the bait EV attract maybe?
|
||||
|
||||
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
|
||||
return false
|
||||
}*/
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
-27
@@ -116,24 +116,6 @@ abstract class SpawningCondition<T : SpawningContext> {
|
||||
}
|
||||
) {
|
||||
return false
|
||||
} else if (minLureLevel != null && ctx is FishingSpawningContext) { // check for the lureLevel of the rod
|
||||
val pokerodStack = (ctx as FishingSpawningContext).rodStack
|
||||
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(ctx.enchantmentRegistry.getHolder(Enchantments.LURE).get(), pokerodStack)
|
||||
if (lureLevel < minLureLevel!!) {
|
||||
return false
|
||||
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
|
||||
return false
|
||||
}
|
||||
} else if (bait != null && ctx is FishingSpawningContext) { // check for the bait on the bobber
|
||||
val pokerodBait = (ctx as FishingSpawningContext).rodBait?.item
|
||||
if (pokerodBait != bait) {
|
||||
return false
|
||||
}
|
||||
} else if (rodType != null && ctx is FishingSpawningContext) { // check for the type of pokerod being used
|
||||
val pokerodItem = (ctx as FishingSpawningContext).rodItem
|
||||
if (pokerodItem?.pokeRodId != rodType) {
|
||||
return false
|
||||
}
|
||||
} else if (isSlimeChunk != null && isSlimeChunk != false) {
|
||||
val isSlimeChunk = WorldgenRandom.seedSlimeChunk(ctx.position.x shr 4, ctx.position.z shr 4, ctx.world.seed, 987234911L).nextInt(10) == 0
|
||||
|
||||
@@ -152,15 +134,6 @@ abstract class SpawningCondition<T : SpawningContext> {
|
||||
return random.nextInt(10) == 0*/
|
||||
}
|
||||
|
||||
/*else if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
|
||||
val pokerodItem = (ctx as FishingSpawningContext).rodItem
|
||||
|
||||
// todo check if the EV yield of the berry matches the bait EV attract maybe?
|
||||
|
||||
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
|
||||
return false
|
||||
}*/
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
|
||||
/**
|
||||
* A [SpawnAction] that will spawn a single [PokemonEntity].
|
||||
|
||||
@@ -269,7 +269,7 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
BattlePokemon.playerOwned(it)
|
||||
}.also { if (healPokemon) it.effectedPokemon.heal() }
|
||||
}.toMutableList()
|
||||
if(leadingPokemon != null) {
|
||||
if (leadingPokemon != null) {
|
||||
Collections.rotate(result, result.size - this.indexOfFirst { it.uuid == leadingPokemon })
|
||||
}
|
||||
return result
|
||||
|
||||
+23
-11
@@ -102,20 +102,32 @@ open class PlayerPartyStore(
|
||||
for (pokemon in this) {
|
||||
// Awake from fainted
|
||||
if (pokemon.isFainted()) {
|
||||
pokemon.faintedTimer -= 1
|
||||
if (pokemon.faintedTimer <= -1) {
|
||||
val php = ceil(pokemon.maxHealth * Cobblemon.config.faintAwakenHealthPercent)
|
||||
pokemon.currentHealth = php.toInt()
|
||||
player.sendSystemMessage(Component.translatable("cobblemon.party.faintRecover", pokemon.getDisplayName()))
|
||||
//Skip awaken logic check if config value is 0
|
||||
if (Cobblemon.config.faintAwakenHealthPercent > 0) {
|
||||
pokemon.faintedTimer -= 1
|
||||
if (pokemon.faintedTimer <= -1) {
|
||||
val php = ceil(pokemon.maxHealth * Cobblemon.config.faintAwakenHealthPercent)
|
||||
pokemon.currentHealth = php.toInt()
|
||||
player.sendSystemMessage(
|
||||
Component.translatable(
|
||||
"cobblemon.party.faintRecover",
|
||||
pokemon.getDisplayName()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Passive healing while less than full health
|
||||
else if (pokemon.currentHealth < pokemon.maxHealth) {
|
||||
pokemon.healTimer--
|
||||
if (pokemon.healTimer <= -1) {
|
||||
pokemon.healTimer = Cobblemon.config.healTimer
|
||||
val healAmount = 1.0.coerceAtLeast(pokemon.maxHealth.toDouble() * Cobblemon.config.healPercent)
|
||||
pokemon.currentHealth = pokemon.currentHealth + round(healAmount).toInt()
|
||||
//Skip passive healing logic check if config value is 0
|
||||
if (Cobblemon.config.healPercent > 0) {
|
||||
pokemon.healTimer--
|
||||
if (pokemon.healTimer <= -1) {
|
||||
pokemon.healTimer = Cobblemon.config.healTimer
|
||||
val healAmount =
|
||||
1.0.coerceAtLeast(pokemon.maxHealth.toDouble() * Cobblemon.config.healPercent)
|
||||
pokemon.currentHealth = pokemon.currentHealth + round(healAmount).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,4 +216,4 @@ open class PlayerPartyStore(
|
||||
super.set(position, pokemon)
|
||||
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ object BattleBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
if (actor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[actor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[actor] += BattleStartError.alreadyInBattle(player)
|
||||
}
|
||||
@@ -178,6 +182,10 @@ object BattleBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
if (actor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[actor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[actor] += BattleStartError.alreadyInBattle(player)
|
||||
}
|
||||
@@ -248,7 +256,7 @@ object BattleBuilder {
|
||||
val wildActor = PokemonBattleActor(pokemonEntity.pokemon.uuid, BattlePokemon(pokemonEntity.pokemon), fleeDistance)
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
if(playerTeam.isNotEmpty() && playerTeam[0].health <= 0){
|
||||
if (playerTeam.isNotEmpty() && playerTeam[0].health <= 0){
|
||||
errors.participantErrors[playerActor] += BattleStartError.insufficientPokemon(
|
||||
actorEntity = player,
|
||||
requiredCount = battleFormat.battleType.slotsPerActor,
|
||||
@@ -264,6 +272,10 @@ object BattleBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
if (BattleRegistry.getBattleByParticipatingPlayer(player) != null) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.alreadyInBattle(playerActor)
|
||||
}
|
||||
@@ -315,7 +327,7 @@ object BattleBuilder {
|
||||
): BattleStartResult {
|
||||
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon)
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
val npcParty = npcEntity.getPartyForChallenge(player)
|
||||
val npcParty = npcEntity.getPartyForChallenge(listOf(player))
|
||||
val errors = ErroredBattleStart()
|
||||
|
||||
if (playerActor.pokemonList.size < battleFormat.battleType.slotsPerActor) {
|
||||
@@ -348,6 +360,10 @@ object BattleBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
if (playerActor.pokemonList.any { it.entity?.isBusy == true }) {
|
||||
errors.participantErrors[playerActor] += BattleStartError.targetIsBusy(player.displayName ?: player.name)
|
||||
}
|
||||
|
||||
playerActor.battleTheme = npcEntity.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
@@ -394,7 +410,7 @@ interface BattleStartError {
|
||||
fun alreadyInBattle(pokemonEntity: PokemonEntity) = AlreadyInBattleError(pokemonEntity.uuid, pokemonEntity.effectiveName())
|
||||
fun alreadyInBattle(actor: BattleActor) = AlreadyInBattleError(actor.uuid, actor.getName())
|
||||
fun noParty(npcEntity: NPCEntity) = NoPartyError(npcEntity)
|
||||
fun targetIsBusy(targetName: MutableComponent) = BusyError(targetName)
|
||||
fun targetIsBusy(targetName: Component) = BusyError(targetName)
|
||||
fun insufficientPokemon(
|
||||
actorEntity: Entity,
|
||||
requiredCount: Int,
|
||||
@@ -474,7 +490,7 @@ class AlreadyInBattleError(
|
||||
}
|
||||
}
|
||||
class BusyError(
|
||||
val targetName: MutableComponent
|
||||
val targetName: Component
|
||||
): BattleStartError {
|
||||
override fun getMessageFor(entity: Entity) = battleLang("errors.busy", targetName)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.runner
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,7 @@ interface ShowdownService {
|
||||
fun getAbilityIds(): JsonArray
|
||||
fun getMoves(): JsonArray
|
||||
fun getItemIds(): JsonArray
|
||||
fun getTypeChart(): JsonObject
|
||||
fun registerSpecies()
|
||||
fun registerBagItems()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
@@ -38,4 +40,4 @@ interface ShowdownService {
|
||||
companion object {
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
@@ -152,6 +153,12 @@ class GraalShowdownService : ShowdownService {
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getTypeChart(): JsonObject {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getTypeChart")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonObject::class.java)
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
|
||||
+8
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
@@ -123,6 +124,13 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getTypeChart(): JsonObject {
|
||||
writer.write(">getTypeChart")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonObject::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
|
||||
@@ -157,7 +157,7 @@ class GildedChestBlock(settings: Properties, val type: Type = Type.RED) : BaseEn
|
||||
// The yaw based on the block's facing direction
|
||||
val yaw = facingToYaw[state.getValue(HorizontalDirectionalBlock.FACING)] ?: 0.0F
|
||||
|
||||
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, facingToYaw[state.getValue(HorizontalDirectionalBlock.FACING)])
|
||||
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, yaw)
|
||||
val offsetDir = state.getValue(HorizontalDirectionalBlock.FACING)
|
||||
val vec = pos.toVec3d().add(offsetDir.stepX * 0.1 + 0.5, 0.0, offsetDir.stepZ * 0.1 + 0.5)
|
||||
entity.moveTo(vec.x, vec.y, vec.z, yaw, entity.xRot)
|
||||
|
||||
@@ -125,6 +125,7 @@ object ClientMoLangFunctions {
|
||||
val maxPitch = params.getDoubleOrNull(3) ?: 70F
|
||||
val minPitch = params.getDoubleOrNull(4) ?: -45F
|
||||
val maxYaw = params.getDoubleOrNull(5) ?: 45F
|
||||
val minYaw = params.getDoubleOrNull(6) ?: -45F
|
||||
ObjectValue(
|
||||
SingleBoneLookAnimation(
|
||||
bone = model.getPart(boneName),
|
||||
@@ -132,7 +133,8 @@ object ClientMoLangFunctions {
|
||||
yawMultiplier = yawMultiplier.toFloat(),
|
||||
maxPitch = maxPitch.toFloat(),
|
||||
minPitch = minPitch.toFloat(),
|
||||
maxYaw = maxYaw.toFloat()
|
||||
maxYaw = maxYaw.toFloat(),
|
||||
minYaw = minYaw.toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import net.minecraft.client.gui.components.Renderable
|
||||
|
||||
interface CobblemonRenderable : Renderable
|
||||
@@ -18,7 +18,7 @@ import net.minecraft.network.chat.Component
|
||||
class ExitButton(
|
||||
pX: Int, pY: Int,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Component.literal("Exit"), onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Component.literal("Exit"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 26F
|
||||
|
||||
@@ -31,7 +31,7 @@ abstract class ScrollingWidget<T : ObjectSelectionList.Entry<T>>(
|
||||
height, // Height
|
||||
top, // Top
|
||||
slotHeight // Slot Height
|
||||
) {
|
||||
), CobblemonRenderable {
|
||||
override fun renderListBackground(guiGraphics: GuiGraphics) {}
|
||||
override fun renderSelection(context: GuiGraphics, y: Int, entryWidth: Int, entryHeight: Int, borderColor: Int, fillColor: Int) {}
|
||||
override fun renderDecorations(context: GuiGraphics, mouseX: Int, mouseY: Int) {}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.battles.ShowdownActionResponse
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattleActor
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleActionSelection
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleBackButton
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleGeneralActionSelection
|
||||
@@ -29,7 +30,7 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
|
||||
class BattleGUI : Screen(battleLang("gui.title")) {
|
||||
class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
companion object {
|
||||
const val OPTION_VERTICAL_SPACING = 3
|
||||
const val OPTION_HORIZONTAL_SPACING = 3
|
||||
|
||||
+3
-2
@@ -9,18 +9,19 @@
|
||||
package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
|
||||
class BattleBackButton(val x: Float, val y: Float) {
|
||||
class BattleBackButton(val x: Float, val y: Float) : CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 58
|
||||
const val HEIGHT = 34
|
||||
const val SCALE = 0.5F
|
||||
}
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = cobblemonResource("textures/gui/battle/battle_back.png"),
|
||||
|
||||
+47
-7
@@ -11,12 +11,11 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.moves.TypeChart
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
@@ -24,12 +23,16 @@ import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.google.common.collect.Lists
|
||||
import net.fabricmc.loader.impl.util.StringUtil
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.util.Mth.floor
|
||||
|
||||
class BattleMoveSelection(
|
||||
@@ -91,16 +94,16 @@ class BattleMoveSelection(
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(move.id, targetPnx)
|
||||
open val selectable: Boolean get() = !move.disabled
|
||||
|
||||
val targetPnx: String? get() = targetList?.let { targets ->
|
||||
val target: Targetable? get() = targetList?.let { targets ->
|
||||
return@let when {
|
||||
targets.isEmpty() -> null
|
||||
targets.size == 1 -> targets[0].getPNX()
|
||||
targets.size == 1 -> targets[0]
|
||||
else -> null // TODO: multi-battles
|
||||
}
|
||||
}
|
||||
val targetPnx: String? get() = target?.getPNX()
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
val selectConditionOpacity = moveSelection.opacity * if (!selectable) 0.5F else 1F
|
||||
|
||||
blitk(
|
||||
@@ -173,6 +176,8 @@ class BattleMoveSelection(
|
||||
opacity = moveSelection.opacity,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawTooltip(context, mouseX, mouseY)
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT
|
||||
@@ -182,6 +187,41 @@ class BattleMoveSelection(
|
||||
moveSelection.playDownSound(Minecraft.getInstance().soundManager)
|
||||
moveSelection.battleGUI.selectAction(moveSelection.request, response)
|
||||
}
|
||||
|
||||
private fun drawTooltip(context: GuiGraphics, mouseX: Int, mouseY: Int) {
|
||||
if (!isHovered(mouseX.toDouble(), mouseY.toDouble())) return
|
||||
|
||||
val textTooltip: MutableList<MutableComponent> = mutableListOf()
|
||||
for (descriptionLine in StringUtil.wrapLines(moveTemplate.description.string, 80).split("\n")) {
|
||||
textTooltip.add(Component.literal(descriptionLine).gray())
|
||||
}
|
||||
|
||||
textTooltip.add(Component.literal(""))
|
||||
|
||||
if (moveTemplate.power > 0) textTooltip.add(lang("ui.power").append(String.format(": %d", moveTemplate.power.toInt())))
|
||||
if (moveTemplate.accuracy > 0) textTooltip.add(lang("ui.accuracy").append(String.format(": %d%%", moveTemplate.accuracy.toInt())))
|
||||
|
||||
if (moveTemplate.power > 0 && moveTemplate.target != MoveTarget.self && target is ActiveClientBattlePokemon) {
|
||||
val species = (target as ActiveClientBattlePokemon).battlePokemon?.species
|
||||
if (species != null) {
|
||||
val moveEffectiveness: Double = TypeChart.getMoveEffectiveness(elementalType, listOf(species.primaryType, species.secondaryType))
|
||||
|
||||
textTooltip.add(when {
|
||||
moveEffectiveness == 0.0 -> lang("battle.ui.effectiveness.immunity", moveEffectiveness).gray()
|
||||
moveEffectiveness < 1.0 -> lang("battle.ui.effectiveness.resistance", moveEffectiveness).gray()
|
||||
moveEffectiveness > 1.0 -> lang("battle.ui.effectiveness.super_effective", moveEffectiveness).yellow()
|
||||
else -> lang("battle.ui.effectiveness.normal", moveEffectiveness).white()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
context.renderTooltip(
|
||||
Minecraft.getInstance().font,
|
||||
Lists.transform(textTooltip, Component::getVisualOrderText),
|
||||
mouseX,
|
||||
mouseY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.battle.widgets
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattleMessageQueue
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -32,7 +33,7 @@ class BattleMessagePane(
|
||||
FRAME_HEIGHT, // height
|
||||
1, // top
|
||||
LINE_HEIGHT
|
||||
) {
|
||||
), CobblemonRenderable {
|
||||
var opacity = 1F
|
||||
private var scrolling = false
|
||||
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.battle.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -28,7 +29,7 @@ class BattleOptionTile(
|
||||
val resource: ResourceLocation,
|
||||
val text: MutableComponent,
|
||||
val onClick: () -> Unit
|
||||
) : Renderable, GuiEventListener, NarratableEntry {
|
||||
) : CobblemonRenderable, GuiEventListener, NarratableEntry {
|
||||
companion object {
|
||||
const val OPTION_WIDTH = 90
|
||||
const val OPTION_HEIGHT = 26
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueBox
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialoguePortraitWidget
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueNameWidget
|
||||
@@ -36,7 +37,7 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
|
||||
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()) {
|
||||
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable {
|
||||
val speakers = dialogueDTO.speakers?.mapNotNull { (key, value) ->
|
||||
val name = value.name
|
||||
when (val face = value.face) {
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.dialogue.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -22,7 +23,7 @@ class DialogueNameWidget(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val text: MutableComponent?
|
||||
) : Renderable, GuiEventListener {
|
||||
) : CobblemonRenderable, GuiEventListener {
|
||||
companion object {
|
||||
val nameResource = cobblemonResource("textures/gui/dialogue/dialogue_name.png")
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.dialogue.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -22,7 +23,7 @@ class DialoguePortraitWidget(
|
||||
val y: Int,
|
||||
val width: Int,
|
||||
val height: Int
|
||||
) : Renderable, GuiEventListener {
|
||||
) : CobblemonRenderable, GuiEventListener {
|
||||
companion object {
|
||||
val DIALOGUE_ARROW_HEIGHT = 11
|
||||
val DIALOGUE_ARROW_WIDTH = 6
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.dialogue.widgets
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.gui.drawCenteredText
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
@@ -36,7 +37,7 @@ class DialogueTextInputWidget(
|
||||
width,
|
||||
height,
|
||||
"gui_dialogue_text_input".text()
|
||||
) {
|
||||
), CobblemonRenderable {
|
||||
init {
|
||||
setMaxLength(maxLength)
|
||||
isFocused = true
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.dialogue.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -22,7 +23,7 @@ class DialogueTimerWidget(
|
||||
val y: Int,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
) : Renderable, GuiEventListener {
|
||||
) : CobblemonRenderable, GuiEventListener {
|
||||
companion object {
|
||||
val timerResource = cobblemonResource("textures/gui/dialogue/dialogue_timer_bar.png")
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattleChallenge
|
||||
import com.cobblemon.mod.common.client.battle.ClientTeamRequest
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
@@ -35,7 +36,7 @@ class BattleConfigureGUI(
|
||||
private val packet: PlayerInteractOptionsPacket,
|
||||
private val activeRequest: ClientBattleChallenge? = null,
|
||||
private val activeTeamRequest: ClientTeamRequest? = null
|
||||
) : Screen(lang("ui.challenge.challenge_title")) {
|
||||
) : Screen(lang("ui.challenge.challenge_title")), CobblemonRenderable {
|
||||
companion object {
|
||||
const val SIZE = 113
|
||||
private val backgroundResource = cobblemonResource("textures/gui/interact/request/battle_request.png")
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.interact.battleRequest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -25,7 +26,7 @@ class BattleRequestNavigationButton(
|
||||
private val forwardIcon: ResourceLocation = forwardButtonResource,
|
||||
private val backwardIcon: ResourceLocation = backwardsButtonResource,
|
||||
onPress: OnPress
|
||||
): Button(pX.toInt(), pY.toInt(), (WIDTH * SCALE).toInt(), (clickHeight * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX.toInt(), pY.toInt(), (WIDTH * SCALE).toInt(), (clickHeight * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
const val HEIGHT = 16
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.interact.battleRequest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -21,7 +22,7 @@ class BattleResponseButton(
|
||||
pX: Int, pY: Int,
|
||||
private val accept: Boolean,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 34F
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
@@ -24,7 +25,7 @@ class BattleRequestButton(
|
||||
pX: Int, pY: Int,
|
||||
var text: MutableComponent,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), text, onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), text, onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 69F
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.callback.MoveSelectDTO
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
@@ -35,7 +36,7 @@ class MoveSelectConfiguration(
|
||||
|
||||
class MoveSelectGUI(
|
||||
val config: MoveSelectConfiguration
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")) {
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 122
|
||||
const val HEIGHT = 133
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
@@ -35,7 +36,7 @@ class MoveSlotButton(
|
||||
val ppMax: Int,
|
||||
val enabled: Boolean = true,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }) {
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Move"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private val moveResource = cobblemonResource("textures/gui/summary/summary_move.png")
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.callback.PartySelectPokemonDTO
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
@@ -37,7 +38,7 @@ class PartySelectConfiguration(
|
||||
)
|
||||
class PartySelectGUI(
|
||||
val config: PartySelectConfiguration
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")) {
|
||||
) : Screen(Component.translatable("cobblemon.ui.interact.moveselect")), CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 163
|
||||
const val HEIGHT = 132
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
@@ -42,7 +43,7 @@ class PartySlotButton(
|
||||
val enabled: Boolean = true,
|
||||
val parent: PartySelectGUI,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Pokemon"), onPress, CreateNarration { "".text() }) {
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Pokemon"), onPress, CreateNarration { "".text() }), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/interact/party_select_slot.png")
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -29,7 +30,7 @@ class InteractWheelButton(
|
||||
private val colour: () -> Vector3f?,
|
||||
onPress: OnPress,
|
||||
private val canHover: (Double, Double) -> Boolean
|
||||
) : Button(x, y, BUTTON_SIZE, BUTTON_SIZE, Component.literal("Interact"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(x, y, BUTTON_SIZE, BUTTON_SIZE, Component.literal("Interact"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
const val BUTTON_SIZE = 69
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.preview.ArrowButton
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.common.collect.Multimap
|
||||
@@ -20,7 +21,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) {
|
||||
class InteractWheelGUI(private val options: Multimap<Orientation, InteractWheelOption>, title: Component) : Screen(title), CobblemonRenderable {
|
||||
companion object {
|
||||
const val SIZE = 140
|
||||
const val OPTION_SIZE = 71
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.npc
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
class NPCEditorButton(
|
||||
var buttonX: Float,
|
||||
var buttonY: Float,
|
||||
var label: MutableComponent,
|
||||
var cycleButtonState: Boolean? = null,
|
||||
var buttonWidth: Int = Minecraft.getInstance().font.width(label) + (BUTTON_PADDING * 2),
|
||||
val silent: Boolean = false,
|
||||
val alignRight: Boolean = false,
|
||||
val clickAction: OnPress
|
||||
): Button(if (alignRight) (buttonX - buttonWidth).toInt() else buttonX.toInt(), buttonY.toInt(), buttonWidth, HEIGHT, "".text(), clickAction, DEFAULT_NARRATION) {
|
||||
|
||||
companion object {
|
||||
val HEIGHT = 16
|
||||
val BORDER_WIDTH = 2
|
||||
val BUTTON_PADDING = 6
|
||||
val buttonResource = cobblemonResource("textures/gui/npc/button_base.png")
|
||||
val buttonBorderResource = cobblemonResource("textures/gui/npc/button_border.png")
|
||||
}
|
||||
|
||||
var isWidgetActive = false
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
val matrices = context.pose()
|
||||
|
||||
// Left border
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = buttonBorderResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = BORDER_WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
|
||||
textureWidth = 3,
|
||||
textureHeight = HEIGHT * 2,
|
||||
)
|
||||
// Base
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = buttonResource,
|
||||
x = x + BORDER_WIDTH,
|
||||
y = y,
|
||||
width = buttonWidth - (BORDER_WIDTH * 2),
|
||||
height = HEIGHT,
|
||||
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2,
|
||||
)
|
||||
// Right border
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = buttonBorderResource,
|
||||
x = x + (buttonWidth - BORDER_WIDTH),
|
||||
y = y,
|
||||
width = BORDER_WIDTH,
|
||||
height = HEIGHT,
|
||||
uOffset = 1,
|
||||
vOffset = if (isMouseOver(mouseX.toDouble(), mouseY.toDouble()) || isWidgetActive) HEIGHT else 0,
|
||||
textureWidth = 3,
|
||||
textureHeight = HEIGHT * 2,
|
||||
)
|
||||
|
||||
var buttonLabel = if (cycleButtonState != null) "options.${if (cycleButtonState as Boolean) "on" else "off"}.composed".asTranslated(label) else label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = buttonLabel,
|
||||
x = x + (buttonWidth / 2),
|
||||
y = y + 4,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (active && isMouseOver(mouseX, mouseY)) {
|
||||
super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
if (active && !this.silent) {
|
||||
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,26 +9,35 @@
|
||||
package com.cobblemon.mod.common.client.gui.npc
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.gui.drawText
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.ConfigVariableList
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget.Companion.HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.NPCRenderWidget.Companion.WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.SimpleNPCTextInputWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.dto.NPCConfigurationDTO
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import java.util.UUID
|
||||
|
||||
class NPCEditorScreen(
|
||||
val npcId: Int,
|
||||
val dto: NPCConfigurationDTO
|
||||
) : Screen("gui.npc_editor.title".asTranslated()) {
|
||||
) : Screen("gui.npc_editor.title".asTranslated()), CobblemonRenderable {
|
||||
companion object {
|
||||
const val BASE_WIDTH = 331
|
||||
const val BASE_HEIGHT = 161
|
||||
const val BASE_WIDTH = 360
|
||||
const val BASE_HEIGHT = 220
|
||||
|
||||
private val baseResource = cobblemonResource("textures/gui/npc/editor_base.png")
|
||||
val baseResource = cobblemonResource("textures/gui/npc/base.png")
|
||||
}
|
||||
|
||||
val middleX: Int
|
||||
@@ -43,42 +52,52 @@ class NPCEditorScreen(
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
addRenderableOnly(NPCRenderWidget(leftX + 6, topY + 32, dto.npcClass, dto.aspects))
|
||||
addRenderableOnly(NPCRenderWidget(leftX + 12, topY + 35, dto.npcClass, dto.aspects))
|
||||
addRenderableWidget(SimpleNPCTextInputWidget(
|
||||
texture = cobblemonResource("textures/gui/npc/basic_text_input.png"),
|
||||
getter = { dto.npcName.string },
|
||||
setter = { dto.npcName = it.text() },
|
||||
x = leftX + 79,
|
||||
y = topY + 14,
|
||||
width = 130,
|
||||
height = 22
|
||||
posX = leftX + 12,
|
||||
posY = topY + 9,
|
||||
width = 192,
|
||||
height = 22,
|
||||
maxLength = 32
|
||||
))
|
||||
|
||||
addRenderableWidget(SimpleNPCTextInputWidget(
|
||||
texture = cobblemonResource("textures/gui/npc/aspects_text_input.png"),
|
||||
getter = { dto.aspects.joinToString() },
|
||||
setter = {
|
||||
dto.aspects.clear()
|
||||
dto.aspects.addAll(it.split(",").map { it.trim() })
|
||||
},
|
||||
x = leftX + 79,
|
||||
y = topY + 36,
|
||||
width = 130,
|
||||
posX = leftX + 12,
|
||||
posY = topY + 155,
|
||||
width = 116,
|
||||
height = 40,
|
||||
maxLength = 75,
|
||||
maxLength = 72,
|
||||
wrap = true
|
||||
))
|
||||
|
||||
addRenderableWidget(
|
||||
Button
|
||||
.builder("Save".text()) {
|
||||
SaveNPCPacket(npcId, dto).sendToServer()
|
||||
this.minecraft!!.setScreen(null)
|
||||
}
|
||||
.pos(leftX + BASE_WIDTH - 42, topY + BASE_HEIGHT - 18)
|
||||
.size(40, 16)
|
||||
.build()
|
||||
NPCEditorButton(
|
||||
buttonX = leftX + 348F,
|
||||
buttonY = topY + 201F,
|
||||
label = lang("ui.generic.save"),
|
||||
alignRight = true
|
||||
) {
|
||||
SaveNPCPacket(npcId, dto).sendToServer()
|
||||
this.minecraft!!.setScreen(null)
|
||||
}
|
||||
)
|
||||
|
||||
addRenderableWidget(ConfigVariableList(leftX + 134, topY + 35, this))
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) {}
|
||||
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
|
||||
override fun isPauseScreen() = false
|
||||
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
@@ -88,6 +107,18 @@ class NPCEditorScreen(
|
||||
height = BASE_HEIGHT,
|
||||
width = BASE_WIDTH
|
||||
)
|
||||
|
||||
var extendedId = npcId.toString()
|
||||
while (extendedId.length < 12) extendedId = "0$extendedId"
|
||||
|
||||
drawScaledTextJustifiedRight(
|
||||
context = context,
|
||||
text = extendedId.text(),
|
||||
x = leftX + 348,
|
||||
y = topY + 9,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.npc.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable
|
||||
import com.cobblemon.mod.common.api.npc.configuration.NPCConfigVariable.NPCVariableType
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.npc.NPCEditorButton
|
||||
import com.cobblemon.mod.common.client.gui.npc.NPCEditorScreen
|
||||
import com.cobblemon.mod.common.client.gui.npc.widgets.ConfigVariableList.ConfigVariable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.client.gui.components.Tooltip
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
import net.minecraft.client.gui.narration.NarratableEntry
|
||||
|
||||
class ConfigVariableList(
|
||||
val listX: Int,
|
||||
var listY: Int,
|
||||
val parent: NPCEditorScreen
|
||||
) : ContainerObjectSelectionList<ConfigVariable>(
|
||||
Minecraft.getInstance(),
|
||||
WIDTH, // width
|
||||
HEIGHT, // height
|
||||
0, // top
|
||||
SLOT_HEIGHT + SLOT_SPACING
|
||||
), CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 211
|
||||
const val HEIGHT = 160
|
||||
const val SLOT_WIDTH = 196
|
||||
const val SLOT_HEIGHT = 33
|
||||
const val SLOT_SPACING = 0
|
||||
|
||||
val textInputBackground = cobblemonResource("textures/gui/npc/text_input_base.png")
|
||||
}
|
||||
|
||||
private var scrolling = false
|
||||
|
||||
override fun getRowWidth() = SLOT_WIDTH
|
||||
|
||||
init {
|
||||
listY += 4
|
||||
this.x = listX
|
||||
this.y = listY
|
||||
correctSize()
|
||||
setRenderHeader(false, 0)
|
||||
val npcClass = NPCClasses.getByIdentifier(parent.dto.npcClass)
|
||||
if (npcClass != null) {
|
||||
npcClass.config.forEach { variable ->
|
||||
val value = parent.dto.variables[variable.variableName] ?: variable.defaultValue
|
||||
addEntry(ConfigVariable(variable, value, this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition() = x + width - 3
|
||||
|
||||
public override fun addEntry(entry: ConfigVariable) = super.addEntry(entry)
|
||||
public override fun removeEntry(entry: ConfigVariable) = super.removeEntry(entry)
|
||||
|
||||
override fun renderListSeparators(guiGraphics: GuiGraphics) {}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
correctSize()
|
||||
|
||||
super.renderWidget(context, mouseX, mouseY, partialTicks)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
updateScrollingState(mouseX, mouseY)
|
||||
if (scrolling) {
|
||||
focused = getEntryAtPosition(mouseX, mouseY)
|
||||
isDragging = true
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (scrolling) {
|
||||
if (mouseY < this.listY) {
|
||||
scrollAmount = 0.0
|
||||
} else if (mouseY > bottom) {
|
||||
scrollAmount = maxScroll.toDouble()
|
||||
} else {
|
||||
scrollAmount += deltaY
|
||||
}
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
|
||||
scrolling = mouseX >= this.scrollbarPosition.toDouble()
|
||||
&& mouseX < (this.scrollbarPosition + 3).toDouble()
|
||||
&& mouseY >= listY
|
||||
&& mouseY < bottom
|
||||
}
|
||||
|
||||
override fun renderListBackground(context: GuiGraphics) {}
|
||||
|
||||
private fun correctSize() {
|
||||
setRectangle(WIDTH, HEIGHT, listX, (listY - 4))
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
|
||||
|
||||
class ConfigVariable(val variable: NPCConfigVariable, val value: String, private val parent: ConfigVariableList) : Entry<ConfigVariable>() {
|
||||
val client: Minecraft = Minecraft.getInstance()
|
||||
var _focused = false
|
||||
var children = mutableListOf<GuiEventListener>()
|
||||
|
||||
var textValue = value
|
||||
var booleanValue = value.let { it.toDoubleOrNull() == 1.0 || it.toBooleanStrictOrNull() == true }
|
||||
|
||||
val editBox = EditBox(
|
||||
client.font,
|
||||
parent.listX,
|
||||
parent.listY + 8,
|
||||
SLOT_WIDTH - 8,
|
||||
SLOT_HEIGHT - 8,
|
||||
variable.displayName,
|
||||
).also {
|
||||
it.isBordered = false
|
||||
it.tooltip = Tooltip.create(variable.description)
|
||||
it.height = SLOT_HEIGHT - 16
|
||||
it.setMaxLength(250)
|
||||
it.value = value
|
||||
it.setResponder {
|
||||
textValue = it
|
||||
parent.parent.dto.variables[variable.variableName] = it
|
||||
}
|
||||
if (variable.type == NPCVariableType.NUMBER) {
|
||||
it.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() || value == "." || value == "-" }
|
||||
}
|
||||
}
|
||||
val toggleButton = NPCEditorButton(
|
||||
parent.listX.toFloat(),
|
||||
parent.listY + 6F,
|
||||
variable.displayName.copy(),
|
||||
booleanValue,
|
||||
SLOT_WIDTH
|
||||
) {
|
||||
booleanValue = !booleanValue
|
||||
parent.parent.dto.variables[variable.variableName] = if (booleanValue) "1" else "0"
|
||||
(it as NPCEditorButton).cycleButtonState = booleanValue
|
||||
}.also {
|
||||
it.tooltip = Tooltip.create(variable.description)
|
||||
}
|
||||
|
||||
init {
|
||||
if (variable.type == NPCVariableType.BOOLEAN) {
|
||||
children.add(toggleButton) // children.add(cycleButton)
|
||||
} else {
|
||||
children.add(editBox)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isFocused() = _focused
|
||||
override fun setFocused(focused: Boolean) {
|
||||
this._focused = focused
|
||||
}
|
||||
|
||||
override fun children() = children
|
||||
override fun narratables(): List<NarratableEntry> {
|
||||
return children.filterIsInstance<NarratableEntry>()
|
||||
}
|
||||
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
index: Int,
|
||||
rowTop: Int,
|
||||
rowLeft: Int,
|
||||
rowWidth: Int,
|
||||
rowHeight: Int,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
isHovered: Boolean,
|
||||
partialTicks: Float
|
||||
) {
|
||||
val x = rowLeft - 4
|
||||
val y = rowTop
|
||||
if (variable.type == NPCVariableType.BOOLEAN) {
|
||||
toggleButton.x = x
|
||||
toggleButton.y = y + 9
|
||||
toggleButton.render(context, mouseX, mouseY, partialTicks)
|
||||
} else {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = variable.displayName.visualOrderText,
|
||||
x = x,
|
||||
y = y + 4,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x, y = y + 16, height = 12, width = 1)
|
||||
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x + 1, y = y + 15, height = 14, width = SLOT_WIDTH - 2)
|
||||
blitk(matrixStack = context.pose(), texture = textInputBackground, x = x + SLOT_WIDTH - 1, y = y + 16, height = 12, width = 1)
|
||||
|
||||
editBox.x = x + 4
|
||||
editBox.y = y + 18
|
||||
editBox.render(context, mouseX, mouseY, partialTicks)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-16
@@ -8,11 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.npc.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.gui.drawProfile
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Renderable
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
@@ -23,41 +22,40 @@ class NPCRenderWidget(
|
||||
val y: Int,
|
||||
var identifier: ResourceLocation,
|
||||
val aspects: MutableSet<String>
|
||||
) : Renderable, GuiEventListener {
|
||||
) : CobblemonRenderable, GuiEventListener {
|
||||
val state = FloatingState().also {
|
||||
it.currentAspects = aspects
|
||||
}
|
||||
|
||||
companion object {
|
||||
val profileBackgroundResource = cobblemonResource("textures/gui/npc/profile_background.png")
|
||||
const val WIDTH = 66
|
||||
const val HEIGHT = 66
|
||||
const val WIDTH = 116
|
||||
const val HEIGHT = 112
|
||||
}
|
||||
|
||||
override fun isFocused() = false
|
||||
override fun setFocused(focused: Boolean) {}
|
||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = profileBackgroundResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT
|
||||
)
|
||||
|
||||
context.enableScissor(
|
||||
x,
|
||||
y,
|
||||
x + WIDTH,
|
||||
y + HEIGHT
|
||||
)
|
||||
context.pose().pushPose()
|
||||
context.pose().translate(x + WIDTH / 2F, y + HEIGHT / 2F, 0F)
|
||||
// Decrease on Z-axis to prevent model from rendering above other components like tooltips
|
||||
context.pose().translate(x + (WIDTH / 2F), y + HEIGHT + (HEIGHT / 4F), -500F)
|
||||
|
||||
drawProfile(
|
||||
repository = NPCModelRepository,
|
||||
resourceIdentifier = identifier,
|
||||
matrixStack = context.pose(),
|
||||
partialTicks = delta,
|
||||
scale = 30F,
|
||||
scale = 120F,
|
||||
state = state
|
||||
)
|
||||
|
||||
context.pose().popPose()
|
||||
context.disableScissor()
|
||||
}
|
||||
}
|
||||
+99
-46
@@ -9,89 +9,142 @@
|
||||
package com.cobblemon.mod.common.client.gui.npc.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.gui.drawCenteredText
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.contents.PlainTextContents.LiteralContents
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.network.chat.Style
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.math.floor
|
||||
|
||||
class SimpleNPCTextInputWidget(
|
||||
getter: () -> String,
|
||||
val texture: ResourceLocation,
|
||||
val texture: ResourceLocation? = null,
|
||||
private val setter: (String) -> Unit,
|
||||
x: Int,
|
||||
y: Int,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
width: Int,
|
||||
height: Int,
|
||||
maxLength: Int = 100,
|
||||
val shadow: Boolean = true,
|
||||
val wrap: Boolean = false
|
||||
) : EditBox(
|
||||
Minecraft.getInstance().font,
|
||||
x,
|
||||
y,
|
||||
posX + TEXT_PADDING,
|
||||
posY,
|
||||
width,
|
||||
height,
|
||||
"input".text()
|
||||
) {
|
||||
), CobblemonRenderable {
|
||||
companion object {
|
||||
const val TEXT_PADDING = 4
|
||||
}
|
||||
|
||||
var focusedTime: Long = 0
|
||||
|
||||
init {
|
||||
setMaxLength(maxLength)
|
||||
isFocused = true
|
||||
focusedTime = Util.getMillis()
|
||||
value = getter()
|
||||
this.setResponder { setter(it) }
|
||||
setResponder { setter(it) }
|
||||
setFocused(false)
|
||||
isBordered = false
|
||||
}
|
||||
|
||||
override fun setFocused(focused: Boolean) {
|
||||
if (focused) focusedTime = Util.getMillis()
|
||||
super.setFocused(focused)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
return if (mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)) {
|
||||
isFocused = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
val clickedState = super.mouseClicked(mouseX, mouseY, button)
|
||||
|
||||
if (wrap) {
|
||||
val splitLines = Minecraft.getInstance().font.splitter.splitLines(value, width - 8, Style.EMPTY)
|
||||
val clickedLineIndex = floor((mouseY - y)/11).toInt()
|
||||
|
||||
if (clickedLineIndex >= 0 && splitLines.size > clickedLineIndex) {
|
||||
val lineAtIndex = splitLines.get(clickedLineIndex)
|
||||
var lineSubstring = ""
|
||||
for (char in lineAtIndex.string) {
|
||||
lineSubstring += char
|
||||
val startToLineWidth = Minecraft.getInstance().font.width(lineSubstring.text())
|
||||
if ((mouseX - x) <= startToLineWidth) break
|
||||
}
|
||||
var unwrappedLine = ""
|
||||
splitLines.forEachIndexed { index, line ->
|
||||
if (index == clickedLineIndex) {
|
||||
unwrappedLine += lineSubstring
|
||||
} else if (index < clickedLineIndex) {
|
||||
unwrappedLine += line.string
|
||||
}
|
||||
}
|
||||
|
||||
this.moveCursorTo(maxOf(0, unwrappedLine.length - 1), Screen.hasShiftDown())
|
||||
}
|
||||
}
|
||||
|
||||
return clickedState
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (cursorPosition != value.length) {
|
||||
moveCursorToEnd(false)
|
||||
if (texture != null) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
texture = texture
|
||||
)
|
||||
}
|
||||
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
texture = texture
|
||||
)
|
||||
|
||||
context.pose().pushPose()
|
||||
val scale = 0.8F
|
||||
context.pose().scale(scale, scale, 1F)
|
||||
if (wrap) {
|
||||
val wrappedLines = Minecraft.getInstance().font.split(MutableComponent.create(LiteralContents(value)), ((width - 8) / scale).toInt())
|
||||
val showCursor = isFocused && ((Util.getMillis() - this.focusedTime) / 300L % 2L == 0L)
|
||||
val input = (if (isFocused) "${value}${if ((cursorPosition == value.length) && showCursor) "_" else ""}" else value)
|
||||
|
||||
var cursorLine = ""
|
||||
var cursorLineIndex = -1
|
||||
var cursorLinePos = 0
|
||||
var accumlatedLength = 0
|
||||
val wrappedLines = Minecraft.getInstance().font.splitter.splitLines(input, width - (TEXT_PADDING * 2), Style.EMPTY)
|
||||
|
||||
for ((index, line) in wrappedLines.withIndex()) {
|
||||
context.drawString(
|
||||
Minecraft.getInstance().font,
|
||||
line,
|
||||
((x + 4) / scale).toInt(),
|
||||
((y + index * 10 + 5) / scale).toInt(),
|
||||
0xFFFFFF,
|
||||
false
|
||||
accumlatedLength += line.string.length
|
||||
if (cursorLineIndex < 0 && accumlatedLength > cursorPosition) {
|
||||
cursorLineIndex = index
|
||||
cursorLine = line.string
|
||||
cursorLinePos = if (cursorLineIndex > 0) cursorPosition - (accumlatedLength - line.string.length) else cursorPosition
|
||||
}
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = line.string.text(),
|
||||
x = x,
|
||||
y = y + index * 10 + 1,
|
||||
colour = 0xDDDDDD,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
if (showCursor && !value.isEmpty() && cursorPosition != value.length) {
|
||||
val startToCursorWidth = Minecraft.getInstance().font.width((cursorLine.substring(0, cursorLinePos).text()))
|
||||
context.fill(
|
||||
RenderType.guiTextHighlight(),
|
||||
x + startToCursorWidth - 1,
|
||||
y + cursorLineIndex * 10 + 1,
|
||||
x + startToCursorWidth,
|
||||
y + (cursorLineIndex * 10 + 1) + 9,
|
||||
-3092272
|
||||
)
|
||||
}
|
||||
} else {
|
||||
drawCenteredText(
|
||||
context = context,
|
||||
text = value.text(),
|
||||
x = (x + width / 2F) / scale,
|
||||
y = (y + height / 2 - 4) / scale,
|
||||
shadow = true,
|
||||
colour = 0xFFFFFF
|
||||
)
|
||||
super.renderWidget(context, mouseX, mouseY, delta)
|
||||
}
|
||||
context.pose().popPose()
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pc.StorageSlot
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartySlotWidget
|
||||
@@ -40,7 +41,7 @@ class PasturePokemonScrollList(
|
||||
HEIGHT, // height
|
||||
0, // top
|
||||
SLOT_HEIGHT + SLOT_SPACING
|
||||
) {
|
||||
), CobblemonRenderable {
|
||||
companion object {
|
||||
const val WIDTH = 70
|
||||
const val HEIGHT = 120
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.pasture
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -18,7 +19,7 @@ import net.minecraft.network.chat.Component
|
||||
class PastureSlotIconButton(
|
||||
var xPos: Int, var yPos: Int,
|
||||
onPress: OnPress
|
||||
) : Button(xPos, yPos, (SIZE * SCALE).toInt(), (SIZE * SCALE).toInt(), Component.literal("Pasture Move"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(xPos, yPos, (SIZE * SCALE).toInt(), (SIZE * SCALE).toInt(), Component.literal("Pasture Move"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
const val SIZE = 14
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pasture
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -22,7 +23,7 @@ import net.minecraft.network.chat.Component
|
||||
class RecallButton(
|
||||
x: Int, y: Int,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Retrieve"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Retrieve"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 70
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.pc
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
@@ -21,7 +22,7 @@ class NavigationButton(
|
||||
pX: Int, pY: Int,
|
||||
private val forward: Boolean,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION) {
|
||||
): Button(pX, pY, (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.literal("Navigation"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 8F
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
@@ -40,7 +41,7 @@ class PCGUI(
|
||||
val pc: ClientPC,
|
||||
val party: ClientParty,
|
||||
val configuration: PCGUIConfiguration
|
||||
) : Screen(Component.translatable("cobblemon.ui.pc.title")) {
|
||||
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 349
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pc
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -23,7 +24,7 @@ class ReleaseButton(
|
||||
x: Int, y: Int,
|
||||
private val parent: StorageWidget,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Release"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Release"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 58
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pc
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -24,7 +25,7 @@ class ReleaseConfirmButton(
|
||||
private val parent: StorageWidget,
|
||||
private val subKey: String,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("ReleaseConfirm"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(x, y, WIDTH, HEIGHT, Component.literal("ReleaseConfirm"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 30
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.pc
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
@@ -31,7 +32,7 @@ open class StorageSlot(
|
||||
x: Int, y: Int,
|
||||
private val parent: StorageWidget,
|
||||
onPress: OnPress
|
||||
) : Button(x, y, SIZE, SIZE, Component.literal("StorageSlot"), onPress, DEFAULT_NARRATION) {
|
||||
) : Button(x, y, SIZE, SIZE, Component.literal("StorageSlot"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
val state = FloatingState()
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HEADER_BAR_HEIGHT
|
||||
@@ -75,7 +76,7 @@ class PokedexGUI private constructor(
|
||||
val type: PokedexType,
|
||||
val initSpecies: ResourceLocation?,
|
||||
val blockPos: BlockPos?
|
||||
): Screen(Component.translatable("cobblemon.ui.pokedex.title")) {
|
||||
): Screen(Component.translatable("cobblemon.ui.pokedex.title")), CobblemonRenderable {
|
||||
companion object {
|
||||
private val screenBackground = cobblemonResource("textures/gui/pokedex/pokedex_screen.png")
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pokedex
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput
|
||||
@@ -27,7 +28,7 @@ class ScaledButton(
|
||||
val scale: Float = 0.5F,
|
||||
val silent: Boolean = false,
|
||||
val clickAction: Button.OnPress
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION) {
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
var isWidgetActive = false
|
||||
|
||||
|
||||
+2
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.gui.ParentWidget
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
@@ -688,6 +689,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
fun isWithinPortraitSpace(mouseX: Double, mouseY: Double): Boolean =
|
||||
mouseX.toInt() in pX + 15..(pX + 15 + PORTRAIT_POKE_BALL_WIDTH)
|
||||
&& mouseY.toInt() in pY + 25..(pY + 25 + PORTRAIT_POKE_BALL_HEIGHT)
|
||||
&& !children.any { it.isMouseOver(mouseX, mouseY) && it is ScaledButton }
|
||||
|
||||
private fun isSelectedPokemonOwned(): Boolean {
|
||||
return currentEntry?.let { CobblemonClient.clientPokedexData.getKnowledgeForSpecies(it.speciesId) } == PokedexEntryProgress.CAUGHT
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user