Compare commits

...
65 Commits
Author SHA1 Message Date
Guilherme Pereira 1986cb480b add moves tooltips 2024-12-15 22:47:47 -03:00
Polymeta c8b0f36049 Merge branch 'bugfixes-apion' into 'main'
Fixes #1182

Closes #1182

See merge request cable-mc/cobblemon!1373
2024-12-15 18:25:02 +00:00
ApionandPolymeta be2a0be806 Fixes #1182 2024-12-15 18:25:02 +00:00
Paul ba4d42ccf6 Try tweaking spawn direction handling 2024-12-15 23:04:08 +10:00
Paul 5545d3498f Include 2 more variables in clamping of rotations 2024-12-15 20:53:50 +10:00
Hiroku 641bd7ebaa Merge branch 'advancing' into 'main'
1.6 Advancement Reworks

See merge request cable-mc/cobblemon!1374
2024-12-15 09:16:28 +00:00
dhi_holoandHiroku 234c4fa0d4 1.6 Advancement Reworks 2024-12-15 09:16:28 +00:00
Hiroku 3dfda71ed5 Merge branch 'AbilityPatchCapsuleRefactor' into 'main'
AbilityPatchCapsuleRefactor

See merge request cable-mc/cobblemon!1377
2024-12-15 09:08:05 +00:00
Hiroku 65ee6de93f Merge branch 'chest-boat-interact' into 'main'
moved changes to chest boat (i'm dumb)

Closes #1214

See merge request cable-mc/cobblemon!1379
2024-12-15 09:07:48 +00:00
Paul be88b727bc Added useFixedRandom to pool party provider, added randomizePartyOrder to NPC classes and presets for randomly choosing lead battle pokemon 2024-12-15 18:04:04 +10:00
luke a69fc462ee moved changes to chest boat (i'm dumb) 2024-12-15 00:38:08 -07:00
Hiroku 7d5c30d27c Merge branch 'shift-click-interact' into 'main'
Changed distance to use player attribute

Closes #1232

See merge request cable-mc/cobblemon!1378
2024-12-15 07:32:39 +00:00
Hiroku f7c9c9d4f4 Update AbilityTypeChanger.kt 2024-12-15 07:30:12 +00:00
luke 41bc14168a Changed distance to use player attribute 2024-12-15 00:24:38 -07:00
Paul 9540c38a72 Updated readme example for NPC 2024-12-15 16:58:31 +10:00
JazzMcNade 7a96ed7cd1 Refactored queryPossible() as to not to require explicit Priority values 2024-12-15 00:45:51 -06:00
Hiroku 677adc8d2f Merge branch 'starter-selection-cry' into 'main'
Play cry when Pokémon is clicked on the starter selection screen

See merge request cable-mc/cobblemon!1362
2024-12-15 06:25:29 +00:00
lionandHiroku a99b1dfc6c Play cry when Pokémon is clicked on the starter selection screen 2024-12-15 06:25:29 +00:00
Hiroku 4a718ad0f6 Merge branch 'dex-update-event' into 'main'
Dex Data Updated Event

See merge request cable-mc/cobblemon!1353
2024-12-15 05:27:08 +00:00
Paul ddd417d458 Added player to PokedexDataChangedEvent's flow context 2024-12-15 15:19:43 +10:00
Paul 10a12a897e Merge remote-tracking branch 'origin/main' into dex-update-event 2024-12-15 15:15:48 +10:00
Paul 9fce99ed73 Improved dex event, added dex update flow, expanded dex molang and added q.player.pokedex 2024-12-15 15:15:13 +10:00
Hiroku c35f60dd5d Merge branch 'AbilitiesFixesRedux' into 'main'
AbilitiesFixesRedux

Closes #1245

See merge request cable-mc/cobblemon!1376
2024-12-15 03:11:07 +00:00
JazzMcNade 54e5a5e819 Fixed single abilities mons always evolving into the hidden ability. Fixed ability capsule / patch not being able to revert abilities. 2024-12-14 20:35:23 -06:00
Test 79ae35ea9e Merge branch 'n-p-c' into 'main'
fix npc scaling + eye height, update edit interface

See merge request cable-mc/cobblemon!1375
2024-12-15 02:17:45 +00:00
Test 58f85e6d0d fix npc scaling + eye height, update edit interface 2024-12-15 02:17:45 +00:00
Polymeta 71bfdf41f4 Merge branch 'the-else-strikes-back' into 'main'
Fix Feebas spawning

Closes #1187

See merge request cable-mc/cobblemon!1365
2024-12-15 01:13:26 +00:00
JordanandPolymeta 9976965673 Fix Feebas spawning 2024-12-15 01:13:26 +00:00
Hiroku 098df1a5a6 Merge branch 'dex-travelling-dimensions' into 'main'
Fix Pokédex screen getting stuck when changing dimensions or opening the battle GUI

See merge request cable-mc/cobblemon!1370
2024-12-15 01:09:47 +00:00
Guilherme PereiraandHiroku f49f34efa3 Fix Pokédex screen getting stuck when changing dimensions or opening the battle GUI 2024-12-15 01:09:47 +00:00
Hiroku eac19929c4 Merge branch 'fix-dex-animations-not-playing' into 'main'
Fix animations not playing on Pokédex

See merge request cable-mc/cobblemon!1355
2024-12-15 00:43:09 +00:00
Guilherme PereiraandHiroku 59cced5b2c Fix animations not playing on Pokédex 2024-12-15 00:43:08 +00:00
Apion f07707221f Merge branch 'implement_pokemon' into 'main'
Pokémon fixes and tweaks

Closes #1230 and #1137

See merge request cable-mc/cobblemon!1371
2024-12-14 23:57:34 +00:00
PolymetaandApion 89b89592ab Pokémon fixes and tweaks 2024-12-14 23:57:33 +00:00
Hiroku cdeb977769 Update DexInformationChangedEvent.kt 2024-12-14 23:55:43 +00:00
Paul a3987b433c Merge remote-tracking branch 'origin/main' 2024-12-14 22:19:51 +10:00
Paul 99de91bd7e Added optional level property to pool party providers for using molang to force a level value, closes #1244 2024-12-14 22:19:39 +10:00
Paul 4b292b1241 Less zealous on the warning logging 2024-12-14 22:16:07 +10:00
Hiroku 1e6e4d8c5f Merge branch 'moe-eats-bugs' into 'main'
Added ignore checks to prevent passive healing/revival when config values are 0

Closes #875

See merge request cable-mc/cobblemon!1372
2024-12-14 11:41:35 +00:00
MoeBoy76 9a61c3471b Added ignore checks to prevent passive healing/revival when config values are 0 2024-12-14 22:12:08 +11:00
Paul 77f7439853 Fixed brewing on neoforged 2024-12-14 14:51:01 +10:00
Paul e9718b7e8b Merge remote-tracking branch 'origin/main' 2024-12-14 14:29:28 +10:00
Hiroku 4cadd4e20f Merge branch 'fix/watchdog' into 'main'
Watchdog: Possible fix for watchdog crashing

See merge request cable-mc/cobblemon!1368
2024-12-14 04:26:24 +00:00
Hiroku 17a3d86324 Update PokemonEntity.kt 2024-12-14 04:25:19 +00:00
Hiroku 48653073f5 if ( 2024-12-14 04:21:49 +00:00
Nick 82c7c03e28 Initial tests for watchdog fix 2024-12-13 20:18:13 -08:00
Nick 2d7f3257d1 Merge branch 'hiro-fix' into 'main'
I fix bugs too

Closes #1212, #1222, and #1228

See merge request cable-mc/cobblemon!1367
2024-12-14 04:16:35 +00:00
HirokuandNick bc7b4f94c2 I fix bugs too 2024-12-14 04:16:35 +00:00
Paul 7a9e9d7ced Fixed battles being startable while pokemon are busy, closes #1212 2024-12-14 13:52:19 +10:00
Paul 0f85ece16f Fixed cry sound effects playing twice for specific cases, closes #1222 2024-12-14 13:36:53 +10:00
Paul c4656e1a4e Fixed cubone alola bias not being visible, closes #1228 2024-12-14 12:57:17 +10:00
Paul 0c48a318c5 Added /runmolangscript, updated molang version 2024-12-14 12:22:25 +10:00
Paul dab89a9f8c Merge remote-tracking branch 'origin/main' 2024-12-14 11:05:55 +10:00
Paul 53fd2dba2d Fixed flows double-registering then triple-registering then quadruple-registering then- 2024-12-14 11:05:48 +10:00
Polymeta a5b15c6127 Merge branch 'more-items-tooltips' into 'main'
Add tooltips to many items

See merge request cable-mc/cobblemon!1346
2024-12-14 00:48:11 +00:00
Guilherme PereiraandPolymeta 174fb59bbb Add tooltips to many items 2024-12-14 00:48:11 +00:00
Paul 2f8bd44938 Added q.set_query for assigning new query function values, closes #1240 2024-12-14 10:36:53 +10:00
Paul 9d58c7126e Fixed set_npc_variable not initializing NPC data properly 2024-12-14 10:12:50 +10:00
Hiroku 2afd722125 Incl default return for get_npc_variable when no data exists for the NPC yet 2024-12-14 00:03:25 +00:00
Polymeta c9cfe489d6 Merge branch 'EV-Berry-Lang-fixes' into 'main'
Fixed lang files for EV lowering berries to actually be correct

See merge request cable-mc/cobblemon!1364
2024-12-13 11:23:29 +00:00
Plastered_Crab 4ee029fa54 Fixed lang files for EV lowering berries to actually be correct 2024-12-12 09:41:17 -08:00
amo d1a8171079 resolve threads 2024-12-09 23:57:38 +00:00
amo d4b673ea38 Merge branch 'main' into dex-update-event 2024-12-09 23:54:08 +00:00
amo de21b7c402 forgot me loighter 2024-12-07 04:44:27 +00:00
amo 10fe3c4362 🍋‍🟩 Chore: Add Dex data update event 2024-12-07 04:40:50 +00:00
228 changed files with 4722 additions and 1970 deletions
+10
View File
@@ -397,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.
@@ -427,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`.
@@ -458,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.
@@ -486,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:
@@ -1720,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.
@@ -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
@@ -48,6 +48,8 @@ object CobblemonFlows : DataRegistry {
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 ->
@@ -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
@@ -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))
@@ -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"))
}
}
}
@@ -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")
}
}
@@ -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
@@ -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
)
}
@@ -14,5 +14,4 @@ import java.util.UUID
data class PokemonGainedEvent(
val playerId: UUID,
val pokemon: Pokemon
) {
}
)
@@ -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,7 +23,7 @@ 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) {
val children: MutableList<GuiEventListener> = mutableListOf()
@@ -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].
@@ -29,6 +29,13 @@ 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
@@ -50,6 +57,7 @@ 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
@@ -102,6 +110,12 @@ object MoLangFunctions {
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)
@@ -320,6 +334,8 @@ object MoLangFunctions {
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 ->
@@ -328,16 +344,14 @@ object MoLangFunctions {
val value = params.get<MoValue>(2)
val saveAfterwards = params.getBooleanOrNull(3) != false
val data = Cobblemon.molangData.load(player.uuid)
if (data.map.containsKey(npcId)) {
(data.map[npcId] as VariableStruct).map[variable] = value
if (saveAfterwards) {
Cobblemon.molangData.save(player.uuid)
}
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
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
}
@@ -594,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)) }
@@ -804,6 +819,78 @@ object MoLangFunctions {
}
)
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())
@@ -935,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()
@@ -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 }
@@ -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": [
@@ -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
@@ -306,7 +312,8 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
"type": "pool",
"minPokemon": "3",
"maxPokemon": "6",
"isStatic": true,
"isStatic": false,
"useFixedRandom": true,
"pool": [
{
"pokemon": "weedle",
@@ -343,7 +350,9 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
"pokemon": "butterfree",
"weight": "5",
"npcLevels": "10-15",
"selectableTimes": "1"
"selectableTimes": "1",
"level": "q.player.party.highest_level"
}
]
}
@@ -395,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).
@@ -26,6 +26,7 @@ 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
/**
@@ -50,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>()
@@ -63,6 +65,7 @@ class PoolPartyProvider : NPCPartyProvider {
return@map DynamicPokemon(
PokemonProperties.parse(it.asString),
"0".asExpression(),
null,
1..100,
"1".asExpression(),
"1".asExpression()
@@ -71,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()
@@ -99,6 +105,7 @@ class PoolPartyProvider : NPCPartyProvider {
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) {
@@ -107,14 +114,14 @@ class PoolPartyProvider : NPCPartyProvider {
}
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 && runtime.resolveInt(it.selectableTimes) > 0 }.toMutableList()
val useCounts = mutableMapOf<DynamicPokemon, Int>()
while (desiredPokemonCount > 0 && workingPool.any { it.hasWeight(runtime) }) {
val selected = workingPool.filter { it.getWeight(runtime) == -1F }.randomOrNull()
?: workingPool.weightedSelection { it.getWeight(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)
@@ -124,9 +131,10 @@ 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)
}
}
@@ -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 {
@@ -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.*/
@@ -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)
}
@@ -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 {
@@ -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
}
@@ -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
@@ -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)
}
@@ -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() }
}
}
}
@@ -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();")
@@ -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
@@ -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"),
@@ -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) {
@@ -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
@@ -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
@@ -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) {
@@ -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")
}
@@ -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
@@ -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
@@ -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")
}
@@ -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")
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
@@ -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
@@ -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")
@@ -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
@@ -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))
}
}
}
@@ -8,25 +8,36 @@
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
val baseResource = cobblemonResource("textures/gui/npc/base.png")
}
val middleX: Int
@@ -41,53 +52,73 @@ class NPCEditorScreen(
override fun init() {
super.init()
addRenderableOnly(NPCRenderWidget(leftX + 6, topY + 22, 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 + 6,
y = topY + 80,
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 + 6,
y = topY + 100,
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 - 8)
.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 + 200, topY + 30, this))
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(),
// texture = baseResource,
// x = leftX,
// y = topY,
// height = BASE_HEIGHT,
// width = BASE_WIDTH
// )
blitk(
matrixStack = context.pose(),
texture = baseResource,
x = leftX,
y = topY,
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)
}
}
@@ -8,16 +8,19 @@
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.CycleButton
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.client.gui.components.events.GuiEventListener
@@ -25,7 +28,7 @@ import net.minecraft.client.gui.narration.NarratableEntry
class ConfigVariableList(
val listX: Int,
val listY: Int,
var listY: Int,
val parent: NPCEditorScreen
) : ContainerObjectSelectionList<ConfigVariable>(
Minecraft.getInstance(),
@@ -33,13 +36,15 @@ class ConfigVariableList(
HEIGHT, // height
0, // top
SLOT_HEIGHT + SLOT_SPACING
) {
), CobblemonRenderable {
companion object {
const val WIDTH = 200
const val HEIGHT = 120
const val SLOT_WIDTH = 160
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
@@ -47,15 +52,16 @@ class ConfigVariableList(
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 { t ->
val value = parent.dto.variables[t.variableName] ?: t.defaultValue
addEntry(ConfigVariable(t, value, this))
npcClass.config.forEach { variable ->
val value = parent.dto.variables[variable.variableName] ?: variable.defaultValue
addEntry(ConfigVariable(variable, value, this))
}
}
}
@@ -65,20 +71,12 @@ class ConfigVariableList(
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)
// Scroll Overlay
// blitk(
// matrixStack = context.pose(),
// texture = scrollOverlayResource,
// x = listX,
// y = listY - 13,
// height = 131,
// width = WIDTH
// )
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@@ -114,7 +112,6 @@ class ConfigVariableList(
private fun correctSize() {
setRectangle(WIDTH, HEIGHT, listX, (listY - 4))
// setX(listX)
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
@@ -130,11 +127,12 @@ class ConfigVariableList(
val editBox = EditBox(
client.font,
parent.listX,
parent.listY + 4,
if (variable.type == NPCVariableType.NUMBER) SLOT_WIDTH - 100 else SLOT_WIDTH,
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)
@@ -147,18 +145,23 @@ class ConfigVariableList(
it.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() || value == "." || value == "-" }
}
}
val cycleButton = CycleButton.onOffBuilder(booleanValue).create(variable.displayName) { _, value ->
booleanValue = value
parent.parent.dto.variables[variable.variableName] = if (value) "1" else "0"
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.height = SLOT_HEIGHT - 14
it.width = SLOT_WIDTH - 8
it.tooltip = Tooltip.create(variable.description)
}
init {
if (variable.type == NPCVariableType.BOOLEAN) {
children.add(cycleButton)
children.add(toggleButton) // children.add(cycleButton)
} else {
children.add(editBox)
}
@@ -188,42 +191,27 @@ class ConfigVariableList(
) {
val x = rowLeft - 4
val y = rowTop
val matrixStack = context.pose()
// blitk(
// matrixStack = matrixStack,
// texture = slotResource,
// x = x,
// y = y,
// height = SLOT_HEIGHT,
// width = rowWidth,
// vOffset = if (isHovered) SLOT_HEIGHT else 0,
// textureHeight = SLOT_HEIGHT * 2
// )
if (variable.type == NPCVariableType.BOOLEAN) {
cycleButton.x = x
cycleButton.y = y + 6
cycleButton.render(context, mouseX, mouseY, partialTicks)
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
y = y + 4,
shadow = true
)
editBox.x = x
editBox.y = y + 14
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)
}
}
// override fun mouseClicked(mouseX: Double, mouseY: Double, delta: Int): Boolean {
// if (moveButton.isHovered(mouseX, mouseY) && canUnpasture()) {
// moveButton.onPress()
// return true
// }
// return false
// }
}
}
@@ -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()
}
}
@@ -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()
}
}
@@ -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
@@ -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
@@ -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
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.font
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.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
@@ -33,7 +34,7 @@ class SearchWidget(
height: Int,
text: Component = "Search".text(),
val update: () -> (Unit)
): EditBox(Minecraft.getInstance().font, posX.toInt(), posY.toInt(), width, height, text) {
): EditBox(Minecraft.getInstance().font, posX.toInt(), posY.toInt(), width, height, text), CobblemonRenderable {
companion object {
private val backgroundOverlay = cobblemonResource("textures/gui/pokedex/pokedex_screen_search_overlay.png")
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pokenav
import com.cobblemon.mod.common.Cobblemon
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.summary.Summary
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.PokeNavigatorBinding
@@ -28,7 +29,7 @@ import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
class PokeNav : Screen(Component.translatable("cobblemon.ui.pokenav.title")) {
class PokeNav : Screen(Component.translatable("cobblemon.ui.pokenav.title")), CobblemonRenderable {
companion object {
// Limiting
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.gui.ColourLibrary
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.asTranslated
import net.minecraft.client.gui.GuiGraphics
@@ -31,7 +32,7 @@ open class PokeNavImageButton(
private val text: MutableComponent,
private val canClick: () -> Boolean = { true }
// TODO: Make lang key per button
): Button(pX, pY, pWidth, pHeight, "cobblemon.ui.pokenav.narrator.backbutton".asTranslated(), onPress, DEFAULT_NARRATION) {
): Button(pX, pY, pWidth, pHeight, "cobblemon.ui.pokenav.narrator.backbutton".asTranslated(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
// Render Button Image
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
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.startselection.widgets.CategoryList
import com.cobblemon.mod.common.client.gui.startselection.widgets.ExitButton
import com.cobblemon.mod.common.client.gui.startselection.widgets.preview.ArrowButton
@@ -47,7 +48,7 @@ import net.minecraft.network.chat.Component
* @author Qu
* @since 2022-06-18
*/
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()), CobblemonRenderable {
companion object {
// Size of UI at scale 1
@@ -132,7 +133,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
pX = x + 85, pY = y + 50,
pWidth = 102, pHeight = 100,
pokemon = this,
baseScale = 2.0f
baseScale = 2.0f,
playCryOnClick = true,
)
typeWidget = typeWidget(this, x, y)
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.startselection.widgets
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.client.gui.startselection.StarterSelectionScreen
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
@@ -37,7 +38,7 @@ class CategoryList(
paneHeight,
topOffset,
entryHeight
) {
), CobblemonRenderable {
companion object {
private const val CATEGORY_BUTTON_WIDTH = 51.5f
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.startselection.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
@@ -20,7 +21,7 @@ class ExitButton(
pWidth: Int, pHeight: Int,
pXTexStart: Int, pYTexStart: Int, pYDiffText: Int,
onPress: OnPress
): Button(pX, pY, EXIT_BUTTON_WIDTH.toInt(), EXIT_BUTTON_HEIGHT.toInt(), "cobblemon.ui.starter.narrator.backbutton".asTranslated(), onPress, DEFAULT_NARRATION) {
): Button(pX, pY, EXIT_BUTTON_WIDTH.toInt(), EXIT_BUTTON_HEIGHT.toInt(), "cobblemon.ui.starter.narrator.backbutton".asTranslated(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private const val EXIT_BUTTON_WIDTH = 15.95F
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.startselection.widgets.preview
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
@@ -22,7 +23,7 @@ class ArrowButton(
right: Boolean,
private val texture: ResourceLocation = if (right) RIGHT_ARROW_BUTTON_RESOURCE else LEFT_ARROW_BUTTON_RESOURCE,
onPress: OnPress
) : Button(pX, pY, pWidth, pHeight, Component.empty(), onPress, DEFAULT_NARRATION) {
) : Button(pX, pY, pWidth, pHeight, Component.empty(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private val RIGHT_ARROW_BUTTON_RESOURCE = cobblemonResource("textures/gui/starterselection/starterselection_arrow_right.png")
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.startselection.widgets.preview
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.ColourLibrary
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 com.cobblemon.mod.common.util.lang
@@ -25,7 +26,7 @@ class SelectionButton(
pX: Int, pY: Int,
pWidth: Int, pHeight: Int,
onPress: OnPress
): Button(pX, pY, pWidth, pHeight, Component.literal("SelectionButton"), onPress, DEFAULT_NARRATION) {
): Button(pX, pY, pWidth, pHeight, Component.literal("SelectionButton"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private val buttonTexture = cobblemonResource("textures/gui/starterselection/starterselection_button.png")
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
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.ExitButton
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.widgets.EvolutionSelectScreen
@@ -63,7 +64,7 @@ import net.minecraft.sounds.SoundEvent
* @param selection The index the [party] will have as the base [selectedPokemon].
*/
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, private val selection: Int): Screen(
Component.translatable("cobblemon.ui.summary.title")), Schedulable {
Component.translatable("cobblemon.ui.summary.title")), Schedulable, CobblemonRenderable {
companion object {
const val BASE_WIDTH = 331
@@ -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 net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -38,7 +39,7 @@ class SummaryButton(
private val boldText: Boolean = true,
private val largeText: Boolean = true,
private val textScale: Float = 1F
): 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 {
companion object {
const val TEXT_HEIGHT = 9
@@ -23,7 +23,8 @@ class ModelWidget(
var pokemon: RenderablePokemon,
val baseScale: Float = 2.7F,
var rotationY: Float = 35F,
var offsetY: Double = 0.0
var offsetY: Double = 0.0,
val playCryOnClick: Boolean = false,
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Summary - ModelWidget")) {
companion object {
@@ -70,6 +71,15 @@ class ModelWidget(
matrices.popPose()
}
override fun onClick(pMouseX: Double, pMouseY: Double) {
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (this.isHovered) playCry()
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
private fun playCry() {
if (playCryOnClick) {
state.activeAnimations.clear()
state.addFirstAnimation(setOf("cry"))
}
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.CobblemonNetwork
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.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler.MAX_NAME_LENGTH
@@ -28,7 +29,7 @@ class NicknameEntryWidget(
): EditBox(
Minecraft.getInstance().font,
x, y, width, height, text
) {
), CobblemonRenderable {
var pokemonName = ""
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.common
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.Minecraft
@@ -29,7 +30,7 @@ abstract class SummaryScrollList<T : ObjectSelectionList.Entry<T>>(
HEIGHT, // height
0, // top
slotHeight
) {
), CobblemonRenderable {
companion object {
const val WIDTH = 108
const val HEIGHT = 112
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens
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 SummaryTab(
val label: MutableComponent? = null,
val icon: ResourceLocation? = null,
onPress: OnPress
): Button(pX, pY, 50, 13, label, onPress, DEFAULT_NARRATION) {
): Button(pX, pY, 50, 13, label, onPress, DEFAULT_NARRATION), CobblemonRenderable {
private var isActive = false
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
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.ObjectSelectionList
@@ -24,7 +25,7 @@ class MoveDescriptionScrollList(
30, // height
0, // top
slotHeight
) {
), CobblemonRenderable {
private var scrolling = false
init {
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
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 ReorderMoveButton(
val pX: Int, val pY: Int,
private val isUp: Boolean,
onPress: OnPress
): Button((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.empty(), onPress, DEFAULT_NARRATION) {
): Button((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.empty(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private const val WIDTH = 8
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.MoveTemplate
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
@@ -28,7 +29,7 @@ class SwapMoveButton(
var move: MoveTemplate?,
var movesWidget: MovesWidget,
onPress: OnPress
): Button((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.empty(), onPress, DEFAULT_NARRATION) {
): Button((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Component.empty(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double): Boolean {
return false
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.trade
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.render.drawScaledText
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
@@ -31,7 +32,7 @@ open class PartySlot(
private val parent: TradeGUI,
private val isOpposing: Boolean = false,
onPress: OnPress
) : Button(x, y, SIZE, SIZE, Component.literal("PartySlot"), onPress, DEFAULT_NARRATION) {
) : Button(x, y, SIZE, SIZE, Component.literal("PartySlot"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
val state = FloatingState()
companion object {
const val SIZE = 25
@@ -12,6 +12,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.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
@@ -24,7 +25,7 @@ class TradeButton(
x: Int, y: Int,
val parent: TradeGUI,
onPress: OnPress
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Trade"), onPress, DEFAULT_NARRATION) {
) : Button(x, y, WIDTH, HEIGHT, Component.literal("Trade"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private const val WIDTH = 53
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.storage.party.PartyPosition
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
@@ -52,7 +53,7 @@ class TradeGUI(
val traderName: MutableComponent,
val traderParty: MutableList<TradeablePokemon?>,
val party: MutableList<TradeablePokemon?>
): Screen(lang("trade.gui.title")) {
): Screen(lang("trade.gui.title")), CobblemonRenderable {
companion object {
const val BASE_WIDTH = 293
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.isUsingPokedex
import com.cobblemon.mod.common.util.traceFirstEntityCollision
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.Minecraft
@@ -43,7 +44,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
secondsSinceActioned = 0F
}
fun canAction() = canApplyChange
fun canAction() = canApplyChange && Minecraft.getInstance().player?.isUsingPokedex() == false
override fun onTick() {
if (secondsSinceActioned < 100) {
@@ -21,6 +21,7 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.world.entity.Entity
import java.lang.reflect.Type
/**
@@ -43,35 +44,20 @@ class PoseAdapter(
val pose = JsonPose(model, obj)
val conditionsList = mutableListOf<(PosableState) -> Boolean>()
val mustBeTouchingWater = json.get("isTouchingWater")?.asBoolean
if (mustBeTouchingWater != null) {
conditionsList.add { mustBeTouchingWater == it.getEntity()?.isInWater }
fun addCondition(jsonKey: String, condition: (Entity, Boolean) -> Boolean) {
json.get(jsonKey)?.asBoolean?.let { expectedValue ->
conditionsList.add { it.getEntity()?.let { entity -> condition(entity, expectedValue) } ?: true }
}
}
val mustBeTouchingWaterOrRain = json.get("isInWaterOrRain")?.asBoolean
if (mustBeTouchingWaterOrRain != null) {
conditionsList.add { mustBeTouchingWaterOrRain == it.getEntity()?.isInWaterOrRain }
}
val mustBeSubmergedInWater = json.get("isUnderWater")?.asBoolean
if (mustBeSubmergedInWater != null) {
conditionsList.add { mustBeSubmergedInWater == it.getEntity()?.isUnderWater }
}
val mustBeStandingOnRedSand = json.get("isStandingOnRedSand")?.asBoolean
if (mustBeStandingOnRedSand != null) {
conditionsList.add { mustBeStandingOnRedSand == it.getEntity()?.isStandingOnRedSand() }
}
val mustBeStandingOnSand = json.get("isStandingOnSand")?.asBoolean
if (mustBeStandingOnSand != null) {
conditionsList.add { mustBeStandingOnSand == it.getEntity()?.isStandingOnSand() }
}
val mustBeStandingOnSandOrRedSand = json.get("isStandingOnSandOrRedSand")?.asBoolean
if (mustBeStandingOnSandOrRedSand != null) {
conditionsList.add { mustBeStandingOnSandOrRedSand == it.getEntity()?.isStandingOnSandOrRedSand() }
}
val mustBeDusk = json.get("isDusk")?.asBoolean
if (mustBeDusk != null) {
conditionsList.add { mustBeDusk == it.getEntity()?.isDusk() }
}
addCondition("isTouchingWater") { entity, expectedValue -> entity.isInWater == expectedValue }
addCondition("isInWaterOrRain") { entity, expectedValue -> entity.isInWaterOrRain == expectedValue }
addCondition("isUnderWater") { entity, expectedValue -> entity.isUnderWater == expectedValue }
addCondition("isStandingOnRedSand") { entity, expectedValue -> entity.isStandingOnRedSand() == expectedValue }
addCondition("isStandingOnSand") { entity, expectedValue -> entity.isStandingOnSand() == expectedValue }
addCondition("isStandingOnSandOrRedSand") { entity, expectedValue -> entity.isStandingOnSandOrRedSand() == expectedValue }
addCondition("isDusk") { entity, expectedValue -> entity.isDusk() == expectedValue }
conditionsList.addAll(poseConditionReader(json))
@@ -35,6 +35,11 @@ open class BedrockActiveAnimation(
it.afterAction = action
}
override fun start(state: PosableState) {
super.start(state)
startedSeconds = state.animationSeconds
}
override fun run(
context: RenderContext,
model: PosableModel,
@@ -46,10 +51,6 @@ open class BedrockActiveAnimation(
headPitch: Float,
intensity: Float
): Boolean {
if (startedSeconds == -1F) {
startedSeconds = state.animationSeconds
}
return animation.run(context, model, state, state.animationSeconds - startedSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity).also {
if (!it) {
afterAction(context, state)
@@ -58,9 +59,6 @@ open class BedrockActiveAnimation(
}
override fun applyEffects(entity: Entity?, state: PosableState, previousSeconds: Float, newSeconds: Float) {
if (startedSeconds == -1F) {
startedSeconds = state.animationSeconds
}
val previousSecondsOffset = previousSeconds - startedSeconds
val currentSecondsOffset = newSeconds - startedSeconds
animation.applyEffects(entity, state, previousSecondsOffset, currentSecondsOffset)
@@ -1,74 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class RaichuAlolanModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("raichu_alolan")
override val head = getPart("head")
override var portraitScale = 2.3F
override var portraitTranslation = Vec3(-0.2, 0.6, 0.0)
override var profileScale = 0.65F
override var profileTranslation = Vec3(0.0, 0.75, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("raichu_alolan", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("raichu_alolan", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("raichu_alolan", "ground_idle")
)
)
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("raichu_alolan", "sleep"))
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("raichu_alolan", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk, battleidle, sleep)) bedrockStateful("raichu_alolan", "faint") else null
}
@@ -179,7 +179,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
battleidle = registerPose(
poseName = "battleidle",
poseTypes = PoseType.STATIONARY_POSES,
condition = { it.isBattling && it.getEntity()?.isStandingOnSandOrRedSand() == true },
condition = { it.isBattling && it.getEntity()?.isStandingOnSandOrRedSand() != true },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -1,77 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen6
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class TrevenantModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("trevenant")
override var portraitScale = 1.6F
override var portraitTranslation = Vec3(-0.2, 1.8, 0.0)
override var profileScale = 0.45F
override var profileTranslation = Vec3(0.0, 0.93, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
lateinit var battleidle: Pose
override val cryAnimation = CryProvider { bedrockStateful("trevenant", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("trevenant", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("trevenant", "sleep"))
)
standing = registerPose(
poseName = "stand",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
condition = { !it.isBattling },
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("trevenant", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 5,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("trevenant", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battle_idle",
poseTypes = PoseType.STATIONARY_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
condition = { it.isBattling },
animations = arrayOf(
bedrock("trevenant", "battle_idle")
)
)
}
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, sleep)) bedrockStateful("trevenant", "faint") else
if (state.isPosedIn(battleidle)) bedrockStateful("trevenant", "battle_faint")
else null
}

Some files were not shown because too many files have changed in this diff Show More