mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ff62821cd | ||
|
|
1fb300cd86 | ||
|
|
dae01c545d | ||
|
|
02c4947513 | ||
|
|
3048e3d81d | ||
|
|
77fb94d9ef | ||
|
|
9078ee2191 | ||
|
|
06edc2810e | ||
|
|
81b83aabc1 | ||
|
|
6e7895c3b3 | ||
|
|
c69de804b9 | ||
|
|
fbbed7b20a | ||
|
|
a1b5f0a567 | ||
|
|
e42e06781d | ||
|
|
0a48014931 | ||
|
|
a70fed25c5 | ||
|
|
c2daed803f | ||
|
|
658fed638d | ||
|
|
3faa2a0467 | ||
|
|
77788a91b7 | ||
|
|
745b48e77b | ||
|
|
9a4815f453 | ||
|
|
2ff41cdf9b | ||
|
|
d5268e8751 | ||
|
|
518799ee25 | ||
|
|
b597bb0818 | ||
|
|
1b39a64927 | ||
|
|
dda128e9e3 |
@@ -31,7 +31,9 @@
|
||||
- Added `/pctake <player> <box> <slot>` command that takes a specific Pokémon from a player's PC. Removes the pokemon if target is self or ran from the server.
|
||||
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
|
||||
- Added Galarica Nut Bushes
|
||||
- Many Pokémon (mostly cats) are now feared by phantoms
|
||||
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
|
||||
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
|
||||
|
||||
### Pokémon Added
|
||||
|
||||
@@ -225,6 +227,10 @@
|
||||
- Updated interaction interface to include 4 more option spaces
|
||||
- Made lecterns that hold a Pokédex emit light.
|
||||
- Updated light levels for active PC, Pasture, Healing Machine, and Data Monitor.
|
||||
- Vivichokes now always drop one seed when harvested, and converting a fully grown Vivichoke to seeds via crafting results in 1 seed.
|
||||
- Healing Machine recipe rebalanced.
|
||||
- Reorganised the `block` texture folder to be more organised, in line with the `item` texture folder.
|
||||
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -322,6 +328,9 @@
|
||||
- `PokemonEntity` instances spawned into the world now appropriately finalize the spawn for mod compatibility.
|
||||
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
|
||||
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
- `ElementalType` now implelments `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
|
||||
### MoLang & Datapacks
|
||||
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
|
||||
- The `requiredContext` for an item interaction evolution
|
||||
|
||||
@@ -1,39 +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.mixin;
|
||||
|
||||
import net.minecraft.world.entity.monster.Phantom;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(targets = "net/minecraft/world/entity/monster/Phantom$PhantomSweepAttackGoal")
|
||||
public class EntityPhantomMixin {
|
||||
// TODO: Figure out how to access the outer class that wraps SwoopMovementGoal
|
||||
// Need to find the cat mons that are near the phantom
|
||||
|
||||
// @Inject(method = "shouldContinue", at = @At(value = "RETURN"), cancellable = true)
|
||||
// public void cobblemon$shouldContinue(CallbackInfoReturnable<Boolean> ci) {
|
||||
//// var goalSelectorField = PhantomEntity.class.getClasses();// PhantomEntity.class.getFields();// .class.getDeclaredField("goalSelector");
|
||||
//
|
||||
//// goalSelectorField.setAccessible(true);
|
||||
//// GoalSelector goalSelector = (GoalSelector) goalSelectorField.get(mobEntity);
|
||||
//// final SwoopMovementGoal phantom = (SwoopMovementGoal) (Object) this;
|
||||
//// if(ci.getReturnValue() == false) {
|
||||
//// var result = this$0.getWorld().getEntitiesByClass(
|
||||
//// PokemonEntity.class,
|
||||
//// this$0.getBoundingBox().expand(16.0),
|
||||
//// entity -> {
|
||||
//// return ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom();
|
||||
//// }
|
||||
//// ).isEmpty();
|
||||
////
|
||||
//// ci.setReturnValue(result);
|
||||
//// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokemon.ai.EntityBehaviour;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.monster.Phantom;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.entity.monster.Phantom$PhantomSweepAttackGoal")
|
||||
public abstract class PhantomSweepAttackGoalMixin extends Goal {
|
||||
|
||||
@Unique
|
||||
private Phantom phantomSelf; // Allows access to the outer class
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void onInit(Phantom phantom, CallbackInfo ci) {
|
||||
this.phantomSelf = phantom;
|
||||
}
|
||||
|
||||
@Inject(method = "canContinueToUse", at = @At("RETURN"), cancellable = true)
|
||||
private void modifyCanContinueToUse(CallbackInfoReturnable<Boolean> cir) {
|
||||
Phantom phantom = this.phantomSelf; // Outer class instance
|
||||
if(phantom == null) return;
|
||||
|
||||
if (cir.getReturnValue()) {
|
||||
var nearbyFearedEntities = phantom.level().getEntities(phantom, phantom.getBoundingBox().inflate(16.0),
|
||||
(entity -> ((entity instanceof PokemonEntity && ((PokemonEntity) entity).getBeamMode() == 0 && ((PokemonEntity) entity).getBehaviour().getEntityInteract().getAvoidedByPhantom())
|
||||
|| (entity instanceof ServerPlayer && EntityBehaviour.Companion.hasPhantomFearedShoulderMount((ServerPlayer)entity)))));
|
||||
if (!nearbyFearedEntities.isEmpty()) {
|
||||
nearbyFearedEntities.forEach(entity -> {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
((PokemonEntity) entity).cry();
|
||||
}
|
||||
//TODO: Find a way to make shoulder mounted Pokemon cry
|
||||
});
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,21 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.world.entity.monster.piglin.PiglinAi;
|
||||
import com.cobblemon.mod.common.CobblemonMemories;
|
||||
import com.cobblemon.mod.common.entity.ai.PiglinDanceTarget;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.monster.piglin.*;
|
||||
import net.minecraft.world.entity.schedule.Activity;
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Mixin(PiglinAi.class)
|
||||
public class PiglinBarterMixin {
|
||||
@@ -29,5 +37,31 @@ public class PiglinBarterMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "makeBrain", at = @At("TAIL"), cancellable = true)
|
||||
private static void cobblemon$makeBrain(Piglin piglin, Brain<Piglin> brain, CallbackInfoReturnable<Brain<?>> cir) {
|
||||
// Get access to private task map
|
||||
brain.addActivityAndRemoveMemoryWhenStopped(Activity.INVESTIGATE, 10,
|
||||
ImmutableList.of(
|
||||
new PiglinDanceTarget()
|
||||
), CobblemonMemories.INSTANCE.getNEAREST_LOVED_ENTITY());
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "updateActivity(Lnet/minecraft/world/entity/monster/piglin/Piglin;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/ai/Brain;setActiveActivityToFirstValid(Ljava/util/List;)V"
|
||||
)
|
||||
)
|
||||
private static void redirectSetActiveActivityToFirstValid(
|
||||
Brain<Piglin> brain,
|
||||
List<Activity> originalList
|
||||
) {
|
||||
// Add new activity to the front
|
||||
List<Activity> newList = new ArrayList<>(List.of(Activity.INVESTIGATE));
|
||||
newList.addAll(originalList);
|
||||
|
||||
// Call original method with modified list
|
||||
brain.setActiveActivityToFirstValid(newList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,19 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import com.cobblemon.mod.common.CobblemonMemories;
|
||||
import com.cobblemon.mod.common.CobblemonSensors;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor;
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType;
|
||||
import net.minecraft.world.entity.monster.piglin.Piglin;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@@ -28,4 +37,27 @@ public abstract class PiglinEntityMixin {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private static ImmutableList<SensorType<? extends Sensor<? super Piglin>>> SENSOR_TYPES;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private static ImmutableList<MemoryModuleType<?>> MEMORY_TYPES;
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("TAIL"))
|
||||
private static void cobblemon$onInit(CallbackInfo ci) {
|
||||
SENSOR_TYPES = ImmutableList.<SensorType<? extends Sensor<? super Piglin>>>builder()
|
||||
.addAll(SENSOR_TYPES)
|
||||
.add(CobblemonSensors.INSTANCE.getNEARBY_PIGLIN_LOVED_POKEMON())
|
||||
.build();
|
||||
MEMORY_TYPES = ImmutableList.<MemoryModuleType<?>>builder()
|
||||
.addAll(MEMORY_TYPES)
|
||||
.add(CobblemonMemories.INSTANCE.getNEAREST_LOVED_ENTITY())
|
||||
.build();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities;
|
||||
import net.minecraft.world.entity.ai.sensing.PiglinSpecificSensor;
|
||||
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.CallbackInfo;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(PiglinSpecificSensor.class)
|
||||
public abstract class PiglinSpecificSensorMixin {
|
||||
|
||||
@Inject(method = "doTick", at = @At("TAIL"))
|
||||
protected void cobblemon$doTick(ServerLevel level, LivingEntity entity, CallbackInfo ci) {
|
||||
Brain<?> brain = entity.getBrain();
|
||||
if (!brain.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED)) {
|
||||
NearestVisibleLivingEntities nearestVisibleLivingEntities = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(NearestVisibleLivingEntities.empty());
|
||||
Optional<LivingEntity> fearedEntity = Optional.empty();
|
||||
for (LivingEntity livingEntity : nearestVisibleLivingEntities.findAll((x) -> true)) {
|
||||
if (fearedEntity.isEmpty() && livingEntity instanceof PokemonEntity && ((PokemonEntity) livingEntity).getPokemon().getSpecies().getBehaviour().getEntityInteract().getAvoidedByPiglin()) {
|
||||
fearedEntity = Optional.of(livingEntity);
|
||||
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, livingEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
import com.cobblemon.mod.common.CobblemonRecipeCategories;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotBookCategory;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase;
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotShapelessRecipe;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -48,10 +49,8 @@ public abstract class ClientRecipeBookMixin {
|
||||
@Inject(method = "getCategory", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategories> cir) {
|
||||
Recipe<?> recipe2 = recipe.value();
|
||||
if (recipe2 instanceof CookingPotRecipe || recipe2 instanceof CookingPotShapelessRecipe) {
|
||||
CookingPotBookCategory category = recipe2 instanceof CookingPotRecipe
|
||||
? ((CookingPotRecipe) recipe2).getCategory()
|
||||
: ((CookingPotShapelessRecipe) recipe2).category();
|
||||
if (recipe2 instanceof CookingPotRecipeBase) {
|
||||
CookingPotBookCategory category = ((CookingPotRecipeBase) recipe2).getCategory();
|
||||
RecipeBookCategories var7;
|
||||
switch (category) {
|
||||
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
|
||||
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
@@ -48,6 +49,7 @@ object CobblemonMemories {
|
||||
/** how many are following me rn? */
|
||||
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
|
||||
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
|
||||
val NEAREST_LOVED_ENTITY = register<PokemonEntity>("nearst_loved_entity")
|
||||
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
val memoryModule = MemoryModuleType(Optional.of(codec))
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DefendOwnerSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonAdultSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonGrowableCropSensor
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonPiglinLovedSensor
|
||||
import com.cobblemon.mod.common.pokemon.ai.PokemonDisturbancesSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.BattlingPokemonSensor
|
||||
import com.cobblemon.mod.common.entity.sensor.NPCBattlingSensor
|
||||
@@ -36,6 +37,8 @@ object CobblemonSensors {
|
||||
|
||||
val NEARBY_GROWABLE_CROPS = register("nearby_growable_crops", ::PokemonGrowableCropSensor)
|
||||
|
||||
val NEARBY_PIGLIN_LOVED_POKEMON = register("nearby_golden_pokemon", ::PokemonPiglinLovedSensor)
|
||||
|
||||
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
|
||||
val sensor = SensorType(supplier)
|
||||
sensors[id] = sensor
|
||||
|
||||
@@ -1552,8 +1552,8 @@ object MoLangFunctions {
|
||||
|
||||
map.put("identifier") { StringValue(species.resourceIdentifier.toString()) }
|
||||
map.put("name") { StringValue(species.name) }
|
||||
map.put("primary_type") { StringValue(species.primaryType.name) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.name ?: "null") }
|
||||
map.put("primary_type") { StringValue(species.primaryType.showdownId) }
|
||||
map.put("secondary_type") { StringValue(species.secondaryType?.showdownId ?: "null") }
|
||||
map.put("experience_group") { StringValue(species.experienceGroup.name) }
|
||||
map.put("height") { DoubleValue(species.height) }
|
||||
map.put("weight") { DoubleValue(species.weight) }
|
||||
|
||||
@@ -103,7 +103,7 @@ open class Move(
|
||||
"name" to Function { StringValue(name) },
|
||||
"display_name" to Function { StringValue(displayName.string) },
|
||||
"description" to Function { StringValue(description.string) },
|
||||
"type" to Function { StringValue(type.name) },
|
||||
"type" to Function { StringValue(type.showdownId) },
|
||||
"damage_category" to Function { StringValue(damageCategory.name) },
|
||||
"power" to Function { DoubleValue(power) },
|
||||
"accuracy" to Function { DoubleValue(accuracy) },
|
||||
|
||||
@@ -59,7 +59,7 @@ open class MoveTemplate(
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
.addFunction("name") { StringValue(name) }
|
||||
.addFunction("type") { StringValue(elementalType.name) }
|
||||
.addFunction("type") { StringValue(elementalType.showdownId) }
|
||||
.addFunction("damage_category") { StringValue(damageCategory.name) }
|
||||
.addFunction("power") { DoubleValue(power) }
|
||||
.addFunction("target") { StringValue(target.name) }
|
||||
|
||||
@@ -136,7 +136,7 @@ open class PokemonProperties {
|
||||
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
|
||||
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
|
||||
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
|
||||
props.type = parseStringOfRegistry(keyPairs, listOf("type", "elemental_type")) { ElementalTypes.get(it)?.name }
|
||||
props.type = parseStringOfRegistry(keyPairs, listOf("type", "elemental_type")) { ElementalTypes.get(it)?.showdownId }
|
||||
props.teraType = parseIdentifierOfRegistry(keyPairs, listOf("tera_type", "tera")) { TeraTypes.get(it)?.id?.simplify() }
|
||||
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
|
||||
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
enum class PokemonSortMode(val comparator: Comparator<Pokemon?>, val reverseComparator: Comparator<Pokemon?>) {
|
||||
NAME({ it?.getDisplayName()?.string }),
|
||||
LEVEL({ it?.level }),
|
||||
TYPE({ it?.primaryType?.name }),
|
||||
TYPE({ it?.primaryType?.showdownId }),
|
||||
POKEDEX_NUMBER({ it?.species?.nationalPokedexNumber }),
|
||||
GENDER({ it?.gender });
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
"H" to "No Ability",
|
||||
"S" to "No Ability"
|
||||
)
|
||||
val types = (form?.types ?: species.types).map { it.name.replaceFirstChar(Char::uppercase) }
|
||||
val types = (form?.types ?: species.types).map { it.name }
|
||||
val preevo: String? = (form?.preEvolution ?: species.preEvolution)?.let { if (it.form == it.species.standardForm) createShowdownName(it.species) else "${createShowdownName(it.species)}-${it.form.name}" }
|
||||
// For the context of battles the content here doesn't matter whatsoever and due to how PokemonProperties work we can't guarantee an actual specific species is defined.
|
||||
val evos = if ((form?.evolutions ?: species.evolutions).isEmpty()) emptyList() else arrayListOf("")
|
||||
|
||||
@@ -41,11 +41,15 @@ class EmitterSpace(
|
||||
|
||||
if (scaling == ScalingMode.ENTITY) {
|
||||
locatorMatrix.matrix.getScale(scale)
|
||||
|
||||
// If any value is zero we get NaNs, so default to a large enough value to be safe
|
||||
scale.x.coerceAtLeast(.01f)
|
||||
scale.y.coerceAtLeast(.01f)
|
||||
scale.z.coerceAtLeast(.01f)
|
||||
}
|
||||
|
||||
val particleScale = Vector3f(scale.x, scale.y, scale.z)
|
||||
//Presumably we will want to make the initial rotation configurable instead of always using root
|
||||
val particleRawMatrix = Matrix4f().scale(particleScale).rotate(rootRotation)
|
||||
val particleRawMatrix = Matrix4f().scale(scale).rotate(rootRotation)
|
||||
return MatrixWrapper().updateMatrix(particleRawMatrix).updatePosition(locatorMatrix.getOrigin())
|
||||
}
|
||||
|
||||
|
||||
+62
-10
@@ -53,9 +53,12 @@ interface ParticleEmitterShape : CodecMapped {
|
||||
}
|
||||
|
||||
val type: ParticleEmitterShapeType
|
||||
val defaultAttachedOptions: MutableMap<AttachedType, Boolean>
|
||||
get() = mutableMapOf(AttachedType.POSITION to true, AttachedType.ROTATION to false, AttachedType.SCALE to false)
|
||||
|
||||
fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3
|
||||
fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3
|
||||
fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> = defaultAttachedOptions // Default Values
|
||||
}
|
||||
|
||||
enum class ParticleEmitterShapeType {
|
||||
@@ -66,6 +69,12 @@ enum class ParticleEmitterShapeType {
|
||||
ENTITY_BOUNDING_BOX
|
||||
}
|
||||
|
||||
enum class AttachedType {
|
||||
ROTATION,
|
||||
SCALE,
|
||||
POSITION
|
||||
}
|
||||
|
||||
class SphereParticleEmitterShape(
|
||||
var offset: Triple<Expression, Expression, Expression> = Triple(
|
||||
NumberExpression(0.0),
|
||||
@@ -73,7 +82,8 @@ class SphereParticleEmitterShape(
|
||||
NumberExpression(0.0)
|
||||
),
|
||||
var radius: Expression = NumberExpression(0.0),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
) : ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<SphereParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -114,9 +124,7 @@ class SphereParticleEmitterShape(
|
||||
buffer.writeBoolean(surfaceOnly)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
return runtime.resolveVec3d(offset)
|
||||
}
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
val radius = runtime.resolveDouble(radius) * if (surfaceOnly) 1.0 else Random.Default.nextDouble()
|
||||
@@ -124,6 +132,16 @@ class SphereParticleEmitterShape(
|
||||
val psi = Math.PI * 2 * Random.Default.nextDouble()
|
||||
return getCenter(runtime, entity).add(convertSphericalToCartesian(radius = radius, theta = theta, psi = psi))
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
class PointParticleEmitterShape(
|
||||
@@ -131,7 +149,8 @@ class PointParticleEmitterShape(
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0)
|
||||
)
|
||||
),
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
): ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<PointParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -145,11 +164,21 @@ class PointParticleEmitterShape(
|
||||
}
|
||||
|
||||
override val type = ParticleEmitterShapeType.POINT
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
offset = Triple(
|
||||
@@ -177,7 +206,8 @@ class BoxParticleEmitterShape(
|
||||
NumberExpression(1.0),
|
||||
NumberExpression(1.0)
|
||||
),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
) : ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<BoxParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -226,7 +256,7 @@ class BoxParticleEmitterShape(
|
||||
buffer.writeBoolean(surfaceOnly)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?): Vec3 {
|
||||
val center = getCenter(runtime, entity)
|
||||
@@ -274,6 +304,16 @@ class BoxParticleEmitterShape(
|
||||
|
||||
return center.add(disposition)
|
||||
}
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
class DiscParticleEmitterShape(
|
||||
@@ -288,7 +328,8 @@ class DiscParticleEmitterShape(
|
||||
NumberExpression(1.0),
|
||||
NumberExpression(0.0)
|
||||
),
|
||||
var surfaceOnly: Boolean = false
|
||||
var surfaceOnly: Boolean = false,
|
||||
val pAttachmentOptions: Map<AttachedType, Boolean>? = null
|
||||
): ParticleEmitterShape {
|
||||
companion object {
|
||||
val CODEC: Codec<DiscParticleEmitterShape> = RecordCodecBuilder.create { instance ->
|
||||
@@ -333,7 +374,18 @@ class DiscParticleEmitterShape(
|
||||
return center.add(displacement)
|
||||
}
|
||||
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3 = runtime.resolveVec3d(offset).multiply(Vec3(-1.0, 1.0, 1.0))
|
||||
|
||||
override fun getAttachmentOptions(): MutableMap<AttachedType, Boolean> {
|
||||
val map = super.getAttachmentOptions()
|
||||
if (pAttachmentOptions != null) {
|
||||
AttachedType.entries.forEach {
|
||||
pAttachmentOptions[it]?.let { bool -> map.put(it, bool) }
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: RegistryFriendlyByteBuf) {
|
||||
offset = Triple(
|
||||
|
||||
@@ -100,7 +100,7 @@ class ParticleEvent(
|
||||
|
||||
val locatorMatrix = when (effect.type) {
|
||||
EventParticleOptions.EventParticleType.EMITTER -> particleMatrix
|
||||
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.locatorSpaceMatrix
|
||||
EventParticleOptions.EventParticleType.EMITTER_BOUND -> storm.attachedMatrix
|
||||
EventParticleOptions.EventParticleType.PARTICLE,
|
||||
EventParticleOptions.EventParticleType.PARTICLE_WITH_VELOCITY -> particleMatrix
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class ParticleEvent(
|
||||
val newStorm = ParticleStorm(
|
||||
effect = bedrockParticleOptions,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
locatorSpaceMatrix = locatorMatrix,
|
||||
attachedMatrix = locatorMatrix,
|
||||
world = storm.world,
|
||||
sourceVelocity = sourceVelocity,
|
||||
sourceAlive = storm.sourceAlive,
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
|
||||
if (species != null) {
|
||||
labels.addAll(
|
||||
species.secondaryType?.let { listOf(species.primaryType.name.lowercase(), it.name.lowercase()) }
|
||||
?: listOf(species.primaryType.name.lowercase())
|
||||
species.secondaryType?.let { listOf(species.primaryType.showdownId, it.showdownId) }
|
||||
?: listOf(species.primaryType.showdownId)
|
||||
)
|
||||
|
||||
if (height == -1) {
|
||||
|
||||
+6
-6
@@ -82,18 +82,18 @@ class PlayerAdvancementData {
|
||||
}
|
||||
|
||||
fun getTotalTypeCaptureCount(type: ElementalType): Int {
|
||||
if (!totalTypeCaptureCounts.containsKey(key = type.name)) {
|
||||
totalTypeCaptureCounts[type.name] = 0
|
||||
if (!totalTypeCaptureCounts.containsKey(key = type.showdownId)) {
|
||||
totalTypeCaptureCounts[type.showdownId] = 0
|
||||
}
|
||||
return totalTypeCaptureCounts.get(key = type.name) ?: 0
|
||||
return totalTypeCaptureCounts.get(key = type.showdownId) ?: 0
|
||||
}
|
||||
|
||||
fun updateTotalTypeCaptureCount(type: ElementalType) {
|
||||
val count = totalTypeCaptureCounts[type.name] ?: 0
|
||||
val count = totalTypeCaptureCounts[type.showdownId] ?: 0
|
||||
if (count == 0) {
|
||||
totalTypeCaptureCounts[type.name] = 1
|
||||
totalTypeCaptureCounts[type.showdownId] = 1
|
||||
} else {
|
||||
totalTypeCaptureCounts.replace(type.name, count + 1)
|
||||
totalTypeCaptureCounts.replace(type.showdownId, count + 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.util.codec.CodecUtils
|
||||
import com.mojang.serialization.Codec
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
@@ -21,20 +22,26 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @param displayName: A Component used to display the name, normally a TranslatableText
|
||||
* @param textureXMultiplier: The multiplier by which the TypeWidget shall move the display
|
||||
* @param resourceLocation: The location of the resource used in the TypeWidget
|
||||
* @param showdownId: The showdown ID of that element without spaces and all lowercase
|
||||
*/
|
||||
class ElementalType(
|
||||
val name: String,
|
||||
val displayName: MutableComponent,
|
||||
val hue: Int,
|
||||
val textureXMultiplier: Int,
|
||||
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png")
|
||||
) {
|
||||
val resourceLocation: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "ui/types.png"),
|
||||
val showdownId: String = ShowdownIdentifiable.REGEX.replace(name.lowercase(), "")
|
||||
) : ShowdownIdentifiable {
|
||||
|
||||
override fun showdownId(): String {
|
||||
return showdownId
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val BY_STRING_CODEC: Codec<ElementalType> = CodecUtils.createByStringCodec(
|
||||
ElementalTypes::get,
|
||||
ElementalType::name
|
||||
{it.showdownId}
|
||||
) { id -> "No ElementalType for ID $id" }
|
||||
}
|
||||
|
||||
|
||||
@@ -19,126 +19,126 @@ object ElementalTypes {
|
||||
private val allTypes = mutableListOf<ElementalType>()
|
||||
|
||||
val NORMAL = register(
|
||||
name = "normal",
|
||||
name = "Normal",
|
||||
displayName = Component.translatable("cobblemon.type.normal"),
|
||||
hue = 0xDDDDCF,
|
||||
textureXMultiplier = 0
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "fire",
|
||||
name = "Fire",
|
||||
displayName = Component.translatable("cobblemon.type.fire"),
|
||||
hue = 0xE55C32,
|
||||
textureXMultiplier = 1
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "water",
|
||||
name = "Water",
|
||||
displayName = Component.translatable("cobblemon.type.water"),
|
||||
hue = 0x4A9BE8,
|
||||
textureXMultiplier = 2
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "grass",
|
||||
name = "Grass",
|
||||
displayName = Component.translatable("cobblemon.type.grass"),
|
||||
hue = 0x4DBC3C,
|
||||
textureXMultiplier = 3
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "electric",
|
||||
name = "Electric",
|
||||
displayName = Component.translatable("cobblemon.type.electric"),
|
||||
hue = 0xEFD128,
|
||||
textureXMultiplier = 4
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "ice",
|
||||
name = "Ice",
|
||||
displayName = Component.translatable("cobblemon.type.ice"),
|
||||
hue = 0x6BC3EF,
|
||||
textureXMultiplier = 5
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "fighting",
|
||||
name = "Fighting",
|
||||
displayName = Component.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xC44C5C,
|
||||
textureXMultiplier = 6
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "poison",
|
||||
name = "Poison",
|
||||
displayName = Component.translatable("cobblemon.type.poison"),
|
||||
hue = 0xA24BD8,
|
||||
textureXMultiplier = 7
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "ground",
|
||||
name = "Ground",
|
||||
displayName = Component.translatable("cobblemon.type.ground"),
|
||||
hue = 0xD89950,
|
||||
textureXMultiplier = 8
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "flying",
|
||||
name = "Flying",
|
||||
displayName = Component.translatable("cobblemon.type.flying"),
|
||||
hue = 0xBCC1FF,
|
||||
textureXMultiplier = 9
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "psychic",
|
||||
name = "Psychic",
|
||||
displayName = Component.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xD86AD6,
|
||||
textureXMultiplier = 10
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "bug",
|
||||
name = "Bug",
|
||||
displayName = Component.translatable("cobblemon.type.bug"),
|
||||
hue = 0xA2C831,
|
||||
textureXMultiplier = 11
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "rock",
|
||||
name = "Rock",
|
||||
displayName = Component.translatable("cobblemon.type.rock"),
|
||||
hue = 0xAA9666,
|
||||
textureXMultiplier = 12
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "ghost",
|
||||
name = "Ghost",
|
||||
displayName = Component.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9572E5,
|
||||
textureXMultiplier = 13
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "dragon",
|
||||
name = "Dragon",
|
||||
displayName = Component.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x535DE8,
|
||||
textureXMultiplier = 14
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "dark",
|
||||
name = "Dark",
|
||||
displayName = Component.translatable("cobblemon.type.dark"),
|
||||
hue = 0x5C6CB2,
|
||||
textureXMultiplier = 15
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "steel",
|
||||
name = "Steel",
|
||||
displayName = Component.translatable("cobblemon.type.steel"),
|
||||
hue = 0xC3CCE0,
|
||||
textureXMultiplier = 16
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "fairy",
|
||||
name = "Fairy",
|
||||
displayName = Component.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xEA727E,
|
||||
textureXMultiplier = 17
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import java.lang.reflect.Type
|
||||
|
||||
object ElementalTypeAdapter: JsonSerializer<ElementalType>, JsonDeserializer<ElementalType> {
|
||||
override fun serialize(src: ElementalType, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.name)
|
||||
return JsonPrimitive(src.showdownId)
|
||||
}
|
||||
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ElementalType {
|
||||
|
||||
@@ -27,6 +27,11 @@ interface TeraType : ShowdownIdentifiable {
|
||||
*/
|
||||
val id: ResourceLocation
|
||||
|
||||
/**
|
||||
* The english readable name of the type, used when packing Pokemon for showdown
|
||||
*/
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* If this tera type can be selected naturally.
|
||||
*/
|
||||
@@ -44,5 +49,4 @@ interface TeraType : ShowdownIdentifiable {
|
||||
TeraType::id
|
||||
) { identifier -> "No TeraType for ID $identifier" }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,7 +110,13 @@ object TeraTypes : Iterable<TeraType> {
|
||||
* @return The found [TeraType] or null.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun get(id: String): TeraType? = this.get(cobblemonResource(id))
|
||||
fun get(id: String): TeraType? = this.get(cobblemonResource(id)) ?: this.getByName(id)
|
||||
|
||||
/**
|
||||
* Attempts to retrieve the [TeraType] by its english name.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getByName(name: String) = this.types.values.firstOrNull { it.name.equals(name, true) }
|
||||
|
||||
/**
|
||||
* Gets the corresponding tera type for a [ElementalType].
|
||||
@@ -119,7 +125,7 @@ object TeraTypes : Iterable<TeraType> {
|
||||
* @return The associated [TeraType].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun forElementalType(type: ElementalType): TeraType = this.get(cobblemonResource(type.name))!! // it's safe to do
|
||||
fun forElementalType(type: ElementalType): TeraType = this.get(cobblemonResource(type.showdownId))!! // it's safe to do
|
||||
|
||||
private fun create(id: ResourceLocation, type: TeraType): TeraType {
|
||||
this.types[id] = type
|
||||
|
||||
+4
-2
@@ -17,9 +17,11 @@ import net.minecraft.resources.ResourceLocation
|
||||
class ElementalTypeTeraType(val type: ElementalType) : TeraType {
|
||||
override val legalAsStatic: Boolean = true
|
||||
|
||||
override val id: ResourceLocation = cobblemonResource(this.type.name)
|
||||
override val name: String = this.type.name
|
||||
|
||||
override val id: ResourceLocation = cobblemonResource(this.type.showdownId)
|
||||
|
||||
override val displayName: Component = this.type.displayName
|
||||
|
||||
override fun showdownId(): String = this.type.name
|
||||
override fun showdownId(): String = this.type.showdownId()
|
||||
}
|
||||
+2
@@ -17,6 +17,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
class StellarTeraType : TeraType {
|
||||
override val id: ResourceLocation = ID
|
||||
|
||||
override val name: String = "Stellar"
|
||||
|
||||
override val legalAsStatic: Boolean = false
|
||||
|
||||
override val displayName: Component = LANG
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
@@ -113,7 +112,7 @@ object BattleRegistry {
|
||||
packedTeamBuilder.append("$pokeball,")
|
||||
// Hidden Power Type
|
||||
// This is empty if the pokemon is not Hyper Trained, and the provided type corresponds to base IVs and not battle IVs.
|
||||
val hiddenPowerType = if (pk.ivs.hyperTrainedIVs.any()) HiddenPowerUtil.getHiddenPowerType(pk).name.replaceFirstChar { it.uppercase() } else ""
|
||||
val hiddenPowerType = if (pk.ivs.hyperTrainedIVs.any()) HiddenPowerUtil.getHiddenPowerType(pk).name else ""
|
||||
packedTeamBuilder.append("$hiddenPowerType,")
|
||||
// Gigantamax
|
||||
packedTeamBuilder.append("${if (pk.gmaxFactor) "G" else ""},")
|
||||
@@ -121,7 +120,7 @@ object BattleRegistry {
|
||||
// 0 - 9, empty == 10
|
||||
packedTeamBuilder.append("${if (pk.dmaxLevel < 10) pk.dmaxLevel else ""},")
|
||||
// Teratype
|
||||
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.showdownId()},")
|
||||
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.name},")
|
||||
|
||||
team.add(packedTeamBuilder.toString())
|
||||
}
|
||||
|
||||
+38
-30
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
@@ -165,7 +166,9 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
val potItem = blockEntity.getPotItem()
|
||||
|
||||
if (!byWater && player != null) {
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, potItem)
|
||||
if (!player.isCreative && potItem != null) {
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, potItem)
|
||||
}
|
||||
} else {
|
||||
val direction = blockState.getValue(FACING) as Direction
|
||||
val f = 0.25F * direction.stepX.toFloat()
|
||||
@@ -267,35 +270,6 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player): BlockState {
|
||||
if (!level.isClientSide) {
|
||||
val blockEntity = level.getBlockEntity(blockPos)
|
||||
if (blockEntity is CampfireBlockEntity && !player.isCreative) {
|
||||
Containers.dropContents(level, blockPos, blockEntity)
|
||||
val potItem = blockEntity.getPotItem() ?: ItemStack.EMPTY
|
||||
|
||||
if (!potItem.isEmpty) {
|
||||
val direction = blockState.getValue(FACING) as Direction
|
||||
val f = 0.25F * direction.stepX.toFloat()
|
||||
val g = 0.25F * direction.stepZ.toFloat()
|
||||
|
||||
val itemEntity = ItemEntity(level, blockPos.x.toDouble() + 0.5 + f.toDouble(), (blockPos.y + 1).toDouble(), blockPos.z.toDouble() + 0.5 + g.toDouble(), potItem)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
level.playSoundServer(
|
||||
position = blockPos.toVec3d(),
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_OPEN,
|
||||
volume = 0.25F
|
||||
)
|
||||
}
|
||||
|
||||
return super.playerWillDestroy(level, blockPos, blockState, player)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
return if (state.getValue(SOUL)) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
}
|
||||
@@ -321,4 +295,38 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
override fun isSignalSource(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRemove(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
newState: BlockState,
|
||||
movedByPiston: Boolean
|
||||
) {
|
||||
if (!state.`is`(newState.block)) {
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (blockEntity is CampfireBlockEntity) {
|
||||
if (level is ServerLevel) {
|
||||
Containers.dropContents(level, pos, blockEntity)
|
||||
val potItem = blockEntity.getPotItem() ?: ItemStack.EMPTY
|
||||
|
||||
if (!potItem.isEmpty) {
|
||||
val direction = state.getValue(FACING) as Direction
|
||||
val f = 0.25F * direction.stepX.toFloat()
|
||||
val g = 0.25F * direction.stepZ.toFloat()
|
||||
|
||||
val itemEntity = ItemEntity(level, pos.x.toDouble() + 0.5 + f.toDouble(), (pos.y + 1).toDouble(), pos.z.toDouble() + 0.5 + g.toDouble(), potItem)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
super.onRemove(state, level, pos, newState, movedByPiston)
|
||||
level.updateNeighbourForOutputSignal(pos, this)
|
||||
} else {
|
||||
super.onRemove(state, level, pos, newState, movedByPiston)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val castedRecipe = recipe as RecipeHolder<CookingPotRecipeBase>
|
||||
|
||||
// Save seasoning contents in case the next recipe absorbs seasoning data
|
||||
// Save seasoning contents
|
||||
val seasoningSlots = CampfireBlockEntity.SEASONING_SLOTS
|
||||
val preservedSeasonings = seasoningSlots.map { container.getItem(it).copy() }
|
||||
|
||||
@@ -127,14 +127,9 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
this.finishPlacingRecipe(castedRecipe)
|
||||
}
|
||||
|
||||
// Check if new result item supports seasoning
|
||||
val result = castedRecipe.value().result
|
||||
if (itemTakesSeasoningData(result)) {
|
||||
seasoningSlots.forEachIndexed { index, slot ->
|
||||
container.setItem(slot, preservedSeasonings[index])
|
||||
}
|
||||
seasoningSlots.forEachIndexed { index, slot ->
|
||||
container.setItem(slot, preservedSeasonings[index])
|
||||
}
|
||||
|
||||
} else {
|
||||
throw IllegalArgumentException("Unsupported recipe type: ${recipeValue::class.java.name}")
|
||||
}
|
||||
@@ -192,7 +187,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun shouldMoveToInventory(slotIndex: Int): Boolean {
|
||||
return slotIndex != CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
return slotIndex != CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT && !CampfireBlockEntity.SEASONING_SLOTS.contains(slotIndex)
|
||||
}
|
||||
|
||||
override fun quickMoveStack(
|
||||
|
||||
+15
-8
@@ -143,7 +143,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
return optional.map { it as RecipeHolder<CookingPotRecipeBase> }
|
||||
}
|
||||
|
||||
// Check for both COOKING_POT_COOKING and COOKING_POT_SHAPELESS recipes
|
||||
// Check for all Cooking Pot Recipe Types recipes
|
||||
val optionalRecipe = fetchRecipe(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
.orElseGet { fetchRecipe(CobblemonRecipeTypes.COOKING_POT_SHAPELESS).orElse(null) }
|
||||
|
||||
@@ -191,7 +191,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
resultSlotItem.grow(cookedItem.count)
|
||||
}
|
||||
|
||||
campfireBlockEntity.consumeCraftingIngredients()
|
||||
campfireBlockEntity.consumeCraftingIngredients(recipe)
|
||||
|
||||
level.playSoundServer(
|
||||
position = pos.bottomCenter,
|
||||
@@ -270,21 +270,21 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
return particleStorm
|
||||
}
|
||||
|
||||
fun consumeCraftingIngredients() {
|
||||
for (i in CRAFTING_GRID_SLOTS.first..SEASONING_SLOTS.last) {
|
||||
val itemInSlot = getItem(i)
|
||||
fun consumeCraftingIngredients(recipe: CookingPotRecipeBase) {
|
||||
fun consumeItem(slot: Int) {
|
||||
val itemInSlot = getItem(slot)
|
||||
if (!itemInSlot.isEmpty) {
|
||||
when (itemInSlot.item) {
|
||||
Items.LAVA_BUCKET, Items.WATER_BUCKET, Items.MILK_BUCKET -> {
|
||||
// Replace with empty bucket
|
||||
setItem(i, ItemStack(Items.BUCKET))
|
||||
setItem(slot, ItemStack(Items.BUCKET))
|
||||
}
|
||||
|
||||
Items.HONEY_BOTTLE -> {
|
||||
// TODO: Currently eats the empty bottles until the honey bottle stack is empty, replace with better system later.
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(i, ItemStack.EMPTY)
|
||||
setItem(slot, ItemStack.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,12 +292,19 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
// Decrease the stack size by 1
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(i, ItemStack.EMPTY) // Clear the slot if empty
|
||||
setItem(slot, ItemStack.EMPTY) // Clear the slot if empty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i in CRAFTING_GRID_SLOTS.first..CRAFTING_GRID_SLOTS.last) {
|
||||
consumeItem(i)
|
||||
}
|
||||
for (i in SEASONING_SLOTS.first..SEASONING_SLOTS.last) {
|
||||
if (recipe.seasoningProcessors.any { it.consumesItem(getItem(i)) }) consumeItem(i)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSeasonings(): List<ItemStack> =
|
||||
|
||||
+7
@@ -35,6 +35,7 @@ import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.ClickType
|
||||
import net.minecraft.world.inventory.RecipeBookMenu
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
@@ -259,4 +260,10 @@ class CookingPotScreen(
|
||||
else Tooltip.create(Component.translatable("gui.recipebook.toggleRecipes.all"))
|
||||
)
|
||||
}
|
||||
|
||||
// Java nullability with kotlin is my greatest enemy, it'll complain but this works
|
||||
override fun slotClicked(slot: Slot?, slotId: Int, mouseButton: Int, type: ClickType) {
|
||||
super.slotClicked(slot, slotId, mouseButton, type)
|
||||
recipeBookComponent.slotClicked(slot)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -669,7 +669,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
val primaryType = type[0]
|
||||
if (primaryType != null) {
|
||||
return try {
|
||||
cobblemonResource("textures/gui/pokedex/platform_base_${primaryType.name}.png")
|
||||
cobblemonResource("textures/gui/pokedex/platform_base_${primaryType.showdownId}.png")
|
||||
} catch (error: FileNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
|
||||
val storm = ParticleStorm(
|
||||
effect = effect,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
locatorSpaceMatrix = locatorMatrix,
|
||||
attachedMatrix = locatorMatrix,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { sourceEntity.deltaMovement },
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.snowstorm.AttachedType
|
||||
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleOptions
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
@@ -33,6 +34,8 @@ import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.AxisAngle4f
|
||||
import org.joml.Vector3f
|
||||
import org.joml.Vector4f
|
||||
|
||||
/**
|
||||
@@ -44,11 +47,7 @@ import org.joml.Vector4f
|
||||
class ParticleStorm(
|
||||
val effect: BedrockParticleOptions,
|
||||
val emitterSpaceMatrix: MatrixWrapper,
|
||||
//This is a mildly inaccurate name. This matrix is basically the matrix we want to peg our movement to.
|
||||
//Often times, this is the locator matrix, but sometimes, when we want the emitter to NOT track the locator, this value is the same obj as emitterSpaceMatrix.
|
||||
//Currently only position is tracked. Ideally we would come up with a way to configure whether the emitter tracks any combination of position/rotation (or neither)
|
||||
//on this matrix.
|
||||
val locatorSpaceMatrix: MatrixWrapper,
|
||||
val attachedMatrix: MatrixWrapper, // Can be the same as emitterSpaceMatrix
|
||||
val world: ClientLevel,
|
||||
val sourceVelocity: () -> Vec3 = { Vec3.ZERO },
|
||||
val sourceAlive: () -> Boolean = { true },
|
||||
@@ -173,7 +172,7 @@ class ParticleStorm(
|
||||
ParticleStorm(
|
||||
effect = effect,
|
||||
emitterSpaceMatrix = matrixWrapper,
|
||||
locatorSpaceMatrix = matrixWrapper,
|
||||
attachedMatrix = matrixWrapper,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { entity.deltaMovement },
|
||||
@@ -191,7 +190,7 @@ class ParticleStorm(
|
||||
ParticleStorm(
|
||||
effect = effect,
|
||||
emitterSpaceMatrix = matrixWrapper,
|
||||
locatorSpaceMatrix = matrixWrapper,
|
||||
attachedMatrix = matrixWrapper,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { entity.deltaMovement },
|
||||
@@ -247,7 +246,7 @@ class ParticleStorm(
|
||||
return
|
||||
}
|
||||
|
||||
val pos = locatorSpaceMatrix.getOrigin()
|
||||
val pos = attachedMatrix.getOrigin()
|
||||
xo = x
|
||||
yo = y
|
||||
zo = z
|
||||
@@ -256,8 +255,38 @@ class ParticleStorm(
|
||||
y = pos.y
|
||||
z = pos.z
|
||||
|
||||
//Keeps emitter attached to locator
|
||||
emitterSpaceMatrix.updatePosition(locatorSpaceMatrix.getOrigin())
|
||||
if (emitterSpaceMatrix != attachedMatrix){
|
||||
val attachmentOptions = effect.emitter.shape.getAttachmentOptions()
|
||||
//Keeps emitter attached to locator based on the settings
|
||||
if (attachmentOptions[AttachedType.POSITION] == true){
|
||||
emitterSpaceMatrix.updatePosition(attachedMatrix.getOrigin())
|
||||
}
|
||||
|
||||
val setRotation = attachmentOptions[AttachedType.ROTATION] == true
|
||||
val setScale = attachmentOptions[AttachedType.SCALE] == true
|
||||
if (setRotation || setScale){
|
||||
val translation = emitterSpaceMatrix.matrix.getTranslation(Vector3f())
|
||||
val rotation = if (setRotation){
|
||||
attachedMatrix.matrix.getRotation(AxisAngle4f())
|
||||
} else {
|
||||
emitterSpaceMatrix.matrix.getRotation(AxisAngle4f())
|
||||
}
|
||||
val scale = if (setScale) {
|
||||
attachedMatrix.matrix.getScale(Vector3f())
|
||||
} else {
|
||||
emitterSpaceMatrix.matrix.getScale(Vector3f())
|
||||
}
|
||||
// If any value is zero we get NaNs, so default to a large enough value to be safe
|
||||
scale.x.coerceAtLeast(0.01f)
|
||||
scale.y.coerceAtLeast(0.01f)
|
||||
scale.z.coerceAtLeast(0.01f)
|
||||
|
||||
emitterSpaceMatrix.matrix.identity()
|
||||
.translate(translation)
|
||||
.rotate(rotation)
|
||||
.scale(scale)
|
||||
}
|
||||
}
|
||||
|
||||
val oldDistanceTravelled = distanceTravelled
|
||||
distanceTravelled += Vec3(x - xo, y - yo, z - zo).length().toFloat()
|
||||
@@ -292,12 +321,22 @@ class ParticleStorm(
|
||||
runtime.environment.setSimpleVariable("particle_random_3", DoubleValue(Random.Default.nextDouble()))
|
||||
runtime.environment.setSimpleVariable("particle_random_4", DoubleValue(Random.Default.nextDouble()))
|
||||
|
||||
val newPosition = transformPosition(effect.emitter.shape.getNewParticlePosition(runtime, entity))
|
||||
val pos = effect.emitter.shape.getNewParticlePosition(runtime, entity)
|
||||
val newPosition = if (effect.emitter.shape.getAttachmentOptions()[AttachedType.SCALE] == true) {
|
||||
transformPosition(pos)
|
||||
} else {
|
||||
transformPositionWithoutScale(pos)
|
||||
}
|
||||
return newPosition
|
||||
}
|
||||
|
||||
fun getNextParticleVelocity(nextParticlePosition: Vec3): Vec3 {
|
||||
val center = transformPosition(effect.emitter.shape.getCenter(runtime, entity))
|
||||
val pos = effect.emitter.shape.getCenter(runtime, entity)
|
||||
val center = if (effect.emitter.shape.getAttachmentOptions()[AttachedType.SCALE] == true) {
|
||||
transformPosition(pos)
|
||||
} else {
|
||||
transformPositionWithoutScale(pos)
|
||||
}
|
||||
val initialVelocity = effect.particle.motion.getInitialVelocity(runtime, storm = this, particlePos = nextParticlePosition, emitterPos = center)
|
||||
return initialVelocity
|
||||
.scale(1 / 20.0)
|
||||
@@ -313,6 +352,14 @@ class ParticleStorm(
|
||||
contextStorm = null
|
||||
}
|
||||
|
||||
// Sometimes we need to transform without scale, IE just rotation and position.
|
||||
fun transformPositionWithoutScale(position: Vec3): Vec3 {
|
||||
val matrixWrapper = emitterSpaceMatrix.clone()
|
||||
val scale = matrixWrapper.matrix.getScale(Vector3f())
|
||||
matrixWrapper.matrix.scale(1 / scale.x, 1 / scale.y, 1 / scale.z)
|
||||
return matrixWrapper.transformPosition(position)
|
||||
}
|
||||
|
||||
fun transformPosition(position: Vec3): Vec3 = emitterSpaceMatrix.transformPosition(position)
|
||||
|
||||
fun transformDirection(direction: Vec3): Vec3 = emitterSpaceMatrix.matrix.transformDirection(direction)
|
||||
|
||||
@@ -15,9 +15,7 @@ import com.cobblemon.mod.common.ModAPI
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleMaterial
|
||||
import com.cobblemon.mod.common.api.snowstorm.UVDetails
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformDirection
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.mojang.blaze3d.platform.GlStateManager
|
||||
@@ -289,7 +287,7 @@ class SnowstormParticle(
|
||||
//So instead of being bound to the emitter, the particle is actually bound to the locator for local rotation.
|
||||
//If you're messing with this - make sure evo particles still rotate with the pokemon
|
||||
if (storm.effect.space.localRotation) {
|
||||
storm.locatorSpaceMatrix.matrix.getRotation(axisRotation)
|
||||
storm.attachedMatrix.matrix.getRotation(axisRotation)
|
||||
}
|
||||
else {
|
||||
matrixWrapper.matrix.getRotation(axisRotation)
|
||||
@@ -371,7 +369,7 @@ class SnowstormParticle(
|
||||
}
|
||||
|
||||
fun updatePosition() {
|
||||
val localVector = if (storm.effect.space.localRotation) storm.locatorSpaceMatrix.matrix.transformDirection(
|
||||
val localVector = if (storm.effect.space.localRotation) storm.attachedMatrix.matrix.transformDirection(
|
||||
Vec3(
|
||||
localX,
|
||||
localY,
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : B
|
||||
|
||||
class CakeTexture(textureName: String, val color: LayerColor) {
|
||||
|
||||
val texture = loadTexture("block/${textureName}")
|
||||
val texture = loadTexture("block/food/${textureName}")
|
||||
|
||||
fun getColor(primaryColor: Int, secondaryColor: Int, tertiaryColor: Int): Int {
|
||||
return when (color) {
|
||||
|
||||
+2
-2
@@ -39,8 +39,8 @@ class CampfireBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : Bl
|
||||
const val BROTH_START = 0F
|
||||
const val BROTH_END = 1F
|
||||
const val BROTH_HEIGHT = 0.3F
|
||||
val BROTH_BASIC_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot_broth_basic"))
|
||||
val BROTH_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot_broth"))
|
||||
val BROTH_BASIC_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot/campfire_pot_broth_basic"))
|
||||
val BROTH_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot/campfire_pot_broth"))
|
||||
|
||||
const val CIRCLE_RADIUS = 0.4F
|
||||
const val ROTATION_SPEED = 1.5F
|
||||
|
||||
+2
@@ -742,6 +742,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
|
||||
if (entity is PokemonEntity) {
|
||||
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
|
||||
// If scale is 0 we start getting NaNs
|
||||
scale.coerceAtLeast(0.01f)
|
||||
if (entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
|
||||
&& (entity.controllingPassenger as OrientationControllable).orientationController.active){
|
||||
val controllingPassenger = entity.controllingPassenger as OrientationControllable
|
||||
|
||||
+1
-2
@@ -45,7 +45,6 @@ import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.commands.arguments.item.ItemParser
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.phys.Vec3
|
||||
@@ -359,7 +358,7 @@ abstract class PosableState : Schedulable {
|
||||
effect = effect,
|
||||
entity = entity,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
locatorSpaceMatrix = locatorMatrix,
|
||||
attachedMatrix = locatorMatrix,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { entity.deltaMovement },
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ class BedrockParticleKeyframe(
|
||||
val storm = ParticleStorm(
|
||||
effect = effect,
|
||||
emitterSpaceMatrix = particleMatrix,
|
||||
locatorSpaceMatrix = locatorMatrix,
|
||||
attachedMatrix = locatorMatrix,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { entity.deltaMovement },
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.ai
|
||||
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.EntityTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.entity.ai.util.LandRandomPos
|
||||
import net.minecraft.world.entity.monster.piglin.Piglin
|
||||
import java.util.*
|
||||
|
||||
class PiglinDanceTarget : Behavior<Piglin>(
|
||||
ImmutableMap.of(
|
||||
CobblemonMemories.NEAREST_LOVED_ENTITY, MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED,
|
||||
),
|
||||
Int.MAX_VALUE,
|
||||
Int.MAX_VALUE
|
||||
) {
|
||||
var lovedPokemon: PokemonEntity? = null
|
||||
val wayTooFar = 20F
|
||||
var tooFar = 5F
|
||||
var closeEnough = 3F
|
||||
var tooClose = 1.5F
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: Piglin): Boolean {
|
||||
lovedPokemon = (owner.brain.getMemory(CobblemonMemories.NEAREST_LOVED_ENTITY).orElse(null) ?: return false)
|
||||
return lovedPokemon != null
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: Piglin, gameTime: Long) : Boolean {
|
||||
return lovedPokemon != null && lovedPokemon?.isAlive == true && (lovedPokemon?.distanceTo(entity) ?: 999F) < wayTooFar
|
||||
}
|
||||
|
||||
|
||||
override fun tick(level: ServerLevel, entity: Piglin, gameTime: Long) {
|
||||
val leader : PokemonEntity = lovedPokemon ?: return
|
||||
if (entity.brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
|
||||
return // We're busy goin' n' shit
|
||||
}
|
||||
|
||||
if (leader.distanceTo(entity) > tooFar) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
CobblemonWalkTarget(
|
||||
pos = leader.blockPosition(),
|
||||
speedModifier = 0.6F,
|
||||
completionRange = closeEnough.toInt()
|
||||
)
|
||||
)
|
||||
} else if (leader.distanceTo(entity) < tooClose) {
|
||||
// Give the dude some space
|
||||
for (j in 0..9) {
|
||||
val vec3 = LandRandomPos.getPosAway(entity, 5, 7, leader.position())
|
||||
if (vec3 != null) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(
|
||||
vec3,
|
||||
0.4F,
|
||||
closeEnough.toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (leader.brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) {
|
||||
// Go to the leader's walk target, roughly.
|
||||
val walkTarget = leader.brain.getMemory(MemoryModuleType.WALK_TARGET).orElse(null) ?: return
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
CobblemonWalkTarget(
|
||||
pos = walkTarget.target.currentBlockPosition(),
|
||||
speedModifier = walkTarget.speedModifier,
|
||||
nodeTypeFilter = (walkTarget as? CobblemonWalkTarget)?.nodeTypeFilter ?: { true },
|
||||
completionRange = closeEnough.toInt()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Maybe wander around and mingle
|
||||
if (entity.random.nextFloat() < 0.01f) {
|
||||
val vec3 = LandRandomPos.getPosTowards(entity,5, 7, leader.position())
|
||||
if (vec3 != null) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(
|
||||
vec3,
|
||||
0.5F,
|
||||
1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: Piglin, gameTime: Long) {
|
||||
val pokemonEntity = lovedPokemon ?: return
|
||||
entity.brain.setMemory(MemoryModuleType.DANCING, true)
|
||||
entity.brain.setMemory(MemoryModuleType.CELEBRATE_LOCATION, Optional.of(pokemonEntity.blockPosition()))
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, EntityTracker(pokemonEntity, true))
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: Piglin, gameTime: Long) {
|
||||
entity.brain.setMemory(MemoryModuleType.DANCING, false)
|
||||
entity.brain.eraseMemory(MemoryModuleType.CELEBRATE_LOCATION)
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.entity.pokemon.ai.sensors
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||
|
||||
/**
|
||||
* Senses when the Pokémon is near an adult of the same species.
|
||||
*
|
||||
* @author JazzMcNade
|
||||
* @since May 15th, 2024
|
||||
*/
|
||||
class PokemonPiglinLovedSensor : Sensor<LivingEntity>(100) {
|
||||
override fun requires() = setOf(CobblemonMemories.NEAREST_LOVED_ENTITY, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)
|
||||
|
||||
override fun doTick(world: ServerLevel, entity: LivingEntity) {
|
||||
entity.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).ifPresent { visibleMobs ->
|
||||
setNearestPokemonAdult(entity, visibleMobs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNearestPokemonAdult(entity: LivingEntity, visibleMobs: NearestVisibleLivingEntities) {
|
||||
val nearestGoldenIdol = visibleMobs.findClosest { mob ->
|
||||
mob is PokemonEntity && mob.exposedSpecies.behaviour.entityInteract.lovedByPiglin
|
||||
}.map { mob -> mob as PokemonEntity }
|
||||
|
||||
entity.brain.setMemory(CobblemonMemories.NEAREST_LOVED_ENTITY, nearestGoldenIdol)
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ object AdvancementHandler : EventHandler {
|
||||
CobblemonCriteria.CATCH_POKEMON.trigger(event.player, CountablePokemonTypeContext(advancementData.totalCaptureCount, "any"))
|
||||
event.pokemon.types.forEach {
|
||||
advancementData.updateTotalTypeCaptureCount(it)
|
||||
CobblemonCriteria.CATCH_POKEMON.trigger(event.player, CountablePokemonTypeContext(advancementData.getTotalTypeCaptureCount(it), it.name))
|
||||
CobblemonCriteria.CATCH_POKEMON.trigger(event.player, CountablePokemonTypeContext(advancementData.getTotalTypeCaptureCount(it), it.showdownId))
|
||||
}
|
||||
if (event.pokemon.shiny) {
|
||||
advancementData.updateTotalShinyCaptureCount()
|
||||
|
||||
@@ -30,8 +30,8 @@ class RegionalFoodItem(properties: Properties) : Item(properties), PokemonSelect
|
||||
override fun use(world: Level, player: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
val stack = player.getItemInHand(hand)
|
||||
|
||||
// Allow eating normally if player needs food
|
||||
if (player.foodData.needsFood()) {
|
||||
// Allow eating normally if player needs food OR in creative
|
||||
if (player.foodData.needsFood() || player.isCreative) {
|
||||
player.startUsingItem(hand)
|
||||
return InteractionResultHolder.consume(stack)
|
||||
}
|
||||
|
||||
+5
@@ -33,4 +33,9 @@ object BaitSeasoningProcessor : SeasoningProcessor {
|
||||
}
|
||||
result.set(CobblemonItemComponents.BAIT_EFFECTS, BaitEffectsComponent(baitIdentifiers.toList()))
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData != null && !seasoningData.baitEffects.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
+5
@@ -39,4 +39,9 @@ object FlavourSeasoningProcessor : SeasoningProcessor {
|
||||
|
||||
result.set(CobblemonItemComponents.FLAVOUR, FlavourComponent(flavours))
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData != null && !seasoningData.flavours.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -19,9 +19,14 @@ object FoodColourSeasoningProcessor : SeasoningProcessor {
|
||||
override fun apply(result: ItemStack, seasoning: List<ItemStack>) {
|
||||
val colours = mutableListOf<DyeColor>()
|
||||
for (seasoningStack in seasoning) {
|
||||
val seasoning = Seasonings.getFromItemStack(seasoningStack)
|
||||
seasoning?.colour?.run(colours::add)
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoningStack)
|
||||
seasoningData?.colour?.run(colours::add)
|
||||
}
|
||||
result.set(CobblemonItemComponents.FOOD_COLOUR, FoodColourComponent(colours))
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData != null
|
||||
}
|
||||
}
|
||||
+5
@@ -36,4 +36,9 @@ object FoodSeasoningProcessor : SeasoningProcessor {
|
||||
result.set(CobblemonItemComponents.FOOD, FoodComponent(baseHunger, baseSaturation))
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData?.food != null
|
||||
}
|
||||
}
|
||||
+5
@@ -28,4 +28,9 @@ object IngredientSeasoningProcessor : SeasoningProcessor {
|
||||
result.set(CobblemonItemComponents.INGREDIENT, IngredientComponent(ingredients))
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData != null
|
||||
}
|
||||
}
|
||||
+5
@@ -29,4 +29,9 @@ object MobEffectSeasoningProcessor : SeasoningProcessor {
|
||||
result.set(CobblemonItemComponents.MOB_EFFECTS, MobEffectsComponent(mergedEffects))
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumesItem(seasoning: ItemStack): Boolean {
|
||||
val seasoningData = Seasonings.getFromItemStack(seasoning)
|
||||
return seasoningData?.mobEffects != null
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,5 @@ interface SeasoningProcessor {
|
||||
|
||||
val type: String
|
||||
fun apply(result: ItemStack, seasoning: List<ItemStack>)
|
||||
fun consumesItem(seasoning: ItemStack): Boolean
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ class MovesRegistrySyncPacket(moves: List<MoveTemplate>) : DataRegistrySyncPacke
|
||||
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: MoveTemplate) {
|
||||
buffer.writeString(entry.name)
|
||||
buffer.writeInt(entry.num)
|
||||
buffer.writeString(entry.elementalType.name)
|
||||
buffer.writeString(entry.elementalType.showdownId)
|
||||
buffer.writeString(entry.damageCategory.name)
|
||||
buffer.writeDouble(entry.power)
|
||||
buffer.writeEnumConstant(entry.target)
|
||||
|
||||
+41
-12
@@ -21,6 +21,8 @@ import com.google.gson.JsonPrimitive
|
||||
import com.mojang.serialization.JsonOps
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.joml.Vector4f
|
||||
import java.util.Locale
|
||||
import java.util.Locale.getDefault
|
||||
|
||||
object SnowstormParticleReader {
|
||||
fun loadEffect(json: JsonObject): BedrockParticleOptions {
|
||||
@@ -182,21 +184,36 @@ object SnowstormParticleReader {
|
||||
}
|
||||
}
|
||||
|
||||
val space = spaceJson?.let {
|
||||
val spaceRotation = it.get("rotation")?.asBoolean ?: false
|
||||
ParticleSpace(
|
||||
localPosition = if (spaceRotation) true else it.get("position")?.asBoolean ?: false,
|
||||
localRotation = spaceRotation,
|
||||
localVelocity = it.get("velocity")?.asBoolean ?: false
|
||||
)
|
||||
} ?: ParticleSpace()
|
||||
|
||||
val shape = if (emitterShapePointJson != null) {
|
||||
val arr = emitterShapePointJson.get("offset")?.asJsonArray?.map { it.asString.asExpression() } ?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
|
||||
val map = emitterShapePointJson.get("attachment_options")?.asJsonObject?.let { parseAttachment(it) } ?: mutableMapOf()
|
||||
if (space.localRotation && map[AttachedType.ROTATION] == null) map[AttachedType.ROTATION] = true
|
||||
resolveDirection(emitterShapePointJson)
|
||||
PointParticleEmitterShape(offset = Triple(arr[0], arr[1], arr[2]))
|
||||
PointParticleEmitterShape(offset = Triple(arr[0], arr[1], arr[2]), pAttachmentOptions = map)
|
||||
} else if (emitterShapeSphereJson != null) {
|
||||
val arr = emitterShapeSphereJson.get("offset")?.asJsonArray?.map { it.asString.asExpression() } ?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
|
||||
val map = emitterShapeSphereJson.get("attachment_options")?.asJsonObject?.let { parseAttachment(it) }
|
||||
resolveDirection(emitterShapeSphereJson)
|
||||
SphereParticleEmitterShape(
|
||||
offset = Triple(arr[0], arr[1], arr[2]),
|
||||
radius = emitterShapeSphereJson.get("radius")?.asString?.asExpression() ?: 0.0.asExpression(),
|
||||
surfaceOnly = emitterShapeSphereJson.get("surface_only")?.asBoolean ?: false
|
||||
surfaceOnly = emitterShapeSphereJson.get("surface_only")?.asBoolean ?: false,
|
||||
pAttachmentOptions = map
|
||||
)
|
||||
} else if (emitterShapeDiscJson != null) {
|
||||
resolveDirection(emitterShapeDiscJson)
|
||||
val offsetExpressions = emitterShapeDiscJson.get("offset")?.asJsonArray?.map { it.asString.asExpression() } ?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
|
||||
val map = emitterShapeDiscJson.get("attachment_options")?.asJsonObject?.let { parseAttachment(it) } ?: mutableMapOf()
|
||||
if (space.localRotation && map[AttachedType.ROTATION] == null) map[AttachedType.ROTATION] = true
|
||||
val normalJson = emitterShapeDiscJson.get("plane_normal") ?: JsonPrimitive("y")
|
||||
val normal: Triple<Expression, Expression, Expression> = if (normalJson.isJsonArray) {
|
||||
val normalArr = normalJson.asJsonArray.map { it.asString.asExpression() }
|
||||
@@ -214,7 +231,8 @@ object SnowstormParticleReader {
|
||||
offset = Triple(offsetExpressions[0], offsetExpressions[1], offsetExpressions[2]),
|
||||
radius = emitterShapeDiscJson.get("radius")?.asString?.asExpression() ?: 0.0.asExpression(),
|
||||
surfaceOnly = emitterShapeDiscJson.get("surface_only")?.asBoolean ?: false,
|
||||
normal = normal
|
||||
normal = normal,
|
||||
pAttachmentOptions = map
|
||||
)
|
||||
} else if (emitterShapeBoxJson != null) {
|
||||
resolveDirection(emitterShapeBoxJson)
|
||||
@@ -222,10 +240,13 @@ object SnowstormParticleReader {
|
||||
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
|
||||
val boxSizeExpressions = emitterShapeBoxJson.get("half_dimensions")?.asJsonArray?.map { it.asString.asExpression() }
|
||||
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
|
||||
val map = emitterShapeBoxJson.get("attachment_options")?.asJsonObject?.let { parseAttachment(it) } ?: mutableMapOf()
|
||||
if (space.localRotation && map[AttachedType.ROTATION] == null) map[AttachedType.ROTATION] = true
|
||||
BoxParticleEmitterShape(
|
||||
offset = Triple(offsetExpressions[0], offsetExpressions[1], offsetExpressions[2]),
|
||||
boxSize = Triple(boxSizeExpressions[0], boxSizeExpressions[1], boxSizeExpressions[2]),
|
||||
surfaceOnly = emitterShapeBoxJson.get("surface_only")?.asBoolean ?: false
|
||||
surfaceOnly = emitterShapeBoxJson.get("surface_only")?.asBoolean ?: false,
|
||||
pAttachmentOptions = map
|
||||
)
|
||||
} else if (emitterShapeEntityBoundingBoxJson != null) {
|
||||
resolveDirection(emitterShapeEntityBoundingBoxJson)
|
||||
@@ -356,14 +377,6 @@ object SnowstormParticleReader {
|
||||
expiresOnContact = it.get("expire_on_contact")?.asBoolean ?: false
|
||||
)
|
||||
} ?: ParticleCollision()
|
||||
val space = spaceJson?.let {
|
||||
val spaceRotation = it.get("rotation")?.asBoolean ?: false
|
||||
ParticleSpace(
|
||||
localPosition = if (spaceRotation) true else it.get("position")?.asBoolean ?: false,
|
||||
localRotation = spaceRotation,
|
||||
localVelocity = it.get("velocity")?.asBoolean ?: false
|
||||
)
|
||||
} ?: ParticleSpace()
|
||||
|
||||
val particleEventSet = particleLifetimeEventsJson?.let {
|
||||
val creationEvents = it.get("creation_event")?.normalizeToArray()?.map { SimpleEventTrigger(it.asString) }?.toMutableList() ?: mutableListOf()
|
||||
@@ -450,4 +463,20 @@ object SnowstormParticleReader {
|
||||
alphaHex.toInt(16) / 255F
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseAttachment(jsonObject: JsonObject): MutableMap<AttachedType, Boolean> {
|
||||
return jsonObject
|
||||
.asMap()
|
||||
.filter{ (key, _) -> safeValueOf<AttachedType>(key.uppercase()) != null }
|
||||
.map { (key, value) -> enumValueOf<AttachedType>(key.uppercase()) to value.asBoolean }.toMap().toMutableMap()
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/35666815/enum-valueof-in-kotlin
|
||||
inline fun <reified T : Enum<T>> safeValueOf(string: String): T? {
|
||||
return try {
|
||||
enumValueOf<T>(string)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,8 +284,8 @@ class FormData(
|
||||
{ _, value -> buffer.writeSizedInt(IntSize.U_SHORT, value) }
|
||||
)
|
||||
}
|
||||
buffer.writeNullable(this._primaryType) { pb, type -> pb.writeString(type.name) }
|
||||
buffer.writeNullable(this._secondaryType) { pb, type -> pb.writeString(type.name) }
|
||||
buffer.writeNullable(this._primaryType) { pb, type -> pb.writeString(type.showdownId) }
|
||||
buffer.writeNullable(this._secondaryType) { pb, type -> pb.writeString(type.showdownId) }
|
||||
buffer.writeNullable(this._experienceGroup) { pb, value -> pb.writeString(value.name) }
|
||||
buffer.writeNullable(this._height) { pb, height -> pb.writeFloat(height) }
|
||||
buffer.writeNullable(this._weight) { pb, weight -> pb.writeFloat(weight) }
|
||||
|
||||
@@ -230,8 +230,8 @@ class Species : ClientDataSynchronizer<Species>, ShowdownIdentifiable {
|
||||
{ _, value -> buffer.writeSizedInt(IntSize.U_SHORT, value) }
|
||||
)
|
||||
// ToDo remake once we have custom typing support
|
||||
buffer.writeString(this.primaryType.name)
|
||||
buffer.writeNullable(this.secondaryType) { pb, type -> pb.writeString(type.name) }
|
||||
buffer.writeString(this.primaryType.showdownId)
|
||||
buffer.writeNullable(this.secondaryType) { pb, type -> pb.writeString(type.showdownId) }
|
||||
buffer.writeString(this.experienceGroup.name)
|
||||
buffer.writeFloat(this.height)
|
||||
buffer.writeFloat(this.weight)
|
||||
|
||||
@@ -20,14 +20,18 @@ import net.minecraft.server.level.ServerPlayer
|
||||
*/
|
||||
class EntityBehaviour {
|
||||
val avoidedByCreeper = false
|
||||
// val avoidedByPhantom = false
|
||||
val avoidedByPhantom = false
|
||||
val avoidedBySkeleton = false
|
||||
val lovedByPiglin = false
|
||||
val avoidedByPiglin = false
|
||||
|
||||
@Transient
|
||||
val struct = ObjectValue(this).also {
|
||||
it.addFunction("avoided_by_creeper") { DoubleValue(avoidedByCreeper) }
|
||||
// it.addFunction("avoided_by_phantom") { DoubleValue(avoidedByPhantom) }
|
||||
it.addFunction("avoided_by_phantom") { DoubleValue(avoidedByPhantom) }
|
||||
it.addFunction("avoided_by_skeleton") { DoubleValue(avoidedBySkeleton) }
|
||||
it.addFunction("loved_by_piglin") { DoubleValue(lovedByPiglin) }
|
||||
it.addFunction("avoid_by_piglin") { DoubleValue(avoidedByPiglin) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -38,5 +42,9 @@ class EntityBehaviour {
|
||||
fun hasSkeletonFearedShoulderMount(player: ServerPlayer) : Boolean {
|
||||
return player.party().any { pokemon -> pokemon.state is ShoulderedState && pokemon.form.behaviour.entityInteract.avoidedBySkeleton }
|
||||
}
|
||||
|
||||
fun hasPhantomFearedShoulderMount(player: ServerPlayer) : Boolean {
|
||||
return player.party().any { pokemon -> pokemon.state is ShoulderedState && pokemon.form.behaviour.entityInteract.avoidedByPhantom }
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/black_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/black_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/blue_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/blue_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/green_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/green_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/pink_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/pink_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/white_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/white_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:gilded_chest",
|
||||
"model": "cobblemon:gilded_chest.geo",
|
||||
"texture": "cobblemon:textures/block/yellow_gilded_chest.png",
|
||||
"texture": "cobblemon:textures/block/functional/yellow_gilded_chest.png",
|
||||
"layers": []
|
||||
}
|
||||
]
|
||||
|
||||
+1
@@ -30,6 +30,7 @@
|
||||
0,
|
||||
1
|
||||
],
|
||||
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "builtin/entity"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
{
|
||||
"variants": {
|
||||
"facing=down": {
|
||||
"model": "cobblemon:block/saccharine_log_slathered_0"
|
||||
},
|
||||
"facing=up": {
|
||||
"model": "cobblemon:block/saccharine_log_slathered_0"
|
||||
},
|
||||
"facing=east,honey_type=0": {
|
||||
"model": "cobblemon:block/saccharine_log_slathered_0",
|
||||
"y": 90
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/button",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/button_inventory",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/button_pressed",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_bottom_left",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_bottom_left_open",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_bottom_right",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_bottom_right_open",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_top_left",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_top_left_open",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_top_right",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/door_top_right_open",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/apricorn_door_top"
|
||||
"bottom": "cobblemon:block/wood/apricorn_door_bottom",
|
||||
"top": "cobblemon:block/wood/apricorn_door_top"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_fence_gate",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_fence_gate_open",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_fence_gate_wall",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/template_fence_gate_wall_open",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/fence_inventory",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/fence_post",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/fence_side",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "cobblemon:block/stripped_apricorn_log"
|
||||
"particle": "cobblemon:block/wood/stripped_apricorn_log"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "cobblemon:block/apricorn_leaves"
|
||||
"all": "cobblemon:block/wood/apricorn_leaves"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_column",
|
||||
"textures": {
|
||||
"end": "cobblemon:block/apricorn_log_top",
|
||||
"side": "cobblemon:block/apricorn_log"
|
||||
"end": "cobblemon:block/wood/apricorn_log_top",
|
||||
"side": "cobblemon:block/wood/apricorn_log"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_column_horizontal",
|
||||
"textures": {
|
||||
"end": "cobblemon:block/apricorn_log_top",
|
||||
"side": "cobblemon:block/apricorn_log"
|
||||
"end": "cobblemon:block/wood/apricorn_log_top",
|
||||
"side": "cobblemon:block/wood/apricorn_log"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "cobblemon:block/apricorn_planks"
|
||||
"all": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pressure_plate_up",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/pressure_plate_down",
|
||||
"textures": {
|
||||
"texture": "cobblemon:block/apricorn_planks"
|
||||
"texture": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "cobblemon:block/apricorn_planks"
|
||||
"particle": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"parent": "minecraft:block/slab",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_planks",
|
||||
"top": "cobblemon:block/apricorn_planks",
|
||||
"side": "cobblemon:block/apricorn_planks"
|
||||
"bottom": "cobblemon:block/wood/apricorn_planks",
|
||||
"top": "cobblemon:block/wood/apricorn_planks",
|
||||
"side": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"parent": "minecraft:block/slab_top",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_planks",
|
||||
"top": "cobblemon:block/apricorn_planks",
|
||||
"side": "cobblemon:block/apricorn_planks"
|
||||
"bottom": "cobblemon:block/wood/apricorn_planks",
|
||||
"top": "cobblemon:block/wood/apricorn_planks",
|
||||
"side": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"textures": {
|
||||
"1": "cobblemon:block/apricorn_stage_0",
|
||||
"particle": "cobblemon:block/apricorn_stage_0"
|
||||
"1": "cobblemon:block/crops/apricorn_stage_0",
|
||||
"particle": "cobblemon:block/crops/apricorn_stage_0"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"textures": {
|
||||
"1": "cobblemon:block/apricorn_stage_1",
|
||||
"particle": "cobblemon:block/apricorn_stage_1"
|
||||
"1": "cobblemon:block/crops/apricorn_stage_1",
|
||||
"particle": "cobblemon:block/crops/apricorn_stage_1"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"texture_size": [32, 16],
|
||||
"textures": {
|
||||
"1": "cobblemon:block/apricorn_stage_2",
|
||||
"particle": "cobblemon:block/apricorn_stage_2"
|
||||
"1": "cobblemon:block/crops/apricorn_stage_2",
|
||||
"particle": "cobblemon:block/crops/apricorn_stage_2"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"parent": "minecraft:block/stairs",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_planks",
|
||||
"top": "cobblemon:block/apricorn_planks",
|
||||
"side": "cobblemon:block/apricorn_planks"
|
||||
"bottom": "cobblemon:block/wood/apricorn_planks",
|
||||
"top": "cobblemon:block/wood/apricorn_planks",
|
||||
"side": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"parent": "minecraft:block/inner_stairs",
|
||||
"textures": {
|
||||
"bottom": "cobblemon:block/apricorn_planks",
|
||||
"top": "cobblemon:block/apricorn_planks",
|
||||
"side": "cobblemon:block/apricorn_planks"
|
||||
"bottom": "cobblemon:block/wood/apricorn_planks",
|
||||
"top": "cobblemon:block/wood/apricorn_planks",
|
||||
"side": "cobblemon:block/wood/apricorn_planks"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user