mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-11-02 22:38:54 -05:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c10a4bd0d | ||
|
|
7bed04c42e | ||
|
|
6c71490c8c |
No files matched your search
@@ -47,6 +47,7 @@ import com.cobblemon.mod.common.block.RevivalHerbBlock
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import com.cobblemon.mod.common.block.SaccharineLogBlock
|
||||
import com.cobblemon.mod.common.block.SaccharineLogSlatheredBlock
|
||||
import com.cobblemon.mod.common.block.ScarekrowBlock
|
||||
import com.cobblemon.mod.common.block.StackableItemBlock
|
||||
import com.cobblemon.mod.common.block.TumblestoneBlock
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock
|
||||
@@ -1160,6 +1161,15 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
}
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val SCAREKROW = this.create("scarekrow", ScarekrowBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
.sound(SoundType.GRASS)
|
||||
.strength(0.1f)
|
||||
.noOcclusion()
|
||||
))
|
||||
|
||||
init {
|
||||
/**
|
||||
* Makes all blocks in array flammable by adding them to FireBlock's flammableRegistry.
|
||||
|
||||
@@ -205,6 +205,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
|
||||
|
||||
@JvmField
|
||||
val SCAREKROW = blockItem("scarekrow", CobblemonBlocks.SCAREKROW)
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
|
||||
@JvmField
|
||||
|
||||
@@ -114,6 +114,8 @@ object CobblemonMemories {
|
||||
val DISABLE_WALK_TO_WANTED_ITEM = register<Boolean>("disable_walk_to_wanted_item")
|
||||
@JvmField
|
||||
val TIME_TRYING_TO_REACH_WANTED_ITEM = register<Int>("time_trying_to_wanted_item")
|
||||
@JvmField
|
||||
val NEARBY_SCAREKROW = register<BlockPos>("nearby_scarekrow", BlockPos.CODEC)
|
||||
|
||||
@JvmStatic
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.biggerbrain.objective
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asSimplifiedExpression
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.BiggerBrain
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.LookingTracker
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.RegularMovementTracker
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.Distraction
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.DistractionType
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.ConditionalLockFixation
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.EntityFixation
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.LookAndMoveFixation
|
||||
import com.cobblemon.mod.common.api.ai.config.objective.ObjectiveConfig
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.ai.util.LandRandomPos
|
||||
|
||||
class AvoidScarekrow(
|
||||
private val speedMultiplier: Expression = "0.5".asExpression(),
|
||||
) : Objective {
|
||||
private var movementTask: EntityFixation? = null
|
||||
private var remainingCooldownTicks = 0
|
||||
|
||||
override fun getDescription(entity: LivingEntity) = "Avoiding target block"
|
||||
|
||||
override fun perform(
|
||||
entity: LivingEntity,
|
||||
brain: BiggerBrain,
|
||||
activeObjectiveTags: MutableSet<String>
|
||||
): ObjectiveStatus {
|
||||
if (entity !is PokemonEntity || !entity.isAlive) return ObjectiveStatus.INVALID
|
||||
|
||||
val target = brain.getMemory(CobblemonMemories.NEARBY_SCAREKROW)
|
||||
?: return ObjectiveStatus.INACTIVE
|
||||
|
||||
// if the scarekrow is gone we dont need to be scared anymore
|
||||
if (!entity.level().getBlockState(target).`is`(CobblemonBlockTags.SCAREKROWS)) {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_SCAREKROW)
|
||||
return ObjectiveStatus.INACTIVE
|
||||
}
|
||||
|
||||
// finish existing movement task
|
||||
if (brain.isFixation(movementTask)) {
|
||||
return ObjectiveStatus.FIXATED
|
||||
}
|
||||
|
||||
// aggro against the player should take priority
|
||||
if (brain.getMemory(MemoryModuleType.ATTACK_TARGET) != null) {
|
||||
return ObjectiveStatus.BLOCKED
|
||||
}
|
||||
|
||||
if (remainingCooldownTicks > 0) {
|
||||
remainingCooldownTicks--
|
||||
}
|
||||
val runtime = mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val speed = runtime.resolveFloat(speedMultiplier)
|
||||
|
||||
if (!brain.distract(Distraction.FORCE)) return ObjectiveStatus.BLOCKED
|
||||
var targetPos : BlockPos? = null
|
||||
|
||||
// fixate on running from the scarekrow until it is out of range
|
||||
brain.fixate(ConditionalLockFixation("Avoiding scarekrow") {
|
||||
brain.getMemory(CobblemonMemories.NEARBY_SCAREKROW) != null
|
||||
})
|
||||
|
||||
// find position
|
||||
val direction = entity.position().subtract(target.toVec3d())
|
||||
for (j in 0..9) {
|
||||
val targetVec3 = LandRandomPos.getPosAway(entity, (Cobblemon.config.scarekrowRadius * 1.2).toInt(), 7, direction)
|
||||
if (targetVec3 != null) {
|
||||
targetPos = BlockPos.containing(targetVec3)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Start a look-and-move fixation only when navigation accepts the selected position.
|
||||
if (targetPos != null) {
|
||||
val movementTracker = RegularMovementTracker.attempt(
|
||||
entity = entity,
|
||||
walkTarget = WalkTarget(
|
||||
targetPos,
|
||||
speed,
|
||||
0
|
||||
)
|
||||
)
|
||||
if (movementTracker != null) {
|
||||
movementTask = brain.fixate(
|
||||
LookAndMoveFixation(
|
||||
distractionTypes = setOf(
|
||||
DistractionType.FORCE
|
||||
),
|
||||
lookingTracker = LookingTracker.fromNavigationPathOrWalkTarget(
|
||||
entity,
|
||||
movementTracker.walkTarget
|
||||
),
|
||||
movementTracker = movementTracker,
|
||||
description = "Moving away from target block",
|
||||
)
|
||||
)
|
||||
return ObjectiveStatus.FIXATED
|
||||
}
|
||||
}
|
||||
|
||||
return ObjectiveStatus.FIXATED
|
||||
}
|
||||
|
||||
override fun onRegister(entity: LivingEntity, brain: BiggerBrain) {
|
||||
brain.subscribe(
|
||||
priority = Priority.LOW,
|
||||
memory = CobblemonMemories.NEARBY_SCAREKROW,
|
||||
) { value, labels ->
|
||||
if (value == null) {
|
||||
if (brain.isFixation(movementTask)) {
|
||||
brain.distract(Distraction.FORCE)
|
||||
movementTask = null
|
||||
}
|
||||
}
|
||||
return@subscribe true
|
||||
}
|
||||
}
|
||||
|
||||
class Config : ObjectiveConfig {
|
||||
val speedMultiplier: ExpressionOrEntityVariable = Either.left("0.5".asExpression())
|
||||
|
||||
override fun getVariables(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): List<MoLangConfigVariable> {
|
||||
return listOf(speedMultiplier).asVariables()
|
||||
}
|
||||
|
||||
override fun create(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): Objective {
|
||||
return AvoidScarekrow(
|
||||
speedMultiplier = speedMultiplier.asSimplifiedExpression(entity),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -300,7 +300,7 @@ class DefendNest(
|
||||
description = "patrol method in DefendNest objective",
|
||||
lookingTracker = LookingTracker.fromNavigationPathOrWalkTarget(entity, tracker.walkTarget),
|
||||
movementTracker = tracker,
|
||||
onMovementComplete = { _, _ -> patrolCooldownTicks = PATROL_PAUSE_TICKS + entity.random.nextInt(PATROL_PAUSE_RANDOM_TICKS) }
|
||||
onMovementComplete = { entity1, brain1, status -> patrolCooldownTicks = PATROL_PAUSE_TICKS + entity.random.nextInt(PATROL_PAUSE_RANDOM_TICKS) }
|
||||
)
|
||||
)
|
||||
return ObjectiveStatus.FIXATED
|
||||
|
||||
+1
-2
@@ -33,7 +33,6 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.EntityTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
|
||||
|
||||
@@ -156,7 +155,7 @@ class FriendlyBehaviour(
|
||||
),
|
||||
lookingTracker = LookingTracker.forTargetEntity(entity, trackedEntity!!),
|
||||
movementTracker = movementTracker,
|
||||
onMovementComplete = { _, _ -> attemptFixation(entity, brain) } // maintains fixation between moving and looking
|
||||
onMovementComplete = { entity1, brain1, status -> attemptFixation(entity, brain) } // maintains fixation between moving and looking
|
||||
)
|
||||
)
|
||||
return ObjectiveStatus.FIXATED
|
||||
|
||||
+1
-7
@@ -14,21 +14,15 @@ import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.BiggerBrain
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.LookingTracker
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.MovementTracker
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.RegularMovementTracker
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.Distraction
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.DistractionType
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.comfortDistraction
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.distraction.idleBoredomDistraction
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.EntityFixation
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.LookAndMoveFixation
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.fixation.MovementTrackerFixation
|
||||
import com.cobblemon.mod.common.api.ai.config.objective.ObjectiveConfig
|
||||
import com.cobblemon.mod.common.api.ai.resolveFloat
|
||||
import com.cobblemon.mod.common.api.ai.resolveInt
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -92,7 +86,7 @@ class WalkTowardsParent(
|
||||
distractionTypes = setOf(DistractionType.DAMAGE, DistractionType.FORCE), // We are very fine to ignore this movement if forced or hurt
|
||||
lookingTracker = LookingTracker.forTargetEntity(entity, adult),
|
||||
movementTracker = movementTracker,
|
||||
onMovementComplete = { _, _ -> cooldownTicks = 60 }
|
||||
onMovementComplete = { entity1, brain1, status -> cooldownTicks = 60 }
|
||||
)
|
||||
)
|
||||
return ObjectiveStatus.FIXATED // We are now fixated on
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.biggerbrain.sense
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.BiggerBrain
|
||||
import com.cobblemon.mod.common.api.ai.config.sense.SenseConfig
|
||||
import com.cobblemon.mod.common.api.ai.resolveInt
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
class ScarekrowSense(
|
||||
private var cooldownTicks: Int = SCAN_INTERVAL
|
||||
) : Sense {
|
||||
override var ticksUntilNextExecution: Int = cooldownTicks
|
||||
override val memories = setOf(
|
||||
CobblemonMemories.NEARBY_SCAREKROW,
|
||||
)
|
||||
|
||||
override fun merge(other: Sense): Boolean {
|
||||
if (other is ScarekrowSense) {
|
||||
this.cooldownTicks = minOf(this.cooldownTicks, other.cooldownTicks)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun tick(entity: LivingEntity, brain: BiggerBrain) {
|
||||
ticksUntilNextExecution = cooldownTicks
|
||||
val brain = entity.brain
|
||||
|
||||
val searchRadius = Cobblemon.config.scarekrowRadius
|
||||
val currPos: BlockPos? = brain.getMemorySafely(CobblemonMemories.NEARBY_SCAREKROW).orElse(null)
|
||||
|
||||
if (currPos != null && entity.level().getBlockState(currPos).`is`(CobblemonBlockTags.SCAREKROWS)) {
|
||||
val distance = entity.distanceTo(currPos)
|
||||
if (distance > searchRadius) {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_SCAREKROW)
|
||||
}
|
||||
}
|
||||
|
||||
val centerPos = entity.blockPosition()
|
||||
var scarekrowPos: BlockPos? = null
|
||||
var shortestDist = Double.MAX_VALUE
|
||||
val world = entity.level()
|
||||
BlockPos.betweenClosedStream(
|
||||
centerPos.offset(-searchRadius, -searchRadius, -searchRadius),
|
||||
centerPos.offset(searchRadius, searchRadius, searchRadius)
|
||||
).forEach { pos ->
|
||||
val state = world.getBlockState(pos)
|
||||
if (state.`is`(CobblemonBlockTags.SCAREKROWS)) {
|
||||
val distance = pos.distSqr(Vec3i(centerPos.x, centerPos.y, centerPos.z))
|
||||
if (distance < shortestDist) {
|
||||
scarekrowPos = BlockPos(pos)
|
||||
shortestDist = distance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scarekrowPos != null) {
|
||||
brain.setMemory(CobblemonMemories.NEARBY_SCAREKROW, scarekrowPos)
|
||||
} else {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_SCAREKROW)
|
||||
}
|
||||
}
|
||||
|
||||
class Config : SenseConfig {
|
||||
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
|
||||
var cooldownTicks: ExpressionOrEntityVariable = Either.left("3".asExpression())
|
||||
|
||||
override fun getVariables(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
) = listOf(cooldownTicks).asVariables()
|
||||
|
||||
override fun create(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext): Sense {
|
||||
val cooldownTicks = cooldownTicks.resolveInt(behaviourConfigurationContext.runtime)
|
||||
return ScarekrowSense(
|
||||
cooldownTicks = cooldownTicks
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SCAN_INTERVAL = 3
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+2
-1
@@ -40,6 +40,7 @@ import com.cobblemon.mod.common.api.ai.biggerbrain.objective.WanderAround
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.JumpOutOfWater
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.NeutralBehaviour
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.PointToSpawn
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.AvoidScarekrow
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.SkittishBehaviour
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.TerritorialBehaviour
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.objective.WalkTowardsParent
|
||||
@@ -86,7 +87,7 @@ interface ObjectiveConfig {
|
||||
cobblemonResource("combat_neutral") to NeutralBehaviour.Config::class.java,
|
||||
cobblemonResource("combat_skittish") to SkittishBehaviour.Config::class.java,
|
||||
cobblemonResource("behaviour_friendly") to FriendlyBehaviour.Config::class.java,
|
||||
|
||||
cobblemonResource("avoid_scarekrow") to AvoidScarekrow.Config::class.java,
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.api.ai.biggerbrain.sense.NearbyEntitiesSense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.PokemonAdultSense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.PokemonItemSense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.SaccLeafSense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.ScarekrowSense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.Sense
|
||||
import com.cobblemon.mod.common.api.ai.biggerbrain.sense.SweetBerryBushSense
|
||||
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
@@ -57,6 +58,7 @@ interface SenseConfig {
|
||||
cobblemonResource("flower_sense") to FlowerSense.Config::class.java,
|
||||
cobblemonResource("beehive_sense") to BeeHiveSense.Config::class.java,
|
||||
cobblemonResource("sacc_leaf_sense") to SaccLeafSense.Config::class.java,
|
||||
cobblemonResource("scarekrow_sense") to ScarekrowSense.Config::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val SEES_SKY = createTag("sees_sky")
|
||||
@JvmField val SHINY_STONE_ORES = createTag("shiny_stone_ores")
|
||||
@JvmField val SIGNS = createTag("signs")
|
||||
@JvmField val SCAREKROWS = createTag("scarekrows")
|
||||
@JvmField val SKY_TUMBLESTONE_BRICKS = createTag("sky_tumblestone_bricks")
|
||||
@JvmField val SKY_TUMBLESTONES = createTag("sky_tumblestones")
|
||||
@JvmField val SMALL_FLOWERS = createTag("small_flowers")
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.LevelEvent
|
||||
import net.minecraft.world.level.block.Mirror
|
||||
import net.minecraft.world.level.block.Rotation
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
|
||||
/**
|
||||
* Based off PCBlock's double block functionality, but for less specific use cases
|
||||
*
|
||||
* @author whatsy
|
||||
* @since September 30, 2026
|
||||
* @see PCBlock
|
||||
*/
|
||||
open class DoubleBlock(properties: Properties): Block(properties) {
|
||||
|
||||
companion object {
|
||||
val HALF: Property<Half> = EnumProperty.create("half", Half::class.java)
|
||||
}
|
||||
|
||||
enum class Half(private val label: String): StringRepresentable {
|
||||
TOP("top"),
|
||||
BOTTOM("bottom"), ;
|
||||
override fun getSerializedName(): String = label
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HALF, Half.BOTTOM)
|
||||
.setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH)
|
||||
)
|
||||
}
|
||||
|
||||
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
|
||||
return if(state.getValue(HALF) == Half.BOTTOM) {
|
||||
pos.above()
|
||||
} else {
|
||||
pos.below()
|
||||
}
|
||||
}
|
||||
|
||||
fun getBasePosition(state: BlockState, pos: BlockPos): BlockPos {
|
||||
return if (isBase(state)) {
|
||||
pos
|
||||
} else {
|
||||
pos.below()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBase(state: BlockState): Boolean = state.getValue(HALF) == Half.BOTTOM
|
||||
|
||||
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
|
||||
level.setBlock(pos.above(),state.setValue(HALF, Half.TOP), 3)
|
||||
level.blockUpdated(pos, Blocks.AIR)
|
||||
state.updateNeighbourShapes(level, pos, 3)
|
||||
}
|
||||
|
||||
override fun getDrops(state: BlockState, params: LootParams.Builder): List<ItemStack> {
|
||||
return if (state.getValue(HALF) == Half.TOP) emptyList() else super.getDrops(state, params)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val abovePosition = context.clickedPos.above()
|
||||
val world = context.level
|
||||
if (world.getBlockState(abovePosition).canBeReplaced(context) && !world.isOutsideBuildHeight(abovePosition)) {
|
||||
return defaultBlockState()
|
||||
.setValue(HorizontalDirectionalBlock.FACING, context.horizontalDirection)
|
||||
.setValue(HALF, Half.BOTTOM)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
if (!world.isClientSide && player.isCreative) {
|
||||
var blockPos: BlockPos = BlockPos.ZERO
|
||||
var blockState: BlockState = state
|
||||
val part = state.getValue(HALF)
|
||||
if (part == Half.TOP && world.getBlockState(pos.below().also { blockPos = it }).also { blockState = it }.`is`(state.block) && blockState.getValue(HALF) == Half.BOTTOM) {
|
||||
val blockState2 = if (blockState.fluidState.`is`(Fluids.WATER)) Blocks.WATER.defaultBlockState() else Blocks.AIR.defaultBlockState()
|
||||
world.setBlock(blockPos, blockState2, UPDATE_ALL or UPDATE_SUPPRESS_DROPS)
|
||||
world.levelEvent(player, LevelEvent.PARTICLES_DESTROY_BLOCK, blockPos, getId(blockState))
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(world, pos, state, player)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun isPathfindable(
|
||||
blockState: BlockState,
|
||||
pathComputationType: PathComputationType
|
||||
): Boolean = false
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(HorizontalDirectionalBlock.FACING)
|
||||
builder.add(HALF)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun rotate(blockState: BlockState, rotation: Rotation) =
|
||||
blockState.setValue(
|
||||
HorizontalDirectionalBlock.FACING, rotation.rotate(blockState.getValue(
|
||||
HorizontalDirectionalBlock.FACING)))
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun mirror(blockState: BlockState, mirror: Mirror): BlockState {
|
||||
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
|
||||
}
|
||||
|
||||
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
|
||||
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: LevelAccessor,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
val isThis = neighborState.`is`(this)
|
||||
val part = state.getValue(HALF)
|
||||
if (!isThis && part == Half.TOP && neighborPos == pos.below()) {
|
||||
return Blocks.AIR.defaultBlockState()
|
||||
} else if (!isThis && part == Half.BOTTOM && neighborPos == pos.above()) {
|
||||
return Blocks.AIR.defaultBlockState()
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class ScarekrowBlock(properties: Properties): DoubleBlock(properties) {
|
||||
|
||||
companion object {
|
||||
val POSE = EnumProperty.create("pose", Pose::class.java)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HALF, Half.BOTTOM)
|
||||
.setValue(POSE, Pose.DEFAULT)
|
||||
.setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(POSE)
|
||||
super.createBlockStateDefinition(builder)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun isPathfindable(
|
||||
blockState: BlockState,
|
||||
pathComputationType: PathComputationType
|
||||
): Boolean = false
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (player !is ServerPlayer) return InteractionResult.SUCCESS
|
||||
|
||||
level.setBlock(pos, state.cycle(POSE), 3)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
enum class Pose(private val label: String): StringRepresentable {
|
||||
DEFAULT("default"),
|
||||
RELAXED("relaxed"),
|
||||
SCARY("scary");
|
||||
override fun getSerializedName(): String = label
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return super.getShape(state, level, pos, context)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -441,6 +441,9 @@ class CobblemonConfig {
|
||||
@CobblemonConfigField(Category.Spawning, lang = "data_monitor_shiny_rate", SERVER)
|
||||
var monitorShinyRate = 200
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "scarekrow_radius", SERVER)
|
||||
var scarekrowRadius = 16
|
||||
|
||||
@Transient
|
||||
val struct = ObjectValue(this).also {
|
||||
it.addFunction("last_saved_version") { StringValue(lastSavedVersion) }
|
||||
|
||||
@@ -364,7 +364,7 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.SACCHARINE_HANGING_SIGN)
|
||||
entries.accept(CobblemonItems.SACCHARINE_LEAVES)
|
||||
//entries.accept(CobblemonItems.SACCHARINE_SAPLING)
|
||||
|
||||
entries.accept(CobblemonItems.SCAREKROW)
|
||||
entries.accept(CobblemonItems.HEARTY_GRAIN_BALE)
|
||||
|
||||
entries.accept(CobblemonItems.TATAMI_BLOCK)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"variants": {
|
||||
"facing=south,half=top": {
|
||||
"model": "cobblemon:block/pc_top"
|
||||
},
|
||||
"facing=west,half=top": {
|
||||
"model": "cobblemon:block/pc_top",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=top": {
|
||||
"model": "cobblemon:block/pc_top",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=top": {
|
||||
"model": "cobblemon:block/pc_top",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south,half=bottom": {
|
||||
"model": "cobblemon:block/pc_bottom"
|
||||
},
|
||||
"facing=west,half=bottom": {
|
||||
"model": "cobblemon:block/pc_bottom",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=bottom": {
|
||||
"model": "cobblemon:block/pc_bottom",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=bottom": {
|
||||
"model": "cobblemon:block/pc_bottom",
|
||||
"y": 270
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "block/cube_all",
|
||||
"textures": {
|
||||
"all": "cobblemon:block/evolution/moon_stone_ore"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "block/cube_all",
|
||||
"textures": {
|
||||
"all": "cobblemon:block/evolution/moon_stone_ore"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "block/cube_all",
|
||||
"textures": {
|
||||
"all": "cobblemon:block/evolution/moon_stone_ore"
|
||||
}
|
||||
}
|
||||
+16
@@ -71,6 +71,22 @@
|
||||
"senses": [
|
||||
"cobblemon:pokemon_nearby_wanted_item"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_senses",
|
||||
"condition": "!q.entity.is_in_party && !q.entity.is_alpha",
|
||||
"senses": [
|
||||
"cobblemon:scarekrow_sense"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_objectives",
|
||||
"condition": "!q.entity.is_in_party && !q.entity.is_alpha",
|
||||
"objectivesByPriority": {
|
||||
"0": [
|
||||
"cobblemon:avoid_scarekrow"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1.0,
|
||||
"bonus_rolls": 0.0,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:scarekrow"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:survives_explosion"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"cobblemon:scarekrow"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user