mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 22:56:23 -04:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f82f381d4 | ||
|
|
4d75103a68 | ||
|
|
ff2db8fa11 | ||
|
|
42d330e1c0 | ||
|
|
062cee2b9a | ||
|
|
5730d812a0 | ||
|
|
cb4ad09a11 | ||
|
|
1d81df91eb | ||
|
|
d962d30074 | ||
|
|
5096d9a15a | ||
|
|
24737cf606 | ||
|
|
7bd1c24b57 | ||
|
|
36b1bec04b | ||
|
|
7f14b60efb | ||
|
|
239559cfd3 | ||
|
|
c4b2a1546a | ||
|
|
278b36234e | ||
|
|
1598c8209a | ||
|
|
deb7ece946 | ||
|
|
d8f9f8926b | ||
|
|
9d8803caf2 | ||
|
|
08827a742f | ||
|
|
a6a76052a6 | ||
|
|
abeb005bbb | ||
|
|
6835232aa6 | ||
|
|
78ebc9a9eb | ||
|
|
f5e85971f3 | ||
|
|
e85b2aa138 | ||
|
|
371e1426a3 | ||
|
|
256e09846d | ||
|
|
dd2d441b0c | ||
|
|
ddc57ecd8d | ||
|
|
202a6a0496 | ||
|
|
fbc639cbf3 | ||
|
|
78faf30b32 | ||
|
|
39cbb448a6 | ||
|
|
fd10f8efd8 | ||
|
|
9161f61017 | ||
|
|
4244680956 | ||
|
|
92ad81f8d1 | ||
|
|
c1c7106bff | ||
|
|
e36b1b22c4 | ||
|
|
e21d445e14 | ||
|
|
098ff1f96b | ||
|
|
f93445f081 | ||
|
|
85a962e924 | ||
|
|
f6d0f11c99 | ||
|
|
e74b838fb2 |
+8
-3
@@ -8,11 +8,13 @@
|
||||
- Added big roots, generating from cave ceiling dirt and sometimes spreading into energy roots.
|
||||
- Added trading between players. Press R while looking at another player, you'll figure it out.
|
||||
- Added pasture blocks, used to let your PC Pokémon roam around your base.
|
||||
- Added 69 berry trees. Some are found in village farms, some from planting different berries close to each other.
|
||||
- Added medicine brewing using medicinal leeks and berries in brewing stands.
|
||||
- Added Pokémon cries when in battles and being sent out.
|
||||
- Added support for scale in animations.
|
||||
- Added support for jump keyframes (i.e. pre and post keyframes)
|
||||
- Made Nosepass point towards world spawn while idle.
|
||||
- Added AI for Nosepass to point towards world spawn when idle.
|
||||
- Added structure spawn conditions
|
||||
- Added Pokémon cry system for battles and sending out.
|
||||
- Added cries for Gen 1-9 starters and their evolutions.
|
||||
- Added cries for Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Cleffa, Clefairy, Clefable, Tauros, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
|
||||
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, Health Feather, Muscle Feather, Resist Feather, Genius Feather, Clever Feather, Genius Feather
|
||||
@@ -192,6 +194,8 @@
|
||||
- Updated PC Recipe.
|
||||
- Improved Pokémon AI and movement.
|
||||
- Friendship will slowly increase when Pokémon are shoulder-mounted.
|
||||
- Master balls are now unable to be burned when dropped into fire/lava.
|
||||
- Pokémon will appear red when hurt, like regular entities, except when they're fainting.
|
||||
|
||||
### Fixes
|
||||
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong
|
||||
@@ -222,10 +226,11 @@
|
||||
- Fixed particle animations not running when a Pokémon is off-screen.
|
||||
- Fixed Pokémon variants and layers not rendering correctly when shouldered and playing on a dedicated server, existing shoulders affected will need to be retrieved and shouldered again.
|
||||
- Shoulder effects will now stay applied through situations that remove potion effects such as drinking milk.
|
||||
- Fixed Shedninja evolving to use the consumed Pokeball and removed the held item.
|
||||
- Fixed Shedinja evolving to use the consumed Pokeball and removed the held item.
|
||||
- Fixed non-existent species in spawn pool file causing random species to spawn.
|
||||
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
|
||||
- Fixed some alignment issues in the stat hexagon of the summary menu.
|
||||
- Fixed capture calculations not applying ball bonuses entirely correctly.
|
||||
|
||||
### Developer
|
||||
- Added SpawnEvent
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright (C) 2023 Cobblemon Contributors
|
||||
* *
|
||||
* * This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* * License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
val accessWidenerFile = "src/main/resources/cobblemon-common.accesswidener"
|
||||
@@ -13,6 +23,8 @@ plugins {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
//JEI
|
||||
maven("https://maven.blamejared.com/")
|
||||
}
|
||||
|
||||
license {
|
||||
@@ -55,4 +67,4 @@ tasks {
|
||||
withType<Jar> {
|
||||
from(rootProject.file("LICENSE"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright (C) 2023 Cobblemon Contributors
|
||||
* *
|
||||
* * This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* * License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("cobblemon.base-conventions")
|
||||
id("cobblemon.publish-conventions")
|
||||
@@ -18,6 +28,7 @@ dependencies {
|
||||
|
||||
modImplementation(libs.fabricLoader)
|
||||
modApi(libs.molang)
|
||||
compileOnlyApi(libs.jeiApi)
|
||||
|
||||
// For Showdown
|
||||
modCompileOnly(libs.graal)
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.bridges;
|
||||
|
||||
import net.minecraft.structure.processor.StructureProcessor;
|
||||
|
||||
public interface StructureProcessorListBridge {
|
||||
void append(StructureProcessor processor);
|
||||
void clear();
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.cobblemonstructures;
|
||||
|
||||
public class CobblemonStructureIDs {
|
||||
//Location of the nbt
|
||||
//Not sure if this is a good way to store them but doing this meant I wouldnt have to keep typing this out during development
|
||||
public static final String PLAINS_POKECENTER = "cobblemon:village_plains/village_plains_pokecenter";
|
||||
public static final String DESERT_POKECENTER = "cobblemon:village_desert/village_desert_pokecenter";
|
||||
public static final String SAVANNA_POKECENTER = "cobblemon:village_savanna/village_savanna_pokecenter";
|
||||
public static final String SNOWY_POKECENTER = "cobblemon:village_snowy/village_snowy_pokecenter";
|
||||
public static final String TAIGA_POKECENTER = "cobblemon:village_taiga/village_taiga_pokecenter";
|
||||
|
||||
public static final String PLAINS_LONG_PATH = "cobblemon:village_plains/village_plains_long_path";
|
||||
public static final String DESERT_LONG_PATH = "cobblemon:village_desert/village_desert_long_path";
|
||||
public static final String SAVANNA_LONG_PATH = "cobblemon:village_savanna/village_savanna_long_path";
|
||||
public static final String SNOWY_LONG_PATH = "cobblemon:village_snowy/village_snowy_long_path";
|
||||
public static final String TAIGA_LONG_PATH = "cobblemon:village_taiga/village_taiga_long_path";
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.cobblemonstructures;
|
||||
|
||||
import com.cobblemon.mod.common.mixin.StructurePoolAccessor;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.structure.pool.LegacySinglePoolElement;
|
||||
import net.minecraft.structure.pool.SinglePoolElement;
|
||||
import net.minecraft.structure.pool.StructurePool;
|
||||
import net.minecraft.structure.pool.StructurePoolElement;
|
||||
import net.minecraft.structure.processor.StructureProcessorList;
|
||||
import net.minecraft.structure.processor.StructureProcessorLists;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CobblemonStructures {
|
||||
|
||||
private static final RegistryKey<StructureProcessorList> EMPTY_PROCESSOR_LIST_KEY = RegistryKey.of(RegistryKeys.PROCESSOR_LIST, new Identifier("minecraft", "empty"));
|
||||
private static final Integer pokecenterWeight = 35;
|
||||
private static final Integer longPathWeight = 10;
|
||||
|
||||
public static void registerJigsaws(MinecraftServer server) {
|
||||
Registry<StructurePool> templatePoolRegistry = server.getRegistryManager().get(RegistryKeys.TEMPLATE_POOL);
|
||||
Registry<StructureProcessorList> processorListRegistry = server.getRegistryManager().get(RegistryKeys.PROCESSOR_LIST);
|
||||
|
||||
//No pokecenters yet
|
||||
//addPokecenters(templatePoolRegistry, processorListRegistry);
|
||||
//addLongPaths(templatePoolRegistry, processorListRegistry);
|
||||
}
|
||||
|
||||
private static void addPokecenters(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry) {
|
||||
Identifier plainsHousesPoolLocation = new Identifier("minecraft:village/plains/houses");
|
||||
Identifier desertHousesPoolLocation = new Identifier("minecraft:village/desert/houses");
|
||||
Identifier savannaHousesPoolLocation = new Identifier("minecraft:village/savanna/houses");
|
||||
Identifier snowyHousesPoolLocation = new Identifier("minecraft:village/snowy/houses");
|
||||
Identifier taigaHousesPoolLocation = new Identifier("minecraft:village/taiga/houses");
|
||||
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, plainsHousesPoolLocation, CobblemonStructureIDs.PLAINS_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, desertHousesPoolLocation, CobblemonStructureIDs.DESERT_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, savannaHousesPoolLocation, CobblemonStructureIDs.SAVANNA_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, snowyHousesPoolLocation, CobblemonStructureIDs.SNOWY_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, taigaHousesPoolLocation, CobblemonStructureIDs.TAIGA_POKECENTER, pokecenterWeight, StructurePool.Projection.RIGID, StructureProcessorLists.MOSSIFY_10_PERCENT);
|
||||
}
|
||||
private static void addLongPaths(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry) {
|
||||
Identifier plainsStreetsPoolLocation = new Identifier("minecraft:village/plains/streets");
|
||||
Identifier desertStreetsPoolLocation = new Identifier("minecraft:village/desert/streets");
|
||||
Identifier savannaStreetsPoolLocation = new Identifier("minecraft:village/savanna/streets");
|
||||
Identifier snowyStreetsPoolLocation = new Identifier("minecraft:village/snowy/streets");
|
||||
Identifier taigaStreetsPoolLocation = new Identifier("minecraft:village/taiga/streets");
|
||||
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, plainsStreetsPoolLocation, CobblemonStructureIDs.PLAINS_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_PLAINS);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, desertStreetsPoolLocation, CobblemonStructureIDs.DESERT_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, EMPTY_PROCESSOR_LIST_KEY);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, savannaStreetsPoolLocation, CobblemonStructureIDs.SAVANNA_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SAVANNA);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, snowyStreetsPoolLocation, CobblemonStructureIDs.SNOWY_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SNOWY_OR_TAIGA);
|
||||
addLegacyBuildingToPool(templatePoolRegistry, processorListRegistry, taigaStreetsPoolLocation, CobblemonStructureIDs.TAIGA_LONG_PATH, longPathWeight, StructurePool.Projection.TERRAIN_MATCHING, StructureProcessorLists.STREET_SNOWY_OR_TAIGA);
|
||||
}
|
||||
|
||||
public static void addLegacyBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey) {
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, poolRL, nbtPieceRL, weight, projection, processorListKey, true);
|
||||
}
|
||||
|
||||
public static void addBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey) {
|
||||
addBuildingToPool(templatePoolRegistry, processorListRegistry, poolRL, nbtPieceRL, weight, projection, processorListKey, false);
|
||||
}
|
||||
|
||||
public static void addBuildingToPool(Registry<StructurePool> templatePoolRegistry, Registry<StructureProcessorList> processorListRegistry, Identifier poolRL, String nbtPieceRL, int weight, StructurePool.Projection projection, RegistryKey<StructureProcessorList> processorListKey, boolean shouldUseLegacySingePoolElement) {
|
||||
if (processorListRegistry.getEntry(processorListKey).isEmpty()) return;
|
||||
RegistryEntry.Reference<StructureProcessorList> processorList = processorListRegistry.getEntry(processorListKey).get();
|
||||
|
||||
StructurePool pool = templatePoolRegistry.get(poolRL);
|
||||
if (pool == null) return;
|
||||
|
||||
SinglePoolElement piece;
|
||||
if (shouldUseLegacySingePoolElement) {
|
||||
piece = LegacySinglePoolElement.ofProcessedLegacySingle(nbtPieceRL, processorList).apply(projection);
|
||||
} else {
|
||||
piece = SinglePoolElement.ofProcessedSingle(nbtPieceRL, processorList).apply(projection);
|
||||
}
|
||||
|
||||
for (int i = 0; i < weight; i++) {
|
||||
((StructurePoolAccessor) pool).getElements().add(piece);
|
||||
}
|
||||
|
||||
List<Pair<StructurePoolElement, Integer>> listOfPieceEntries = new ArrayList<>(((StructurePoolAccessor) pool).getElementCounts());
|
||||
listOfPieceEntries.add(new Pair<>(piece, weight));
|
||||
((StructurePoolAccessor) pool).setElementCounts(listOfPieceEntries);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.structure.pool.StructurePool;
|
||||
import net.minecraft.structure.pool.StructurePoolElement;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(StructurePool.class)
|
||||
public interface StructurePoolAccessor {
|
||||
@Accessor("elementCounts")
|
||||
List<Pair<StructurePoolElement, Integer>> getElementCounts();
|
||||
|
||||
@Mutable
|
||||
@Accessor("elementCounts")
|
||||
void setElementCounts(List<Pair<StructurePoolElement, Integer>> elementCounts);
|
||||
|
||||
@Accessor("elements")
|
||||
ObjectArrayList<StructurePoolElement> getElements();
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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.cobblemonstructures.CobblemonStructureIDs;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.structure.PoolStructurePiece;
|
||||
import net.minecraft.structure.StructureTemplateManager;
|
||||
import net.minecraft.structure.pool.*;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.HeightLimitView;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.noise.NoiseConfig;
|
||||
import org.apache.commons.lang3.mutable.MutableObject;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
|
||||
public abstract class StructurePoolGeneratorMixin {
|
||||
@Final
|
||||
@Mutable
|
||||
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
|
||||
|
||||
Map<String, Integer> generatedStructureCounts;
|
||||
|
||||
private static final Map<String, Integer> structureMaxes;
|
||||
static {
|
||||
//Mapped using location string as key
|
||||
Map<String, Integer> aMap = new HashMap<>();
|
||||
aMap.put(CobblemonStructureIDs.PLAINS_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.DESERT_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.SAVANNA_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.SNOWY_POKECENTER, 1);
|
||||
aMap.put(CobblemonStructureIDs.TAIGA_POKECENTER, 1);
|
||||
|
||||
// aMap.put(CobblemonStructureIDs.PLAINS_LONG_PATH, 3);
|
||||
// aMap.put(CobblemonStructureIDs.DESERT_LONG_PATH, 3);
|
||||
// aMap.put(CobblemonStructureIDs.SAVANNA_LONG_PATH, 3);
|
||||
// aMap.put(CobblemonStructureIDs.SNOWY_LONG_PATH, 3);
|
||||
// aMap.put(CobblemonStructureIDs.TAIGA_LONG_PATH, 3);
|
||||
structureMaxes = Collections.unmodifiableMap(aMap);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onStructurePoolGeneratorCreation(Registry<StructurePool> registry, int maxSize, ChunkGenerator chunkGenerator, StructureTemplateManager structureTemplateManager, List<? super PoolStructurePiece> children, Random random, CallbackInfo ci) {
|
||||
generatedStructureCounts = new HashMap<>();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
|
||||
private Iterator<StructurePoolElement> reduceStructurePoolELementIterator(Iterator<StructurePoolElement> iterator) {
|
||||
List<StructurePoolElement> reducedList = new ArrayList<>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
StructurePoolElement structure = iterator.next();
|
||||
String structurePieceLocationKey = getCobblemonOnlyLocation(structure);
|
||||
if (structurePieceLocationKey == null) {
|
||||
reducedList.add(structure);
|
||||
continue;
|
||||
}
|
||||
|
||||
Integer currentlyPlacedCount = generatedStructureCounts.get(structurePieceLocationKey);
|
||||
if (currentlyPlacedCount == null) currentlyPlacedCount = 0;
|
||||
|
||||
Integer maxAllowed = structureMaxes.get(structurePieceLocationKey);
|
||||
if (maxAllowed == null) {
|
||||
reducedList.add(structure);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentlyPlacedCount < maxAllowed) {
|
||||
reducedList.add(structure);
|
||||
}
|
||||
}
|
||||
|
||||
for (String maxStructureLocationKey : structureMaxes.keySet()) {
|
||||
Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
|
||||
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
|
||||
Integer allowedInstances = maxAllowed - currentlyGenerated;
|
||||
reducedList = removeInstanceOfLocationKeyFrom(reducedList, allowedInstances, maxStructureLocationKey);
|
||||
}
|
||||
|
||||
return reducedList.iterator();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
|
||||
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
|
||||
String structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
|
||||
if (structureLocationKey != null) {
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
|
||||
}
|
||||
|
||||
return poolStructurePiece;
|
||||
}
|
||||
|
||||
@Inject(method = "generatePiece", at = @At("HEAD"))
|
||||
private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
|
||||
String structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
|
||||
|
||||
if (structureLocationKey != null && structureLocationKey.equals("cobblemon:village_snowy/village_snowy_pokecenter")) System.out.println("Before Counts" + generatedStructureCounts);
|
||||
|
||||
if (structureLocationKey != null) {
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
|
||||
}
|
||||
|
||||
List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
|
||||
|
||||
for (String maxStructureLocationKey : structureMaxes.keySet()) {
|
||||
Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
|
||||
|
||||
Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
|
||||
if (currentlyGenerated == null) currentlyGenerated = 0;
|
||||
if (currentlyGenerated < maxAllowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Already have max so need to remove
|
||||
reducedStructurePiecesList = reducedStructurePiecesList.stream()
|
||||
.filter(shapedStructurePiece -> {
|
||||
String locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
|
||||
if (locationKey == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !locationKey.equals(maxStructureLocationKey);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
|
||||
}
|
||||
|
||||
private static String getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
|
||||
String location = getLocationIfAvailable(structurePoolElement);
|
||||
if (location == null) return null;
|
||||
|
||||
if (!location.contains("cobblemon")) return null;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private static String getLocationIfAvailable(StructurePoolElement structurePoolElement) {
|
||||
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
|
||||
if (legacySinglePoolElement.location.left().isEmpty()) return null;
|
||||
|
||||
return legacySinglePoolElement.location.left().get().toString();
|
||||
} else if (structurePoolElement instanceof SinglePoolElement singlePoolElement) {
|
||||
if (singlePoolElement.location.left().isEmpty()) return null;
|
||||
|
||||
return singlePoolElement.location.left().get().toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, String locationKey) {
|
||||
List<StructurePoolElement> reducedList = new ArrayList<>();
|
||||
int instancesFound = 0;
|
||||
|
||||
for (StructurePoolElement structurePoolElement: structureList) {
|
||||
String structureKey = getCobblemonOnlyLocation(structurePoolElement);
|
||||
if (structureKey == null || !structureKey.equals(locationKey)) {
|
||||
reducedList.add(structurePoolElement);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instancesFound >= allowedNumberOfInstances) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reducedList.add(structurePoolElement);
|
||||
instancesFound++;
|
||||
}
|
||||
|
||||
return reducedList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.bridges.StructureProcessorListBridge;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.minecraft.structure.processor.StructureProcessor;
|
||||
import net.minecraft.structure.processor.StructureProcessorList;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(StructureProcessorList.class)
|
||||
public class StructureProcessorListMixin implements StructureProcessorListBridge {
|
||||
|
||||
@Final
|
||||
@Mutable
|
||||
@Shadow
|
||||
private List<StructureProcessor> list;
|
||||
|
||||
@Override
|
||||
public void append(StructureProcessor processor) {
|
||||
List<StructureProcessor> mutable = new ArrayList<>(this.list);
|
||||
mutable.add(processor);
|
||||
this.list = ImmutableList.copyOf(mutable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.list = ImmutableList.of();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
@@ -27,6 +28,7 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val HEALING_MACHINE: BlockEntityType<HealingMachineBlockEntity> = this.create("healing_machine", BlockEntityType.Builder.create(::HealingMachineBlockEntity, CobblemonBlocks.HEALING_MACHINE).build(null))
|
||||
@JvmField
|
||||
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.create(::PCBlockEntity, CobblemonBlocks.PC).build(null))
|
||||
val BERRY = this.create("berry", BlockEntityType.Builder.create(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
|
||||
@JvmField
|
||||
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.create(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.piston.PistonBehavior
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
@@ -25,6 +27,7 @@ import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.sound.BlockSoundGroup
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.intprovider.UniformIntProvider
|
||||
|
||||
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
|
||||
@@ -227,6 +230,87 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
|
||||
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
|
||||
|
||||
private val berries = mutableMapOf<Identifier, BerryBlock>()
|
||||
|
||||
val AGUAV_BERRY = this.berryBlock("aguav")
|
||||
val APICOT_BERRY = this.berryBlock("apicot")
|
||||
val ASPEAR_BERRY = this.berryBlock("aspear")
|
||||
val BABIRI_BERRY = this.berryBlock("babiri")
|
||||
val BELUE_BERRY = this.berryBlock("belue")
|
||||
val BLUK_BERRY = this.berryBlock("bluk")
|
||||
val CHARTI_BERRY = this.berryBlock("charti")
|
||||
val CHERI_BERRY = this.berryBlock("cheri")
|
||||
val CHESTO_BERRY = this.berryBlock("chesto")
|
||||
val CHILAN_BERRY = this.berryBlock("chilan")
|
||||
val CHOPLE_BERRY = this.berryBlock("chople")
|
||||
val COBA_BERRY = this.berryBlock("coba")
|
||||
val COLBUR_BERRY = this.berryBlock("colbur")
|
||||
val CORNN_BERRY = this.berryBlock("cornn")
|
||||
val CUSTAP_BERRY = this.berryBlock("custap")
|
||||
val DURIN_BERRY = this.berryBlock("durin")
|
||||
val ENIGMA_BERRY = this.berryBlock("enigma")
|
||||
val FIGY_BERRY = this.berryBlock("figy")
|
||||
val GANLON_BERRY = this.berryBlock("ganlon")
|
||||
val GREPA_BERRY = this.berryBlock("grepa")
|
||||
val HABAN_BERRY = this.berryBlock("haban")
|
||||
val HONDEW_BERRY = this.berryBlock("hondew")
|
||||
val HOPO_BERRY = this.berryBlock("hopo")
|
||||
val IAPAPA_BERRY = this.berryBlock("iapapa")
|
||||
val JABOCA_BERRY = this.berryBlock("jaboca")
|
||||
val KASIB_BERRY = this.berryBlock("kasib")
|
||||
val KEBIA_BERRY = this.berryBlock("kebia")
|
||||
val KEE_BERRY = this.berryBlock("kee")
|
||||
val KELPSY_BERRY = this.berryBlock("kelpsy")
|
||||
val LANSAT_BERRY = this.berryBlock("lansat")
|
||||
val LEPPA_BERRY = this.berryBlock("leppa")
|
||||
val LIECHI_BERRY = this.berryBlock("liechi")
|
||||
val LUM_BERRY = this.berryBlock("lum")
|
||||
val MAGO_BERRY = this.berryBlock("mago")
|
||||
val MAGOST_BERRY = this.berryBlock("magost")
|
||||
val MARANGA_BERRY = this.berryBlock("maranga")
|
||||
val MICLE_BERRY = this.berryBlock("micle")
|
||||
val NANAB_BERRY = this.berryBlock("nanab")
|
||||
val NOMEL_BERRY = this.berryBlock("nomel")
|
||||
val OCCA_BERRY = this.berryBlock("occa")
|
||||
val ORAN_BERRY = this.berryBlock("oran")
|
||||
val PAMTRE_BERRY = this.berryBlock("pamtre")
|
||||
val PASSHO_BERRY = this.berryBlock("passho")
|
||||
val PAYAPA_BERRY = this.berryBlock("payapa")
|
||||
val PECHA_BERRY = this.berryBlock("pecha")
|
||||
val PERSIM_BERRY = this.berryBlock("persim")
|
||||
val PETAYA_BERRY = this.berryBlock("petaya")
|
||||
val PINAP_BERRY = this.berryBlock("pinap")
|
||||
val POMEG_BERRY = this.berryBlock("pomeg")
|
||||
val QUALOT_BERRY = this.berryBlock("qualot")
|
||||
val RABUTA_BERRY = this.berryBlock("rabuta")
|
||||
val RAWST_BERRY = this.berryBlock("rawst")
|
||||
val RAZZ_BERRY = this.berryBlock("razz")
|
||||
val RINDO_BERRY = this.berryBlock("rindo")
|
||||
val ROSELI_BERRY = this.berryBlock("roseli")
|
||||
val ROWAP_BERRY = this.berryBlock("rowap")
|
||||
val SALAC_BERRY = this.berryBlock("salac")
|
||||
val SHUCA_BERRY = this.berryBlock("shuca")
|
||||
val SITRUS_BERRY = this.berryBlock("sitrus")
|
||||
val SPELON_BERRY = this.berryBlock("spelon")
|
||||
val STARF_BERRY = this.berryBlock("starf")
|
||||
val TAMATO_BERRY = this.berryBlock("tamato")
|
||||
val TANGA_BERRY = this.berryBlock("tanga")
|
||||
val TOUGA_BERRY = this.berryBlock("touga")
|
||||
val WACAN_BERRY = this.berryBlock("wacan")
|
||||
val WATMEL_BERRY = this.berryBlock("watmel")
|
||||
val WEPEAR_BERRY = this.berryBlock("wepear")
|
||||
val WIKI_BERRY = this.berryBlock("wiki")
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
|
||||
fun berries() = this.berries.toMap()
|
||||
|
||||
private fun berryBlock(name: String): BerryBlock {
|
||||
val identifier = cobblemonResource("${name}_berry")
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP)))
|
||||
this.berries[identifier] = block
|
||||
return block
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for creating logs
|
||||
* copied over from Vanilla
|
||||
@@ -248,4 +332,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,27 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.battle.DireHitItem
|
||||
import com.cobblemon.mod.common.item.battle.GuardSpecItem
|
||||
import com.cobblemon.mod.common.item.battle.XStatItem
|
||||
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.HealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.PotionItem
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.ComposterBlock
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.effect.StatusEffectInstance
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
import net.minecraft.item.*
|
||||
@@ -32,6 +41,8 @@ import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.World
|
||||
|
||||
@Suppress("unused")
|
||||
object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<Item>>, Item>() {
|
||||
@@ -220,8 +231,84 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
|
||||
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
|
||||
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
|
||||
// ToDo enable me when malicious armor goes in the game
|
||||
//@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
|
||||
// Armor
|
||||
@JvmField val AUSPICIOUS_ARMOR = armorItem("auspicious_armor", AuspiciousArmorMaterial(), ArmorItem.Type.CHESTPLATE, Item.Settings().fireproof())
|
||||
@JvmField val MALICIOUS_ARMOR = armorItem("malicious_armor", MaliciousArmorMaterial(), ArmorItem.Type.CHESTPLATE, Item.Settings().fireproof())
|
||||
|
||||
private val berries = mutableMapOf<Identifier, BerryItem>()
|
||||
// Plants
|
||||
@JvmField val ORAN_BERRY = berryItem("oran", HealingBerryItem(CobblemonBlocks.ORAN_BERRY) { CobblemonMechanics.berries.oranRestoreAmount })
|
||||
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
|
||||
@JvmField val CHESTO_BERRY = berryItem("chesto", StatusCuringBerryItem(CobblemonBlocks.CHESTO_BERRY, Statuses.SLEEP))
|
||||
@JvmField val PECHA_BERRY = berryItem("pecha", StatusCuringBerryItem(CobblemonBlocks.PECHA_BERRY, Statuses.POISON, Statuses.POISON_BADLY))
|
||||
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
|
||||
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
|
||||
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
|
||||
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
|
||||
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
|
||||
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
|
||||
@JvmField val WEPEAR_BERRY = berryItem("wepear", CobblemonBlocks.WEPEAR_BERRY)
|
||||
@JvmField val PINAP_BERRY = berryItem("pinap", CobblemonBlocks.PINAP_BERRY)
|
||||
@JvmField val OCCA_BERRY = berryItem("occa", CobblemonBlocks.OCCA_BERRY)
|
||||
@JvmField val PASSHO_BERRY = berryItem("passho", CobblemonBlocks.PASSHO_BERRY)
|
||||
@JvmField val WACAN_BERRY = berryItem("wacan", CobblemonBlocks.WACAN_BERRY)
|
||||
@JvmField val RINDO_BERRY = berryItem("rindo", CobblemonBlocks.RINDO_BERRY)
|
||||
@JvmField val YACHE_BERRY = berryItem("yache", CobblemonBlocks.YACHE_BERRY)
|
||||
@JvmField val CHOPLE_BERRY = berryItem("chople", CobblemonBlocks.CHOPLE_BERRY)
|
||||
@JvmField val KEBIA_BERRY = berryItem("kebia", CobblemonBlocks.KEBIA_BERRY)
|
||||
@JvmField val SHUCA_BERRY = berryItem("shuca", CobblemonBlocks.SHUCA_BERRY)
|
||||
@JvmField val COBA_BERRY = berryItem("coba", CobblemonBlocks.COBA_BERRY)
|
||||
@JvmField val PAYAPA_BERRY = berryItem("payapa", CobblemonBlocks.PAYAPA_BERRY)
|
||||
@JvmField val TANGA_BERRY = berryItem("tanga", CobblemonBlocks.TANGA_BERRY)
|
||||
@JvmField val CHARTI_BERRY = berryItem("charti", CobblemonBlocks.CHARTI_BERRY)
|
||||
@JvmField val KASIB_BERRY = berryItem("kasib", CobblemonBlocks.KASIB_BERRY)
|
||||
@JvmField val HABAN_BERRY = berryItem("haban", CobblemonBlocks.HABAN_BERRY)
|
||||
@JvmField val COLBUR_BERRY = berryItem("colbur", CobblemonBlocks.COLBUR_BERRY)
|
||||
@JvmField val BABIRI_BERRY = berryItem("babiri", CobblemonBlocks.BABIRI_BERRY)
|
||||
@JvmField val CHILAN_BERRY = berryItem("chilan", CobblemonBlocks.CHILAN_BERRY)
|
||||
@JvmField val ROSELI_BERRY = berryItem("roseli", CobblemonBlocks.ROSELI_BERRY)
|
||||
@JvmField val LEPPA_BERRY = berryItem("leppa", PPRestoringBerryItem(CobblemonBlocks.LEPPA_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
|
||||
@JvmField val LUM_BERRY = berryItem("lum", StatusCuringBerryItem(CobblemonBlocks.LUM_BERRY))
|
||||
@JvmField val FIGY_BERRY = berryItem("figy", PortionHealingBerryItem(CobblemonBlocks.FIGY_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
|
||||
@JvmField val WIKI_BERRY = berryItem("wiki", PortionHealingBerryItem(CobblemonBlocks.WIKI_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
|
||||
@JvmField val MAGO_BERRY = berryItem("mago", PortionHealingBerryItem(CobblemonBlocks.MAGO_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
|
||||
@JvmField val AGUAV_BERRY = berryItem("aguav", PortionHealingBerryItem(CobblemonBlocks.AGUAV_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
|
||||
@JvmField val IAPAPA_BERRY = berryItem("iapapa", PortionHealingBerryItem(CobblemonBlocks.IAPAPA_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
|
||||
@JvmField val SITRUS_BERRY = berryItem("sitrus", HealingBerryItem(CobblemonBlocks.SITRUS_BERRY) { CobblemonMechanics.berries.sitrusHealAmount })
|
||||
@JvmField val TOUGA_BERRY = berryItem("touga", CobblemonBlocks.TOUGA_BERRY)
|
||||
@JvmField val CORNN_BERRY = berryItem("cornn", CobblemonBlocks.CORNN_BERRY)
|
||||
@JvmField val MAGOST_BERRY = berryItem("magost", CobblemonBlocks.MAGOST_BERRY)
|
||||
@JvmField val RABUTA_BERRY = berryItem("rabuta", CobblemonBlocks.RABUTA_BERRY)
|
||||
@JvmField val NOMEL_BERRY = berryItem("nomel", CobblemonBlocks.NOMEL_BERRY)
|
||||
@JvmField val ENIGMA_BERRY = berryItem("enigma", CobblemonBlocks.ENIGMA_BERRY)
|
||||
@JvmField val POMEG_BERRY = berryItem("pomeg", FriendshipRaisingBerryItem(CobblemonBlocks.POMEG_BERRY, Stats.HP))
|
||||
@JvmField val KELPSY_BERRY = berryItem("kelpsy", FriendshipRaisingBerryItem(CobblemonBlocks.KELPSY_BERRY, Stats.ATTACK))
|
||||
@JvmField val QUALOT_BERRY = berryItem("qualot", FriendshipRaisingBerryItem(CobblemonBlocks.QUALOT_BERRY, Stats.DEFENCE))
|
||||
@JvmField val HONDEW_BERRY = berryItem("hondew", FriendshipRaisingBerryItem(CobblemonBlocks.HONDEW_BERRY, Stats.SPECIAL_ATTACK))
|
||||
@JvmField val GREPA_BERRY = berryItem("grepa", FriendshipRaisingBerryItem(CobblemonBlocks.GREPA_BERRY, Stats.SPECIAL_DEFENCE))
|
||||
@JvmField val TAMATO_BERRY = berryItem("tamato", FriendshipRaisingBerryItem(CobblemonBlocks.TAMATO_BERRY, Stats.SPEED))
|
||||
@JvmField val SPELON_BERRY = berryItem("spelon", CobblemonBlocks.SPELON_BERRY)
|
||||
@JvmField val PAMTRE_BERRY = berryItem("pamtre", CobblemonBlocks.PAMTRE_BERRY)
|
||||
@JvmField val WATMEL_BERRY = berryItem("watmel", CobblemonBlocks.WATMEL_BERRY)
|
||||
@JvmField val DURIN_BERRY = berryItem("durin", CobblemonBlocks.DURIN_BERRY)
|
||||
@JvmField val BELUE_BERRY = berryItem("belue", CobblemonBlocks.BELUE_BERRY)
|
||||
@JvmField val KEE_BERRY = berryItem("kee", CobblemonBlocks.KEE_BERRY)
|
||||
@JvmField val MARANGA_BERRY = berryItem("maranga", CobblemonBlocks.MARANGA_BERRY)
|
||||
@JvmField val HOPO_BERRY = berryItem("hopo", PPRestoringBerryItem(CobblemonBlocks.HOPO_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
|
||||
@JvmField val LIECHI_BERRY = berryItem("liechi", CobblemonBlocks.LIECHI_BERRY)
|
||||
@JvmField val GANLON_BERRY = berryItem("ganlon", CobblemonBlocks.GANLON_BERRY)
|
||||
@JvmField val SALAC_BERRY = berryItem("salac", CobblemonBlocks.SALAC_BERRY)
|
||||
@JvmField val PETAYA_BERRY = berryItem("petaya", CobblemonBlocks.PETAYA_BERRY)
|
||||
@JvmField val APICOT_BERRY = berryItem("apicot", CobblemonBlocks.APICOT_BERRY)
|
||||
@JvmField val LANSAT_BERRY = berryItem("lansat", CobblemonBlocks.LANSAT_BERRY)
|
||||
@JvmField val STARF_BERRY = berryItem("starf", CobblemonBlocks.STARF_BERRY)
|
||||
@JvmField val MICLE_BERRY = berryItem("micle", CobblemonBlocks.MICLE_BERRY)
|
||||
@JvmField val CUSTAP_BERRY = berryItem("custap", CobblemonBlocks.CUSTAP_BERRY)
|
||||
@JvmField val JABOCA_BERRY = berryItem("jaboca", CobblemonBlocks.JABOCA_BERRY)
|
||||
@JvmField val ROWAP_BERRY = berryItem("rowap", CobblemonBlocks.ROWAP_BERRY)
|
||||
|
||||
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
|
||||
|
||||
// Medicine
|
||||
@JvmField
|
||||
@@ -255,14 +342,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().maxCount(1).food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
|
||||
@JvmField
|
||||
val VIVICHOKE_DIP = create("vivichoke_dip", StewItem(Item.Settings().maxCount(1)
|
||||
val VIVICHOKE_DIP = create("vivichoke_dip", object : StewItem(Settings().maxCount(1)
|
||||
.food(FoodComponent.Builder()
|
||||
.hunger(10)
|
||||
.saturationModifier(1.2F)
|
||||
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 600, 0), 1F)
|
||||
.build()
|
||||
)
|
||||
))
|
||||
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsing(stack: ItemStack?, world: World?, user: LivingEntity?): ItemStack {
|
||||
user?.clearStatusEffects()
|
||||
return super.finishUsing(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val ENERGY_ROOT = create("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
|
||||
@JvmField
|
||||
@@ -544,6 +635,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
@JvmField
|
||||
val EVERSTONE = heldItem("everstone")
|
||||
@JvmField
|
||||
val POWER_ANKLET = heldItem("power_anklet")
|
||||
@JvmField
|
||||
val POWER_BAND = heldItem("power_band")
|
||||
@JvmField
|
||||
val POWER_BELT = heldItem("power_belt")
|
||||
@JvmField
|
||||
val POWER_BRACER = heldItem("power_bracer")
|
||||
@JvmField
|
||||
val POWER_LENS = heldItem("power_lens")
|
||||
@JvmField
|
||||
val POWER_WEIGHT = heldItem("power_weight")
|
||||
|
||||
// Mulch
|
||||
@JvmField
|
||||
@@ -573,6 +676,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
|
||||
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Settings()))
|
||||
|
||||
fun berries() = this.berries.toMap()
|
||||
|
||||
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
|
||||
|
||||
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
|
||||
@@ -593,4 +698,23 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
private fun armorItem(name: String, type: ArmorMaterial, slot: ArmorItem.Type, settings: Item.Settings): ArmorItem {
|
||||
val armor = ArmorItem(type, slot, settings)
|
||||
val item = create(name, armor)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun berryItem(name: String, berryBlock: BerryBlock): BerryItem {
|
||||
val finalName = "${name}_berry"
|
||||
val item = this.create(finalName, BerryItem(berryBlock))
|
||||
this.berries[cobblemonResource(finalName)] = item
|
||||
return item
|
||||
}
|
||||
|
||||
private fun berryItem(name: String, berryItem: BerryItem): BerryItem {
|
||||
val finalName = "${name}_berry"
|
||||
val item = this.create(finalName, berryItem)
|
||||
this.berries[cobblemonResource(finalName)] = item
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.mechanics.BerriesMechanic
|
||||
import com.cobblemon.mod.common.mechanics.PotionsMechanic
|
||||
import com.cobblemon.mod.common.mechanics.RemediesMechanic
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -30,11 +32,19 @@ object CobblemonMechanics : DataRegistry {
|
||||
.create()
|
||||
|
||||
var remedies = RemediesMechanic()
|
||||
var berries = BerriesMechanic()
|
||||
var potions = PotionsMechanic()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun reload(manager: ResourceManager) {
|
||||
manager.getResourceOrThrow(cobblemonResource("mechanics/remedies.json")).inputStream.use {
|
||||
remedies = gson.fromJson(it.reader(), RemediesMechanic::class.java)
|
||||
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
|
||||
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
|
||||
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
|
||||
}
|
||||
|
||||
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
|
||||
manager.getResourceOrThrow(cobblemonResource("mechanics/$name.json")).inputStream.use {
|
||||
return gson.fromJson(it.reader(), clazz)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.battle.*
|
||||
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
@@ -56,6 +56,7 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPac
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
@@ -273,6 +274,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
|
||||
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
|
||||
// Effects
|
||||
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
|
||||
|
||||
@@ -78,6 +78,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
|
||||
|
||||
@JvmField
|
||||
val BERRY_EAT = this.create("berry.eat")
|
||||
|
||||
@JvmField
|
||||
val MULCH_PLACE = this.create("mulch.place")
|
||||
@JvmField
|
||||
val MULCH_REMOVE = this.create("mulch.remove")
|
||||
|
||||
@JvmField
|
||||
val AUSPICIOUS_ARMOR_EQUIP = this.create("auspicious_armor.equip")
|
||||
@JvmField
|
||||
val MALICIOUS_ARMOR_EQUIP = this.create("malicious_armor.equip")
|
||||
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
|
||||
}
|
||||
@@ -34,22 +34,19 @@ import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import net.minecraft.entity.Entity
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import net.minecraft.text.Text
|
||||
import java.io.File
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.writeLines
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Individual battle instance
|
||||
@@ -62,7 +59,7 @@ open class PokemonBattle(
|
||||
val side1: BattleSide,
|
||||
val side2: BattleSide
|
||||
) {
|
||||
/** Whether or not logging will be silenced for this battle. */
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
|
||||
init {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Status
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.awt.Color
|
||||
import net.minecraft.predicate.NumberRange
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* The data registry for [Berry].
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
object Berries : JsonDataRegistry<Berry> {
|
||||
|
||||
override val id: Identifier = cobblemonResource("berries")
|
||||
override val type: ResourceType = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<Berries>()
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
|
||||
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
|
||||
.registerTypeAdapter(Status::class.java, StatusAdapter)
|
||||
.registerTypeAdapter(Box::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(GrowthFactor::class.java, CobblemonGrowthFactorAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
|
||||
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
|
||||
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Biome::class.java).type, TagKeyAdapter(RegistryKeys.BIOME))
|
||||
.registerTypeAdapter(BerrySpawnCondition::class.java, CobblemonBerrySpawnConditionAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<Berry> = TypeToken.get(Berry::class.java)
|
||||
override val resourcePath = "berries"
|
||||
|
||||
private val berries = hashMapOf<Identifier, Berry>()
|
||||
|
||||
val PECHA
|
||||
get() = this.getByName("")
|
||||
|
||||
override fun reload(data: Map<Identifier, Berry>) {
|
||||
this.berries.clear()
|
||||
data.forEach { (identifier, berry) ->
|
||||
try {
|
||||
berry.identifier = identifier
|
||||
berry.validate()
|
||||
this.berries[identifier] = berry
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Skipped loading the {} berry", identifier, e)
|
||||
}
|
||||
}
|
||||
Cobblemon.LOGGER.info("Loaded {} berries", this.berries.size)
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
BerryRegistrySyncPacket(this.all()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
fun all() = this.berries.values.toList()
|
||||
|
||||
/**
|
||||
* Gets a berry if loaded.
|
||||
*
|
||||
* @param identifier The identifier of the berry.
|
||||
* @return The [Berry] if loaded otherwise null.
|
||||
*/
|
||||
fun getByIdentifier(identifier: Identifier): Berry? = this.berries[identifier]
|
||||
|
||||
/**
|
||||
* Gets a berry if loaded.
|
||||
*
|
||||
* @param name The path of the identifier of the berry under the [Cobblemon.MODID] namespace.
|
||||
* @return The [Berry] if loaded otherwise null.
|
||||
*/
|
||||
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.item.BerryItem
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.util.readBox
|
||||
import com.cobblemon.mod.common.util.writeBox
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.awt.Color
|
||||
import java.util.EnumSet
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* Represents the data behind a berry.
|
||||
*
|
||||
* @property identifier The [Identifier] of this berry.
|
||||
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
|
||||
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
|
||||
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
|
||||
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
|
||||
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
|
||||
* @property favoriteMulches The types of mulches that benefit this berries yield
|
||||
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
|
||||
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
|
||||
* @property growthPoints A collection of [GrowthPoint]s for the berry flowers and fruit.
|
||||
* @property sproutShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the sprouting stages.
|
||||
* @property matureShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the mature stages.
|
||||
* @property flavors The [Flavor] values.
|
||||
* @property tintIndexes Determines tints at specific indexes if any.
|
||||
* @property flowerModelIdentifier The [Identifier] for the model of the berry in flower form.
|
||||
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
|
||||
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
|
||||
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
|
||||
*
|
||||
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
|
||||
*/
|
||||
class Berry(
|
||||
identifier: Identifier,
|
||||
val baseYield: IntRange,
|
||||
val preferredBiomeTags: List<TagKey<Biome>>,
|
||||
val growthTime: IntRange,
|
||||
val refreshRate: IntRange,
|
||||
//val lifeCycles: IntRange,
|
||||
val favoriteMulches: EnumSet<MulchVariant>,
|
||||
val growthFactors: Collection<GrowthFactor>,
|
||||
val spawnConditions: List<BerrySpawnCondition>,
|
||||
var growthPoints: Array<GrowthPoint>,
|
||||
val randomizedGrowthPoints: Boolean = true,
|
||||
val mutations: Map<Identifier, Identifier>,
|
||||
@SerializedName("sproutShape")
|
||||
private val sproutShapeBoxes: Collection<Box>,
|
||||
@SerializedName("matureShape")
|
||||
private val matureShapeBoxes: Collection<Box>,
|
||||
private val flavors: Map<Flavor, Int>,
|
||||
val tintIndexes: Map<Int, Color>,
|
||||
@SerializedName("flowerModel")
|
||||
val flowerModelIdentifier: Identifier,
|
||||
val flowerTexture: Identifier,
|
||||
@SerializedName("fruitModel")
|
||||
val fruitModelIdentifier: Identifier,
|
||||
val fruitTexture: Identifier,
|
||||
val weight: Float
|
||||
) {
|
||||
|
||||
@Transient
|
||||
var identifier: Identifier = identifier
|
||||
internal set
|
||||
|
||||
@Transient
|
||||
private lateinit var shapedFlower: HashMap<Int, VoxelShape>
|
||||
|
||||
@Transient
|
||||
private lateinit var shapedFruit: HashMap<Int, VoxelShape>
|
||||
|
||||
/**
|
||||
* The [VoxelShape] of the tree during the sprouting stages.
|
||||
*/
|
||||
@Transient
|
||||
lateinit var sproutShape: VoxelShape
|
||||
private set
|
||||
|
||||
/**
|
||||
* The [VoxelShape] of the tree during the mature stages.
|
||||
*/
|
||||
@Transient
|
||||
lateinit var matureShape: VoxelShape
|
||||
private set
|
||||
|
||||
init {
|
||||
this.validate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the [BerryItem] that uses this berry for data.
|
||||
*
|
||||
* @return The [BerryItem] if existing.
|
||||
*/
|
||||
fun item(): BerryItem? = CobblemonItems.berries()[this.identifier]
|
||||
|
||||
/**
|
||||
* Finds the [BerryBlock] that uses this berry for data.
|
||||
*
|
||||
* @return The [BerryBlock] if existing.
|
||||
*/
|
||||
fun block(): BerryBlock? = CobblemonBlocks.berries()[this.identifier]
|
||||
|
||||
/**
|
||||
* Query the value of a certain flavor.
|
||||
*
|
||||
* @param flavor The [Flavor] being queried.
|
||||
* @return The value if any or 0.
|
||||
*/
|
||||
fun flavor(flavor: Flavor): Int = this.flavors[flavor] ?: 0
|
||||
|
||||
fun dislikedBy(nature: Nature): Boolean {
|
||||
val dislikedFlavor = nature.dislikedFlavor ?: return false
|
||||
return flavor(dislikedFlavor) > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
|
||||
* Triggers [BerryYieldCalculationEvent].
|
||||
*
|
||||
* @param world The [World] the tree is present in.
|
||||
* @param state The [BlockState] of the tree.
|
||||
* @param pos The [BlockPos] of the tree.
|
||||
* @param placer The [LivingEntity] tending to the tree, if any.
|
||||
* @return The total berry stack count.
|
||||
*/
|
||||
fun calculateYield(world: World, state: BlockState, pos: BlockPos, placer: LivingEntity? = null): Int {
|
||||
val base = this.baseYield.random()
|
||||
val bonus = this.bonusYield(world, state, pos)
|
||||
var yield = base + bonus.first
|
||||
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (treeEntity.mulchVariant == MulchVariant.RICH) {
|
||||
yield = yield.times(1.2).coerceAtMost(maxYield().toDouble()).toInt()
|
||||
treeEntity.mulchDuration--
|
||||
}
|
||||
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
|
||||
return yield
|
||||
//Just for testing the renderer
|
||||
//return growthPoints.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and returns the minimum possible yield by summing [baseYield] and [growthFactors] minimum values.
|
||||
*
|
||||
* @return The minimum possible yield.
|
||||
*/
|
||||
fun minYield() = this.baseYield.first + this.growthFactors.sumOf { it.minYield() }
|
||||
|
||||
/**
|
||||
* Calculates and returns the maximum possible yield by summing [baseYield] and [growthFactors] max values.
|
||||
*
|
||||
* @return The maximum possible yield.
|
||||
*/
|
||||
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
|
||||
|
||||
/**
|
||||
* Checks if this berry can mutate with the given [partner].
|
||||
* For the berry the mutations may produce use [mutationWith].
|
||||
*
|
||||
* @param partner The [Berry] being checked as a possible mutation partner.
|
||||
* @return If a mutation is possible.
|
||||
*/
|
||||
fun canMutateWith(partner: Berry) = this.mutationWith(partner) != null
|
||||
|
||||
/**
|
||||
* Query for the possible mutation with the given [partner].
|
||||
*
|
||||
* @param partner The [Berry] being mutated with.
|
||||
* @return The resulting [Berry] if the mutation is possible.
|
||||
*/
|
||||
fun mutationWith(partner: Berry): Berry? {
|
||||
val berryIdentifier = this.mutations[partner.identifier] ?: return null
|
||||
return Berries.getByIdentifier(berryIdentifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for a possible partner to mutate into the given [resulting].
|
||||
*
|
||||
* @param resulting The [Berry] the mutation should result in.
|
||||
* @return The [Berry] that mutates with this berry into the given [resulting] if possible.
|
||||
*/
|
||||
fun partnerForMutation(resulting: Berry): Berry? {
|
||||
return this.mutations.firstNotNullOfOrNull { (partner, result) ->
|
||||
if (result == resulting.identifier)
|
||||
Berries.getByIdentifier(partner)
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// A cheat since gson doesn't invoke init block
|
||||
internal fun validate() {
|
||||
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
|
||||
throw IllegalArgumentException("A berry base yield must be a positive range")
|
||||
}
|
||||
if (this.growthTime.first < 0 || this.growthTime.last < 0) {
|
||||
throw IllegalArgumentException("The growth time must be a positive range")
|
||||
}
|
||||
if (this.refreshRate.first < 0 || this.refreshRate.last < 0) {
|
||||
throw IllegalArgumentException("The refresh rate must be a positive range")
|
||||
}
|
||||
/*
|
||||
if (this.lifeCycles.first < 0 || this.lifeCycles.last < 0) {
|
||||
throw IllegalArgumentException("A berry life cycle must be a positive range")
|
||||
}
|
||||
*/
|
||||
this.growthFactors.forEach { it.validateArguments() }
|
||||
val maxYield = this.maxYield()
|
||||
if (this.growthPoints.size < maxYield) {
|
||||
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
|
||||
}
|
||||
this.shapedFlower = hashMapOf()
|
||||
this.shapedFruit = hashMapOf()
|
||||
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
|
||||
this.matureShape = this.createAndUniteShapes(this.matureShapeBoxes)
|
||||
}
|
||||
|
||||
internal fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeIdentifier(this.identifier)
|
||||
buffer.writeInt(this.baseYield.first)
|
||||
buffer.writeInt(this.baseYield.last)
|
||||
buffer.writeEnumSet(favoriteMulches, MulchVariant::class.java)
|
||||
//buffer.writeInt(this.lifeCycles.first)
|
||||
//buffer.writeInt(this.lifeCycles.last)
|
||||
buffer.writeInt(this.growthTime.first)
|
||||
buffer.writeInt(this.growthTime.last)
|
||||
buffer.writeInt(this.refreshRate.first)
|
||||
buffer.writeInt(this.refreshRate.last)
|
||||
buffer.writeCollection(this.growthPoints.toList()) { writer, value ->
|
||||
writer.writeDouble(value.position.x)
|
||||
writer.writeDouble(value.position.y)
|
||||
writer.writeDouble(value.position.z)
|
||||
writer.writeDouble(value.rotation.x)
|
||||
writer.writeDouble(value.rotation.y)
|
||||
writer.writeDouble(value.rotation.z)
|
||||
}
|
||||
buffer.writeBoolean(this.randomizedGrowthPoints)
|
||||
buffer.writeMap(this.mutations, { writer, key -> writer.writeIdentifier(key) }, { writer, value -> writer.writeIdentifier(value) })
|
||||
buffer.writeCollection(this.sproutShapeBoxes) { writer, value ->
|
||||
writer.writeBox(value)
|
||||
}
|
||||
buffer.writeCollection(this.matureShapeBoxes) { writer, value ->
|
||||
writer.writeBox(value)
|
||||
}
|
||||
buffer.writeMap(this.flavors, { writer, key -> writer.writeEnumConstant(key) }, { writer, value -> writer.writeInt(value) })
|
||||
buffer.writeMap(this.tintIndexes, { writer, key -> writer.writeInt(key) }, { writer, value -> writer.writeInt(value.rgb) })
|
||||
buffer.writeIdentifier(this.flowerModelIdentifier)
|
||||
buffer.writeIdentifier(this.flowerTexture)
|
||||
buffer.writeIdentifier(this.fruitModelIdentifier)
|
||||
buffer.writeIdentifier(this.fruitTexture)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the bonus yield for the berry tree.
|
||||
*
|
||||
* @param world The [World] the tree is present in.
|
||||
* @param state The [BlockState] of the tree.
|
||||
* @param pos The [BlockPos] of the tree.
|
||||
* @return The bonus yield, the growth factors that passed.
|
||||
*/
|
||||
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
|
||||
var bonus = 0
|
||||
val passed = arrayListOf<GrowthFactor>()
|
||||
val entity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
val hasBiomeMulch = favoriteMulches.contains(entity.mulchVariant)
|
||||
this.growthFactors.forEach { factor ->
|
||||
if (factor.isValid(world, state, pos)) {
|
||||
bonus += factor.yield()
|
||||
passed += factor
|
||||
}
|
||||
else if (hasBiomeMulch) {
|
||||
bonus += factor.yield()
|
||||
}
|
||||
}
|
||||
return bonus to passed
|
||||
}
|
||||
|
||||
private fun createAndUniteShapes(boxes: Collection<Box>): VoxelShape {
|
||||
var shape: VoxelShape? = null
|
||||
boxes.forEach { box ->
|
||||
shape = if (shape == null) {
|
||||
Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)
|
||||
} else {
|
||||
VoxelShapes.union(shape, Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ))
|
||||
}
|
||||
}
|
||||
return shape ?: VoxelShapes.fullCube()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal fun decode(buffer: PacketByteBuf): Berry {
|
||||
val identifier = buffer.readIdentifier()
|
||||
val baseYield = IntRange(buffer.readInt(), buffer.readInt())
|
||||
val favMulchs = buffer.readEnumSet(MulchVariant::class.java)
|
||||
//val lifeCycles = IntRange(buffer.readInt(), buffer.readInt())
|
||||
val growthTime = IntRange(buffer.readInt(), buffer.readInt())
|
||||
val refreshRate = IntRange(buffer.readInt(), buffer.readInt())
|
||||
val growthPoints = buffer.readList { reader ->
|
||||
GrowthPoint(Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()))
|
||||
}.toTypedArray()
|
||||
val randomizedGrowthPoints = buffer.readBoolean()
|
||||
val mutations = buffer.readMap({ reader -> reader.readIdentifier() }, { reader -> reader.readIdentifier() })
|
||||
val sproutShapeBoxes = buffer.readList { it.readBox() }
|
||||
val matureShapeBoxes = buffer.readList { it.readBox() }
|
||||
val flavors = buffer.readMap({ reader -> reader.readEnumConstant(Flavor::class.java) }, { reader -> reader.readInt() })
|
||||
val tintIndexes = buffer.readMap({ reader -> reader.readInt() }, { reader -> Color(reader.readInt()) })
|
||||
val flowerModelIdentifier = buffer.readIdentifier()
|
||||
val flowerTexture = buffer.readIdentifier()
|
||||
val fruitModelIdentifier = buffer.readIdentifier()
|
||||
val fruitTexture = buffer.readIdentifier()
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.world.feature.BerryGroveFeature
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.common.cache.CacheLoader
|
||||
import com.google.common.cache.LoadingCache
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
object BerryHelper {
|
||||
private val CACHE_LOADER = object : CacheLoader<RegistryEntry<Biome>, List<BerryBlock>>() {
|
||||
override fun load(key: RegistryEntry<Biome>): List<BerryBlock> {
|
||||
return naturalBerries.filter { berryBlock ->
|
||||
val berry = berryBlock.berry()
|
||||
berry?.spawnConditions?.any { it.canSpawn(berry, key) } ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val naturalBerries = CobblemonBlocks.berries().values.filter {
|
||||
(it.berry()?.spawnConditions?.size ?: 0) > 0
|
||||
}
|
||||
|
||||
private val validBerryCache: LoadingCache<RegistryEntry<Biome>, List<BerryBlock>> = CacheBuilder.newBuilder()
|
||||
.maximumSize(4)
|
||||
.build(CACHE_LOADER)
|
||||
|
||||
|
||||
fun getBerriesForBiome(biome: RegistryEntry<Biome>): List<BerryBlock> {
|
||||
return validBerryCache.get(biome)
|
||||
}
|
||||
|
||||
fun getNaturallyGeneratingBerries(): List<BerryBlock> {
|
||||
return naturalBerries
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
/**
|
||||
* Represents the different flavors associated with berries and other Pokémon consumables.
|
||||
* See the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Flavor) article for more information.
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
enum class Flavor {
|
||||
|
||||
SPICY,
|
||||
DRY,
|
||||
SWEET,
|
||||
BITTER,
|
||||
SOUR
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
/**
|
||||
* Represents a condition that impacts extra berry yield.
|
||||
*
|
||||
* @author Licious
|
||||
* @since December 2nd, 2022
|
||||
*/
|
||||
interface GrowthFactor {
|
||||
|
||||
/**
|
||||
* Validates the arguments contained in this factor, this is invoked during loading.
|
||||
*
|
||||
* @throws IllegalArgumentException If an argument isn't valid.
|
||||
*/
|
||||
fun validateArguments()
|
||||
|
||||
/**
|
||||
* Checks if this factor should yield a bonus.
|
||||
*
|
||||
* @param world The [WorldView] the berry tree is in.
|
||||
* @param state The [BlockState] of the berry tree.
|
||||
* @param pos The [BlockPos] of the berry tree.
|
||||
* @return If the bonus should activate.
|
||||
*/
|
||||
fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean
|
||||
|
||||
/**
|
||||
* Resolves the amount of bonus berries to grow if [isValid] was true.
|
||||
*
|
||||
* @return The amount of extra berries to grow.
|
||||
*/
|
||||
fun yield(): Int
|
||||
|
||||
/**
|
||||
* Returns the minimum possible yield for this factor.
|
||||
*
|
||||
* @return The minimum possible yield for this factor.
|
||||
*/
|
||||
fun minYield(): Int
|
||||
|
||||
/**
|
||||
* Returns the maximum possible yield for this factor.
|
||||
*
|
||||
* @return The maximum possible yield for this factor.
|
||||
*/
|
||||
fun maxYield(): Int
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry
|
||||
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Represents a position in a berry tree where a berry will flower then fruit.
|
||||
*
|
||||
* @property position The coordinates of the berry in the tree block.
|
||||
* @property rotation The degree value for the rotation each axis.
|
||||
*
|
||||
* @author Licious
|
||||
* @since December 19th, 2022
|
||||
*/
|
||||
data class GrowthPoint(
|
||||
val position: Vec3d,
|
||||
val rotation: Vec3d
|
||||
)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonGrowthFactorAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import net.minecraft.util.Identifier
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A type adapter for [GrowthFactor]s.
|
||||
* For the default implementation see [CobblemonGrowthFactorAdapter].
|
||||
*
|
||||
* @author Licious
|
||||
* @since December 2nd, 2022
|
||||
*/
|
||||
interface GrowthFactorAdapter : JsonDeserializer<GrowthFactor>, JsonSerializer<GrowthFactor> {
|
||||
|
||||
/**
|
||||
* Register a [GrowthFactor] to be used by this adapter.
|
||||
*
|
||||
* @param type The [KClass] of the [GrowthFactor].
|
||||
* @param identifier The expected [Identifier] in the parsed JSON.
|
||||
*/
|
||||
fun register(type: KClass<out GrowthFactor>, identifier: Identifier)
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonBerrySpawnConditionAdapter
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import net.minecraft.util.Identifier
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A type adapter for [GrowthFactor]s.
|
||||
* For the default implementation see [CobblemonBerrySpawnConditionAdapter].
|
||||
*
|
||||
* @author Apion
|
||||
* @since August 14, 2023
|
||||
*/
|
||||
interface SpawnConditionAdapter : JsonDeserializer<BerrySpawnCondition>, JsonSerializer<BerrySpawnCondition> {
|
||||
/**
|
||||
* Register a [BerrySpawnCondition] to be used by this adapter.
|
||||
*
|
||||
* @param type The [KClass] of the [BerrySpawnCondition].
|
||||
* @param identifier The expected [Identifier] in the parsed JSON.
|
||||
*/
|
||||
fun register(type: KClass<out BerrySpawnCondition>, identifier: Identifier)
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.spawncondition
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.util.math.random.Random
|
||||
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
class AllBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
|
||||
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getGroveSize(random: Random): Int {
|
||||
return random.nextBetween(minGroveSize, maxGroveSize)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("all_biome")
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.spawncondition
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
interface BerrySpawnCondition {
|
||||
fun getGroveSize(random: Random): Int
|
||||
|
||||
fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.berry.spawncondition
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.entry.RegistryEntry
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.world.biome.Biome
|
||||
import kotlin.math.min
|
||||
|
||||
class PreferredBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
|
||||
|
||||
override fun getGroveSize(random: Random): Int {
|
||||
return random.nextBetween(minGroveSize, maxGroveSize)
|
||||
}
|
||||
|
||||
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
|
||||
val preferredBiomeTags = berry.preferredBiomeTags
|
||||
return preferredBiomeTags.any {biome.isIn(it)}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("preferred_biome")
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.UUID
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.MutableText
|
||||
|
||||
/**
|
||||
* Used for opening a party select screen for players which routes to move selection when they choose a Pokémon.
|
||||
@@ -30,6 +32,7 @@ object PartyMoveSelectCallbacks {
|
||||
@JvmOverloads
|
||||
fun create(
|
||||
player: ServerPlayerEntity,
|
||||
partyTitle: MutableText = lang("ui.party"),
|
||||
pokemon: List<Pair<PartySelectPokemonDTO, List<MoveSelectDTO>>>,
|
||||
cancel: (ServerPlayerEntity) -> Unit = {},
|
||||
handler: (ServerPlayerEntity, pokemonIndex: Int, PartySelectPokemonDTO, moveIndex: Int, MoveSelectDTO) -> Unit
|
||||
@@ -40,12 +43,13 @@ object PartyMoveSelectCallbacks {
|
||||
handler = handler
|
||||
)
|
||||
callbacks[player.uuid] = callback
|
||||
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, callback.pokemon))
|
||||
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, partyTitle, callback.pokemon))
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun createFromPokemon(
|
||||
player: ServerPlayerEntity,
|
||||
partyTitle: MutableText = lang("ui.party"),
|
||||
pokemon: List<Pokemon>,
|
||||
moves: (Pokemon) -> List<Move> = { it.moveSet.getMoves() },
|
||||
canSelectPokemon: (Pokemon) -> Boolean = { true },
|
||||
@@ -62,6 +66,7 @@ object PartyMoveSelectCallbacks {
|
||||
|
||||
create(
|
||||
player = player,
|
||||
partyTitle = partyTitle,
|
||||
pokemon = pokemonList,
|
||||
cancel = cancel,
|
||||
handler = { _, pkIndex, _, moveIndex, _ ->
|
||||
|
||||
+14
-6
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.UUID
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
@@ -36,7 +36,7 @@ object PartySelectCallbacks {
|
||||
@JvmOverloads
|
||||
fun create(
|
||||
player: ServerPlayerEntity,
|
||||
title: Text = "".text(),
|
||||
title: Text = lang("ui.party"),
|
||||
pokemon: List<PartySelectPokemonDTO>,
|
||||
cancel: (ServerPlayerEntity) -> Unit = {},
|
||||
handler: (ServerPlayerEntity, index: Int) -> Unit
|
||||
@@ -61,12 +61,14 @@ object PartySelectCallbacks {
|
||||
@JvmOverloads
|
||||
fun createBattleSelect(
|
||||
player: ServerPlayerEntity,
|
||||
title: Text = lang("ui.party"),
|
||||
pokemon: List<BattlePokemon>,
|
||||
canSelect: (BattlePokemon) -> Boolean,
|
||||
cancel: (ServerPlayerEntity) -> Unit = {},
|
||||
handler: (BattlePokemon) -> Unit
|
||||
) = create(
|
||||
player = player,
|
||||
title = title,
|
||||
cancel = cancel,
|
||||
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk.effectedPokemon).also { it.enabled = canSelect(pk) } }
|
||||
) { _, index -> handler(pokemon[index]) }
|
||||
@@ -74,12 +76,14 @@ object PartySelectCallbacks {
|
||||
@JvmOverloads
|
||||
fun createFromPokemon(
|
||||
player: ServerPlayerEntity,
|
||||
title: Text = lang("ui.party"),
|
||||
pokemon: List<Pokemon>,
|
||||
canSelect: (Pokemon) -> Boolean,
|
||||
cancel: (ServerPlayerEntity) -> Unit = {},
|
||||
handler: (Pokemon) -> Unit
|
||||
) = create(
|
||||
player = player,
|
||||
title = title,
|
||||
cancel = cancel,
|
||||
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk).also { it.enabled = canSelect(pk) } }
|
||||
) { _, index -> handler(pokemon[index]) }
|
||||
@@ -118,7 +122,8 @@ class PartySelectCallback(
|
||||
open class PartySelectPokemonDTO(
|
||||
val pokemonProperties: PokemonProperties,
|
||||
val heldItem: ItemStack = ItemStack.EMPTY,
|
||||
var hpRatio: Float = 1F,
|
||||
var currentHealth: Int,
|
||||
var maxHealth: Int,
|
||||
var enabled: Boolean
|
||||
) {
|
||||
@JvmOverloads
|
||||
@@ -132,21 +137,24 @@ open class PartySelectPokemonDTO(
|
||||
PokemonPropertyExtractor.STATUS
|
||||
),
|
||||
heldItem = pokemon.heldItemNoCopy(),
|
||||
hpRatio = pokemon.currentHealth / pokemon.hp.toFloat(),
|
||||
currentHealth = pokemon.currentHealth,
|
||||
maxHealth = pokemon.hp,
|
||||
enabled = enabled
|
||||
)
|
||||
|
||||
constructor(buffer: PacketByteBuf): this(
|
||||
pokemonProperties = PokemonProperties().loadFromNBT(buffer.readNbt() as NbtCompound),
|
||||
heldItem = buffer.readItemStack(),
|
||||
hpRatio = buffer.readFloat(),
|
||||
currentHealth = buffer.readInt(),
|
||||
maxHealth = buffer.readInt(),
|
||||
enabled = buffer.readBoolean()
|
||||
)
|
||||
|
||||
fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeNbt(pokemonProperties.saveToNBT())
|
||||
buffer.writeItemStack(heldItem)
|
||||
buffer.writeFloat(hpRatio)
|
||||
buffer.writeInt(currentHealth)
|
||||
buffer.writeInt(maxHealth)
|
||||
buffer.writeBoolean(enabled)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.*
|
||||
import com.cobblemon.mod.common.api.events.battles.*
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
|
||||
@@ -20,6 +22,8 @@ import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
|
||||
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
|
||||
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.*
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
|
||||
@@ -59,6 +63,10 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
|
||||
@JvmField
|
||||
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
|
||||
@JvmField
|
||||
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
|
||||
// @JvmField
|
||||
// val EGG_HATCH = EventObservable<HatchEggEvent>()
|
||||
@@ -120,6 +128,15 @@ object CobblemonEvents {
|
||||
|
||||
@JvmField
|
||||
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
|
||||
// Berries
|
||||
@JvmField
|
||||
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
|
||||
@JvmField
|
||||
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
|
||||
@JvmField
|
||||
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
|
||||
@JvmField
|
||||
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
|
||||
@JvmField
|
||||
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
|
||||
@JvmField
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
|
||||
/**
|
||||
* The base of all Berry related events.
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
interface BerryEvent {
|
||||
|
||||
/**
|
||||
* The [Berry] related to the event trigger
|
||||
*/
|
||||
val berry: Berry
|
||||
|
||||
}
|
||||
|
||||
@@ -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.api.events.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* An event fired when [BerryBlockEntity.harvest] is invoked.
|
||||
*
|
||||
* @property berry The [Berry] the tree is attached to.
|
||||
* @property player The [ServerPlayerEntity] harvesting the tree.
|
||||
* @property world The [World] the tree is in.
|
||||
* @property pos The [BlockPos] of the tree.
|
||||
* @property state The [BlockState] of the tree.
|
||||
* @property blockEntity The backing [BerryBlockEntity]-
|
||||
* @property drops A collection of [ItemStack]s produced by this harvest.
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
data class BerryHarvestEvent(
|
||||
override val berry: Berry,
|
||||
val player: ServerPlayerEntity,
|
||||
val world: World,
|
||||
val pos: BlockPos,
|
||||
val state: BlockState,
|
||||
val blockEntity: BerryBlockEntity,
|
||||
val drops: MutableList<ItemStack>
|
||||
) : BerryEvent
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
|
||||
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
|
||||
*
|
||||
* @property world The [World] the berry tree is in.
|
||||
* @property state The [BlockState] of the berry tree.
|
||||
* @property pos The [BlockPos] of the berry tree.
|
||||
* @property mutations The possible [Berry] mutations.
|
||||
*
|
||||
* @author Licious
|
||||
* @since January 19th, 2022
|
||||
*/
|
||||
data class BerryMutationOfferEvent(
|
||||
override val berry: Berry,
|
||||
val world: World,
|
||||
val state: BlockState,
|
||||
val pos: BlockPos,
|
||||
val mutations: MutableSet<Berry>
|
||||
) : BerryEvent
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
|
||||
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
|
||||
*
|
||||
* @property world The [World] the berry tree is in.
|
||||
* @property state The [BlockState] of the berry tree.
|
||||
* @property pos The [BlockPos] of the berry tree.
|
||||
* @property mutations The possible [Berry] mutations.
|
||||
* @property pickedMutation The [Berry] chosen to mutate into, if null the mutation will not occur.
|
||||
*
|
||||
* @author Licious
|
||||
* @since January 19th, 2022
|
||||
*/
|
||||
data class BerryMutationResultEvent(
|
||||
override val berry: Berry,
|
||||
val world: World,
|
||||
val state: BlockState,
|
||||
val pos: BlockPos,
|
||||
val mutations: Set<Berry>,
|
||||
var pickedMutation: Berry?
|
||||
) : BerryEvent
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* The event fired when [Berry.calculateYield] is invoked.
|
||||
*
|
||||
* @property world The [World] the berry tree is in.
|
||||
* @property state The [BlockState] of the berry tree.
|
||||
* @property pos The [BlockPos] of the berry tree.
|
||||
* @property placer The [LivingEntity] triggering the calculation.
|
||||
* @property yield The current yield of berries.
|
||||
* @property passedGrowthFactors The [Berry.growthFactors] where [GrowthFactor.isValid] was true.
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 28th, 2022
|
||||
*/
|
||||
class BerryYieldCalculationEvent(
|
||||
override val berry: Berry,
|
||||
val world: World,
|
||||
val state: BlockState,
|
||||
val pos: BlockPos,
|
||||
val placer: LivingEntity?,
|
||||
yield: Int,
|
||||
val passedGrowthFactors: Collection<GrowthFactor>
|
||||
) : BerryEvent {
|
||||
|
||||
/**
|
||||
* The amount of berries this tree will yield.
|
||||
*
|
||||
* @throws IllegalArgumentException If a new value exceeds [Berry.maxYield] or be lesser than 0.
|
||||
*/
|
||||
var yield: Int = yield
|
||||
set(value) {
|
||||
val max = this.berry.maxYield()
|
||||
if (value > max) {
|
||||
throw IllegalArgumentException("Cannot set the berry yield for ${this.berry.identifier} above $max")
|
||||
}
|
||||
if (value < 0) {
|
||||
throw IllegalArgumentException("A berry tree cannot yield a negative amount of berries")
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokeball
|
||||
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* Event fired when a Poké Ball has completed its capture calculation and is about to begin shaking
|
||||
* or breaking free. The result of the capture can be changed by replacing [captureResult].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 20th, 2023
|
||||
*/
|
||||
class PokeBallCaptureCalculatedEvent(
|
||||
val thrower: LivingEntity,
|
||||
val pokemonEntity: PokemonEntity,
|
||||
val pokeBallEntity: EmptyPokeBallEntity,
|
||||
var captureResult: CaptureContext
|
||||
) {
|
||||
fun ifPlayer(action: PokeBallCaptureCalculatedEvent.(player: ServerPlayerEntity) -> Unit) {
|
||||
if (thrower is ServerPlayerEntity) {
|
||||
action(this, thrower)
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokeball
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
/**
|
||||
* Event fired when a capture calculator is accessing a Pokémon's catch rate. The [catchRate] property can
|
||||
* be changed to adjust the rate.
|
||||
*
|
||||
* If you want to prevent a Pokémon from being caught, you should be handling the [PokeBallCaptureCalculatedEvent]
|
||||
* event instead.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 20th, 2023
|
||||
*/
|
||||
class PokemonCatchRateEvent(
|
||||
val thrower: LivingEntity,
|
||||
val pokeBallEntity: EmptyPokeBallEntity,
|
||||
val pokemonEntity: PokemonEntity,
|
||||
var catchRate: Float
|
||||
)
|
||||
+3
-1
@@ -8,10 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
data class PokemonCapturedEvent (
|
||||
val pokemon: Pokemon,
|
||||
val player: ServerPlayerEntity
|
||||
val player: ServerPlayerEntity,
|
||||
val pokeBallEntity: EmptyPokeBallEntity
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* An interaction that will affect an [Entity].
|
||||
* These need to be triggered.
|
||||
*
|
||||
* @param T The type of the [Entity] this interaction will affect.
|
||||
*/
|
||||
interface EntityInteraction<T : Entity> {
|
||||
|
||||
/**
|
||||
* Fired when a [ServerPlayerEntity] interacts with the target entity.
|
||||
*
|
||||
* @param player The [ServerPlayerEntity] interacting with the [entity].
|
||||
* @param entity The [Entity] of type [T] being interacted.
|
||||
* @param stack The [ItemStack] used in this interaction.
|
||||
* @return true if the interaction was successful and no further interactions should be processed
|
||||
*/
|
||||
fun onInteraction(player: ServerPlayerEntity, entity: T, stack: ItemStack): Boolean
|
||||
|
||||
/**
|
||||
* Decreases the stack size by a given amount.
|
||||
* The stack size should be validated beforehand.
|
||||
* If the [player] is in creative mode the decrement won't be performed.
|
||||
*
|
||||
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
|
||||
* @param stack The [ItemStack] being mutated.
|
||||
* @param amount The amount to deduct from the stack, default is 1.
|
||||
*/
|
||||
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
|
||||
if (!player.isCreative) {
|
||||
stack.decrement(amount)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A type adapter for [EntityInteraction]s.
|
||||
* For the default implementation with Pokémon entities see [CobblemonPokemonEntityInteractionTypeAdapter].
|
||||
*
|
||||
* @param T The type of [EntityInteraction].
|
||||
*
|
||||
* @author Licious
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
interface EntityInteractionTypeAdapter<T : EntityInteraction<*>> : JsonDeserializer<T>, JsonSerializer<T> {
|
||||
|
||||
fun registerInteraction(identifier: Identifier, type: KClass<out T>)
|
||||
|
||||
}
|
||||
+12
-24
@@ -6,23 +6,26 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.item.interactive
|
||||
package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokemonInteractiveItem.Ownership
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundEvent
|
||||
|
||||
/**
|
||||
* An [InteractiveItem] targeting [PokemonEntity]s.
|
||||
*
|
||||
* @param accepted The [Ownership] variants for this interaction to fire [PokemonInteractiveItem.processInteraction].
|
||||
* An [EntityInteraction] targeting [PokemonEntity]s.
|
||||
* These need to be manually triggered if not implemented by an item.
|
||||
*/
|
||||
interface PokemonInteractiveItem : InteractiveItem<PokemonEntity> {
|
||||
interface PokemonEntityInteraction : EntityInteraction<PokemonEntity> {
|
||||
|
||||
/**
|
||||
* The accepted [Ownership] for the Pokémon entity in order for the interaction to fire.
|
||||
*/
|
||||
val accepted: Set<Ownership>
|
||||
fun getSound(): SoundEvent? = CobblemonSounds.ITEM_USE
|
||||
val sound: SoundEvent?
|
||||
get() = CobblemonSounds.ITEM_USE
|
||||
|
||||
override fun onInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean {
|
||||
val pokemon = entity.pokemon
|
||||
@@ -40,30 +43,15 @@ interface PokemonInteractiveItem : InteractiveItem<PokemonEntity> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired after [InteractiveItem.onInteraction] the [Ownership] is checked if contained in [accepted].
|
||||
* Fired after [EntityInteraction.onInteraction] the [Ownership] is checked if contained in [accepted].
|
||||
*
|
||||
* @param player The [ServerPlayerEntity] interacting with the [entity].
|
||||
* @param entity The [PokemonEntity] being interacted with.
|
||||
* @param stack The [ItemStack] used in this interaction. [ItemStack.getItem] will always be of the same type as this [InteractiveItem].
|
||||
* @param stack The [ItemStack] used in this interaction.
|
||||
* @return true if the interaction was successful and no further interactions should be processed.
|
||||
*/
|
||||
fun processInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean
|
||||
|
||||
/**
|
||||
* Decreases the stack size by a given amount.
|
||||
* The stack size should be validated beforehand.
|
||||
* If the [player] is in creative mode the decrement won't be performed.
|
||||
*
|
||||
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
|
||||
* @param stack The [ItemStack] being mutated.
|
||||
* @param amount The amount to deduct from the stack, default is 1.
|
||||
*/
|
||||
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
|
||||
if (!player.isCreative) {
|
||||
stack.decrement(amount)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the ownership status of a Pokemon relative to a Player.
|
||||
*
|
||||
@@ -106,9 +106,14 @@ interface PokemonSelectingItem {
|
||||
}
|
||||
|
||||
fun interactGeneral(player: ServerPlayerEntity, stack: ItemStack): TypedActionResult<ItemStack> {
|
||||
val party = player.party().toList()
|
||||
if (party.isEmpty()) {
|
||||
return TypedActionResult.fail(stack)
|
||||
}
|
||||
|
||||
PartySelectCallbacks.createFromPokemon(
|
||||
player = player,
|
||||
pokemon = player.party().toList(),
|
||||
pokemon = party,
|
||||
canSelect = ::canUseOnPokemon,
|
||||
handler = { pk ->
|
||||
if (stack.isHeld(player)) {
|
||||
|
||||
@@ -10,21 +10,22 @@ package com.cobblemon.mod.common.api.mulch
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.item.MulchItem
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* Represents the different types of Mulch implemented in the mod.
|
||||
*
|
||||
*/
|
||||
enum class MulchVariant {
|
||||
|
||||
enum class MulchVariant(val isBiomeMulch: Boolean = true, val duration: Int = 0) : StringIdentifiable {
|
||||
COARSE,
|
||||
GROWTH,
|
||||
GROWTH(false, 3),
|
||||
HUMID,
|
||||
LOAMY,
|
||||
PEAT,
|
||||
RICH,
|
||||
RICH(false, 3),
|
||||
SANDY,
|
||||
SURPRISE;
|
||||
SURPRISE(false, 3);
|
||||
|
||||
/**
|
||||
* Resolves the item version of the Mulch.
|
||||
@@ -42,4 +43,7 @@ enum class MulchVariant {
|
||||
SURPRISE -> CobblemonItems.SURPRISE_MULCH
|
||||
}
|
||||
|
||||
}
|
||||
override fun asString(): String {
|
||||
return name.lowercase()
|
||||
}
|
||||
}
|
||||
|
||||
+20
-6
@@ -8,9 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokeball.catching.calculators
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
/**
|
||||
@@ -24,7 +27,6 @@ import net.minecraft.entity.LivingEntity
|
||||
* @since November 30, 2021
|
||||
*/
|
||||
interface CaptureCalculator {
|
||||
|
||||
/**
|
||||
* The literal ID of this calculator.
|
||||
* Used when registering the calculator to the registry in order to be used by the game rule.
|
||||
@@ -37,10 +39,22 @@ interface CaptureCalculator {
|
||||
* Processes a capture attempt with the given params.
|
||||
*
|
||||
* @param thrower The [LivingEntity] that threw the [PokeBall].
|
||||
* @param pokeBall The [PokeBall] used.
|
||||
* @param target The target [Pokemon] attempting to be captured.
|
||||
* @return
|
||||
* @param pokeBallEntity The [EmptyPokeBallEntity] used.
|
||||
* @param target The target [PokemonEntity] attempting to be captured.
|
||||
* @return a [CaptureContext] that is the result of the capture attempt.
|
||||
*/
|
||||
fun processCapture(thrower: LivingEntity, pokeBall: PokeBall, target: Pokemon) : CaptureContext
|
||||
fun processCapture(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity) : CaptureContext
|
||||
|
||||
fun getCatchRate(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity, catchRate: Float): Float {
|
||||
val event = PokemonCatchRateEvent(
|
||||
thrower = thrower,
|
||||
pokemonEntity = target,
|
||||
pokeBallEntity = pokeBallEntity,
|
||||
catchRate = catchRate
|
||||
)
|
||||
|
||||
CobblemonEvents.POKEMON_CATCH_RATE.post(event)
|
||||
|
||||
return event.catchRate
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.item.Flavor
|
||||
import com.cobblemon.mod.common.api.berry.Flavor
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
@@ -287,12 +287,12 @@ open class PokemonProperties {
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
pokemon.ivs[stat.key] = stat.value
|
||||
pokemon.setIV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
evs?.let { evs ->
|
||||
evs.forEach { stat ->
|
||||
pokemon.evs[stat.key] = stat.value
|
||||
pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,12 +322,12 @@ open class PokemonProperties {
|
||||
customProperties.forEach { it.apply(pokemonEntity) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
pokemonEntity.pokemon.ivs[stat.key] = stat.value
|
||||
pokemonEntity.pokemon.setIV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
evs?.let { evs ->
|
||||
evs.forEach { stat ->
|
||||
pokemonEntity.pokemon.evs[stat.key] = stat.value
|
||||
pokemonEntity.pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
val heightm = form?.height ?: species.height
|
||||
val weightkg = form?.weight ?: species.weight
|
||||
// This is ugly, but we already have it hardcoded in the mod anyway
|
||||
val maxHp = if (species.showdownId() == "shedinja") 1 else null
|
||||
val maxHP = if (species.showdownId() == "shedinja") 1 else null
|
||||
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
|
||||
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
|
||||
// ToDo battleOnly
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.stats
|
||||
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
|
||||
/**
|
||||
@@ -53,9 +54,24 @@ interface EvCalculator {
|
||||
|
||||
object Generation8EvCalculator : EvCalculator {
|
||||
|
||||
private val powerItems = mapOf(
|
||||
Stats.SPEED to CobblemonItemTags.POWER_ANKLET,
|
||||
Stats.SPECIAL_DEFENCE to CobblemonItemTags.POWER_BAND,
|
||||
Stats.DEFENCE to CobblemonItemTags.POWER_BELT,
|
||||
Stats.ATTACK to CobblemonItemTags.POWER_BRACER,
|
||||
Stats.SPECIAL_ATTACK to CobblemonItemTags.POWER_LENS,
|
||||
Stats.HP to CobblemonItemTags.POWER_WEIGHT
|
||||
)
|
||||
|
||||
override fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon): Map<Stat, Int> {
|
||||
// ToDo Once held items and EV related items are implemented update this
|
||||
return opponentPokemon.originalPokemon.form.evYield
|
||||
val heldItem = battlePokemon.effectedPokemon.heldItemNoCopy()
|
||||
val evYield = mutableMapOf<Stat, Int>()
|
||||
|
||||
for ((stat, value) in opponentPokemon.originalPokemon.form.evYield) {
|
||||
val boost = if (!heldItem.isEmpty && heldItem.isIn(powerItems[stat])) 8 else 0
|
||||
evYield[stat] = evYield.getOrDefault(stat, 0) + value + boost
|
||||
}
|
||||
return evYield
|
||||
}
|
||||
|
||||
}
|
||||
-20
@@ -1,20 +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.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
|
||||
|
||||
open class GameRuleInfluence : SpawningInfluence {
|
||||
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
|
||||
return detail !is PokemonSpawnDetail || ctx.world.gameRules.getBoolean(DO_POKEMON_SPAWNING)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.chunk.Chunk
|
||||
import net.minecraft.world.chunk.ChunkStatus
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
|
||||
|
||||
/**
|
||||
* A type of [TickingSpawner] that operates within some area. When this spawner type
|
||||
@@ -60,6 +61,9 @@ abstract class AreaSpawner(
|
||||
|
||||
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
|
||||
val area = getArea(cause)
|
||||
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
|
||||
return null
|
||||
}
|
||||
val constrainedArea = if (area != null) constrainArea(area) else null
|
||||
if (constrainedArea != null) {
|
||||
val numberNearby = constrainedArea.world.getEntitiesByClass(
|
||||
|
||||
+2
-3
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.spawner
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnerManager
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.GameRuleInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
@@ -26,7 +25,7 @@ import net.minecraft.server.network.ServerPlayerEntity
|
||||
*/
|
||||
object PlayerSpawnerFactory {
|
||||
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) }, { GameRuleInfluence() })
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
|
||||
|
||||
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
|
||||
val influences = influenceBuilders.mapNotNull { it(player) }
|
||||
@@ -34,4 +33,4 @@ object PlayerSpawnerFactory {
|
||||
it.influences.addAll(influences)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -121,8 +121,12 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
|
||||
evs[Stats.SPECIAL_DEFENCE] = nbt.getInt("EVSpecialDefense")
|
||||
evs[Stats.SPEED] = nbt.getInt("EVSpeed")
|
||||
|
||||
result.evs = evs
|
||||
result.ivs = ivs
|
||||
ivs.forEach { stat ->
|
||||
result.setIV(stat.key, stat.value)
|
||||
}
|
||||
evs.forEach { stat ->
|
||||
result.setEV(stat.key, stat.value)
|
||||
}
|
||||
|
||||
for (move in nbt.getList("Moveset", 10)) {
|
||||
val compound = move as NbtCompound
|
||||
|
||||
@@ -26,6 +26,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
|
||||
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
|
||||
@JvmField val APRICORNS = createTag("apricorns")
|
||||
@JvmField val BERRY_SOIL = createTag("berry_soil")
|
||||
@JvmField val CROPS = createTag("crops")
|
||||
@JvmField val DRIPSTONE_GROWABLE = createTag("dripstone_growable")
|
||||
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
|
||||
@@ -38,4 +39,4 @@ object CobblemonBlockTags {
|
||||
|
||||
private fun createTag(name: String) = TagKey.of(RegistryKeys.BLOCK, cobblemonResource(name))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,18 @@ object CobblemonItemTags {
|
||||
@JvmField
|
||||
val EVERSTONE = create("held/everstone")
|
||||
@JvmField
|
||||
val POWER_ANKLET = create("held/power_anklet")
|
||||
@JvmField
|
||||
val POWER_BAND = create("held/power_band")
|
||||
@JvmField
|
||||
val POWER_BELT = create("held/power_belt")
|
||||
@JvmField
|
||||
val POWER_BRACER = create("held/power_bracer")
|
||||
@JvmField
|
||||
val POWER_LENS = create("held/power_lens")
|
||||
@JvmField
|
||||
val POWER_WEIGHT = create("held/power_weight")
|
||||
@JvmField
|
||||
val EVOLUTION_STONES = create("evolution_stones")
|
||||
@JvmField
|
||||
val EVOLUTION_ITEMS = create("evolution_items")
|
||||
|
||||
@@ -21,24 +21,27 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
object BattleBuilder {
|
||||
@JvmOverloads
|
||||
fun pvp1v1(
|
||||
player1: ServerPlayerEntity,
|
||||
player2: ServerPlayerEntity,
|
||||
leadingPokemonPlayer1: UUID? = null,
|
||||
leadingPokemonPlayer2: UUID? = null,
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayerEntity) -> PartyStore = { it.party() }
|
||||
): BattleStartResult {
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
|
||||
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer1)
|
||||
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer2)
|
||||
|
||||
val player1Actor = PlayerBattleActor(player1.uuid, team1)
|
||||
val player2Actor = PlayerBattleActor(player2.uuid, team2)
|
||||
@@ -89,6 +92,7 @@ object BattleBuilder {
|
||||
* @param fleeDistance How far away the player must get to flee the Pokémon. If the value is -1, it cannot be fled.
|
||||
* @param party The party of the player to use for the battle. This does not need to be their actual party. Defaults to it though.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun pve(
|
||||
player: ServerPlayerEntity,
|
||||
pokemonEntity: PokemonEntity,
|
||||
|
||||
@@ -31,6 +31,7 @@ object BattleRegistry {
|
||||
class BattleChallenge(
|
||||
val challengeId: UUID,
|
||||
val challengedPlayerUUID: UUID,
|
||||
val selectedPokemonId: UUID,
|
||||
var expiryTimeSeconds: Int = 60
|
||||
) {
|
||||
val challengedTime = Instant.now()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.*
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -678,7 +679,7 @@ object ShowdownInterpreter {
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
// Move may be null as it's not always given
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it.replace(" ",""))?.displayName } ?: "".text()
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
|
||||
|
||||
val lang = when (effectID) {
|
||||
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
|
||||
@@ -814,7 +815,7 @@ object ShowdownInterpreter {
|
||||
private fun handleRechargeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>){
|
||||
battle.dispatchWaiting(2F){
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName() ?: ""))
|
||||
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName()))
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
@@ -949,7 +950,7 @@ object ShowdownInterpreter {
|
||||
}
|
||||
// Includes revealed move
|
||||
"forewarn" -> {
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it.replace(" ",""))?.displayName } ?: "".text()
|
||||
val moveName = message.argumentAt(2)?.let { Moves.getByName(it)?.displayName } ?: Text.EMPTY
|
||||
battleLang("activate.forewarn", sourceName, moveName)
|
||||
}
|
||||
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, message.effectAt(1)!!.typelessData)
|
||||
@@ -1224,11 +1225,12 @@ object ShowdownInterpreter {
|
||||
val request = BattleRegistry.gson.fromJson(message.rawMessage.split("|request|")[1], ShowdownActionRequest::class.java)
|
||||
if (battle.started) {
|
||||
battle.dispatchGo {
|
||||
// This request won't be acted on until the start of next turn
|
||||
battleActor.sendUpdate(BattleQueueRequestPacket(request))
|
||||
battleActor.request = request
|
||||
battleActor.responses.clear()
|
||||
// We need to send this out because 'upkeep' isn't received until the request is handled since the turn won't swap
|
||||
if (request.forceSwitch.withIndex().any { it.value && battleActor.activePokemon.getOrNull(it.index)?.isGone() == false }) {
|
||||
if (request.forceSwitch.contains(true)) {
|
||||
battle.doWhenClear {
|
||||
battleActor.mustChoose = true
|
||||
battleActor.sendUpdate(BattleMakeChoicePacket())
|
||||
@@ -1513,6 +1515,9 @@ object ShowdownInterpreter {
|
||||
battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battlePokemon.contextManager.remove(item.id, BattleContext.Type.ITEM)
|
||||
if (message.hasOptionalArgument("eat")) {
|
||||
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1613,7 +1618,7 @@ object ShowdownInterpreter {
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-clearallboost
|
||||
* |-clearallboost|
|
||||
*
|
||||
* Clears all boosts from all Pokémon on both sides.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.pokemon
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
|
||||
@@ -89,4 +90,8 @@ open class BattlePokemon(
|
||||
|
||||
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
|
||||
fun canBeSentOut() = !isSentOut() && !willBeSwitchedIn && health > 0
|
||||
|
||||
fun writeVariables(struct: VariableStruct) {
|
||||
effectedPokemon.writeVariables(struct)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.predicate.NumberRange
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.WorldView
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* A [GrowthFactor] that is active based on the downfall property of a [Biome].
|
||||
*
|
||||
* @property range The range of possible [Biome.getDownfall] values.
|
||||
* @property bonusYield The range of possible bonus values.
|
||||
*
|
||||
* @author Licious
|
||||
* @since December 2nd, 2022
|
||||
*/
|
||||
class BiomeDownfallGrowthFactor(
|
||||
val range: NumberRange.FloatRange,
|
||||
val bonusYield: IntRange
|
||||
) : GrowthFactor {
|
||||
|
||||
override fun validateArguments() {
|
||||
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
|
||||
throw IllegalArgumentException("$ID bonusYield must be a positive range")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
|
||||
val biome = world.getBiome(pos).value()
|
||||
return this.range.test(biome.weather.downfall.toDouble())
|
||||
}
|
||||
|
||||
override fun yield() = this.bonusYield.random()
|
||||
|
||||
override fun minYield() = this.bonusYield.first
|
||||
|
||||
override fun maxYield() = this.bonusYield.last
|
||||
|
||||
companion object {
|
||||
|
||||
val ID = cobblemonResource("biome_downfall")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.registry.BiomeIdentifierCondition
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.WorldView
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
class PreferredBiomeGrowthFactor(
|
||||
val bonusYield: IntRange
|
||||
) : GrowthFactor{
|
||||
override fun validateArguments() {
|
||||
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
|
||||
throw IllegalArgumentException("$ID bonusYield must be a positive range")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
|
||||
val biome = world.getBiome(pos)
|
||||
val block = state.block as BerryBlock
|
||||
val biomeTags = block.berry()?.preferredBiomeTags ?: emptyList()
|
||||
|
||||
return biomeTags.any { biome.isIn(it) }
|
||||
}
|
||||
|
||||
override fun yield() = this.bonusYield.random()
|
||||
|
||||
override fun minYield() = this.bonusYield.first
|
||||
|
||||
override fun maxYield() = this.bonusYield.last
|
||||
|
||||
companion object {
|
||||
|
||||
val ID = cobblemonResource("preferred_biome")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.berry
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.GrowthFactor
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.predicate.NumberRange
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.WorldView
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* A [GrowthFactor] that is active based on the temperature property of a [Biome].
|
||||
*
|
||||
* @property range The range of possible [Biome.getTemperature] values.
|
||||
* @property bonusYield The range of possible bonus values.
|
||||
*
|
||||
* @author Licious
|
||||
* @since December 2nd, 2022
|
||||
*/
|
||||
class BiomeTemperatureGrowthFactor(
|
||||
val range: NumberRange.FloatRange,
|
||||
val bonusYield: IntRange
|
||||
) : GrowthFactor {
|
||||
|
||||
override fun validateArguments() {
|
||||
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
|
||||
throw IllegalArgumentException("$ID bonusYield must be a positive range")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
|
||||
val biome = world.getBiome(pos).value()
|
||||
return this.range.test(biome.temperature.toDouble())
|
||||
}
|
||||
|
||||
override fun yield() = this.bonusYield.random()
|
||||
|
||||
override fun minYield() = this.bonusYield.first
|
||||
|
||||
override fun maxYield() = this.bonusYield.last
|
||||
|
||||
companion object {
|
||||
|
||||
val ID = cobblemonResource("biome_temperature")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.block
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.mulch.Mulchable
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.item.ShovelItem
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.state.property.IntProperty
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) : BlockWithEntity(settings), Fertilizable, Mulchable {
|
||||
|
||||
private val lookupDirections = setOf(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)
|
||||
|
||||
/**
|
||||
* Returns the [Berry] behind this block,
|
||||
* This will be null if it doesn't exist in the [Berries] registry.
|
||||
*
|
||||
* @return The [Berry] if existing.
|
||||
*/
|
||||
fun berry(): Berry? = Berries.getByIdentifier(this.berryIdentifier)
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState) = BerryBlockEntity(pos, state, berryIdentifier)
|
||||
|
||||
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = !this.isMaxAge(state)
|
||||
|
||||
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = !this.isMaxAge(state)
|
||||
|
||||
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = checkType(blockWithEntityType, CobblemonBlockEntities.BERRY, BerryBlockEntity.TICKER)
|
||||
|
||||
init {
|
||||
defaultState = this.stateManager.defaultState
|
||||
.with(WAS_GENERATED, false)
|
||||
.with(AGE, 0)
|
||||
}
|
||||
|
||||
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
|
||||
val curAge = state.get(AGE)
|
||||
val newAge = curAge + 1
|
||||
if (newAge > FRUIT_AGE) return
|
||||
var newState = state.with(AGE, newAge)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (curAge == MATURE_AGE) {
|
||||
determineMutation(world, random, pos, state)
|
||||
}
|
||||
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
|
||||
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
|
||||
treeEntity.markDirty()
|
||||
}
|
||||
|
||||
fun determineMutation(world: World, random: Random, pos: BlockPos, state: BlockState) {
|
||||
val mutations = hashSetOf<Berry>()
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
for (direction in this.lookupDirections) {
|
||||
val redirectedPos = pos.add(direction.vector)
|
||||
val redirectedState = world.getBlockState(redirectedPos)
|
||||
val berryBlock = redirectedState.block as? BerryBlock ?: continue
|
||||
val berry = berryBlock.berry() ?: continue
|
||||
val mutation = this.berry()?.mutationWith(berry) ?: continue
|
||||
mutations += mutation
|
||||
}
|
||||
this.berry()?.let { berry ->
|
||||
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
|
||||
if (berryMutationOffer.mutations.isNotEmpty()) {
|
||||
var mutateChance = 125
|
||||
if (treeEntity.mulchVariant == MulchVariant.SURPRISE) {
|
||||
mutateChance *= 4
|
||||
treeEntity.mulchDuration--
|
||||
}
|
||||
val mutation = if (random.nextInt(1000) < mutateChance) mutations.random() else null
|
||||
(world.getBlockEntity(pos) as? BerryBlockEntity)?.let { blockEntity ->
|
||||
CobblemonEvents.BERRY_MUTATION_RESULT.post(BerryMutationResultEvent(berry, world, state, pos, berryMutationOffer.mutations, mutation)) { berryMutationResult ->
|
||||
berryMutationResult.pickedMutation?.let { mutation -> blockEntity.mutate(mutation) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun canHaveMulchApplied(
|
||||
world: ServerWorld,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
variant: MulchVariant
|
||||
): Boolean {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
return treeEntity.mulchVariant == null
|
||||
}
|
||||
|
||||
override fun applyMulch(
|
||||
world: ServerWorld,
|
||||
random: Random,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
variant: MulchVariant
|
||||
) {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
treeEntity.mulchVariant = variant
|
||||
treeEntity.mulchDuration = variant.duration
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if(player.getStackInHand(hand).item is ShovelItem && treeEntity.mulchVariant != null) {
|
||||
treeEntity.mulchVariant = null
|
||||
treeEntity.markDirty()
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getStackInHand(hand).isOf(Items.BONE_MEAL)) {
|
||||
return ActionResult.PASS
|
||||
}
|
||||
else if (this.isMaxAge(state)) {
|
||||
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return ActionResult.PASS
|
||||
blockEntity.harvest(world, state, pos, player).forEach { drop ->
|
||||
Block.dropStack(world, pos, drop)
|
||||
}
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
return state.get(WAS_GENERATED) || world.getBlockState(pos.down()).isIn(CobblemonBlockTags.BERRY_SOIL)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
return if (state.canPlaceAt(world, pos)) super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos) else Blocks.AIR.defaultState
|
||||
}
|
||||
|
||||
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) {
|
||||
if (!world.isClient) {
|
||||
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
|
||||
blockEntity.generateGrowthPoints(world, state, pos, placer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(AGE)
|
||||
builder.add(WAS_GENERATED)
|
||||
}
|
||||
|
||||
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
|
||||
val berryItem = this.berry()?.item() ?: return ItemStack.EMPTY
|
||||
return ItemStack(berryItem)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
val berry = this.berry() ?: return VoxelShapes.fullCube()
|
||||
return if (state.get(AGE) >= MATURE_AGE) berry.matureShape else berry.sproutShape
|
||||
}
|
||||
|
||||
private fun isMaxAge(state: BlockState) = state.get(AGE) == FRUIT_AGE
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getRenderType(blockState: BlockState) = BlockRenderType.MODEL
|
||||
|
||||
companion object {
|
||||
const val MATURE_AGE = 3
|
||||
const val FLOWER_AGE = 4
|
||||
const val FRUIT_AGE = 5
|
||||
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
|
||||
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
|
||||
world.playSoundServer(
|
||||
position = blockPos.toVec3d(),
|
||||
sound = CobblemonSounds.PC_ON,
|
||||
volume = 1F,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
)
|
||||
return ActionResult.SUCCESS
|
||||
|
||||
@@ -246,7 +246,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
|
||||
world.playSoundServer(
|
||||
position = pos.toVec3d(),
|
||||
sound = CobblemonSounds.PC_ON,
|
||||
volume = 1F,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val floor = world.getBlockState(pos.down())
|
||||
// A bit of a copy pasta but we don't have access to the BlockState being attempted to be placed above on the canPlantOnTop
|
||||
return (world.getBaseLightLevel(pos, 0) >= 8 || world.isSkyVisible(pos)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
|
||||
return (world.getBlockState(pos).isIn(BlockTags.REPLACEABLE_BY_TREES)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
|
||||
}
|
||||
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AGE_SHAPES.getOrNull(this.getAge(state)) ?: VoxelShapes.fullCube()
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.berry.Berry
|
||||
import com.cobblemon.mod.common.api.berry.GrowthPoint
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.InvalidIdentifierException
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.event.GameEvent
|
||||
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state) {
|
||||
lateinit var berryIdentifier: Identifier
|
||||
private val ticksPerMinute = 1200
|
||||
//The time left for the tree to grow to stage 5
|
||||
var growthTimer: Int = 72000
|
||||
set(value) {
|
||||
if (value < 0) {
|
||||
throw IllegalArgumentException("You cannot set the growth time to less than zero")
|
||||
}
|
||||
if (field != value) {
|
||||
this.markDirty()
|
||||
}
|
||||
field = value
|
||||
}
|
||||
//The time left for the tree to grow to the next stage
|
||||
var stageTimer: Int = growthTimer / BerryBlock.MATURE_AGE
|
||||
set(value) {
|
||||
if (value < 0) {
|
||||
throw IllegalArgumentException("You cannot set the stage growth time to less than zero")
|
||||
}
|
||||
if (field != value) {
|
||||
this.markDirty()
|
||||
}
|
||||
field = value
|
||||
}
|
||||
private val growthPoints = arrayListOf<Identifier>()
|
||||
|
||||
/**
|
||||
* The idea behind the growth point sequence is it's a 16-long string of
|
||||
* hexadecimal numbers. They represent the order of the growth points that
|
||||
* will get used by this tree. If the berry type has randomized growth points,
|
||||
* this sequence gets scrambled, and then when lining up berries with their
|
||||
* growth points, it will take the berry index, check the symbol at that
|
||||
* position in this sequence, and use that as the growth point index from
|
||||
* reference data.
|
||||
*
|
||||
* Kinda confusing but it works without saving an array or something. Protections
|
||||
* exist for when there are fewer than 16 growth points on a tree (currently always).
|
||||
*/
|
||||
private var growthPointSequence = "0123456789ABCDEF"
|
||||
// Just a cheat to not invoke markDirty unnecessarily
|
||||
private var wasLoading = false
|
||||
var mulchVariant: MulchVariant? = null
|
||||
var mulchDuration = 0
|
||||
set(value) {
|
||||
if (value == 0 && !(mulchVariant?.isBiomeMulch ?: false)) {
|
||||
mulchVariant = null
|
||||
}
|
||||
field = value
|
||||
this.markDirty()
|
||||
}
|
||||
|
||||
constructor(pos: BlockPos, state: BlockState, berryIdentifier: Identifier): this(pos, state) {
|
||||
this.berryIdentifier = berryIdentifier
|
||||
resetGrowTimers(pos, state)
|
||||
if (state.get(BerryBlock.WAS_GENERATED) && state.get(BerryBlock.AGE) >= 4) {
|
||||
generateSimpleYields()
|
||||
}
|
||||
}
|
||||
|
||||
fun resetGrowTimers(pos: BlockPos, state: BlockState) {
|
||||
val curAge = state.get(BerryBlock.AGE)
|
||||
if (curAge != 5) {
|
||||
val berry = Berries.getByIdentifier(berryIdentifier)!!
|
||||
var multiplier = 10
|
||||
if (mulchVariant == MulchVariant.GROWTH) {
|
||||
mulchDuration--
|
||||
multiplier = 15
|
||||
}
|
||||
val upperGrowthLimit = ((if (curAge == 0) berry.growthTime.last else berry.refreshRate.last) * multiplier) / 10
|
||||
val lowerGrowthLimit = ((if (curAge == 0) berry.growthTime.first else berry.refreshRate.first) * multiplier) / 10
|
||||
val growthRange = lowerGrowthLimit..upperGrowthLimit
|
||||
growthTimer = growthRange.random() * ticksPerMinute
|
||||
|
||||
goToNextStageTimer(BerryBlock.FRUIT_AGE - curAge)
|
||||
}
|
||||
}
|
||||
|
||||
fun goToNextStageTimer(stagesLeft: Int) {
|
||||
val avgStageTime = growthTimer / stagesLeft
|
||||
stageTimer = this.world?.random?.nextBetween((avgStageTime * 8) / 10, avgStageTime) ?:
|
||||
(((Math.random() * 0.2) + 0.8) * avgStageTime).toInt()
|
||||
growthTimer -= stageTimer
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Berry] behind this entity,
|
||||
* This will be null if it doesn't exist in the [Berries] registry.
|
||||
*
|
||||
* @return The [Berry] if existing.
|
||||
*/
|
||||
fun berry() = Berries.getByIdentifier(berryIdentifier)
|
||||
|
||||
/**
|
||||
* Generates a random amount of growth points for this tree.
|
||||
*
|
||||
* @param world The [World] the tree is in.
|
||||
* @param state The [BlockState] of the tree.
|
||||
* @param pos The [BlockPos] of the tree.
|
||||
* @param placer The [LivingEntity] tending to the tree if any.
|
||||
*/
|
||||
fun generateGrowthPoints(world: World, state: BlockState, pos: BlockPos, placer: LivingEntity?) {
|
||||
val berry = this.berry() ?: return
|
||||
val yield = berry.calculateYield(world, state, pos, placer)
|
||||
this.growthPoints.clear()
|
||||
repeat(yield) {
|
||||
this.growthPoints += berry.identifier
|
||||
}
|
||||
this.growthPointSequence = this.growthPointSequence.toCharArray().also { if (berry.randomizedGrowthPoints) it.shuffle() }.concatToString()
|
||||
this.markDirty()
|
||||
}
|
||||
|
||||
//Used for naturally spawning berries
|
||||
fun generateSimpleYields() {
|
||||
val numBerries = berry()?.baseYield?.random() ?: return
|
||||
repeat(numBerries) {
|
||||
growthPoints.add(berryIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the berry produce in [ItemStack] form.
|
||||
*
|
||||
* @param world The [World] the tree is in.
|
||||
* @param state The [BlockState] of the tree.
|
||||
* @param pos The [BlockPos] of the tree.
|
||||
* @param player The [PlayerEntity] harvesting the tree.
|
||||
* @return The resulting [ItemStack]s to be dropped.
|
||||
*/
|
||||
fun harvest(world: World, state: BlockState, pos: BlockPos, player: PlayerEntity): Collection<ItemStack> {
|
||||
val drops = arrayListOf<ItemStack>()
|
||||
val unique = this.growthPoints.groupingBy { it }.eachCount()
|
||||
unique.forEach { (identifier, amount) ->
|
||||
val berryItem = Berries.getByIdentifier(identifier)?.item()
|
||||
if (berryItem != null) {
|
||||
var remain = amount
|
||||
while (remain > 0) {
|
||||
val count = remain.coerceAtMost(berryItem.maxCount)
|
||||
drops += ItemStack(berryItem, count)
|
||||
remain -= count
|
||||
}
|
||||
}
|
||||
}
|
||||
this.berry()?.let { berry ->
|
||||
if (player is ServerPlayerEntity) {
|
||||
CobblemonEvents.BERRY_HARVEST.post(BerryHarvestEvent(berry, player, world, pos, state, this, drops))
|
||||
}
|
||||
}
|
||||
refresh(world, pos, state, player)
|
||||
return drops
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
this.berryIdentifier = Identifier(nbt.getString(BERRY).takeIf { it.isNotBlank() } ?: "cobblemon:pecha")
|
||||
this.wasLoading = true
|
||||
this.growthPoints.clear()
|
||||
this.growthTimer = nbt.getInt(GROWTH_TIMER).coerceAtLeast(0)
|
||||
this.stageTimer = nbt.getInt(STAGE_TIMER).coerceAtLeast(0)
|
||||
//this.lifeCycles = nbt.getInt(LIFE_CYCLES).coerceAtLeast(0)
|
||||
nbt.getList(GROWTH_POINTS, NbtList.STRING_TYPE.toInt()).filterIsInstance<NbtString>().forEach { element ->
|
||||
// In case some 3rd party mutates the NBT incorrectly
|
||||
try {
|
||||
val identifier = Identifier(element.asString())
|
||||
this.growthPoints += identifier
|
||||
} catch (ignored: InvalidIdentifierException) {}
|
||||
}
|
||||
this.mulchVariant = if (nbt.contains(MULCH_VARIANT)) {
|
||||
MulchVariant.valueOf(nbt.getString(MULCH_VARIANT))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
this.mulchDuration = nbt.getInt(MULCH_DURATION)
|
||||
this.wasLoading = false
|
||||
if (nbt.contains(GROWTH_POINTS_SEQUENCE)) {
|
||||
growthPointSequence = nbt.getString(GROWTH_POINTS_SEQUENCE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
nbt.putInt(GROWTH_TIMER, this.growthTimer)
|
||||
nbt.putInt(STAGE_TIMER, this.stageTimer)
|
||||
val list = NbtList()
|
||||
list += this.growthPoints.map { NbtString.of(it.toString()) }
|
||||
nbt.put(GROWTH_POINTS, list)
|
||||
nbt.putString(BERRY, berryIdentifier.toString())
|
||||
if (mulchVariant != null) {
|
||||
nbt.putString(MULCH_VARIANT, mulchVariant.toString())
|
||||
}
|
||||
nbt.putInt(MULCH_DURATION, mulchDuration)
|
||||
nbt.putString(GROWTH_POINTS_SEQUENCE, growthPointSequence)
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
if (!this.wasLoading) {
|
||||
super.markDirty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||
return this.createNbt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects each [Berry] and [GrowthPoint].
|
||||
*
|
||||
* @return Collection of the [Berry] and [GrowthPoint].
|
||||
*/
|
||||
internal fun berryAndGrowthPoint(): List<Pair<Berry, GrowthPoint>> {
|
||||
val baseBerry = this.berry() ?: return emptyList()
|
||||
val berryPoints = arrayListOf<Pair<Berry, GrowthPoint>>()
|
||||
val sequenceIndices = growthPointSequence.toCharArray().filter { it.digitToInt(16) < baseBerry.growthPoints.size }
|
||||
for ((index, identifier) in this.growthPoints.withIndex()) {
|
||||
val berry = Berries.getByIdentifier(identifier) ?: continue
|
||||
val sequenceIndexHex = sequenceIndices[index].digitToInt(16)
|
||||
berryPoints.add(berry to baseBerry.growthPoints[sequenceIndexHex])
|
||||
}
|
||||
return berryPoints
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given [berry] as a mutation in a random [growthPoints].
|
||||
*
|
||||
* @param berry The [Berry] being mutated.
|
||||
*/
|
||||
internal fun mutate(berry: Berry) {
|
||||
if (this.growthPoints.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val index = this.growthPoints.indices.random()
|
||||
this.growthPoints[index] = berry.identifier
|
||||
this.markDirty()
|
||||
}
|
||||
|
||||
private fun refresh(world: World, pos: BlockPos, state:BlockState, player: PlayerEntity) {
|
||||
val newState = state.with(BerryBlock.AGE, 3)
|
||||
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
|
||||
world.emitGameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
this.generateGrowthPoints(world, newState, pos, player)
|
||||
resetGrowTimers(pos, newState)
|
||||
return
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal val TICKER = BlockEntityTicker<BerryBlockEntity> { world, pos, state, blockEntity ->
|
||||
if (world.isClient) return@BlockEntityTicker
|
||||
if (blockEntity.stageTimer > 0) {
|
||||
blockEntity.stageTimer--
|
||||
}
|
||||
if (blockEntity.stageTimer == 0) {
|
||||
(state.block as BerryBlock).grow(world as ServerWorld, world.random, pos, state)
|
||||
}
|
||||
}
|
||||
//private const val LIFE_CYCLES = "life_cycles"
|
||||
private const val GROWTH_POINTS = "GrowthPoints"
|
||||
private const val GROWTH_POINTS_SEQUENCE = "GrowthPointsSequence"
|
||||
private const val GROWTH_TIMER = "GrowthTimer"
|
||||
private const val STAGE_TIMER = "StageTimer"
|
||||
private const val BERRY = "Berry"
|
||||
private const val MULCH_VARIANT = "MulchVariant"
|
||||
private const val MULCH_DURATION = "MulchDuration"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.brewing
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.potion.Potion
|
||||
import net.minecraft.potion.Potions
|
||||
import net.minecraft.recipe.BrewingRecipeRegistry
|
||||
@@ -21,7 +20,21 @@ object BrewingRecipes {
|
||||
@JvmField
|
||||
val brewableItems = mutableListOf(
|
||||
CobblemonItems.MEDICINAL_BREW,
|
||||
CobblemonItems.ZINC
|
||||
CobblemonItems.POTION,
|
||||
CobblemonItems.SUPER_POTION,
|
||||
CobblemonItems.HYPER_POTION,
|
||||
CobblemonItems.MAX_POTION,
|
||||
CobblemonItems.FULL_RESTORE,
|
||||
CobblemonItems.FULL_HEAL,
|
||||
CobblemonItems.ANTIDOTE,
|
||||
CobblemonItems.AWAKENING,
|
||||
CobblemonItems.BURN_HEAL,
|
||||
CobblemonItems.ICE_HEAL,
|
||||
CobblemonItems.PARALYZE_HEAL,
|
||||
CobblemonItems.ETHER,
|
||||
CobblemonItems.MAX_ETHER,
|
||||
CobblemonItems.ELIXIR,
|
||||
CobblemonItems.MAX_ELIXIR
|
||||
)
|
||||
|
||||
fun registerPotionTypes() {
|
||||
@@ -32,14 +45,38 @@ object BrewingRecipes {
|
||||
|
||||
fun getPotionRecipes(): List<Triple<Potion, Ingredient, Potion>> {
|
||||
return listOf(
|
||||
Triple(Potions.WATER, Ingredient.ofItems(CobblemonItems.MEDICINAL_LEEK), Potions.WATER) // This one is Mixin'd to result in brew rather than water
|
||||
Triple(Potions.WATER, Ingredient.ofItems(CobblemonItems.MEDICINAL_LEEK), Potions.WATER), // This one is Mixin'd to result in brew rather than water
|
||||
)
|
||||
}
|
||||
|
||||
fun getItemRecipes(): List<Triple<Item, Ingredient, Item>> {
|
||||
// This is where the majority of our recipes go. First arg is thing down the bottom, second is thing up the top, third is results.
|
||||
return listOf(
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(Items.GOLD_INGOT), CobblemonItems.ZINC)
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.LEPPA_BERRY), CobblemonItems.ETHER),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.HOPO_BERRY), CobblemonItems.ELIXIR),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.ORAN_BERRY), CobblemonItems.POTION),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.SITRUS_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.PECHA_BERRY), CobblemonItems.ANTIDOTE),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.CHESTO_BERRY), CobblemonItems.AWAKENING),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.RAWST_BERRY), CobblemonItems.BURN_HEAL),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.ASPEAR_BERRY), CobblemonItems.ICE_HEAL),
|
||||
Triple(CobblemonItems.MEDICINAL_BREW, Ingredient.ofItems(CobblemonItems.CHERI_BERRY), CobblemonItems.PARALYZE_HEAL),
|
||||
Triple(CobblemonItems.BURN_HEAL, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.ANTIDOTE, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.AWAKENING, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.ICE_HEAL, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.PARALYZE_HEAL, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_HEAL),
|
||||
Triple(CobblemonItems.ETHER, Ingredient.ofItems(CobblemonItems.PEP_UP_FLOWER), CobblemonItems.MAX_ETHER),
|
||||
Triple(CobblemonItems.ELIXIR, Ingredient.ofItems(CobblemonItems.PEP_UP_FLOWER), CobblemonItems.MAX_ELIXIR),
|
||||
Triple(CobblemonItems.POTION, Ingredient.ofItems(CobblemonItems.ENERGY_ROOT), CobblemonItems.SUPER_POTION),
|
||||
Triple(CobblemonItems.SUPER_POTION, Ingredient.ofItems(CobblemonItems.FIGY_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.SUPER_POTION, Ingredient.ofItems(CobblemonItems.WIKI_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.SUPER_POTION, Ingredient.ofItems(CobblemonItems.MAGO_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.SUPER_POTION, Ingredient.ofItems(CobblemonItems.AGUAV_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.SUPER_POTION, Ingredient.ofItems(CobblemonItems.IAPAPA_BERRY), CobblemonItems.HYPER_POTION),
|
||||
Triple(CobblemonItems.HYPER_POTION, Ingredient.ofItems(CobblemonItems.VIVICHOKE), CobblemonItems.MAX_POTION),
|
||||
Triple(CobblemonItems.MAX_POTION, Ingredient.ofItems(CobblemonItems.LUM_BERRY), CobblemonItems.FULL_RESTORE)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.texture.SpriteAtlasHolder
|
||||
import net.minecraft.client.texture.TextureManager
|
||||
|
||||
class CobblemonBerryAtlas(textureManager: TextureManager) : SpriteAtlasHolder(
|
||||
textureManager,
|
||||
cobblemonResource("textures/atlas/berries.png"),
|
||||
cobblemonResource("berries")
|
||||
) {
|
||||
}
|
||||
@@ -8,17 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.*
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonClientImplementation
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTaskTracker
|
||||
import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
@@ -34,11 +31,16 @@ import com.cobblemon.mod.common.client.storage.ClientStorageManager
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import java.awt.Color
|
||||
import net.minecraft.client.color.block.BlockColorProvider
|
||||
import net.minecraft.client.color.block.BlockColors
|
||||
import net.minecraft.client.color.item.ItemColorProvider
|
||||
import net.minecraft.client.color.world.BiomeColors
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.entity.EntityRenderer
|
||||
@@ -88,6 +90,7 @@ object CobblemonClient {
|
||||
PlatformEvents.CLIENT_PLAYER_LOGOUT.subscribe { onLogout() }
|
||||
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HEALING_MACHINE, ::HealingMachineRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.BERRY, ::BerryBlockRenderer)
|
||||
|
||||
registerBlockRenderTypes()
|
||||
registerColors()
|
||||
@@ -164,7 +167,9 @@ object CobblemonClient {
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
CobblemonBlocks.VIVICHOKE_SEEDS,
|
||||
CobblemonBlocks.PEP_UP_FLOWER,
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
*CobblemonBlocks.berries().values.toTypedArray()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -203,6 +208,7 @@ object CobblemonClient {
|
||||
)
|
||||
PokemonModelRepository.reload(resourceManager)
|
||||
PokeBallModelRepository.reload(resourceManager)
|
||||
BerryModelRepository.reload(resourceManager)
|
||||
LOGGER.info("Loaded assets")
|
||||
// PokeBallModelRepository.reload(resourceManager)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ class ClientBallDisplay(val pokeBall: PokeBall, val aspects: Set<String>) : Poke
|
||||
override val stateEmitter = SettableObservable(EmptyPokeBallEntity.CaptureState.FALL)
|
||||
override val shakeEmitter = SimpleObservable<Unit>()
|
||||
|
||||
override fun getEntity() = null
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks += partialTicks
|
||||
}
|
||||
|
||||
+5
-1
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.client.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.EntitySideDelegate
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallPoseableState
|
||||
@@ -21,11 +20,16 @@ class EmptyPokeBallClientDelegate : PokeBallPoseableState(), EntitySideDelegate<
|
||||
override val stateEmitter: SettableObservable<CaptureState> = SettableObservable(NOT)
|
||||
override val shakeEmitter = SimpleObservable<Unit>()
|
||||
|
||||
lateinit var currentEntity: EmptyPokeBallEntity
|
||||
|
||||
override fun getEntity() = currentEntity
|
||||
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks = partialTicks
|
||||
}
|
||||
|
||||
override fun initialize(entity: EmptyPokeBallEntity) {
|
||||
this.currentEntity = entity
|
||||
age = entity.age
|
||||
initSubscriptions()
|
||||
entity.captureState.subscribe {
|
||||
|
||||
+21
-19
@@ -28,10 +28,12 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
const val BEAM_EXTEND_TIME = 0.2F
|
||||
}
|
||||
|
||||
lateinit var entity: PokemonEntity
|
||||
lateinit var currentEntity: PokemonEntity
|
||||
var phaseTarget: Entity? = null
|
||||
var entityScaleModifier = 1F
|
||||
|
||||
override fun getEntity() = currentEntity
|
||||
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks = partialTicks
|
||||
}
|
||||
@@ -46,26 +48,26 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
private val intensityVelocityCap = -0.5F
|
||||
override fun changePokemon(pokemon: Pokemon) {
|
||||
pokemon.isClient = true
|
||||
entity.subscriptions.add(entity.species.subscribeIncludingCurrent {
|
||||
currentEntity.subscriptions.add(currentEntity.species.subscribeIncludingCurrent {
|
||||
currentPose = null
|
||||
entity.pokemon.species = PokemonSpecies.getByIdentifier(Identifier(it))!! // TODO exception handling
|
||||
currentEntity.pokemon.species = PokemonSpecies.getByIdentifier(Identifier(it))!! // TODO exception handling
|
||||
})
|
||||
|
||||
// entity.subscriptions.add(entity.nickname.subscribeIncludingCurrent {
|
||||
// entity.pokemon.nickname = it?.copy()
|
||||
// currentEntity.subscriptions.add(currentEntity.nickname.subscribeIncludingCurrent {
|
||||
// currentEntity.pokemon.nickname = it?.copy()
|
||||
// })
|
||||
|
||||
entity.subscriptions.add(entity.deathEffectsStarted.subscribe {
|
||||
currentEntity.subscriptions.add(currentEntity.deathEffectsStarted.subscribe {
|
||||
if (it) {
|
||||
val model = (currentModel ?: return@subscribe) as PokemonPoseableModel
|
||||
val animation = try { model.getFaintAnimation(entity, this) } catch (e: Exception) { e.printStackTrace(); null } ?: return@subscribe
|
||||
val animation = try { model.getFaintAnimation(currentEntity, this) } catch (e: Exception) { e.printStackTrace(); null } ?: return@subscribe
|
||||
statefulAnimations.add(animation)
|
||||
}
|
||||
})
|
||||
|
||||
entity.subscriptions.add(entity.labelLevel.subscribeIncludingCurrent { if (it > 0) entity.pokemon.level = it })
|
||||
currentEntity.subscriptions.add(currentEntity.labelLevel.subscribeIncludingCurrent { if (it > 0) currentEntity.pokemon.level = it })
|
||||
|
||||
entity.subscriptions.add(entity.phasingTargetId.subscribe {
|
||||
currentEntity.subscriptions.add(currentEntity.phasingTargetId.subscribe {
|
||||
if (it != -1) {
|
||||
setPhaseTarget(it)
|
||||
} else {
|
||||
@@ -73,22 +75,22 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
})
|
||||
|
||||
// pokemon.aspects = entity.aspects.get()
|
||||
// entity.aspects.pipe(emitWhile { pokemon == entity.pokemon }).subscribe {
|
||||
// pokemon.aspects = currentEntity.aspects.get()
|
||||
// currentEntity.aspects.pipe(emitWhile { pokemon == currentEntity.pokemon }).subscribe {
|
||||
// pokemon.aspects = it
|
||||
// }
|
||||
|
||||
entity.subscriptions.add(entity.beamModeEmitter.subscribeIncludingCurrent {
|
||||
currentEntity.subscriptions.add(currentEntity.beamModeEmitter.subscribeIncludingCurrent {
|
||||
if (it == 0.toByte()) {
|
||||
// Do nothing
|
||||
} else if (it == 1.toByte()) {
|
||||
// Scaling up out of pokeball
|
||||
entityScaleModifier = 0F
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
entity.isInvisible = true
|
||||
currentEntity.isInvisible = true
|
||||
after(seconds = BEAM_EXTEND_TIME) {
|
||||
lerp(BEAM_SHRINK_TIME) { entityScaleModifier = it }
|
||||
entity.isInvisible = false
|
||||
currentEntity.isInvisible = false
|
||||
}
|
||||
} else {
|
||||
// Scaling down into pokeball
|
||||
@@ -104,7 +106,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
|
||||
override fun initialize(entity: PokemonEntity) {
|
||||
this.entity = entity
|
||||
this.currentEntity = entity
|
||||
this.age = entity.age
|
||||
}
|
||||
|
||||
@@ -128,25 +130,25 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
|
||||
fun setPhaseTarget(targetId: Int) {
|
||||
this.phaseTarget = entity.world.getEntityById(targetId)
|
||||
this.phaseTarget = currentEntity.world.getEntityById(targetId)
|
||||
}
|
||||
|
||||
override fun handleStatus(status: Byte) {
|
||||
if (status == 10.toByte()) {
|
||||
val model = (currentModel ?: return) as PokemonPoseableModel
|
||||
val animation = model.getEatAnimation(entity, this) ?: return
|
||||
val animation = model.getEatAnimation(currentEntity, this) ?: return
|
||||
statefulAnimations.add(animation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatePostDeath() {
|
||||
++entity.deathTime
|
||||
++currentEntity.deathTime
|
||||
}
|
||||
|
||||
fun cry() {
|
||||
val model = currentModel ?: return
|
||||
if (model is PokemonPoseableModel) {
|
||||
val animation = model.cryAnimation(entity, this) ?: return
|
||||
val animation = model.cryAnimation(currentEntity, this) ?: return
|
||||
statefulAnimations.add(animation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
|
||||
model.setupAnimStateful(null, state, 0F, 0F, 0F, 0F, 0F)
|
||||
|
||||
matrixStack.scale(scale, scale, -scale)
|
||||
matrixStack.translate(0.0, -2.0, -4.0)
|
||||
matrixStack.translate(0.0, 5.5, -4.0)
|
||||
matrixStack.push()
|
||||
|
||||
matrixStack.scale(scale * state.scale, scale * state.scale, 0.1F)
|
||||
@@ -408,8 +408,8 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
|
||||
matrixStack.multiply(quaternion1)
|
||||
matrixStack.multiply(quaternion2)
|
||||
|
||||
val light1 = Vector3f(0.2F, 1.0F, -1.0F)
|
||||
val light2 = Vector3f(0.1F, -1.0F, 2.0F)
|
||||
val light1 = Vector3f(2.2F, 4.0F, -4.0F)
|
||||
val light2 = Vector3f(1.1F, -4.0F, 7.0F)
|
||||
RenderSystem.setShaderLights(light1, light2)
|
||||
quaternion1.conjugate()
|
||||
|
||||
|
||||
+1
@@ -131,6 +131,7 @@ class MoveSelectGUI(
|
||||
}
|
||||
|
||||
override fun shouldCloseOnEsc() = true
|
||||
override fun shouldPause() = false
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
|
||||
|
||||
+45
-11
@@ -10,13 +10,12 @@ package com.cobblemon.mod.common.client.gui.interact.partyselect
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.callback.MoveSelectDTO
|
||||
import com.cobblemon.mod.common.api.callback.PartySelectPokemonDTO
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.interact.moveselect.MoveSelectGUI
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -40,10 +39,12 @@ class PartySelectGUI(
|
||||
val config: PartySelectConfiguration
|
||||
) : Screen(Text.translatable("cobblemon.ui.interact.moveselect")) {
|
||||
companion object {
|
||||
const val WIDTH = 102
|
||||
const val HEIGHT = 115
|
||||
const val WIDTH = 163
|
||||
const val HEIGHT = 132
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private val baseBackgroundResource = cobblemonResource("textures/gui/interact/party_select.png")
|
||||
private val spacerResource = cobblemonResource("textures/gui/interact/party_select_spacer.png")
|
||||
}
|
||||
|
||||
var closed = false
|
||||
@@ -75,8 +76,19 @@ class PartySelectGUI(
|
||||
val y = (height - HEIGHT) / 2
|
||||
|
||||
config.pokemon.forEachIndexed { index, pokemon ->
|
||||
val slotX = x + 5 + (index % 2) * (PartySlotButton.WIDTH + 2)
|
||||
val slotY = y + 5 + (index / 2) * (PartySlotButton.HEIGHT + 2)
|
||||
var slotX = x + 11
|
||||
var slotY = y + 23
|
||||
|
||||
if (index > 0) {
|
||||
val isEven = index % 2 == 0
|
||||
val offsetIndex = (index - (if (isEven) 0 else 1)) / 2
|
||||
val offsetX = if (isEven) 0 else 74
|
||||
val offsetY = if (isEven) 0 else -8
|
||||
|
||||
slotX += offsetX
|
||||
slotY += (31 * offsetIndex) + offsetY
|
||||
}
|
||||
|
||||
addDrawableChild(
|
||||
PartySlotButton(
|
||||
parent = this,
|
||||
@@ -84,7 +96,8 @@ class PartySelectGUI(
|
||||
y = slotY,
|
||||
pokemon = pokemon.pokemonProperties,
|
||||
heldItem = pokemon.heldItem,
|
||||
hpRatio = pokemon.hpRatio,
|
||||
currentHealth = pokemon.currentHealth,
|
||||
maxHealth = pokemon.maxHealth,
|
||||
enabled = pokemon.enabled
|
||||
) { onPress(pokemon) }
|
||||
)
|
||||
@@ -93,8 +106,8 @@ class PartySelectGUI(
|
||||
// Add Exit Button
|
||||
addDrawableChild(
|
||||
ExitButton(
|
||||
pX = x + 72,
|
||||
pY = y + 96
|
||||
pX = x + 134,
|
||||
pY = y + 116
|
||||
) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
config.onBack(this)
|
||||
@@ -117,6 +130,26 @@ class PartySelectGUI(
|
||||
height = HEIGHT
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = config.title.bold(),
|
||||
x = x + 37,
|
||||
y = y + 1.5,
|
||||
centered = true
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = spacerResource,
|
||||
x = (x + 86.5) / SCALE,
|
||||
y = (y + 111) / SCALE,
|
||||
width = 79,
|
||||
height = 12,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, mouseX, mouseY, partialTicks)
|
||||
}
|
||||
@@ -137,6 +170,7 @@ class PartySelectGUI(
|
||||
}
|
||||
|
||||
override fun shouldCloseOnEsc() = true
|
||||
override fun shouldPause() = false
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
|
||||
|
||||
+68
-50
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.interact.partyselect
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
@@ -34,7 +35,8 @@ import org.joml.Vector3f
|
||||
class PartySlotButton(
|
||||
x: Int, y: Int,
|
||||
val pokemon: PokemonProperties,
|
||||
val hpRatio: Float,
|
||||
val currentHealth: Int,
|
||||
val maxHealth: Int,
|
||||
val heldItem: ItemStack,
|
||||
val enabled: Boolean = true,
|
||||
val parent: PartySelectGUI,
|
||||
@@ -42,16 +44,14 @@ class PartySlotButton(
|
||||
) : ButtonWidget(x, y, WIDTH, HEIGHT, Text.literal("Pokemon"), onPress, NarrationSupplier { "".text() }) {
|
||||
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/summary/summary_party_slot.png")
|
||||
private val slotFaintedResource = cobblemonResource("textures/gui/summary/summary_party_slot_fainted.png")
|
||||
private val slotEmptyResource = cobblemonResource("textures/gui/summary/summary_party_slot_empty.png")
|
||||
private val slotResource = cobblemonResource("textures/gui/interact/party_select_slot.png")
|
||||
private val slotFaintedResource = cobblemonResource("textures/gui/interact/party_select_slot_fainted.png")
|
||||
|
||||
const val WIDTH = 46
|
||||
const val WIDTH = 69
|
||||
const val HEIGHT = 27
|
||||
const val SCALE = 0.5F
|
||||
}
|
||||
|
||||
var animationSeconds = 0F
|
||||
var lastTime = System.currentTimeMillis()
|
||||
val state = PokemonFloatingState()
|
||||
|
||||
private val renderablePokemon = pokemon.asRenderablePokemon()
|
||||
@@ -63,7 +63,7 @@ class PartySlotButton(
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (hpRatio <= 0F) slotFaintedResource else slotResource,
|
||||
texture = if (currentHealth <= 0F) slotFaintedResource else slotResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
@@ -90,41 +90,61 @@ class PartySlotButton(
|
||||
)
|
||||
context.matrices.pop()
|
||||
|
||||
val halfScale = 0.5F
|
||||
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.pokeball!!.asIdentifierDefaultingNamespace().path + ".png")
|
||||
val ballHeight = 22
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = ballIcon,
|
||||
x = (x - 2) / halfScale,
|
||||
y = (y - 3) / halfScale,
|
||||
x = (x - 2) / SCALE,
|
||||
y = (y - 3) / SCALE,
|
||||
height = ballHeight,
|
||||
width = 18,
|
||||
textureHeight = ballHeight * 2,
|
||||
scale = halfScale
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
val status = pokemon.status
|
||||
if (hpRatio > 0F && status != null) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.lv.number", pokemon.level!!),
|
||||
x = x + 24,
|
||||
y = y + 6.5,
|
||||
shadow = true,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Pokémon Name
|
||||
val displayName = pokemon.nickname ?: renderablePokemon.species.translatedName
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = displayName.copy(),
|
||||
x = x + 24,
|
||||
y = y + 12.5,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
if ("male" in pokemon.aspects || "female" in pokemon.aspects) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/party/status_$status.png"),
|
||||
x = x + 42,
|
||||
y = y + 5,
|
||||
height = 14,
|
||||
width = 4
|
||||
texture = if ("male" in pokemon.aspects) PartySlotWidget.genderIconMale else PartySlotWidget.genderIconFemale,
|
||||
x = (x + 60.5) / SCALE,
|
||||
y = (y + 12.5) / SCALE,
|
||||
height = 7,
|
||||
width = 5,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
val barWidthMax = 37
|
||||
// HP
|
||||
val hpRatio = currentHealth / maxHealth.toFloat()
|
||||
val barWidthMax = 65
|
||||
val barWidth = hpRatio * barWidthMax
|
||||
val (red, green) = getDepletableRedGreen(hpRatio)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 4,
|
||||
y = y + 25,
|
||||
x = x + 1,
|
||||
y = y + 20,
|
||||
width = barWidth,
|
||||
height = 1,
|
||||
textureWidth = barWidth / hpRatio,
|
||||
@@ -134,28 +154,36 @@ class PartySlotButton(
|
||||
blue = 0.27F
|
||||
)
|
||||
|
||||
if ("male" in pokemon.aspects || "female" in pokemon.aspects) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if ("male" in pokemon.aspects) PartySlotWidget.genderIconMale else PartySlotWidget.genderIconFemale,
|
||||
x = (x + 40) / halfScale,
|
||||
y = (y + 20) / halfScale,
|
||||
height = 7,
|
||||
width = 5,
|
||||
scale = halfScale
|
||||
)
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.lv.number", pokemon.level!!),
|
||||
x = x + 31,
|
||||
y = y + 13,
|
||||
centered = true,
|
||||
shadow = true,
|
||||
scale = halfScale
|
||||
text = "$currentHealth/$maxHealth".text(),
|
||||
x = x + 14,
|
||||
y = y + 22.5,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
val status = pokemon.status
|
||||
if (hpRatio > 0F && status != null) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/interact/party_select_status_$status.png"),
|
||||
x = x + 27,
|
||||
y = y + 22,
|
||||
height = 5,
|
||||
width = 37
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.status.$status").bold(),
|
||||
x = x + 32.5,
|
||||
y = y + 22.5,
|
||||
shadow = true,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
// Held Item
|
||||
if (!heldItem.isEmpty) {
|
||||
renderScaledGuiItemIcon(
|
||||
@@ -166,16 +194,6 @@ class PartySlotButton(
|
||||
matrixStack = matrices
|
||||
)
|
||||
}
|
||||
|
||||
// Pokémon Name
|
||||
val displayName = pokemon.nickname ?: renderablePokemon.species.translatedName
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = displayName.copy(),
|
||||
x = x + 4,
|
||||
y = y + HEIGHT - 7,
|
||||
scale = halfScale
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class ArrowButton(
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager?) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
return
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
|
||||
@@ -145,62 +145,62 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
// Evolve Button
|
||||
addDrawableChild(
|
||||
SummaryButton(
|
||||
buttonX = x + 12F,
|
||||
buttonY = y + 145F,
|
||||
buttonWidth = 54,
|
||||
buttonHeight = 15,
|
||||
clickAction = {
|
||||
after(ticks = 0, serverThread = false) {
|
||||
displaySideScreen(if (sideScreenIndex == EVOLVE) PARTY else EVOLVE)
|
||||
}
|
||||
},
|
||||
text = lang("ui.evolve"),
|
||||
resource = evolveButtonResource,
|
||||
renderRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() },
|
||||
clickRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() }
|
||||
)
|
||||
SummaryButton(
|
||||
buttonX = x + 12F,
|
||||
buttonY = y + 145F,
|
||||
buttonWidth = 54,
|
||||
buttonHeight = 15,
|
||||
clickAction = {
|
||||
after(ticks = 0, serverThread = false) {
|
||||
displaySideScreen(if (sideScreenIndex == EVOLVE) PARTY else EVOLVE)
|
||||
}
|
||||
},
|
||||
text = lang("ui.evolve"),
|
||||
resource = evolveButtonResource,
|
||||
renderRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() },
|
||||
clickRequirement = { selectedPokemon.evolutionProxy.client().isNotEmpty() }
|
||||
)
|
||||
)
|
||||
|
||||
// Init Tabs
|
||||
summaryTabs.clear()
|
||||
summaryTabs.add(
|
||||
SummaryTab(
|
||||
pX = x + 78,
|
||||
pY = y - 1,
|
||||
label = lang("ui.info")
|
||||
) {
|
||||
if (mainScreenIndex != INFO) {
|
||||
displayMainScreen(INFO)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
SummaryTab(
|
||||
pX = x + 78,
|
||||
pY = y - 1,
|
||||
label = lang("ui.info")
|
||||
) {
|
||||
if (mainScreenIndex != INFO) {
|
||||
displayMainScreen(INFO)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
summaryTabs.add(
|
||||
SummaryTab(
|
||||
pX = x + 119,
|
||||
pY = y - 1,
|
||||
label = lang("ui.moves")
|
||||
) {
|
||||
if (mainScreenIndex != MOVES) {
|
||||
displayMainScreen(MOVES)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
SummaryTab(
|
||||
pX = x + 119,
|
||||
pY = y - 1,
|
||||
label = lang("ui.moves")
|
||||
) {
|
||||
if (mainScreenIndex != MOVES) {
|
||||
displayMainScreen(MOVES)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
summaryTabs.add(
|
||||
SummaryTab(
|
||||
pX = x + 160,
|
||||
pY = y - 1,
|
||||
label = lang("ui.stats")
|
||||
) {
|
||||
if (mainScreenIndex != STATS) {
|
||||
displayMainScreen(STATS)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
SummaryTab(
|
||||
pX = x + 160,
|
||||
pY = y - 1,
|
||||
label = lang("ui.stats")
|
||||
) {
|
||||
if (mainScreenIndex != STATS) {
|
||||
displayMainScreen(STATS)
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
summaryTabs[mainScreenIndex].toggleTab()
|
||||
@@ -208,24 +208,24 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
// Add Exit Button
|
||||
addDrawableChild(
|
||||
ExitButton(
|
||||
pX = x + 302,
|
||||
pY = y + 145
|
||||
) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
MinecraftClient.getInstance().setScreen(null)
|
||||
}
|
||||
ExitButton(
|
||||
pX = x + 302,
|
||||
pY = y + 145
|
||||
) {
|
||||
playSound(CobblemonSounds.GUI_CLICK)
|
||||
MinecraftClient.getInstance().setScreen(null)
|
||||
}
|
||||
)
|
||||
|
||||
// Add Nickname Entry
|
||||
nicknameEntryWidget = NicknameEntryWidget(
|
||||
selectedPokemon,
|
||||
x = x + 12,
|
||||
y = (y + 14.5).toInt(),
|
||||
width = 50,
|
||||
height = 10,
|
||||
isParty = true,
|
||||
lang("ui.nickname")
|
||||
selectedPokemon,
|
||||
x = x + 12,
|
||||
y = (y + 14.5).toInt(),
|
||||
width = 50,
|
||||
height = 10,
|
||||
isParty = true,
|
||||
lang("ui.nickname")
|
||||
)
|
||||
focused = nicknameEntryWidget
|
||||
nicknameEntryWidget.isFocused = false
|
||||
@@ -233,14 +233,14 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
// Add Model Preview
|
||||
modelWidget = ModelWidget(
|
||||
pX = x + 6,
|
||||
pY = y + 32,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = selectedPokemon.asRenderablePokemon(),
|
||||
baseScale = 2F,
|
||||
rotationY = 325F,
|
||||
offsetY = -10.0
|
||||
pX = x + 6,
|
||||
pY = y + 32,
|
||||
pWidth = PORTRAIT_SIZE,
|
||||
pHeight = PORTRAIT_SIZE,
|
||||
pokemon = selectedPokemon.asRenderablePokemon(),
|
||||
baseScale = 2F,
|
||||
rotationY = 325F,
|
||||
offsetY = -10.0
|
||||
)
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
val targetPosition = PartyPosition(targetIndex)
|
||||
|
||||
val packet = targetPokemon?.let { SwapPartyPokemonPacket(it.uuid, targetPosition, sourcePokemon.uuid, sourcePosition) }
|
||||
?: MovePartyPokemonPacket(sourcePokemon.uuid, sourcePosition, targetPosition)
|
||||
?: MovePartyPokemonPacket(sourcePokemon.uuid, sourcePosition, targetPosition)
|
||||
packet.sendToServer()
|
||||
|
||||
// Update change in UI
|
||||
@@ -289,11 +289,11 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
*/
|
||||
private fun listenToMoveSet() {
|
||||
moveSetSubscription = selectedPokemon.moveSet.observable
|
||||
.pipe(emitWhile { isOpen() })
|
||||
.subscribe {
|
||||
if (mainScreen is MovesWidget)
|
||||
displayMainScreen(MOVES)
|
||||
}
|
||||
.pipe(emitWhile { isOpen() })
|
||||
.subscribe {
|
||||
if (mainScreen is MovesWidget)
|
||||
displayMainScreen(MOVES)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,7 +312,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
if (::mainScreen.isInitialized) remove(mainScreen)
|
||||
if (sideScreenIndex == MOVE_SWAP) displaySideScreen(PARTY)
|
||||
|
||||
summaryTabs.forEachIndexed {index, item ->
|
||||
summaryTabs.forEachIndexed { index, item ->
|
||||
if (index == screen) item.toggleTab() else item.toggleTab(false)
|
||||
}
|
||||
|
||||
@@ -322,24 +322,26 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
when (screen) {
|
||||
INFO -> {
|
||||
mainScreen = InfoWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon
|
||||
)
|
||||
}
|
||||
|
||||
MOVES -> {
|
||||
mainScreen = MovesWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
summary = this
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
summary = this
|
||||
)
|
||||
}
|
||||
|
||||
STATS -> {
|
||||
mainScreen = StatWidget(
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon,
|
||||
tabIndex = subIndex
|
||||
pX = x + 77,
|
||||
pY = y + 12,
|
||||
pokemon = this.selectedPokemon,
|
||||
tabIndex = subIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -359,38 +361,41 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
when (screen) {
|
||||
PARTY -> {
|
||||
sideScreen = PartyWidget(
|
||||
pX = x + 216,
|
||||
pY = y + 24,
|
||||
isParty = selectedPokemon in CobblemonClient.storage.myParty,
|
||||
summary = this,
|
||||
partyList = this.party
|
||||
pX = x + 216,
|
||||
pY = y + 24,
|
||||
isParty = selectedPokemon in CobblemonClient.storage.myParty,
|
||||
summary = this,
|
||||
partyList = this.party
|
||||
)
|
||||
}
|
||||
|
||||
MOVE_SWAP -> {
|
||||
val movesWidget = mainScreen
|
||||
if (movesWidget is MovesWidget && move != null) {
|
||||
sideScreen = MoveSwapScreen(
|
||||
x + 216,
|
||||
y + 22,
|
||||
movesWidget = movesWidget,
|
||||
replacedMove = move
|
||||
x + 216,
|
||||
y + 22,
|
||||
movesWidget = movesWidget,
|
||||
replacedMove = move
|
||||
).also { switchPane ->
|
||||
val pokemon = selectedPokemon
|
||||
pokemon.allAccessibleMoves
|
||||
.filter { template -> pokemon.moveSet.none { it.template == template } }
|
||||
.map { template ->
|
||||
val benched = pokemon.benchedMoves.find { it.moveTemplate == template }
|
||||
MoveSwapScreen.MoveSlot(switchPane, template, benched?.ppRaisedStages ?: 0)
|
||||
}
|
||||
.forEach { switchPane.addEntry(it) }
|
||||
.filter { template -> pokemon.moveSet.none { it.template == template } }
|
||||
.map { template ->
|
||||
val benched = pokemon.benchedMoves.find { it.moveTemplate == template }
|
||||
MoveSwapScreen.MoveSlot(switchPane, template, benched?.ppRaisedStages
|
||||
?: 0)
|
||||
}
|
||||
.forEach { switchPane.addEntry(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EVOLVE -> {
|
||||
sideScreen = EvolutionSelectScreen(
|
||||
x + 216,
|
||||
y + 22,
|
||||
pokemon = selectedPokemon
|
||||
x + 216,
|
||||
y + 22,
|
||||
pokemon = selectedPokemon
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -409,24 +414,24 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
// Render Portrait Background
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = portraitBackgroundResource,
|
||||
x = x + 6,
|
||||
y = y + 32,
|
||||
width = PORTRAIT_SIZE,
|
||||
height = PORTRAIT_SIZE
|
||||
matrixStack = matrices,
|
||||
texture = portraitBackgroundResource,
|
||||
x = x + 6,
|
||||
y = y + 32,
|
||||
width = PORTRAIT_SIZE,
|
||||
height = PORTRAIT_SIZE
|
||||
)
|
||||
|
||||
modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
|
||||
// Render Base Resource
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = baseResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = BASE_WIDTH,
|
||||
height = BASE_HEIGHT
|
||||
matrixStack = matrices,
|
||||
texture = baseResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = BASE_WIDTH,
|
||||
height = BASE_HEIGHT
|
||||
)
|
||||
|
||||
// Status
|
||||
@@ -434,100 +439,100 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
if (selectedPokemon.isFainted() || status != null) {
|
||||
val statusName = if (selectedPokemon.isFainted()) "fnt" else status?.showdownName
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/battle/battle_status_$statusName.png"),
|
||||
x = x + 34,
|
||||
y = y + 4,
|
||||
height = 7,
|
||||
width = 39,
|
||||
uOffset = 35,
|
||||
textureWidth = 74
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/battle/battle_status_$statusName.png"),
|
||||
x = x + 34,
|
||||
y = y + 4,
|
||||
height = 7,
|
||||
width = 39,
|
||||
uOffset = 35,
|
||||
textureWidth = 74
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/summary/status_trim.png"),
|
||||
x = x + 34,
|
||||
y = y + 5,
|
||||
height = 6,
|
||||
width = 3
|
||||
matrixStack = matrices,
|
||||
texture = cobblemonResource("textures/gui/summary/status_trim.png"),
|
||||
x = x + 34,
|
||||
y = y + 5,
|
||||
height = 6,
|
||||
width = 3
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.status.$statusName").bold(),
|
||||
x = x + 39,
|
||||
y = y + 3
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.status.$statusName").bold(),
|
||||
x = x + 39,
|
||||
y = y + 3
|
||||
)
|
||||
}
|
||||
|
||||
// Poké Ball
|
||||
val ballResource = cobblemonResource("textures/item/poke_balls/" + selectedPokemon.caughtBall.name.path + ".png")
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = ballResource,
|
||||
x = (x + 3.5) / SCALE,
|
||||
y = (y + 15) / SCALE,
|
||||
width = 16,
|
||||
height = 16,
|
||||
scale = SCALE
|
||||
matrixStack = matrices,
|
||||
texture = ballResource,
|
||||
x = (x + 3.5) / SCALE,
|
||||
y = (y + 15) / SCALE,
|
||||
width = 16,
|
||||
height = 16,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
if (selectedPokemon.gender != Gender.GENDERLESS) {
|
||||
val isMale = selectedPokemon.gender == Gender.MALE
|
||||
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = textSymbol,
|
||||
x = x + 69, // 64 when tag icon is implemented
|
||||
y = y + 14.5,
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = textSymbol,
|
||||
x = x + 69, // 64 when tag icon is implemented
|
||||
y = y + 14.5,
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.lv").bold(),
|
||||
x = x + 6,
|
||||
y = y + 4.5,
|
||||
shadow = true
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.lv").bold(),
|
||||
x = x + 6,
|
||||
y = y + 4.5,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = selectedPokemon.level.toString().text().bold(),
|
||||
x = x + 19,
|
||||
y = y + 4.5,
|
||||
shadow = true
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = selectedPokemon.level.toString().text().bold(),
|
||||
x = x + 19,
|
||||
y = y + 4.5,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
// Shiny Icon
|
||||
if (selectedPokemon.shiny) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = iconShinyResource,
|
||||
x = (x + 63.5) / SCALE,
|
||||
y = (y + 33.5) / SCALE,
|
||||
width = 14,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
matrixStack = matrices,
|
||||
texture = iconShinyResource,
|
||||
x = (x + 63.5) / SCALE,
|
||||
y = (y + 33.5) / SCALE,
|
||||
width = 14,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
// Type Icon(s)
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (selectedPokemon.secondaryType != null) typeSpacerDoubleResource else typeSpacerResource,
|
||||
x = (x + 5.5) / SCALE,
|
||||
y = (y + 126) / SCALE,
|
||||
width = 134,
|
||||
height = 24,
|
||||
scale = SCALE
|
||||
matrixStack = matrices,
|
||||
texture = if (selectedPokemon.secondaryType != null) typeSpacerDoubleResource else typeSpacerResource,
|
||||
x = (x + 5.5) / SCALE,
|
||||
y = (y + 126) / SCALE,
|
||||
width = 134,
|
||||
height = 24,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Held Item
|
||||
@@ -540,29 +545,29 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("held_item"),
|
||||
x = x + 27,
|
||||
y = y + 114.5,
|
||||
scale = SCALE
|
||||
context = context,
|
||||
text = lang("held_item"),
|
||||
x = x + 27,
|
||||
y = y + 114.5,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
TypeIcon(
|
||||
x = x + 39,
|
||||
y = y + 123,
|
||||
type = selectedPokemon.primaryType,
|
||||
secondaryType = selectedPokemon.secondaryType,
|
||||
centeredX = true
|
||||
x = x + 39,
|
||||
y = y + 123,
|
||||
type = selectedPokemon.primaryType,
|
||||
secondaryType = selectedPokemon.secondaryType,
|
||||
centeredX = true
|
||||
).render(context)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = sideSpacerResource,
|
||||
x = (x + 217) / SCALE,
|
||||
y = (y + 141) / SCALE,
|
||||
width = 144,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
matrixStack = matrices,
|
||||
texture = sideSpacerResource,
|
||||
x = (x + 217) / SCALE,
|
||||
y = (y + 141) / SCALE,
|
||||
width = 144,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Render all added Widgets
|
||||
@@ -609,4 +614,4 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-9
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget
|
||||
import net.minecraft.client.util.InputUtil
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class NicknameEntryWidget(
|
||||
@@ -64,16 +65,20 @@ class NicknameEntryWidget(
|
||||
text = text.trim().ifBlank { pokemonName }
|
||||
if (!focused) {
|
||||
val newNickname = if (text == pokemonName) null else text
|
||||
if (oldText != lastSavedName && !(newNickname == null && pokemon.nickname == null)) {
|
||||
lastSavedName = text
|
||||
CobblemonNetwork.sendToServer(
|
||||
updateNickname(oldText, newNickname)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateNickname(oldText: String, newNickname: String?) {
|
||||
if (oldText != lastSavedName && !(newNickname == null && pokemon.nickname == null)) {
|
||||
lastSavedName = text
|
||||
CobblemonNetwork.sendToServer(
|
||||
SetNicknamePacket(
|
||||
pokemonUUID = pokemon.uuid,
|
||||
nickname = newNickname,
|
||||
isParty = isParty
|
||||
pokemonUUID = pokemon.uuid,
|
||||
nickname = newNickname,
|
||||
isParty = isParty
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,4 +94,14 @@ class NicknameEntryWidget(
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
if (keyCode == InputUtil.GLFW_KEY_ESCAPE) {
|
||||
val oldText = text.trim()
|
||||
val pokemonName = pokemon.species.translatedName.string
|
||||
val newNickname = if (text == pokemonName) null else text
|
||||
updateNickname(oldText, newNickname)
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ object OpenPartyMoveCallbackHandler : ClientNetworkPacketHandler<OpenPartyMoveCa
|
||||
}
|
||||
|
||||
partySelectConfiguration = PartySelectConfiguration(
|
||||
title = "".text(),
|
||||
title = packet.partyTitle,
|
||||
pokemon = pokemonToMoves.keys.toList(),
|
||||
onCancel = cancel,
|
||||
onBack = cancel,
|
||||
|
||||
@@ -15,9 +15,7 @@ import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.particle.SnowstormParticleEffect
|
||||
import com.cobblemon.mod.common.util.math.geometry.getOrigin
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformDirection
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformPosition
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.particle.NoRenderParticle
|
||||
@@ -38,6 +36,7 @@ class ParticleStorm(
|
||||
val sourceVelocity: () -> Vec3d = { Vec3d.ZERO },
|
||||
val sourceAlive: () -> Boolean = { true },
|
||||
val sourceVisible: () -> Boolean = { true },
|
||||
val onDespawn: () -> Unit = {},
|
||||
val runtime: MoLangRuntime = MoLangRuntime()
|
||||
): NoRenderParticle(world, matrixWrapper.getOrigin().x, matrixWrapper.getOrigin().y, matrixWrapper.getOrigin().z) {
|
||||
fun spawn() {
|
||||
@@ -55,6 +54,9 @@ class ParticleStorm(
|
||||
val particles = mutableListOf<SnowstormParticle>()
|
||||
var started = false
|
||||
var stopped = false
|
||||
var despawned = false
|
||||
// The idea is that some instantaneous particle effects could teeeechnically be over before they start.
|
||||
var hasPlayedOnce = false
|
||||
|
||||
var entity: Entity? = null
|
||||
|
||||
@@ -72,11 +74,24 @@ class ParticleStorm(
|
||||
return if (stopped) 0 else Int.MAX_VALUE
|
||||
}
|
||||
|
||||
override fun markDead() {
|
||||
super.markDead()
|
||||
if (!despawned) {
|
||||
despawned = true
|
||||
onDespawn()
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
setMaxAge(getMaxAge())
|
||||
super.tick()
|
||||
|
||||
if (!sourceAlive()) {
|
||||
if (!hasPlayedOnce) {
|
||||
age = 0
|
||||
hasPlayedOnce = true
|
||||
}
|
||||
|
||||
if (!sourceAlive() && !stopped) {
|
||||
stopped = true
|
||||
markDead()
|
||||
}
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ class VaryingRenderableResolver<E : Entity, M : PoseableEntityModel<E>>(
|
||||
posers.clear()
|
||||
getAllModels().forEach { identifier ->
|
||||
try {
|
||||
models[identifier] = repository.texturedModels[identifier]!!.invoke()
|
||||
models[identifier] = repository.texturedModels[identifier]!!.invoke(repository.isForLivingEntityRenderer)
|
||||
} catch (e: Exception) {
|
||||
throw IllegalStateException("Unable to load model $identifier for $name", e)
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.setPosition
|
||||
import com.cobblemon.mod.common.util.math.geometry.Axis
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
|
||||
class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<BerryBlockEntity> {
|
||||
|
||||
override fun isInRenderDistance(blockEntity: BerryBlockEntity, pos: Vec3d): Boolean {
|
||||
return super.isInRenderDistance(blockEntity, pos)
|
||||
&& MinecraftClient.getInstance().worldRenderer.frustum.isVisible(Box.of(pos, 2.0, 1.5, 2.0))
|
||||
}
|
||||
|
||||
override fun render(entity: BerryBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int) {
|
||||
if (!isInRenderDistance(entity, entity.pos.toVec3d())) return
|
||||
val blockState = entity.cachedState
|
||||
val age = blockState.get(BerryBlock.AGE)
|
||||
if (age <= BerryBlock.MATURE_AGE) {
|
||||
return
|
||||
}
|
||||
val isFlower = age == BerryBlock.FLOWER_AGE
|
||||
for ((berry, growthPoint) in entity.berryAndGrowthPoint()) {
|
||||
val model = (if (isFlower) BerryModelRepository.modelOf(berry.flowerModelIdentifier) else BerryModelRepository.modelOf(berry.fruitModelIdentifier)) ?: continue
|
||||
val texture = if (isFlower) berry.flowerTexture else berry.fruitTexture
|
||||
val layer = RenderLayer.getEntityCutoutNoCull(texture)
|
||||
model.setAngles(
|
||||
Math.toRadians(180.0 - growthPoint.rotation.x).toFloat(),
|
||||
Math.toRadians(180.0 + growthPoint.rotation.y).toFloat(),
|
||||
Math.toRadians(growthPoint.rotation.z).toFloat()
|
||||
)
|
||||
model.setPosition(Axis.X_AXIS.ordinal, growthPoint.position.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, growthPoint.position.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, growthPoint.position.z.toFloat())
|
||||
model.render(matrices, vertexConsumers.getBuffer(layer), light, overlay)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+9
-17
@@ -12,13 +12,11 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.Pokem
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.item.PokemonItem
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.DiffuseLighting
|
||||
import net.minecraft.client.render.LightmapTextureManager
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.model.json.ModelTransformation
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.item.ItemStack
|
||||
@@ -36,37 +34,31 @@ class PokemonItemRenderer : CobblemonBuiltinItemRenderer {
|
||||
|
||||
val transformations = positions[mode]!!
|
||||
|
||||
RenderSystem.applyModelViewMatrix()
|
||||
DiffuseLighting.enableGuiDepthLighting()
|
||||
matrices.scale(transformations.scale.x, transformations.scale.y, transformations.scale.z)
|
||||
matrices.translate(transformations.translation.x, transformations.translation.y, transformations.translation.z)
|
||||
model.setupAnimStateless(PoseType.PROFILE)
|
||||
matrices.translate(model.profileTranslation.x, model.profileTranslation.y, model.profileTranslation.z - 4.0)
|
||||
matrices.scale(model.profileScale, model.profileScale, 0.1F)
|
||||
matrices.scale(model.profileScale, model.profileScale, 0.15F)
|
||||
|
||||
val rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(transformations.rotation.x, transformations.rotation.y, transformations.rotation.z))
|
||||
matrices.multiply(rotation)
|
||||
rotation.conjugate()
|
||||
val oldRotation = MinecraftClient.getInstance().entityRenderDispatcher.rotation
|
||||
MinecraftClient.getInstance().entityRenderDispatcher.rotation = rotation
|
||||
|
||||
val light1 = Vector3f(-1F, 1F, 1.0F)
|
||||
val light2 = Vector3f(1.3F, -1F, 1.0F)
|
||||
val previous = RenderSystem.shaderLightDirections.let { it[0] to it[1] }
|
||||
RenderSystem.setShaderLights(light1, light2)
|
||||
// val packedLight = LightmapTextureManager.pack(12, 12)
|
||||
val vertexConsumer: VertexConsumer = vertexConsumers.getBuffer(renderLayer)
|
||||
matrices.push()
|
||||
val packedLight = if (mode == ModelTransformationMode.GUI) {
|
||||
LightmapTextureManager.pack(13, 13)
|
||||
} else {
|
||||
light
|
||||
}
|
||||
|
||||
val packedLight = LightmapTextureManager.pack(11, 7)
|
||||
model.withLayerContext(vertexConsumers, null, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
|
||||
model.render(matrices, vertexConsumer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
|
||||
}
|
||||
|
||||
matrices.pop()
|
||||
matrices.pop()
|
||||
|
||||
RenderSystem.setShaderLights(previous.first, previous.second)
|
||||
MinecraftClient.getInstance().entityRenderDispatcher.rotation = oldRotation
|
||||
DiffuseLighting.disableGuiDepthLighting()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+44
-7
@@ -10,7 +10,11 @@ package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.ModelLayer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseTransitionAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.RotationFunctionStatelessAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatelessAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.TranslationFunctionStatelessAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockStatefulAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockStatelessAnimation
|
||||
@@ -26,10 +30,17 @@ import com.cobblemon.mod.common.entity.Poseable
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.client.render.*
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderPhase
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.client.render.entity.model.EntityModel
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
|
||||
@@ -46,6 +57,9 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
) : EntityModel<T>(renderTypeFunc), ModelFrame {
|
||||
var currentEntity: T? = null
|
||||
|
||||
/** Whether the renderer that will process this is going to do the weird -1.5 Y offset bullshit that the living entity renderer does. */
|
||||
abstract val isForLivingEntityRenderer: Boolean
|
||||
|
||||
val poses = mutableMapOf<String, Pose<T, out ModelFrame>>()
|
||||
lateinit var locatorAccess: LocatorAccess
|
||||
|
||||
@@ -102,6 +116,19 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
currentState = null
|
||||
}
|
||||
|
||||
open fun getOverlayTexture(entity: T?): Int? {
|
||||
return if (entity is LivingEntity) {
|
||||
OverlayTexture.packUv(
|
||||
OverlayTexture.getU(0F),
|
||||
OverlayTexture.getV(entity.hurtTime > 0 || entity.deathTime > 0)
|
||||
)
|
||||
} else if (entity != null) {
|
||||
OverlayTexture.DEFAULT_UV
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a pose for this model.
|
||||
*
|
||||
@@ -249,7 +276,7 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
stack,
|
||||
buffer,
|
||||
packedLight,
|
||||
OverlayTexture.DEFAULT_UV,
|
||||
getOverlayTexture(currentEntity) ?: packedOverlay,
|
||||
red * r,
|
||||
green * g,
|
||||
blue * b,
|
||||
@@ -268,7 +295,7 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
stack,
|
||||
consumer,
|
||||
packedLight,
|
||||
OverlayTexture.DEFAULT_UV,
|
||||
getOverlayTexture(currentEntity) ?: packedOverlay,
|
||||
layer.tint.x,
|
||||
layer.tint.y,
|
||||
layer.tint.z,
|
||||
@@ -295,7 +322,14 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
|
||||
fun makeLayer(texture: Identifier, emissive: Boolean, translucent: Boolean): RenderLayer {
|
||||
val multiPhaseParameters: RenderLayer.MultiPhaseParameters = RenderLayer.MultiPhaseParameters.builder()
|
||||
.program(if (emissive) RenderPhase.ENTITY_TRANSLUCENT_EMISSIVE_PROGRAM else RenderPhase.ENTITY_TRANSLUCENT_PROGRAM)
|
||||
.program(
|
||||
when {
|
||||
emissive && translucent -> RenderPhase.ENTITY_TRANSLUCENT_EMISSIVE_PROGRAM
|
||||
!emissive && translucent -> RenderPhase.ENTITY_TRANSLUCENT_PROGRAM
|
||||
!emissive && !translucent -> RenderPhase.ENTITY_CUTOUT_PROGRAM
|
||||
else -> RenderPhase.ENTITY_TRANSLUCENT_EMISSIVE_PROGRAM // This one should be changed to maybe a custom shader? Translucent stuffs with things
|
||||
}
|
||||
)
|
||||
.texture(RenderPhase.Texture(texture, false, false))
|
||||
.transparency(if (translucent) RenderPhase.TRANSLUCENT_TRANSPARENCY else RenderPhase.NO_TRANSPARENCY)
|
||||
.cull(RenderPhase.ENABLE_CULLING)
|
||||
@@ -492,8 +526,11 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
matrixStack.scale(1F, -1F, -1F)
|
||||
matrixStack.scale(0.7F, 0.7F, 0.7F)
|
||||
}
|
||||
// Standard living entity offset, only God knows why Mojang did this.
|
||||
matrixStack.translate(0.0, -1.5, 0.0)
|
||||
|
||||
if (isForLivingEntityRenderer) {
|
||||
// Standard living entity offset, only God knows why Mojang did this.
|
||||
matrixStack.translate(0.0, -1.5, 0.0)
|
||||
}
|
||||
|
||||
locatorAccess.update(matrixStack, state.locatorStates)
|
||||
}
|
||||
|
||||
+10
-1
@@ -46,6 +46,7 @@ abstract class PoseableEntityState<T : Entity> {
|
||||
protected var age = 0
|
||||
protected var currentPartialTicks = 0F
|
||||
|
||||
abstract fun getEntity(): T?
|
||||
fun getPartialTicks() = currentPartialTicks
|
||||
open fun updateAge(age: Int) {
|
||||
this.age = age
|
||||
@@ -93,8 +94,16 @@ abstract class PoseableEntityState<T : Entity> {
|
||||
val model = currentModel
|
||||
if (model != null) {
|
||||
val poseImpl = model.getPose(pose) ?: return
|
||||
poseParticles.removeIf { it !in poseImpl.idleAnimations.filterIsInstance<BedrockStatelessAnimation<*>>().flatMap { it.particleKeyFrames } }
|
||||
poseParticles.removeIf { particle -> poseImpl.idleAnimations.filterIsInstance<BedrockStatelessAnimation<*>>().flatMap { it.particleKeyFrames }.none(particle::isSameAs) }
|
||||
poseImpl.onTransitionedInto(this)
|
||||
val entity = getEntity()
|
||||
if (entity != null) {
|
||||
poseImpl.idleAnimations
|
||||
.filterIsInstance<BedrockStatelessAnimation<*>>()
|
||||
.flatMap { it.particleKeyFrames }
|
||||
.filter { particle -> particle.seconds == 0F && poseParticles.none(particle::isSameAs) }
|
||||
.forEach { it.run(entity, this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ class TexturedModel {
|
||||
@SerializedName("minecraft:geometry")
|
||||
val geometry: List<ModelGeometry>? = null
|
||||
|
||||
fun create() : TexturedModelData {
|
||||
fun create(isForLivingEntityRenderer: Boolean) : TexturedModelData {
|
||||
val modelData = ModelData()
|
||||
val parts = HashMap<String, ModelPartData>()
|
||||
val bones = HashMap<String, ModelBone>()
|
||||
@@ -58,7 +58,7 @@ class TexturedModel {
|
||||
when {
|
||||
bone.parent == null -> {
|
||||
// The root part always has a 24 Y offset. One of life's great mysteries.
|
||||
modelTransform = ModelTransform.pivot(0F, 24F, 0F)
|
||||
modelTransform = ModelTransform.pivot(0F, if (isForLivingEntityRenderer) 24F else 0F, 0F)
|
||||
}
|
||||
boneRotation != null -> {
|
||||
modelTransform = ModelTransform.of(
|
||||
|
||||
+42
-2
@@ -14,12 +14,15 @@ import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.MoScope
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleEffect
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import java.util.SortedMap
|
||||
@@ -49,6 +52,20 @@ class BedrockParticleKeyframe(
|
||||
val locator: String,
|
||||
val scripts: List<Expression>
|
||||
) : BedrockEffectKeyframe(seconds) {
|
||||
fun isSameAs(other: BedrockParticleKeyframe): Boolean {
|
||||
return if (seconds != other.seconds) {
|
||||
false
|
||||
} else if (effect != other.effect) {
|
||||
false
|
||||
} else if (locator != other.locator) {
|
||||
false
|
||||
} else if (scripts.map { it.getString() }.toSet() != other.scripts.map { it.getString() }.toSet()) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : Entity> run(entity: T, state: PoseableEntityState<T>) {
|
||||
val world = entity.world as ClientWorld
|
||||
val matrixWrapper = state.locatorStates[locator] ?: state.locatorStates["root"]!!
|
||||
@@ -58,16 +75,18 @@ class BedrockParticleKeyframe(
|
||||
return
|
||||
}
|
||||
|
||||
state.poseParticles.add(this)
|
||||
|
||||
val storm = ParticleStorm(
|
||||
effect = effect,
|
||||
matrixWrapper = matrixWrapper,
|
||||
world = world,
|
||||
sourceVelocity = { entity.velocity },
|
||||
sourceAlive = { !entity.isRemoved && this in state.poseParticles },
|
||||
sourceVisible = { !entity.isInvisible }
|
||||
sourceVisible = { !entity.isInvisible },
|
||||
onDespawn = { state.poseParticles.remove(this) }
|
||||
)
|
||||
|
||||
state.poseParticles.add(this)
|
||||
storm.runtime.execute(this.scripts)
|
||||
storm.spawn()
|
||||
}
|
||||
@@ -149,6 +168,27 @@ data class BedrockAnimation(
|
||||
MinecraftClient.getInstance().player?.sendMessage(params.getString(0).text())
|
||||
Unit
|
||||
}
|
||||
registerInstruction<Entity>("sound") { entity, _, params ->
|
||||
// Means we don't need to setup a sound registry entry for every single thing
|
||||
val soundEvent = SoundEvent.of(params.getString(0).asIdentifierDefaultingNamespace())
|
||||
if (soundEvent != null) {
|
||||
val volume = if (params.contains(1)) params.getDouble(1).toFloat() else 1F
|
||||
val pitch = if (params.contains(2)) params.getDouble(2).toFloat() else 1F
|
||||
MinecraftClient.getInstance().soundManager.play(
|
||||
PositionedSoundInstance(soundEvent, SoundCategory.NEUTRAL, volume, pitch, entity.world.random, entity.x, entity.y, entity.z)
|
||||
)
|
||||
}
|
||||
Unit
|
||||
}
|
||||
registerInstruction<Entity>("random") { entity, _, params ->
|
||||
val options = mutableListOf<MoValue>()
|
||||
var index = 0
|
||||
while (params.contains(index)) {
|
||||
options.add(params.get(index))
|
||||
index++
|
||||
}
|
||||
return@registerInstruction options.random() // Can throw an exception if they specified no args. They'd be idiots though.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityMo
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatelessAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1.PikachuModel
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
/**
|
||||
@@ -34,6 +33,12 @@ class BedrockStatelessAnimation<T: Entity>(frame: ModelFrame, val animation: Bed
|
||||
}
|
||||
|
||||
override fun applyEffects(entity: T, state: PoseableEntityState<T>, previousSeconds: Float, newSeconds: Float) {
|
||||
animation.applyEffects(entity, state, (previousSeconds % animation.animationLength).toFloat(), (newSeconds % animation.animationLength).toFloat())
|
||||
val effectiveAnimationLength = animation.animationLength.takeUnless { it <= 0 }?.toFloat() ?: animation.effects.maxOfOrNull { it.seconds }?.takeIf { it != 0F }
|
||||
val (loopedPreviousSeconds, loopedNewSeconds) = if (effectiveAnimationLength != null) {
|
||||
(previousSeconds % effectiveAnimationLength) to (newSeconds % effectiveAnimationLength)
|
||||
} else {
|
||||
previousSeconds to newSeconds
|
||||
}
|
||||
animation.applyEffects(entity, state, loopedPreviousSeconds, loopedNewSeconds)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ class PokeBallModel(root: ModelPart) : PoseableEntityModel<EmptyPokeBallEntity>(
|
||||
override val rootPart = root.registerChildWithAllChildren("poke_ball")
|
||||
override val base = getPart("base")
|
||||
override val lid = getPart("lid")
|
||||
|
||||
override val isForLivingEntityRenderer = false
|
||||
|
||||
lateinit var shut: PokeBallPose
|
||||
lateinit var open: PokeBallPose
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
* @since May 1st, 2022
|
||||
*/
|
||||
class PokemonFloatingState : PoseableEntityState<PokemonEntity>() {
|
||||
override fun getEntity() = null
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks += partialTicks
|
||||
}
|
||||
|
||||
+13
-1
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.TransformedModelPart
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
@@ -27,7 +28,7 @@ import net.minecraft.util.math.Vec3d
|
||||
* @since December 4th, 2021
|
||||
*/
|
||||
abstract class PokemonPoseableModel : PoseableEntityModel<PokemonEntity>() {
|
||||
|
||||
override val isForLivingEntityRenderer = true
|
||||
override fun getState(entity: PokemonEntity) = entity.delegate as PokemonClientDelegate
|
||||
|
||||
/** Registers the same configuration for both left and right shoulder poses. */
|
||||
@@ -71,6 +72,17 @@ abstract class PokemonPoseableModel : PoseableEntityModel<PokemonEntity>() {
|
||||
state: PoseableEntityState<PokemonEntity>
|
||||
): StatefulAnimation<PokemonEntity, ModelFrame>? = null
|
||||
|
||||
override fun getOverlayTexture(entity: PokemonEntity?): Int {
|
||||
return if (entity != null) {
|
||||
OverlayTexture.packUv(
|
||||
OverlayTexture.getU(0F),
|
||||
OverlayTexture.getV(entity.hurtTime > 0)
|
||||
)
|
||||
} else {
|
||||
OverlayTexture.DEFAULT_UV
|
||||
}
|
||||
}
|
||||
|
||||
open val cryAnimation: CryProvider = CryProvider { _, _ -> null }
|
||||
//
|
||||
// open fun getCryAnimation(
|
||||
|
||||
+6
-2
@@ -35,6 +35,7 @@ class PikachuModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
lateinit var standing: PokemonPose
|
||||
lateinit var walk: PokemonPose
|
||||
lateinit var battleidle: PokemonPose
|
||||
lateinit var sleep: PokemonPose
|
||||
|
||||
val shoulderOffset = 1.5
|
||||
override fun registerPoses() {
|
||||
@@ -51,13 +52,17 @@ class PikachuModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
)
|
||||
)
|
||||
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
idleAnimations = arrayOf(bedrock("pikachu", "ground_sleep"))
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = MOVING_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("pikachu", "ground_walk")
|
||||
)
|
||||
)
|
||||
@@ -93,7 +98,6 @@ class PikachuModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("pikachu", "battle_idle")
|
||||
)
|
||||
|
||||
|
||||
-2
@@ -59,7 +59,6 @@ class RaichuModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
quirks = arrayOf(blink),
|
||||
condition = { !it.isBattling },
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("raichu", "ground_walk")
|
||||
)
|
||||
)
|
||||
@@ -71,7 +70,6 @@ class RaichuModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("raichu", "battle_idle")
|
||||
)
|
||||
)
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.repository
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* The data registry responsible for berry fruit and flower models.
|
||||
* This is only present on the client.
|
||||
*/
|
||||
object BerryModelRepository : JsonDataRegistry<TexturedModel> {
|
||||
|
||||
override val id = cobblemonResource("berry_models")
|
||||
override val type = ResourceType.CLIENT_RESOURCES
|
||||
override val observable = SimpleObservable<BerryModelRepository>()
|
||||
override val gson: Gson = TexturedModel.GSON
|
||||
override val typeToken: TypeToken<TexturedModel> = TypeToken.get(TexturedModel::class.java)
|
||||
override val resourcePath = "bedrock/berries"
|
||||
private val models = hashMapOf<Identifier, ModelPart>()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
|
||||
override fun reload(data: Map<Identifier, TexturedModel>) {
|
||||
this.models.clear()
|
||||
data.forEach { (identifier, model) ->
|
||||
this.models[identifier] = model.create(isForLivingEntityRenderer = false).createModel()
|
||||
}
|
||||
Cobblemon.LOGGER.info("Loaded {} berry models", this.models.size)
|
||||
}
|
||||
|
||||
fun modelOf(identifier: Identifier) = this.models[identifier]
|
||||
|
||||
}
|
||||
+1
@@ -20,6 +20,7 @@ object PokeBallModelRepository : VaryingModelRepository<EmptyPokeBallEntity, Pok
|
||||
override val poserDirectories: List<String> = listOf("bedrock/$type/posers")
|
||||
override val modelDirectories: List<String> = listOf("bedrock/$type/models")
|
||||
override val animationDirectories: List<String> = listOf("bedrock/$type/animations")
|
||||
override val isForLivingEntityRenderer = false
|
||||
|
||||
override val fallback = PokeBalls.POKE_BALL.name
|
||||
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ object PokemonModelRepository : VaryingModelRepository<PokemonEntity, PokemonPos
|
||||
override val poserDirectories = listOf("bedrock/$type/posers", "bedrock/posers")
|
||||
override val modelDirectories = listOf("bedrock/$type/models", "bedrock/models")
|
||||
override val animationDirectories: List<String> = listOf("bedrock/animations", "bedrock/$type/animations")
|
||||
override val isForLivingEntityRenderer = true
|
||||
|
||||
override val fallback = cobblemonResource("substitute")
|
||||
|
||||
|
||||
+14
-13
@@ -29,13 +29,14 @@ import net.minecraft.util.Pair
|
||||
import java.io.File
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.function.BiFunction
|
||||
import java.util.function.Function
|
||||
import java.util.function.Supplier
|
||||
|
||||
abstract class VaryingModelRepository<E : Entity, M : PoseableEntityModel<E>> {
|
||||
val posers = mutableMapOf<Identifier, (Bone) -> M>()
|
||||
val variations = mutableMapOf<Identifier, VaryingRenderableResolver<E, M>>()
|
||||
|
||||
val texturedModels = mutableMapOf<Identifier, () -> Bone>()
|
||||
val texturedModels = mutableMapOf<Identifier, (isForLivingEntityRenderer: Boolean) -> Bone>()
|
||||
|
||||
abstract val title: String
|
||||
abstract val type: String
|
||||
@@ -44,6 +45,8 @@ abstract class VaryingModelRepository<E : Entity, M : PoseableEntityModel<E>> {
|
||||
abstract val modelDirectories: List<String>
|
||||
abstract val animationDirectories: List<String>
|
||||
abstract val fallback: Identifier
|
||||
/** When using the living entity renderer in Java Edition, a root joint 24F (1.5) Y offset is necessary. I've no fucking idea why. */
|
||||
abstract val isForLivingEntityRenderer: Boolean
|
||||
|
||||
abstract fun loadJsonPoser(json: String): (Bone) -> M
|
||||
|
||||
@@ -98,13 +101,11 @@ abstract class VaryingModelRepository<E : Entity, M : PoseableEntityModel<E>> {
|
||||
fun registerModels(resourceManager: ResourceManager) {
|
||||
var models = 0
|
||||
for (directory in modelDirectories) {
|
||||
MODEL_FACTORIES.forEach { entry ->
|
||||
resourceManager.findResources(directory) { path ->
|
||||
path.endsWith(entry.key) }
|
||||
.map {
|
||||
entry.value.apply(it.key, it.value) }
|
||||
MODEL_FACTORIES.forEach { (key, func) ->
|
||||
resourceManager.findResources(directory) { path -> path.endsWith(key) }
|
||||
.map { func.apply(it.key, it.value) }
|
||||
.forEach {
|
||||
texturedModels[it.left] = it.right::get
|
||||
texturedModels[it.left] = { isForLivingEntityRenderer -> it.right.apply(isForLivingEntityRenderer) }
|
||||
models++
|
||||
}
|
||||
}
|
||||
@@ -156,7 +157,7 @@ abstract class VaryingModelRepository<E : Entity, M : PoseableEntityModel<E>> {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun registerFactory(id: String, factory: BiFunction<Identifier, Resource, Pair<Identifier, Supplier<Bone>>>) {
|
||||
fun registerFactory(id: String, factory: BiFunction<Identifier, Resource, Pair<Identifier, Function<Boolean, Bone>>>) {
|
||||
MODEL_FACTORIES[id] = factory
|
||||
}
|
||||
|
||||
@@ -164,15 +165,15 @@ abstract class VaryingModelRepository<E : Entity, M : PoseableEntityModel<E>> {
|
||||
Needs to be java function to work with non kotlin sidemods.
|
||||
- Waterpicker
|
||||
*/
|
||||
private var MODEL_FACTORIES = mutableMapOf<String, BiFunction<Identifier, Resource, Pair<Identifier, Supplier<Bone>>>>().also {
|
||||
it[".geo.json"] = BiFunction<Identifier, Resource, Pair<Identifier, Supplier<Bone>>> { identifier: Identifier, resource: Resource ->
|
||||
private var MODEL_FACTORIES = mutableMapOf<String, BiFunction<Identifier, Resource, Pair<Identifier, Function<Boolean, Bone>>>>().also {
|
||||
it[".geo.json"] = BiFunction<Identifier, Resource, Pair<Identifier, Function<Boolean, Bone>>> { identifier: Identifier, resource: Resource ->
|
||||
resource.inputStream.use { stream ->
|
||||
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
|
||||
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
|
||||
val texturedModel = TexturedModel.from(json).create()
|
||||
val supplier = Supplier<Bone>{texturedModel.createModel()}
|
||||
Pair(resolvedIdentifier, supplier)
|
||||
val texturedModel = TexturedModel.from(json)
|
||||
val boneCreator: Function<Boolean, Bone> = Function { texturedModel.create(it).createModel() }
|
||||
Pair(resolvedIdentifier, boneCreator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -30,7 +30,6 @@ class PokeBallRenderer(context: EntityRendererFactory.Context) : EntityRenderer<
|
||||
override fun render(entity: EmptyPokeBallEntity, yaw: Float, partialTicks: Float, poseStack: MatrixStack, buffer: VertexConsumerProvider, packedLight: Int) {
|
||||
val model = PokeBallModelRepository.getPoser(entity.pokeBall.name, entity.aspects.get())
|
||||
poseStack.push()
|
||||
poseStack.translate(0.0, 1.5 * 0.7F, 0.0)
|
||||
poseStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw))
|
||||
poseStack.scale(0.7F, -0.7F, -0.7F)
|
||||
val vertexConsumer = ItemRenderer.getDirectItemGlintConsumer(buffer, model.getLayer(getTexture(entity)), false, false)
|
||||
|
||||
@@ -45,7 +45,7 @@ object PcCommand {
|
||||
context.source.world.playSoundServer(
|
||||
position = context.source.player!!.pos,
|
||||
sound = CobblemonSounds.PC_ON,
|
||||
volume = 1F,
|
||||
volume = 0.5F,
|
||||
pitch = 1F
|
||||
)
|
||||
return Command.SINGLE_SUCCESS
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user