Merge branch 'main' into implement_pokemon
@@ -329,6 +329,7 @@
|
||||
- Clicking the summary interface exit button while the swap moves or evolve screen is open will cause the interface to switch back to the party screen. The button will exit the interface otherwise.
|
||||
|
||||
### Fixes
|
||||
- Fixed awarding Pokémon experience upon forfeiting battles.
|
||||
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating.
|
||||
- Fixed Cubone's cry not having a sound.
|
||||
- Fixed the sendout sound erroneously playing when a wild Pokémon breaks out of a Poké ball.
|
||||
@@ -406,6 +407,7 @@
|
||||
- Fixed aspect tracking for Advancements.
|
||||
- Fixed illusion not copying aspects or caught ball.
|
||||
- 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.
|
||||
|
||||
### Developer
|
||||
- `SpawnCause` is now an implementation of `SpawningInfluence`.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ItemProperties.class)
|
||||
public class ItemPropertiesMixin {
|
||||
|
||||
// Mixins into the fishing rod's register() lambda expression
|
||||
// Without this, the fishing rod item model appears cast when using a PokeRod in the other hand
|
||||
@Inject(method = "method_27883", at = @At(value = "RETURN", ordinal = 1), cancellable = true)
|
||||
private static void cobblemon$preventRodMixup(ItemStack itemStack, ClientLevel clientLevel, LivingEntity livingEntity, int i, CallbackInfoReturnable<Float> cir) {
|
||||
if (!(livingEntity instanceof Player) || ((Player) livingEntity).fishing instanceof PokeRodFishingBobberEntity) {
|
||||
cir.setReturnValue(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ object CobblemonCommands {
|
||||
BedrockParticleCommand.register(dispatcher)
|
||||
OpenDialogueCommand.register(dispatcher)
|
||||
NPCEditCommand.register(dispatcher)
|
||||
NPCDeleteCommand.register(dispatcher)
|
||||
AbandonMultiTeam.register(dispatcher)
|
||||
FreezePokemonCommand.register(dispatcher)
|
||||
ApplyPlayerTextureCommand.register(dispatcher)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.VillagerCalmDown
|
||||
|
||||
class CalmDownTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
return VillagerCalmDown.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class ExitBattleWhenHurtTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var includePassiveDamage = true
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
|
||||
if (entity is NPCEntity) {
|
||||
fun cancelNPCBattles(npcEntity: NPCEntity): Boolean {
|
||||
val battles = npcEntity.battleIds.mapNotNull(BattleRegistry::getBattle)
|
||||
battles.forEach { it.end() }
|
||||
return battles.isNotEmpty()
|
||||
}
|
||||
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.NPC_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } }
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.NPC_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelNPCBattles(entity as NPCEntity) } }
|
||||
}
|
||||
}
|
||||
} else { return null }/* else if (entity is PokemonEntity) { TODO uncomment when pokemon have brain :)
|
||||
fun cancelPokemonBattle(pokemonEntity: PokemonEntity): Boolean {
|
||||
val battle = BattleRegistry.getBattle(pokemonEntity.battleId ?: return false)
|
||||
battle?.end()
|
||||
return battle != null
|
||||
}
|
||||
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY), it.present(CobblemonMemories.POKEMON_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY), it.present(CobblemonMemories.POKEMON_BATTLING))
|
||||
.apply(it) { _, _ -> Trigger { world, entity, _ -> return@Trigger cancelPokemonBattle(entity as PokemonEntity) } }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class FleeAttackerTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var speedMultiplier = "0.5".asExpression()
|
||||
var desiredDistance = "9".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
|
||||
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
|
||||
val desiredDistance = runtime.resolveInt(desiredDistance)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
class FleeNearestHostileTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var speedMultiplier = "0.5".asExpression()
|
||||
var desiredDistance = "9".asExpression()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition) || entity !is PathfinderMob) return null
|
||||
val speedMultiplier = runtime.resolveFloat(speedMultiplier)
|
||||
val desiredDistance = runtime.resolveInt(desiredDistance)
|
||||
return WrapperLivingEntityTask(
|
||||
SetWalkTargetAwayFrom.entity(MemoryModuleType.NEAREST_HOSTILE, speedMultiplier, desiredDistance, false),
|
||||
PathfinderMob::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -89,20 +89,24 @@ on the idle activity so that it automatically transitions activities when a batt
|
||||
### switch_npc_from_battle
|
||||
The 'switch_npc_from_battle' type creates a task that will switch NPC entities from the battle activity when it is no longer in a battle. You should
|
||||
put this in the battle activity so that it automatically transitions activities when a battle ends. If the NPC is in multiple battles, this
|
||||
will only activate when no battles remain.
|
||||
- `activity`: The activity to switch to when the NPC is no longer in a battle. Defaults to `minecraft:idle`.
|
||||
will only activate when no battles remain. It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### look_at_battling_pokemon
|
||||
The 'look_at_battling_pokemon' type creates a task that will set the entity's look target to switch between the battling Pokémon.
|
||||
- `minDurationTicks`: A MoLang expression that determines the minimum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 40.
|
||||
- `maxDurationTicks`: A MoLang expression that determines the maximum number of ticks that the entity will look at a specific battling Pokémon. Defaults to 80.
|
||||
|
||||
### exit_battle_when_hurt
|
||||
The 'exit_battle_when_hurt' type creates a task that will make the entity exit the battle when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to true.
|
||||
|
||||
### switch_to_chatting
|
||||
The 'switch_to_chatting' type creates a task that will switch the entity to the chatting activity when a dialogue has been opened with that NPC.
|
||||
|
||||
### switch_from_chatting
|
||||
The 'switch_from_chatting' type creates a task that will switch the entity from the chatting activity when the dialogue has been closed with that NPC.
|
||||
- `activity`: The activity to switch to when the NPC is no longer chatting. Defaults to `minecraft:idle`.
|
||||
It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### look_at_speaker
|
||||
The 'look_at_speaker' type creates a task that will set the entity's look target to the speaker of the dialogue.
|
||||
@@ -112,7 +116,7 @@ The 'switch_to_action_effect' type creates a task that will switch the entity to
|
||||
|
||||
### switch_from_action_effect
|
||||
The 'switch_from_action_effect' type creates a task that will switch the entity from the action effect activity when it is no longer performing an action effect.
|
||||
- `activity`: The activity to switch to when the NPC is no longer performing an action effect. Defaults to `minecraft:idle`.
|
||||
It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### switch_to_fight
|
||||
The 'switch_to_fight' type creates a task that will switch the entity to the fight activity when it has an attack target.
|
||||
@@ -120,8 +124,7 @@ The 'switch_to_fight' type creates a task that will switch the entity to the fig
|
||||
- `activity`: The activity to switch to when the NPC is in a fight. Defaults to `minecraft:fight`.
|
||||
|
||||
### switch_from_fight
|
||||
The 'switch_from_fight' type creates a task that will switch the entity from the fight activity when it no longer has an attack target.
|
||||
- `activity`: The activity to switch to when the NPC is no longer in a fight. Defaults to `minecraft:idle`.
|
||||
The 'switch_from_fight' type creates a task that will switch the entity from the fight activity when it no longer has an attack target. It will rely on the brain schedule to decide what activity to switch back to (usually idle).
|
||||
|
||||
### get_angry_at_attacker
|
||||
The 'get_angry_at_attacker' type creates a task that will make the entity get angry at the attacker when it is attacked.
|
||||
@@ -143,4 +146,30 @@ The 'move_to_attack_target' type creates a task that will make the entity move t
|
||||
The 'melee_attack' type creates a task that will make the entity perform a melee attack on its attack target when it is close enough. The damage dealt is determined by entity properties, which cannot be easily configured at time of writing.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `range`: A MoLang expression that determines the maximum distance from the attack target that the entity can be to perform the attack. Defaults to 1.5.
|
||||
- `cooldownTicks`: A MoLang expression that determines the number of ticks to wait before performing another attack. Defaults to 30.
|
||||
- `cooldownTicks`: A MoLang expression that determines the number of ticks to wait before performing another attack. Defaults to 30.
|
||||
|
||||
### switch_to_panic_when_hurt
|
||||
The 'switch_to_panic_when_hurt' type creates a task that will switch the entity to the panic activity when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `includePassiveDamage`: A boolean that determines if passive damage (like poison or cacti) should trigger this task. Defaults to false.
|
||||
|
||||
### switch_to_panic_when_hostiles_nearby
|
||||
The 'switch_to_panic_when_hostiles_nearby' type creates a task that will switch the entity to the panic activity when hostiles are nearby. The definition of nearby depends
|
||||
on the entity type and this piggy-backs off the Minecraft list of aggressive mobs and the right distances.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### calm_down
|
||||
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
|
||||
### flee_attacker
|
||||
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
|
||||
|
||||
### flee_nearest_hostile
|
||||
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
|
||||
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
|
||||
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the nearest hostile. Defaults to 0.5 which is an elevated speed to usual.
|
||||
- `desiredDistance`: A MoLang expression that determines the distance from the nearest hostile that the entity should try to maintain. Defaults to 9.
|
||||
@@ -8,15 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.npc.ai.SwitchFromActionEffectTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
|
||||
class SwitchFromActionEffectTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = SwitchFromActionEffectTask.create(activity)
|
||||
) = BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
).apply(it) { _ ->
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,8 @@ import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchFromChattingTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
@@ -24,8 +22,8 @@ class SwitchFromChattingTaskConfig : SingleTaskConfig {
|
||||
it.group(
|
||||
it.absent(CobblemonMemories.DIALOGUES)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,8 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchFromFightTaskConfig : SingleTaskConfig {
|
||||
val activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
@@ -24,8 +22,8 @@ class SwitchFromFightTaskConfig : SingleTaskConfig {
|
||||
it.group(
|
||||
it.absent(MemoryModuleType.ATTACK_TARGET)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.npc.ai.SwitchFromBattleTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
|
||||
class SwitchFromNPCBattleTaskConfig : SingleTaskConfig {
|
||||
val activity: Activity = Activity.IDLE
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
return SwitchFromBattleTask.create(activity)
|
||||
return BehaviorBuilder.create {
|
||||
it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ ->
|
||||
Trigger { level, entity, _ ->
|
||||
entity.brain.updateActivityFromSchedule(level.dayTime, level.gameTime)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchToPanicWhenHostilesNearbyTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
) = BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.NEAREST_HOSTILE))
|
||||
.apply(it) { nearestHostile ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.ai.BrainConfigurationContext
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
class SwitchToPanicWhenHurtTaskConfig : SingleTaskConfig {
|
||||
var condition = "true".asExpression()
|
||||
var includePassiveDamage = false
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
brainConfigurationContext: BrainConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
runtime.withQueryValue("entity", (entity as? PosableEntity)?.struct ?: QueryStruct(hashMapOf()))
|
||||
if (!runtime.resolveBoolean(condition)) return null
|
||||
return if (includePassiveDamage) {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY))
|
||||
.apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BehaviorBuilder.create {
|
||||
it.group(it.present(MemoryModuleType.HURT_BY_ENTITY))
|
||||
.apply(it) { _ ->
|
||||
Trigger { world, entity, _ ->
|
||||
(entity as? PathfinderMob)?.navigation?.stop()
|
||||
entity.brain.setActiveActivityIfPossible(Activity.PANIC)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,12 @@ fun interface TaskConfig {
|
||||
"look_at_speaker" to LookAtSpeakerTaskConfig::class.java,
|
||||
"switch_to_action_effect" to SwitchToActionEffectTaskConfig::class.java,
|
||||
"switch_from_action_effect" to SwitchFromActionEffectTaskConfig::class.java,
|
||||
"exit_battle_when_hurt" to ExitBattleWhenHurtTaskConfig::class.java,
|
||||
"switch_to_panic_when_hurt" to SwitchToPanicWhenHurtTaskConfig::class.java,
|
||||
"switch_to_panic_when_hostiles_nearby" to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
|
||||
"calm_down" to CalmDownTaskConfig::class.java,
|
||||
"flee_attacker" to FleeAttackerTaskConfig::class.java,
|
||||
"flee_nearest_hostile" to FleeNearestHostileTaskConfig::class.java,
|
||||
)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.battles.model
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
@@ -22,7 +21,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
@@ -51,17 +49,16 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.network.chat.Component
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -271,22 +268,31 @@ open class PokemonBattle(
|
||||
else -> continue
|
||||
}
|
||||
val experience = Cobblemon.experienceCalculator.calculate(opponentPokemon, faintedPokemon, multiplier)
|
||||
if (experience > 0) {
|
||||
if (experience > 0 && actor.pokemonList.all { it.health <= 0 }) {
|
||||
opponent.awardExperience(opponentPokemon, experience)
|
||||
}
|
||||
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
|
||||
pokemon.evs.add(stat, amount)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) {
|
||||
if (actor.itemsUsed.isNotEmpty() && actor.getPlayerUUIDs().count() > 0) {
|
||||
val player = actor.getPlayerUUIDs().first().getPlayer()
|
||||
player?.level()?.itemRegistry.let { registry ->
|
||||
actor.itemsUsed.mapNotNull { registry?.get(ResourceLocation.tryBySeparator(it.itemName.substringAfter('.'), '.')) }
|
||||
.forEach { player?.giveOrDropItemStack(ItemStack(it))}
|
||||
}
|
||||
}
|
||||
|
||||
// Heal NPC's team if enabled
|
||||
if (actor is NPCBattleActor) {
|
||||
if (actor.npc.npc.autoHealParty) {
|
||||
actor.npc.party?.heal()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Heal Mon if wild
|
||||
actors.filter { it.type == ActorType.WILD }
|
||||
|
||||
@@ -40,6 +40,7 @@ abstract class BattleActor(
|
||||
|
||||
lateinit var showdownId: String
|
||||
lateinit var battle: PokemonBattle
|
||||
fun isInitialized() = this::battle.isInitialized && this::showdownId.isInitialized
|
||||
|
||||
val activePokemon = mutableListOf<ActiveBattlePokemon>()
|
||||
var canDynamax = false
|
||||
|
||||
@@ -100,7 +100,7 @@ object CobblemonEvents {
|
||||
|
||||
// instructions
|
||||
@JvmField
|
||||
val MEGA_EVOLUTON = EventObservable<MegaEvolutionEvent>()
|
||||
val MEGA_EVOLUTION = EventObservable<MegaEvolutionEvent>()
|
||||
@JvmField
|
||||
val TERASTALLIZATION = EventObservable<TerastallizationEvent>()
|
||||
@JvmField
|
||||
|
||||
@@ -10,16 +10,14 @@ package com.cobblemon.mod.common.api.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitEffectFunctionRegistryEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait.Effect
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.codec.kotlinOptionalFieldOf
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import java.util.Optional
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -36,11 +34,13 @@ data class FishingBait(
|
||||
val chance: Double = 0.0,
|
||||
val value: Double = 0.0
|
||||
) {
|
||||
constructor(type: ResourceLocation, subcategory: Optional<ResourceLocation>, chance: Double, value: Double) : this(type, subcategory.orElse(null), chance, value)
|
||||
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<Effect> { instance ->
|
||||
instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("type").forGetter { it.type },
|
||||
ResourceLocation.CODEC.kotlinOptionalFieldOf("subcategory").forGetter { it.subcategory },
|
||||
ResourceLocation.CODEC.optionalFieldOf("subcategory").forGetter { Optional.ofNullable(it.subcategory) },
|
||||
Codec.DOUBLE.fieldOf("chance").forGetter { it.chance },
|
||||
Codec.DOUBLE.fieldOf("value").forGetter { it.value }
|
||||
).apply(instance, ::Effect)
|
||||
|
||||
@@ -27,9 +27,16 @@ 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.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
|
||||
@@ -43,6 +50,7 @@ import java.util.UUID
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.DoubleTag
|
||||
@@ -125,15 +133,12 @@ object MoLangFunctions {
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("username") { _ -> StringValue(player.gameProfile.name) }
|
||||
map.put("uuid") { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map.put("data") { _ -> return@put if (player is ServerPlayer) Cobblemon.molangData.load(player.uuid) else DoubleValue(0) }
|
||||
map.put("save_data") { _ -> if (player is ServerPlayer) Cobblemon.molangData.save(player.uuid) else DoubleValue(0) }
|
||||
map.put("main_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.mainHandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("off_held_item") { _ -> player.level().itemRegistry.wrapAsHolder(player.offhandItem.item).asMoLangValue(Registries.ITEM) }
|
||||
map.put("face") { params -> ObjectValue(PlayerDialogueFaceProvider(player.uuid, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("swing_hand") { _ -> player.swing(player.usedItemHand) }
|
||||
map.put("food_level") { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
map.put("saturation_level") { _ -> DoubleValue(player.foodData.saturationLevel) }
|
||||
// map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, Permission)) }
|
||||
map.put("tell") { params ->
|
||||
val message = params.getString(0).text()
|
||||
val overlay = params.getBooleanOrNull(1) ?: false
|
||||
@@ -176,6 +181,13 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("party") { player.party().struct }
|
||||
map.put("pc") { player.pc().struct }
|
||||
map.put("has_permission") { params -> DoubleValue(Cobblemon.permissionValidator.hasPermission(player, params.getString(0), params.getIntOrNull(1) ?: 4)) }
|
||||
map.put("data") { _ -> Cobblemon.molangData.load(player.uuid) }
|
||||
map.put("save_data") { _ -> Cobblemon.molangData.save(player.uuid) }
|
||||
map.put("in_battle") { DoubleValue(player.isInBattle()) }
|
||||
map.put("battle") { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
}
|
||||
map
|
||||
}
|
||||
@@ -255,6 +267,17 @@ object MoLangFunctions {
|
||||
map.put("has_aspect") { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) }
|
||||
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
|
||||
map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) }
|
||||
map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map.put("is_doing_activity") { params ->
|
||||
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
|
||||
val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) }
|
||||
if (activities.isNotEmpty()) {
|
||||
return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) })
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("run_script_on_client") { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
@@ -311,6 +334,8 @@ object MoLangFunctions {
|
||||
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
|
||||
}
|
||||
map.put("environment") { _ -> npc.runtime.environment }
|
||||
map.put("party") { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
map.put("has_party") { DoubleValue(npc.party != null) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -329,6 +354,7 @@ object MoLangFunctions {
|
||||
val actor = battle.actors.find { it.uuid == uuid } ?: return@put DoubleValue.ZERO
|
||||
return@put actor.struct
|
||||
}
|
||||
map.put("stop") { _ -> battle.stop() }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -336,10 +362,30 @@ object MoLangFunctions {
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("id") { StringValue(pokemon.uuid.toString()) }
|
||||
map.put("level") { DoubleValue(pokemon.level.toDouble()) }
|
||||
map.put("max_hp") { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
map.put("current_hp") { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
map.put("friendship") { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
map.put("evs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("ivs") {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map.put("is_wild") { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map.put("is_shiny") { DoubleValue(pokemon.shiny) }
|
||||
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)) }
|
||||
map
|
||||
}
|
||||
)
|
||||
@@ -355,21 +401,140 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonStoreFunctions = mutableListOf<(PokemonStore<*>) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ store ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("add") { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0)
|
||||
return@put DoubleValue(store.add(pokemon.obj))
|
||||
}
|
||||
map.put("add_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val player = (store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
|
||||
val pokemon = props.create(player = player)
|
||||
return@put DoubleValue(store.add(pokemon))
|
||||
}
|
||||
map.put("find_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.find { props.matches(it) }
|
||||
return@put pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("find_all_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.filter { props.matches(it) }
|
||||
return@put ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap())
|
||||
}
|
||||
map.put("find_by_id") { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id }
|
||||
return@put pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("remove_by_id") { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id } ?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(store.remove(pokemon))
|
||||
}
|
||||
map.put("average_level") { _ ->
|
||||
var numberOfPokemon = 0
|
||||
var totalLevel = 0
|
||||
for (pokemon in store) {
|
||||
totalLevel += pokemon.level
|
||||
numberOfPokemon++
|
||||
}
|
||||
if (numberOfPokemon == 0) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
return@put DoubleValue(totalLevel.toDouble() / numberOfPokemon)
|
||||
}
|
||||
map.put("count") { _ -> DoubleValue(store.count()) }
|
||||
map.put("count_by_properties") { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
return@put DoubleValue(store.count { props.matches(it) })
|
||||
}
|
||||
map.put("highest_level") {
|
||||
val highest = store.maxOfOrNull { it.level } ?: 0
|
||||
return@put DoubleValue(highest)
|
||||
}
|
||||
map.put("lowest_level") {
|
||||
val lowest = store.minOfOrNull { it.level } ?: 0
|
||||
return@put DoubleValue(lowest)
|
||||
}
|
||||
map.put("heal") {
|
||||
for (pokemon in store) {
|
||||
pokemon.heal()
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("healing_remainder_percent") { _ ->
|
||||
var totalPercent = 0.0f
|
||||
for (pokemon in store) {
|
||||
totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth))
|
||||
}
|
||||
DoubleValue(totalPercent)
|
||||
}
|
||||
map.put("has_usable_pokemon") { _ -> DoubleValue(store.any { !it.isFainted() }) }
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
val partyFunctions = mutableListOf<(PartyStore) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ party ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("get_pokemon") { params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = party.get(index) ?: return@put DoubleValue.ZERO
|
||||
val struct = VariableStruct()
|
||||
pokemon.writeVariables(struct)
|
||||
return@put struct
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("healing_remainder_percent") { _ -> DoubleValue(party.getHealingRemainderPercent()) }
|
||||
|
||||
return@mutableListOf map
|
||||
})
|
||||
|
||||
val pcFunctions = mutableListOf<(PCStore) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pc ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("get_pokemon") { params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = pc[PCPosition(box, slot)] ?: return@put DoubleValue.ZERO
|
||||
return@put pokemon.struct
|
||||
}
|
||||
map.put("resize") { params ->
|
||||
val newSize = params.getInt(0)
|
||||
val lockNewSize = params.getBooleanOrNull(1) == true
|
||||
pc.resize(newSize, lockNewSize)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("get_box_count") { _ -> DoubleValue(pc.boxes.size.toDouble()) }
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val spawningContextFunctions = mutableListOf<(SpawningContext) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ spawningContext ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
val biomeValue = spawningContext.biomeRegistry.wrapAsHolder(spawningContext.biome).asBiomeMoLangValue()
|
||||
map.put("biome") { _ -> biomeValue }
|
||||
map.put("world") { _ -> worldValue }
|
||||
map.put("light") { _ -> DoubleValue(spawningContext.light.toDouble()) }
|
||||
map.put("x") { _ -> DoubleValue(spawningContext.position.x.toDouble()) }
|
||||
map.put("y") { _ -> DoubleValue(spawningContext.position.y.toDouble()) }
|
||||
map.put("z") { _ -> DoubleValue(spawningContext.position.z.toDouble()) }
|
||||
map.put("moon_phase") { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
map.put("can_see_sky") { _ -> DoubleValue(spawningContext.canSeeSky) }
|
||||
map.put("sky_light") { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
map.put("bucket") { _ -> StringValue(spawningContext.cause.bucket.name) }
|
||||
map.put("player") { _ ->
|
||||
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) {
|
||||
return@put causeEntity.asMoLangValue()
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
)
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME).addFunctions(biomeFunctions)
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION).addFunctions(worldFunctions)
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK).addFunctions(blockFunctions)
|
||||
@@ -384,6 +549,33 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
// We need to migrate the writeVariables thing to be all about query structs, variable doesn't make sense and I don't want to break fringe 1.6 compatibility issues close to release
|
||||
fun Pokemon.asStruct(): ObjectValue<Pokemon> {
|
||||
val queryStruct = ObjectValue(this)
|
||||
queryStruct.addFunctions(pokemonFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return queryStruct
|
||||
}
|
||||
|
||||
fun PartyStore.asMoLangValue(): ObjectValue<PartyStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(partyFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(pokemonStoreFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
value.addFunctions(pcFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
@@ -403,6 +595,15 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun SpawningContext.asMoLangValue(): ObjectValue<SpawningContext> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(spawningContextFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
|
||||
@@ -51,6 +51,9 @@ class NPCClass {
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var ai: MutableList<BrainConfig> = mutableListOf()
|
||||
var isMovable: Boolean = true
|
||||
var isInvulnerable = false
|
||||
var isLeashable = true
|
||||
var allowProjectileHits = true
|
||||
|
||||
// If you're adding stuff here, add it to NPCPreset and NPCClassAdapter too
|
||||
|
||||
@@ -86,6 +89,9 @@ class NPCClass {
|
||||
}
|
||||
buffer.writeNullable(battleTheme) { _, v -> buffer.writeIdentifier(v) }
|
||||
buffer.writeBoolean(isMovable)
|
||||
buffer.writeBoolean(isInvulnerable)
|
||||
buffer.writeBoolean(isLeashable)
|
||||
buffer.writeBoolean(allowProjectileHits)
|
||||
}
|
||||
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -132,5 +138,8 @@ class NPCClass {
|
||||
}
|
||||
battleTheme = buffer.readNullable { buffer.readIdentifier() }
|
||||
isMovable = buffer.readBoolean()
|
||||
isInvulnerable = buffer.readBoolean()
|
||||
isLeashable = buffer.readBoolean()
|
||||
allowProjectileHits = buffer.readBoolean()
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,9 @@ class NPCPreset {
|
||||
var autoHealParty: Boolean? = null
|
||||
var battleTheme: ResourceLocation? = null
|
||||
var isMovable: Boolean? = null
|
||||
var isInvulnerable: Boolean? = null
|
||||
var isLeashable: Boolean? = null
|
||||
var allowProjectileHits: Boolean? = null
|
||||
|
||||
fun applyTo(npcClass: NPCClass) {
|
||||
resourceIdentifier?.let { npcClass.resourceIdentifier = it }
|
||||
@@ -53,5 +56,8 @@ class NPCPreset {
|
||||
battleTheme?.let { npcClass.battleTheme = it }
|
||||
ai?.let { npcClass.ai.addAll(it) }
|
||||
isMovable?.let { npcClass.isMovable = it }
|
||||
isInvulnerable?.let { npcClass.isInvulnerable = it }
|
||||
isLeashable?.let { npcClass.isLeashable = it }
|
||||
allowProjectileHits?.let { npcClass.allowProjectileHits = it }
|
||||
}
|
||||
}
|
||||
@@ -349,6 +349,21 @@ Whether or not the NPC's party will be healed between battles. If the NPC has a
|
||||
do nothing. If the NPC has a static party and this option is false, their Pokémon will not be healed and the
|
||||
NPC will have to use healing machines to heal them if they have that enabled in their AI.
|
||||
|
||||
### isMovable
|
||||
Controls whether the NPC can be moved by players or other entities. If disabled, the NPC will remain
|
||||
stationary.
|
||||
|
||||
### isInvulnerable
|
||||
Controls whether the NPC is immune to all damage sources.
|
||||
|
||||
### isLeashable
|
||||
Controls whether players can attach a lead to a NPC. If disabled, players will be unable to leash
|
||||
the NPC and move them using leads.
|
||||
|
||||
### allowProjectileHits
|
||||
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.
|
||||
|
||||
### 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).
|
||||
@@ -76,6 +76,7 @@ object CobblemonPermissions {
|
||||
val POKEDEX = this.create("${COMMAND_PREFIX}pokedex", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
val NPC_EDIT = this.create("${COMMAND_PREFIX}npcedit", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
val NPC_DELETE = this.create("${COMMAND_PREFIX}npcdelete", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
val FREEZE_POKEMON = this.create("${COMMAND_PREFIX}freezepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
val APPLY_PLAYER_TEXTURE = this.create("${COMMAND_PREFIX}applyplayertexture", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
|
||||
|
||||
|
||||
@@ -22,6 +22,11 @@ enum class PermissionLevel(val numericalValue: Int) {
|
||||
SPAWN_PROTECTION_BYPASS(1),
|
||||
CHEAT_COMMANDS_AND_COMMAND_BLOCKS(2),
|
||||
MULTIPLAYER_MANAGEMENT(3),
|
||||
ALL_COMMANDS(4)
|
||||
ALL_COMMANDS(4);
|
||||
|
||||
companion object {
|
||||
fun byNumericValue(value: Int): PermissionLevel {
|
||||
return entries.first { it.numericalValue == value }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ interface PermissionValidator {
|
||||
|
||||
/**
|
||||
* Invoked when the validator replaces the existing one in [Cobblemon.permissionValidator].
|
||||
*
|
||||
*/
|
||||
fun initialize()
|
||||
|
||||
@@ -36,6 +35,14 @@ interface PermissionValidator {
|
||||
* @return If the [player] has the [permission].
|
||||
*/
|
||||
fun hasPermission(player: ServerPlayer, permission: Permission): Boolean
|
||||
/**
|
||||
* Validates a permission for [ServerPlayer] based only on a permission string and a permission level.
|
||||
*
|
||||
* @param player The target [ServerPlayer].
|
||||
* @param permission The permission string being queried such as cobblemon.command.giveallpokemon.
|
||||
* @param level The permission level being queried. 4 is generally used for cheats.
|
||||
*/
|
||||
fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean = hasPermission(player, CobblemonPermission(permission, PermissionLevel.byNumericValue(level)))
|
||||
|
||||
/**
|
||||
* Validates a permission for [CommandSource].
|
||||
@@ -45,5 +52,12 @@ interface PermissionValidator {
|
||||
* @return If the [source] has the [permission].
|
||||
*/
|
||||
fun hasPermission(source: CommandSourceStack, permission: Permission): Boolean
|
||||
|
||||
/**
|
||||
* Validates a permission for [CommandSourceStack] based only on a permission string and a permission level.
|
||||
*
|
||||
* @param source The target [CommandSourceStack].
|
||||
* @param permission The permission string being queried such as cobblemon.command.giveallpokemon.
|
||||
* @param level The permission level being queried. 4 is generally used for cheats.
|
||||
*/
|
||||
fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean = hasPermission(source, CobblemonPermission(permission, PermissionLevel.byNumericValue(level)))
|
||||
}
|
||||
@@ -133,7 +133,7 @@ object CatchRateModifiers {
|
||||
*/
|
||||
val LURE = WorldStateModifier { _, entity ->
|
||||
if (FishingSpawnCause.FISHED_ASPECT in entity.aspects)
|
||||
4f
|
||||
4F
|
||||
else
|
||||
1F
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
@@ -69,6 +72,9 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.create()
|
||||
|
||||
val presetTypes = mutableMapOf<String, Class<out SpawnDetailPreset>>()
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropMethod
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
@@ -68,6 +71,9 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
.registerTypeAdapter(NPCClass::class.java, NPCClassReferenceAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.create()
|
||||
|
||||
var deserializingConditionClass: Class<out SpawningCondition<*>>? = null
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.context
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
@@ -55,6 +56,10 @@ abstract class SpawningContext {
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
val runtime = MoLangRuntime().setup()
|
||||
.also { runtime -> runtime.environment.query.addFunction("context") { getOrSetupStruct() } }
|
||||
|
||||
/** What caused the spawn context, as a [SpawnCause]. */
|
||||
abstract val cause: SpawnCause
|
||||
val spawner: Spawner
|
||||
@@ -87,7 +92,7 @@ abstract class SpawningContext {
|
||||
val biomeName: ResourceLocation
|
||||
get() = this.biomeRegistry.getKey(biome)!!
|
||||
|
||||
private val struct = VariableStruct()
|
||||
private var struct = QueryStruct(hashMapOf())
|
||||
private var structCompiled = false
|
||||
|
||||
class StructureChunkCache {
|
||||
@@ -168,19 +173,11 @@ abstract class SpawningContext {
|
||||
return weight
|
||||
}
|
||||
|
||||
fun getOrSetupStruct(): VariableStruct {
|
||||
fun getOrSetupStruct(): QueryStruct {
|
||||
if (structCompiled) {
|
||||
return struct
|
||||
}
|
||||
|
||||
struct.setDirectly("light", DoubleValue(light.toDouble()))
|
||||
struct.setDirectly("x", DoubleValue(position.x.toDouble()))
|
||||
struct.setDirectly("y", DoubleValue(position.y.toDouble()))
|
||||
struct.setDirectly("z", DoubleValue(position.z.toDouble()))
|
||||
struct.setDirectly("moon_phase", DoubleValue(moonPhase.toDouble()))
|
||||
struct.setDirectly("world", ObjectValue(world.registryAccess().registryOrThrow(Registries.DIMENSION).wrapAsHolder(world)))
|
||||
struct.setDirectly("biome", ObjectValue(biomeRegistry.wrapAsHolder(biome)))
|
||||
|
||||
struct = this.asMoLangValue()
|
||||
structCompiled = true
|
||||
return struct
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
|
||||
/**
|
||||
* A [SpawnAction] for creating [NPCEntity]s.
|
||||
@@ -19,13 +19,15 @@ import net.minecraft.server.level.ServerPlayer
|
||||
* @author Hiroku
|
||||
* @since October 8th, 2023
|
||||
*/
|
||||
class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SpawnAction<EntitySpawnResult>(ctx, detail) {
|
||||
override fun run(): EntitySpawnResult {
|
||||
class NPCSpawnAction(ctx: SpawningContext, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(ctx, detail) {
|
||||
override fun createEntity(): NPCEntity {
|
||||
val npc = NPCEntity(ctx.world)
|
||||
npc.npc = detail.npcClass
|
||||
npc.appliedAspects.addAll(detail.aspects)
|
||||
val seedLevel = (ctx.cause.entity as? ServerPlayer)?.let { it.party().maxOfOrNull { it.level } } ?: 10
|
||||
val minLevel = ctx.runtime.resolveInt(detail.minLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val maxLevel = ctx.runtime.resolveInt(detail.maxLevel).coerceIn(1, Cobblemon.config.maxPokemonLevel)
|
||||
val seedLevel = (minLevel..maxLevel).random()
|
||||
npc.initialize(seedLevel)
|
||||
return EntitySpawnResult(listOf(npc))
|
||||
return npc
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.npc.NPCConfiguration
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
/**
|
||||
@@ -30,8 +31,9 @@ class NPCSpawnDetail : SpawnDetail() {
|
||||
|
||||
@SerializedName(value = "npcClass", alternate = ["class", "npc"])
|
||||
val npcClass: NPCClass = blankClass
|
||||
val configuration: NPCConfiguration? = null
|
||||
val aspects: Set<String> = emptySet()
|
||||
val minLevel: Expression = "1".asExpression()
|
||||
val maxLevel: Expression = "100".asExpression()
|
||||
|
||||
override fun doSpawn(ctx: SpawningContext) = NPCSpawnAction(ctx, this)
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.storage.party
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
@@ -25,11 +26,11 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.Collections
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Collections
|
||||
import net.minecraft.core.RegistryAccess
|
||||
|
||||
/**
|
||||
* A [PokemonStore] for a party of Pokémon. This is a simple structure that by default will hold 6 nullable slots of Pokémon.
|
||||
@@ -47,6 +48,8 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
/** A list of player UUIDs representing players that are observing this store. This is NOT serialized/deserialized. */
|
||||
var observerUUIDs = mutableListOf<UUID>()
|
||||
|
||||
val struct = asMoLangValue()
|
||||
|
||||
override fun iterator() = slots.filterNotNull().iterator()
|
||||
/** Gets the Pokémon at the specified slot. It will return null if the slot is empty or the given slot is out of bounds. */
|
||||
fun get(slot: Int) = slot.takeIf { it < slots.size && it >= 0 }?.let { slots[it] }
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.storage.pc
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.BottomlessStore
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
@@ -23,11 +24,11 @@ import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.*
|
||||
import net.minecraft.core.RegistryAccess
|
||||
|
||||
/**
|
||||
* The store used for PCs. It is divided into some number of [PCBox]es, and can
|
||||
@@ -54,6 +55,9 @@ open class PCStore(
|
||||
|
||||
override fun iterator() = boxes.flatMap { it.toList() }.iterator()
|
||||
override fun getObservingPlayers() = observingUUIDs.mapNotNull { it.getPlayer() }
|
||||
|
||||
val struct = asMoLangValue()
|
||||
|
||||
fun addObserver(player: ServerPlayer) {
|
||||
observingUUIDs.add(player.uuid)
|
||||
sendTo(player)
|
||||
|
||||
@@ -105,6 +105,9 @@ object BattleBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
player1Actor.battleTheme = player2.getBattleTheme()
|
||||
player2Actor.battleTheme = player1.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
var result: BattleStartResult = errors
|
||||
BattleRegistry.startBattle(
|
||||
@@ -112,8 +115,6 @@ object BattleBuilder {
|
||||
side1 = BattleSide(player1Actor),
|
||||
side2 = BattleSide(player2Actor)
|
||||
).ifSuccessful {
|
||||
player1Actor.battleTheme = player2.getBattleTheme()
|
||||
player2Actor.battleTheme = player1.getBattleTheme()
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
result = SuccessfulBattleStart(it)
|
||||
}
|
||||
@@ -197,19 +198,18 @@ object BattleBuilder {
|
||||
playerActors.swap(2,3)
|
||||
}
|
||||
|
||||
// TODO: less hard coding
|
||||
playerActors[0].battleTheme = players[2].getBattleTheme()
|
||||
playerActors[1].battleTheme = players[2].getBattleTheme()
|
||||
playerActors[2].battleTheme = players[0].getBattleTheme()
|
||||
playerActors[3].battleTheme = players[0].getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(playerActors[0], playerActors[1]),
|
||||
side2 = BattleSide(playerActors[2], playerActors[3])
|
||||
).ifSuccessful {
|
||||
// TODO: less hard coding
|
||||
playerActors[0].battleTheme = players[2].getBattleTheme()
|
||||
playerActors[1].battleTheme = players[2].getBattleTheme()
|
||||
|
||||
playerActors[2].battleTheme = players[0].getBattleTheme()
|
||||
playerActors[3].battleTheme = players[0].getBattleTheme()
|
||||
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
}
|
||||
errors
|
||||
@@ -272,6 +272,8 @@ object BattleBuilder {
|
||||
errors.participantErrors[wildActor] += BattleStartError.alreadyInBattle(wildActor)
|
||||
}
|
||||
|
||||
playerActor.battleTheme = pokemonEntity.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
var result: BattleStartResult = errors
|
||||
BattleRegistry.startBattle(
|
||||
@@ -282,7 +284,6 @@ object BattleBuilder {
|
||||
if (!cloneParties) {
|
||||
pokemonEntity.battleId = it.battleId
|
||||
}
|
||||
playerActor.battleTheme = pokemonEntity.getBattleTheme()
|
||||
result = SuccessfulBattleStart(it)
|
||||
}
|
||||
result
|
||||
@@ -347,6 +348,8 @@ object BattleBuilder {
|
||||
)
|
||||
}
|
||||
|
||||
playerActor.battleTheme = npcEntity.getBattleTheme()
|
||||
|
||||
return if (errors.isEmpty) {
|
||||
var result: BattleStartResult = errors
|
||||
BattleRegistry.startBattle(
|
||||
@@ -354,7 +357,6 @@ object BattleBuilder {
|
||||
side1 = BattleSide(playerActor),
|
||||
side2 = BattleSide(npcActor)
|
||||
).ifSuccessful { battle ->
|
||||
playerActor.battleTheme = npcEntity.getBattleTheme()
|
||||
npcEntity.entityData.update(NPCEntity.BATTLE_IDS) { it + battle.battleId }
|
||||
result = SuccessfulBattleStart(battle)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,12 @@ import java.util.UUID
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PlayerBattleActor(
|
||||
uuid: UUID,
|
||||
pokemonList: List<BattlePokemon>
|
||||
pokemonList: List<BattlePokemon>,
|
||||
) : BattleActor(uuid, pokemonList.toMutableList()), EntityBackedBattleActor<ServerPlayer> {
|
||||
|
||||
override val initialPos: Vec3?
|
||||
@@ -41,8 +42,7 @@ class PlayerBattleActor(
|
||||
/** The [SoundEvent] to play to the player during a battle. Will start playing as soon as the battle starts. */
|
||||
var battleTheme: SoundEvent? = null
|
||||
set(value) {
|
||||
if (field != value && this.battle.started)
|
||||
this.sendUpdate(BattleMusicPacket(value))
|
||||
if (this.isInitialized() && this.battle.started) this.sendUpdate(BattleMusicPacket(value))
|
||||
field = value
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
|
||||
@@ -30,7 +30,7 @@ class MegaInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
battle.dispatchWaiting {
|
||||
val pokemonName = battlePokemon.getName()
|
||||
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
|
||||
CobblemonEvents.MEGA_EVOLUTON.post(MegaEvolutionEvent(battle, battlePokemon))
|
||||
CobblemonEvents.MEGA_EVOLUTION.post(MegaEvolutionEvent(battle, battlePokemon))
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
|
||||
@@ -107,9 +106,5 @@ open class BattlePokemon(
|
||||
!isSentOut() && !willBeSwitchedIn && health > 0
|
||||
}
|
||||
|
||||
fun writeVariables(struct: VariableStruct) {
|
||||
effectedPokemon.writeVariables(struct)
|
||||
}
|
||||
|
||||
fun getIllusion(): BattlePokemon? = this.actor.activePokemon.find { it.battlePokemon == this }?.illusion
|
||||
}
|
||||
@@ -173,10 +173,6 @@ class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cob
|
||||
|
||||
override fun getMaxStackSize() = 1
|
||||
|
||||
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, dir: Direction?): Boolean {
|
||||
if(dir == Direction.DOWN) return false
|
||||
return getStack().isEmpty
|
||||
}
|
||||
|
||||
override fun canPlaceItemThroughFace(slot: Int, stack: ItemStack, dir: Direction?): Boolean = (getStack().isEmpty)
|
||||
override fun canTakeItemThroughFace(slot: Int, stack: ItemStack, direction: Direction): Boolean = (direction == Direction.DOWN)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ object CobblemonClient {
|
||||
return@subscribe
|
||||
}
|
||||
val nearbyShinies = player?.level()?.getEntities(player, AABB.ofSize(player.position(), 16.0, 16.0, 16.0)) { (it is PokemonEntity) && it.pokemon.shiny }
|
||||
nearbyShinies?.firstOrNull { player.isLookingAt(it) }.let {
|
||||
nearbyShinies?.firstOrNull { player.isLookingAt(it) && !player.isSpectator }.let {
|
||||
if(it is PokemonEntity)
|
||||
it.delegate.spawnShinyParticle(player!!)
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
val player = Minecraft.getInstance().player ?: return
|
||||
val isWithinRange = player.position().distanceTo(currentEntity.position()) <= Cobblemon.config.shinyNoticeParticlesDistance
|
||||
|
||||
if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null) {
|
||||
if (currentEntity.pokemon.shiny && currentEntity.ownerUUID == null && !player.isSpectator ) {
|
||||
if (isWithinRange && !shined) {
|
||||
playShinyEffect("cobblemon:wild_shiny_ring")
|
||||
shined = true
|
||||
|
||||
@@ -47,8 +47,8 @@ class BattleSwitchPokemonSelection(
|
||||
battleGUI,
|
||||
request,
|
||||
x = 0,
|
||||
y = if (Minecraft.getInstance().window.guiScaledHeight > 630) Minecraft.getInstance().window.guiScaledHeight / 2 - 148 / 2
|
||||
else Minecraft.getInstance().window.guiScaledHeight - 226,
|
||||
y = if (Minecraft.getInstance().window.guiScaledHeight > 304) (Minecraft.getInstance().window.guiScaledHeight / 2) - (BACKGROUND_HEIGHT / 2)
|
||||
else Minecraft.getInstance().window.guiScaledHeight - (BACKGROUND_HEIGHT + 78),
|
||||
width = Minecraft.getInstance().window.guiScaledWidth,
|
||||
height = Minecraft.getInstance().window.guiScaledHeight,
|
||||
battleLang("switch_pokemon")
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.client.battle.ActiveClientBattlePokemon
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.subscreen.BattleSwitchPokemonSelection.Companion
|
||||
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
|
||||
@@ -47,8 +48,8 @@ class BattleTargetSelection(
|
||||
battleGUI = battleGUI,
|
||||
request = request,
|
||||
x = 0,
|
||||
y = if (Minecraft.getInstance().window.guiScaledHeight > 630) Minecraft.getInstance().window.guiScaledHeight / 2 - 148 / 2
|
||||
else Minecraft.getInstance().window.guiScaledHeight - 226,
|
||||
y = if (Minecraft.getInstance().window.guiScaledHeight > 304) (Minecraft.getInstance().window.guiScaledHeight / 2) - (BattleSwitchPokemonSelection.BACKGROUND_HEIGHT / 2)
|
||||
else Minecraft.getInstance().window.guiScaledHeight - (BattleSwitchPokemonSelection.BACKGROUND_HEIGHT + 78),
|
||||
width = 100,
|
||||
height = 100,
|
||||
battleLang("ui.select_move")
|
||||
|
||||
@@ -368,7 +368,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
matrices.translate(
|
||||
pX.toDouble() + (POKEMON_PORTRAIT_WIDTH.toDouble() + 2)/2,
|
||||
pY.toDouble() + portraitStartY - 12,
|
||||
0.0
|
||||
1000.0 // Prevent model from clipping into background
|
||||
)
|
||||
matrices.scale(scaleAmount, scaleAmount, scaleAmount)
|
||||
val rotationVector = Vector3f(13F, rotationY, 0F)
|
||||
@@ -398,7 +398,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
// Ensure elements are not hidden behind Pokémon render
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 1000.0)
|
||||
matrices.translate(0.0, 0.0, 2000.0)
|
||||
|
||||
if (isSelectedPokemonOwned()) {
|
||||
val primaryType = type[0]
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
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.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
|
||||
@@ -17,9 +18,11 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVER
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
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.client.renderer.RenderType
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
class SearchWidget(
|
||||
@@ -36,20 +39,24 @@ class SearchWidget(
|
||||
private val searchIcon = cobblemonResource("textures/gui/pokedex/search_icon.png")
|
||||
}
|
||||
|
||||
var focusedTime: Long = 0
|
||||
|
||||
init {
|
||||
this.setMaxLength(24)
|
||||
this.setResponder {
|
||||
update.invoke()
|
||||
}
|
||||
focusedTime = Util.getMillis()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if (mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)) isFocused = true
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
@@ -73,29 +80,30 @@ class SearchWidget(
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// if(text.isEmpty() && !isFocused) {
|
||||
// drawScaledText(
|
||||
// context = context,
|
||||
// font = CobblemonResources.DEFAULT_LARGE,
|
||||
// text = Component.translatable("cobblemon.ui.pokedex.search").bold(),
|
||||
// x = posX.toInt() + 13,
|
||||
// y = posY.toInt() + 1,
|
||||
// shadow = true
|
||||
// )
|
||||
// }
|
||||
if (cursorPosition != value.length) moveCursorToEnd(false)
|
||||
|
||||
val input = if (isFocused) "${value}_".text()
|
||||
val showCursor = isFocused && ((Util.getMillis() - this.focusedTime) / 300L % 2L == 0L)
|
||||
val input = if (isFocused) "${value}${if ((cursorPosition == value.length) && showCursor) "_" else ""}".text()
|
||||
else (if(value.isEmpty()) Component.translatable("cobblemon.ui.pokedex.search") else value.text())
|
||||
|
||||
val startX = posX.toInt() + 13
|
||||
val startY = posY.toInt() + 1
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = input.bold(),
|
||||
// text = Component.translatable(if (isFocused) "${text}_" else text).bold(),
|
||||
x = posX.toInt() + 13,
|
||||
y = posY.toInt() + 1,
|
||||
x = startX,
|
||||
y = startY,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
if (showCursor && !value.isEmpty() && cursorPosition != value.length) {
|
||||
val startToCursorWidth = Minecraft.getInstance().font.width((input.getString(cursorPosition).text().bold()).font(CobblemonResources.DEFAULT_LARGE))
|
||||
context.fill(
|
||||
RenderType.guiTextHighlight(),
|
||||
startX + startToCursorWidth - 1,
|
||||
startY,
|
||||
startX + startToCursorWidth,
|
||||
startY + 9,
|
||||
-3092272
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ class SummaryTab(
|
||||
texture = icon,
|
||||
x = (x + 21) / 0.5F,
|
||||
y = (y + 3.5) / 0.5F,
|
||||
width = 16,
|
||||
height = 16,
|
||||
width = 17,
|
||||
height = 17,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
|
||||
@@ -179,8 +179,8 @@ class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : Entit
|
||||
matrixStack.popPose() // close bobber rendering transforms
|
||||
|
||||
var armOffset = if (playerEntity.mainArm == HumanoidArm.RIGHT) 1 else -1
|
||||
val itemStack = playerEntity.mainHandItem
|
||||
if (itemStack.item !is PokerodItem) {
|
||||
val item = playerEntity.mainHandItem.item
|
||||
if ((item !is PokerodItem) || item.pokeRodId != pokeRodId) {
|
||||
armOffset = -armOffset
|
||||
}
|
||||
val handSwingProgress = playerEntity.getAttackAnim(tickDelta)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.render.item
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
import net.minecraft.client.renderer.item.ItemProperties
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -71,17 +72,21 @@ object CobblemonModelPredicateRegistry {
|
||||
|
||||
rods.forEach { rod ->
|
||||
ItemProperties.register(rod, ResourceLocation.parse("cast")) { stack, world, entity, seed ->
|
||||
if (entity == null) {
|
||||
0.0f
|
||||
} else {
|
||||
val isMainHand = entity.mainHandItem == stack
|
||||
var isOffHand = entity.offhandItem == stack
|
||||
if (entity.mainHandItem.item is PokerodItem) {
|
||||
isOffHand = false
|
||||
}
|
||||
if (entity !is Player || entity.fishing !is PokeRodFishingBobberEntity) return@register 0.0f
|
||||
|
||||
if ((isMainHand || isOffHand) && entity is Player && entity.fishing != null) 1.0f else 0.0f
|
||||
val rodId = entity.fishing!!.entityData.get(PokeRodFishingBobberEntity.POKEROD_ID)
|
||||
|
||||
val isMainHand = stack == entity.mainHandItem
|
||||
var isOffHand = stack == entity.offhandItem
|
||||
|
||||
var mainHandItem = entity.mainHandItem.item
|
||||
val isFishingWithMainHand = mainHandItem is PokerodItem && rodId == mainHandItem.pokeRodId.toString()
|
||||
|
||||
if (isFishingWithMainHand) {
|
||||
isOffHand = false
|
||||
}
|
||||
|
||||
if (isMainHand && isFishingWithMainHand || isOffHand) 1.0f else 0.0f
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.command
|
||||
|
||||
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.commandLang
|
||||
import com.cobblemon.mod.common.util.requiresWithPermission
|
||||
import com.cobblemon.mod.common.util.traceFirstEntityCollision
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import net.minecraft.commands.CommandSourceStack
|
||||
import net.minecraft.commands.Commands
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object NPCDeleteCommand {
|
||||
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
|
||||
dispatcher.register(
|
||||
Commands.literal("npcdelete")
|
||||
.requiresWithPermission(CobblemonPermissions.NPC_DELETE) { it.player != null }
|
||||
.executes { execute(it, it.source.playerOrException) })
|
||||
}
|
||||
|
||||
private fun execute(context: CommandContext<CommandSourceStack>, player: ServerPlayer) : Int {
|
||||
val targetEntity = player.traceFirstEntityCollision(entityClass = NPCEntity::class.java)
|
||||
if (targetEntity == null) {
|
||||
player.sendSystemMessage(commandLang("npcedit.non_npc").red())
|
||||
return 0
|
||||
}
|
||||
|
||||
targetEntity.discard()
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,8 @@ import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity>, world: Level) : FishingHook(type, world) {
|
||||
|
||||
private var lastRippleSpawnTime: Long = 0
|
||||
private val rippleCooldown: Long = 20
|
||||
private val velocityRandom = RandomSource.create()
|
||||
private var caughtFish = false
|
||||
private var outOfOpenWaterTicks = 0
|
||||
@@ -306,8 +308,7 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
val l = h * 0.04f
|
||||
|
||||
// todo the fish trail that leads to the bobber
|
||||
serverWorld.sendParticles(ParticleTypes.FISHING, offsetX, offsetY, j, 0, l.toDouble(), 0.01, -k.toDouble(), 1.0)
|
||||
serverWorld.sendParticles(ParticleTypes.FISHING, offsetX, offsetY, j, 0, -l.toDouble(), 0.01, k.toDouble(), 1.0)
|
||||
particleCatchHandler(offsetX, offsetY, j, this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_wake"))
|
||||
// create tiny splash particles for fishing trail
|
||||
//particleEntityHandler(this, Identifier.of("cobblemon","bob_splash"))
|
||||
}
|
||||
@@ -318,6 +319,7 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
|
||||
// create tiny splash particle when there is a bite
|
||||
particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "bob_splash"))
|
||||
particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_bobber_big_ripple"))
|
||||
|
||||
val m = this.y + 0.5
|
||||
serverWorld.sendParticles(ParticleTypes.BUBBLE, this.x, m, this.z, (1.0f + this.bbWidth * 20.0f).toInt(), this.bbWidth.toDouble(), 0.0, this.bbWidth.toDouble(), 0.2)
|
||||
@@ -365,7 +367,12 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
val j = this.z + (Mth.cos(g) * h).toDouble() * 0.1 // randomized Z value
|
||||
val blockState = serverWorld.getBlockState(BlockPos.containing(d, e - 1.0, j))
|
||||
if (blockState.`is`(Blocks.WATER)) {
|
||||
serverWorld.sendParticles(ParticleTypes.SPLASH, d, e, j, 2 + random.nextInt(2), 0.10000000149011612, 0.0, 0.10000000149011612, 0.0)
|
||||
val currentTime = serverWorld.gameTime
|
||||
if (currentTime - lastRippleSpawnTime >= rippleCooldown) {
|
||||
particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_bobber_ripple"))
|
||||
lastRippleSpawnTime = currentTime
|
||||
}
|
||||
particleEntityHandler(this, ResourceLocation.fromNamespaceAndPath("cobblemon", "fishing_surface_ripple"))
|
||||
}
|
||||
}
|
||||
if (this.waitCountdown <= 0) {
|
||||
@@ -778,6 +785,12 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
spawnSnowstormParticlePacket.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
}
|
||||
|
||||
private fun particleCatchHandler(x: Double, y: Double, z: Double, entity: Entity, particle: ResourceLocation) {
|
||||
var particlePosition = Vec3(x, y, z)
|
||||
val spawnSnowstormParticlePacket = SpawnSnowstormParticlePacket(particle, particlePosition)
|
||||
spawnSnowstormParticlePacket.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
|
||||
}
|
||||
|
||||
enum class TypeCaught {
|
||||
ITEM,
|
||||
POKEMON
|
||||
|
||||
@@ -79,17 +79,11 @@ class NPCBattleActor(
|
||||
override fun win(otherWinners: List<BattleActor>, losers: List<BattleActor>) {
|
||||
super.win(otherWinners, losers)
|
||||
npc.playAnimation(NPCEntity.WIN_ANIMATION)
|
||||
if (npc.npc.autoHealParty) {
|
||||
npc.party?.heal()
|
||||
}
|
||||
}
|
||||
|
||||
override fun lose(winners: List<BattleActor>, otherLosers: List<BattleActor>) {
|
||||
super.lose(winners, otherLosers)
|
||||
npc.playAnimation(NPCEntity.LOSE_ANIMATION)
|
||||
if (npc.npc.autoHealParty) {
|
||||
npc.party?.heal()
|
||||
}
|
||||
// winners.forEach {
|
||||
// rewards.forEach {
|
||||
// winner give reward :))
|
||||
|
||||
@@ -52,7 +52,6 @@ import com.google.common.collect.ImmutableList
|
||||
import com.mojang.authlib.GameProfile
|
||||
import com.mojang.authlib.ProfileLookupCallback
|
||||
import com.mojang.serialization.Dynamic
|
||||
import java.lang.Exception
|
||||
import java.net.URI
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@@ -66,6 +65,7 @@ import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
|
||||
import net.minecraft.network.protocol.game.DebugPackets
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
import net.minecraft.network.syncher.EntityDataSerializers
|
||||
import net.minecraft.network.syncher.SynchedEntityData
|
||||
@@ -75,6 +75,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
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.Pose
|
||||
@@ -119,6 +120,12 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
|
||||
var isMovable: Boolean? = null
|
||||
|
||||
var isInvulnerable: Boolean? = null
|
||||
|
||||
var isLeashable: Boolean? = null
|
||||
|
||||
var allowProjectileHits: Boolean? = null
|
||||
|
||||
fun getPartyForChallenge(player: ServerPlayer): NPCPartyStore? {
|
||||
val party = this.party
|
||||
return if (party != null) {
|
||||
@@ -203,7 +210,8 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
SensorType.HURT_BY,
|
||||
SensorType.NEAREST_PLAYERS,
|
||||
CobblemonSensors.BATTLING_POKEMON,
|
||||
CobblemonSensors.NPC_BATTLING
|
||||
CobblemonSensors.NPC_BATTLING,
|
||||
SensorType.VILLAGER_HOSTILES
|
||||
)
|
||||
|
||||
val MEMORY_MODULES: List<MemoryModuleType<*>> = ImmutableList.of(
|
||||
@@ -223,6 +231,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
MemoryModuleType.ATTACK_COOLING_DOWN,
|
||||
CobblemonMemories.DIALOGUES,
|
||||
CobblemonMemories.ACTIVE_ACTION_EFFECT,
|
||||
MemoryModuleType.NEAREST_HOSTILE
|
||||
)
|
||||
|
||||
const val SEND_OUT_ANIMATION = "send_out"
|
||||
@@ -294,6 +303,13 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
getBrain().tick(level() as ServerLevel, this)
|
||||
}
|
||||
|
||||
override fun sendDebugPackets() {
|
||||
super.sendDebugPackets()
|
||||
DebugPackets.sendEntityBrain(this)
|
||||
DebugPackets.sendGoalSelector(level(), this, this.goalSelector)
|
||||
DebugPackets.sendPathFindingPacket(level(), this, this.navigation.path, this.navigation.path?.distToTarget ?: 0F)
|
||||
}
|
||||
|
||||
override fun saveWithoutId(nbt: CompoundTag): CompoundTag {
|
||||
super.saveWithoutId(nbt)
|
||||
nbt.put(DataKeys.NPC_DATA, MoLangFunctions.writeMoValueToNBT(data))
|
||||
@@ -333,6 +349,18 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
if (isMovable != null) {
|
||||
nbt.putBoolean(DataKeys.NPC_IS_MOVABLE, isMovable)
|
||||
}
|
||||
val isInvulnerable = isInvulnerable
|
||||
if (isInvulnerable != null) {
|
||||
nbt.putBoolean(DataKeys.NPC_IS_INVULNERABLE, isInvulnerable)
|
||||
}
|
||||
val isLeashable = isLeashable
|
||||
if (isLeashable != null) {
|
||||
nbt.putBoolean(DataKeys.NPC_IS_LEASHABLE, isLeashable)
|
||||
}
|
||||
val allowProjectileHits = allowProjectileHits
|
||||
if (allowProjectileHits != null) {
|
||||
nbt.putBoolean(DataKeys.NPC_ALLOW_PROJECTILE_HITS, allowProjectileHits)
|
||||
}
|
||||
return nbt
|
||||
}
|
||||
|
||||
@@ -367,6 +395,10 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
entityData.set(NPC_PLAYER_TEXTURE, NPCPlayerTexture(texture, model))
|
||||
}
|
||||
this.isMovable = if (nbt.contains(DataKeys.NPC_IS_MOVABLE)) nbt.getBoolean(DataKeys.NPC_IS_MOVABLE) else null
|
||||
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
|
||||
|
||||
updateAspects()
|
||||
}
|
||||
|
||||
@@ -404,6 +436,18 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
return isMovable ?: npc.isMovable
|
||||
}
|
||||
|
||||
override fun isInvulnerableTo(source: DamageSource): Boolean {
|
||||
return isInvulnerable ?: npc.isInvulnerable
|
||||
}
|
||||
|
||||
override fun canBeLeashed(): Boolean {
|
||||
return isLeashable ?: npc.isLeashable
|
||||
}
|
||||
|
||||
override fun canBeHitByProjectile(): Boolean {
|
||||
return allowProjectileHits ?: npc.allowProjectileHits
|
||||
}
|
||||
|
||||
fun initialize(level: Int) {
|
||||
appliedAspects.clear()
|
||||
entityData.set(LEVEL, level)
|
||||
|
||||
@@ -1,32 +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.npc.ai
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
object SwitchFromActionEffectTask {
|
||||
fun create(activity: Activity = Activity.IDLE): OneShot<in LivingEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
).apply(it) { _ ->
|
||||
Trigger { world, entity, time ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +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.npc.ai
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
|
||||
/**
|
||||
* When NPC battling memory is removed, this task swaps the NPC's activity over to idle.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 24th, 2024
|
||||
*/
|
||||
object SwitchFromBattleTask {
|
||||
fun create(activity: Activity): OneShot<LivingEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(it.absent(CobblemonMemories.NPC_BATTLING)).apply(it) { _ ->
|
||||
Trigger { _, entity, _ ->
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,7 +380,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
after(seconds = 0.8F) {
|
||||
if (pokemon.pokemon.shiny) {
|
||||
SpawnSnowstormEntityParticlePacket(cobblemonResource("wild_shiny_ring"), pokemon.id, listOf("shiny_particles", "middle"))
|
||||
SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), pokemon.id, listOf("shiny_particles", "middle"))
|
||||
.sendToPlayersAround(pokemon.x, pokemon.y, pokemon.z, 32.0, pokemon.level().dimension())
|
||||
}
|
||||
discard()
|
||||
|
||||
@@ -26,4 +26,6 @@ class LaxPermissionValidator : PermissionValidator {
|
||||
|
||||
override fun hasPermission(player: ServerPlayer, permission: Permission) = player.hasPermissions(permission.level.numericalValue)
|
||||
override fun hasPermission(source: CommandSourceStack, permission: Permission) = source.hasPermission(permission.level.numericalValue)
|
||||
override fun hasPermission(player: ServerPlayer, permission: String, level: Int) = player.hasPermissions(level)
|
||||
override fun hasPermission(source: CommandSourceStack, permission: String, level: Int) = source.hasPermission(level)
|
||||
}
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
@@ -23,14 +21,25 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.FRIENDSHIP_UPDATED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_FAINTED
|
||||
import com.cobblemon.mod.common.api.events.pokemon.*
|
||||
import com.cobblemon.mod.common.api.events.pokemon.healing.PokemonHealedEvent
|
||||
import com.cobblemon.mod.common.api.moves.*
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMoves
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.*
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionController
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionDisplay
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.EvolutionProxy
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
|
||||
@@ -55,7 +64,6 @@ import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraType
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.datafixer.CobblemonSchemas
|
||||
import com.cobblemon.mod.common.datafixer.CobblemonTypeReferences
|
||||
@@ -68,7 +76,11 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.THROW_DURATION
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.activestate.*
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.InactivePokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.PokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionProxy
|
||||
import com.cobblemon.mod.common.pokemon.evolution.controller.ClientEvolutionController
|
||||
import com.cobblemon.mod.common.pokemon.evolution.controller.ServerEvolutionController
|
||||
@@ -80,12 +92,19 @@ import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.codec.internal.*
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1
|
||||
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP2
|
||||
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP3
|
||||
import com.cobblemon.mod.common.util.codec.internal.PokemonP1
|
||||
import com.cobblemon.mod.common.util.codec.internal.PokemonP2
|
||||
import com.cobblemon.mod.common.util.codec.internal.PokemonP3
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.setPositionSafely
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.google.gson.JsonObject
|
||||
import com.mojang.datafixers.util.Pair
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -102,7 +121,9 @@ import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.network.chat.contents.PlainTextContents
|
||||
@@ -117,10 +138,14 @@ import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.vehicle.Boat
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.CactusBlock
|
||||
import net.minecraft.world.level.block.CampfireBlock
|
||||
import net.minecraft.world.level.block.FireBlock
|
||||
import net.minecraft.world.level.block.MagmaBlock
|
||||
import net.minecraft.world.level.block.SweetBerryBushBlock
|
||||
import net.minecraft.world.level.block.WitherRoseBlock
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
enum class OriginalTrainerType : StringRepresentable {
|
||||
@@ -1497,6 +1522,9 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
private val observables = mutableListOf<Observable<*>>()
|
||||
val anyChangeObservable = SimpleObservable<Pokemon>()
|
||||
|
||||
val struct = ObjectValue<Pokemon>(this)
|
||||
.addPokemonFunctions(this)
|
||||
|
||||
fun markFeatureDirty(feature: SpeciesFeature) {
|
||||
_features.emit(feature)
|
||||
}
|
||||
@@ -1505,19 +1533,6 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
/** Returns an [Observable] that emits Unit whenever any change is made to this Pokémon. The change itself is not included. */
|
||||
fun getChangeObservable(): Observable<Pokemon> = anyChangeObservable
|
||||
|
||||
fun writeVariables(struct: VariableStruct) {
|
||||
struct.setDirectly("level", DoubleValue(level.toDouble()))
|
||||
struct.setDirectly("max_hp", DoubleValue(maxHealth.toDouble()))
|
||||
struct.setDirectly("current_hp", DoubleValue(currentHealth.toDouble()))
|
||||
struct.setDirectly("friendship", DoubleValue(friendship.toDouble()))
|
||||
struct.setDirectly("shiny", DoubleValue(shiny))
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.setDirectly("ev_${stat.showdownId}", DoubleValue(evs.getOrDefault(stat).toDouble()))
|
||||
struct.setDirectly("iv_${stat.showdownId}", DoubleValue(ivs.getOrDefault(stat).toDouble()))
|
||||
struct.setDirectly("stat_${stat.showdownId}", DoubleValue(getStat(stat).toDouble()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for when 'this' would be called in leaking context.
|
||||
*
|
||||
|
||||
@@ -213,6 +213,9 @@ object DataKeys {
|
||||
const val NPC_LEVEL = "Level"
|
||||
const val NPC_IS_MOVABLE = "IsMovable"
|
||||
const val NPC_SKILL = "Skill"
|
||||
const val NPC_IS_INVULNERABLE = "IsInvulnerable"
|
||||
const val NPC_IS_LEASHABLE = "IsLeashable"
|
||||
const val NPC_ALLOW_PROJECTILE_HITS = "AllowProjectileHits"
|
||||
|
||||
// PokemonProperties
|
||||
const val ELEMENTAL_TYPE = "ElementalType"
|
||||
|
||||
@@ -16,7 +16,6 @@ import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.ContextStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
@@ -161,15 +160,11 @@ fun String.asExpressionLike() = try {
|
||||
fun Double.asExpression() = toString().asExpression() // Use the string route because it remembers the original string value for serialization
|
||||
|
||||
fun MoLangEnvironment.writePokemon(pokemon: Pokemon) {
|
||||
val pokemonStruct = VariableStruct()
|
||||
pokemon.writeVariables(pokemonStruct)
|
||||
setSimpleVariable("pokemon", pokemonStruct)
|
||||
setSimpleVariable("pokemon", pokemon.struct)
|
||||
}
|
||||
|
||||
fun MoLangEnvironment.writePokemon(pokemon: BattlePokemon) {
|
||||
val pokemonStruct = VariableStruct()
|
||||
pokemon.writeVariables(pokemonStruct)
|
||||
setSimpleVariable("pokemon", pokemonStruct)
|
||||
setSimpleVariable("pokemon", pokemon.effectedPokemon.struct)
|
||||
}
|
||||
|
||||
fun List<String>.asExpressionLike() = joinToString(separator = "\n").asExpressionLike()
|
||||
@@ -182,6 +177,7 @@ fun List<Expression>.resolveObject(runtime: MoLangRuntime, context: Map<String,
|
||||
fun MoParams.getStringOrNull(index: Int) = if (params.size > index) getString(index) else null
|
||||
fun MoParams.getDoubleOrNull(index: Int) = if (params.size > index) getDouble(index) else null
|
||||
fun MoParams.getBooleanOrNull(index: Int) = if (params.size > index) getDouble(index) == 1.0 else null
|
||||
fun MoParams.getIntOrNull(index: Int) = if (params.size > index) getDouble(index).toInt() else null
|
||||
|
||||
fun MoLangRuntime.withQueryValue(name: String, value: MoValue): MoLangRuntime {
|
||||
environment.query.functions.put(name) { value }
|
||||
|
||||
@@ -73,6 +73,9 @@ object NPCClassAdapter : JsonDeserializer<NPCClass> {
|
||||
obj.get("battleTheme")?.let { npcClass.battleTheme = it.asString.asIdentifierDefaultingNamespace() }
|
||||
obj.get("ai")?.let { npcClass.ai.addAll(it.asJsonArray.map<JsonElement, BrainConfig> { ctx.deserialize(it, BrainConfig::class.java) }.toMutableList()) }
|
||||
obj.get("isMovable")?.let { npcClass.isMovable = it.asBoolean }
|
||||
obj.get("isInvulnerable")?.let { npcClass.isInvulnerable = it.asBoolean }
|
||||
obj.get("isLeashable")?.let { npcClass.isLeashable = it.asBoolean }
|
||||
obj.get("allowProjectileHits")?.let { npcClass.allowProjectileHits = it.asBoolean }
|
||||
|
||||
return npcClass
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object NPCClassReferenceAdapter : JsonDeserializer<NPCClass> {
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): NPCClass {
|
||||
val resourceLocation = json.asString.asIdentifierDefaultingNamespace()
|
||||
return NPCClasses.getByIdentifier(resourceLocation)
|
||||
?: throw IllegalArgumentException("No such NPC class: $resourceLocation")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:fishing_bobber_big_ripple",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/water_ripple"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.size": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [1, 10]
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 6,
|
||||
"max_particles": 2
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.5
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
"offset": [0, 0.05, 0]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"creation_event": "event_splash"
|
||||
},
|
||||
"minecraft:particle_initial_speed": 0,
|
||||
"minecraft:particle_motion_dynamic": {},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.4 * v.size", "0.4 * v.size"],
|
||||
"facing_camera_mode": "emitter_transform_xz",
|
||||
"uv": {
|
||||
"texture_width": 117,
|
||||
"texture_height": 13,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [13, 13],
|
||||
"step_UV": [13, 0],
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 9
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_lighting": {},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": {
|
||||
"interpolant": "v.particle_age / v.particle_lifetime",
|
||||
"gradient": {
|
||||
"0.0": "#FFFFFFFF",
|
||||
"0.1": "#B0FFFFFF",
|
||||
"0.5": "#00FFFFFF",
|
||||
"1.0": "#00FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:fishing_bobber_ripple",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/water_ripple"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.size": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [1, 1.5]
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.5
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
"offset": [0, 0.05, 0]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"creation_event": "event_splash"
|
||||
},
|
||||
"minecraft:particle_initial_speed": 0,
|
||||
"minecraft:particle_motion_dynamic": {},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["math.clamp(v.particle_random_2,0.5,0.7) * variable.size", "math.clamp(v.particle_random_2,0.5,0.7) * variable.size"],
|
||||
"facing_camera_mode": "emitter_transform_xz",
|
||||
"uv": {
|
||||
"texture_width": 117,
|
||||
"texture_height": 13,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [13, 13],
|
||||
"step_UV": [13, 0],
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 9
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_lighting": {},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": {
|
||||
"interpolant": "v.particle_age / v.particle_lifetime",
|
||||
"gradient": {
|
||||
"0.0": "#FFFFFFFF",
|
||||
"0.56": "#00FFFFFF",
|
||||
"1.0": "#00FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:fishing_surface_ripple",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/water_ripple"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.size": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [1, 1.5]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"event_splash": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:splash",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": "math.random(1,3)"
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"radius": "math.random(2,5)",
|
||||
"plane_normal": [0, 0.05, 0],
|
||||
"surface_only": true,
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"creation_event": "event_splash"
|
||||
},
|
||||
"minecraft:particle_initial_speed": 0,
|
||||
"minecraft:particle_motion_dynamic": {},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["math.clamp(v.particle_random_1,0.6,0.8)", "math.clamp(v.particle_random_1,0.6,0.8)"],
|
||||
"facing_camera_mode": "emitter_transform_xz",
|
||||
"uv": {
|
||||
"texture_width": 117,
|
||||
"texture_height": 13,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [13, 13],
|
||||
"step_UV": [13, 0],
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 9
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_lighting": {},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": {
|
||||
"interpolant": "v.particle_age / v.particle_lifetime",
|
||||
"gradient": {
|
||||
"0.0": "#8CFFFFFF",
|
||||
"0.48": "#00FFFFFF",
|
||||
"1.0": "#00FFFFFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:fishing_wake",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/white"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"event_splash": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:splash",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 32
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.6
|
||||
},
|
||||
"minecraft:emitter_lifetime_events": {
|
||||
"creation_event": "event_splash"
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"offset": [0, 0.01, 0],
|
||||
"radius": 0.1,
|
||||
"direction": ["math.random(-10,10)", 0, "math.random(-10,10)"]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.7
|
||||
},
|
||||
"minecraft:particle_initial_speed": 1.5,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_drag_coefficient": 5
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": [0.05, 0.05],
|
||||
"facing_camera_mode": "emitter_transform_xz",
|
||||
"uv": {
|
||||
"texture_width": 8,
|
||||
"texture_height": 8,
|
||||
"uv": [0, 0],
|
||||
"uv_size": [8, 8]
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_lighting": {},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": {
|
||||
"interpolant": "v.particle_age / v.particle_lifetime",
|
||||
"gradient": {
|
||||
"0.0": "#54C8FDFF",
|
||||
"0.19": "#54C8FDFF",
|
||||
"0.59": "#695DB3FF",
|
||||
"1.0": "#005DB3FF"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:splash",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_alpha",
|
||||
"texture": "textures/particles/rain_splash"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": "math.random(1,2)"
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
"direction": ["math.random(-0.6,0.6)", 1, "math.random(-0.6,0.6)"]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
},
|
||||
"minecraft:particle_initial_speed": 4,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_acceleration": [0, -10, 0],
|
||||
"linear_drag_coefficient": 2
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": [0.2, 0.2],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 8,
|
||||
"texture_height": 32,
|
||||
"uv": [0, "math.floor(variable.particle_random_2 * 4) * 8"],
|
||||
"uv_size": [8, 8]
|
||||
}
|
||||
},
|
||||
"minecraft:particle_motion_collision": {
|
||||
"expire_on_contact": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,13 @@
|
||||
"texture": "textures/particles/powder"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"event_sound": {
|
||||
"sound_effect": {
|
||||
"event_name": "cobblemon:move.sandattack.actor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 500,
|
||||
@@ -16,6 +23,9 @@
|
||||
"minecraft:emitter_lifetime_looping": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_events": {
|
||||
"creation_event": "event_sound"
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
"offset": [0, 0.05, "-(q.entity_radius*0.7))"],
|
||||
"direction": ["math.random(-0.81,0.81)", "math.random(0.95,1.55)", "math.random(-4,-1)"]
|
||||
|
||||
@@ -8,7 +8,17 @@
|
||||
"texture": "textures/particles/powder"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"event_sound": {
|
||||
"sound_effect": {
|
||||
"event_name": "cobblemon:move.sandattack.target"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_lifetime_events": {
|
||||
"creation_event": "event_sound"
|
||||
},
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 500,
|
||||
"max_particles": 25
|
||||
|
||||
@@ -1101,11 +1101,13 @@
|
||||
"item.cobblemon.ganlon_berry": "Ganlon Berry",
|
||||
"item.cobblemon.ganlon_berry.tooltip": "Raises holder's Defense by 1 stage when at 1/4 max HP or less",
|
||||
"item.cobblemon.grepa_berry": "Grepa Berry",
|
||||
"item.cobblemon.grepa_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.grepa_berry.tooltip_1": "Lowers the pokémon's Defense EVs by 10, while raising friendship",
|
||||
"item.cobblemon.grepa_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.haban_berry": "Haban Berry",
|
||||
"item.cobblemon.haban_berry.tooltip": "Halves damage taken from a supereffective Dragon-type attack",
|
||||
"item.cobblemon.hondew_berry": "Hondew Berry",
|
||||
"item.cobblemon.hondew_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.hondew_berry.tooltip_1": "Lowers the pokémon's Special Defense EVs by 10, while raising friendship",
|
||||
"item.cobblemon.hondew_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.hopo_berry": "Hopo Berry",
|
||||
"item.cobblemon.hopo_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.iapapa_berry": "Iapapa Berry",
|
||||
@@ -1119,7 +1121,8 @@
|
||||
"item.cobblemon.kee_berry": "Kee Berry",
|
||||
"item.cobblemon.kee_berry.tooltip": "Raises holder's Defense by 1 stage after it is hit by a physical attack",
|
||||
"item.cobblemon.kelpsy_berry": "Kelpsy Berry",
|
||||
"item.cobblemon.kelpsy_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.kelpsy_berry.tooltip_1": "Lowers the pokémon's Special Attack EVs by 10, while raising friendship",
|
||||
"item.cobblemon.kelpsy_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.lansat_berry": "Lansat Berry",
|
||||
"item.cobblemon.lansat_berry.tooltip": "Holder gains the Focus Energy effect when at 1/4 max HP or less",
|
||||
"item.cobblemon.leppa_berry": "Leppa Berry",
|
||||
@@ -1161,9 +1164,11 @@
|
||||
"item.cobblemon.pinap_berry": "Pinap Berry",
|
||||
"item.cobblemon.pinap_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.pomeg_berry": "Pomeg Berry",
|
||||
"item.cobblemon.pomeg_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.pomeg_berry.tooltip_1": "Lowers the pokémon's HP EVs by 10, while raising friendship",
|
||||
"item.cobblemon.pomeg_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.qualot_berry": "Qualot Berry",
|
||||
"item.cobblemon.qualot_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.qualot_berry.tooltip_1": "Lowers the pokémon's Speed EVs by 10, while raising friendship",
|
||||
"item.cobblemon.qualot_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.rabuta_berry": "Rabuta Berry",
|
||||
"item.cobblemon.rabuta_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.rawst_berry": "Rawst Berry",
|
||||
@@ -1187,7 +1192,8 @@
|
||||
"item.cobblemon.starf_berry": "Starf Berry",
|
||||
"item.cobblemon.starf_berry.tooltip": "Raises a random stat by 2 when at 1/4 max HP or less",
|
||||
"item.cobblemon.tamato_berry": "Tamato Berry",
|
||||
"item.cobblemon.tamato_berry.tooltip": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.tamato_berry.tooltip_1": "Lowers the pokémon's Attack EVs by 10, while raising friendship",
|
||||
"item.cobblemon.tamato_berry.tooltip_2": "Cannot be eaten by the holder",
|
||||
"item.cobblemon.tanga_berry": "Tanga Berry",
|
||||
"item.cobblemon.tanga_berry.tooltip": "Halves damage taken from a supereffective Bug-type attack",
|
||||
"item.cobblemon.wacan_berry": "Wacan Berry",
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
"cobblemon:spiral",
|
||||
"cobblemon:mud_bubble",
|
||||
"cobblemon:mud_splash",
|
||||
"cobblemon:splash"
|
||||
"cobblemon:splash",
|
||||
"cobblemon:rain_splash",
|
||||
"cobblemon:water_ripple"
|
||||
]
|
||||
}
|
||||
@@ -555,10 +555,10 @@
|
||||
"sounds": [ { "name": "cobblemon:move/flamethrower/flamethrower_target", "volume": 0.8 } ]
|
||||
},
|
||||
"move.sandattack.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/sandattack/sand_attack_actor", "volume": 0.8 } ]
|
||||
"sounds": [ { "name": "cobblemon:move/sand_attack/sand_attack_actor", "volume": 0.8 } ]
|
||||
},
|
||||
"move.sandattack.target": {
|
||||
"sounds": [ { "name": "cobblemon:move/sandattack/sand_attack_target", "volume": 0.8 } ]
|
||||
"sounds": [ { "name": "cobblemon:move/sand_attack/sand_attack_target", "volume": 0.8 } ]
|
||||
},
|
||||
"move.confusion.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/confusion/confusion_actor", "volume": 0.9 } ]
|
||||
@@ -924,10 +924,10 @@
|
||||
},
|
||||
"particle.shiny_ambient_chime": {
|
||||
"sounds": [
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_1", "volume": 0.05 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_2", "volume": 0.05 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_3", "volume": 0.05 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_4", "volume": 0.05 }
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_1", "volume": 0.3 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_2", "volume": 0.3 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_3", "volume": 0.3 },
|
||||
{ "name": "cobblemon:shiny/shiny_ambient_chime_4", "volume": 0.3 }
|
||||
]
|
||||
},
|
||||
"particle.wild_shiny_ambient_chime": {
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 117 B |
|
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 186 B |
|
Before Width: | Height: | Size: 152 B After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 157 B |
|
After Width: | Height: | Size: 479 B |
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "cobblemon:battling",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
{
|
||||
"type": "exit_battle_when_hurt",
|
||||
"includePassiveDamage": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "panic",
|
||||
"tasksByPriority": {
|
||||
"0": ["calm_down", "follow_walk_target"],
|
||||
"1": ["flee_nearest_hostile"],
|
||||
"2": ["flee_attacker"],
|
||||
"3": [
|
||||
{
|
||||
"type": "wander",
|
||||
"speedMultiplier": 0.5,
|
||||
"horizontalRange": 3,
|
||||
"verticalRange": 3,
|
||||
"shouldWander": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
{
|
||||
"type": "switch_to_panic_when_hurt",
|
||||
"includePassiveDamage": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"initializationAction": "q.npc.is_in_battle_with(q.player) ? q.dialogue.close()",
|
||||
"initializationAction": "(q.npc.is_in_battle_with(q.player) || !q.npc.is_doing_activity('minecraft:idle', 'cobblemon:battling')) ? q.dialogue.close()",
|
||||
"speakers": {
|
||||
"npc": {
|
||||
"name": {
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
"cobblemon:looks",
|
||||
"cobblemon:floats",
|
||||
"cobblemon:pauses_in_battle",
|
||||
"cobblemon:cancels_battle_when_hurt_by_anything",
|
||||
"cobblemon:uses_healing_machine",
|
||||
"cobblemon:chats",
|
||||
"cobblemon:retaliates",
|
||||
"cobblemon:fights_melee"
|
||||
"cobblemon:panics_when_hurt_by_anything",
|
||||
"cobblemon:panics"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"EntityRenderDispatcherMixin",
|
||||
"GameRendererMixin",
|
||||
"ItemInHandRendererMixin",
|
||||
"ItemPropertiesMixin",
|
||||
"ItemRendererMixin",
|
||||
"ModelLoaderMixin",
|
||||
"ModelPartMixin",
|
||||
|
||||
@@ -23,4 +23,8 @@ class FabricPermissionValidator : PermissionValidator {
|
||||
override fun hasPermission(player: ServerPlayer, permission: Permission) = Permissions.check(player, permission.literal, permission.level.numericalValue)
|
||||
|
||||
override fun hasPermission(source: CommandSourceStack, permission: Permission) = Permissions.check(source, permission.literal, permission.level.numericalValue)
|
||||
|
||||
override fun hasPermission(player: ServerPlayer, permission: String, level: Int) = Permissions.check(player, permission, level)
|
||||
|
||||
override fun hasPermission(source: CommandSourceStack, permission: String, level: Int) = Permissions.check(source, permission, level)
|
||||
}
|
||||
@@ -42,12 +42,23 @@ object ForgePermissionValidator : PermissionValidator {
|
||||
return PermissionAPI.getPermission(player, node)
|
||||
}
|
||||
|
||||
override fun hasPermission(player: ServerPlayer, permission: String, level: Int): Boolean {
|
||||
val node = PermissionNode(permission.split(".").first(), permission.substringAfter("."), PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(level) == true })
|
||||
return PermissionAPI.getPermission(player, node)
|
||||
}
|
||||
|
||||
override fun hasPermission(source: CommandSourceStack, permission: Permission): Boolean {
|
||||
val player = this.extractPlayerFromSource(source) ?: return source.hasPermission(permission.level.numericalValue)
|
||||
val node = this.findNode(permission) ?: return source.hasPermission(permission.level.numericalValue)
|
||||
return PermissionAPI.getPermission(player, node)
|
||||
}
|
||||
|
||||
override fun hasPermission(source: CommandSourceStack, permission: String, level: Int): Boolean {
|
||||
val player = this.extractPlayerFromSource(source) ?: return source.hasPermission(4)
|
||||
val node = PermissionNode(permission.split(".").first(), permission.substringAfter("."), PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(level) == true })
|
||||
return PermissionAPI.getPermission(player, node)
|
||||
}
|
||||
|
||||
private fun createNodes() = CobblemonPermissions.all().map { permission ->
|
||||
// 3rd arg is default value if no implementation is present essentially
|
||||
val node = PermissionNode(permission.identifier, PermissionTypes.BOOLEAN, { player, _, _ -> player?.hasPermissions(permission.level.numericalValue) == true })
|
||||
|
||||