mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-28 21:08:53 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1986cb480b |
No files matched your search
@@ -431,7 +431,6 @@
|
||||
- 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.
|
||||
- Fixed an issue where rebinding the R key to a mouse button prevented players from closing the battle interface using that button.
|
||||
|
||||
### Developer
|
||||
- `SpawnCause` is now an implementation of `SpawningInfluence`.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -62,9 +62,9 @@ class NPCClass {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(resourceIdentifier.toString())
|
||||
buffer.writeCollection(names) { _, v -> buffer.writeText(v) }
|
||||
buffer.writeFloat(hitbox.width)
|
||||
buffer.writeFloat(hitbox.height)
|
||||
buffer.writeBoolean(hitbox.fixed)
|
||||
buffer.writeFloat(this.hitbox.width)
|
||||
buffer.writeFloat(this.hitbox.height)
|
||||
buffer.writeBoolean(this.hitbox.fixed)
|
||||
battleConfiguration.encode(buffer)
|
||||
buffer.writeNullable(interaction) { _, value ->
|
||||
buffer.writeString(value.type)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.runner
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,7 @@ interface ShowdownService {
|
||||
fun getAbilityIds(): JsonArray
|
||||
fun getMoves(): JsonArray
|
||||
fun getItemIds(): JsonArray
|
||||
fun getTypeChart(): JsonObject
|
||||
fun registerSpecies()
|
||||
fun registerBagItems()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
@@ -38,4 +40,4 @@ interface ShowdownService {
|
||||
companion object {
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
@@ -152,6 +153,12 @@ class GraalShowdownService : ShowdownService {
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getTypeChart(): JsonObject {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getTypeChart")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonObject::class.java)
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
|
||||
+8
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
@@ -123,6 +124,13 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getTypeChart(): JsonObject {
|
||||
writer.write(">getTypeChart")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonObject::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
|
||||
@@ -184,16 +184,15 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
|
||||
override fun charTyped(chr: Char, modifiers: Int): Boolean {
|
||||
if (chr.toString().equals(PartySendBinding.boundKey().displayName.string, ignoreCase = true) && CobblemonClient.battleOverlay.opacity == BattleOverlay.MAX_OPACITY && PartySendBinding.canAction()) {
|
||||
return minimizeBattle()
|
||||
val battle = CobblemonClient.battle ?: return false
|
||||
battle.minimised = !battle.minimised
|
||||
PartySendBinding.actioned()
|
||||
return true
|
||||
}
|
||||
return super.charTyped(chr, modifiers)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (button == PartySendBinding.boundKey().value && CobblemonClient.battleOverlay.opacity == BattleOverlay.MAX_OPACITY && PartySendBinding.canAction()) {
|
||||
return minimizeBattle()
|
||||
}
|
||||
|
||||
val battle = CobblemonClient.battle
|
||||
if (battle?.spectating == true && specBackButton.isHovered(mouseX, mouseY)) {
|
||||
RemoveSpectatorPacket(battle.battleId).sendToServer()
|
||||
@@ -201,11 +200,4 @@ class BattleGUI : Screen(battleLang("gui.title")), CobblemonRenderable {
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
private fun minimizeBattle(): Boolean {
|
||||
val battle = CobblemonClient.battle ?: return false
|
||||
battle.minimised = !battle.minimised
|
||||
PartySendBinding.actioned()
|
||||
return true
|
||||
}
|
||||
}
|
||||
+47
-7
@@ -11,12 +11,11 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.moves.TypeChart
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
@@ -24,12 +23,16 @@ import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.toRGB
|
||||
import com.google.common.collect.Lists
|
||||
import net.fabricmc.loader.impl.util.StringUtil
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.util.Mth.floor
|
||||
|
||||
class BattleMoveSelection(
|
||||
@@ -91,16 +94,16 @@ class BattleMoveSelection(
|
||||
open val response: MoveActionResponse get() = MoveActionResponse(move.id, targetPnx)
|
||||
open val selectable: Boolean get() = !move.disabled
|
||||
|
||||
val targetPnx: String? get() = targetList?.let { targets ->
|
||||
val target: Targetable? get() = targetList?.let { targets ->
|
||||
return@let when {
|
||||
targets.isEmpty() -> null
|
||||
targets.size == 1 -> targets[0].getPNX()
|
||||
targets.size == 1 -> targets[0]
|
||||
else -> null // TODO: multi-battles
|
||||
}
|
||||
}
|
||||
val targetPnx: String? get() = target?.getPNX()
|
||||
|
||||
fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
val selectConditionOpacity = moveSelection.opacity * if (!selectable) 0.5F else 1F
|
||||
|
||||
blitk(
|
||||
@@ -173,6 +176,8 @@ class BattleMoveSelection(
|
||||
opacity = moveSelection.opacity,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawTooltip(context, mouseX, mouseY)
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX >= x && mouseX <= x + MOVE_WIDTH && mouseY >= y && mouseY <= y + MOVE_HEIGHT
|
||||
@@ -182,6 +187,41 @@ class BattleMoveSelection(
|
||||
moveSelection.playDownSound(Minecraft.getInstance().soundManager)
|
||||
moveSelection.battleGUI.selectAction(moveSelection.request, response)
|
||||
}
|
||||
|
||||
private fun drawTooltip(context: GuiGraphics, mouseX: Int, mouseY: Int) {
|
||||
if (!isHovered(mouseX.toDouble(), mouseY.toDouble())) return
|
||||
|
||||
val textTooltip: MutableList<MutableComponent> = mutableListOf()
|
||||
for (descriptionLine in StringUtil.wrapLines(moveTemplate.description.string, 80).split("\n")) {
|
||||
textTooltip.add(Component.literal(descriptionLine).gray())
|
||||
}
|
||||
|
||||
textTooltip.add(Component.literal(""))
|
||||
|
||||
if (moveTemplate.power > 0) textTooltip.add(lang("ui.power").append(String.format(": %d", moveTemplate.power.toInt())))
|
||||
if (moveTemplate.accuracy > 0) textTooltip.add(lang("ui.accuracy").append(String.format(": %d%%", moveTemplate.accuracy.toInt())))
|
||||
|
||||
if (moveTemplate.power > 0 && moveTemplate.target != MoveTarget.self && target is ActiveClientBattlePokemon) {
|
||||
val species = (target as ActiveClientBattlePokemon).battlePokemon?.species
|
||||
if (species != null) {
|
||||
val moveEffectiveness: Double = TypeChart.getMoveEffectiveness(elementalType, listOf(species.primaryType, species.secondaryType))
|
||||
|
||||
textTooltip.add(when {
|
||||
moveEffectiveness == 0.0 -> lang("battle.ui.effectiveness.immunity", moveEffectiveness).gray()
|
||||
moveEffectiveness < 1.0 -> lang("battle.ui.effectiveness.resistance", moveEffectiveness).gray()
|
||||
moveEffectiveness > 1.0 -> lang("battle.ui.effectiveness.super_effective", moveEffectiveness).yellow()
|
||||
else -> lang("battle.ui.effectiveness.normal", moveEffectiveness).white()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
context.renderTooltip(
|
||||
Minecraft.getInstance().font,
|
||||
Lists.transform(textTooltip, Component::getVisualOrderText),
|
||||
mouseX,
|
||||
mouseY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
@@ -70,7 +70,6 @@ import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.entity.AgeableMob
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.EntityDimensions
|
||||
import net.minecraft.world.entity.Pose
|
||||
import net.minecraft.world.entity.ai.Brain
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
||||
@@ -102,8 +101,6 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
brain = makeBrain(brainDynamic ?: makeEmptyBrainDynamic())
|
||||
}
|
||||
}
|
||||
|
||||
this.refreshDimensions()
|
||||
}
|
||||
|
||||
val level: Int
|
||||
@@ -111,8 +108,6 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
|
||||
var skill: Int? = null // range from 0 - 5
|
||||
|
||||
var hitbox: EntityDimensions? = null
|
||||
|
||||
var party: NPCPartyStore? = null
|
||||
|
||||
var isMovable: Boolean? = null
|
||||
@@ -347,14 +342,6 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
it.putByteArray(DataKeys.NPC_PLAYER_TEXTURE_TEXTURE, playerTexture.texture)
|
||||
})
|
||||
}
|
||||
val hitbox = hitbox
|
||||
if (hitbox != null) {
|
||||
nbt.put(DataKeys.NPC_HITBOX, CompoundTag().also {
|
||||
it.putFloat(DataKeys.NPC_HITBOX_WIDTH, hitbox.width)
|
||||
it.putFloat(DataKeys.NPC_HITBOX_HEIGHT, hitbox.height)
|
||||
it.putBoolean(DataKeys.NPC_HITBOX_FIXED, hitbox.fixed)
|
||||
})
|
||||
}
|
||||
val isMovable = isMovable
|
||||
if (isMovable != null) {
|
||||
nbt.putBoolean(DataKeys.NPC_IS_MOVABLE, isMovable)
|
||||
@@ -374,6 +361,9 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
return nbt
|
||||
}
|
||||
|
||||
override fun hasCustomName() = true
|
||||
override fun isCustomNameVisible() = true
|
||||
|
||||
override fun load(nbt: CompoundTag) {
|
||||
npc = NPCClasses.getByIdentifier(ResourceLocation.parse(nbt.getString(DataKeys.NPC_CLASS))) ?: NPCClasses.classes.first()
|
||||
entityData.set(LEVEL, nbt.getInt(DataKeys.NPC_LEVEL).takeIf { it != 0 } ?: 1)
|
||||
@@ -406,17 +396,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
this.isInvulnerable = if (nbt.contains(DataKeys.NPC_IS_INVULNERABLE)) nbt.getBoolean(DataKeys.NPC_IS_INVULNERABLE) else null
|
||||
this.isLeashable = if (nbt.contains(DataKeys.NPC_IS_LEASHABLE)) nbt.getBoolean(DataKeys.NPC_IS_LEASHABLE) else null
|
||||
this.allowProjectileHits = if (nbt.contains(DataKeys.NPC_ALLOW_PROJECTILE_HITS)) nbt.getBoolean(DataKeys.NPC_ALLOW_PROJECTILE_HITS) else null
|
||||
this.hitbox = if (nbt.contains(DataKeys.NPC_HITBOX)) {
|
||||
val hitboxNBT = nbt.getCompound(DataKeys.NPC_HITBOX)
|
||||
|
||||
val width = hitboxNBT.getFloat(DataKeys.NPC_HITBOX_WIDTH)
|
||||
val height = hitboxNBT.getFloat(DataKeys.NPC_HITBOX_HEIGHT)
|
||||
val fixed = hitboxNBT.getBoolean(DataKeys.NPC_HITBOX_FIXED)
|
||||
|
||||
if (fixed) EntityDimensions.fixed(width, height) else EntityDimensions.scalable(width, height)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
updateAspects()
|
||||
}
|
||||
|
||||
@@ -446,13 +426,9 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
updateAspects()
|
||||
}
|
||||
|
||||
override fun hasCustomName() = true
|
||||
|
||||
override fun isCustomNameVisible() = true
|
||||
|
||||
override fun isPersistenceRequired() = !npc.canDespawn
|
||||
|
||||
override fun getDimensions(pose: Pose): EntityDimensions = hitbox ?: npc.hitbox
|
||||
override fun getDefaultDimensions(pose: Pose) = npc.hitbox
|
||||
|
||||
override fun isPushable(): Boolean {
|
||||
return isMovable ?: npc.isMovable
|
||||
|
||||
@@ -394,10 +394,9 @@ open class PokemonEntity(
|
||||
// Deploy a platform if a non-wild Pokemon is touching water but not underwater.
|
||||
// This can't be done in the BattleMovementGoal as the sleep goal will override it.
|
||||
if (ticksLived > 5 && platform == PlatformType.NONE
|
||||
&& ownerUUID != null
|
||||
&& this.ownerUUID != null
|
||||
&& isInWater && !isUnderWater
|
||||
&& !exposedForm.behaviour.moving.swim.canBreatheUnderwater && !exposedForm.behaviour.moving.swim.canWalkOnWater
|
||||
&& !getBehaviourFlag(PokemonBehaviourFlag.FLYING)) {
|
||||
&& !exposedForm.behaviour.moving.swim.canBreatheUnderwater && !exposedForm.behaviour.moving.swim.canWalkOnWater) {
|
||||
platform = PlatformType.getPlatformTypeForPokemon((exposedForm))
|
||||
}
|
||||
} else {
|
||||
@@ -745,7 +744,6 @@ open class PokemonEntity(
|
||||
goalSelector.addGoal(4, PokemonMoveIntoFluidGoal(this))
|
||||
goalSelector.addGoal(5, SleepOnTrainerGoal(this))
|
||||
goalSelector.addGoal(5, WildRestGoal(this))
|
||||
goalSelector.addGoal(5, PokemonHideInGrassGoal(this))
|
||||
|
||||
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.HAS_BEEN_SHEARED) != null) {
|
||||
goalSelector.addGoal(5, EatBlockGoal(this))
|
||||
|
||||
-111
@@ -1,111 +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.entity.pokemon.ai.goals
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.ai.goal.Goal
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
class PokemonHideInGrassGoal(private val pokemonEntity: PokemonEntity): Goal() {
|
||||
|
||||
companion object {
|
||||
private const val GRASS_SEARCH_HORIZONTAL_RANGE = 16.0
|
||||
private const val GRASS_SEARCH_VERTICAL_RANGE = 2.0
|
||||
private val SECONDS_IN_SAME_GRASS = 5 .. 10
|
||||
private val VALID_GRASS_BLOCKS = setOf(
|
||||
Blocks.SHORT_GRASS,
|
||||
Blocks.TALL_GRASS,
|
||||
Blocks.FERN,
|
||||
Blocks.LARGE_FERN,
|
||||
)
|
||||
}
|
||||
|
||||
private var pickedGrassBlockPos: BlockPos? = null
|
||||
private var ticksInGrass = 0
|
||||
private var wantedTicksInGrass = 0
|
||||
|
||||
override fun canUse(): Boolean =
|
||||
pokemonEntity.pokemon.species.behaviour.moving.hideInGrass
|
||||
&& pokemonEntity.pokemon.isWild()
|
||||
&& getNearbyGrassBlocks().isNotEmpty()
|
||||
|
||||
override fun start() {
|
||||
super.start()
|
||||
|
||||
pickedGrassBlockPos = getNearbyGrassBlocks().random()
|
||||
ticksInGrass = 0
|
||||
wantedTicksInGrass = pokemonEntity.random.nextIntBetweenInclusive(SECONDS_IN_SAME_GRASS.first, SECONDS_IN_SAME_GRASS.last) * 20
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
val grassBlockCenterPos = pickedGrassBlockPos?.bottomCenter ?: return stop()
|
||||
val couldMoveToGrass = pokemonEntity.navigation.moveTo(
|
||||
grassBlockCenterPos.x,
|
||||
grassBlockCenterPos.y,
|
||||
grassBlockCenterPos.z,
|
||||
1.0
|
||||
)
|
||||
|
||||
if (!couldMoveToGrass) return stop()
|
||||
|
||||
ticksInGrass++
|
||||
}
|
||||
|
||||
override fun canContinueToUse(): Boolean {
|
||||
if (ticksInGrass >= wantedTicksInGrass) return false
|
||||
|
||||
pickedGrassBlockPos?.let {
|
||||
val grassStillExists = isGrass(pokemonEntity.level().getBlockState(it))
|
||||
if (!grassStillExists) return false
|
||||
|
||||
val grassBox = AABB.ofSize(it.toVec3d(), 0.5, 0.5, 0.5)
|
||||
val isGrassOccupiedByAnotherEntity = pokemonEntity.level().getEntities(pokemonEntity, grassBox).isNotEmpty()
|
||||
if (isGrassOccupiedByAnotherEntity) {
|
||||
return false
|
||||
}
|
||||
} ?: return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
super.stop()
|
||||
|
||||
pickedGrassBlockPos = null
|
||||
ticksInGrass = 0
|
||||
pokemonEntity.navigation.stop()
|
||||
}
|
||||
|
||||
private fun getNearbyGrassBlocks(): List<BlockPos> {
|
||||
val searchBox = AABB.ofSize(
|
||||
pokemonEntity.position(),
|
||||
GRASS_SEARCH_HORIZONTAL_RANGE,
|
||||
GRASS_SEARCH_VERTICAL_RANGE,
|
||||
GRASS_SEARCH_HORIZONTAL_RANGE
|
||||
)
|
||||
|
||||
val grassBlocks: MutableList<BlockPos> = mutableListOf()
|
||||
BlockPos.betweenClosedStream(searchBox).forEach {
|
||||
val blockState = pokemonEntity.level().getBlockState(it)
|
||||
if (isGrass(blockState)) {
|
||||
grassBlocks.add(BlockPos(it))
|
||||
}
|
||||
}
|
||||
|
||||
return grassBlocks
|
||||
}
|
||||
|
||||
private fun isGrass(blockState: BlockState): Boolean = VALID_GRASS_BLOCKS.contains(blockState.block)
|
||||
}
|
||||
+1
-1
@@ -50,7 +50,7 @@ class PokemonInBattleMovementGoal(val entity: PokemonEntity, val range: Int) : G
|
||||
entity.navigation.moveTo(battlePos!!.x, battlePos!!.y, battlePos!!.z, 1.0)
|
||||
hasMovedToPos = true
|
||||
}
|
||||
if (entity.ticksLived > 1 && !entity.getBehaviourFlag(PokemonBehaviourFlag.FLYING) && entity.navigation.isAirborne(entity.level(), entity.blockPosition())) {
|
||||
if (entity.ticksLived > 1 && !entity.onGround() && !entity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) {
|
||||
// Let flyers fly in battle if they're in the air
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
|
||||
}
|
||||
|
||||
@@ -23,5 +23,4 @@ class MoveBehaviour {
|
||||
val wanderSpeed = 1.0
|
||||
val canLook = true
|
||||
val looksAtEntities = true
|
||||
val hideInGrass = false
|
||||
}
|
||||
@@ -219,10 +219,6 @@ object DataKeys {
|
||||
const val NPC_IS_INVULNERABLE = "IsInvulnerable"
|
||||
const val NPC_IS_LEASHABLE = "IsLeashable"
|
||||
const val NPC_ALLOW_PROJECTILE_HITS = "AllowProjectileHits"
|
||||
const val NPC_HITBOX = "Hitbox"
|
||||
const val NPC_HITBOX_WIDTH = "Width"
|
||||
const val NPC_HITBOX_HEIGHT = "Height"
|
||||
const val NPC_HITBOX_FIXED = "Fixed"
|
||||
|
||||
// PokemonProperties
|
||||
const val ELEMENTAL_TYPE = "ElementalType"
|
||||
|
||||
@@ -1268,6 +1268,7 @@
|
||||
"cobblemon.ui.status.psn": "PSN",
|
||||
"cobblemon.ui.status.slp": "SLP",
|
||||
"cobblemon.ui.status.tox": "PSN",
|
||||
|
||||
"cobblemon.battle.ui.fight": "Fight",
|
||||
"cobblemon.battle.ui.switch": "Switch",
|
||||
"cobblemon.battle.ui.capture": "Catch",
|
||||
@@ -1277,6 +1278,11 @@
|
||||
"cobblemon.battle.ui.hide_label": "Press %1$s to move around.",
|
||||
"cobblemon.battle.ui.actions_label": "You need to choose an action. Press %1$s.",
|
||||
|
||||
"cobblemon.battle.ui.effectiveness.normal": "%1$sx Normal",
|
||||
"cobblemon.battle.ui.effectiveness.super_effective": "%1$sx Super Effective",
|
||||
"cobblemon.battle.ui.effectiveness.resistance": "%1$sx Resistance",
|
||||
"cobblemon.battle.ui.effectiveness.immunity": "%1$sx Immunity",
|
||||
|
||||
"cobblemon.ui.evolve": "Evolve",
|
||||
"cobblemon.ui.evolution": "Evolution",
|
||||
"cobblemon.ui.evolve.hint": "%1$s seems ready to evolve!",
|
||||
@@ -6770,7 +6776,7 @@
|
||||
"cobblemon.ability.schooling": "Schooling",
|
||||
"cobblemon.ability.schooling.desc": "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.",
|
||||
"cobblemon.ability.scrappy": "Scrappy",
|
||||
"cobblemon.ability.scrappy.desc": "The Pokémon can hit Ghost-type Pokémon with Normal- and Fighting-type moves. It also grants immunity to Intimidate.",
|
||||
"cobblemon.ability.scrappy.desc": "The Pokémon can hit Ghost-type Pokémon with Normal- and Fighting-type moves.",
|
||||
"cobblemon.ability.screencleaner": "Screen Cleaner",
|
||||
"cobblemon.ability.screencleaner.desc": "When the Pokémon enters a battle, the effects of Light Screen, Reflect, and Aurora Veil are nullified for both opposing and ally Pokémon.",
|
||||
"cobblemon.ability.seedsower": "Seed Sower",
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:enigma_berry",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:ha_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"item": "cobblemon:enigma_berry",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:ha_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:berry_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:clover_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:flower_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:love_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:ribbon_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:star_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"item": "cobblemon:strawberry_sweet",
|
||||
"effects": [
|
||||
{
|
||||
"type": "cobblemon:inert"
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" GA",
|
||||
"GGA",
|
||||
"II "
|
||||
"GCA",
|
||||
"ICA",
|
||||
"I "
|
||||
],
|
||||
"key": {
|
||||
"I": {
|
||||
@@ -12,6 +12,9 @@
|
||||
"G": {
|
||||
"item": "minecraft:gold_ingot"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:copper_ingot"
|
||||
},
|
||||
"A": {
|
||||
"item": "minecraft:amethyst_shard"
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
"IA",
|
||||
"CI",
|
||||
"C "
|
||||
" Q",
|
||||
"CPC",
|
||||
"Q "
|
||||
],
|
||||
"key": {
|
||||
"I": {
|
||||
"item": "minecraft:iron_ingot"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:copper_ingot"
|
||||
},
|
||||
"A": {
|
||||
"item": "minecraft:amethyst_shard"
|
||||
}
|
||||
"Q": {
|
||||
"item": "minecraft:quartz"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:copper_ingot"
|
||||
},
|
||||
"P": {
|
||||
"item": "minecraft:ender_pearl"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"id": "cobblemon:wide_lens"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
"AGG",
|
||||
"GCC",
|
||||
"AGC",
|
||||
"GGC",
|
||||
" C"
|
||||
],
|
||||
"key": {
|
||||
|
||||
@@ -5,7 +5,22 @@
|
||||
],
|
||||
"maxY": 62,
|
||||
"neededNearbyBlocks": [
|
||||
"#cobblemon:ancient_city_blocks"
|
||||
"minecraft:chiseled_deepslate",
|
||||
"minecraft:cobbled_deepslate_wall",
|
||||
"minecraft:cracked_deepslate_bricks",
|
||||
"minecraft:cracked_deepslate_tiles",
|
||||
"minecraft:deepslate_brick_slab",
|
||||
"minecraft:deepslate_brick_stairs",
|
||||
"minecraft:deepslate_brick_wall",
|
||||
"minecraft:deepslate_bricks",
|
||||
"minecraft:deepslate_tile_slab",
|
||||
"minecraft:deepslate_tile_stairs",
|
||||
"minecraft:deepslate_tile_wall",
|
||||
"minecraft:deepslate_tiles",
|
||||
"minecraft:polished_basalt",
|
||||
"minecraft:polished_deepslate",
|
||||
"minecraft:polished_deepslate_slab",
|
||||
"minecraft:polished_deepslate_wall"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loaded 100 of 470 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user