Compare commits
186
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
694f74be13 | ||
|
|
79d097b17c | ||
|
|
7ef30653bb | ||
|
|
1ecb13f7fc | ||
|
|
75f76ae228 | ||
|
|
31a955fed1 | ||
|
|
6772d95d25 | ||
|
|
a29f9d7bce | ||
|
|
2ff6559b5e | ||
|
|
fc6739f640 | ||
|
|
6ff4eef7dc | ||
|
|
82151c3944 | ||
|
|
3c07ca973b | ||
|
|
60f3393f3f | ||
|
|
c6a1518e0d | ||
|
|
d19878edf6 | ||
|
|
e816e41df1 | ||
|
|
96a9844fa4 | ||
|
|
8febe77edb | ||
|
|
ef89d8fcd8 | ||
|
|
0253a4dd7a | ||
|
|
4f98d1e054 | ||
|
|
7b8e07aef3 | ||
|
|
5259ea0455 | ||
|
|
f637768e3d | ||
|
|
ef867a02d5 | ||
|
|
e0456939a9 | ||
|
|
cba0c138b0 | ||
|
|
b748e39877 | ||
|
|
a1031f3dc9 | ||
|
|
e753e05c67 | ||
|
|
ed292bcaea | ||
|
|
c2f1176aca | ||
|
|
637ad2e510 | ||
|
|
e08ddc4fc4 | ||
|
|
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 |
No files matched your search
@@ -6,6 +6,8 @@
|
||||
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
|
||||
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
|
||||
- Pokémon now follow the mouse cursor on the Summary screen, with an option to disable this in the settings.
|
||||
- Pokémon's held items can now be rendered, with a visibility toggle in the Summary screen.
|
||||
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
|
||||
|
||||
### Changes
|
||||
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
|
||||
@@ -21,6 +23,7 @@
|
||||
- Fixed fling not using Item Names for minecraft held items that substitute Pokémon items
|
||||
- Fixed evolutions sometimes preventing players from logging in to servers
|
||||
- Fixed rendering of shoulder-mounted Pokémon desyncing between clients
|
||||
- Fixed apricorn chest boats forgetting their inventories when being unloaded
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
|
||||
@@ -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
|
||||
@@ -114,6 +117,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
@@ -121,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
|
||||
@@ -151,6 +148,7 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
@@ -309,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
|
||||
@@ -362,6 +361,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Pokédex scanning
|
||||
list.add(PacketRegisterInfo(ServerConfirmedRegisterPacket.ID, ServerConfirmedRegisterPacket::decode, ServerConfirmedRegisterHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -369,6 +369,7 @@ object CobblemonNetwork {
|
||||
val list = mutableListOf<PacketRegisterInfo<*>>()
|
||||
// Pokemon Update Packets
|
||||
list.add(PacketRegisterInfo(SetNicknamePacket.ID, SetNicknamePacket::decode, SetNicknameHandler))
|
||||
list.add(PacketRegisterInfo(SetItemHiddenPacket.ID, SetItemHiddenPacket::decode, SetItemHiddenHandler))
|
||||
|
||||
// Evolution Packets
|
||||
list.add(PacketRegisterInfo(AcceptEvolutionPacket.ID, AcceptEvolutionPacket::decode, AcceptEvolutionHandler))
|
||||
@@ -455,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
|
||||
|
||||
@@ -320,7 +320,7 @@ a single battle challenge and this is a dynamic party, there will also be a `q.p
|
||||
"pokemon": "weedle",
|
||||
"weight": "10",
|
||||
"selectableTimes": "2",
|
||||
"levelVariation": "2"
|
||||
"levelVariation": "2",
|
||||
"npcLevels": "1-8"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -124,7 +124,10 @@ object CobblemonItemTags {
|
||||
@JvmField val POWER_LENS = create("held/power_lens")
|
||||
@JvmField val POWER_WEIGHT = create("held/power_weight")
|
||||
@JvmField val TERRAIN_SEEDS = create("held/terrain_seeds")
|
||||
|
||||
//Held Item Visibility Tags
|
||||
@JvmField val WEARABLE_FACE_ITEMS = create("held/visibility/face")
|
||||
@JvmField val WEARABLE_HAT_ITEMS = create("held/visibility/hat")
|
||||
@JvmField val HIDDEN_ITEMS = create("held/visibility/hidden")
|
||||
|
||||
|
||||
private fun create(path: String) = TagKey.create(Registries.ITEM, cobblemonResource(path))
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.gui.summary
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
@@ -36,6 +37,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWi
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
@@ -89,6 +91,8 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
private val typeSpacerDoubleResource = cobblemonResource("textures/gui/summary/type_spacer_double.png")
|
||||
private val sideSpacerResource = cobblemonResource("textures/gui/summary/summary_side_spacer.png")
|
||||
private val evolveButtonResource = cobblemonResource("textures/gui/summary/summary_evolve_button.png")
|
||||
private val itemVisibleResource = cobblemonResource("textures/gui/summary/item_visible.png")
|
||||
private val itemHiddenResource = cobblemonResource("textures/gui/summary/item_hidden.png")
|
||||
private val tabIconInfo = cobblemonResource("textures/gui/summary/summary_tab_icon_info.png")
|
||||
private val tabIconMoves = cobblemonResource("textures/gui/summary/summary_tab_icon_moves.png")
|
||||
private val tabIconStats = cobblemonResource("textures/gui/summary/summary_tab_icon_stats.png")
|
||||
@@ -168,6 +172,40 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
)
|
||||
)
|
||||
|
||||
//Item Visibility Button
|
||||
val hideHeldItemBtn = SummaryButton(
|
||||
buttonX = x + 22f,
|
||||
buttonY = y + 101f,
|
||||
buttonWidth = 13,
|
||||
buttonHeight = 9,
|
||||
clickAction = {
|
||||
selectedPokemon.heldItemVisible=!selectedPokemon.heldItemVisible
|
||||
modelWidget.heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
|
||||
// Send item visibility update to server
|
||||
sendToServer(
|
||||
SetItemHiddenPacket(
|
||||
selectedPokemon.uuid,
|
||||
selectedPokemon.heldItemVisible
|
||||
)
|
||||
)
|
||||
},
|
||||
resource = itemVisibleResource,
|
||||
renderRequirement = { selectedPokemon.heldItemVisible },
|
||||
clickRequirement = { true },
|
||||
)
|
||||
val showHeldItemBtn = SummaryButton(
|
||||
buttonX = x + 22f,
|
||||
buttonY = y + 101f,
|
||||
buttonWidth = 13,
|
||||
buttonHeight = 9,
|
||||
clickAction = { },
|
||||
resource = itemHiddenResource,
|
||||
renderRequirement = { !selectedPokemon.heldItemVisible },
|
||||
clickRequirement = { false },
|
||||
)
|
||||
addRenderableWidget(showHeldItemBtn)
|
||||
addRenderableWidget(hideHeldItemBtn)
|
||||
|
||||
// Init Tabs
|
||||
summaryTabs.clear()
|
||||
summaryTabs.add(
|
||||
@@ -237,6 +275,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
rotationY = 325F,
|
||||
offsetY = -10.0,
|
||||
shouldFollowCursor = true,
|
||||
heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
|
||||
)
|
||||
addRenderableOnly(this.modelWidget)
|
||||
}
|
||||
@@ -278,6 +317,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
if (::modelWidget.isInitialized) {
|
||||
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
modelWidget.heldItem = if (selectedPokemon.heldItemVisible) selectedPokemon.heldItem else null
|
||||
}
|
||||
|
||||
if (this::nicknameEntryWidget.isInitialized) {
|
||||
|
||||
+13
@@ -12,10 +12,12 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.calculateHeadYawAndPitch
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
@@ -28,6 +30,7 @@ class ModelWidget(
|
||||
var offsetY: Double = 0.0,
|
||||
val playCryOnClick: Boolean = false,
|
||||
val shouldFollowCursor: Boolean = false,
|
||||
var heldItem: ItemStack? = null
|
||||
): SoundlessWidget(pX, pY, pWidth, pHeight, Component.literal("Summary - ModelWidget")) {
|
||||
|
||||
companion object {
|
||||
@@ -39,8 +42,11 @@ class ModelWidget(
|
||||
set (value) {
|
||||
field = value
|
||||
currentYawAndPitch = Pair(0f, 0f)
|
||||
state = FloatingState()
|
||||
}
|
||||
|
||||
private val heldItemRenderer = HeldItemRenderer()
|
||||
|
||||
var state = FloatingState()
|
||||
var lookStartTime: Long? = null
|
||||
var currentYawAndPitch: Pair<Float, Float> = Pair(0f, 0f)
|
||||
@@ -106,6 +112,13 @@ class ModelWidget(
|
||||
headPitch = currentYawAndPitch.second,
|
||||
)
|
||||
|
||||
heldItemRenderer.renderOnModel(
|
||||
heldItem?: ItemStack.EMPTY,
|
||||
state.locatorStates,
|
||||
matrices,
|
||||
context.bufferSource()
|
||||
)
|
||||
|
||||
matrices.popPose()
|
||||
context.disableScissor()
|
||||
|
||||
|
||||
+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
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_FACE_ITEMS
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_HAT_ITEMS
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.NullObjectParser
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.LightTexture
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class HeldItemRenderer() {
|
||||
private val itemRenderer = Minecraft.getInstance().itemRenderer
|
||||
private var displayContext = ItemDisplayContext.FIXED
|
||||
private var scale = 1.0f
|
||||
private var prevItem = ItemStack.EMPTY
|
||||
|
||||
fun render(
|
||||
entity: PokemonEntity?,
|
||||
item: ItemStack,
|
||||
locators: Map<String, MatrixWrapper>,
|
||||
poseStack: PoseStack,
|
||||
buffer: MultiBufferSource,
|
||||
light: Int,
|
||||
seed: Int
|
||||
) {
|
||||
if (item.isEmpty) return
|
||||
val locatorName: String
|
||||
|
||||
poseStack.pushPose()
|
||||
when {
|
||||
(locators.containsKey("item_face") && item.`is`(WEARABLE_FACE_ITEMS)) -> {
|
||||
locatorName="item_face"
|
||||
displayContext = ItemDisplayContext.HEAD
|
||||
poseStack.mulPose(locators["item_face"]!!.matrix)
|
||||
poseStack.translate(0f, 0f, .28f * scale)
|
||||
poseStack.scale(0.7f * scale, 0.7f * scale, 0.7f * scale)
|
||||
}
|
||||
(locators.containsKey("item_hat") && item.`is`(WEARABLE_HAT_ITEMS)) -> {
|
||||
locatorName="item_hat"
|
||||
displayContext = ItemDisplayContext.HEAD
|
||||
poseStack.mulPose(locators["item_hat"]!!.matrix)
|
||||
poseStack.translate(0f, -0.26f * scale, 0f)
|
||||
poseStack.scale(.68f * scale, .68f * scale, .68f * scale)
|
||||
}
|
||||
(locators.containsKey("item")) -> {
|
||||
locatorName="item"
|
||||
poseStack.mulPose(locators["item"]!!.matrix)
|
||||
when(displayContext) {
|
||||
ItemDisplayContext.FIXED -> {
|
||||
poseStack.translate(0f, 0.01666f * scale, 0f)
|
||||
poseStack.scale(.5f * scale, .5f * scale, .5f * scale)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
|
||||
}
|
||||
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND -> {
|
||||
poseStack.translate(0.075f* scale, 0f * scale, -0.05f* scale)
|
||||
poseStack.scale(scale, scale, scale)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(-90f))
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(-90f))
|
||||
}
|
||||
else -> {
|
||||
poseStack.scale(scale, scale, scale)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> { // Don't render any item
|
||||
poseStack.popPose()
|
||||
return
|
||||
}
|
||||
}
|
||||
//Modifiers only need to be updated once if the item changes
|
||||
if (!ItemStack.isSameItemSameComponents(prevItem, item)) {
|
||||
updateModifiers(locatorName,locators)
|
||||
prevItem=item
|
||||
}
|
||||
|
||||
itemRenderer.renderStatic(entity, item, displayContext, false, poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
fun renderOnModel(
|
||||
item: ItemStack,
|
||||
locators: Map<String, MatrixWrapper>,
|
||||
poseStack: PoseStack,
|
||||
buffer: MultiBufferSource,
|
||||
light: Int = LightTexture.pack(11, 7)
|
||||
){
|
||||
if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) render(null, item, locators, poseStack, buffer, light, 0)
|
||||
}
|
||||
|
||||
fun renderOnEntity(
|
||||
entity: PokemonEntity,
|
||||
delegate: PokemonClientDelegate,
|
||||
poseStack: PoseStack,
|
||||
buffer: MultiBufferSource,
|
||||
light: Int
|
||||
) {
|
||||
val shownItem = entity.shownItem
|
||||
val locators: Map<String, MatrixWrapper> = delegate.locatorStates
|
||||
|
||||
render(entity, shownItem, locators, poseStack, buffer, light, 0)
|
||||
}
|
||||
|
||||
private fun updateModifiers(name: String, locators: Map<String, MatrixWrapper>) {
|
||||
locators.forEach { (locator: String, m: MatrixWrapper) ->
|
||||
val modifiers: Map<String, Float>
|
||||
if (locator.startsWith("_null_$name[")) {
|
||||
modifiers = NullObjectParser.parseNullObject(locator).modifiers
|
||||
if (modifiers.containsKey("scale")) scale = modifiers["scale"]!!
|
||||
if (modifiers.containsKey("mode")) displayContext = ItemDisplayContext.entries[((modifiers["mode"]!!).toInt()) % 9]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.render.layer
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
@@ -35,6 +36,7 @@ import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>) : RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
|
||||
|
||||
@@ -42,6 +44,8 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
|
||||
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
|
||||
}
|
||||
|
||||
private val heldItemRenderer = HeldItemRenderer()
|
||||
|
||||
var leftState = FloatingState()
|
||||
var lastRenderedLeft: ShoulderData? = null
|
||||
var rightState = FloatingState()
|
||||
@@ -155,6 +159,14 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
|
||||
model.withLayerContext(buffer, state, VaryingModelRepository.getLayers(shoulderData.species.resourceIdentifier, state)) {
|
||||
model.render(context, matrixStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
|
||||
}
|
||||
|
||||
heldItemRenderer.renderOnModel(
|
||||
shoulderData.shownItem,
|
||||
state.locatorStates,
|
||||
matrixStack,
|
||||
buffer
|
||||
)
|
||||
|
||||
model.setDefault()
|
||||
matrixStack.popPose()
|
||||
livingEntity.level().profiler.pop()
|
||||
@@ -177,7 +189,7 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
|
||||
if (!shoulderNbt.contains(DataKeys.SHOULDER_SPECIES)) {
|
||||
return Pokemon.CLIENT_CODEC.decode(NbtOps.INSTANCE, shoulderNbt.getCompound(DataKeys.POKEMON))
|
||||
.map { it.first }
|
||||
.mapOrElse({ ShoulderData(pokemonUUID, it.species, it.form, it.aspects, it.scaleModifier) }, { null })
|
||||
.mapOrElse({ ShoulderData(pokemonUUID, it.species, it.form, it.aspects, it.scaleModifier, it.heldItem) }, { null })
|
||||
}
|
||||
val species = PokemonSpecies.getByIdentifier(ResourceLocation.parse(shoulderNbt.getString(DataKeys.SHOULDER_SPECIES)))
|
||||
?: return null
|
||||
@@ -185,7 +197,10 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
|
||||
val form = species.forms.firstOrNull { it.name == formName } ?: species.standardForm
|
||||
val aspects = shoulderNbt.getList(DataKeys.SHOULDER_ASPECTS, Tag.TAG_STRING.toInt()).map { it.asString }.toSet()
|
||||
val scaleModifier = shoulderNbt.getFloat(DataKeys.SHOULDER_SCALE_MODIFIER)
|
||||
return ShoulderData(pokemonUUID, species, form, aspects, scaleModifier)
|
||||
val shownItem = Minecraft.getInstance().level?.registryAccess()
|
||||
?.let { ItemStack.parseOptional(it, shoulderNbt.getCompound(DataKeys.SHOULDER_ITEM)) }
|
||||
?: ItemStack.EMPTY
|
||||
return ShoulderData(pokemonUUID, species, form, aspects, scaleModifier, shownItem)
|
||||
}
|
||||
|
||||
private data class ShoulderCache(
|
||||
@@ -198,7 +213,8 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
|
||||
val species: Species,
|
||||
val form: FormData,
|
||||
val aspects: Set<String>,
|
||||
val scaleModifier: Float
|
||||
val scaleModifier: Float,
|
||||
val shownItem: ItemStack
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
+2
-2
@@ -69,11 +69,11 @@ class LocatorAccess(
|
||||
* Updates all of the locator states with the position at this current frame.
|
||||
* This is the same logic as ModelPart uses, that's why we reuse ModelPart#rotate.
|
||||
*/
|
||||
fun update(matrixStack: PoseStack, entity: Entity, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
|
||||
fun update(matrixStack: PoseStack, entity: Entity?, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
|
||||
matrixStack.pushPose()
|
||||
joint.transform(matrixStack)
|
||||
|
||||
if (isRoot) {
|
||||
if (isRoot && null!=entity) {
|
||||
matrixStack.pushPose()
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
state.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.last().pose())
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
|
||||
/**
|
||||
* Uses the name of null objects in a model to get modifier values for a locator.
|
||||
* @author Josh
|
||||
*/
|
||||
object NullObjectParser {
|
||||
fun parseNullObject(input: String): NullObjectModifier {
|
||||
// Extract modifiedLocator
|
||||
val modifiedLocator = input.replaceFirst("^_null_".toRegex(), "").replace("\\[.*\\]$".toRegex(), "")
|
||||
|
||||
// Extract modifiers
|
||||
val modifiers: MutableMap<String, Float> = HashMap()
|
||||
val start = input.indexOf('[')
|
||||
val end = input.indexOf(']')
|
||||
|
||||
if (start != -1 && end != -1 && end > start) {
|
||||
val modifierPart = input.substring(start + 1, end)
|
||||
val modifierPairs = modifierPart.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
|
||||
for (pair in modifierPairs) {
|
||||
val keyValue = pair.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (keyValue.size == 2) {
|
||||
val key = keyValue[0].trim { it <= ' ' }
|
||||
try {
|
||||
val value = keyValue[1].trim { it <= ' ' }.toFloat()
|
||||
modifiers[key] = value
|
||||
} catch (e: NumberFormatException) {
|
||||
System.err.println("Invalid number format for: " + keyValue[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NullObjectModifier(modifiedLocator, modifiers)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores modifiers for a particular locator.
|
||||
*/
|
||||
class NullObjectModifier(val locatorName: String, val modifiers: Map<String, Float>)
|
||||
}
|
||||
-1
@@ -669,7 +669,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
* found and used from other client-side systems.
|
||||
*/
|
||||
fun updateLocators(entity: Entity?, state: PosableState) {
|
||||
entity ?: return
|
||||
val matrixStack = PoseStack()
|
||||
var scale = 1F
|
||||
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
|
||||
|
||||
+13
-1
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BE
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BEAM_SHRINK_TIME
|
||||
import com.cobblemon.mod.common.client.keybind.boundKey
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
|
||||
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PosablePokemonEntityModel
|
||||
@@ -40,7 +41,6 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.math.remap
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
import kotlin.math.*
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.Font.DisplayMode
|
||||
@@ -60,6 +60,7 @@ import org.joml.Math
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import org.joml.Vector4f
|
||||
import kotlin.math.*
|
||||
|
||||
class PokemonRenderer(
|
||||
context: EntityRendererProvider.Context
|
||||
@@ -86,6 +87,8 @@ class PokemonRenderer(
|
||||
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
|
||||
}
|
||||
|
||||
private val heldItemRenderer = HeldItemRenderer()
|
||||
|
||||
override fun getTextureLocation(entity: PokemonEntity): ResourceLocation {
|
||||
return VaryingModelRepository.getTexture(entity.pokemon.species.resourceIdentifier, entity.delegate as PokemonClientDelegate)
|
||||
}
|
||||
@@ -149,6 +152,15 @@ class PokemonRenderer(
|
||||
this.renderNameTag(entity, entity.effectiveName(), poseMatrix, buffer, packedLight, partialTicks)
|
||||
}
|
||||
// Minecraft.getInstance().bufferBuilders.entityVertexConsumers.draw()
|
||||
|
||||
//Render Held Item
|
||||
heldItemRenderer.renderOnEntity(
|
||||
entity,
|
||||
clientDelegate,
|
||||
poseMatrix,
|
||||
buffer,
|
||||
packedLight
|
||||
)
|
||||
}
|
||||
|
||||
fun renderTransition(
|
||||
|
||||
+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()
|
||||
|
||||
|
||||
@@ -9,16 +9,7 @@
|
||||
package com.cobblemon.mod.common.datafixer
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.datafixer.fix.BlockPosUpdateFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.EvolutionProxyNestingFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.FeatureFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.IvEvToIdentifierFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.MovesetJsonFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.NicknameFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.RaisedPPStagesFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.ShoulderStateJsonFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.TeraTypeFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.TradeableMissingFix
|
||||
import com.cobblemon.mod.common.datafixer.fix.*
|
||||
import com.cobblemon.mod.common.datafixer.schema.CobblemonRootSchema
|
||||
import com.cobblemon.mod.common.datafixer.schema.CobblemonSchemaV2
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
+35
@@ -10,11 +10,15 @@ package com.cobblemon.mod.common.entity.boat
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonEntities
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.Containers
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.entity.HasCustomInventoryScreen
|
||||
import net.minecraft.world.entity.SlotAccess
|
||||
import net.minecraft.world.entity.monster.piglin.PiglinAi
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -52,6 +56,31 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMaxPassengers() = 1
|
||||
|
||||
override fun addAdditionalSaveData(compound: CompoundTag) {
|
||||
super.addAdditionalSaveData(compound)
|
||||
this.addChestVehicleSaveData(compound, this.registryAccess())
|
||||
}
|
||||
|
||||
override fun readAdditionalSaveData(compound: CompoundTag) {
|
||||
super.readAdditionalSaveData(compound)
|
||||
this.readChestVehicleSaveData(compound, this.registryAccess())
|
||||
}
|
||||
|
||||
public override fun destroy(source: DamageSource) {
|
||||
this.destroy(this.getDropItem())
|
||||
this.chestVehicleDestroyed(source, this.level(), this)
|
||||
}
|
||||
|
||||
override fun remove(reason: RemovalReason) {
|
||||
if (!this.level().isClientSide && reason.shouldDestroy()) {
|
||||
Containers.dropContents(this.level(), this, this)
|
||||
}
|
||||
|
||||
super.remove(reason)
|
||||
}
|
||||
|
||||
override fun interact(player: Player, hand: InteractionHand): InteractionResult {
|
||||
if (!player.isSecondaryUseActive) {
|
||||
val interactionResult = super.interact(player, hand)
|
||||
@@ -86,6 +115,8 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
|
||||
|
||||
override fun setItem(slot: Int, stack: ItemStack) = this.setChestVehicleItem(slot, stack)
|
||||
|
||||
override fun getSlot(slot: Int): SlotAccess? = this.getChestVehicleSlot(slot)
|
||||
|
||||
override fun setChanged() {}
|
||||
|
||||
override fun stillValid(player: Player): Boolean = this.isChestVehicleStillValid(player)
|
||||
@@ -120,6 +151,10 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
|
||||
|
||||
private fun emptyInventory(): NonNullList<ItemStack> = NonNullList.withSize(INVENTORY_SLOTS, ItemStack.EMPTY)
|
||||
|
||||
override fun stopOpen(player: Player) {
|
||||
this.level().gameEvent(GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of(player))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val INVENTORY_SLOTS = 27
|
||||
|
||||
+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()
|
||||
|
||||
Loaded 100 of 663 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user