forked from mirrors/cobblemon
Compare commits
158
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
694f74be13 | ||
|
|
79d097b17c | ||
|
|
7ef30653bb | ||
|
|
1ecb13f7fc | ||
|
|
d19878edf6 | ||
|
|
4f98d1e054 | ||
|
|
7b8e07aef3 | ||
|
|
1442bf5c34 | ||
|
|
2ac37f6c4d | ||
|
|
9795dce227 | ||
|
|
d9cf802a9e | ||
|
|
3fbf2595cc | ||
|
|
6665285253 | ||
|
|
61ae29b745 | ||
|
|
a17bf5eeeb | ||
|
|
c71d91a46d | ||
|
|
c86103fd31 | ||
|
|
5190bbe723 | ||
|
|
3663ce2555 | ||
|
|
b5fc52d9b1 | ||
|
|
ca79b70815 | ||
|
|
898c956f17 | ||
|
|
2ce48c9dff | ||
|
|
f64ec12a6a | ||
|
|
474d3f3b55 | ||
|
|
c87e6a391d | ||
|
|
192fd4dba7 | ||
|
|
c26786c644 | ||
|
|
ebb0277d80 | ||
|
|
2a73efa479 | ||
|
|
6e68d22d4a | ||
|
|
dee08e6572 | ||
|
|
2383686e42 | ||
|
|
b8de9c415e | ||
|
|
f0269a3638 | ||
|
|
dbbbd309dc | ||
|
|
b7a905ad79 | ||
|
|
60c8049459 | ||
|
|
8c395b6fee | ||
|
|
e9935a41e3 | ||
|
|
f0ecd4d50d | ||
|
|
c0661022ab | ||
|
|
e7305a3dfb | ||
|
|
86f52f176a | ||
|
|
beb9796e75 | ||
|
|
6bb55adebc | ||
|
|
011bc85d18 | ||
|
|
fad75c498b | ||
|
|
915c69b443 | ||
|
|
05b574543c | ||
|
|
5198b2a709 | ||
|
|
509973a027 | ||
|
|
0253475f50 | ||
|
|
6961c92c8d | ||
|
|
906d7b361d | ||
|
|
54121199b6 | ||
|
|
78b3e0a1aa | ||
|
|
aa45795944 | ||
|
|
353209c72c | ||
|
|
d07eaefdd4 | ||
|
|
2ead9f6f02 | ||
|
|
472a349709 | ||
|
|
9043520364 | ||
|
|
88ea77e2f3 | ||
|
|
a609e2d561 | ||
|
|
6f1c791df4 | ||
|
|
dc41e7b50f | ||
|
|
1b22da8bd9 | ||
|
|
773eb9e1c3 | ||
|
|
87deea9748 | ||
|
|
556636f41e | ||
|
|
daab3dd3dd | ||
|
|
5535390298 | ||
|
|
f54eaf3101 | ||
|
|
1d86ace385 | ||
|
|
4a02a0a67d | ||
|
|
7f193c11e1 | ||
|
|
9f90a08276 | ||
|
|
f49a114f0f | ||
|
|
281fc300a6 | ||
|
|
ea7078b749 | ||
|
|
03aabb55ca | ||
|
|
078da5b213 | ||
|
|
22af77c7e6 | ||
|
|
7ee7a6624f | ||
|
|
85c9fcd28e | ||
|
|
619da2e19a | ||
|
|
c457cdfe5e | ||
|
|
a87c319d10 | ||
|
|
db5bdb0ef1 | ||
|
|
adc79a34d1 | ||
|
|
8776dffed4 | ||
|
|
77f5434b78 | ||
|
|
0191373ff5 | ||
|
|
0daf995c70 | ||
|
|
dd9437c663 | ||
|
|
752675ffb0 | ||
|
|
5bcc0fb343 | ||
|
|
2cd726c003 | ||
|
|
f911ad57b9 | ||
|
|
bc2a547b86 | ||
|
|
d5ba9bc3f5 | ||
|
|
3dc8493b76 | ||
|
|
0b991c4ae3 | ||
|
|
1352508070 | ||
|
|
87d467ec67 | ||
|
|
c020deb728 | ||
|
|
d5e15f7bf2 | ||
|
|
59622a1bfb | ||
|
|
1c237503a0 | ||
|
|
138a83c844 | ||
|
|
e8c86f4b4e | ||
|
|
344d2ae3e6 | ||
|
|
19a571147b | ||
|
|
2f369fb6ee | ||
|
|
c1d16f5185 | ||
|
|
2382895a42 | ||
|
|
68adae082b | ||
|
|
eac1b573c9 | ||
|
|
a29a17a28b | ||
|
|
4805659a68 | ||
|
|
fbdcfb20ed | ||
|
|
4a2b186ef7 | ||
|
|
fa41b30a4c | ||
|
|
505282986e | ||
|
|
9e6e74db7c | ||
|
|
cf68e88103 | ||
|
|
84ae73d0d1 | ||
|
|
9bb0f13793 | ||
|
|
912485067b | ||
|
|
128f7c15a6 | ||
|
|
5d67d0fdfc | ||
|
|
3f90d19801 | ||
|
|
897e1651c5 | ||
|
|
9c5bb2fcdb | ||
|
|
8ee8d84c8b | ||
|
|
b25fbf42ab | ||
|
|
f9f3392901 | ||
|
|
35249f5804 | ||
|
|
b32fdeda9c | ||
|
|
1d033701ed | ||
|
|
6422cffde3 | ||
|
|
e8588370e5 | ||
|
|
a96692e5cf | ||
|
|
81c272778f | ||
|
|
31351116b5 | ||
|
|
fd18e4d7f1 | ||
|
|
7eb5364ba7 | ||
|
|
97b679bbe0 | ||
|
|
85dc3ad1d7 | ||
|
|
a90e9544be | ||
|
|
6261163728 | ||
|
|
9288ae8776 | ||
|
|
f887f678f1 | ||
|
|
59ba74606e | ||
|
|
130b1c99db | ||
|
|
adc4b424c5 | ||
|
|
4ad140f7a3 |
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.block.SaccharineLeafBlock;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Desc;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.entity.animal.Bee$BeeGrowCropGoal")
|
||||
public abstract class BeeEntityMixin {
|
||||
|
||||
@Unique
|
||||
private BlockState cobblemon$result = null;
|
||||
|
||||
@Inject(method = "tick()V", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/block/BonemealableBlock;performBonemeal(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V",
|
||||
shift = At.Shift.BY,
|
||||
by = 2
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void injectCustomGrowth(CallbackInfo ci, int i, BlockPos blockPos, BlockState blockState, Block block) {
|
||||
if (block instanceof SaccharineLeafBlock) {
|
||||
|
||||
int age = blockState.getValue(SaccharineLeafBlock.Companion.getAGE());
|
||||
|
||||
if (age < 2) {
|
||||
this.cobblemon$result = blockState.setValue(SaccharineLeafBlock.Companion.getAGE(), age + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "tick", at = @At(value = "LOAD", ordinal = 0), index = 5)
|
||||
private BlockState applyCustomBlockState(BlockState before) {
|
||||
if(this.cobblemon$result != null && before == null) {
|
||||
BlockState result = this.cobblemon$result;
|
||||
this.cobblemon$result = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
return before;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.CobblemonRecipeCategories;
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes;
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotBookCategory;
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotRecipe;
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotShapelessRecipe;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.client.ClientRecipeBook;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.RecipeType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ClientRecipeBook.class)
|
||||
public abstract class ClientRecipeBookMixin {
|
||||
|
||||
@Shadow
|
||||
protected static Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> categorizeAndGroupRecipes(Iterable<RecipeHolder<?>> recipes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Shadow private Map<RecipeBookCategories, List<RecipeCollection>> collectionsByTab;
|
||||
|
||||
@Shadow private List<RecipeCollection> allCollections;
|
||||
|
||||
@Inject(method = "getCategory", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategories> cir) {
|
||||
Recipe<?> recipe2 = recipe.value();
|
||||
if (recipe2 instanceof CookingPotRecipe || recipe2 instanceof CookingPotShapelessRecipe) {
|
||||
CookingPotBookCategory category = recipe2 instanceof CookingPotRecipe
|
||||
? ((CookingPotRecipe) recipe2).getCategory()
|
||||
: ((CookingPotShapelessRecipe) recipe2).category();
|
||||
RecipeBookCategories var7;
|
||||
System.out.println(category);
|
||||
switch (category) {
|
||||
case MISC -> var7 = CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory();
|
||||
case FOODS -> var7 = CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory();
|
||||
case MEDICINES -> var7 = CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory();
|
||||
case BAITS -> var7 = CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory();
|
||||
default -> throw new MatchException(null, null);
|
||||
}
|
||||
cir.setReturnValue(var7);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setupCollections", at = @At("RETURN"))
|
||||
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci) {
|
||||
Map<RecipeBookCategories, List<List<RecipeHolder<?>>>> map = categorizeAndGroupRecipes(recipes);
|
||||
Map<RecipeBookCategories, List<RecipeCollection>> map2 = Maps.newHashMap();
|
||||
ImmutableList.Builder<RecipeCollection> builder = ImmutableList.builder();
|
||||
|
||||
map.forEach((recipeBookCategories, list) -> {
|
||||
List<RecipeCollection> collections = list.stream()
|
||||
.map(listx -> new RecipeCollection(registryAccess, listx))
|
||||
.peek(builder::add)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
map2.put(recipeBookCategories, collections);
|
||||
});
|
||||
RecipeBookCategories.AGGREGATE_CATEGORIES.forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List)list.stream().flatMap((recipeBookCategoriesx) -> ((List)map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
|
||||
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List)list.stream().flatMap((recipeBookCategoriesx) -> ((List)map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
|
||||
|
||||
this.collectionsByTab = ImmutableMap.copyOf(map2);
|
||||
this.allCollections = builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.CobblemonItems;
|
||||
import com.cobblemon.mod.common.CobblemonRecipeCategories;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.world.inventory.RecipeBookType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
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.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Mixin(RecipeBookCategories.class)
|
||||
public class RecipeBookCategoriesMixin {
|
||||
|
||||
@Final
|
||||
@Mutable
|
||||
@Shadow
|
||||
private static RecipeBookCategories[] $VALUES;
|
||||
|
||||
@Invoker("<init>")
|
||||
private static RecipeBookCategories cobblemon$createCategory(String name, int ordinal, ItemStack... icons) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "<clinit>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
|
||||
ArrayList<RecipeBookCategories> types = new ArrayList<>(List.of($VALUES));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_SEARCH", $VALUES.length, new ItemStack(Items.COMPASS)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_FOODS", $VALUES.length + 1, new ItemStack(CobblemonItems.ROASTED_LEEK)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MEDICINES", $VALUES.length + 2, new ItemStack(CobblemonItems.HP_UP)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_BAITS", $VALUES.length + 3, new ItemStack(CobblemonItems.PERSIM_BERRY)));
|
||||
types.add(cobblemon$createCategory("COOKING_POT_MISC", $VALUES.length + 4, new ItemStack(CobblemonItems.MIRROR_HERB)));
|
||||
$VALUES = types.toArray(RecipeBookCategories[]::new);
|
||||
}
|
||||
|
||||
@Inject(method = "getCategories", at = @At("HEAD"), cancellable = true)
|
||||
private static void modifyCategories(RecipeBookType recipeBookType, CallbackInfoReturnable<List<RecipeBookCategories>> cir) {
|
||||
if (recipeBookType == RecipeBookType.valueOf("COOKING_POT")) {
|
||||
List var10000 = ImmutableList.of(
|
||||
CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_FOODS.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_MEDICINES.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_BAITS.toVanillaCategory(),
|
||||
CobblemonRecipeCategories.COOKING_POT_MISC.toVanillaCategory());
|
||||
cir.setReturnValue(var10000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.CobblemonRecipeCategories;
|
||||
import net.minecraft.client.ClientRecipeBook;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookPage;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RecipeBookComponent.class)
|
||||
public class RecipeBookComponentMixin {
|
||||
|
||||
@Shadow private ClientRecipeBook book;
|
||||
|
||||
@Shadow protected Minecraft minecraft;
|
||||
|
||||
@Shadow private int width;
|
||||
|
||||
@Shadow private int xOffset;
|
||||
|
||||
@Shadow private int height;
|
||||
|
||||
@Shadow @Final private RecipeBookPage recipeBookPage;
|
||||
|
||||
@Inject(method = "updateTabs", at = @At("HEAD"), cancellable = true)
|
||||
private void customUpdateTabs(CallbackInfo ci) {
|
||||
RecipeBookComponent instance = (RecipeBookComponent) (Object) this;
|
||||
int i = (width - 147) / 2 - xOffset - 30;
|
||||
int j = (height - 166) / 2 + 3;
|
||||
int k = 27;
|
||||
int l = 0;
|
||||
|
||||
for (RecipeBookTabButton recipeBookTabButton : instance.tabButtons) {
|
||||
RecipeBookCategories recipeBookCategories = recipeBookTabButton.getCategory();
|
||||
if (recipeBookCategories != RecipeBookCategories.CRAFTING_SEARCH && recipeBookCategories != RecipeBookCategories.FURNACE_SEARCH && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory()) {
|
||||
if (recipeBookTabButton.updateVisibility(book)) {
|
||||
recipeBookTabButton.setPosition(i, j + 27 * l++);
|
||||
recipeBookTabButton.startAnimation(minecraft);
|
||||
}
|
||||
} else {
|
||||
recipeBookTabButton.visible = true;
|
||||
recipeBookTabButton.setPosition(i, j + k * l++);
|
||||
}
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import net.minecraft.stats.RecipeBookSettings;
|
||||
import net.minecraft.world.inventory.RecipeBookType;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(RecipeBookSettings.class)
|
||||
public class RecipeBookSettingsMixin {
|
||||
|
||||
|
||||
@Shadow @Final public Map<RecipeBookType, RecipeBookSettings.TypeSettings> states;
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void cobblemon$init(CallbackInfo ci) {
|
||||
states.put(RecipeBookType.valueOf("COOKING_POT"), new RecipeBookSettings.TypeSettings(true, true));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import net.minecraft.client.ClientRecipeBook;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookTabButton;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(RecipeBookTabButton.class)
|
||||
public class RecipeBookTabButtonMixin {
|
||||
|
||||
@Inject(method = "updateVisibility", at = @At("HEAD"), cancellable = true)
|
||||
private void customUpdateVisibility(ClientRecipeBook recipeBook, CallbackInfoReturnable<Boolean> cir) {
|
||||
RecipeBookTabButton instance = (RecipeBookTabButton) (Object) this;
|
||||
List<RecipeCollection> list = recipeBook.getCollection(instance.getCategory());
|
||||
boolean visible = false;
|
||||
if (list != null) {
|
||||
for (RecipeCollection recipeCollection : list) {
|
||||
if (recipeCollection.hasKnownRecipes() && recipeCollection.hasFitting()) {
|
||||
visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.visible = visible;
|
||||
cir.setReturnValue(visible);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import net.minecraft.world.inventory.RecipeBookType;
|
||||
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.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(value = RecipeBookType.class)
|
||||
public class RecipeBookTypeMixin {
|
||||
|
||||
@Final
|
||||
@Mutable
|
||||
@Shadow
|
||||
private static RecipeBookType[] $VALUES; // moj
|
||||
|
||||
@Invoker("<init>")
|
||||
private static RecipeBookType cobblemon$createBookType(String name, int ordinal) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "<clinit>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private static void cobblemon$addRecipeBookType(CallbackInfo ci) {
|
||||
ArrayList<RecipeBookType> types = new ArrayList<>(List.of($VALUES));
|
||||
types.add(cobblemon$createBookType("COOKING_POT", $VALUES.length));
|
||||
$VALUES = types.toArray(RecipeBookType[]::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
|
||||
import net.minecraft.stats.RecipeBook;
|
||||
import net.minecraft.world.entity.player.StackedContents;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(RecipeCollection.class)
|
||||
public class RecipeCollectionMixin {
|
||||
|
||||
@Shadow @Final private Set<RecipeHolder<?>> fitsDimensions;
|
||||
|
||||
@Shadow @Final private Set<RecipeHolder<?>> craftable;
|
||||
|
||||
@Shadow @Final private List<RecipeHolder<?>> recipes;
|
||||
|
||||
@Inject(method = "canCraft", at = @At("HEAD"), cancellable = true)
|
||||
private void customCanCraft(StackedContents handler, int width, int height, RecipeBook book, CallbackInfo ci) {
|
||||
for (RecipeHolder<?> recipeHolder : recipes) {
|
||||
boolean bl = recipeHolder.value().canCraftInDimensions(width, height) && book.contains(recipeHolder);
|
||||
if (bl) {
|
||||
fitsDimensions.add(recipeHolder);
|
||||
} else {
|
||||
fitsDimensions.remove(recipeHolder);
|
||||
}
|
||||
|
||||
if (bl && handler.canCraft(recipeHolder.value(), null)) {
|
||||
craftable.add(recipeHolder);
|
||||
} else {
|
||||
craftable.remove(recipeHolder);
|
||||
}
|
||||
}
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -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.mixin;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(RecipeManager.class)
|
||||
public class RecipeManagerMixin {
|
||||
|
||||
@Inject(method = "apply*", at = @At(value = "HEAD"))
|
||||
private void logRecipeCreation(Map<ResourceLocation, JsonElement> object, ResourceManager resourceManager, ProfilerFiller profiler, CallbackInfo ci) {
|
||||
for (Map.Entry<ResourceLocation, JsonElement> entry : object.entrySet()) {
|
||||
ResourceLocation resourceLocation = entry.getKey();
|
||||
System.out.println("Recipe found: " + resourceLocation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.accessor;
|
||||
|
||||
import net.minecraft.client.gui.components.StateSwitchingButton;
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(RecipeBookComponent.class)
|
||||
public interface RecipeBookComponentAccessor {
|
||||
@Accessor("filterButton")
|
||||
StateSwitchingButton getFilterButton();
|
||||
}
|
||||
@@ -99,6 +99,7 @@ import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -172,6 +173,8 @@ object Cobblemon {
|
||||
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
|
||||
}
|
||||
|
||||
implementation.registerRecipeSerializers()
|
||||
implementation.registerRecipeTypes()
|
||||
implementation.registerPermissionValidator()
|
||||
implementation.registerSoundEvents()
|
||||
implementation.registerDataComponents()
|
||||
@@ -183,6 +186,7 @@ object Cobblemon {
|
||||
implementation.registerVillagers()
|
||||
implementation.registerWorldGenFeatures()
|
||||
implementation.registerParticles()
|
||||
implementation.registerMenu()
|
||||
implementation.registerEntityDataSerializers()
|
||||
implementation.registerCriteria()
|
||||
implementation.registerEntitySubPredicates()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
@@ -45,9 +46,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.of(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
|
||||
@JvmField
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN).build(null))
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.of(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN, CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.of(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN).build(null))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.of(::GildedChestBlockEntity,
|
||||
@@ -86,4 +87,36 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
|
||||
BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE: BlockEntityType<CampfireBlockEntity> = this.create("campfire_pot",
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
|
||||
)
|
||||
|
||||
/*@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
|
||||
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
|
||||
CobblemonBlocks.LURE_CAKE
|
||||
).build(null)*/
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
|
||||
"lure_cake",
|
||||
BlockEntityType.Builder.of(::LureCakeBlockEntity, CobblemonBlocks.LURE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val POKE_CAKE: BlockEntityType<PokeCakeBlockEntity> = this.create(
|
||||
"poke_cake",
|
||||
BlockEntityType.Builder.of(::PokeCakeBlockEntity, CobblemonBlocks.POKE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE: BlockEntityType<CandlePokeCakeBlockEntity> = this.create(
|
||||
"candle_poke_cake",
|
||||
BlockEntityType.Builder.of(::CandlePokeCakeBlockEntity, CobblemonBlocks.CANDLE_POKE_CAKE).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.block.*
|
||||
import com.cobblemon.mod.common.block.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.LecternBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
@@ -20,6 +21,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
@@ -27,9 +29,13 @@ import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.valueproviders.UniformInt
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockSetType
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
@@ -43,6 +49,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
val APRICORN_BLOCK_SET_TYPE = BlockSetType("apricorn")
|
||||
val APRICORN_WOOD_TYPE = WoodType.register(WoodType("apricorn", APRICORN_BLOCK_SET_TYPE))
|
||||
val SACCHARINE_BLOCK_SET_TYPE = BlockSetType("saccharine")
|
||||
val SACCHARINE_WOOD_TYPE = WoodType.register(WoodType("saccharine", SACCHARINE_BLOCK_SET_TYPE))
|
||||
|
||||
// Evolution Ores
|
||||
@JvmField
|
||||
@@ -159,15 +167,77 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
|
||||
|
||||
// Saccharines
|
||||
@JvmField
|
||||
val SACCHARINE_LOG = log("saccharine_log", arg2 = MapColor.COLOR_BLUE)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_LOG = this.create("stripped_saccharine_log", SaccharineStrippedLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
|
||||
@JvmField
|
||||
val SACCHARINE_WOOD = log("saccharine_wood")
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", SaccharineStrippedLogBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava())) //log("stripped_saccharine_log")
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(
|
||||
BlockBehaviour.Properties.of().mapColor(
|
||||
MapColor.PLANT
|
||||
).strength(0.2f).randomTicks().sound(SoundType.GRASS).noOcclusion()
|
||||
.isValidSpawn { _: BlockState?, _: BlockGetter?, _: BlockPos?, entity: EntityType<*>? ->
|
||||
entity === EntityType.OCELOT || entity === EntityType.PARROT
|
||||
}.isSuffocating { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
false
|
||||
}.isViewBlocking { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
false
|
||||
}.ignitedByLava().pushReaction(PushReaction.DESTROY)
|
||||
.isRedstoneConductor { _: BlockState?, _: BlockGetter?, _: BlockPos? ->
|
||||
false
|
||||
}))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(SACCHARINE_WOOD_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
|
||||
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
|
||||
@JvmField
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).noCollission().strength(0.5f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_HANGING_SIGN = this.create("saccharine_wall_hanging_sign", CobblemonWallHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0f, 3.0f).sound(SoundType.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = this.create("saccharine_stairs", StairsBlockInvoker.`cobblemon$create`(SACCHARINE_PLANKS.defaultBlockState(), BlockBehaviour.Properties.ofFullCopy(SACCHARINE_PLANKS)))
|
||||
@JvmField
|
||||
val SACCHARINE_DOOR = this.create("saccharine_door", DoorBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(SACCHARINE_PLANKS.defaultMapColor()).instrument(NoteBlockInstrument.BASS).strength(3.0F).sound(SoundType.WOOD).noOcclusion()))
|
||||
@JvmField
|
||||
val SACCHARINE_TRAPDOOR = this.create("saccharine_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(SACCHARINE_BLOCK_SET_TYPE, BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(3.0F).sound(SoundType.WOOD).noOcclusion()))
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = this.create("saccharine_sapling", SaccharineSaplingBlock(PLANT_PROPERTIES))
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.CRIMSON_NYLIUM).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.DIRT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val BIG_ROOT = this.create("big_root", BigRootBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.BIG_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = this.create("lure_cake", LureCakeBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val POKE_CAKE = this.create("poke_cake", PokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE)))
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
@@ -319,6 +389,24 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
|
||||
|
||||
@JvmField
|
||||
val BLACK_CAMPFIRE_POT = create("campfire_pot_black", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLACK).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val BLUE_CAMPFIRE_POT = create("campfire_pot_blue", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val GREEN_CAMPFIRE_POT = create("campfire_pot_green", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GREEN).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val PINK_CAMPFIRE_POT = create("campfire_pot_pink", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_PINK).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val RED_CAMPFIRE_POT = create("campfire_pot_red", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_RED).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val WHITE_CAMPFIRE_POT = create("campfire_pot_white", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_GRAY).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
@JvmField
|
||||
val YELLOW_CAMPFIRE_POT = create("campfire_pot_yellow", CampfirePotBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_YELLOW).sound(CobblemonSounds.CAMPFIRE_POT_SOUNDS).strength(0.5F).pushReaction(PushReaction.BLOCK).noOcclusion()))
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel { if ((it.getValue(CampfireBlock.SOUL) as Boolean)) 9 else 14 }))
|
||||
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
||||
@JvmField
|
||||
@@ -360,6 +448,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F)))
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 10 else 0 }))
|
||||
|
||||
@@ -381,6 +472,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(MobEffects.LEVITATION, 10F, BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.VIVICHOKE_SOUNDS)))
|
||||
|
||||
/**
|
||||
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
|
||||
@@ -389,7 +482,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
*/
|
||||
fun strippedBlocks(): Map<Block, Block> = mapOf(
|
||||
APRICORN_WOOD to STRIPPED_APRICORN_WOOD,
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG,
|
||||
SACCHARINE_WOOD to STRIPPED_SACCHARINE_WOOD,
|
||||
SACCHARINE_LOG to STRIPPED_SACCHARINE_LOG
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -100,6 +100,10 @@ interface CobblemonImplementation {
|
||||
*/
|
||||
fun registerVillagers()
|
||||
|
||||
fun registerRecipeSerializers()
|
||||
fun registerRecipeTypes()
|
||||
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
@@ -108,6 +112,8 @@ interface CobblemonImplementation {
|
||||
|
||||
fun registerParticles()
|
||||
|
||||
fun registerMenu()
|
||||
|
||||
fun registerEntitySubPredicates()
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.PotComponent
|
||||
import com.cobblemon.mod.common.item.components.HeldItemCapableComponent
|
||||
import com.cobblemon.mod.common.item.components.PokemonItemComponent
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import com.cobblemon.mod.common.item.RodBaitComponent
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
@@ -36,11 +38,22 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
|
||||
.networkSynchronized(RodBaitComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val POT_DATA: DataComponentType<PotComponent> = create("cooking_pot_item", DataComponentType.builder<PotComponent>()
|
||||
.persistent(PotComponent.CODEC)
|
||||
.networkSynchronized(PotComponent.PACKET_CODEC)
|
||||
.build())
|
||||
|
||||
val COOKING_COMPONENT: DataComponentType<CookingComponent> = create("cooking_component", DataComponentType.builder<CookingComponent>()
|
||||
.persistent(CookingComponent.CODEC)
|
||||
.networkSynchronized(CookingComponent.PACKET_CODEC) // Use CODEC if no specific PACKET_CODEC is defined
|
||||
.build())
|
||||
|
||||
|
||||
fun register() {
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:cooking_pot_item"), POT_DATA)
|
||||
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:cooking_component"), COOKING_COMPONENT)
|
||||
}
|
||||
|
||||
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.client.cooking.AprijuiceTypes
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
@@ -171,6 +172,25 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val POKEDEX_WHITE = pokedexItem(PokedexType.WHITE)
|
||||
|
||||
val campfire_pots = mutableListOf<CampfirePotItem>()
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
|
||||
|
||||
@JvmField
|
||||
val Hearty_Grains = compostableItem("hearty_grains", HeartyGrainsItem(CobblemonBlocks.HEARTY_GRAINS))
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE = compostableItem("vivichoke")
|
||||
|
||||
@@ -261,6 +281,307 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Properties()))
|
||||
|
||||
// Saccharines
|
||||
@JvmField
|
||||
val SACCHARINE_LOG = blockItem("saccharine_log", CobblemonBlocks.SACCHARINE_LOG)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_LOG = blockItem("stripped_saccharine_log", CobblemonBlocks.STRIPPED_SACCHARINE_LOG)
|
||||
@JvmField
|
||||
val SACCHARINE_WOOD = blockItem("saccharine_wood", CobblemonBlocks.SACCHARINE_WOOD)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = blockItem("stripped_saccharine_wood", CobblemonBlocks.STRIPPED_SACCHARINE_WOOD)
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = blockItem("saccharine_planks", CobblemonBlocks.SACCHARINE_PLANKS)
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = compostableBlockItem("saccharine_leaves", CobblemonBlocks.SACCHARINE_LEAVES)
|
||||
@JvmField
|
||||
val SACCHARINE_BOAT = create("saccharine_boat", CobblemonBoatItem(CobblemonBoatType.SACCHARINE, false, Item.Properties().stacksTo(1)))
|
||||
@JvmField
|
||||
val SACCHARINE_CHEST_BOAT = create("saccharine_chest_boat", CobblemonBoatItem(CobblemonBoatType.SACCHARINE, true, Item.Properties().stacksTo(1)))
|
||||
|
||||
@JvmField
|
||||
val SACCHARINE_DOOR = blockItem("saccharine_door", CobblemonBlocks.SACCHARINE_DOOR)
|
||||
@JvmField
|
||||
val SACCHARINE_TRAPDOOR = blockItem("saccharine_trapdoor", CobblemonBlocks.SACCHARINE_TRAPDOOR)
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = blockItem("saccharine_fence", CobblemonBlocks.SACCHARINE_FENCE)
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = blockItem("saccharine_fence_gate", CobblemonBlocks.SACCHARINE_FENCE_GATE)
|
||||
@JvmField
|
||||
val SACCHARINE_BUTTON = blockItem("saccharine_button", CobblemonBlocks.SACCHARINE_BUTTON)
|
||||
@JvmField
|
||||
val SACCHARINE_PRESSURE_PLATE = blockItem("saccharine_pressure_plate", CobblemonBlocks.SACCHARINE_PRESSURE_PLATE)
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = blockItem("saccharine_slab", CobblemonBlocks.SACCHARINE_SLAB)
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = blockItem("saccharine_stairs", CobblemonBlocks.SACCHARINE_STAIRS)
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", SignItem(Item.Properties().stacksTo(16), CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN))
|
||||
@JvmField
|
||||
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", HangingSignItem(CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN, Item.Properties().stacksTo(16)))
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = saccharineSaplingItem("saccharine_sapling", SaccharineSaplingItem(CobblemonBlocks.SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val SWEET_SAP = noSettingsItem("sweet_sap")
|
||||
@JvmField
|
||||
val BUGWORT = bugwortItem("bugwort", BugwortItem(CobblemonBlocks.BUGWORT))
|
||||
@JvmField
|
||||
val POKE_BAIT = noSettingsItem("poke_bait")
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = blockItem("lure_cake", CobblemonBlocks.LURE_CAKE)
|
||||
@JvmField
|
||||
val POKE_CAKE = blockItem("poke_cake", CobblemonBlocks.POKE_CAKE)
|
||||
|
||||
val aprijuices = mutableListOf<AprijuiceItem>()
|
||||
@JvmField
|
||||
val APRIJUICE_BLACK = aprijuiceItem(AprijuiceTypes.BLACK)
|
||||
@JvmField
|
||||
val APRIJUICE_RED = aprijuiceItem(AprijuiceTypes.RED)
|
||||
@JvmField
|
||||
val APRIJUICE_BLUE = aprijuiceItem(AprijuiceTypes.BLUE)
|
||||
@JvmField
|
||||
val APRIJUICE_GREEN = aprijuiceItem(AprijuiceTypes.GREEN)
|
||||
@JvmField
|
||||
val APRIJUICE_YELLOW = aprijuiceItem(AprijuiceTypes.YELLOW)
|
||||
@JvmField
|
||||
val APRIJUICE_WHITE = aprijuiceItem(AprijuiceTypes.WHITE)
|
||||
@JvmField
|
||||
val APRIJUICE_PINK = aprijuiceItem(AprijuiceTypes.PINK)
|
||||
|
||||
@JvmField
|
||||
val POKE_PUFF = noSettingsItem("poke_puff") // todo make a PokePuffItem class for friendship boosting purposes
|
||||
|
||||
// FOODS
|
||||
@JvmField
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
|
||||
@JvmField
|
||||
val PEWTER_CRUNCHIES = create("pewter_crunchies", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val RAGE_CANDY_BAR = create("rage_candy_bar", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val LAVA_COOKIE = create("lava_cookie", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val OLD_GATEAU = create("old_gateau", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val CASTELIACONE = create("casteliacone", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val SHALOR_SABLE = create("shalor_sable", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = create("lumiose_galette", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val BIG_MALASADA = create("big_malasada", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
/*@JvmField // todo see what we are doing for these (are there different kinds?)
|
||||
val CURRY = create("curry", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.usingConvertsTo(Items.BOWL)
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})*/
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = create("jubilife_muffin", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
/*@JvmField // todo see what we are doing for these (are there different kinds?)
|
||||
val SANDWICH = create("sandwich", object : Item(Properties().stacksTo(16) // todo add all the effects and/or an item class for it for special interactions?
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.usingConvertsTo(Items.BOWL)
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})*/
|
||||
|
||||
@JvmField
|
||||
val CHOICE_DUMPLING = noSettingsItem("choice_dumpling") // todo make a ChoiceDumpingItem class for breeding purposes
|
||||
@JvmField
|
||||
val SWAP_SNACK = noSettingsItem("swap_snack") // todo make a SwapSnackItem class for breeding purposes
|
||||
@JvmField
|
||||
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for breeding purposes
|
||||
|
||||
@JvmField
|
||||
val POTATO_MOCHI = create("potato_mochi", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val FRIED_RICE = create("fried_rice", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val CANDIED_APPLE = create("candied_apple", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val CANDIED_BERRY = create("candied_berry", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
@JvmField
|
||||
val JELLY_DOUGHNUT = create("jelly_doughnut", object : Item(Properties().stacksTo(16)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(10)
|
||||
.saturationModifier(1.2F)
|
||||
.effect(MobEffectInstance(MobEffects.ABSORPTION, 900, 0), 1F)
|
||||
.alwaysEdible()
|
||||
.build())) {
|
||||
override fun finishUsingItem(stack: ItemStack, world: Level, user: LivingEntity): ItemStack {
|
||||
user.removeAllEffects()
|
||||
return super.finishUsingItem(stack, world, user)
|
||||
}
|
||||
})
|
||||
|
||||
// todo etc?
|
||||
|
||||
//@JvmField
|
||||
//val SCATTER_BANG = this.create("scatter_bang", ScatterBangItem(Item.Settings()))
|
||||
//@JvmField
|
||||
//val STICKY_GLOB = this.create("sticky_glob", StickyGlobItem(Item.Settings()))
|
||||
|
||||
@JvmField
|
||||
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
|
||||
@JvmField
|
||||
@@ -276,7 +597,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val LECTERN = blockItem("lectern", CobblemonBlocks.LECTERN)
|
||||
val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET)
|
||||
|
||||
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@@ -421,6 +743,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val HEALTH_MOCHI = create("health_mochi", MochiItem(Stats.HP))
|
||||
@JvmField
|
||||
val MUSCLE_MOCHI = create("muscle_mochi", MochiItem(Stats.ATTACK))
|
||||
@JvmField
|
||||
val RESIST_MOCHI = create("resist_mochi", MochiItem(Stats.DEFENCE))
|
||||
@JvmField
|
||||
val GENIUS_MOCHI = create("genius_mochi", MochiItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField
|
||||
val CLEVER_MOCHI = create("clever_mochi", MochiItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val SWIFT_MOCHI = create("swift_mochi", MochiItem(Stats.SPEED))
|
||||
@JvmField
|
||||
val FRESH_START_MOCHI = create("fresh_start_mochi", FreshStartMochiItem())
|
||||
@JvmField
|
||||
val GENIUS_FEATHER = create("genius_feather", FeatherItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField
|
||||
val SWIFT_FEATHER = create("swift_feather", FeatherItem(Stats.SPEED))
|
||||
@@ -1263,6 +1599,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
|
||||
val item = create("campfire_pot_${type}", CampfirePotItem(block))
|
||||
campfire_pots.add(item)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun aprijuiceItem(type: AprijuiceTypes): AprijuiceItem {
|
||||
val item = create("aprijuice_${type.name.lowercase()}", AprijuiceItem(type))
|
||||
aprijuices.add(item)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
CobblemonItem(Item.Properties()).also {
|
||||
@@ -1321,6 +1669,21 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun saccharineSaplingItem(name: String, saccharineSaplingItem: SaccharineSaplingItem): SaccharineSaplingItem {
|
||||
val finalName = "saccharine_sapling"
|
||||
val item = this.create(finalName, saccharineSaplingItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun bugwortItem(name: String, bugwortItem: BugwortItem): BugwortItem {
|
||||
val finalName = "bugwort"
|
||||
val item = this.create(finalName, bugwortItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
private fun mintSeed(name: String, mintBlock: MintBlock): Item {
|
||||
val finalName = "${name}_mint_seeds"
|
||||
val item = this.blockItem(finalName, mintBlock)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.client.gui.screens.MenuScreens
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.inventory.MenuType
|
||||
|
||||
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
|
||||
|
||||
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
|
||||
|
||||
init {
|
||||
MenuScreens.register(COOKING_POT, ::CookingPotScreen)
|
||||
}
|
||||
|
||||
override val registry: Registry<MenuType<*>>
|
||||
get() = BuiltInRegistries.MENU
|
||||
override val resourceKey: ResourceKey<Registry<MenuType<*>>>
|
||||
get() = Registries.MENU
|
||||
}
|
||||
@@ -16,6 +16,7 @@ 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.cooking.ToggleCookingPotLidHandler
|
||||
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.dialogue.DialogueClosedHandler
|
||||
@@ -56,6 +57,8 @@ import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
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.cooking.SeasoningRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
@@ -122,18 +125,11 @@ import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPa
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.PCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestRenamePCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SortPCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
@@ -311,6 +307,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(PokedexDexSyncPacket.ID, PokedexDexSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(DexEntrySyncPacket.ID, DexEntrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FishingBaitRegistrySyncPacket.ID, FishingBaitRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
// Effects
|
||||
@@ -364,6 +361,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -458,6 +456,9 @@ object CobblemonNetwork {
|
||||
// NPC packets
|
||||
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import net.minecraft.client.RecipeBookCategories
|
||||
|
||||
enum class CobblemonRecipeCategories {
|
||||
|
||||
COOKING_POT_SEARCH("COOKING_POT_SEARCH"),
|
||||
COOKING_POT_FOODS("COOKING_POT_FOODS"),
|
||||
COOKING_POT_MEDICINES("COOKING_POT_MEDICINES"),
|
||||
COOKING_POT_BAITS("COOKING_POT_BAITS"),
|
||||
COOKING_POT_MISC("COOKING_POT_MISC");
|
||||
|
||||
companion object {
|
||||
val customAggregateCategories: Map<RecipeBookCategories, List<RecipeBookCategories>> = mapOf(
|
||||
COOKING_POT_SEARCH.toVanillaCategory() to listOf(
|
||||
COOKING_POT_FOODS.toVanillaCategory(), COOKING_POT_MISC.toVanillaCategory(), COOKING_POT_MEDICINES.toVanillaCategory(), COOKING_POT_BAITS.toVanillaCategory()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var id: String
|
||||
|
||||
constructor(id: String) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
fun toVanillaCategory(): RecipeBookCategories {
|
||||
return RecipeBookCategories.valueOf(this.id)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotRecipe
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotShapelessRecipe
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||
|
||||
object CobblemonRecipeSerializers : PlatformRegistry<Registry<RecipeSerializer<*>>, ResourceKey<Registry<RecipeSerializer<*>>>, RecipeSerializer<*>>() {
|
||||
|
||||
val COOKING_POT_COOKING: RecipeSerializer<*> = register("cobblemon:cooking_pot", CookingPotRecipe.Serializer()).also {
|
||||
println("Registered cooking pot recipe serializer: $it")
|
||||
}
|
||||
|
||||
val COOKING_POT_SHAPELESS: RecipeSerializer<*> = register(
|
||||
"cobblemon:cooking_pot_shapeless",
|
||||
CookingPotShapelessRecipe.Serializer()
|
||||
).also {
|
||||
println("Registered cooking pot shapeless recipe serializer: $it")
|
||||
}
|
||||
|
||||
override val registry: Registry<RecipeSerializer<*>>
|
||||
get() = BuiltInRegistries.RECIPE_SERIALIZER
|
||||
override val resourceKey: ResourceKey<Registry<RecipeSerializer<*>>>
|
||||
get() = Registries.RECIPE_SERIALIZER
|
||||
|
||||
fun <S : RecipeSerializer<T>, T : Recipe<*>> register(key: String, recipeSerializer: S): S {
|
||||
return Registry.register(BuiltInRegistries.RECIPE_SERIALIZER, key, recipeSerializer) as S
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotRecipe
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotShapelessRecipe
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
|
||||
object CobblemonRecipeTypes : PlatformRegistry<Registry<RecipeType<*>>, ResourceKey<Registry<RecipeType<*>>>, RecipeType<*>>() {
|
||||
|
||||
val COOKING_POT_COOKING: RecipeType<CookingPotRecipe> = register<CookingPotRecipe>("cobblemon:cooking_pot")
|
||||
val COOKING_POT_SHAPELESS: RecipeType<CookingPotShapelessRecipe> = register<CookingPotShapelessRecipe>("cobblemon:cooking_pot_shapeless")
|
||||
|
||||
override val registry: Registry<RecipeType<*>>
|
||||
get() = BuiltInRegistries.RECIPE_TYPE
|
||||
override val resourceKey: ResourceKey<Registry<RecipeType<*>>>
|
||||
get() = Registries.RECIPE_TYPE
|
||||
|
||||
fun <T : Recipe<*>> register(identifier: String): RecipeType<T> {
|
||||
return Registry.register(
|
||||
BuiltInRegistries.RECIPE_TYPE,
|
||||
identifier,
|
||||
object : RecipeType<T> {
|
||||
override fun toString(): String {
|
||||
return identifier
|
||||
}
|
||||
}
|
||||
).also { println("Registered recipe type with identifier: $identifier") }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,6 +110,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val MEDICINE_SPRAY_USE = this.create("item.medicine.spray.use")
|
||||
@JvmField
|
||||
val MEDICINE_FEATHER_USE = this.create("item.medicine.feather.use")
|
||||
@JvmField
|
||||
val MOCHI_USE = this.create("item.medicine.feather.use")
|
||||
|
||||
@JvmField
|
||||
val MULCH_PLACE = this.create("block.mulch.place")
|
||||
@@ -423,5 +425,37 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
DISPLAY_CASE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE_CAMPFIRE = this.create("block.campfire_pot.place_campfire")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RETRIEVE = this.create("block.campfire_pot.retrieve")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_OPEN = this.create("block.campfire_pot.open")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CLOSE = this.create("block.campfire_pot.close")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_USE = this.create("block.campfire_pot.use")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_COOK = this.create("block.campfire_pot.cook")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CRAFT = this.create("block.campfire_pot.craft")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE = this.create("block.campfire_pot.place")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BREAK = this.create("block.campfire_pot.break")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_HIT = this.create("block.campfire_pot.hit")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_STEP = this.create("block.campfire_pot.step")
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_SOUNDS = SoundType(1f, 1.1f,
|
||||
CAMPFIRE_POT_BREAK,
|
||||
CAMPFIRE_POT_STEP,
|
||||
CAMPFIRE_POT_PLACE,
|
||||
CAMPFIRE_POT_HIT,
|
||||
CAMPFIRE_POT_STEP
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.collections.flatMap
|
||||
|
||||
private val colorMap = mapOf(
|
||||
"spicy" to 0xFEB37D,
|
||||
"dry" to 0x8AE9FC,
|
||||
"sweet" to 0xFFBEED,
|
||||
"bitter" to 0x9EED8F,
|
||||
"sour" to 0xFCF38A
|
||||
)
|
||||
|
||||
private val bubbleColorMap = mapOf(
|
||||
"spicy" to 0xFFD9AD,
|
||||
"dry" to 0xBCF8FE,
|
||||
"sweet" to 0xFEE3F9,
|
||||
"bitter" to 0xC8F7BC,
|
||||
"sour" to 0xFDFAB8
|
||||
)
|
||||
|
||||
fun getColorMixFromSeasonings(seasonings: List<ItemStack>, forBubbles: Boolean = false): Int? {
|
||||
val flavors = seasonings
|
||||
.flatMap { Seasonings.getFromItemStack(it)?.flavors?.entries ?: emptyList() }
|
||||
.groupingBy { it.key }
|
||||
.fold(0) { acc, entry -> acc + entry.value }
|
||||
|
||||
val maxFlavorValue = flavors.values.maxOrNull()
|
||||
val dominantFlavors = flavors.filter { it.value == maxFlavorValue }.map { it.key }
|
||||
|
||||
return getColorMixFromCookingComponent(dominantFlavors, forBubbles)
|
||||
}
|
||||
|
||||
fun getTransparentColorMixFromSeasonings(seasonings: List<ItemStack>): Int? {
|
||||
val baseColor = getColorMixFromSeasonings(seasonings) // This gets the original color
|
||||
if (baseColor == null) return null
|
||||
|
||||
// Apply 50% transparency by ensuring the alpha channel is 0x80
|
||||
return (baseColor and 0x00FFFFFF) or (0x80 shl 24)
|
||||
}
|
||||
|
||||
|
||||
fun getColorMixFromCookingComponent(cookingComponent: CookingComponent): Int? {
|
||||
val dominantFlavors = cookingComponent.getDominantFlavors()
|
||||
return getColorMixFromCookingComponent(dominantFlavors)
|
||||
}
|
||||
|
||||
fun getColorMixFromCookingComponent(dominantFlavors: List<String>, forBubbles: Boolean = false): Int? {
|
||||
val colors =
|
||||
dominantFlavors.mapNotNull { if (forBubbles) bubbleColorMap[it] else colorMap[it] }
|
||||
.map { FastColor.ARGB32.opaque(it) }
|
||||
|
||||
if (colors.isEmpty()) return null
|
||||
|
||||
val (alphaSum, redSum, greenSum, blueSum) = colors.fold(IntArray(4)) { acc, color ->
|
||||
acc[0] += FastColor.ARGB32.alpha(color)
|
||||
acc[1] += FastColor.ARGB32.red(color)
|
||||
acc[2] += FastColor.ARGB32.green(color)
|
||||
acc[3] += FastColor.ARGB32.blue(color)
|
||||
acc
|
||||
}
|
||||
|
||||
return FastColor.ARGB32.color(
|
||||
alphaSum / colors.size,
|
||||
redSum / colors.size,
|
||||
greenSum / colors.size,
|
||||
blueSum / colors.size
|
||||
)
|
||||
}
|
||||
|
||||
fun getColor(color: String): Int? {
|
||||
return when (color.lowercase()) {
|
||||
"red" -> DyeColor.RED
|
||||
"orange" -> DyeColor.ORANGE
|
||||
"yellow" -> DyeColor.YELLOW
|
||||
"lime" -> DyeColor.LIME
|
||||
"green" -> DyeColor.GREEN
|
||||
"cyan" -> DyeColor.CYAN
|
||||
"light blue" -> DyeColor.LIGHT_BLUE
|
||||
"blue" -> DyeColor.BLUE
|
||||
"purple" -> DyeColor.PURPLE
|
||||
"magenta" -> DyeColor.MAGENTA
|
||||
"pink" -> DyeColor.PINK
|
||||
"white" -> DyeColor.WHITE
|
||||
else -> null
|
||||
}?.textureDiffuseColor
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
data class Seasoning(
|
||||
val ingredient: ResourceLocation,
|
||||
val flavors: Map<String, Int>, // Updated to store a map of flavor types and values
|
||||
val color: String,
|
||||
val quality: Int
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<Seasoning> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
ResourceLocation.CODEC.fieldOf("ingredient").forGetter<Seasoning> { it.ingredient },
|
||||
Codec.unboundedMap(Codec.STRING, Codec.INT).fieldOf("flavors").forGetter<Seasoning> { it.flavors }, // Use map codec
|
||||
Codec.STRING.fieldOf("color").forGetter<Seasoning> { it.color },
|
||||
Codec.INT.fieldOf("quality").forGetter<Seasoning> { it.quality }
|
||||
).apply(builder, ::Seasoning)
|
||||
}
|
||||
|
||||
val BLANK_SEASONING = Seasoning(
|
||||
ingredient = cobblemonResource("blank"),
|
||||
flavors = mapOf("spicy" to 0, "dry" to 0, "sweet" to 0, "bitter" to 0, "sour" to 0), // Default to all 0
|
||||
color = "",
|
||||
quality = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.SeasoningRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object Seasonings : JsonDataRegistry<Seasoning> {
|
||||
override val id = cobblemonResource("seasonings")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<Seasonings>()
|
||||
override val typeToken: TypeToken<Seasoning> = TypeToken.get(Seasoning::class.java)
|
||||
override val resourcePath = "seasonings"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val itemMap = mutableMapOf<ResourceLocation, Seasoning>()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
SeasoningRegistrySyncPacket(this.itemMap.values.toList()).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, Seasoning>) {
|
||||
itemMap.clear()
|
||||
data.forEach { id, seasoning ->
|
||||
//println("Loaded Seasoning: $id -> $seasoning") // Debugging output
|
||||
itemMap[id] = seasoning
|
||||
}
|
||||
}
|
||||
|
||||
fun getFromItemStack(stack: ItemStack): Seasoning? {
|
||||
val itemId = BuiltInRegistries.ITEM.getKey(stack.item)
|
||||
//println("Checking ItemStack: ${stack.item} -> Identifier: $itemId")
|
||||
return if (itemId != null) getFromIdentifier(itemId) else null
|
||||
}
|
||||
|
||||
fun getFromIdentifier(identifier: ResourceLocation): Seasoning? {
|
||||
//println("Looking for Identifier: $identifier in ${itemMap.keys}")
|
||||
return itemMap[identifier]
|
||||
}
|
||||
|
||||
|
||||
fun isSeasoning(stack: ItemStack) = BuiltInRegistries.ITEM.getKey(stack.item)?.let { itemMap.containsKey(it) } == true
|
||||
}
|
||||
@@ -247,6 +247,11 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val BAIT_EFFECT_REGISTRATION = EventObservable<BaitEffectFunctionRegistryEvent>()
|
||||
|
||||
// Baits and Lures
|
||||
@JvmField
|
||||
val BAIT_SPAWN_POKEMON_MODIFY = EventObservable<BaitSpawnPokemonEvent.Modify>()
|
||||
|
||||
|
||||
@JvmField
|
||||
val COLLECT_EGG = CancelableObservable<CollectEggEvent>()
|
||||
@JvmField
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
/**
|
||||
* Event that is fired when a Pokemon is spawned by a bobber.
|
||||
*/
|
||||
interface BaitSpawnPokemonEvent {
|
||||
|
||||
/**
|
||||
* Event that is fired before a Pokemon is spawned by a bobber.
|
||||
* @param bobber The PokeRodFishingBobberEntity that is spawning the Pokemon.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param rod The ItemStack of the rod that the bobber is attached to.
|
||||
*/
|
||||
data class Pre(
|
||||
val bobber: PokeRodFishingBobberEntity, // todo replace this with the bait entity
|
||||
val chosenBucket: SpawnBucket,
|
||||
val rod: ItemStack
|
||||
) : Cancelable(), BobberSpawnPokemonEvent
|
||||
|
||||
/**
|
||||
* Event that is fired when a Pokemon is modified before it is spawned by a bobber.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param rod The ItemStack of the rod that the bobber is attached to.
|
||||
* @param pokemon The Pokemon that is modified.
|
||||
*/
|
||||
data class Modify(
|
||||
val chosenBucket: SpawnBucket,
|
||||
val rod: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
|
||||
/**
|
||||
* Event that is fired after a Pokemon is spawned by a bobber.
|
||||
* @param bobber The PokeRodFishingBobberEntity that is spawning the Pokemon.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param bait The ItemStack of the bait that is set on the rod.
|
||||
* @param pokemon The Pokemon that is spawned.
|
||||
*/
|
||||
data class Post(
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val chosenBucket: SpawnBucket,
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitEffectFunctionRegistryEvent
|
||||
import com.cobblemon.mod.common.api.spawning.bait.BaitSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
@@ -127,6 +127,7 @@ class ParticleEvent(
|
||||
sourceAlive = storm.sourceAlive,
|
||||
sourceVisible = storm.sourceVisible,
|
||||
onDespawn = {},
|
||||
getParticleColor = storm.getParticleColor,
|
||||
targetPos = storm.targetPos,
|
||||
runtime = MoLangRuntime().setup(),
|
||||
entity = storm.entity
|
||||
|
||||
@@ -30,7 +30,8 @@ import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.bait.BaitSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.context.BaitSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
|
||||
import com.cobblemon.mod.common.api.spawning.preset.PokemonSpawnDetailPreset
|
||||
@@ -82,6 +83,7 @@ object BestSpawner {
|
||||
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
|
||||
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
|
||||
lateinit var fishingSpawner: FishingSpawner
|
||||
lateinit var baitSpawner: BaitSpawner
|
||||
|
||||
fun init() {
|
||||
LOGGER.info("Starting the Best Spawner...")
|
||||
@@ -93,6 +95,7 @@ object BestSpawner {
|
||||
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
|
||||
SpawningCondition.register(SeafloorSpawningCondition.NAME, SeafloorSpawningCondition::class.java)
|
||||
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
|
||||
SpawningCondition.register(BaitSpawningCondition.NAME, BaitSpawningCondition::class.java)
|
||||
|
||||
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
|
||||
|
||||
@@ -108,6 +111,8 @@ object BestSpawner {
|
||||
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "bait", clazz = BaitSpawningContext::class.java, defaultCondition = BaitSpawningCondition.NAME)
|
||||
// todo do we want a bait context? Or just use Surface?
|
||||
|
||||
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
|
||||
|
||||
@@ -133,5 +138,6 @@ object BestSpawner {
|
||||
fun onServerStarted() {
|
||||
spawnerManagers.forEach(SpawnerManager::onServerStarted)
|
||||
fishingSpawner = FishingSpawner()
|
||||
baitSpawner = BaitSpawner()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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.bait
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
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.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.collections.any
|
||||
import kotlin.collections.contains
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.filterIsInstance
|
||||
import kotlin.collections.firstOrNull
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.random
|
||||
import kotlin.collections.randomOrNull
|
||||
import kotlin.let
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
import kotlin.run
|
||||
|
||||
/**
|
||||
* A spawning cause that is embellished with bait information. Could probably also
|
||||
* have the bait entity or something.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class BaitSpawnCause(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
entity: Entity?,
|
||||
val baitStack: ItemStack,
|
||||
val baitEffect: FishingBait?
|
||||
) : SpawnCause(spawner, bucket, entity) {
|
||||
companion object {
|
||||
const val BAIT_ASPECT = "lured"
|
||||
fun shinyReroll(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
if (pokemonEntity.pokemon.shiny) return
|
||||
|
||||
val shinyOdds = Cobblemon.config.shinyRate.toInt()
|
||||
if (shinyOdds <= 0) {
|
||||
return
|
||||
}
|
||||
val randomNumber = nextInt(0, shinyOdds + 1)
|
||||
|
||||
if (randomNumber <= effect.value.toInt()) {
|
||||
pokemonEntity.pokemon.shiny = true
|
||||
}
|
||||
}
|
||||
|
||||
fun alterNatureAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
val baitStat = effect.subcategory?.let { it1 -> Stats.getStat(it1.path).identifier } ?: run {
|
||||
LOGGER.warn("One of your nature baits is missing a subcategory and failed to effect a fished Pokemon")
|
||||
return
|
||||
}
|
||||
// TIMNOTE: This replaces the static lists. It's less performant because it's being reviewed every time,
|
||||
// but also it's not something that goes off too often.
|
||||
val possibleNatures = Natures.all().filter { it.increasedStat?.identifier == baitStat }
|
||||
if (possibleNatures.isEmpty() || possibleNatures.any { it == pokemonEntity.pokemon.nature }) return
|
||||
val takenNature = possibleNatures.random()
|
||||
|
||||
pokemonEntity.pokemon.nature = Natures.getNature(takenNature.name.path) ?: return
|
||||
}
|
||||
|
||||
fun alterIVAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
val iv = effect.subcategory ?: return
|
||||
|
||||
if ((pokemonEntity.pokemon.ivs[Stats.getStat(iv.path)] ?: 0) + effect.value > 31) // if HP IV is already less than 3 away from 31
|
||||
pokemonEntity.pokemon.ivs.set(Stats.getStat(iv.path), 31)
|
||||
else
|
||||
pokemonEntity.pokemon.ivs.set(Stats.getStat(iv.path), (pokemonEntity.pokemon.ivs[Stats.getStat(iv.path)] ?: 0) + (effect.value).toInt())
|
||||
}
|
||||
|
||||
fun alterGenderAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
if (pokemonEntity.pokemon.species.maleRatio > 0 && pokemonEntity.pokemon.species.maleRatio < 1) // if the pokemon is allowed to be male or female
|
||||
when (effect.subcategory) {
|
||||
cobblemonResource("male") -> if (pokemonEntity.pokemon.gender != Gender.MALE) pokemonEntity.pokemon.gender = Gender.MALE
|
||||
cobblemonResource("female") -> if (pokemonEntity.pokemon.gender != Gender.FEMALE) pokemonEntity.pokemon.gender = Gender.FEMALE
|
||||
}
|
||||
}
|
||||
|
||||
fun alterLevelAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
var level = pokemonEntity.pokemon.level + effect.value.toInt()
|
||||
if (level > Cobblemon.config.maxPokemonLevel)
|
||||
level = Cobblemon.config.maxPokemonLevel
|
||||
pokemonEntity.pokemon.level = level
|
||||
}
|
||||
|
||||
fun alterHAAttempt(pokemonEntity: PokemonEntity) {
|
||||
//val species = pokemonEntity.pokemon.species.let { PokemonSpecies.getByName(it.name) } ?: return
|
||||
//val ability = species.abilities.mapping[Priority.LOW]?.first()?.template?.name ?: return
|
||||
|
||||
//pokemonEntity.pokemon.ability = Abilities.get(ability)?.create(false) ?: return
|
||||
|
||||
// Old code from Licious that might be helpful if the above proves to not work
|
||||
|
||||
// This will iterate from highest to lowest priority
|
||||
pokemonEntity.pokemon.form.abilities.mapping.values.forEach { abilities ->
|
||||
abilities.filterIsInstance<HiddenAbility>()
|
||||
.randomOrNull ()
|
||||
?.let { ability ->
|
||||
// No need to force, this is legal
|
||||
pokemonEntity.pokemon.ability = ability.template.create(false, ability.priority)
|
||||
return
|
||||
}
|
||||
}
|
||||
// There was never a hidden ability :( possible but not by default
|
||||
return
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun alterFriendshipAttempt(pokemonEntity: PokemonEntity, effect: FishingBait.Effect) {
|
||||
if (pokemonEntity.pokemon.friendship + effect.value > 255)
|
||||
pokemonEntity.pokemon.setFriendship(255)
|
||||
else
|
||||
pokemonEntity.pokemon.setFriendship(pokemonEntity.pokemon.friendship + effect.value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
val bait: FishingBait? = baitEffect
|
||||
|
||||
override fun affectSpawn(entity: Entity) {
|
||||
super.affectSpawn(entity)
|
||||
if (entity is PokemonEntity) {
|
||||
entity.pokemon.forcedAspects += BAIT_ASPECT
|
||||
|
||||
bait?.effects?.forEach { it ->
|
||||
if (Math.random() > it.chance) return
|
||||
FishingBait.Effects.getEffectFunction(it.type)?.invoke(entity, it)
|
||||
}
|
||||
|
||||
// Some of the bait actions might have changed the aspects and we need it to be
|
||||
// in the entityData IMMEDIATELY otherwise it will flash as what it would be
|
||||
// with the old aspects.
|
||||
// New aspects copy into the entity data only on the next tick.
|
||||
entity.entityData.set(PokemonEntity.ASPECTS, entity.pokemon.aspects)
|
||||
CobblemonEvents.BAIT_SPAWN_POKEMON_MODIFY.post(BaitSpawnPokemonEvent.Modify(bucket, baitStack, entity))
|
||||
}
|
||||
}
|
||||
|
||||
// EV related bait effects
|
||||
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
|
||||
// if bait exists and any effects are related to EV yields
|
||||
if (bait != null && bait.effects.any{ it.type == FishingBait.Effects.EV }){
|
||||
if (detail is PokemonSpawnDetail) {
|
||||
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
|
||||
val baitEVStat = bait.effects.firstOrNull { it.type == FishingBait.Effects.EV }?.subcategory?.path?.let { Stats.getStat(it) }
|
||||
|
||||
if (detailSpecies != null && baitEVStat != null) {
|
||||
val evYieldValue = detailSpecies.evYield[baitEVStat]?.toFloat() ?: 0f
|
||||
return when {
|
||||
evYieldValue > 0 -> super.affectWeight(detail, ctx, weight) // use original weight if EV yield is greater than 0
|
||||
else -> super.affectWeight(detail, ctx, 0f) // use spawn weight of 0 if EV yield is 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if bait exists and any effects are related to Typing
|
||||
if (bait != null && bait.effects.any{ it.type == FishingBait.Effects.TYPING }){
|
||||
if (detail is PokemonSpawnDetail) {
|
||||
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
|
||||
val baitEffect = bait.effects.firstOrNull { it.type == FishingBait.Effects.TYPING }
|
||||
val baitTypingEffect = baitEffect?.subcategory?.path?.let { ElementalTypes.get(it) }
|
||||
|
||||
if (detailSpecies != null && baitTypingEffect != null) {
|
||||
val isMatchingType = detailSpecies.types.contains(baitTypingEffect)
|
||||
return when {
|
||||
isMatchingType -> super.affectWeight(detail, ctx, weight * baitEffect.value.toFloat()) // multiply weight by multiplier of bait effect if typing is found
|
||||
else -> super.affectWeight(detail, ctx, weight) // use base spawn weight if typing is not same as bait
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if bait exists and any effects are related to Egg Groups
|
||||
if (bait != null && bait.effects.any { it.type == FishingBait.Effects.EGG_GROUP }) {
|
||||
if (detail is PokemonSpawnDetail) {
|
||||
val detailSpecies = detail.pokemon.species?.let { PokemonSpecies.getByName(it) }
|
||||
|
||||
if (detailSpecies != null) {
|
||||
// Collect all the egg group effects
|
||||
val eggGroupEffects = bait.effects.filter { it.type == FishingBait.Effects.EGG_GROUP }
|
||||
|
||||
// Check if any of the egg group effects match the species' egg groups
|
||||
val matchingEffect = eggGroupEffects.firstOrNull { effect ->
|
||||
val effectEggGroupKey = effect.subcategory?.path ?: return@firstOrNull false
|
||||
val eggGroup = EggGroup.fromIdentifier(effectEggGroupKey)
|
||||
if (eggGroup == null) {
|
||||
LOGGER.warn("Unknown egg group identifier: $effectEggGroupKey")
|
||||
return@firstOrNull false
|
||||
}
|
||||
detailSpecies.eggGroups.contains(eggGroup)
|
||||
}
|
||||
|
||||
if (matchingEffect != null) {
|
||||
val multiplier = matchingEffect.value
|
||||
return super.affectWeight(detail, ctx, (weight * multiplier).toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.affectWeight(detail, ctx, weight)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.bait
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.context.BaitSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
|
||||
/**
|
||||
* A spawner that takes in a [com.cobblemon.mod.common.api.spawning.bait.FishingSpawnCause] and spawns things.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
open class BaitSpawner(
|
||||
override val name: String = "bait",
|
||||
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
) : TriggerSpawner<BaitSpawnCause>(name, pool) {
|
||||
override fun parseContext(cause: BaitSpawnCause, world: ServerLevel, pos: BlockPos): BaitSpawningContext? {
|
||||
// Maybe confirm that it's water we're fishing in?
|
||||
val ctx = BaitSpawningContext(
|
||||
cause = cause,
|
||||
world = world,
|
||||
pos = pos,
|
||||
influences = influences
|
||||
)
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.BaitSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
/**
|
||||
* A spawning condition that applies to [BaitSpawningContext]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class BaitSpawningCondition: SpawningCondition<BaitSpawningContext>() {
|
||||
override fun contextClass() = BaitSpawningContext::class.java
|
||||
|
||||
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
var baitType: ResourceLocation? = null
|
||||
|
||||
override fun fits(ctx: BaitSpawningContext): Boolean {
|
||||
if (!super.fits(ctx)) {
|
||||
return false
|
||||
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (baitType != null) { // check for the type of bait being used
|
||||
val baitItem = ctx.baitItem
|
||||
if (baitItem != baitType) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*if (ctx is FishingSpawningContext && (ctx as FishingSpawningContext).rodItem != null) { // check if the bait attracts certain EV yields
|
||||
val pokerodItem = (ctx as FishingSpawningContext).rodItem
|
||||
|
||||
// todo check if the EV yield of the berry matches the bait EV attract maybe?
|
||||
|
||||
if (// todo if bait EV yield != EV yield of pokemon consideration //Registries.ITEM.getId(pokerodItem?.bait?.item).path == )
|
||||
return false
|
||||
}*/
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "bait"
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.context
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.bait.BaitSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
/**
|
||||
* A spawning context that was generated for a Bait action.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 3rd, 2024
|
||||
*/
|
||||
class BaitSpawningContext(
|
||||
cause: BaitSpawnCause,
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
influences: MutableList<SpawningInfluence>
|
||||
) : TriggerSpawningContext(
|
||||
cause = cause,
|
||||
world = world,
|
||||
position = pos,
|
||||
light = world.getMaxLocalRawBrightness(pos),
|
||||
skyLight = world.getMaxLocalRawBrightness(pos.above()),
|
||||
canSeeSky = world.canSeeSkyFromBelowWater(pos),
|
||||
influences = influences
|
||||
) {
|
||||
val nearbyBlocks = world.getBlockStates(AABB.ofSize(pos.toVec3d(), 10.0, 10.0, 10.0))
|
||||
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.map { it.block }.distinct().toList() }
|
||||
val baitItem = cause.baitStack
|
||||
}
|
||||
+6
@@ -10,6 +10,9 @@ package com.cobblemon.mod.common.api.spawning.fishing
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BobberSpawnPokemonEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
@@ -26,9 +29,11 @@ import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem.Companion.getCookingComponentOnRod
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
@@ -133,6 +138,7 @@ class FishingSpawnCause(
|
||||
}
|
||||
|
||||
val rodItem = rodStack.item as? PokerodItem
|
||||
// Determine the combined bait effects if any
|
||||
val bait = PokerodItem.getBaitOnRod(rodStack)
|
||||
|
||||
override fun affectSpawn(entity: Entity) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import net.minecraft.tags.TagKey
|
||||
object CobblemonBlockTags {
|
||||
|
||||
@JvmField val ALL_HANGING_SIGNS = createTag("all_hanging_signs")
|
||||
@JvmField val ALL_SAPLINGS = createTag("all_saplings")
|
||||
@JvmField val ALL_SIGNS = createTag("all_signs")
|
||||
@JvmField val ANCIENT_CITY_BLOCKS = createTag("ancient_city_blocks")
|
||||
@JvmField val APRICORN_LEAVES = createTag("apricorn_leaves")
|
||||
@@ -56,6 +57,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val MACHINES = createTag("machines")
|
||||
@JvmField val MANSION_BLOCKS = createTag("mansion_blocks")
|
||||
@JvmField val MEDICINAL_LEEK_PLANTABLE = createTag("medicinal_leek_plantable")
|
||||
@JvmField val HEARTY_GRAINS_PLANTABLE = createTag("hearty_grains_plantable")
|
||||
@JvmField val MINTS = createTag("mints")
|
||||
@JvmField val MOON_STONE_ORES = createTag("moon_stone_ores")
|
||||
@JvmField val NATURAL = createTag("natural")
|
||||
@@ -67,6 +69,9 @@ object CobblemonBlockTags {
|
||||
@JvmField val REDSTONE_BLOCKS = createTag("redstone_blocks")
|
||||
@JvmField val ROOTS = createTag("roots")
|
||||
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
|
||||
@JvmField val SACCHARINE_LEAVES = createTag("saccharine_leaves")
|
||||
@JvmField val SACCHARINE_LOGS = createTag("saccharine_logs")
|
||||
@JvmField val SACCHARINE_SAPLING = createTag("saccharine_sapling")
|
||||
@JvmField val RUINED_PORTAL_BLOCKS = createTag("ruined_portal_blocks")
|
||||
@JvmField val SEES_SKY = createTag("sees_sky")
|
||||
@JvmField val SHINY_STONE_ORES = createTag("shiny_stone_ores")
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.tags.BlockTags
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.BonemealableBlock
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import kotlin.random.Random
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class BugwortBlock(settings: Properties) : CropBlock(settings), BonemealableBlock {
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(AGE, 0)
|
||||
.setValue(IS_WILD, false))
|
||||
}
|
||||
|
||||
override fun getAgeProperty(): IntegerProperty = AGE
|
||||
|
||||
override fun getMaxAge(): Int = MATURE_AGE
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(this.ageProperty)
|
||||
builder.add(IS_WILD)
|
||||
}
|
||||
|
||||
override fun getBaseSeedId(): ItemLike = CobblemonItems.BUGWORT
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = AGE_TO_SHAPE[this.getAge(state)]
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
// This is specified as growing fast like sugar cane
|
||||
// They have 15 age stages until they grow upwards, this is an attempt at a chance based but likely event
|
||||
if (this.isMaxAge(state) || random.nextInt(4) != 0) {
|
||||
return
|
||||
}
|
||||
this.growCrops(world, pos, state)
|
||||
}
|
||||
|
||||
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
|
||||
// We implement applyGrowth & getGrowthAmount for them
|
||||
override fun isValidBonemealTarget(world: LevelReader, pos: BlockPos, state: BlockState) = state.getValue(BugwortBlock.AGE) < BugwortBlock.MATURE_AGE
|
||||
|
||||
override fun isBonemealSuccess(world: Level, random: RandomSource, pos: BlockPos, state: BlockState) = true
|
||||
|
||||
override fun performBonemeal(world: ServerLevel, random: RandomSource, pos: BlockPos, state: BlockState) {
|
||||
world.setBlock(pos, state.setValue(AGE, state.getValue(AGE) + 1), 2)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
|
||||
val floor = world.getBlockState(pos.below())
|
||||
return (world.getRawBrightness(pos, 0) >= 8 || world.canSeeSky(pos)) && ((this.isWild(state) && (floor.`is`(BlockTags.DIRT) || floor.`is`(Blocks.FARMLAND)) || this.mayPlaceOn(floor, world, pos)))
|
||||
}
|
||||
|
||||
fun isWild(state: BlockState): Boolean = state.getValue(IS_WILD)
|
||||
override fun mayPlaceOn(state: BlockState, world: BlockGetter, pos: BlockPos): Boolean {
|
||||
val blockPos = pos!!.below()
|
||||
val down = pos.below()
|
||||
val floor = world.getBlockState(down)
|
||||
return floor.`is`(BlockTags.DIRT) || floor.`is`(Blocks.FARMLAND)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (state != null && world != null && pos != null) {
|
||||
// if bugwort is at max age then revert age to 0 and drop items
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
return InteractionResult.PASS
|
||||
} else if (this.isMaxAge(state)) {
|
||||
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
|
||||
world.setBlock(pos, state.setValue(this.ageProperty, (0).coerceAtMost(this.maxAge)), UPDATE_CLIENTS)
|
||||
Block.popResource(world, pos, ItemStack(CobblemonItems.BUGWORT, Random.nextInt(2, 4)))
|
||||
return InteractionResult.sidedSuccess(world.isClientSide)
|
||||
}
|
||||
}
|
||||
return super.useWithoutItem(state, world, pos, player, hit)
|
||||
}
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
|
||||
companion object {
|
||||
|
||||
const val MATURE_AGE = 3
|
||||
val AGE: IntegerProperty = BlockStateProperties.AGE_3
|
||||
val IS_WILD: BooleanProperty = BooleanProperty.create("is_wild")
|
||||
val AGE_TO_SHAPE = arrayOf(
|
||||
box(0.0, 0.0, 0.0, 16.0, 2.0, 16.0),
|
||||
box(0.0, 0.0, 0.0, 16.0, 5.0, 16.0),
|
||||
box(0.0, 0.0, 0.0, 16.0, 8.0, 16.0),
|
||||
box(0.0, 0.0, 0.0, 16.0, 11.0, 16.0)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
|
||||
companion object {
|
||||
val SHAPE_BY_BITE = arrayOf(
|
||||
box(1.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(3.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(5.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(7.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(9.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(11.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(13.0, 0.0, 1.0, 15.0, 9.0, 15.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity ?: return ItemStack.EMPTY
|
||||
return blockEntity.toItemStack()
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape? {
|
||||
return SHAPE_BY_BITE[getBites(level, pos)]
|
||||
}
|
||||
|
||||
override fun spawnDestroyParticles(
|
||||
level: Level,
|
||||
player: Player,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
val color = blockEntity?.cookingComponent?.seasoning3?.color
|
||||
val woolBlock = when (color?.lowercase()) {
|
||||
"red" -> Blocks.RED_WOOL
|
||||
"orange" -> Blocks.ORANGE_WOOL
|
||||
"yellow" -> Blocks.YELLOW_WOOL
|
||||
"lime" -> Blocks.LIME_WOOL
|
||||
"green" -> Blocks.GREEN_WOOL
|
||||
"cyan" -> Blocks.CYAN_WOOL
|
||||
"light blue" -> Blocks.LIGHT_BLUE_WOOL
|
||||
"blue" -> Blocks.BLUE_WOOL
|
||||
"purple" -> Blocks.PURPLE_WOOL
|
||||
"magenta" -> Blocks.MAGENTA_WOOL
|
||||
"pink" -> Blocks.PINK_WOOL
|
||||
"white" -> Blocks.WHITE_WOOL
|
||||
else -> Blocks.WHITE_WOOL
|
||||
}
|
||||
|
||||
level.levelEvent(player, 2001, pos, getId(woolBlock.defaultBlockState()))
|
||||
}
|
||||
|
||||
fun getCookingComponent(level: BlockGetter, pos: BlockPos): CookingComponent? {
|
||||
return (level.getBlockEntity(pos) as? CakeBlockEntity)?.cookingComponent
|
||||
}
|
||||
|
||||
fun setCookingComponent(level: BlockGetter, pos: BlockPos, cookingComponent: CookingComponent) {
|
||||
(level.getBlockEntity(pos) as? CakeBlockEntity)?.cookingComponent = cookingComponent
|
||||
}
|
||||
|
||||
fun getBites(level: BlockGetter, pos: BlockPos): Int = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites ?: 0
|
||||
fun setBites(level: BlockGetter, pos: BlockPos, bites: Int) = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites = bites
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.item.CampfirePotItem
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.Containers
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock.FACING
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import net.minecraft.world.level.block.CampfireBlock as MCCampfireBlock
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::CampfireBlock)
|
||||
val ITEM_DIRECTION = DirectionProperty.create("item_facing")
|
||||
val LIT = BlockStateProperties.LIT
|
||||
var SOUL = BooleanProperty.create("soul")
|
||||
|
||||
private val campfireAABB = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0)
|
||||
private val AABB = Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0),
|
||||
Shapes.box(0.1875, 0.5, 0.125, 0.875, 0.8125, 0.1875),
|
||||
Shapes.box(0.125, 0.4375, 0.125, 0.875, 0.5, 0.875),
|
||||
Shapes.box(0.8125, 0.5, 0.1875, 0.875, 0.8125, 0.875),
|
||||
Shapes.box(0.125, 0.5, 0.125, 0.1875, 0.8125, 0.8125),
|
||||
Shapes.box(0.125, 0.5, 0.8125, 0.8125, 0.8125, 0.875)
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(LIT, true)
|
||||
.setValue(SOUL, false)
|
||||
.setValue(ITEM_DIRECTION, Direction.NORTH))
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
var blockState = defaultBlockState()
|
||||
val worldView = ctx.level
|
||||
val blockPos = ctx.clickedPos
|
||||
ctx.nearestLookingDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState
|
||||
.setValue(FACING, direction)
|
||||
.setValue(ITEM_DIRECTION, direction)
|
||||
as BlockState
|
||||
if (blockState.canSurvive(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getShape(blockState: BlockState, blockGetter: BlockGetter, blockPos: BlockPos, collisionContext: CollisionContext): VoxelShape = campfireAABB
|
||||
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = AABB
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (blockEntity is CampfireBlockEntity) {
|
||||
val potItem = blockEntity.getPotItem()
|
||||
if (!potItem?.isEmpty!! && potItem.item is CampfirePotItem) {
|
||||
if (!level.isClientSide) {
|
||||
if (player.isCrouching) {
|
||||
// Remove the pot item and give it to the player
|
||||
removePotItem(blockEntity, state, level, pos, player)
|
||||
} else {
|
||||
// Open the cooking pot's container
|
||||
openContainer(level, pos, player)
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
} else if (player.getItemInHand(InteractionHand.MAIN_HAND).item is CampfirePotItem) {
|
||||
// Add the pot item to the block entity
|
||||
if (potItem.isEmpty) {
|
||||
val heldItem = player.getItemInHand(InteractionHand.MAIN_HAND)
|
||||
blockEntity.setPotItem(heldItem.split(1))
|
||||
level.playSoundServer(
|
||||
position = pos.bottomCenter,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_PLACE,
|
||||
)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
return InteractionResult.PASS
|
||||
}
|
||||
|
||||
override fun placeLiquid(level: LevelAccessor, pos: BlockPos, state: BlockState, fluidState: FluidState): Boolean {
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (fluidState.type === Fluids.WATER && blockEntity is CampfireBlockEntity) {
|
||||
if (!level.isClientSide) {
|
||||
removePotItem(blockEntity, state, level as Level, pos, null, true)
|
||||
level.playSoundServer(pos.center, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0F, 1.0F)
|
||||
}
|
||||
|
||||
if (level.isClientSide) for (i in 0..19) MCCampfireBlock.makeParticles(level as Level, pos, false, true)
|
||||
|
||||
level.scheduleTick(pos, fluidState.type, fluidState.type.getTickDelay(level))
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun removePotItem(blockEntity: CampfireBlockEntity, blockState: BlockState, level: Level, blockPos: BlockPos, player: Player? = null, byWater: Boolean = false) {
|
||||
if (!byWater && player != null && !player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty) return
|
||||
|
||||
val potItem = blockEntity.getPotItem()
|
||||
|
||||
if (!byWater && player != null) {
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, potItem)
|
||||
} else {
|
||||
val direction = blockState.getValue(FACING) as Direction
|
||||
val f = 0.25F * direction.stepX.toFloat()
|
||||
val g = 0.25F * direction.stepZ.toFloat()
|
||||
|
||||
val itemEntity = ItemEntity(level, blockPos.x.toDouble() + 0.5 + f.toDouble(), (blockPos.y + 1).toDouble(), blockPos.z.toDouble() + 0.5 + g.toDouble(), potItem)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
|
||||
blockEntity.setPotItem(ItemStack.EMPTY)
|
||||
level.playSoundServer(blockPos.bottomCenter, CobblemonSounds.CAMPFIRE_POT_RETRIEVE)
|
||||
|
||||
blockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
Containers.dropContents(level, blockPos, blockEntity)
|
||||
|
||||
val facing = blockState.getValue(FACING)
|
||||
val isSoul = blockState.getValue(SOUL)
|
||||
blockEntity.setRemoved()
|
||||
|
||||
val newBlockState = if (isSoul) Blocks.SOUL_CAMPFIRE.defaultBlockState().setValue(FACING, facing)
|
||||
else Blocks.CAMPFIRE.defaultBlockState().setValue(FACING, facing)
|
||||
|
||||
level.setBlockAndUpdate(blockPos, if (byWater) newBlockState.setValue(BlockStateProperties.WATERLOGGED, true).setValue(MCCampfireBlock.LIT, false) else newBlockState)
|
||||
}
|
||||
|
||||
fun openContainer(level: Level, pos: BlockPos, player: Player) {
|
||||
val blockEntity = level.getBlockEntity(pos)
|
||||
if (blockEntity is CampfireBlockEntity) player.openMenu(blockEntity)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, ITEM_DIRECTION, LIT, SOUL)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: LevelAccessor,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
return if (direction == state.getValue(FACING) && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
|
||||
|
||||
override fun getAnalogOutputSignal(state: BlockState, world: Level, pos: BlockPos): Int {
|
||||
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
|
||||
|
||||
if (stack.isEmpty) return 0
|
||||
if (stack.item is PokeBallItem) return 3
|
||||
if (stack.item is BlockItem) return 2
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState): Boolean = true
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(
|
||||
level: Level,
|
||||
state: BlockState,
|
||||
blockEntityType: BlockEntityType<T?>
|
||||
): BlockEntityTicker<T?>? {
|
||||
return createCookingPotTicker(level, blockEntityType as BlockEntityType<*>, CobblemonBlockEntities.CAMPFIRE) as BlockEntityTicker<T?>?
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected fun <T : BlockEntity> createCookingPotTicker(
|
||||
level: Level,
|
||||
serverType: BlockEntityType<T>,
|
||||
clientType: BlockEntityType<out CampfireBlockEntity>
|
||||
): BlockEntityTicker<T>? {
|
||||
return if (level.isClientSide) createTickerHelper(serverType, clientType, CampfireBlockEntity::clientTick)
|
||||
else createTickerHelper(serverType, clientType, CampfireBlockEntity::serverTick)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
): BlockEntity? {
|
||||
return CampfireBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun animateTick(state: BlockState, level: Level, pos: BlockPos, random: RandomSource) {
|
||||
if (random.nextInt(10) == 0) {
|
||||
level.playLocalSound(
|
||||
pos.x.toDouble() + 0.5,
|
||||
pos.y.toDouble() + 0.5,
|
||||
pos.z.toDouble() + 0.5,
|
||||
SoundEvents.CAMPFIRE_CRACKLE,
|
||||
SoundSource.BLOCKS,
|
||||
0.5f + random.nextFloat(),
|
||||
random.nextFloat() * 0.7f + 0.6f,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player): BlockState {
|
||||
if (!level.isClientSide) {
|
||||
val blockEntity = level.getBlockEntity(blockPos)
|
||||
if (blockEntity is CampfireBlockEntity && !player.isCreative) {
|
||||
Containers.dropContents(level, blockPos, blockEntity)
|
||||
val potItem = blockEntity.getPotItem() ?: ItemStack.EMPTY
|
||||
|
||||
if (!potItem.isEmpty) {
|
||||
val direction = blockState.getValue(FACING) as Direction
|
||||
val f = 0.25F * direction.stepX.toFloat()
|
||||
val g = 0.25F * direction.stepZ.toFloat()
|
||||
|
||||
val itemEntity = ItemEntity(level, blockPos.x.toDouble() + 0.5 + f.toDouble(), (blockPos.y + 1).toDouble(), blockPos.z.toDouble() + 0.5 + g.toDouble(), potItem)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
level.playSoundServer(position = blockPos.toVec3d(), sound = CobblemonSounds.CAMPFIRE_POT_OPEN, volume = 0.25F)
|
||||
|
||||
return super.playerWillDestroy(level, blockPos, blockState, player)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
return if (state.getValue(SOUL)) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class CampfirePotBlock(settings: Properties) : HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<CampfirePotBlock> = RecordCodecBuilder.mapCodec { it.group(propertiesCodec()).apply(it, ::CampfirePotBlock) }
|
||||
var OPEN = BooleanProperty.create("open")
|
||||
|
||||
private val AABB_NS = Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.375, 0.875),
|
||||
Shapes.box(0.0, 0.3125, 0.375, 0.125, 0.375, 0.625),
|
||||
Shapes.box(0.875, 0.3125, 0.375, 1.0, 0.375, 0.625),
|
||||
Shapes.box(0.4375, 0.375, 0.4375, 0.5625, 0.4375, 0.5625)
|
||||
)
|
||||
|
||||
private val AABB_WE = Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.375, 0.875),
|
||||
Shapes.box(0.375, 0.3125, 0.0, 0.625, 0.375, 0.125),
|
||||
Shapes.box(0.375, 0.3125, 0.875, 0.625, 0.375, 1.0),
|
||||
Shapes.box(0.4375, 0.375, 0.4375, 0.5625, 0.4375, 0.5625),
|
||||
)
|
||||
|
||||
private val AABB_NS_OPEN = Shapes.or(
|
||||
Shapes.box(0.1875, 0.0625, 0.125, 0.875, 0.375, 0.1875),
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.0625, 0.875),
|
||||
Shapes.box(0.8125, 0.0625, 0.1875, 0.875, 0.375, 0.875),
|
||||
Shapes.box(0.125, 0.0625, 0.125, 0.1875, 0.375, 0.8125),
|
||||
Shapes.box(0.125, 0.0625, 0.8125, 0.8125, 0.375, 0.875),
|
||||
Shapes.box(0.0, 0.3125, 0.375, 0.125, 0.375, 0.625),
|
||||
Shapes.box(0.875, 0.3125, 0.375, 1.0, 0.375, 0.625)
|
||||
)
|
||||
|
||||
private val AABB_WE_OPEN = Shapes.or(
|
||||
Shapes.box(0.1875, 0.0625, 0.125, 0.875, 0.375, 0.1875),
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.0625, 0.875),
|
||||
Shapes.box(0.8125, 0.0625, 0.1875, 0.875, 0.375, 0.875),
|
||||
Shapes.box(0.125, 0.0625, 0.125, 0.1875, 0.375, 0.8125),
|
||||
Shapes.box(0.125, 0.0625, 0.8125, 0.8125, 0.375, 0.875),
|
||||
Shapes.box(0.375, 0.3125, 0.0, 0.625, 0.375, 0.125),
|
||||
Shapes.box(0.375, 0.3125, 0.875, 0.625, 0.375, 1.0)
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(OPEN, false)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
val open = state.getValue(OPEN)
|
||||
return if (open)
|
||||
if (direction == Direction.NORTH || direction == Direction.SOUTH) AABB_NS_OPEN else AABB_WE_OPEN
|
||||
else
|
||||
if (direction == Direction.NORTH || direction == Direction.SOUTH) AABB_NS else AABB_WE
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
var blockState = defaultBlockState().setValue(WATERLOGGED, ctx.level.getFluidState(ctx.clickedPos).type == Fluids.WATER)
|
||||
val worldView = ctx.level
|
||||
val blockPos = ctx.clickedPos
|
||||
ctx.nearestLookingDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState.setValue(FACING, direction.opposite) as BlockState
|
||||
if (blockState.canSurvive(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out HorizontalDirectionalBlock> = CODEC
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
|
||||
return if (direction == state.getValue(FACING) && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
builder.add(OPEN)
|
||||
builder.add(WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.CandlePokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.block.CakeBlockEntityRenderer.Companion.TOP_LAYER_HEIGHT
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.AbstractCandleBlock
|
||||
import net.minecraft.world.level.block.AbstractCandleBlock.extinguish
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks.*
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val LIT = BlockStateProperties.LIT
|
||||
val CANDLE_COLOR = IntegerProperty.create("candle_color", 0, MapColor.MATERIAL_COLORS.size - 1)
|
||||
|
||||
fun getCandleByMapColorId(id: Int): CandleBlock {
|
||||
return when (id) {
|
||||
MapColor.SAND.id -> CANDLE
|
||||
MapColor.WOOL.id -> WHITE_CANDLE
|
||||
MapColor.COLOR_ORANGE.id -> ORANGE_CANDLE
|
||||
MapColor.COLOR_MAGENTA.id -> MAGENTA_CANDLE
|
||||
MapColor.COLOR_LIGHT_BLUE.id -> LIGHT_BLUE_CANDLE
|
||||
MapColor.COLOR_YELLOW.id -> YELLOW_CANDLE
|
||||
MapColor.COLOR_LIGHT_GREEN.id -> LIME_CANDLE
|
||||
MapColor.COLOR_PINK.id -> PINK_CANDLE
|
||||
MapColor.COLOR_GRAY.id -> GRAY_CANDLE
|
||||
MapColor.COLOR_LIGHT_GRAY.id -> LIGHT_GRAY_CANDLE
|
||||
MapColor.COLOR_CYAN.id -> CYAN_CANDLE
|
||||
MapColor.COLOR_PURPLE.id -> PURPLE_CANDLE
|
||||
MapColor.COLOR_BLUE.id -> BLUE_CANDLE
|
||||
MapColor.COLOR_BROWN.id -> BROWN_CANDLE
|
||||
MapColor.COLOR_GREEN.id -> GREEN_CANDLE
|
||||
MapColor.COLOR_RED.id -> RED_CANDLE
|
||||
MapColor.COLOR_BLACK.id -> BLACK_CANDLE
|
||||
else -> CANDLE
|
||||
} as CandleBlock
|
||||
}
|
||||
|
||||
val CANDLE_SHAPE: VoxelShape = box(7.0, 8.0, 7.0, 9.0, 15.0, 9.0)
|
||||
val SHAPE: VoxelShape = Shapes.or(SHAPE_BY_BITE[0], CANDLE_SHAPE)
|
||||
val PARTICLE_OFFSET = Vec3(0.5, 0.5 + TOP_LAYER_HEIGHT, 0.5)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(LIT, false)
|
||||
.setValue(CANDLE_COLOR, MapColor.SAND.id)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?>? {
|
||||
return simpleCodec(::CandlePokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
|
||||
return CandlePokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(LIT, CANDLE_COLOR)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult? {
|
||||
return if (!stack.`is`(Items.FLINT_AND_STEEL) && !stack.`is`(Items.FIRE_CHARGE)) {
|
||||
if (candleHit(hitResult) && stack.isEmpty && state.getValue(LIT) == true) {
|
||||
extinguish(player, state, level, pos)
|
||||
level.addParticle(ParticleTypes.SMOKE, PARTICLE_OFFSET.x + pos.x, pos.y + PARTICLE_OFFSET.y, pos.z + PARTICLE_OFFSET.z, 0.0, 0.1, 0.0)
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||
}
|
||||
} else if (state.getValue(LIT) == false) {
|
||||
level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0f, level.random.nextFloat() * 0.4f + 0.8f)
|
||||
level.setBlock(pos, state.setValue(BlockStateProperties.LIT, true), UPDATE_ALL_IMMEDIATE)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
player.getItemInHand(hand).hurtAndBreak(1, player, hand.toEquipmentSlot())
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult? {
|
||||
val oldCookingComponent = getCookingComponent(level, pos)
|
||||
val candle = getCandleByMapColorId(state.getValue(CANDLE_COLOR))
|
||||
|
||||
val newBlockState = CobblemonBlocks.POKE_CAKE.defaultBlockState()
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
val pokeCakeBlock = newBlockState.block as PokeCakeBlock
|
||||
oldCookingComponent?.let {
|
||||
pokeCakeBlock.setCookingComponent(level, pos, it)
|
||||
}
|
||||
|
||||
val interactionResult = pokeCakeBlock.eat(level, pos, player)
|
||||
if (interactionResult.consumesAction()) {
|
||||
popResource(level, pos, ItemStack(candle.asItem()))
|
||||
}
|
||||
|
||||
return interactionResult
|
||||
}
|
||||
|
||||
private fun candleHit(hit: BlockHitResult): Boolean {
|
||||
return hit.location.y - hit.blockPos.y.toDouble() > TOP_LAYER_HEIGHT
|
||||
}
|
||||
|
||||
override fun animateTick(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
random: RandomSource
|
||||
) {
|
||||
if (state.getValue(LIT)) {
|
||||
AbstractCandleBlock.addParticlesAndSound(level, Vec3(PARTICLE_OFFSET.x + pos.x, PARTICLE_OFFSET.y + pos.y, PARTICLE_OFFSET.z + pos.z), random)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape? = SHAPE
|
||||
}
|
||||
@@ -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.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWaterloggedBlock {
|
||||
|
||||
override fun getAgeProperty(): IntegerProperty = AGE
|
||||
|
||||
override fun getMaxAge(): Int = MATURE_AGE
|
||||
|
||||
private val ageAfterHarvest = 3
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(AGE, PART, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getBaseSeedId(): ItemLike = CobblemonItems.Hearty_Grains
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return AGE_TO_SHAPE[this.getAge(state)]
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state.getValue(WATERLOGGED)) Fluids.WATER.defaultFluidState() else Fluids.EMPTY.defaultFluidState()
|
||||
}
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (state.getValue(PART) == Part.UPPER || this.isMaxAge(state) || !canGrow(world, pos)) return
|
||||
|
||||
val lightLevel = world.getMaxLocalRawBrightness(pos)
|
||||
if (lightLevel >= 9 && random.nextInt(7) == 0) {
|
||||
val newAge = state.getValue(AGE) + 1
|
||||
world.setBlock(pos, state.setValue(AGE, newAge), UPDATE_CLIENTS)
|
||||
|
||||
if (newAge == MATURE_AGE) {
|
||||
val abovePos = pos.above()
|
||||
if (world.getBlockState(abovePos).isAir) {
|
||||
world.setBlock(
|
||||
abovePos,
|
||||
this.defaultBlockState().setValue(AGE, newAge).setValue(PART, Part.UPPER),
|
||||
UPDATE_CLIENTS
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun canGrow(world: LevelReader, pos: BlockPos): Boolean {
|
||||
return canSurvive(defaultBlockState().setValue(PART, Part.LOWER), world, pos)
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
if (state.getValue(PART) == Part.UPPER && state.block is HeartyGrainsBlock) {
|
||||
val belowPos = pos.below()
|
||||
val belowState = world.getBlockState(belowPos)
|
||||
world.destroyBlock(pos, true) // Break the lower block without drops
|
||||
world.setBlock(
|
||||
belowPos,
|
||||
belowState.setValue(AGE, ageAfterHarvest), // Reset age to simulate regrowth preparation
|
||||
UPDATE_CLIENTS
|
||||
)
|
||||
} else if (state.getValue(PART) == Part.LOWER) {
|
||||
val abovePos = pos.above()
|
||||
val aboveState = world.getBlockState(abovePos)
|
||||
if (aboveState.block is HeartyGrainsBlock && aboveState.getValue(PART) == Part.UPPER) {
|
||||
world.destroyBlock(abovePos, true) // Break the upper block without drops
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: LevelAccessor,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (!canSurvive(state, world, pos)) Blocks.AIR.defaultBlockState() else super.updateShape(
|
||||
state,
|
||||
direction,
|
||||
neighborState,
|
||||
world,
|
||||
pos,
|
||||
neighborPos
|
||||
)
|
||||
}
|
||||
|
||||
override fun isValidBonemealTarget(world: LevelReader, pos: BlockPos, state: BlockState): Boolean {
|
||||
return !this.isMaxAge(state) && state.getValue(PART) == Part.LOWER
|
||||
}
|
||||
|
||||
override fun growCrops(world: Level, pos: BlockPos, state: BlockState) {
|
||||
if (!canGrow(world, pos)) return
|
||||
|
||||
val newAge = (this.getAge(state) + this.getBonemealAgeIncrease(world)).coerceAtMost(this.maxAge)
|
||||
world.setBlock(pos, state.setValue(this.ageProperty, newAge), UPDATE_CLIENTS)
|
||||
|
||||
if (newAge == MATURE_AGE && state.getValue(PART) == Part.LOWER) {
|
||||
val abovePos = pos.above()
|
||||
if (world.getBlockState(abovePos).isAir) {
|
||||
world.setBlock(
|
||||
abovePos,
|
||||
this.defaultBlockState().setValue(AGE, newAge).setValue(PART, Part.UPPER),
|
||||
UPDATE_CLIENTS
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
|
||||
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
|
||||
return when (state.getValue(PART)) {
|
||||
Part.LOWER -> {
|
||||
val floor = world.getBlockState(pos.below())
|
||||
val fluidState = world.getFluidState(pos)
|
||||
|
||||
// Check if bottom part is submerged in water
|
||||
val isSubmergedInWater = fluidState.`is`(FluidTags.WATER) && fluidState.isSource
|
||||
|
||||
// Check for adjacency to water
|
||||
val isNearWater = BlockPos.betweenClosed(pos.offset(-1, 0, -1), pos.offset(1, 0, 1))
|
||||
.any { world.getFluidState(it).`is`(FluidTags.WATER) }
|
||||
|
||||
// Check waterlogging state
|
||||
val isWaterlogged = state.getValue(WATERLOGGED)
|
||||
|
||||
// Check for valid soil or survival conditions
|
||||
isSubmergedInWater || isWaterlogged || isNearWater || floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_PLANTABLE) || floor.`is`(Blocks.MUD) || floor.`is`(Blocks.FARMLAND)
|
||||
}
|
||||
Part.UPPER -> {
|
||||
val below = world.getBlockState(pos.below())
|
||||
below.block is HeartyGrainsBlock && below.getValue(PART) == Part.LOWER
|
||||
// Upper part survives only if the lower part is valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun codec(): MapCodec<out CropBlock> = CODEC
|
||||
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::HeartyGrainsBlock)
|
||||
const val MATURE_AGE = 4
|
||||
val WATERLOGGED = BlockStateProperties.WATERLOGGED
|
||||
val AGE: IntegerProperty = BlockStateProperties.AGE_4
|
||||
val PART: EnumProperty<Part> = EnumProperty.create("part", Part::class.java)
|
||||
|
||||
val AGE_TO_SHAPE = arrayOf(
|
||||
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 1
|
||||
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 2
|
||||
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 3
|
||||
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0), // Stage 4 (Bottom)
|
||||
box(2.0, 0.0, 0.0, 16.0, 17.0, 16.0) // Stage 5 (Bottom)
|
||||
)
|
||||
}
|
||||
|
||||
enum class Part : StringRepresentable {
|
||||
LOWER, UPPER;
|
||||
|
||||
override fun getSerializedName(): String = name.lowercase()
|
||||
}
|
||||
|
||||
init {
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(AGE, 0)
|
||||
.setValue(PART, Part.LOWER)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -113,11 +113,7 @@ class LecternBlock(properties: Properties): BaseEntityBlock(properties) {
|
||||
return super.playerWillDestroy(level, blockPos, blockState, player)
|
||||
}
|
||||
|
||||
override fun getDrops(blockState: BlockState, builder: LootParams.Builder): MutableList<ItemStack> {
|
||||
return mutableListOf(ItemStack(Blocks.LECTERN))
|
||||
}
|
||||
|
||||
override fun useWithoutItem(blockState: BlockState, level: Level, blockPos: BlockPos, player: Player, blockHitResult: BlockHitResult?): InteractionResult {
|
||||
override fun useWithoutItem(blockState: BlockState, level: Level, blockPos: BlockPos, player: Player, blockHitResult: BlockHitResult): InteractionResult {
|
||||
val blockEntity = level.getBlockEntity(blockPos)
|
||||
if (blockEntity is LecternBlockEntity) {
|
||||
val itemStack = blockEntity.getItemStack()
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitConsumedEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitSetEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBaits
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity.Companion.MAX_NUMBER_OF_BITES
|
||||
import com.cobblemon.mod.common.block.entity.LureCakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.RodBaitComponent
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
|
||||
companion object {
|
||||
fun getBaitOnLureCake(stack: ItemStack): FishingBait? {
|
||||
return getCookingComponentOnRod(stack) ?: stack.components.get(CobblemonItemComponents.BAIT)?.bait
|
||||
}
|
||||
|
||||
fun getBaitStackOnRod(stack: ItemStack): ItemStack {
|
||||
return stack.components.get(CobblemonItemComponents.BAIT)?.stack ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
fun getCookingComponentOnRod(rodStack: ItemStack): FishingBait? {
|
||||
// Check if the stack within the RodBaitComponent has a CookingComponent
|
||||
val cookingComponent = rodStack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return null
|
||||
|
||||
// Combine effects from the CookingComponent
|
||||
val combinedEffects = listOf(
|
||||
cookingComponent.bait1.effects,
|
||||
cookingComponent.bait2.effects,
|
||||
cookingComponent.bait3.effects
|
||||
).flatten()
|
||||
|
||||
// Return a new FishingBait with combined effects
|
||||
return FishingBait(
|
||||
item = BuiltInRegistries.ITEM.getKey(
|
||||
rodStack.components.get(CobblemonItemComponents.BAIT)?.stack?.item ?: ItemStack.EMPTY.item
|
||||
), // Use the rodStack's item as the bait identifier
|
||||
effects = combinedEffects
|
||||
)
|
||||
}
|
||||
|
||||
fun setBait(stack: ItemStack, bait: ItemStack) {
|
||||
CobblemonEvents.BAIT_SET.postThen(BaitSetEvent(stack, bait), { event -> }, {
|
||||
if (bait.isEmpty) {
|
||||
stack.set<RodBaitComponent>(CobblemonItemComponents.BAIT, null)
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve FishingBait and CookingComponent from the bait ItemStack
|
||||
val fishingBait = FishingBaits.getFromBaitItemStack(bait) ?: return
|
||||
val cookingComponent = bait.get(CobblemonItemComponents.COOKING_COMPONENT)
|
||||
|
||||
// Apply both RodBaitComponent and CookingComponent to the rod ItemStack
|
||||
stack.set(CobblemonItemComponents.BAIT, RodBaitComponent(fishingBait, bait))
|
||||
if (cookingComponent != null) {
|
||||
stack.set(CobblemonItemComponents.COOKING_COMPONENT, cookingComponent)
|
||||
} else {
|
||||
// Clear CookingComponent if the new bait does not have it
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun consumeBait(stack: ItemStack) {
|
||||
CobblemonEvents.BAIT_CONSUMED.postThen(BaitConsumedEvent(stack), { event -> }, {
|
||||
val baitStack = getBaitStackOnRod(stack)
|
||||
val baitCount = baitStack.count
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT)
|
||||
|
||||
if (baitCount == 1) {
|
||||
stack.set<RodBaitComponent>(CobblemonItemComponents.BAIT, null)
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (baitCount > 1) {
|
||||
val fishingBait = getBaitOnLureCake(stack) ?: return
|
||||
stack.set<RodBaitComponent>(
|
||||
CobblemonItemComponents.BAIT,
|
||||
RodBaitComponent(fishingBait, ItemStack(baitStack.item, baitCount - 1))
|
||||
)
|
||||
if (cookingComponent != null) {
|
||||
stack.set(CobblemonItemComponents.COOKING_COMPONENT, cookingComponent)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getBaitEffects(stack: ItemStack): List<FishingBait.Effect> {
|
||||
return getBaitOnLureCake(stack)?.effects ?: return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<LureCakeBlock> {
|
||||
return simpleCodec(::LureCakeBlock)
|
||||
}
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
println("randomTick triggered for LureCakeBlock at $pos")
|
||||
|
||||
// Check if block has a BlockEntity
|
||||
val blockEntity = world.getBlockEntity(pos) as? LureCakeBlockEntity
|
||||
if (blockEntity == null) {
|
||||
println("No LureCakeBlockEntity found at $pos")
|
||||
return
|
||||
}
|
||||
|
||||
// Attempt to spawn Pokémon in a 5-block radius
|
||||
val randomXOffset = random.nextInt(-5, 6)
|
||||
val randomZOffset = random.nextInt(-5, 6)
|
||||
val spawnPos = pos.offset(randomXOffset, 0, randomZOffset)
|
||||
|
||||
println("Attempting to spawn Pokémon near $pos at $spawnPos")
|
||||
val spawnedPokemon = blockEntity.spawnPokemon(world, spawnPos)
|
||||
if (spawnedPokemon != null) {
|
||||
println("Pokémon spawned successfully at $spawnPos")
|
||||
|
||||
// Increment the age of the block
|
||||
val newBites = getBites(world, pos) + 1
|
||||
if (newBites >= MAX_NUMBER_OF_BITES) {
|
||||
println("Block at $pos has reached max age. Breaking block.")
|
||||
world.destroyBlock(pos, true) // Break the block and drop its items
|
||||
} else {
|
||||
setBites(world, pos, newBites)
|
||||
println("Block at $pos aged to $newBites")
|
||||
}
|
||||
|
||||
world.sendBlockUpdated(pos, state, state, UPDATE_CLIENTS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return LureCakeBlockEntity(pos, state)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionHand.MAIN_HAND
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
||||
class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?>? {
|
||||
return simpleCodec(::PokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
|
||||
return PokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeCakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult? {
|
||||
if (level.isClientSide) {
|
||||
if (eat(level, pos, player).consumesAction()) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getItemInHand(MAIN_HAND).isEmpty) {
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
|
||||
return eat(level, pos, player)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult? {
|
||||
val item = stack.item
|
||||
val block = state.block as? PokeCakeBlock
|
||||
|
||||
if (stack.`is`(ItemTags.CANDLES) && block?.getBites(level, pos) == 0) {
|
||||
val candleBlock = byItem(item)
|
||||
if (candleBlock is CandleBlock) {
|
||||
stack.shrink(1)
|
||||
level.playSound(null, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
|
||||
val oldCookingComponent = block.getCookingComponent(level, pos)
|
||||
|
||||
val newBlockState = CobblemonBlocks.CANDLE_POKE_CAKE.defaultBlockState()
|
||||
.setValue(CANDLE_COLOR, (item as BlockItem).block.defaultMapColor().id)
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
oldCookingComponent?.let {
|
||||
(newBlockState.block as CakeBlock).setCookingComponent(level, pos, it)
|
||||
}
|
||||
|
||||
player.awardStat(Stats.ITEM_USED[item])
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
|
||||
fun eat(level: LevelAccessor, pos: BlockPos, player: Player): InteractionResult {
|
||||
if (!player.canEat(false)) {
|
||||
return InteractionResult.PASS
|
||||
}
|
||||
|
||||
player.getFoodData().eat(2, 0.1F)
|
||||
level.gameEvent(player, GameEvent.EAT, pos)
|
||||
|
||||
var bites = getBites(level, pos)
|
||||
if (bites < 6) {
|
||||
setBites(level, pos, bites + 1)
|
||||
} else {
|
||||
level.removeBlock(pos, false)
|
||||
level.gameEvent(player, GameEvent.BLOCK_DESTROY, pos)
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.CobblemonItemComponents
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.Optional
|
||||
|
||||
class PotComponent(val potItem: ItemStack) { // No nullable ItemStack
|
||||
|
||||
companion object {
|
||||
// Codec for saving/loading
|
||||
val CODEC: Codec<PotComponent> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
ItemStack.CODEC.fieldOf("potItem").forGetter { it.potItem }
|
||||
).apply(instance) { potItem -> PotComponent(potItem) }
|
||||
}
|
||||
|
||||
// StreamCodec for network synchronization
|
||||
val PACKET_CODEC: StreamCodec<ByteBuf, PotComponent> = ByteBufCodecs.fromCodec(CODEC)
|
||||
|
||||
// Fetch component from stack
|
||||
fun getFrom(stack: ItemStack): PotComponent? {
|
||||
return stack.components.get(CobblemonItemComponents.POT_DATA)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is PotComponent && ItemStack.matches(this.potItem, other.potItem)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return ItemStack.hashItemAndComponents(potItem)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.SaccharineStrippedLogBlock.Companion
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.BlockTags
|
||||
import net.minecraft.util.*
|
||||
import net.minecraft.world.*
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.BonemealableBlock
|
||||
import net.minecraft.world.level.block.LeavesBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.EntityCollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import javax.swing.text.html.BlockView
|
||||
import kotlin.random.Random
|
||||
|
||||
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings), BonemealableBlock {
|
||||
|
||||
init {
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(AGE, MIN_AGE)
|
||||
.setValue(DISTANCE, DISTANCE_MAX)
|
||||
.setValue(PERSISTENT, false)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState {
|
||||
val level = ctx.level
|
||||
val pos = ctx.clickedPos
|
||||
val fluidState = level.getFluidState(pos)
|
||||
return this.defaultBlockState()
|
||||
.setValue(PERSISTENT, true)
|
||||
.setValue(WATERLOGGED, fluidState.`is`(Fluids.WATER))
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, level: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
return if (!state.canSurvive(level, pos)) {
|
||||
Blocks.AIR.defaultBlockState()
|
||||
} else {
|
||||
super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState) = state.getValue(AGE) != 0
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
|
||||
if (random.nextInt(2) == 0) {
|
||||
|
||||
|
||||
for (i in 1..10) {
|
||||
|
||||
val belowPos = pos.below(i)
|
||||
val belowState = world.getBlockState(belowPos)
|
||||
val currentAge = state.getValue(AGE)
|
||||
|
||||
if (belowState.isAir) {
|
||||
continue
|
||||
} else if (belowState.block is SaccharineLeafBlock) {
|
||||
val belowAge = belowState.getValue(AGE)
|
||||
if (belowAge < MAX_AGE) {
|
||||
// Decrease age from the top leaf block
|
||||
val newCurrentAge = (currentAge - 1).coerceAtLeast(MIN_AGE)
|
||||
world.setBlock(pos, state.setValue(AGE, newCurrentAge), 2)
|
||||
|
||||
// Increase age from the bottom leaf block
|
||||
val newBelowAge = (belowAge + 1).coerceAtMost(MAX_AGE)
|
||||
world.setBlock(belowPos, belowState.setValue(AGE, newBelowAge), 2)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.randomTick(state, world, pos, random)
|
||||
}
|
||||
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return SHAPE
|
||||
}
|
||||
// todo make block
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
if (context is EntityCollisionContext && (context.entity as? ItemEntity)?.item?.`is`(CobblemonItemTags.APRICORNS) == true) {
|
||||
return Shapes.empty()
|
||||
}
|
||||
return super.getCollisionShape(state, level, pos, context)
|
||||
}
|
||||
|
||||
override fun isValidBonemealTarget(level: LevelReader, pos: BlockPos, state: BlockState): Boolean = true
|
||||
|
||||
override fun isBonemealSuccess(level: Level, random: RandomSource, pos: BlockPos, state: BlockState) = true
|
||||
|
||||
override fun performBonemeal(level: ServerLevel, random: RandomSource, pos: BlockPos, state: BlockState) {
|
||||
level.setBlock(pos, state.setValue(AGE, state.getValue(AGE) + 1), 2)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(AGE)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun animateTick(state: BlockState, level: Level, pos: BlockPos, random: RandomSource) {
|
||||
|
||||
if (state.getValue(AGE) == 2) {
|
||||
for (i in 0 until random.nextInt(1) + 1) {
|
||||
this.spawnHoneyParticles(level, pos, state, .75f)
|
||||
}
|
||||
} else if (state.getValue(AGE) == 1) {
|
||||
for (i in 0 until random.nextInt(1) + 1) {
|
||||
this.spawnHoneyParticles(level, pos, state, .9f)
|
||||
}
|
||||
}
|
||||
|
||||
// this code was for them aging as time goes on
|
||||
/* if (world.random.nextInt(5) == 0) {
|
||||
|
||||
val currentAge = state.get(AGE)
|
||||
if (currentAge < MAX_AGE) {
|
||||
world.setBlockState(pos, state.setValue(AGE, currentAge + 1), 2)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun spawnHoneyParticles(level: Level, pos: BlockPos, state: BlockState, rate: Float) {
|
||||
if (state.fluidState.isEmpty && !(level.random.nextFloat() < rate)) {
|
||||
val voxelShape = state.getCollisionShape(level, pos)
|
||||
val d = voxelShape.max(Direction.Axis.Y)
|
||||
if (d >= 1.0 && !state.`is`(BlockTags.IMPERMEABLE)) {
|
||||
val e = voxelShape.min(Direction.Axis.Y)
|
||||
if (e > 0.0) {
|
||||
this.addHoneyParticle(level, pos, voxelShape, pos.y.toDouble() + e - 0.05)
|
||||
} else {
|
||||
val blockPos = pos.below()
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
val voxelShape2 = blockState.getCollisionShape(level, blockPos)
|
||||
val f = voxelShape2.max(Direction.Axis.Y)
|
||||
if ((f < 1.0 || !blockState.isSolidRender(level, blockPos)) && blockState.fluidState.isEmpty) {
|
||||
this.addHoneyParticle(level, pos, voxelShape, pos.y.toDouble() - 0.05)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHoneyParticle(level: Level, pos: BlockPos, shape: VoxelShape, height: Double) {
|
||||
this.addHoneyParticle(
|
||||
level, pos.x.toDouble() + shape.min(Direction.Axis.X), pos.x.toDouble() + shape.max(
|
||||
Direction.Axis.X
|
||||
), pos.z.toDouble() + shape.min(Direction.Axis.Z), pos.z.toDouble() + shape.max(
|
||||
Direction.Axis.Z
|
||||
), height
|
||||
)
|
||||
}
|
||||
|
||||
private fun addHoneyParticle(level: Level, minX: Double, maxX: Double, minZ: Double, maxZ: Double, height: Double) {
|
||||
level.addParticle(
|
||||
ParticleTypes.DRIPPING_HONEY,
|
||||
Mth.lerp(level.random.nextDouble(), minX, maxX),
|
||||
height,
|
||||
Mth.lerp(level.random.nextDouble(), minZ, maxZ),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
|
||||
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
|
||||
|
||||
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hit: BlockHitResult): ItemInteractionResult {
|
||||
// todo if item in players hand is glass bottle and AGE is more than 0
|
||||
val itemStack = player.getItemInHand(hand)
|
||||
if (itemStack.`is`(Items.GLASS_BOTTLE) && state.getValue(AGE) > 0)
|
||||
{
|
||||
// decrement stack if not in creative mode
|
||||
if (!player.isCreative)
|
||||
itemStack.shrink(1)
|
||||
|
||||
// give player honey bottle for now
|
||||
player.addItem(Items.HONEY_BOTTLE.defaultInstance)
|
||||
|
||||
// todo reset AGE
|
||||
level.setBlock(pos, state.setValue(AGE, 0), 2)
|
||||
|
||||
val currentAge = state.getValue(AGE)
|
||||
} else if (itemStack.`is`(Items.HONEY_BOTTLE) && state.getValue(AGE) != 2) {
|
||||
// decrement stack if not in creative mode
|
||||
if (!player.isCreative)
|
||||
itemStack.shrink(1)
|
||||
|
||||
// todo set age to 2
|
||||
level.setBlock(pos, state.setValue(AGE, 2), 2)
|
||||
}
|
||||
|
||||
if (state.getValue(AGE) != MAX_AGE) {
|
||||
return super.useItemOn(stack, state, level, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
/*override fun onBlockBreakStart(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity) {
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return super.onBlockBreakStart(state, world, pos, player)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
}*/
|
||||
|
||||
companion object {
|
||||
|
||||
val AGE: IntegerProperty = BlockStateProperties.AGE_2
|
||||
val DISTANCE: IntegerProperty = BlockStateProperties.DISTANCE
|
||||
val PERSISTENT: BooleanProperty = BlockStateProperties.PERSISTENT
|
||||
const val MAX_AGE = 2
|
||||
const val MIN_AGE = 0
|
||||
const val DISTANCE_MAX = 7
|
||||
|
||||
private val SHAPE: VoxelShape = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.block.grower.SaccharineTreeGrower
|
||||
import net.minecraft.world.level.block.SaplingBlock
|
||||
|
||||
class SaccharineSaplingBlock(properties : Properties) : SaplingBlock(SaccharineTreeGrower(), properties)
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.RotatedPillarBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.material.PushReaction
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.EntityCollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class SaccharineStrippedLogBlock(properties: Properties) : RotatedPillarBlock(properties) {
|
||||
|
||||
private var owner: LivingEntity? = null
|
||||
|
||||
init {
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(AGE, MIN_AGE)
|
||||
.setValue(AXIS, Direction.Axis.Y)
|
||||
)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
itemStack: ItemStack
|
||||
) {
|
||||
owner = placer
|
||||
super.setPlacedBy(level, pos, state, placer, itemStack)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean {
|
||||
return state.getValue(AGE) < MAX_AGE
|
||||
}
|
||||
|
||||
|
||||
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
if (random.nextInt(5) == 0 && owner == null) {
|
||||
val currentAge = state.getValue(AGE)
|
||||
if (currentAge < MAX_AGE) {
|
||||
level.setBlock(pos, state.setValue(AGE, currentAge + 1), 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
return SHAPE
|
||||
}
|
||||
|
||||
override fun getCollisionShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
if (context is EntityCollisionContext && (context.entity as? ItemEntity)?.item?.`is`(CobblemonItemTags.APRICORNS) == true) {
|
||||
return Shapes.empty()
|
||||
}
|
||||
return super.getCollisionShape(state, level, pos, context)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
level: LevelAccessor,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
return if (!state.canSurvive(level, pos)) {
|
||||
Blocks.AIR.defaultBlockState()
|
||||
} else {
|
||||
super.updateShape(state, direction, neighborState, level, pos, neighborPos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(AGE, AXIS)
|
||||
}
|
||||
|
||||
override fun isPathfindable(
|
||||
state: BlockState,
|
||||
type: PathComputationType
|
||||
): Boolean = false
|
||||
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val itemStack = player.getItemInHand(hand)
|
||||
if (itemStack.`is`(Items.GLASS_BOTTLE) && state.getValue(AGE) == 1) {
|
||||
if (!player.isCreative) {
|
||||
itemStack.shrink(1)
|
||||
}
|
||||
player.addItem(CobblemonItems.SWEET_SAP.defaultInstance)
|
||||
level.setBlock(pos, state.setValue(AGE, 0), 2)
|
||||
}
|
||||
if (state.getValue(AGE) != MAX_AGE) {
|
||||
return super.useItemOn(stack, state, level, pos, player, hand, hit)
|
||||
}
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
companion object {
|
||||
val AGE: IntegerProperty = BlockStateProperties.AGE_1
|
||||
val AXIS: EnumProperty<Direction.Axis> = BlockStateProperties.AXIS
|
||||
const val MAX_AGE = 1
|
||||
const val MIN_AGE = 0
|
||||
|
||||
private val SHAPE: VoxelShape = Shapes.block()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.block.entity.SweetIncenseBlockEntity
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
||||
class SweetIncenseBlock(properties: Properties) : BaseEntityBlock(properties), SimpleWaterloggedBlock {
|
||||
|
||||
companion object {
|
||||
val LIT: BooleanProperty = BlockStateProperties.LIT
|
||||
val WATERLOGGED: BooleanProperty = BlockStateProperties.WATERLOGGED
|
||||
}
|
||||
|
||||
init {
|
||||
this.registerDefaultState(this.stateDefinition.any()
|
||||
.setValue(LIT, false)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(LIT, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getLightBlock(state: BlockState, level: BlockGetter, pos: BlockPos): Int {
|
||||
return super.getLightBlock(state, level, pos)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
|
||||
return this.defaultBlockState()
|
||||
.setValue(LIT, false)
|
||||
.setValue(WATERLOGGED, context.level.getFluidState(context.clickedPos).type === Fluids.WATER)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return SweetIncenseBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
val CODEC: MapCodec<SweetIncenseBlock> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.BOOL.fieldOf("lit").forGetter { it.defaultBlockState().getValue(LIT) },
|
||||
propertiesCodec()
|
||||
).apply(instance) { lit, properties ->
|
||||
SweetIncenseBlock(properties).apply {
|
||||
registerDefaultState(this.defaultBlockState().setValue(LIT, lit))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out SweetIncenseBlock> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val heldItem = player.getItemInHand(hand)
|
||||
|
||||
return if (!state.getValue(LIT) && (heldItem.`is`(Items.FLINT_AND_STEEL) || heldItem.`is`(Items.FIRE_CHARGE))) {
|
||||
level.setBlock(pos, state.setValue(LIT, true), 3)
|
||||
level.playSound(null, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else if (state.getValue(LIT)) {
|
||||
level.setBlock(pos, state.setValue(LIT, false), 3)
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
}
|
||||
|
||||
override fun animateTick(state: BlockState, level: Level, pos: BlockPos, random: RandomSource) {
|
||||
if (state.getValue(LIT)) {
|
||||
level.addParticle(
|
||||
ParticleTypes.CAMPFIRE_SIGNAL_SMOKE,
|
||||
pos.x + 0.5 + random.nextDouble() / 3.0 * if (random.nextBoolean()) 1 else -1,
|
||||
pos.y + random.nextDouble() + random.nextDouble(),
|
||||
pos.z + 0.5 + random.nextDouble() / 3.0 * if (random.nextBoolean()) 1 else -1,
|
||||
0.0, 0.07, 0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
open class CakeBlockEntity(
|
||||
type: BlockEntityType<*>,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : BlockEntity(type, pos, state) {
|
||||
|
||||
companion object {
|
||||
const val MAX_NUMBER_OF_BITES = 6
|
||||
}
|
||||
|
||||
var cookingComponent: CookingComponent? = null
|
||||
var bites: Int = 0
|
||||
|
||||
fun initializeFromItemStack(itemStack: ItemStack) {
|
||||
cookingComponent = itemStack.get(CobblemonItemComponents.COOKING_COMPONENT)
|
||||
}
|
||||
|
||||
override fun saveAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
tag.putInt("Bites", bites)
|
||||
cookingComponent?.let { component ->
|
||||
CobblemonItemComponents.COOKING_COMPONENT.codec()
|
||||
?.encodeStart(NbtOps.INSTANCE, component)
|
||||
?.result()
|
||||
?.ifPresent { encodedTag ->
|
||||
tag.put("CookingComponent", encodedTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
bites = tag.getInt("Bites")
|
||||
if (tag.contains("CookingComponent")) {
|
||||
CobblemonItemComponents.COOKING_COMPONENT.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.getCompound("CookingComponent"))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
cookingComponent = component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener?>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag? {
|
||||
val tag = CompoundTag()
|
||||
saveAdditional(tag, registries)
|
||||
return tag
|
||||
}
|
||||
|
||||
fun toItemStack(): ItemStack {
|
||||
val stack = ItemStack(this.blockState.block)
|
||||
cookingComponent?.let { component ->
|
||||
stack.getComponents().apply {
|
||||
stack.set(CobblemonItemComponents.COOKING_COMPONENT, component)
|
||||
}
|
||||
}
|
||||
return stack
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* 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.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.cooking.*
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBaits
|
||||
import com.cobblemon.mod.common.block.PotComponent
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotRecipeBase
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.sound.BlockEntitySoundTracker
|
||||
import com.cobblemon.mod.common.client.sound.instances.CancellableSoundInstance
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.WorldlyContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.StackedContents
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Vector4f
|
||||
import java.util.*
|
||||
|
||||
class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(
|
||||
CobblemonBlockEntities.CAMPFIRE,
|
||||
pos,
|
||||
state
|
||||
), WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible, CraftingContainer {
|
||||
|
||||
companion object {
|
||||
const val RESULT_SLOT = 0
|
||||
val CRAFTING_GRID_SLOTS = 1..9
|
||||
val SEASONING_SLOTS = 10..12
|
||||
const val PREVIEW_ITEM_SLOT = 13
|
||||
const val ITEMS_SIZE = 14
|
||||
|
||||
val PLAYER_INVENTORY_SLOTS = 14..40
|
||||
val PLAYER_HOTBAR_SLOTS = 41..49
|
||||
|
||||
const val CRAFTING_GRID_WIDTH = 3
|
||||
const val PLAYER_INVENTORY_WIDTH = 9
|
||||
|
||||
const val COOKING_PROGRESS_PER_TICK = 2
|
||||
|
||||
const val COOKING_PROGRESS_INDEX = 0
|
||||
const val COOKING_PROGRESS_TOTAL_TIME_INDEX = 1
|
||||
const val IS_LID_OPEN_INDEX = 2
|
||||
|
||||
const val BASE_BROTH_COLOR = 0xFFFFFFFF.toInt()
|
||||
|
||||
fun clientTick(level: Level, pos: BlockPos, state: BlockState, campfireBlockEntity: CampfireBlockEntity) {
|
||||
if (!level.isClientSide) return
|
||||
|
||||
val isLit = campfireBlockEntity.dataAccess.get(COOKING_PROGRESS_INDEX) > 0
|
||||
val isSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.runningSound.location)
|
||||
|
||||
if (isLit && !isSoundActive) {
|
||||
BlockEntitySoundTracker.play(pos, CancellableSoundInstance(campfireBlockEntity.runningSound, pos, true, 0.8f, 1.0f))
|
||||
} else if (!isLit && isSoundActive) {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.runningSound.location)
|
||||
}
|
||||
|
||||
campfireBlockEntity.brothColor =
|
||||
getColorMixFromSeasonings(campfireBlockEntity.getSeasonings())
|
||||
?: BASE_BROTH_COLOR
|
||||
|
||||
campfireBlockEntity.bubbleColor =
|
||||
getColorMixFromSeasonings(campfireBlockEntity.getSeasonings(), true)
|
||||
?: BASE_BROTH_COLOR
|
||||
|
||||
if (campfireBlockEntity.particleCooldown > 0) {
|
||||
campfireBlockEntity.particleCooldown--
|
||||
} else {
|
||||
if (isLit) {
|
||||
val position = Vec3(pos.x + 0.5, pos.y + 0.5375, pos.z + 0.5)
|
||||
|
||||
campfireBlockEntity.particleEntityHandler(
|
||||
position = position,
|
||||
level = level,
|
||||
particle = ResourceLocation("cobblemon", if (campfireBlockEntity.getSeasonings().isEmpty()) "broth_bubbles_basic" else "broth_bubbles")
|
||||
)
|
||||
}
|
||||
|
||||
campfireBlockEntity.particleCooldown = 20
|
||||
}
|
||||
}
|
||||
|
||||
fun serverTick(level: Level, pos: BlockPos, state: BlockState, campfireBlockEntity: CampfireBlockEntity) {
|
||||
if (level.isClientSide) return
|
||||
|
||||
val craftingInput = CraftingInput.of(3, 3, campfireBlockEntity.items.subList(1, 10))
|
||||
|
||||
fun <T : CookingPotRecipeBase> fetchRecipe(
|
||||
recipeType: RecipeType<T>
|
||||
): Optional<RecipeHolder<CookingPotRecipeBase>> {
|
||||
val optional = level.recipeManager.getRecipeFor(recipeType, craftingInput, level)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return optional.map { it as RecipeHolder<CookingPotRecipeBase> }
|
||||
}
|
||||
|
||||
// Check for both COOKING_POT_COOKING and COOKING_POT_SHAPELESS recipes
|
||||
val optionalRecipe = fetchRecipe(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
.or { fetchRecipe(CobblemonRecipeTypes.COOKING_POT_SHAPELESS) }
|
||||
|
||||
if (!optionalRecipe.isPresent) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
return
|
||||
}
|
||||
|
||||
val cookingPotRecipe = optionalRecipe.get()
|
||||
val recipe = cookingPotRecipe.value()
|
||||
val cookedItem = recipe.assemble(craftingInput, level.registryAccess())
|
||||
val resultSlotItem = campfireBlockEntity.getItem(0)
|
||||
|
||||
cookedItem.set(CobblemonItemComponents.COOKING_COMPONENT, campfireBlockEntity.createCookingComponentFromSlots())
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, cookedItem)
|
||||
|
||||
if (campfireBlockEntity.isLidOpen) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
}
|
||||
|
||||
if (!resultSlotItem.isEmpty && !ItemStack.isSameItemSameComponents(resultSlotItem, cookedItem)) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
}
|
||||
|
||||
campfireBlockEntity.cookingProgress += COOKING_PROGRESS_PER_TICK
|
||||
if (campfireBlockEntity.cookingProgress == campfireBlockEntity.cookingTotalTime) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
|
||||
if (!cookedItem.isEmpty) {
|
||||
campfireBlockEntity.recipeUsed = cookingPotRecipe
|
||||
|
||||
if (resultSlotItem.isEmpty) {
|
||||
campfireBlockEntity.setItem(0, cookedItem)
|
||||
} else {
|
||||
resultSlotItem.grow(cookedItem.count)
|
||||
}
|
||||
|
||||
campfireBlockEntity.consumeCraftingIngredients()
|
||||
|
||||
level.playSoundServer(
|
||||
position = pos.bottomCenter,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_CRAFT,
|
||||
)
|
||||
|
||||
setChanged(level, pos, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val runningSound = CobblemonSounds.CAMPFIRE_POT_COOK
|
||||
private var cookingProgress : Int = 0
|
||||
private var cookingTotalTime : Int = 200
|
||||
private var isLidOpen : Boolean = true
|
||||
private var items : NonNullList<ItemStack?> = NonNullList.withSize(ITEMS_SIZE, ItemStack.EMPTY)
|
||||
private val recipesUsed: Object2IntOpenHashMap<ResourceLocation> = Object2IntOpenHashMap()
|
||||
private val quickCheck: RecipeManager.CachedCheck<CraftingInput, *> = RecipeManager.createCheck(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
private var potComponent: PotComponent? = null
|
||||
private var particleCooldown: Int = 0
|
||||
var brothColor: Int = BASE_BROTH_COLOR
|
||||
var bubbleColor: Int = 0xFFFFFF
|
||||
|
||||
var dataAccess : ContainerData = object : ContainerData {
|
||||
override fun get(index: Int): Int {
|
||||
return when (index) {
|
||||
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress
|
||||
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime
|
||||
IS_LID_OPEN_INDEX -> if (this@CampfireBlockEntity.isLidOpen) 1 else 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun set(index: Int, value: Int) {
|
||||
when (index) {
|
||||
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress = value
|
||||
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime = value
|
||||
IS_LID_OPEN_INDEX -> this@CampfireBlockEntity.isLidOpen = value == 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
fun particleEntityHandler(position: Vec3, level: Level, particle: ResourceLocation): ParticleStorm {
|
||||
val wrapper = MatrixWrapper()
|
||||
val matrix = PoseStack()
|
||||
wrapper.updateMatrix(matrix.last().pose())
|
||||
wrapper.updatePosition(position)
|
||||
val effect = BedrockParticleOptionsRepository.getEffect(particle) ?: throw IllegalStateException("Particle with resource location $particle not found")
|
||||
|
||||
val particleStorm = ParticleStorm(
|
||||
effect,
|
||||
wrapper,
|
||||
wrapper,
|
||||
level as ClientLevel,
|
||||
sourceAlive = { cookingProgress > 0 },
|
||||
sourceVisible = { cookingProgress > 0 },
|
||||
getParticleColor = {
|
||||
val red = FastColor.ARGB32.red(bubbleColor) / 255F
|
||||
val green = FastColor.ARGB32.green(bubbleColor) / 255F
|
||||
val blue = FastColor.ARGB32.blue(bubbleColor) / 255F
|
||||
val alpha = FastColor.ARGB32.alpha(bubbleColor) / 255F
|
||||
|
||||
Vector4f(red, green, blue, alpha)
|
||||
}
|
||||
).also {
|
||||
it.spawn()
|
||||
}
|
||||
|
||||
return particleStorm
|
||||
}
|
||||
|
||||
fun consumeCraftingIngredients() {
|
||||
for (i in CRAFTING_GRID_SLOTS.first..SEASONING_SLOTS.last) {
|
||||
val itemInSlot = getItem(i)
|
||||
if (!itemInSlot.isEmpty) {
|
||||
when (itemInSlot.item) {
|
||||
Items.LAVA_BUCKET, Items.WATER_BUCKET, Items.MILK_BUCKET -> {
|
||||
// Replace with empty bucket
|
||||
setItem(i, ItemStack(Items.BUCKET))
|
||||
}
|
||||
Items.HONEY_BOTTLE -> {
|
||||
// Replace with empty glass bottle
|
||||
setItem(i, ItemStack(Items.GLASS_BOTTLE))
|
||||
}
|
||||
else -> {
|
||||
// Decrease the stack size by 1
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(i, ItemStack.EMPTY) // Clear the slot if empty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createCookingComponentFromSlots(): CookingComponent {
|
||||
// Variables to store bait and seasoning
|
||||
var bait1 = FishingBait.BLANK_BAIT
|
||||
var bait2 = FishingBait.BLANK_BAIT
|
||||
var bait3 = FishingBait.BLANK_BAIT
|
||||
var seasoning1 = Seasoning.BLANK_SEASONING
|
||||
var seasoning2 = Seasoning.BLANK_SEASONING
|
||||
var seasoning3 = Seasoning.BLANK_SEASONING
|
||||
|
||||
// Iterate through slots 10-12
|
||||
for ((index, slot) in SEASONING_SLOTS.withIndex()) {
|
||||
val itemInSlot = getItem(slot)
|
||||
|
||||
if (!itemInSlot.isEmpty) {
|
||||
// Check if item is a bait
|
||||
val bait = FishingBaits.getFromBaitItemStack(itemInSlot)
|
||||
if (bait != null) {
|
||||
when (index) {
|
||||
0 -> bait1 = bait
|
||||
1 -> bait2 = bait
|
||||
2 -> bait3 = bait
|
||||
}
|
||||
}
|
||||
|
||||
// Check if item is a seasoning
|
||||
val seasoning = Seasonings.getFromItemStack(itemInSlot)
|
||||
if (seasoning != null) {
|
||||
when (index) {
|
||||
0 -> seasoning1 = seasoning
|
||||
1 -> seasoning2 = seasoning
|
||||
2 -> seasoning3 = seasoning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create CookingComponent and attach to result item
|
||||
return CookingComponent(
|
||||
bait1 = bait1,
|
||||
bait2 = bait2,
|
||||
bait3 = bait3,
|
||||
seasoning1 = seasoning1,
|
||||
seasoning2 = seasoning2,
|
||||
seasoning3 = seasoning3
|
||||
)
|
||||
}
|
||||
|
||||
fun getSeasonings(): List<ItemStack> =
|
||||
items.subList(SEASONING_SLOTS.first, SEASONING_SLOTS.last + 1)
|
||||
.filterNotNull()
|
||||
.filter { !it.isEmpty }
|
||||
|
||||
fun getIngredients(): List<ItemStack> =
|
||||
items.subList(CRAFTING_GRID_SLOTS.first, CRAFTING_GRID_SLOTS.last + 1)
|
||||
.filterNotNull()
|
||||
.filter { !it.isEmpty }
|
||||
|
||||
override fun getDefaultName(): Component {
|
||||
return Component.translatable("cobblemon.container.campfire_pot")
|
||||
}
|
||||
|
||||
override fun getWidth(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getHeight(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getItems(): NonNullList<ItemStack?> {
|
||||
level?.let { onItemUpdate(it) }
|
||||
return this.items
|
||||
}
|
||||
|
||||
override fun setItems(items: NonNullList<ItemStack?>) {
|
||||
this.items.clear()
|
||||
this.items.addAll(items)
|
||||
level?.let { onItemUpdate(it) }
|
||||
}
|
||||
|
||||
override fun createMenu(
|
||||
containerId: Int,
|
||||
inventory: Inventory
|
||||
): AbstractContainerMenu {
|
||||
return CookingPotMenu(containerId, inventory, this, this.dataAccess)
|
||||
}
|
||||
|
||||
override fun getContainerSize(): Int {
|
||||
return this.items.size
|
||||
}
|
||||
|
||||
override fun getSlotsForFace(side: Direction): IntArray {
|
||||
return intArrayOf(0, 1, 2)
|
||||
}
|
||||
|
||||
override fun canPlaceItemThroughFace(
|
||||
index: Int,
|
||||
itemStack: ItemStack,
|
||||
direction: Direction?
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canTakeItemThroughFace(
|
||||
index: Int,
|
||||
stack: ItemStack,
|
||||
direction: Direction
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRecipeUsed(recipe: RecipeHolder<*>?) {
|
||||
if (recipe != null) {
|
||||
val resourceLocation = recipe.id()
|
||||
this.recipesUsed.addTo(resourceLocation, 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRecipeUsed(): RecipeHolder<*>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun fillStackedContents(contents: StackedContents) {
|
||||
for (itemStack in this.items) {
|
||||
contents.accountSimpleStack(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItem(slot: Int): ItemStack {
|
||||
return if (slot in 0 until items.size) items[slot] else ItemStack.EMPTY
|
||||
}
|
||||
|
||||
fun getPotItem(): ItemStack? {
|
||||
return potComponent?.potItem
|
||||
}
|
||||
|
||||
fun setPotItem(stack: ItemStack?) {
|
||||
this.potComponent = PotComponent(stack ?: ItemStack.EMPTY) // Ensure a non-null value is passed
|
||||
setChanged()
|
||||
level?.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
tag.putInt("CookingProgress", this.cookingProgress)
|
||||
tag.putBoolean("IsLidOpen", this.isLidOpen)
|
||||
|
||||
ContainerHelper.saveAllItems(tag, this.items, registries)
|
||||
potComponent?.let { component ->
|
||||
PotComponent.CODEC.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
?.ifPresent { encoded -> tag.put("PotComponent", encoded) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
this.cookingProgress = tag.getInt("CookingProgress")
|
||||
this.isLidOpen = tag.getBoolean("IsLidOpen")
|
||||
|
||||
// Minecraft doesn't save empty item stacks to the items tag, so we have to manually clear them
|
||||
// otherwise they would never clear and seasonings would always render wrongly
|
||||
|
||||
clearContent()
|
||||
ContainerHelper.loadAllItems(tag, this.items, registries)
|
||||
|
||||
if (tag.contains("PotComponent")) {
|
||||
val component = PotComponent.CODEC.parse(NbtOps.INSTANCE, tag.getCompound("PotComponent"))
|
||||
.result()
|
||||
?.orElse(null)
|
||||
potComponent = component
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
return saveWithoutMetadata(registryLookup)
|
||||
}
|
||||
|
||||
private fun onItemUpdate(level: Level) {
|
||||
val oldState = level.getBlockState(blockPos)
|
||||
level.sendBlockUpdated(blockPos, oldState, level.getBlockState(blockPos), Block.UPDATE_ALL)
|
||||
level.updateNeighbourForOutputSignal(blockPos, level.getBlockState(blockPos).block)
|
||||
setChanged()
|
||||
level.sendBlockUpdated(blockPos, oldState, level.getBlockState(blockPos), Block.UPDATE_ALL)
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
cookingProgress = 0
|
||||
super.setRemoved()
|
||||
|
||||
if (level?.isClientSide == true) {
|
||||
BlockEntitySoundTracker.stop(blockPos, runningSound.location)
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class CandlePokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.CANDLE_POKE_CAKE, pos, state)
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.bait.BaitSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.LURE_CAKE, pos, state) {
|
||||
|
||||
/**
|
||||
* Generate a `FishingBait` by combining effects from all `RodBaitComponent` data in the `CookingComponent`.
|
||||
*/
|
||||
fun getBaitFromLureCake(): FishingBait? {
|
||||
val component = cookingComponent ?: return null
|
||||
val combinedEffects = listOf(
|
||||
component.bait1.effects,
|
||||
component.bait2.effects,
|
||||
component.bait3.effects
|
||||
).flatten()
|
||||
|
||||
return FishingBait(
|
||||
item = cobblemonResource("lure_cake"), // Directly specify the lure_cake ResourceLocation
|
||||
effects = combinedEffects
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a Pokémon of the given species around the specified position.
|
||||
* @param world The world in which to spawn the Pokémon.
|
||||
* @param pos The position around which to spawn the Pokémon.
|
||||
* @param species The species of the Pokémon to spawn.
|
||||
* @param isMale Whether the Pokémon should be male or female.
|
||||
* @return The spawned Pokémon entity, or null if spawning failed.
|
||||
*/
|
||||
fun spawnPokemon(
|
||||
world: Level,
|
||||
pos: BlockPos
|
||||
): PokemonEntity? {
|
||||
println("Attempting to spawn Pokémon at $pos in world: ${world.dimension().location()}")
|
||||
if (world !is ServerLevel) {
|
||||
println("World is not a ServerLevel, aborting spawn.")
|
||||
return null
|
||||
}
|
||||
|
||||
val spawnPos = pos.offset(world.random.nextInt(-SPAWN_RADIUS.toInt(), SPAWN_RADIUS.toInt()), 0, world.random.nextInt(-SPAWN_RADIUS.toInt(), SPAWN_RADIUS.toInt()))
|
||||
println("Spawn position determined as $spawnPos")
|
||||
|
||||
val spawner = BestSpawner.baitSpawner
|
||||
val buckets = Cobblemon.bestSpawner.config.buckets
|
||||
val chosenBucket = chooseSpawnBucket(buckets)
|
||||
println("Chosen spawn bucket: $chosenBucket")
|
||||
|
||||
val spawnCause = BaitSpawnCause(
|
||||
spawner = spawner,
|
||||
bucket = chosenBucket,
|
||||
entity = null,
|
||||
baitStack = this.toItemStack(),
|
||||
baitEffect = this.getBaitFromLureCake()
|
||||
)
|
||||
println("Spawn cause created: $spawnCause")
|
||||
|
||||
val result = spawner.run(spawnCause, level as ServerLevel, spawnPos)
|
||||
|
||||
var spawnedPokemon: PokemonEntity? = null
|
||||
val resultingSpawn = result?.get()
|
||||
|
||||
if (resultingSpawn is EntitySpawnResult) {
|
||||
for (entity in resultingSpawn.entities) {
|
||||
// we can query the spawned pokemon here
|
||||
spawnedPokemon = (entity as PokemonEntity)
|
||||
|
||||
// CRAB IDEA: todo maybe some grass particles? as if they appeared from the grass or forest?
|
||||
return spawnedPokemon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun chooseSpawnBucket(buckets: List<SpawnBucket>): SpawnBucket {
|
||||
val baseIncreases = listOf(2.5F, 1.0F, 0.6F) // Base increases for the first three buckets beyond the first
|
||||
val adjustedWeights = buckets.mapIndexed { index, bucket ->
|
||||
if (index == 0) {
|
||||
// Placeholder, will be recalculated
|
||||
0.0F
|
||||
} else {
|
||||
val increase = if (index < baseIncreases.size) baseIncreases[index] else baseIncreases.last() + (index - baseIncreases.size + 1) * 0.15F
|
||||
bucket.weight + increase
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
// Recalculate the first bucket's weight to ensure the total is 100%
|
||||
val totalAdjustedWeight = adjustedWeights.sum() - adjustedWeights[0] // Corrected to ensure the list contains Floats
|
||||
adjustedWeights[0] = 100.0F - totalAdjustedWeight + buckets[0].weight
|
||||
|
||||
// Random selection based on adjusted weights
|
||||
val weightSum = adjustedWeights.sum()
|
||||
val chosenSum = kotlin.random.Random.nextDouble(weightSum.toDouble()).toFloat() // Ensure usage of Random from kotlin.random package
|
||||
var sum = 0.0F
|
||||
adjustedWeights.forEachIndexed { index, weight ->
|
||||
sum += weight
|
||||
if (sum >= chosenSum) {
|
||||
return buckets[index]
|
||||
}
|
||||
}
|
||||
|
||||
return buckets.first() // Fallback
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val RANDOM_SPAWN_CHANCE = 100
|
||||
private const val SPAWN_RADIUS = 5.0
|
||||
|
||||
/*val TICKER = BlockEntityTicker<LureCakeBlockEntity> { world, pos, state, blockEntity ->
|
||||
println("Ticker running for LureCakeBlockEntity at $pos in world: ${world.dimension().location()}")
|
||||
if (world.random.nextInt(RANDOM_SPAWN_CHANCE) == 0) {
|
||||
println("Triggering Pokémon spawn")
|
||||
blockEntity.spawnPokemon(world, pos)
|
||||
world.sendBlockUpdated(pos, state, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class PokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.POKE_CAKE, pos, state)
|
||||
+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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class SweetIncenseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.INCENSE_SWEET, pos, state)
|
||||
@@ -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.block.grower
|
||||
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonConfiguredFeatures
|
||||
import net.minecraft.world.level.block.grower.TreeGrower
|
||||
import java.util.*
|
||||
|
||||
class SaccharineTreeGrower() : TreeGrower(
|
||||
"saccharine_tree",
|
||||
Optional.empty(),
|
||||
Optional.of(CobblemonConfiguredFeatures.SACCHARINE_TREE_KEY),
|
||||
Optional.empty()
|
||||
)
|
||||
@@ -23,25 +23,20 @@ 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.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.block.CandlePokeCakeBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.*
|
||||
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
|
||||
@@ -49,10 +44,7 @@ import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequests
|
||||
import com.cobblemon.mod.common.client.sound.BattleMusicController
|
||||
import com.cobblemon.mod.common.client.sound.EntitySoundTracker
|
||||
import com.cobblemon.mod.common.client.storage.ClientStorageManager
|
||||
import com.cobblemon.mod.common.client.tooltips.CobblemonTooltipGenerator
|
||||
import com.cobblemon.mod.common.client.tooltips.FishingBaitTooltipGenerator
|
||||
import com.cobblemon.mod.common.client.tooltips.FishingRodTooltipGenerator
|
||||
import com.cobblemon.mod.common.client.tooltips.TooltipManager
|
||||
import com.cobblemon.mod.common.client.tooltips.*
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
@@ -127,6 +119,7 @@ object CobblemonClient {
|
||||
//registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
this.registerItemColors()
|
||||
Berries.observable.subscribe {
|
||||
BerryModelRepository.patchModels()
|
||||
}
|
||||
@@ -173,6 +166,8 @@ object CobblemonClient {
|
||||
TooltipManager.registerTooltipGenerator(CobblemonTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(FishingBaitTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(FishingRodTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(SeasoningTooltipGenerator)
|
||||
TooltipManager.registerTooltipGenerator(AprijuiceTooltipGenerator)
|
||||
}
|
||||
|
||||
fun registerFlywheelRenderers() {
|
||||
@@ -236,6 +231,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.BIG_ROOT,
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
CobblemonBlocks.VIVICHOKE_SEEDS,
|
||||
CobblemonBlocks.HEARTY_GRAINS,
|
||||
CobblemonBlocks.PEP_UP_FLOWER,
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
@@ -256,7 +252,25 @@ object CobblemonClient {
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE,
|
||||
CobblemonBlocks.LECTERN
|
||||
CobblemonBlocks.SACCHARINE_DOOR,
|
||||
CobblemonBlocks.SACCHARINE_TRAPDOOR,
|
||||
CobblemonBlocks.SACCHARINE_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_WALL_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.LURE_CAKE,
|
||||
CobblemonBlocks.POKE_CAKE,
|
||||
CobblemonBlocks.LECTERN,
|
||||
CobblemonBlocks.CAMPFIRE,
|
||||
CobblemonBlocks.BLACK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.BLUE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.GREEN_CAMPFIRE_POT,
|
||||
CobblemonBlocks.PINK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.RED_CAMPFIRE_POT,
|
||||
CobblemonBlocks.WHITE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.YELLOW_CAMPFIRE_POT
|
||||
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -290,6 +304,10 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LURE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CANDLE_POKE_CAKE, ::CandlePokeCakeBlockEntityRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -311,6 +329,11 @@ object CobblemonClient {
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.NPC, ::NPCRenderer)
|
||||
}
|
||||
|
||||
private fun registerItemColors() {
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
|
||||
implementation.registerItemColors(AprijuiceItemColorProvider, *CobblemonItems.aprijuices.toTypedArray())
|
||||
}
|
||||
|
||||
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
||||
LOGGER.info("Loading assets...")
|
||||
// Particles come first because animations need them.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.cooking
|
||||
|
||||
enum class AprijuiceTypes {
|
||||
|
||||
BLACK,
|
||||
BLUE,
|
||||
GREEN,
|
||||
PINK,
|
||||
RED,
|
||||
WHITE,
|
||||
YELLOW;
|
||||
|
||||
}
|
||||
@@ -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.client.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
|
||||
class CookButton(
|
||||
pX: Int, pY: Int,
|
||||
var selected: Boolean = false,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Component.literal("Cook"), onPress, DEFAULT_NARRATION) {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 20F
|
||||
private const val HEIGHT = 19F
|
||||
|
||||
private val buttonResource = cobblemonResource("textures/gui/campfirepot/button_cook.png")
|
||||
private val buttonSelectedResource = cobblemonResource("textures/gui/campfirepot/button_cook_selected.png")
|
||||
}
|
||||
|
||||
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = if (selected) buttonSelectedResource else buttonResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isMouseOver(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F))
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.util.ByIdMap
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import java.util.function.IntFunction
|
||||
|
||||
enum class CookingPotBookCategory(private val categoryName: String, private val categoryId: Int) : StringRepresentable {
|
||||
FOODS("foods", 0),
|
||||
MEDICINES("medicines", 1),
|
||||
BAITS("baits", 2),
|
||||
MISC("misc", 3);
|
||||
|
||||
override fun getSerializedName(): String {
|
||||
return this.categoryName
|
||||
}
|
||||
|
||||
private fun getCategoryId(): Int {
|
||||
return this.categoryId
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<CookingPotBookCategory> = Codec.STRING.flatXmap(
|
||||
{ name ->
|
||||
val category = CookingPotBookCategory.values().firstOrNull { it.categoryName.equals(name, ignoreCase = true) }
|
||||
if (category != null) {
|
||||
println("Successfully parsed category: $name -> ${category.name}")
|
||||
DataResult.success(category)
|
||||
} else {
|
||||
println("Failed to parse category: $name")
|
||||
DataResult.error { "Unknown category: $name" }
|
||||
}
|
||||
},
|
||||
{ category ->
|
||||
println("Serializing category: ${category.categoryName}")
|
||||
DataResult.success(category.categoryName)
|
||||
}
|
||||
)
|
||||
val BY_ID: IntFunction<CookingPotBookCategory> = ByIdMap.continuous(CookingPotBookCategory::getCategoryId,
|
||||
CookingPotBookCategory.entries.toTypedArray(), ByIdMap.OutOfBoundsStrategy.ZERO)
|
||||
val STREAM_CODEC: StreamCodec<ByteBuf, CookingPotBookCategory> = ByteBufCodecs.idMapper(BY_ID, CookingPotBookCategory::getCategoryId)
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.CRAFTING_GRID_WIDTH
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.ITEMS_SIZE
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.player.StackedContents
|
||||
import net.minecraft.world.inventory.TransientCraftingContainer
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.util.List
|
||||
|
||||
class CookingPotContainer : TransientCraftingContainer {
|
||||
|
||||
val menu : CookingPotMenu?
|
||||
val items : NonNullList<ItemStack>
|
||||
var outputSlot: ItemStack = ItemStack.EMPTY
|
||||
|
||||
constructor(menu : CookingPotMenu, width: Int, height: Int) : super(menu, width, height) {
|
||||
this.menu = menu
|
||||
this.items = NonNullList.withSize(ITEMS_SIZE, ItemStack.EMPTY)
|
||||
}
|
||||
|
||||
override fun getContainerSize(): Int {
|
||||
return this.items.size
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
for (itemStack in this.items) {
|
||||
if (!itemStack.isEmpty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getItem(slot: Int): ItemStack {
|
||||
return if (slot >= this.getContainerSize()) ItemStack.EMPTY else this.items[slot]
|
||||
}
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) {
|
||||
this.items[slot] = stack
|
||||
this.menu?.slotsChanged(this)
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||
return ContainerHelper.takeItem(this.items, slot)
|
||||
}
|
||||
|
||||
override fun removeItem(slot: Int, amount: Int): ItemStack {
|
||||
val itemStack = ContainerHelper.removeItem(this.items, slot, amount)
|
||||
if (!itemStack.isEmpty) {
|
||||
this.menu?.slotsChanged(this)
|
||||
}
|
||||
return itemStack
|
||||
}
|
||||
|
||||
override fun setChanged() {}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun clearContent() {
|
||||
for (index in 0 until this.items.size) {
|
||||
this.items[index] = ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHeight(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getWidth(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getItems(): kotlin.collections.List<ItemStack?>? {
|
||||
return List.copyOf(this.items)
|
||||
}
|
||||
|
||||
override fun fillStackedContents(contents: StackedContents) {
|
||||
for (itemStack in this.items) {
|
||||
contents.accountSimpleStack(itemStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMenuType
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.CRAFTING_GRID_WIDTH
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.ITEMS_SIZE
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PLAYER_HOTBAR_SLOTS
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PLAYER_INVENTORY_SLOTS
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PLAYER_INVENTORY_WIDTH
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.SEASONING_SLOTS
|
||||
import net.minecraft.recipebook.ServerPlaceRecipe
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.player.StackedContents
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, ContainerListener {
|
||||
private val player: Player
|
||||
private val level: Level
|
||||
private val playerInventory: Inventory
|
||||
private val container: CraftingContainer
|
||||
private val resultContainer: ResultContainer
|
||||
val containerData: ContainerData
|
||||
private val recipeType: RecipeType<CookingPotRecipe> = CobblemonRecipeTypes.COOKING_POT_COOKING
|
||||
private val quickCheck = RecipeManager.createCheck(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory) : super(CobblemonMenuType.COOKING_POT, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.container = CookingPotContainer(this, CRAFTING_GRID_WIDTH, CRAFTING_GRID_WIDTH)
|
||||
this.resultContainer = ResultContainer()
|
||||
this.containerData = SimpleContainerData(3)
|
||||
this.addDataSlots(containerData)
|
||||
this.player = playerInventory.player
|
||||
this.level = playerInventory.player.level()
|
||||
initializeSlots(playerInventory)
|
||||
}
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory, container: CraftingContainer, containerData: ContainerData) : super(CobblemonMenuType.COOKING_POT, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
this.container = container
|
||||
this.containerData = containerData
|
||||
this.addDataSlots(containerData)
|
||||
this.resultContainer = ResultContainer()
|
||||
this.player = playerInventory.player
|
||||
this.level = playerInventory.player.level()
|
||||
container.startOpen(playerInventory.player)
|
||||
initializeSlots(playerInventory)
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
private fun initializeSlots(playerInventory: Inventory) {
|
||||
val resultSlotX = 128
|
||||
val resultSlotY = 55
|
||||
|
||||
addSlot(CookingPotResultSlot(this.container, RESULT_SLOT, resultSlotX, resultSlotY))
|
||||
|
||||
for ((index, slotIndex) in CRAFTING_GRID_SLOTS.withIndex()) {
|
||||
val i = index / CRAFTING_GRID_WIDTH
|
||||
val j = index % CRAFTING_GRID_WIDTH
|
||||
addSlot(Slot(this.container, slotIndex, 33 + j * 18, 18 + i * 18))
|
||||
}
|
||||
|
||||
for ((index, slotIndex) in SEASONING_SLOTS.withIndex()) {
|
||||
addSlot(SeasoningSlot(this.container, slotIndex, 110 + index * 18, 18))
|
||||
}
|
||||
|
||||
addSlot(CookingPotPreviewSlot(this.container, PREVIEW_ITEM_SLOT, resultSlotX, resultSlotY))
|
||||
|
||||
for ((index, _) in PLAYER_INVENTORY_SLOTS.withIndex()) {
|
||||
val i = index / PLAYER_INVENTORY_WIDTH
|
||||
val j = index % PLAYER_INVENTORY_WIDTH
|
||||
addSlot(Slot(playerInventory, index + 9, 8 + j * 18, 84 + i * 18))
|
||||
}
|
||||
|
||||
for ((index, _) in PLAYER_HOTBAR_SLOTS.withIndex()) {
|
||||
addSlot(Slot(playerInventory, index, 8 + index * 18, 142))
|
||||
}
|
||||
}
|
||||
|
||||
override fun broadcastChanges() {
|
||||
super.broadcastChanges()
|
||||
}
|
||||
|
||||
override fun handlePlacement(placeAll: Boolean, recipe: RecipeHolder<*>, player: ServerPlayer) {
|
||||
// Check if the recipe value implements CookingPotRecipeBase
|
||||
val recipeValue = recipe.value()
|
||||
if (recipeValue is CookingPotRecipeBase) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val castedRecipe = recipe as RecipeHolder<CookingPotRecipeBase>
|
||||
this.beginPlacingRecipe()
|
||||
try {
|
||||
val serverPlaceRecipe = ServerPlaceRecipe(this)
|
||||
serverPlaceRecipe.recipeClicked(player, castedRecipe, placeAll)
|
||||
} finally {
|
||||
this.finishPlacingRecipe(castedRecipe)
|
||||
}
|
||||
} else {
|
||||
throw IllegalArgumentException("Unsupported recipe type: ${recipeValue::class.java.name}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun removed(player: Player) {
|
||||
super.removed(player)
|
||||
}
|
||||
|
||||
override fun fillCraftSlotsStackedContents(itemHelper: StackedContents) {
|
||||
this.container.fillStackedContents(itemHelper)
|
||||
}
|
||||
|
||||
override fun clearCraftingContent() {
|
||||
container.clearContent()
|
||||
}
|
||||
|
||||
override fun recipeMatches(recipe: RecipeHolder<CookingPotRecipeBase>): Boolean {
|
||||
val recipeValue = recipe.value()
|
||||
return if (recipeValue is CookingPotRecipeBase) {
|
||||
recipeValue.matches(container.asCraftInput(), level)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getResultSlotIndex(): Int {
|
||||
return RESULT_SLOT
|
||||
}
|
||||
|
||||
override fun getGridWidth(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getGridHeight(): Int {
|
||||
return CRAFTING_GRID_WIDTH
|
||||
}
|
||||
|
||||
override fun getSize(): Int {
|
||||
return ITEMS_SIZE
|
||||
}
|
||||
|
||||
fun getBurnProgress(): Float {
|
||||
val i = this.containerData.get(0)
|
||||
val j = this.containerData.get(1)
|
||||
return if (j != 0 && i != 0) {
|
||||
Math.clamp((i.toFloat() / j.toFloat()), 0.0F, 1.0F)
|
||||
} else {
|
||||
0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRecipeBookType(): RecipeBookType {
|
||||
return RecipeBookType.valueOf("COOKING_POT")
|
||||
}
|
||||
|
||||
override fun shouldMoveToInventory(slotIndex: Int): Boolean {
|
||||
return slotIndex != RESULT_SLOT && slotIndex != PREVIEW_ITEM_SLOT
|
||||
}
|
||||
|
||||
override fun quickMoveStack(
|
||||
player: Player,
|
||||
index: Int
|
||||
): ItemStack {
|
||||
var itemStack = ItemStack.EMPTY
|
||||
val slot = slots[index]
|
||||
|
||||
if (slot.hasItem()) {
|
||||
val slotItemStack = slot.item
|
||||
itemStack = slotItemStack.copy()
|
||||
|
||||
if (index == RESULT_SLOT) {
|
||||
if (!this.moveItemStackTo(slotItemStack, PLAYER_INVENTORY_SLOTS.first, PLAYER_HOTBAR_SLOTS.last + 1, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
slot.onQuickCraft(slotItemStack, itemStack);
|
||||
} else if (index in PLAYER_INVENTORY_SLOTS || index in PLAYER_HOTBAR_SLOTS) {
|
||||
if (Seasonings.isSeasoning(slotItemStack)) {
|
||||
if (!this.moveItemStackTo(slotItemStack, SEASONING_SLOTS.first, SEASONING_SLOTS.last + 1, false) &&
|
||||
!this.moveItemStackTo(slotItemStack, CRAFTING_GRID_SLOTS.first, CRAFTING_GRID_SLOTS.last + 1, false)
|
||||
) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if (!this.moveItemStackTo(slotItemStack, CRAFTING_GRID_SLOTS.first, CRAFTING_GRID_SLOTS.last + 1, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if (index in CRAFTING_GRID_SLOTS || index in SEASONING_SLOTS) {
|
||||
if (!this.moveItemStackTo(slotItemStack, PLAYER_INVENTORY_SLOTS.first, PLAYER_HOTBAR_SLOTS.last + 1, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
if (slotItemStack.isEmpty) {
|
||||
slot.setByPlayer(ItemStack.EMPTY)
|
||||
} else {
|
||||
slot.setChanged()
|
||||
}
|
||||
|
||||
if (slotItemStack.count == itemStack.count) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
slot.onTake(player, slotItemStack)
|
||||
}
|
||||
|
||||
return itemStack
|
||||
}
|
||||
|
||||
override fun stillValid(player: Player): Boolean {
|
||||
return this.container.stillValid(player)
|
||||
}
|
||||
|
||||
override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
|
||||
broadcastChanges()
|
||||
}
|
||||
|
||||
override fun dataChanged(
|
||||
containerMenu: AbstractContainerMenu,
|
||||
dataSlotIndex: Int,
|
||||
value: Int
|
||||
) {
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class CookingPotPreviewSlot(
|
||||
container: CraftingContainer,
|
||||
index: Int,
|
||||
x: Int,
|
||||
y: Int
|
||||
) : Slot(container, index, x, y) {
|
||||
override fun isActive(): Boolean {
|
||||
return container.getItem(RESULT_SLOT).isEmpty
|
||||
}
|
||||
|
||||
override fun isFake(): Boolean = true
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun mayPickup(player: Player): Boolean = false
|
||||
override fun isHighlightable() = false
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonRecipeSerializers
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.crafting.*
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
class CookingPotRecipe(
|
||||
val pattern: ShapedRecipePattern,
|
||||
val result: ItemStack,
|
||||
val groupName: String,
|
||||
val category: CookingPotBookCategory,
|
||||
val showNotification: Boolean
|
||||
) : Recipe<CraftingInput>, CookingPotRecipeBase {
|
||||
|
||||
override fun getGroup(): String {
|
||||
return groupName
|
||||
}
|
||||
|
||||
/*override fun matches(input: CraftingInput, level: Level): Boolean {
|
||||
val matches = this.pattern.matches(input)
|
||||
return matches
|
||||
}*/
|
||||
|
||||
/*override fun matches(input: CraftingInput, level: Level): Boolean {
|
||||
val craftingItems = (1..9).map { input.getItem(it) }
|
||||
val filteredInput = CraftingInput.of(3, 3, craftingItems)
|
||||
return this.pattern.matches(filteredInput)
|
||||
}*/
|
||||
|
||||
override fun matches(input: CraftingInput, level: Level): Boolean {
|
||||
//println("Validating recipe match in CookingPotRecipe...")
|
||||
|
||||
// Create a filtered CraftingInput with only slots 1-9
|
||||
val filteredItems = (0..8).mapNotNull { index ->
|
||||
if (index < input.size()) input.getItem(index) else ItemStack.EMPTY
|
||||
}
|
||||
val filteredInput = CraftingInput.of(3, 3, filteredItems)
|
||||
|
||||
/*// Debugging: Log filtered crafting grid contents
|
||||
for (i in 0 until filteredInput.size()) {
|
||||
val itemStack = filteredInput.getItem(i)
|
||||
println("Filtered crafting slot $i: ${itemStack.item} (${itemStack.count})")
|
||||
}*/
|
||||
|
||||
// Perform pattern matching on the filtered input
|
||||
val matches = this.pattern.matches(filteredInput)
|
||||
//println("Pattern match result: $matches")
|
||||
|
||||
/*// Additional logic for specific recipes
|
||||
if (this.pattern.width() == 3 && this.pattern.height() == 3) {
|
||||
if (this.result.item == CobblemonItems.DAWN_STONE_BLOCK.asItem()) {
|
||||
println("Special case for Dawn Stone Block recipe.")
|
||||
}
|
||||
}*/
|
||||
|
||||
if (matches == true) {
|
||||
val test = 1
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
|
||||
override fun assemble(
|
||||
input: CraftingInput,
|
||||
registries: HolderLookup.Provider
|
||||
): ItemStack {
|
||||
return this.getResultItem(registries).copy()
|
||||
}
|
||||
|
||||
override fun canCraftInDimensions(width: Int, height: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getResultItem(registries: HolderLookup.Provider): ItemStack {
|
||||
return this.result
|
||||
}
|
||||
|
||||
override fun getSerializer(): RecipeSerializer<*> {
|
||||
return CobblemonRecipeSerializers.COOKING_POT_COOKING
|
||||
}
|
||||
|
||||
override fun getType(): RecipeType<CookingPotRecipe> {
|
||||
return CobblemonRecipeTypes.COOKING_POT_COOKING
|
||||
}
|
||||
|
||||
override fun getIngredients(): NonNullList<Ingredient> {
|
||||
return this.pattern.ingredients()
|
||||
}
|
||||
|
||||
class Serializer : RecipeSerializer<CookingPotRecipe> {
|
||||
companion object {
|
||||
val CODEC: MapCodec<CookingPotRecipe> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
ShapedRecipePattern.MAP_CODEC.forGetter { recipe -> recipe.pattern },
|
||||
ItemStack.STRICT_CODEC.fieldOf("result").forGetter { recipe -> recipe.result },
|
||||
Codec.STRING.optionalFieldOf("group", "").forGetter { recipe -> recipe.group },
|
||||
CookingPotBookCategory.CODEC.fieldOf("category").orElse(CookingPotBookCategory.MISC).forGetter { recipe -> recipe.category },
|
||||
Codec.BOOL.optionalFieldOf("show_notification", true).forGetter { recipe -> recipe.showNotification }
|
||||
).apply(instance, ::CookingPotRecipe)
|
||||
}
|
||||
|
||||
val STREAM_CODEC: StreamCodec<RegistryFriendlyByteBuf, CookingPotRecipe> = StreamCodec.of(::toNetwork, ::fromNetwork)
|
||||
|
||||
private fun fromNetwork(buffer: RegistryFriendlyByteBuf): CookingPotRecipe {
|
||||
val group = buffer.readUtf()
|
||||
val category = buffer.readEnum(CookingPotBookCategory::class.java)
|
||||
val pattern = ShapedRecipePattern.STREAM_CODEC.decode(buffer)
|
||||
val result = ItemStack.STREAM_CODEC.decode(buffer)
|
||||
val showNotification = buffer.readBoolean()
|
||||
return CookingPotRecipe(pattern, result, group, category, showNotification)
|
||||
}
|
||||
|
||||
private fun toNetwork(buffer: RegistryFriendlyByteBuf, recipe: CookingPotRecipe) {
|
||||
buffer.writeUtf(recipe.groupName)
|
||||
buffer.writeEnum(recipe.category)
|
||||
ShapedRecipePattern.STREAM_CODEC.encode(buffer, recipe.pattern)
|
||||
ItemStack.STREAM_CODEC.encode(buffer, recipe.result)
|
||||
buffer.writeBoolean(recipe.showNotification)
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<CookingPotRecipe> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, CookingPotRecipe> {
|
||||
return STREAM_CODEC
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.client.gui.cookingpot
|
||||
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
interface CookingPotRecipeBase: Recipe<CraftingInput> {
|
||||
override fun matches(input: CraftingInput, level: Level): Boolean
|
||||
override fun assemble(input: CraftingInput, registries: HolderLookup.Provider): ItemStack
|
||||
}
|
||||
+48
@@ -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.client.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class CookingPotResultSlot(
|
||||
container: CraftingContainer,
|
||||
index: Int,
|
||||
x: Int,
|
||||
y: Int
|
||||
) : Slot(container, index, x, y) {
|
||||
|
||||
override fun onTake(player: Player, stack: ItemStack) {
|
||||
val menu = player.containerMenu
|
||||
|
||||
if (menu is CookingPotMenu) {
|
||||
// val cookingComponent = menu.createCookingComponentFromSlots()
|
||||
|
||||
// Attach the CookingComponent to the result stack
|
||||
// stack.set(CobblemonItemComponents.COOKING_COMPONENT, cookingComponent)
|
||||
|
||||
// menu.consumeCraftingIngredients() // Decrement ingredients
|
||||
menu.broadcastChanges() // Notify the client
|
||||
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.CAMPFIRE_POT_USE, 1.0f, 1.0f))
|
||||
} else {
|
||||
println("Player menu is not CookingPotMenu!")
|
||||
}
|
||||
|
||||
super.onTake(player, stack)
|
||||
}
|
||||
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun isActive(): Boolean = hasItem()
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.mixin.accessor.RecipeBookComponentAccessor
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.ImageButton
|
||||
import net.minecraft.client.gui.components.StateSwitchingButton
|
||||
import net.minecraft.client.gui.components.Tooltip
|
||||
import net.minecraft.client.gui.components.WidgetSprites
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent
|
||||
import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.RecipeBookMenu
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import kotlin.math.ceil
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CookingPotScreen(
|
||||
menu: CookingPotMenu,
|
||||
playerInventory: Inventory,
|
||||
title: Component
|
||||
) : AbstractContainerScreen<CookingPotMenu>(
|
||||
menu,
|
||||
playerInventory,
|
||||
Component.translatable("cobblemon.container.campfire_pot")
|
||||
), RecipeUpdateListener {
|
||||
|
||||
companion object {
|
||||
private const val BACKGROUND_HEIGHT = 166
|
||||
private const val BACKGROUND_WIDTH = 176
|
||||
|
||||
private const val COOK_PROGRESS_HEIGHT = 12
|
||||
private const val COOK_PROGRESS_WIDTH = 22
|
||||
|
||||
private val BACKGROUND = cobblemonResource("textures/gui/campfirepot/campfire_pot.png")
|
||||
private val COOK_PROGRESS_SPRITE = cobblemonResource("textures/gui/campfirepot/cook_progress.png")
|
||||
private val FILTER_BUTTON_SPRITES = WidgetSprites(
|
||||
ResourceLocation.withDefaultNamespace("recipe_book/campfire_pot_filter_enabled"),
|
||||
ResourceLocation.withDefaultNamespace("recipe_book/campfire_pot_filter_disabled"),
|
||||
ResourceLocation.withDefaultNamespace("recipe_book/campfire_pot_filter_enabled_highlighted"),
|
||||
ResourceLocation.withDefaultNamespace("recipe_book/campfire_pot_filter_disabled_highlighted")
|
||||
)
|
||||
}
|
||||
|
||||
private val recipeBookComponent: RecipeBookComponent = RecipeBookComponent()
|
||||
private var widthTooNarrow : Boolean = false
|
||||
|
||||
private lateinit var cookButton: CookButton
|
||||
|
||||
init {
|
||||
this.imageWidth = BACKGROUND_WIDTH
|
||||
this.imageHeight = BACKGROUND_HEIGHT
|
||||
}
|
||||
|
||||
override fun containerTick() {
|
||||
super.containerTick()
|
||||
this.recipeBookComponent.tick()
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
this.widthTooNarrow = this.width < 379
|
||||
this.recipeBookComponent.init(this.width, this.height, this.minecraft!!, this.widthTooNarrow, this.menu as RecipeBookMenu<CraftingInput, CookingPotRecipe>)
|
||||
|
||||
if (this.recipeBookComponent.isVisible) {
|
||||
val recipeBookFilterButton = (this.recipeBookComponent as RecipeBookComponentAccessor).filterButton
|
||||
overrideRecipeBookFilterButton(recipeBookFilterButton)
|
||||
}
|
||||
|
||||
this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth)
|
||||
val topPos = ((height - BACKGROUND_HEIGHT) / 2)
|
||||
|
||||
val recipeBookButton = ImageButton(
|
||||
this.leftPos + 5, topPos + 35, 20, 18, RecipeBookComponent.RECIPE_BUTTON_SPRITES
|
||||
) { button ->
|
||||
this.recipeBookComponent.toggleVisibility()
|
||||
if (this.recipeBookComponent.isVisible) overrideRecipeBookFilterButton((this.recipeBookComponent as RecipeBookComponentAccessor).filterButton)
|
||||
this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth)
|
||||
button.setPosition(this.leftPos + 5, topPos + 35)
|
||||
}
|
||||
|
||||
this.addWidget(this.recipeBookComponent)
|
||||
this.addRenderableWidget(recipeBookButton)
|
||||
|
||||
this.titleLabelX = this.imageWidth - (BACKGROUND_WIDTH / 2) - (this.font.width(this.title) / 2)
|
||||
this.titleLabelY = 6
|
||||
|
||||
if (::cookButton.isInitialized) removeWidget(cookButton)
|
||||
cookButton = CookButton(this.leftPos + 97, topPos + 57, menu.containerData.get(IS_LID_OPEN_INDEX) == 0) {
|
||||
val isLidClosed = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
|
||||
sendToServer(ToggleCookingPotLidPacket(isLidClosed))
|
||||
}
|
||||
|
||||
addRenderableWidget(cookButton)
|
||||
}
|
||||
|
||||
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {
|
||||
guiGraphics.drawString(this.font, this.title, this.titleLabelX, this.titleLabelY, 4210752, false)
|
||||
}
|
||||
|
||||
override fun renderBg(
|
||||
context: GuiGraphics,
|
||||
partialTick: Float,
|
||||
mouseX: Int,
|
||||
mouseY: Int
|
||||
) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = BACKGROUND,
|
||||
x = leftPos, y = (height - BACKGROUND_HEIGHT) / 2,
|
||||
width = BACKGROUND_WIDTH, height = BACKGROUND_HEIGHT
|
||||
)
|
||||
|
||||
val cookProgress = ceil(menu.getBurnProgress() * COOK_PROGRESS_WIDTH).toInt()
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = COOK_PROGRESS_SPRITE,
|
||||
x = leftPos + 96,
|
||||
y = topPos + 39,
|
||||
width = cookProgress,
|
||||
height = COOK_PROGRESS_HEIGHT,
|
||||
textureWidth = COOK_PROGRESS_WIDTH
|
||||
)
|
||||
}
|
||||
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
partialTicks: Float
|
||||
) {
|
||||
cookButton.selected = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
|
||||
cookButton.setPosition(this.leftPos + 97, topPos + 57)
|
||||
|
||||
if (this.recipeBookComponent.isVisible && this.widthTooNarrow) {
|
||||
this.renderBackground(context, mouseX, mouseY, partialTicks)
|
||||
this.recipeBookComponent.render(context, mouseX, mouseY, partialTicks)
|
||||
} else {
|
||||
super.render(context, mouseX, mouseY, partialTicks)
|
||||
this.recipeBookComponent.render(context, mouseX, mouseY, partialTicks)
|
||||
this.recipeBookComponent.renderGhostRecipe(context, this.leftPos, this.topPos, true, partialTicks)
|
||||
}
|
||||
|
||||
this.renderTooltip(context, mouseX, mouseY)
|
||||
this.recipeBookComponent.renderTooltip(context, this.leftPos, this.topPos, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun renderSlot(guiGraphics: GuiGraphics, slot: Slot) {
|
||||
if (slot.index != PREVIEW_ITEM_SLOT) {
|
||||
super.renderSlot(guiGraphics, slot)
|
||||
return
|
||||
}
|
||||
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 0.5F)
|
||||
|
||||
guiGraphics.renderFakeItem(slot.item, slot.x, slot.y);
|
||||
guiGraphics.renderItemDecorations(minecraft!!.font, slot.item, slot.x, slot.y)
|
||||
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F)
|
||||
RenderSystem.disableBlend()
|
||||
}
|
||||
|
||||
override fun recipesUpdated() {
|
||||
this.recipeBookComponent.recipesUpdated()
|
||||
}
|
||||
|
||||
override fun getRecipeBookComponent(): RecipeBookComponent = this.recipeBookComponent
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (this.recipeBookComponent.mouseClicked(mouseX, mouseY, button)) {
|
||||
this.focused = this.recipeBookComponent
|
||||
|
||||
if (this.recipeBookComponent.isVisible) {
|
||||
val recipeBookFilterButton = (this.recipeBookComponent as RecipeBookComponentAccessor).filterButton
|
||||
if (recipeBookFilterButton.isMouseOver(mouseX, mouseY)) overrideRecipeBookFilterButton(recipeBookFilterButton)
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
return if (this.widthTooNarrow && this.recipeBookComponent.isVisible) true else super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
}
|
||||
|
||||
private fun overrideRecipeBookFilterButton(filterButton: StateSwitchingButton) {
|
||||
filterButton.initTextureValues(FILTER_BUTTON_SPRITES)
|
||||
filterButton.setTooltip(
|
||||
if (filterButton.isStateTriggered()) Tooltip.create(Component.translatable("cobblemon.container.campfire_pot.recipe_book.toggle_recipes"))
|
||||
else Tooltip.create(Component.translatable("gui.recipebook.toggleRecipes.all"))
|
||||
)
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonRecipeSerializers
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotBookCategory
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.CraftingRecipe
|
||||
import net.minecraft.world.item.crafting.Ingredient
|
||||
import net.minecraft.world.item.crafting.Recipe
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
class CookingPotShapelessRecipe(
|
||||
private val group: String,
|
||||
private val category: CookingPotBookCategory,
|
||||
private val result: ItemStack,
|
||||
private val ingredients: NonNullList<Ingredient>
|
||||
) : Recipe<CraftingInput>, CookingPotRecipeBase {
|
||||
|
||||
override fun getSerializer(): RecipeSerializer<*> {
|
||||
return CobblemonRecipeSerializers.COOKING_POT_SHAPELESS
|
||||
}
|
||||
|
||||
override fun getType(): RecipeType<CookingPotShapelessRecipe> = CobblemonRecipeTypes.COOKING_POT_SHAPELESS
|
||||
|
||||
override fun getGroup(): String {
|
||||
return group
|
||||
}
|
||||
|
||||
fun category(): CookingPotBookCategory {
|
||||
return category
|
||||
}
|
||||
|
||||
override fun getResultItem(registries: HolderLookup.Provider): ItemStack {
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getIngredients(): NonNullList<Ingredient> {
|
||||
return ingredients
|
||||
}
|
||||
|
||||
override fun matches(input: CraftingInput, level: Level): Boolean {
|
||||
//println("Debug: Starting matches check for CookingPotShapelessRecipe")
|
||||
|
||||
// Debug input crafting grid
|
||||
for (i in 0 until input.size()) {
|
||||
val stack = input.getItem(i)
|
||||
//println("Debug: Slot $i contains item ${stack.item} with count ${stack.count}")
|
||||
}
|
||||
|
||||
// Check ingredient count
|
||||
if (input.ingredientCount() != ingredients.size) {
|
||||
//println("Debug: Ingredient count mismatch. Expected ${ingredients.size}, but got ${input.ingredientCount()}")
|
||||
return false
|
||||
}
|
||||
|
||||
// Match ingredients in any order
|
||||
val matchedIngredients = mutableListOf<Ingredient>()
|
||||
for (item in input.items()) {
|
||||
if (item.isEmpty) continue
|
||||
|
||||
// Find matching ingredient
|
||||
val matchingIngredient = ingredients.find { it.test(item) && it !in matchedIngredients }
|
||||
if (matchingIngredient != null) {
|
||||
matchedIngredients.add(matchingIngredient)
|
||||
//println("Debug: Matched item ${item.item} with ingredient $matchingIngredient")
|
||||
} else {
|
||||
//println("Debug: No matching ingredient found for item ${item.item}")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val matches = matchedIngredients.size == ingredients.size
|
||||
//println("Debug: Final matches result: $matches")
|
||||
return matches
|
||||
}
|
||||
|
||||
override fun assemble(input: CraftingInput, registries: HolderLookup.Provider): ItemStack {
|
||||
return result.copy()
|
||||
}
|
||||
|
||||
override fun canCraftInDimensions(width: Int, height: Int): Boolean {
|
||||
return width * height >= ingredients.size
|
||||
}
|
||||
|
||||
class Serializer : RecipeSerializer<CookingPotShapelessRecipe> {
|
||||
companion object {
|
||||
private val CODEC: MapCodec<CookingPotShapelessRecipe> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
Codec.STRING.optionalFieldOf("group", "").forGetter { it.group },
|
||||
CookingPotBookCategory.CODEC.fieldOf("category").orElse(CookingPotBookCategory.MISC).forGetter { it.category },
|
||||
ItemStack.STRICT_CODEC.fieldOf("result").forGetter { it.result },
|
||||
Ingredient.CODEC_NONEMPTY.listOf().fieldOf("ingredients").flatXmap({ list ->
|
||||
val ingredients = list.filter { !it.isEmpty }.toTypedArray()
|
||||
when {
|
||||
ingredients.isEmpty() -> DataResult.error { "No ingredients for shapeless recipe" }
|
||||
ingredients.size > 9 -> DataResult.error { "Too many ingredients for shapeless recipe" }
|
||||
else -> DataResult.success(NonNullList.of(Ingredient.EMPTY, *ingredients))
|
||||
}
|
||||
}, { DataResult.success(it) }).forGetter { it.ingredients }
|
||||
).apply(instance, ::CookingPotShapelessRecipe)
|
||||
}
|
||||
|
||||
val STREAM_CODEC: StreamCodec<RegistryFriendlyByteBuf, CookingPotShapelessRecipe> =
|
||||
StreamCodec.of(::toNetwork, ::fromNetwork)
|
||||
|
||||
private fun fromNetwork(buffer: RegistryFriendlyByteBuf): CookingPotShapelessRecipe {
|
||||
val group = buffer.readUtf()
|
||||
val category = buffer.readEnum(CookingPotBookCategory::class.java)
|
||||
val size = buffer.readVarInt()
|
||||
val ingredients = NonNullList.withSize(size, Ingredient.EMPTY)
|
||||
ingredients.replaceAll { Ingredient.CONTENTS_STREAM_CODEC.decode(buffer) }
|
||||
val result = ItemStack.STREAM_CODEC.decode(buffer)
|
||||
return CookingPotShapelessRecipe(group, category, result, ingredients)
|
||||
}
|
||||
|
||||
private fun toNetwork(buffer: RegistryFriendlyByteBuf, recipe: CookingPotShapelessRecipe) {
|
||||
buffer.writeUtf(recipe.group)
|
||||
buffer.writeEnum(recipe.category)
|
||||
buffer.writeVarInt(recipe.ingredients.size)
|
||||
recipe.ingredients.forEach { ingredient ->
|
||||
Ingredient.CONTENTS_STREAM_CODEC.encode(buffer, ingredient)
|
||||
}
|
||||
ItemStack.STREAM_CODEC.encode(buffer, recipe.result)
|
||||
}
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<CookingPotShapelessRecipe> {
|
||||
return CODEC
|
||||
}
|
||||
|
||||
override fun streamCodec(): StreamCodec<RegistryFriendlyByteBuf, CookingPotShapelessRecipe> {
|
||||
return STREAM_CODEC
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.client.gui.cookingpot
|
||||
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class SeasoningSlot(container: Container, slot: Int, x: Int, y: Int) : Slot(container, slot, x, y) {
|
||||
override fun mayPlace(stack: ItemStack): Boolean {
|
||||
return Seasonings.isSeasoning(stack) && super.mayPlace(stack)
|
||||
}
|
||||
}
|
||||
+47
@@ -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.client.net.cooking
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.block.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidPacket
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.raycast
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.ClipContext
|
||||
|
||||
object ToggleCookingPotLidHandler : ServerNetworkPacketHandler<ToggleCookingPotLidPacket> {
|
||||
override fun handle(
|
||||
packet: ToggleCookingPotLidPacket,
|
||||
server: MinecraftServer,
|
||||
player: ServerPlayer
|
||||
) {
|
||||
if (player.containerMenu !is CookingPotMenu) {
|
||||
Cobblemon.LOGGER.debug("Player {} interacted with invalid menu {}", player, player.containerMenu);
|
||||
return
|
||||
}
|
||||
|
||||
val menu = player.containerMenu as? CookingPotMenu ?: return
|
||||
val isLidOpen = if (packet.value) 1 else 0
|
||||
menu.containerData.set(IS_LID_OPEN_INDEX, isLidOpen)
|
||||
|
||||
val raycastBlockPos = player.raycast(player.blockInteractionRange().toFloat() + 1F, ClipContext.Fluid.ANY).blockPos
|
||||
if (player.level().getBlockState(raycastBlockPos).block is CampfireBlock) {
|
||||
player.level().playSoundServer(
|
||||
position = raycastBlockPos.center,
|
||||
sound = if (isLidOpen == 1) CobblemonSounds.CAMPFIRE_POT_OPEN else CobblemonSounds.CAMPFIRE_POT_CLOSE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import net.minecraft.world.phys.Vec3
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.level.Level
|
||||
import org.joml.Vector4f
|
||||
|
||||
/**
|
||||
* An instance of a bedrock particle effect.
|
||||
@@ -57,6 +58,7 @@ class ParticleStorm(
|
||||
val sourceVisible: () -> Boolean = { true },
|
||||
val targetPos: (() -> Vec3)? = null,
|
||||
val onDespawn: () -> Unit = {},
|
||||
val getParticleColor: () -> Vector4f? = { null },
|
||||
val runtime: MoLangRuntime = MoLangRuntime(),
|
||||
val entity: Entity? = null,
|
||||
): NoRenderParticle(world, emitterSpaceMatrix.getOrigin().x, emitterSpaceMatrix.getOrigin().y, emitterSpaceMatrix.getOrigin().z) {
|
||||
@@ -239,7 +241,12 @@ class ParticleStorm(
|
||||
remove()
|
||||
}
|
||||
|
||||
if (stopped || !sourceVisible()) {
|
||||
if (!sourceVisible()) {
|
||||
this.setInvisible()
|
||||
return
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -317,4 +324,8 @@ class ParticleStorm(
|
||||
fun distanceTo(destinationPos: Vec3): Vec3 {
|
||||
return emitterSpaceMatrix.transformWorldToParticle(Vec3(x, y, z)).subtract(emitterSpaceMatrix.transformWorldToParticle(destinationPos))
|
||||
}
|
||||
}
|
||||
|
||||
fun setInvisible() {
|
||||
particles.forEach { particle -> particle.invisible = true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.client.pot
|
||||
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
enum class CookingQuality {
|
||||
LOW {
|
||||
override fun getLang(): Component = lang("cooking.cooking_quality.low").red()
|
||||
},
|
||||
MEDIUM {
|
||||
override fun getLang(): Component = lang("cooking.cooking_quality.medium").yellow()
|
||||
},
|
||||
HIGH {
|
||||
override fun getLang(): Component = lang("cooking.cooking_quality.high").green()
|
||||
};
|
||||
|
||||
abstract fun getLang(): Component
|
||||
}
|
||||
@@ -130,6 +130,10 @@ class SnowstormParticle(
|
||||
}
|
||||
|
||||
override fun render(vertexConsumer: VertexConsumer, camera: Camera, tickDelta: Float) {
|
||||
if (invisible) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Cobblemon.implementation.modAPI != ModAPI.FORGE) {
|
||||
if (!Minecraft.getInstance().levelRenderer.cullingFrustum.isVisible(boundingBox)) {
|
||||
return
|
||||
@@ -199,7 +203,7 @@ class SnowstormParticle(
|
||||
}
|
||||
|
||||
val uvs = storm.effect.particle.uvMode.get(runtime, age / 20.0, lifetime / 20.0, uvDetails)
|
||||
val colour = storm.effect.particle.tinting.getTint(runtime)
|
||||
val colour = storm.getParticleColor() ?: storm.effect.particle.tinting.getTint(runtime)
|
||||
|
||||
val spriteURange = sprite.u1 - sprite.u0
|
||||
val spriteVRange = sprite.v1 - sprite.v0
|
||||
@@ -531,4 +535,4 @@ class SnowstormParticle(
|
||||
super.remove()
|
||||
storm.particles.remove(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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.api.cooking.getColor
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity.Companion.MAX_NUMBER_OF_BITES
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.inventory.InventoryMenu.BLOCK_ATLAS
|
||||
import org.joml.Vector3d
|
||||
import kotlin.random.Random
|
||||
|
||||
open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<CakeBlockEntity> {
|
||||
|
||||
companion object {
|
||||
const val PIXEL_SHIFT = 1.0 / 16.0
|
||||
const val WHITE_OPAQUE_COLOR = 0xFFFFFFFF.toInt()
|
||||
|
||||
const val ATLAS_START = 0.0f
|
||||
const val ATLAS_END = 1.0f
|
||||
const val HEIGHT = 0.5f
|
||||
const val TOP_LAYER_HEIGHT = 0.5625f
|
||||
const val BOTTOM_LAYER_HEIGHT = 0.0f
|
||||
|
||||
object DefaultCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
object HeartCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer4", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer5", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
val CAKE_LAYOUTS = listOf(DefaultCakeLayout, HeartCakeLayout)
|
||||
|
||||
fun loadTexture(textureName: String): TextureAtlasSprite {
|
||||
return Minecraft.getInstance().getTextureAtlas(BLOCK_ATLAS).apply(ResourceLocation("cobblemon", textureName))
|
||||
}
|
||||
}
|
||||
|
||||
interface CakeLayout {
|
||||
val topLayers: List<CakeTexture>
|
||||
val sideLayers: List<CakeTexture>
|
||||
val bottomLayers: List<CakeTexture>
|
||||
val innerLayers: List<CakeTexture>
|
||||
}
|
||||
|
||||
class CakeTexture(textureName: String, val color: LayerColor) {
|
||||
|
||||
val texture = loadTexture("block/${textureName}")
|
||||
|
||||
fun getColor(primaryColor: Int, secondaryColor: Int, tertiaryColor: Int): Int {
|
||||
return when (color) {
|
||||
LayerColor.PRIMARY -> primaryColor
|
||||
LayerColor.SECONDARY -> secondaryColor
|
||||
LayerColor.TERTIARY -> tertiaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class LayerColor {
|
||||
PRIMARY, SECONDARY, TERTIARY
|
||||
}
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
val cookingComponent = blockEntity.cookingComponent
|
||||
renderCake(cookingComponent, poseStack, bufferSource, packedLight, blockEntity.bites)
|
||||
}
|
||||
|
||||
fun renderCake(
|
||||
cookingComponent: CookingComponent?,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
bites: Int,
|
||||
) {
|
||||
val seedString = "${cookingComponent?.seasoning1?.color}${cookingComponent?.seasoning2?.color}${cookingComponent?.seasoning3?.color}"
|
||||
val random = Random(seedString.hashCode())
|
||||
val cakeLayout: CakeLayout = CAKE_LAYOUTS[random.nextInt(CAKE_LAYOUTS.size)]
|
||||
|
||||
val maxBites = MAX_NUMBER_OF_BITES + 1f
|
||||
val cakeIsFull = bites.toFloat() == 0f
|
||||
val percentageToRender = if (cakeIsFull) 1.0f else PIXEL_SHIFT.toFloat() + (PIXEL_SHIFT.toFloat() * 2f * (maxBites - bites).toFloat())
|
||||
val clippedWidth = (ATLAS_END - ATLAS_START) * percentageToRender
|
||||
val topLayer = cakeLayout.topLayers.first().texture
|
||||
val uClipped = topLayer.u0 + (topLayer.u1 - topLayer.u0) * percentageToRender
|
||||
|
||||
val vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout())
|
||||
val primaryColor = cookingComponent?.seasoning1?.color?.let { getColor(it) } ?: WHITE_OPAQUE_COLOR
|
||||
val secondaryColor = cookingComponent?.seasoning2?.color?.let { getColor(it) } ?: WHITE_OPAQUE_COLOR
|
||||
val tertiaryColor = cookingComponent?.seasoning3?.color?.let { getColor(it) } ?: WHITE_OPAQUE_COLOR
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.5, 0.5)
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(180f))
|
||||
poseStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
poseStack.pushPose()
|
||||
for (layer in cakeLayout.topLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, TOP_LAYER_HEIGHT, ATLAS_START, clippedWidth, TOP_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
poseStack.popPose()
|
||||
|
||||
val translations = arrayOf(
|
||||
Vector3d(-1.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
Vector3d(-1.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
)
|
||||
|
||||
val rotationsY = arrayOf(
|
||||
Axis.YP.rotationDegrees(90f),
|
||||
Axis.YP.rotationDegrees(0f),
|
||||
Axis.YP.rotationDegrees(-90f),
|
||||
Axis.YP.rotationDegrees(-180f)
|
||||
)
|
||||
|
||||
for (i in translations.indices) {
|
||||
poseStack.pushPose()
|
||||
poseStack.mulPose(rotationsY[i])
|
||||
poseStack.translate(translations[i].x, translations[i].y, translations[i].z)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
|
||||
|
||||
if (i == 0 && !cakeIsFull) {
|
||||
poseStack.translate(0.0, -bites * PIXEL_SHIFT * 2, 0.0)
|
||||
for (layer in cakeLayout.innerLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 0 || i == 1) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, clippedWidth, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 2) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 3) {
|
||||
val clippedStartX = ATLAS_START + (ATLAS_END - ATLAS_START) * (1 - percentageToRender)
|
||||
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
val clippedUStart = texture.u0 + (texture.u1 - texture.u0) * (1 - percentageToRender)
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
clippedStartX, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
clippedUStart, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.0, 0.5)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(180f))
|
||||
poseStack.translate(-0.5, 0.0, -0.5)
|
||||
|
||||
for (layer in cakeLayout.bottomLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, BOTTOM_LAYER_HEIGHT, ATLAS_START, clippedWidth, BOTTOM_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun drawQuad(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x0: Float, y0: Float, z0: Float,
|
||||
x1: Float, y1: Float, z1: Float,
|
||||
u0: Float, v0: Float, u1: Float, v1: Float,
|
||||
packedLight: Int, color: Int,
|
||||
) {
|
||||
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color)
|
||||
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y1, z1, u1, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y0, z0, u1, v0, packedLight, color)
|
||||
}
|
||||
|
||||
private fun drawVertex(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x: Float, y: Float, z: Float,
|
||||
u: Float, v: Float,
|
||||
packedLight: Int, color: Int
|
||||
) {
|
||||
builder.addVertex(poseStack.last().pose(), x, y, z)
|
||||
.setColor(color)
|
||||
.setUv(u, v)
|
||||
.setLight(packedLight)
|
||||
.setNormal(0f, 1f, 0f)
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.CampfirePotBlock
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.item.CampfirePotItem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.inventory.InventoryMenu
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock.FACING
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class CampfireBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<CampfireBlockEntity> {
|
||||
|
||||
companion object {
|
||||
const val BROTH_START = 0F
|
||||
const val BROTH_END = 1F
|
||||
const val BROTH_HEIGHT = 0.3F
|
||||
val BROTH_BASIC_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot_broth_basic"))
|
||||
val BROTH_SPRITE: TextureAtlasSprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(ResourceLocation("cobblemon", "block/campfire_pot_broth"))
|
||||
|
||||
const val CIRCLE_RADIUS = 0.4F
|
||||
const val ROTATION_SPEED = 1.5F
|
||||
const val JUMP_AMPLITUDE = 0.025F
|
||||
const val JUMP_SPEED = 0.1F
|
||||
}
|
||||
|
||||
override fun render(
|
||||
blockEntity: CampfireBlockEntity,
|
||||
tickDelta: Float,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val campfirePotItem = blockEntity.getPotItem()?.item as CampfirePotItem?
|
||||
if (campfirePotItem == null) return
|
||||
|
||||
poseStack.pushPose()
|
||||
|
||||
val facing = blockEntity.blockState.getValue(FACING)
|
||||
val rotationAngle = when (facing) {
|
||||
Direction.NORTH -> 180F
|
||||
Direction.SOUTH -> 0F
|
||||
Direction.WEST -> 90F
|
||||
Direction.EAST -> -90F
|
||||
else -> 0f
|
||||
}
|
||||
|
||||
poseStack.translate(0.5, 0.5, 0.5)
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(rotationAngle))
|
||||
poseStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
renderPot(campfirePotItem, blockEntity, tickDelta, poseStack, multiBufferSource, light, overlay)
|
||||
if (!blockEntity.getSeasonings().isEmpty() || !blockEntity.getIngredients().isEmpty()) renderBroth(blockEntity, tickDelta, poseStack, multiBufferSource, light, overlay)
|
||||
|
||||
renderSeasonings(blockEntity, tickDelta, poseStack, multiBufferSource, light, overlay)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun renderPot(
|
||||
campfirePotItem: CampfirePotItem,
|
||||
blockEntity: CampfireBlockEntity,
|
||||
tickDelta: Float,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val isLidOpen = blockEntity.dataAccess.get(IS_LID_OPEN_INDEX) == 1
|
||||
|
||||
val yRot = (blockEntity.blockState.getValue(CampfireBlock.ITEM_DIRECTION).opposite.toYRot() + blockEntity.blockState.getValue(FACING).toYRot()) % 360
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, 0.4375, 0.0)
|
||||
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
campfirePotItem.block.defaultBlockState()
|
||||
.setValue(CampfirePotBlock.OPEN, isLidOpen)
|
||||
.setValue(FACING, Direction.fromYRot(yRot.toDouble())),
|
||||
poseStack,
|
||||
multiBufferSource,
|
||||
light,
|
||||
overlay
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun renderBroth(
|
||||
blockEntity: CampfireBlockEntity,
|
||||
tickDelta: Float,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
poseStack.pushPose()
|
||||
|
||||
val vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout())
|
||||
|
||||
poseStack.translate(0.0, 0.4, 0.0)
|
||||
poseStack.scale(1.0f, 1.0F, 1.0F)
|
||||
|
||||
val brothTextureSprite = if (!blockEntity.getSeasonings().isEmpty()) BROTH_SPRITE else BROTH_BASIC_SPRITE
|
||||
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
BROTH_START, BROTH_HEIGHT, BROTH_START, BROTH_END, BROTH_HEIGHT, BROTH_END,
|
||||
brothTextureSprite.u0, brothTextureSprite.v0, brothTextureSprite.u1, brothTextureSprite.v1,
|
||||
light,
|
||||
blockEntity.brothColor
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun renderSeasonings(
|
||||
blockEntity: CampfireBlockEntity,
|
||||
tickDelta: Float,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val seasonings = blockEntity.getSeasonings()
|
||||
|
||||
val gameTime = (blockEntity.level?.gameTime ?: 0) + tickDelta
|
||||
val rotationAngle = (gameTime * ROTATION_SPEED) % 360
|
||||
|
||||
seasonings.forEachIndexed { index, seasoning ->
|
||||
poseStack.pushPose()
|
||||
poseStack.scale(0.5F, 0.5F, 0.5F)
|
||||
|
||||
val angleOffset = index * (360f / seasonings.size)
|
||||
val angleInRadians = Math.toRadians((rotationAngle + angleOffset).toDouble())
|
||||
|
||||
val xOffset = cos(angleInRadians.toDouble()).toFloat() * CIRCLE_RADIUS
|
||||
val zOffset = sin(angleInRadians.toDouble()).toFloat() * CIRCLE_RADIUS
|
||||
val jumpOffset = sin(gameTime * JUMP_SPEED + index * 2) * JUMP_AMPLITUDE
|
||||
poseStack.translate(1F + xOffset, 1.24F + jumpOffset, 1F + zOffset)
|
||||
val lookAtDirection = Vector3f(1f + xOffset - 1F, 0F, 1F + zOffset - 1F)
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees((-Math.toDegrees(
|
||||
atan2(
|
||||
lookAtDirection.z().toDouble(),
|
||||
lookAtDirection.x().toDouble()
|
||||
)
|
||||
)).toFloat() + 90))
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(22.5F))
|
||||
|
||||
Minecraft.getInstance().itemRenderer.renderStatic(
|
||||
seasoning,
|
||||
ItemDisplayContext.GROUND,
|
||||
light,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
poseStack,
|
||||
multiBufferSource,
|
||||
blockEntity.level,
|
||||
0
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawQuad(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x0: Float, y0: Float, z0: Float,
|
||||
x1: Float, y1: Float, z1: Float,
|
||||
u0: Float, v0: Float, u1: Float, v1: Float,
|
||||
packedLight: Int, color: Int
|
||||
) {
|
||||
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color)
|
||||
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y1, z1, u1, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y0, z0, u1, v0, packedLight, color)
|
||||
}
|
||||
|
||||
private fun drawVertex(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x: Float, y: Float, z: Float,
|
||||
u: Float, v: Float,
|
||||
packedLight: Int, color: Int
|
||||
) {
|
||||
builder.addVertex(poseStack.last().pose(), x, y, z)
|
||||
.setColor(color)
|
||||
.setUv(u, v)
|
||||
.setLight(packedLight)
|
||||
.setNormal(0f, 1f, 0f)
|
||||
}
|
||||
}
|
||||
+48
@@ -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.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.getCandleByMapColorId
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
|
||||
class CandlePokeCakeBlockEntityRenderer(
|
||||
ctx: BlockEntityRendererProvider.Context,
|
||||
) : CakeBlockEntityRenderer(ctx) {
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
super.render(blockEntity, partialTick, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, TOP_LAYER_HEIGHT.toDouble(), 0.0)
|
||||
|
||||
val candleBlock = getCandleByMapColorId(blockEntity.blockState.getValue(CANDLE_COLOR))
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
candleBlock.defaultBlockState().setValue(LIT, blockEntity.blockState.getValue(LIT)),
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
packedOverlay
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -36,7 +36,7 @@ class LecternBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : Blo
|
||||
if (blockEntity !is LecternBlockEntity) return
|
||||
if (!blockEntity.isEmpty()) {
|
||||
val blockState = if (blockEntity.level != null) blockEntity.blockState
|
||||
else (CobblemonBlocks.LECTERN.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, Direction.SOUTH) as BlockState)
|
||||
else (CobblemonBlocks.CAMPFIRE.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, Direction.SOUTH) as BlockState)
|
||||
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
|
||||
|
||||
poseStack.pushPose()
|
||||
|
||||
+47
@@ -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.client.render.color
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.cooking.getColorMixFromCookingComponent
|
||||
import com.cobblemon.mod.common.client.pot.CookingQuality
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object AprijuiceItemColorProvider : ItemColor {
|
||||
|
||||
private const val LEAF_INDEX = 1
|
||||
private const val JUICE_INDEX = 2
|
||||
|
||||
override fun getColor(stack: ItemStack, layer: Int): Int {
|
||||
if (layer == 0) return -1
|
||||
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return -1
|
||||
|
||||
if (layer == LEAF_INDEX) {
|
||||
val quality = cookingComponent.getCookingQuality()
|
||||
val color = when (quality) {
|
||||
CookingQuality.LOW -> ChatFormatting.RED.color
|
||||
CookingQuality.MEDIUM -> ChatFormatting.YELLOW.color
|
||||
CookingQuality.HIGH -> ChatFormatting.GREEN.color
|
||||
}
|
||||
|
||||
color?.let { return FastColor.ARGB32.opaque(color) }
|
||||
}
|
||||
|
||||
if (layer == JUICE_INDEX) {
|
||||
val colorMix = getColorMixFromCookingComponent(cookingComponent)
|
||||
if (colorMix != null) return colorMix
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.color
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import com.cobblemon.mod.common.api.cooking.getColor
|
||||
|
||||
object PokeBaitItemColorProvider : ItemColor {
|
||||
override fun getColor(stack: ItemStack, layer: Int): Int {
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return -1
|
||||
|
||||
val primaryColor = cookingComponent.seasoning1.color
|
||||
val secondaryColor = cookingComponent.seasoning2.color
|
||||
val tertiaryColor = cookingComponent.seasoning3.color
|
||||
|
||||
val color = when (layer) {
|
||||
0 -> getColor(primaryColor)
|
||||
1 -> getColor(secondaryColor)
|
||||
2 -> getColor(tertiaryColor)
|
||||
else -> -1
|
||||
} ?: -1
|
||||
|
||||
return color
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.tooltips
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.item.AprijuiceItem
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object AprijuiceTooltipGenerator : TooltipGenerator() {
|
||||
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.item !is AprijuiceItem) return null
|
||||
return mutableListOf(seasoningHeader)
|
||||
}
|
||||
|
||||
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.item !is AprijuiceItem) return null
|
||||
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return null
|
||||
|
||||
val resultLines = mutableListOf<Component>()
|
||||
|
||||
val quality = cookingComponent.getCookingQuality()
|
||||
resultLines.add(lang("cooking.cooking_quality", quality.getLang()))
|
||||
|
||||
val flavors: Map<String, Int> = cookingComponent.getFlavorsSum()
|
||||
resultLines.addAll(generateAdditionalFlavorTooltip(flavors))
|
||||
|
||||
return resultLines
|
||||
}
|
||||
}
|
||||
+19
-5
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.tooltips
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBaits
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
@@ -36,14 +37,25 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
|
||||
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
val resultLines = mutableListOf<Component>()
|
||||
val bait =
|
||||
(if (stack.item is PokerodItem) PokerodItem.getBaitOnRod(stack) else FishingBaits.getFromBaitItemStack(stack))
|
||||
?: return null
|
||||
|
||||
// Determine the FishingBait or combined effects from poke_bait
|
||||
val baitEffects = when {
|
||||
(stack.item.asItem()?.toString() == "cobblemon:poke_bait" || stack.item.asItem()?.toString() == "cobblemon:lure_cake") -> {
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return null
|
||||
listOf(cookingComponent.bait1, cookingComponent.bait2, cookingComponent.bait3)
|
||||
.flatMap { it.effects }
|
||||
}
|
||||
stack.item is PokerodItem -> PokerodItem.getBaitOnRod(stack)?.effects
|
||||
else -> FishingBaits.getFromBaitItemStack(stack)?.effects
|
||||
} ?: return null
|
||||
|
||||
if (baitEffects.isEmpty()) return null
|
||||
|
||||
resultLines.addLast(this.fishingBaitHeader)
|
||||
|
||||
val formatter = DecimalFormat("0.##")
|
||||
|
||||
bait.effects.forEach { effect ->
|
||||
baitEffects.forEach { effect ->
|
||||
val effectType = effect.type.path.toString()
|
||||
val effectSubcategory = effect.subcategory?.path
|
||||
val effectChance = effect.chance * 100
|
||||
@@ -51,6 +63,7 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
"bite_time" -> (effect.value * 100).toInt()
|
||||
else -> effect.value.toInt()
|
||||
}
|
||||
|
||||
val subcategoryString: Component = if (effectSubcategory != null) {
|
||||
when (effectType) {
|
||||
"nature", "ev", "iv" -> com.cobblemon.mod.common.api.pokemon.stats.Stats.getStat(
|
||||
@@ -74,7 +87,7 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
} ?: Component.literal("cursed").obfuscate()
|
||||
} else Component.literal("cursed").obfuscate()
|
||||
|
||||
// handle reformatting of shiny chance effectChance
|
||||
// Adjust shiny chance effectValue
|
||||
if (effectType == "shiny_reroll") {
|
||||
effectValue++
|
||||
}
|
||||
@@ -91,4 +104,5 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
|
||||
return resultLines
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.tooltips
|
||||
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
val seasoningHeader by lazy { lang("item_class.seasoning").blue() }
|
||||
private val flavorSubHeader by lazy { lang("seasoning_flavor_header").blue() }
|
||||
|
||||
fun generateAdditionalFlavorTooltip(flavors: Map<String, Int>): MutableList<Component> {
|
||||
val resultLines = mutableListOf<Component>()
|
||||
resultLines.add(flavorSubHeader)
|
||||
|
||||
val combinedFlavorsLine = Component.literal("")
|
||||
flavors.forEach { (flavor, value) ->
|
||||
val flavorLangKey = when (flavor.lowercase()) {
|
||||
"spicy" -> lang("seasoning_flavor.spicy").red()
|
||||
"dry" -> lang("seasoning_flavor.dry").darkAqua()
|
||||
"sweet" -> lang("seasoning_flavor.sweet").lightPurple()
|
||||
"bitter" -> lang("seasoning_flavor.bitter").green()
|
||||
"sour" -> lang("seasoning_flavor.sour").yellow()
|
||||
else -> lang("seasoning_flavor.unknown").gray() // Default for unknown flavors
|
||||
}
|
||||
|
||||
if (combinedFlavorsLine.string.isNotEmpty()) {
|
||||
combinedFlavorsLine.append(" ")
|
||||
}
|
||||
|
||||
combinedFlavorsLine.append(flavorLangKey).append(" $value")
|
||||
}
|
||||
|
||||
resultLines.add(combinedFlavorsLine)
|
||||
return resultLines
|
||||
}
|
||||
+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.client.tooltips
|
||||
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object SeasoningTooltipGenerator : TooltipGenerator() {
|
||||
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (!Seasonings.isSeasoning(stack)) return null
|
||||
return mutableListOf(seasoningHeader)
|
||||
}
|
||||
|
||||
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
val flavors: Map<String, Int>? = Seasonings.getFromItemStack(stack)?.flavors
|
||||
flavors?.let {
|
||||
return generateAdditionalFlavorTooltip(flavors)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ import com.cobblemon.mod.common.CobblemonBrainConfigs
|
||||
import com.cobblemon.mod.common.CobblemonMechanics
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.api.cooking.Seasoning
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.api.data.DataProvider
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.pokedex.Dexes
|
||||
@@ -97,6 +99,7 @@ object CobblemonDataProvider : DataProvider {
|
||||
CobblemonSpawnPools.load()
|
||||
this.register(PokeRods)
|
||||
this.register(Berries)
|
||||
this.register(Seasonings)
|
||||
this.register(FishingBaits)
|
||||
FishingBait.Effects.setupEffects()
|
||||
|
||||
|
||||
@@ -21,14 +21,16 @@ import net.minecraft.world.level.block.state.properties.WoodType
|
||||
*/
|
||||
enum class CobblemonBoatType(val mountedOffset: Float) : StringRepresentable {
|
||||
|
||||
APRICORN(-0.1f);
|
||||
APRICORN(-0.1f),
|
||||
SACCHARINE(-.01f);
|
||||
|
||||
/**
|
||||
* The base [Item] form of this [CobblemonBoatType].
|
||||
*/
|
||||
val boatItem: Item
|
||||
get() = when (this) {
|
||||
APRICORN -> CobblemonItems.APRICORN_BOAT
|
||||
APRICORN -> CobblemonItems.APRICORN_BOAT
|
||||
SACCHARINE -> CobblemonItems.SACCHARINE_BOAT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,6 +38,7 @@ enum class CobblemonBoatType(val mountedOffset: Float) : StringRepresentable {
|
||||
*/
|
||||
val chestBoatItem: Item get() = when (this) {
|
||||
APRICORN -> CobblemonItems.APRICORN_CHEST_BOAT
|
||||
SACCHARINE -> CobblemonItems.SACCHARINE_CHEST_BOAT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,7 @@ enum class CobblemonBoatType(val mountedOffset: Float) : StringRepresentable {
|
||||
*/
|
||||
val baseBlock: Block get() = when (this) {
|
||||
APRICORN -> CobblemonBlocks.APRICORN_PLANKS
|
||||
SACCHARINE -> CobblemonBlocks.SACCHARINE_PLANKS
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +54,7 @@ enum class CobblemonBoatType(val mountedOffset: Float) : StringRepresentable {
|
||||
*/
|
||||
val woodType: WoodType get() = when (this) {
|
||||
APRICORN -> CobblemonBlocks.APRICORN_WOOD_TYPE
|
||||
SACCHARINE -> CobblemonBlocks.SACCHARINE_WOOD_TYPE
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
-7
@@ -30,6 +30,7 @@ import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.client.sound.EntitySoundTracker
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem
|
||||
import com.cobblemon.mod.common.item.interactive.PokerodItem.Companion.getCookingComponentOnRod
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -317,7 +318,7 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
serverWorld.sendParticles(ParticleTypes.FISHING, this.x, m, this.z, (1.0f + this.bbWidth * 20.0f).toInt(), this.bbWidth.toDouble(), 0.0, this.bbWidth.toDouble(), 0.2)
|
||||
|
||||
// check for chance to catch pokemon based on the bait
|
||||
if (Mth.nextInt(random, 0, 100) < getPokemonSpawnChance(bobberBait)) {
|
||||
if (Mth.nextInt(random, 0, 100) < getPokemonSpawnChance(this.rodStack ?: bobberBait)) {
|
||||
this.typeCaught = TypeCaught.POKEMON
|
||||
|
||||
val buckets = Cobblemon.bestSpawner.config.buckets
|
||||
@@ -388,8 +389,8 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
this.waitCountdown = 1
|
||||
else {
|
||||
// check for the bait on the hook and see if the waitCountdown is reduced
|
||||
if (checkReduceBiteTime(bobberBait))
|
||||
this.waitCountdown = alterBiteTimeAttempt(this.waitCountdown, this.bobberBait)
|
||||
if (checkReduceBiteTime(this.rodStack ?: bobberBait))
|
||||
this.waitCountdown = alterBiteTimeAttempt(this.waitCountdown, this.rodStack ?: bobberBait)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -755,19 +756,19 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
|
||||
// function to return true of false if the given bait affects time to expect a bite
|
||||
fun checkReduceBiteTime(stack: ItemStack): Boolean {
|
||||
val bait = FishingBaits.getFromBaitItemStack(stack) ?: return false
|
||||
val bait = PokerodItem.getBaitOnRod(stack) ?: return false
|
||||
return bait.effects.any { it.type == FishingBait.Effects.BITE_TIME }
|
||||
}
|
||||
|
||||
// function to return true of false if the given bait to make it so a Pokemon is always reeled in
|
||||
fun checkPokemonFishRate(stack: ItemStack): Boolean {
|
||||
val bait = FishingBaits.getFromBaitItemStack(stack) ?: return false
|
||||
val bait = PokerodItem.getBaitOnRod(stack) ?: return false
|
||||
return bait.effects.any { it.type == FishingBait.Effects.POKEMON_CHANCE }
|
||||
}
|
||||
|
||||
// check if the bite time is reduced based on the bait bonus
|
||||
fun alterBiteTimeAttempt(waitCountdown: Int, stack: ItemStack): Int {
|
||||
val bait = FishingBaits.getFromBaitItemStack(stack) ?: return waitCountdown
|
||||
val bait = PokerodItem.getBaitOnRod(stack) ?: return waitCountdown
|
||||
val effect = bait.effects.filter { it.type == FishingBait.Effects.BITE_TIME }.random()
|
||||
if (!checkBaitSuccessRate(effect.chance)) return waitCountdown
|
||||
return if (waitCountdown - waitCountdown * (effect.value) <= 0)
|
||||
@@ -778,7 +779,7 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
|
||||
// check the chance of a pokemon to spawn and if it is affected by bait
|
||||
fun getPokemonSpawnChance(stack: ItemStack): Int {
|
||||
val bait = FishingBaits.getFromBaitItemStack(stack) ?: return this.pokemonSpawnChance
|
||||
val bait = PokerodItem.getBaitOnRod(stack) ?: return this.pokemonSpawnChance
|
||||
val effectList = bait.effects.filter { it.type == FishingBait.Effects.POKEMON_CHANCE }
|
||||
if (effectList.isEmpty()) return this.pokemonSpawnChance
|
||||
val effect = effectList.random()
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.integration.jei
|
||||
|
||||
import com.cobblemon.mod.common.integration.jei.berry.BerryMutationProvider
|
||||
import com.cobblemon.mod.common.integration.jei.berry.BerryRecipeCategory
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import mezz.jei.api.IModPlugin
|
||||
import mezz.jei.api.JeiPlugin
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.client.cooking.AprijuiceTypes
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.UseAnim
|
||||
|
||||
class AprijuiceItem(val type: AprijuiceTypes): CobblemonItem(Properties().stacksTo(16)) {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.block.BugwortBlock
|
||||
import com.cobblemon.mod.common.block.SaccharineSaplingBlock
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class BugwortItem(block: BugwortBlock) : ItemNameBlockItem(block, Properties()) {
|
||||
|
||||
override fun getPlacementState(context: BlockPlaceContext): BlockState? {
|
||||
|
||||
return super.getPlacementState(context)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.CampfireBlock.Companion.ITEM_DIRECTION
|
||||
import com.cobblemon.mod.common.block.CampfireBlock.Companion.SOUL
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.UseOnContext
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.CampfireBlock
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.entity.CampfireBlockEntity
|
||||
|
||||
class CampfirePotItem(block: Block): BlockItem(block, Properties()) {
|
||||
|
||||
override fun useOn(context: UseOnContext): InteractionResult {
|
||||
val world = context.level
|
||||
val blockPos = context.getClickedPos()
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
val blockEntity = world.getBlockEntity(blockPos)
|
||||
val player = context.player
|
||||
|
||||
if (player != null && blockState.block is CampfireBlock && blockEntity is CampfireBlockEntity) {
|
||||
if (blockState.getValue(CampfireBlock.LIT)) {
|
||||
// Remove the existing block entity and replace with the custom CampfireBlockEntity
|
||||
val facing = blockState.getValue(HorizontalDirectionalBlock.FACING)
|
||||
val itemFacing = Direction.fromYRot(player.getYHeadRot().toDouble())
|
||||
val isSoul = blockState.block.asItem().toString() == "minecraft:soul_campfire"
|
||||
|
||||
// Drop items stored in campfire
|
||||
for (item in blockEntity.items) {
|
||||
if (!item.isEmpty) {
|
||||
val itemEntity = ItemEntity(
|
||||
world,
|
||||
blockPos.x + 0.5,
|
||||
(blockPos.y + 1).toDouble(),
|
||||
blockPos.z + 0.5, item
|
||||
)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
world.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
blockEntity.setRemoved()
|
||||
|
||||
val newBlockState = CobblemonBlocks.CAMPFIRE.defaultBlockState()
|
||||
.setValue(HorizontalDirectionalBlock.FACING, facing)
|
||||
.setValue(ITEM_DIRECTION, itemFacing)
|
||||
.setValue(SOUL, isSoul)
|
||||
world.setBlockAndUpdate(blockPos, newBlockState)
|
||||
|
||||
// Retrieve the new block entity and set the PotItem
|
||||
val newBlockEntity = world.getBlockEntity(blockPos)
|
||||
if (newBlockEntity is com.cobblemon.mod.common.block.entity.CampfireBlockEntity) {
|
||||
if (newBlockEntity.getPotItem() == null || newBlockEntity.getPotItem()!!.isEmpty) {
|
||||
newBlockEntity.setPotItem(ItemStack(this).split(1))
|
||||
if (!player.isCreative) context.itemInHand.consumeAndReturn(1, player)
|
||||
world.playSound(null, blockPos, CobblemonSounds.CAMPFIRE_POT_PLACE_CAMPFIRE, SoundSource.BLOCKS, 0.7F, 1.0F)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't place pot if campfire is not lit
|
||||
else if (!player.isCrouching) {
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
}
|
||||
|
||||
return super.useOn(context)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.block.HeartyGrainsBlock
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.PlaceOnWaterBlockItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.item.context.UseOnContext
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class HeartyGrainsItem(block: HeartyGrainsBlock) : ItemNameBlockItem(block, Properties()) {
|
||||
|
||||
init {
|
||||
Cobblemon.implementation.registerCompostable(this, 0.65F)
|
||||
}
|
||||
|
||||
override fun useOn(context: UseOnContext): InteractionResult {
|
||||
val world = context.level
|
||||
val clickedPos = context.clickedPos
|
||||
val player = context.player ?: return InteractionResult.FAIL
|
||||
|
||||
if (world.isClientSide) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
val clickedBlockState = world.getBlockState(clickedPos)
|
||||
val fluidState = world.getFluidState(clickedPos)
|
||||
val blockAbove = world.getBlockState(clickedPos.above())
|
||||
val fluidStateAbove = world.getFluidState(clickedPos.above())
|
||||
val block2Above = world.getBlockState(clickedPos.above(2))
|
||||
val fluidState2Above = world.getFluidState(clickedPos.above(2))
|
||||
val blockBelow = world.getBlockState(clickedPos.below())
|
||||
|
||||
|
||||
// 1. Prioritize source water block
|
||||
if (fluidState.`is`(FluidTags.WATER) && fluidState.isSource) {
|
||||
// Check if it's 1 block deep water
|
||||
if (blockAbove.isAir && !blockBelow.fluidState.isSource) {
|
||||
if (world.setBlock(clickedPos, this.block.defaultBlockState().setValue(HeartyGrainsBlock.WATERLOGGED, true), Block.UPDATE_CLIENTS)) {
|
||||
context.itemInHand.shrink(1)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
// Fails if not 1 block deep
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
// 2. Check for farmland or mud
|
||||
if (clickedBlockState.`is`(Blocks.FARMLAND) || clickedBlockState.`is`(Blocks.MUD) || (fluidStateAbove.`is`(FluidTags.WATER) && fluidStateAbove.isSource && block2Above.isAir)) {
|
||||
if (world.setBlock(clickedPos.above(), this.block.defaultBlockState(), Block.UPDATE_CLIENTS)) {
|
||||
context.itemInHand.shrink(1)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Default: fail if neither condition matches
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun placeOnLand(context: UseOnContext, targetPos: BlockPos): InteractionResult {
|
||||
val world = context.level
|
||||
val targetState = this.block.defaultBlockState()
|
||||
|
||||
if (world.setBlock(targetPos, targetState, Block.UPDATE_CLIENTS)) {
|
||||
context.itemInHand.shrink(1)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
private fun placeInWater(context: UseOnContext, targetPos: BlockPos): InteractionResult {
|
||||
val world = context.level
|
||||
val targetState = this.block.defaultBlockState().setValue(HeartyGrainsBlock.WATERLOGGED, true)
|
||||
|
||||
if (world.setBlock(targetPos, targetState, Block.UPDATE_CLIENTS)) {
|
||||
context.itemInHand.shrink(1)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
return InteractionResult.FAIL
|
||||
}
|
||||
|
||||
|
||||
// todo is this needed like this? should it be simpler?
|
||||
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
val blockHitResult = PlaceOnWaterBlockItem.getPlayerPOVHitResult(world, user, ClipContext.Fluid.ANY)
|
||||
val stack = user.getItemInHand(hand)
|
||||
|
||||
if (world.isClientSide) {
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
|
||||
val clickedPos = blockHitResult.blockPos
|
||||
val clickedBlockState = world.getBlockState(clickedPos)
|
||||
val fluidState = world.getFluidState(clickedPos)
|
||||
|
||||
// 1. Prioritize source water block
|
||||
if (fluidState.`is`(FluidTags.WATER) && fluidState.isSource) {
|
||||
val blockAbove = world.getBlockState(clickedPos.above())
|
||||
val blockBelow = world.getBlockState(clickedPos.below())
|
||||
|
||||
// Check if it's 1 block deep water
|
||||
if (blockAbove.isAir && !blockBelow.fluidState.isSource) {
|
||||
if (world.setBlock(clickedPos, this.block.defaultBlockState().setValue(HeartyGrainsBlock.WATERLOGGED, true), Block.UPDATE_CLIENTS)) {
|
||||
stack.shrink(1)
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
}
|
||||
|
||||
// Fails if not 1 block deep
|
||||
return InteractionResultHolder.fail(stack)
|
||||
}
|
||||
|
||||
// 2. Check for farmland or mud
|
||||
if (clickedBlockState.`is`(Blocks.FARMLAND) || clickedBlockState.`is`(Blocks.MUD)) {
|
||||
if (world.setBlock(clickedPos.above(), this.block.defaultBlockState(), Block.UPDATE_CLIENTS)) {
|
||||
stack.shrink(1)
|
||||
return InteractionResultHolder.success(stack)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Default: fail if neither condition matches
|
||||
return InteractionResultHolder.fail(stack)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.CastPokeRodContext
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitConsumedEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.BaitSetEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.PokerodCastEvent
|
||||
import com.cobblemon.mod.common.api.events.fishing.PokerodReelEvent
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBaits
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.item.components.CookingComponent
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.enchantmentRegistry
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.SlotAccess
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.projectile.ThrowableItemProjectile
|
||||
import net.minecraft.world.inventory.ClickAction
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.FishingRodItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.Item.Properties
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.item.enchantment.Enchantments
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.collections.flatten
|
||||
import kotlin.ranges.coerceIn
|
||||
|
||||
class PokeBaitItem(val pokeRodId: ResourceLocation, settings: Properties) {//: ThrowableItemProjectile(settings) {
|
||||
|
||||
companion object {
|
||||
fun getBaitOnPokeBait(stack: ItemStack): FishingBait? {
|
||||
return getCookingComponentOnRod(stack) ?: stack.components.get(CobblemonItemComponents.BAIT)?.bait
|
||||
}
|
||||
|
||||
fun getBaitStackOnRod(stack: ItemStack): ItemStack {
|
||||
return stack.components.get(CobblemonItemComponents.BAIT)?.stack ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
fun getCookingComponentOnRod(rodStack: ItemStack): FishingBait? {
|
||||
// Check if the stack within the RodBaitComponent has a CookingComponent
|
||||
val cookingComponent = rodStack.get(CobblemonItemComponents.COOKING_COMPONENT) ?: return null
|
||||
|
||||
// Combine effects from the CookingComponent
|
||||
val combinedEffects = listOf(
|
||||
cookingComponent.bait1.effects,
|
||||
cookingComponent.bait2.effects,
|
||||
cookingComponent.bait3.effects
|
||||
).flatten()
|
||||
|
||||
// Return a new FishingBait with combined effects
|
||||
return FishingBait(
|
||||
item = BuiltInRegistries.ITEM.getKey(
|
||||
rodStack.components.get(CobblemonItemComponents.BAIT)?.stack?.item ?: ItemStack.EMPTY.item
|
||||
), // Use the rodStack's item as the bait identifier
|
||||
effects = combinedEffects
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun setBait(stack: ItemStack, bait: ItemStack) {
|
||||
CobblemonEvents.BAIT_SET.postThen(BaitSetEvent(stack, bait), { event -> }, {
|
||||
if (bait.isEmpty) {
|
||||
stack.set<RodBaitComponent>(CobblemonItemComponents.BAIT, null)
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve FishingBait and CookingComponent from the bait ItemStack
|
||||
val fishingBait = FishingBaits.getFromBaitItemStack(bait) ?: return
|
||||
val cookingComponent = bait.get(CobblemonItemComponents.COOKING_COMPONENT)
|
||||
|
||||
// Apply both RodBaitComponent and CookingComponent to the rod ItemStack
|
||||
stack.set(CobblemonItemComponents.BAIT, RodBaitComponent(fishingBait, bait))
|
||||
if (cookingComponent != null) {
|
||||
stack.set(CobblemonItemComponents.COOKING_COMPONENT, cookingComponent)
|
||||
} else {
|
||||
// Clear CookingComponent if the new bait does not have it
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun consumeBait(stack: ItemStack) {
|
||||
CobblemonEvents.BAIT_CONSUMED.postThen(BaitConsumedEvent(stack), { event -> }, {
|
||||
val baitStack = getBaitStackOnRod(stack)
|
||||
val baitCount = baitStack.count
|
||||
val cookingComponent = stack.get(CobblemonItemComponents.COOKING_COMPONENT)
|
||||
|
||||
if (baitCount == 1) {
|
||||
stack.set<RodBaitComponent>(CobblemonItemComponents.BAIT, null)
|
||||
stack.set<CookingComponent>(CobblemonItemComponents.COOKING_COMPONENT, null)
|
||||
return
|
||||
}
|
||||
|
||||
if (baitCount > 1) {
|
||||
val fishingBait = getBaitOnPokeBait(stack) ?: return
|
||||
stack.set<RodBaitComponent>(
|
||||
CobblemonItemComponents.BAIT,
|
||||
RodBaitComponent(fishingBait, ItemStack(baitStack.item, baitCount - 1))
|
||||
)
|
||||
if (cookingComponent != null) {
|
||||
stack.set(CobblemonItemComponents.COOKING_COMPONENT, cookingComponent)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun getBaitEffects(stack: ItemStack): List<FishingBait.Effect> {
|
||||
return getBaitOnPokeBait(stack)?.effects ?: return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*// Fishing Rod: Bundle edition
|
||||
override fun overrideOtherStackedOnMe(
|
||||
itemStack: ItemStack,
|
||||
itemStack2: ItemStack,
|
||||
slot: Slot,
|
||||
clickAction: ClickAction,
|
||||
player: Player,
|
||||
slotAccess: SlotAccess
|
||||
): Boolean {
|
||||
if (clickAction != ClickAction.SECONDARY || !slot.allowModification(player))
|
||||
return false
|
||||
|
||||
val baitStack = getBaitStackOnRod(itemStack)
|
||||
|
||||
CobblemonEvents.BAIT_SET_PRE.postThen(BaitSetEvent(itemStack, itemStack2), { event ->
|
||||
return event.isCanceled
|
||||
}, {
|
||||
|
||||
// If not holding an item on cursor
|
||||
if (itemStack2.isEmpty) {
|
||||
// Retrieve bait onto cursor
|
||||
if (baitStack != ItemStack.EMPTY) {
|
||||
playDetachSound(player)
|
||||
setBait(itemStack, ItemStack.EMPTY)
|
||||
slotAccess.set(baitStack.copy())
|
||||
return true
|
||||
}
|
||||
}
|
||||
// If holding item on cursor
|
||||
else {
|
||||
|
||||
// If item on cursor is a valid bait
|
||||
if (FishingBaits.getFromBaitItemStack(itemStack2) != null) {
|
||||
|
||||
// Add as much as possible
|
||||
if (baitStack != ItemStack.EMPTY) {
|
||||
if (baitStack.item == itemStack2.item) {
|
||||
|
||||
playAttachSound(player)
|
||||
// Calculate how much bait to add
|
||||
val diff = (baitStack.maxStackSize - baitStack.count).coerceIn(0, itemStack2.count)
|
||||
itemStack2.shrink(diff)
|
||||
baitStack.grow(diff)
|
||||
setBait(itemStack, baitStack)
|
||||
return true
|
||||
}
|
||||
|
||||
// If Item on rod is different from cursor item, swap them
|
||||
playAttachSound(player)
|
||||
setBait(itemStack, itemStack2.copy())
|
||||
slotAccess.set(baitStack.copy())
|
||||
return true
|
||||
}
|
||||
|
||||
// If no bait currently on rod, add all
|
||||
playAttachSound(player)
|
||||
setBait(itemStack, itemStack2.copy())
|
||||
itemStack2.shrink(itemStack2.count)
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
|
||||
val itemStack = user.getItemInHand(hand)
|
||||
val offHandItem = user.getItemInHand(InteractionHand.OFF_HAND)
|
||||
val offHandBait = FishingBaits.getFromBaitItemStack(offHandItem)
|
||||
|
||||
var baitOnRod = getBaitOnRod(itemStack)
|
||||
|
||||
// Check if offhand item is valid bait and the rod is not in use, if so then apply bait from offhand
|
||||
if (!world.isClientSide && user.fishing == null && offHandBait != null) {
|
||||
CobblemonEvents.BAIT_SET_PRE.postThen(BaitSetEvent(itemStack, offHandItem), { event ->
|
||||
return InteractionResultHolder.fail(itemStack)
|
||||
}, {
|
||||
playAttachSound(user)
|
||||
|
||||
// if there is bait on the rod already then drop it on the ground before applying offhand bait
|
||||
val baitStack = baitOnRod?.toItemStack(world.itemRegistry)
|
||||
|
||||
if (baitStack != null && offHandItem.item != baitStack.item) {
|
||||
if (!baitStack.isEmpty) {
|
||||
baitStack.count = getBaitStackOnRod(itemStack).count
|
||||
user.drop(baitStack, true) // Drop the full stack
|
||||
}
|
||||
|
||||
// apply single bait item from offhand
|
||||
val singleBait = offHandItem.copy()
|
||||
singleBait.count = 1
|
||||
setBait(itemStack, singleBait)
|
||||
offHandItem.shrink(1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var i: Int
|
||||
if (user.fishing != null) { // if the bobber is out yet
|
||||
if (!world.isClientSide) {
|
||||
CobblemonEvents.POKEROD_REEL.postThen(
|
||||
PokerodReelEvent(itemStack),
|
||||
{ event -> return InteractionResultHolder.fail(itemStack) },
|
||||
{ event ->
|
||||
i = user.fishing!!.retrieve(itemStack)
|
||||
itemStack.hurtAndBreak(i, user, hand.toEquipmentSlot())
|
||||
world.playSoundServer(
|
||||
Vec3(
|
||||
user.x,
|
||||
user.y,
|
||||
user.z
|
||||
),
|
||||
CobblemonSounds.FISHING_ROD_REEL_IN,
|
||||
SoundSource.PLAYERS,
|
||||
1.0f,
|
||||
1.0f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
world.playSound(null as Player?, user.x, user.y, user.z, CobblemonSounds.FISHING_ROD_REEL_IN, SoundSource.PLAYERS, 1.0f, 1.0f)
|
||||
user.gameEvent(GameEvent.ITEM_INTERACT_FINISH)
|
||||
} else { // if the bobber is not out yet
|
||||
|
||||
if (!world.isClientSide) {
|
||||
val lureLevel = world.enchantmentRegistry.getHolder(Enchantments.LURE).map { EnchantmentHelper.getItemEnchantmentLevel(it, itemStack) }.orElse(0)
|
||||
val luckLevel = world.enchantmentRegistry.getHolder(Enchantments.LUCK_OF_THE_SEA).map { EnchantmentHelper.getItemEnchantmentLevel(it, itemStack) }.orElse(0)
|
||||
|
||||
val bobberEntity = PokeRodFishingBobberEntity(
|
||||
user,
|
||||
pokeRodId,
|
||||
getBaitOnRod(itemStack)?.toItemStack(world.itemRegistry) ?: ItemStack.EMPTY,
|
||||
world,
|
||||
luckLevel,
|
||||
lureLevel,
|
||||
itemStack
|
||||
)
|
||||
CobblemonEvents.POKEROD_CAST_PRE.postThen(
|
||||
PokerodCastEvent.Pre(itemStack, bobberEntity, getBaitStackOnRod(itemStack)),
|
||||
{ event -> return InteractionResultHolder.fail(itemStack) },
|
||||
{ event ->
|
||||
world.addFreshEntity(bobberEntity)
|
||||
var baitId = getBaitOnRod(itemStack)?.item ?: cobblemonResource("empty_bait")
|
||||
CobblemonCriteria.CAST_POKE_ROD.trigger(user as ServerPlayer, CastPokeRodContext(baitId))
|
||||
|
||||
CobblemonEvents.POKEROD_CAST_POST.post(
|
||||
PokerodCastEvent.Post(itemStack, bobberEntity, getBaitStackOnRod(itemStack))
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
user.awardStat(Stats.ITEM_USED.get(this))
|
||||
user.gameEvent(GameEvent.ITEM_INTERACT_START)
|
||||
}
|
||||
return InteractionResultHolder.sidedSuccess(itemStack, world.isClientSide)
|
||||
}*/
|
||||
|
||||
private fun playAttachSound(entity: Entity) {
|
||||
entity.playSound(CobblemonSounds.FISHING_BAIT_ATTACH, 1F, 1F)
|
||||
}
|
||||
|
||||
private fun playDetachSound(entity: Entity) {
|
||||
entity.playSound(CobblemonSounds.FISHING_BAIT_DETACH, 1F, 1F)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.item
|
||||
|
||||
import com.cobblemon.mod.common.block.SaccharineSaplingBlock
|
||||
import net.minecraft.world.item.ItemNameBlockItem
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class SaccharineSaplingItem(block: SaccharineSaplingBlock) : ItemNameBlockItem(block, Properties()) {
|
||||
|
||||
override fun getPlacementState(context: BlockPlaceContext): BlockState? {
|
||||
|
||||
return super.getPlacementState(context)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.entity.throwable.ScatterBangEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.projectile.thrown.SnowballEntity
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.stat.Stats
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.TypedActionResult
|
||||
import net.minecraft.world.World
|
||||
|
||||
|
||||
class ScatterBangItem(settings: Settings?) : Item(settings) {
|
||||
override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult<ItemStack> {
|
||||
val itemStack = user.getStackInHand(hand)
|
||||
world.playSound(
|
||||
null as PlayerEntity?,
|
||||
user.x,
|
||||
user.y,
|
||||
user.z,
|
||||
SoundEvents.ENTITY_SNOWBALL_THROW,
|
||||
SoundCategory.NEUTRAL,
|
||||
0.5f,
|
||||
0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
if (!world.isClient) {
|
||||
val scatterBangEntity = ScatterBangEntity(world, user)
|
||||
scatterBangEntity.setItem(itemStack)
|
||||
scatterBangEntity.setVelocity(user, user.pitch, user.yaw, 0.0f, 1.5f, 1.0f)
|
||||
world.spawnEntity(scatterBangEntity)
|
||||
}
|
||||
|
||||
user.incrementStat(Stats.USED.getOrCreateStat(this))
|
||||
if (!user.abilities.creativeMode) {
|
||||
itemStack.decrement(1)
|
||||
}
|
||||
|
||||
return TypedActionResult.success(itemStack, world.isClient())
|
||||
}
|
||||
}*/
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.item.components
|
||||
|
||||
import com.cobblemon.mod.common.api.cooking.Seasoning
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBait
|
||||
import com.cobblemon.mod.common.api.fishing.FishingBaits
|
||||
import com.cobblemon.mod.common.api.cooking.Seasonings
|
||||
import com.cobblemon.mod.common.client.pot.CookingQuality
|
||||
import net.minecraft.network.codec.ByteBufCodecs
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import io.netty.buffer.ByteBuf
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class CookingComponent(
|
||||
val bait1: FishingBait,
|
||||
val bait2: FishingBait,
|
||||
val bait3: FishingBait,
|
||||
val seasoning1: Seasoning,
|
||||
val seasoning2: Seasoning,
|
||||
val seasoning3: Seasoning
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<CookingComponent> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
ResourceLocation.CODEC.fieldOf("bait1").forGetter<CookingComponent> { it.bait1.item },
|
||||
ResourceLocation.CODEC.fieldOf("bait2").forGetter<CookingComponent> { it.bait2.item },
|
||||
ResourceLocation.CODEC.fieldOf("bait3").forGetter<CookingComponent> { it.bait3.item },
|
||||
ResourceLocation.CODEC.fieldOf("seasoning1").forGetter<CookingComponent> { it.seasoning1.ingredient },
|
||||
ResourceLocation.CODEC.fieldOf("seasoning2").forGetter<CookingComponent> { it.seasoning2.ingredient },
|
||||
ResourceLocation.CODEC.fieldOf("seasoning3").forGetter<CookingComponent> { it.seasoning3.ingredient }
|
||||
).apply(builder) { bait1Loc, bait2Loc, bait3Loc, seasoning1Loc, seasoning2Loc, seasoning3Loc ->
|
||||
CookingComponent(
|
||||
bait1 = FishingBaits.getFromIdentifier(bait1Loc) ?: FishingBait.BLANK_BAIT,
|
||||
bait2 = FishingBaits.getFromIdentifier(bait2Loc) ?: FishingBait.BLANK_BAIT,
|
||||
bait3 = FishingBaits.getFromIdentifier(bait3Loc) ?: FishingBait.BLANK_BAIT,
|
||||
seasoning1 = Seasonings.getFromIdentifier(seasoning1Loc)
|
||||
?: Seasoning(ResourceLocation("minecraft", "unknown"), mapOf(), "", 0),
|
||||
seasoning2 = Seasonings.getFromIdentifier(seasoning2Loc)
|
||||
?: Seasoning(ResourceLocation("minecraft", "unknown"), mapOf(), "", 0),
|
||||
seasoning3 = Seasonings.getFromIdentifier(seasoning3Loc)
|
||||
?: Seasoning(ResourceLocation("minecraft", "unknown"), mapOf(), "", 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val PACKET_CODEC: StreamCodec<ByteBuf, CookingComponent> = ByteBufCodecs.fromCodec(CODEC)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CookingComponent &&
|
||||
bait1 == other.bait1 &&
|
||||
bait2 == other.bait2 &&
|
||||
bait3 == other.bait3 &&
|
||||
seasoning1 == other.seasoning1 &&
|
||||
seasoning2 == other.seasoning2 &&
|
||||
seasoning3 == other.seasoning3
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return listOf(bait1, bait2, bait3, seasoning1, seasoning2, seasoning3).hashCode()
|
||||
}
|
||||
|
||||
fun getCookingQuality(): CookingQuality {
|
||||
val qualityAverage = getQualityAverage()
|
||||
return when {
|
||||
qualityAverage < 10 -> CookingQuality.LOW
|
||||
qualityAverage < 20 -> CookingQuality.MEDIUM
|
||||
else -> CookingQuality.HIGH
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQualityAverage() = getSeasonings().map { it.quality }.average()
|
||||
|
||||
fun getDominantFlavors(): List<String> {
|
||||
val flavors = getFlavorsSum()
|
||||
val maxFlavorValue = flavors.values.maxOrNull()
|
||||
|
||||
return flavors.filter { it.value == maxFlavorValue }.map { it.key }
|
||||
}
|
||||
|
||||
fun getFlavorsSum(): Map<String, Int> =
|
||||
getSeasonings()
|
||||
.flatMap { it.flavors.entries }
|
||||
.groupingBy { it.key }
|
||||
.fold(0) { acc, entry -> acc + entry.value }
|
||||
|
||||
fun getSeasonings() = listOf(seasoning1, seasoning2, seasoning3).filter { it.quality > 0 }
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.item.components
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.Optional
|
||||
|
||||
class SeasoningComponent(
|
||||
val ingredient1: ResourceLocation?,
|
||||
val flavor1: String?,
|
||||
val color1: String?,
|
||||
val quality1: Int?,
|
||||
val ingredient2: ResourceLocation?,
|
||||
val flavor2: String?,
|
||||
val color2: String?,
|
||||
val quality2: Int?,
|
||||
val ingredient3: ResourceLocation?,
|
||||
val flavor3: String?,
|
||||
val color3: String?
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<SeasoningComponent> = RecordCodecBuilder.create { builder ->
|
||||
builder.group(
|
||||
ResourceLocation.CODEC.optionalFieldOf("ingredient1").forGetter { Optional.ofNullable(it.ingredient1) },
|
||||
Codec.STRING.optionalFieldOf("flavor1").forGetter { Optional.ofNullable(it.flavor1) },
|
||||
Codec.STRING.optionalFieldOf("color1").forGetter { Optional.ofNullable(it.color1) },
|
||||
Codec.INT.optionalFieldOf("quality1").forGetter { Optional.ofNullable(it.quality1) },
|
||||
ResourceLocation.CODEC.optionalFieldOf("ingredient2").forGetter { Optional.ofNullable(it.ingredient2) },
|
||||
Codec.STRING.optionalFieldOf("flavor2").forGetter { Optional.ofNullable(it.flavor2) },
|
||||
Codec.STRING.optionalFieldOf("color2").forGetter { Optional.ofNullable(it.color2) },
|
||||
Codec.INT.optionalFieldOf("quality2").forGetter { Optional.ofNullable(it.quality2) },
|
||||
ResourceLocation.CODEC.optionalFieldOf("ingredient3").forGetter { Optional.ofNullable(it.ingredient3) },
|
||||
Codec.STRING.optionalFieldOf("flavor3").forGetter { Optional.ofNullable(it.flavor3) },
|
||||
Codec.STRING.optionalFieldOf("color3").forGetter { Optional.ofNullable(it.color3) }
|
||||
).apply(builder) { ingredient1Opt, flavor1Opt, color1Opt, quality1Opt,
|
||||
ingredient2Opt, flavor2Opt, color2Opt, quality2Opt,
|
||||
ingredient3Opt, flavor3Opt, color3Opt ->
|
||||
SeasoningComponent(
|
||||
ingredient1Opt.orElse(null),
|
||||
flavor1Opt.orElse(null),
|
||||
color1Opt.orElse(null),
|
||||
quality1Opt.orElse(null),
|
||||
ingredient2Opt.orElse(null),
|
||||
flavor2Opt.orElse(null),
|
||||
color2Opt.orElse(null),
|
||||
quality2Opt.orElse(null),
|
||||
ingredient3Opt.orElse(null),
|
||||
flavor3Opt.orElse(null),
|
||||
color3Opt.orElse(null)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is SeasoningComponent &&
|
||||
ingredient1 == other.ingredient1 &&
|
||||
flavor1 == other.flavor1 &&
|
||||
color1 == other.color1 &&
|
||||
quality1 == other.quality1 &&
|
||||
ingredient2 == other.ingredient2 &&
|
||||
flavor2 == other.flavor2 &&
|
||||
color2 == other.color2 &&
|
||||
quality2 == other.quality2 &&
|
||||
ingredient3 == other.ingredient3 &&
|
||||
flavor3 == other.flavor3 &&
|
||||
color3 == other.color3
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return listOf(
|
||||
ingredient1, flavor1, color1, quality1,
|
||||
ingredient2, flavor2, color2, quality2,
|
||||
ingredient3, flavor3, color3
|
||||
).fold(0) { acc, value -> 31 * acc + (value?.hashCode() ?: 0) }
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,10 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.VIVICHOKE)
|
||||
entries.accept(CobblemonItems.VIVICHOKE_SEEDS)
|
||||
entries.accept(CobblemonItems.GALARICA_NUTS)
|
||||
entries.accept(CobblemonItems.Hearty_Grains)
|
||||
entries.accept(CobblemonItems.BUGWORT)
|
||||
|
||||
entries.accept(CobblemonItems.SWEET_SAP)
|
||||
|
||||
entries.accept(CobblemonItems.RED_APRICORN)
|
||||
entries.accept(CobblemonItems.YELLOW_APRICORN)
|
||||
@@ -123,6 +127,8 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.BLACK_APRICORN_SEED)
|
||||
entries.accept(CobblemonItems.WHITE_APRICORN_SEED)
|
||||
|
||||
entries.accept(CobblemonItems.SACCHARINE_SAPLING)
|
||||
|
||||
entries.accept(CobblemonItems.RED_MINT_SEEDS)
|
||||
entries.accept(CobblemonItems.RED_MINT_LEAF)
|
||||
entries.accept(CobblemonItems.BLUE_MINT_SEEDS)
|
||||
@@ -250,6 +256,7 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.RELIC_COIN_POUCH)
|
||||
entries.accept(CobblemonItems.RELIC_COIN_SACK)
|
||||
|
||||
entries.accept(CobblemonItems.INCENSE_SWEET)
|
||||
entries.accept(CobblemonItems.DISPLAY_CASE)
|
||||
entries.accept(CobblemonItems.APRICORN_LOG)
|
||||
entries.accept(CobblemonItems.APRICORN_WOOD)
|
||||
@@ -268,6 +275,25 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.APRICORN_HANGING_SIGN)
|
||||
entries.accept(CobblemonItems.APRICORN_LEAVES)
|
||||
|
||||
entries.accept(CobblemonItems.SACCHARINE_LOG)
|
||||
entries.accept(CobblemonItems.SACCHARINE_WOOD)
|
||||
entries.accept(CobblemonItems.STRIPPED_SACCHARINE_LOG)
|
||||
entries.accept(CobblemonItems.STRIPPED_SACCHARINE_WOOD)
|
||||
entries.accept(CobblemonItems.SACCHARINE_PLANKS)
|
||||
entries.accept(CobblemonItems.SACCHARINE_STAIRS)
|
||||
entries.accept(CobblemonItems.SACCHARINE_SLAB)
|
||||
entries.accept(CobblemonItems.SACCHARINE_FENCE)
|
||||
entries.accept(CobblemonItems.SACCHARINE_FENCE_GATE)
|
||||
entries.accept(CobblemonItems.SACCHARINE_DOOR)
|
||||
entries.accept(CobblemonItems.SACCHARINE_TRAPDOOR)
|
||||
entries.accept(CobblemonItems.SACCHARINE_BUTTON)
|
||||
entries.accept(CobblemonItems.SACCHARINE_PRESSURE_PLATE)
|
||||
entries.accept(CobblemonItems.SACCHARINE_SIGN)
|
||||
entries.accept(CobblemonItems.SACCHARINE_HANGING_SIGN)
|
||||
entries.accept(CobblemonItems.SACCHARINE_LEAVES)
|
||||
//entries.accept(CobblemonItems.SACCHARINE_SAPLING)
|
||||
|
||||
|
||||
entries.accept(CobblemonItems.TUMBLESTONE_BLOCK)
|
||||
entries.accept(CobblemonItems.BLACK_TUMBLESTONE_BLOCK)
|
||||
entries.accept(CobblemonItems.SKY_TUMBLESTONE_BLOCK)
|
||||
@@ -346,6 +372,8 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.SUN_STONE_BLOCK)
|
||||
entries.accept(CobblemonItems.THUNDER_STONE_BLOCK)
|
||||
entries.accept(CobblemonItems.WATER_STONE_BLOCK)
|
||||
|
||||
CobblemonItems.campfire_pots.forEach(entries::accept)
|
||||
}
|
||||
|
||||
private fun consumableEntries(displayContext: ItemDisplayParameters, entries: Output) {
|
||||
@@ -405,6 +433,15 @@ object CobblemonItemGroups {
|
||||
entries.accept(CobblemonItems.CALCIUM)
|
||||
entries.accept(CobblemonItems.ZINC)
|
||||
entries.accept(CobblemonItems.CARBOS)
|
||||
|
||||
entries.accept(CobblemonItems.HEALTH_MOCHI)
|
||||
entries.accept(CobblemonItems.MUSCLE_MOCHI)
|
||||
entries.accept(CobblemonItems.RESIST_MOCHI)
|
||||
entries.accept(CobblemonItems.GENIUS_MOCHI)
|
||||
entries.accept(CobblemonItems.CLEVER_MOCHI)
|
||||
entries.accept(CobblemonItems.SWIFT_MOCHI)
|
||||
entries.accept(CobblemonItems.FRESH_START_MOCHI)
|
||||
|
||||
entries.accept(CobblemonItems.PP_UP)
|
||||
entries.accept(CobblemonItems.PP_MAX)
|
||||
entries.accept(CobblemonItems.EXPERIENCE_CANDY_XS)
|
||||
@@ -648,6 +685,9 @@ object CobblemonItemGroups {
|
||||
injector.putAfter(CobblemonItems.BRAISED_VIVICHOKE, CobblemonItems.ROASTED_LEEK)
|
||||
injector.putAfter(CobblemonItems.LEEK_AND_POTATO_STEW, Items.RABBIT_STEW)
|
||||
injector.putAfter(CobblemonItems.VIVICHOKE_DIP, CobblemonItems.LEEK_AND_POTATO_STEW)
|
||||
injector.putLast(CobblemonItems.POTATO_MOCHI)
|
||||
injector.putLast(CobblemonItems.FRIED_RICE)
|
||||
injector.putLast(CobblemonItems.CANDIED_APPLE)
|
||||
}
|
||||
|
||||
private fun toolsAndUtilitiesInjections(injector: Injector) {
|
||||
|
||||
@@ -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.item.interactive
|
||||
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
abstract class EVIncreaseItem(
|
||||
val stat: Stat,
|
||||
val evIncreaseAmount: Int,
|
||||
) : CobblemonItem(Properties()), PokemonSelectingItem {
|
||||
override val bagItem = null
|
||||
override fun canUseOnPokemon(pokemon: Pokemon) = pokemon.evs.getOrDefault(stat) < EVs.MAX_STAT_VALUE
|
||||
abstract val sound: SoundEvent
|
||||
|
||||
override fun applyToPokemon(
|
||||
player: ServerPlayer,
|
||||
stack: ItemStack,
|
||||
pokemon: Pokemon
|
||||
): InteractionResultHolder<ItemStack> {
|
||||
val evsGained = pokemon.evs.add(stat, evIncreaseAmount, ItemEvSource(player, stack, pokemon))
|
||||
return if (evsGained > 0) {
|
||||
pokemon.entity?.playSound(sound, 1F, 1F)
|
||||
if (!player.isCreative) {
|
||||
stack.shrink(1)
|
||||
}
|
||||
InteractionResultHolder.success(stack)
|
||||
} else {
|
||||
InteractionResultHolder.fail(stack)
|
||||
}
|
||||
}
|
||||
|
||||
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
if (user is ServerPlayer) {
|
||||
return use(user, user.getItemInHand(hand))
|
||||
}
|
||||
return InteractionResultHolder.success(user.getItemInHand(hand))
|
||||
}
|
||||
}
|
||||
@@ -9,49 +9,9 @@
|
||||
package com.cobblemon.mod.common.item.interactive
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResultHolder
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
|
||||
class FeatherItem(val stat: Stat) : CobblemonItem(Properties()), PokemonSelectingItem {
|
||||
|
||||
companion object {
|
||||
const val EV_YIELD = 1
|
||||
}
|
||||
|
||||
override val bagItem = null
|
||||
override fun canUseOnPokemon(pokemon: Pokemon) = pokemon.evs.getOrDefault(stat) < EVs.MAX_STAT_VALUE
|
||||
|
||||
override fun applyToPokemon(
|
||||
player: ServerPlayer,
|
||||
stack: ItemStack,
|
||||
pokemon: Pokemon
|
||||
): InteractionResultHolder<ItemStack> {
|
||||
val evsGained = pokemon.evs.add(stat, EV_YIELD, ItemEvSource(player, stack, pokemon))
|
||||
return if (evsGained > 0) {
|
||||
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_FEATHER_USE, 1F, 1F)
|
||||
if (!player.isCreative) {
|
||||
stack.shrink(1)
|
||||
}
|
||||
InteractionResultHolder.success(stack)
|
||||
} else {
|
||||
InteractionResultHolder.fail(stack)
|
||||
}
|
||||
}
|
||||
|
||||
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
|
||||
if (user is ServerPlayer) {
|
||||
return use(user, user.getItemInHand(hand))
|
||||
}
|
||||
return InteractionResultHolder.success(user.getItemInHand(hand))
|
||||
}
|
||||
class FeatherItem(stat: Stat) : EVIncreaseItem(stat, 1) {
|
||||
override val sound: SoundEvent = CobblemonSounds.MEDICINE_FEATHER_USE
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user