Skip to content

Commit

Permalink
feat: Add CrumblingAttunedShard and recipe (#756)
Browse files Browse the repository at this point in the history
* Add CrumblingAttunedShard and recipe

This variant of an Attuned Shard is less costly to create in
a warp-plate, but can only be used once. For the same target
warp-plate it stacks up to 4 (and one can create the full
stack at once when attuning).

Each separate teleport event will consume 1 Crumbling Attuned
Shard when teleporting to the associated destination.

If multiple shards are in a Plate inventory Crumbling Attuned Shards
are considered like any other shard in terms of random picking.
This can be changed by putting a spider eye in one of the slots,
in which case the picking process will only consider crumbling
attuned shards.

* Rename shards/shards_consumable -> single_use_warp_shards and warp_shards
Create AbstractAttunedShardItem to avoid badly aging constructor overloads
Remove unnecessary stack size limiting that should be deferred to the item stack instead
Rename useShardsPrioritization to
shouldPrioritizeSingleUseShards

---------

Co-authored-by: BlayTheNinth <1933180+BlayTheNinth@users.noreply.github.com>
  • Loading branch information
edralzar and BlayTheNinth authored Dec 5, 2023
1 parent c6e10a6 commit aba9b45
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ protected void addTags(HolderLookup.Provider lookup) {
getOrCreateTagBuilder(ModItemTags.RETURN_SCROLLS).add(ModItems.returnScroll);
getOrCreateTagBuilder(ModItemTags.BOUND_SCROLLS).add(ModItems.boundScroll);
getOrCreateTagBuilder(ModItemTags.WARP_STONES).add(ModItems.warpStone);
getOrCreateTagBuilder(ModItemTags.WARP_SHARDS).add(ModItems.attunedShard, ModItems.crumblingAttunedShard);
getOrCreateTagBuilder(ModItemTags.SINGLE_USE_WARP_SHARDS).add(ModItems.crumblingAttunedShard);
getOrCreateTagBuilder(ModItemTags.WAYSTONES).add(ModBlocks.waystone.asItem(), ModBlocks.mossyWaystone.asItem(), ModBlocks.sandyWaystone.asItem());
FabricTagProvider<Item>.FabricTagBuilder sharestonesTag = getOrCreateTagBuilder(ModItemTags.SHARESTONES);
sharestonesTag.add(ModBlocks.sharestone.asItem());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"waystones:crumbling_attuned_shard"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"waystones:attuned_shard",
"waystones:crumbling_attuned_shard"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
import net.blay09.mods.balm.api.container.ImplementedContainer;
import net.blay09.mods.balm.api.menu.BalmMenuProvider;
import net.blay09.mods.waystones.Waystones;
import net.blay09.mods.waystones.api.*;
import net.blay09.mods.waystones.api.IMutableWaystone;
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.api.WaystoneOrigin;
import net.blay09.mods.waystones.api.WaystonesAPI;
import net.blay09.mods.waystones.block.WarpPlateBlock;
import net.blay09.mods.waystones.config.WaystonesConfig;
import net.blay09.mods.waystones.core.*;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.blay09.mods.waystones.recipe.ModRecipes;
import net.blay09.mods.waystones.recipe.WarpPlateRecipe;
import net.blay09.mods.waystones.tag.ModItemTags;
import net.blay09.mods.waystones.worldgen.namegen.NameGenerationMode;
import net.blay09.mods.waystones.worldgen.namegen.NameGenerator;
import net.minecraft.ChatFormatting;
Expand All @@ -37,7 +41,6 @@
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.state.BlockState;
Expand Down Expand Up @@ -268,9 +271,10 @@ public void serverTick() {
if (!entity.isAlive() || !isEntityOnWarpPlate(entity)) {
iterator.remove();
} else if (ticksPassed > useTime) {
IWaystone targetWaystone = getTargetWaystone();
ItemStack targetAttunementStack = getTargetAttunementStack();
IWaystone targetWaystone = WaystonesAPI.getBoundWaystone(targetAttunementStack).orElse(null);
if (targetWaystone != null && targetWaystone.isValid()) {
teleportToWarpPlate(entity, targetWaystone);
teleportToWarpPlate(entity, targetWaystone, targetAttunementStack);
}

if (entity instanceof Player) {
Expand Down Expand Up @@ -307,9 +311,16 @@ private int getWarpPlateUseTime() {
return Mth.clamp((int) (configuredUseTime * useTimeMultiplier), 1, configuredUseTime * 2);
}

private void teleportToWarpPlate(Entity entity, IWaystone targetWaystone) {
PlayerWaystoneManager.tryTeleportToWaystone(entity, targetWaystone, WarpMode.WARP_PLATE, getWaystone())
.ifLeft(entities -> entities.forEach(this::applyWarpPlateEffects));
private void teleportToWarpPlate(Entity entity, IWaystone targetWaystone, ItemStack targetAttunementStack) {
WaystonesAPI.createDefaultTeleportContext(entity, targetWaystone, WarpMode.WARP_PLATE, getWaystone())
.flatMap(ctx -> {
ctx.setWarpItem(targetAttunementStack);
ctx.setConsumesWarpItem(targetAttunementStack.is(ModItemTags.SINGLE_USE_WARP_SHARDS));
return PlayerWaystoneManager.tryTeleport(ctx);
})
.ifRight(PlayerWaystoneManager.informRejectedTeleport(entity))
.ifLeft(entities -> entities.forEach(this::applyWarpPlateEffects))
.left();
}

private void applyWarpPlateEffects(Entity entity) {
Expand Down Expand Up @@ -372,34 +383,46 @@ private WarpPlateRecipe trySelectRecipe() {
if (!readyForAttunement || level == null) {
return null;
}
if (getItem(0).getCount() > 1) {
return null; //prevents crafting when more than 1 ingredient is present
}

return level.getRecipeManager().getRecipeFor(ModRecipes.warpPlateRecipeType, this, level)
.map(RecipeHolder::value).orElse(null);
}

@Nullable
public IWaystone getTargetWaystone() {
boolean useRoundRobin = false;
public ItemStack getTargetAttunementStack() {
boolean shouldRoundRobin = false;
boolean shouldPrioritizeSingleUseShards = false;
List<ItemStack> attunedShards = new ArrayList<>();
for (int i = 0; i < getContainerSize(); i++) {
ItemStack itemStack = getItem(i);
if (itemStack.getItem() instanceof IAttunementItem) {
IWaystone waystoneAttunedTo = ((IAttunementItem) itemStack.getItem()).getWaystoneAttunedTo(level.getServer(), itemStack);
if (itemStack.is(ModItemTags.WARP_SHARDS)) {
IWaystone waystoneAttunedTo = WaystonesAPI.getBoundWaystone(itemStack).orElse(null);
if (waystoneAttunedTo != null && !waystoneAttunedTo.getWaystoneUid().equals(getWaystone().getWaystoneUid())) {
attunedShards.add(itemStack);
}
} else if (itemStack.getItem() == Items.QUARTZ) {
useRoundRobin = true;
shouldRoundRobin = true;
} else if (itemStack.getItem() == Items.SPIDER_EYE) {
shouldPrioritizeSingleUseShards = true;
}
}
if (shouldPrioritizeSingleUseShards && attunedShards.stream().anyMatch(stack -> stack.is(ModItemTags.SINGLE_USE_WARP_SHARDS))) {
attunedShards.removeIf(stack -> !stack.is(ModItemTags.SINGLE_USE_WARP_SHARDS));
}

if (!attunedShards.isEmpty()) {
lastAttunementSlot = (lastAttunementSlot + 1) % attunedShards.size();
ItemStack itemStack = useRoundRobin ? attunedShards.get(lastAttunementSlot) : attunedShards.get(random.nextInt(attunedShards.size()));
return ((IAttunementItem) itemStack.getItem()).getWaystoneAttunedTo(level.getServer(), itemStack);
return shouldRoundRobin ? attunedShards.get(lastAttunementSlot) : attunedShards.get(random.nextInt(attunedShards.size()));
}

return null;
return ItemStack.EMPTY;
}

@Nullable
public IWaystone getTargetWaystone() {
return WaystonesAPI.getBoundWaystone(getTargetAttunementStack()).orElse(null);
}

public int getMaxAttunementTicks() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

public class PlayerWaystoneManager {

Expand Down Expand Up @@ -205,15 +206,19 @@ private static void informPlayer(Entity entity, String translationKey) {
}
}

public static Consumer<WaystoneTeleportError> informRejectedTeleport(final Entity entityToInform) {
return error -> {
logger.info("Rejected teleport: " + error.getClass().getSimpleName());
if (error.getTranslationKey() != null) {
informPlayer(entityToInform, error.getTranslationKey());
}
};
}

public static Either<List<Entity>, WaystoneTeleportError> tryTeleportToWaystone(Entity entity, IWaystone waystone, WarpMode warpMode, @Nullable IWaystone fromWaystone) {
return WaystonesAPI.createDefaultTeleportContext(entity, waystone, warpMode, fromWaystone)
.flatMap(PlayerWaystoneManager::tryTeleport)
.ifRight(error -> {
logger.info("Rejected teleport: " + error.getClass().getSimpleName());
if (error.getTranslationKey() != null) {
informPlayer(entity, error.getTranslationKey());
}
});
.ifRight(informRejectedTeleport(entity));
}

public static Either<List<Entity>, WaystoneTeleportError> tryTeleport(IWaystoneTeleportContext context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package net.blay09.mods.waystones.item;

import net.blay09.mods.balm.api.Balm;
import net.blay09.mods.waystones.api.IAttunementItem;
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.block.WarpPlateBlock;
import net.blay09.mods.waystones.core.WaystoneProxy;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Objects;

public abstract class AbstractAttunedShardItem extends Item implements IAttunementItem {

public AbstractAttunedShardItem(Properties properties) {
super(properties);
}

@Override
public boolean isFoil(ItemStack itemStack) {
IWaystone waystoneAttunedTo = getWaystoneAttunedTo(null, itemStack);
return waystoneAttunedTo != null && waystoneAttunedTo.isValid();
}

@Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {
super.appendHoverText(stack, world, list, flag);

IWaystone attunedWarpPlate = getWaystoneAttunedTo(null, stack);
if (attunedWarpPlate == null || !attunedWarpPlate.isValid()) {
var textComponent = Component.translatable("tooltip.waystones.attuned_shard.attunement_lost");
textComponent.withStyle(ChatFormatting.GRAY);
list.add(textComponent);
return;
}

list.add(WarpPlateBlock.getGalacticName(attunedWarpPlate));

Player player = Balm.getProxy().getClientPlayer();
if (player != null && player.containerMenu instanceof WarpPlateContainer) {
IWaystone currentWarpPlate = ((WarpPlateContainer) player.containerMenu).getWaystone();
if (attunedWarpPlate.getWaystoneUid().equals(currentWarpPlate.getWaystoneUid())) {
list.add(Component.translatable("tooltip.waystones.attuned_shard.move_to_other_warp_plate"));
} else {
list.add(Component.translatable("tooltip.waystones.attuned_shard.plug_into_warp_plate"));
}
} else {
list.add(Component.translatable("tooltip.waystones.attuned_shard.plug_into_warp_plate"));
}
}

@Nullable
@Override
public IWaystone getWaystoneAttunedTo(MinecraftServer server, ItemStack itemStack) {
CompoundTag compound = itemStack.getTag();
if (compound != null && compound.contains("AttunedToWaystone", Tag.TAG_INT_ARRAY)) {
return new WaystoneProxy(server, NbtUtils.loadUUID(Objects.requireNonNull(compound.get("AttunedToWaystone"))));
}

return null;
}

@Override
public void setWaystoneAttunedTo(ItemStack itemStack, @Nullable IWaystone waystone) {
CompoundTag tagCompound = itemStack.getTag();
if (tagCompound == null) {
tagCompound = new CompoundTag();
itemStack.setTag(tagCompound);
}

if (waystone != null) {
tagCompound.put("AttunedToWaystone", NbtUtils.createUUID(waystone.getWaystoneUid()));
} else {
tagCompound.remove("AttunedToWaystone");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,89 +1,9 @@
package net.blay09.mods.waystones.item;

import net.blay09.mods.balm.api.Balm;
import net.blay09.mods.waystones.api.IAttunementItem;
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.block.WarpPlateBlock;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.blay09.mods.waystones.core.WaystoneProxy;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Objects;

public class AttunedShardItem extends Item implements IAttunementItem {
public class AttunedShardItem extends AbstractAttunedShardItem {

public AttunedShardItem(Properties properties) {
super(properties.stacksTo(1));
}

@Override
public boolean isFoil(ItemStack itemStack) {
IWaystone waystoneAttunedTo = getWaystoneAttunedTo(null, itemStack);
return waystoneAttunedTo != null && waystoneAttunedTo.isValid();
}

@Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {
super.appendHoverText(stack, world, list, flag);

IWaystone attunedWarpPlate = getWaystoneAttunedTo(null, stack);
if (attunedWarpPlate == null || !attunedWarpPlate.isValid()) {
var textComponent = Component.translatable("tooltip.waystones.attuned_shard.attunement_lost");
textComponent.withStyle(ChatFormatting.GRAY);
list.add(textComponent);
return;
}

list.add(WarpPlateBlock.getGalacticName(attunedWarpPlate));

Player player = Balm.getProxy().getClientPlayer();
if (player != null && player.containerMenu instanceof WarpPlateContainer) {
IWaystone currentWarpPlate = ((WarpPlateContainer) player.containerMenu).getWaystone();
if (attunedWarpPlate.getWaystoneUid().equals(currentWarpPlate.getWaystoneUid())) {
list.add(Component.translatable("tooltip.waystones.attuned_shard.move_to_other_warp_plate"));
} else {
list.add(Component.translatable("tooltip.waystones.attuned_shard.plug_into_warp_plate"));
}
} else {
list.add(Component.translatable("tooltip.waystones.attuned_shard.plug_into_warp_plate"));
}
}

@Nullable
@Override
public IWaystone getWaystoneAttunedTo(MinecraftServer server, ItemStack itemStack) {
CompoundTag compound = itemStack.getTag();
if (compound != null && compound.contains("AttunedToWaystone", Tag.TAG_INT_ARRAY)) {
return new WaystoneProxy(server, NbtUtils.loadUUID(Objects.requireNonNull(compound.get("AttunedToWaystone"))));
}

return null;
}

@Override
public void setWaystoneAttunedTo(ItemStack itemStack, @Nullable IWaystone waystone) {
CompoundTag tagCompound = itemStack.getTag();
if (tagCompound == null) {
tagCompound = new CompoundTag();
itemStack.setTag(tagCompound);
}

if (waystone != null) {
tagCompound.put("AttunedToWaystone", NbtUtils.createUUID(waystone.getWaystoneUid()));
} else {
tagCompound.remove("AttunedToWaystone");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.blay09.mods.waystones.item;

import net.blay09.mods.balm.api.Balm;
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.menu.WarpPlateContainer;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public class CrumblingAttunedShardItem extends AbstractAttunedShardItem {

public CrumblingAttunedShardItem(Properties properties) {
super(properties.stacksTo(4));
}

@Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag flag) {
super.appendHoverText(stack, world, list, flag);

IWaystone attunedWarpPlate = getWaystoneAttunedTo(null, stack);
if (attunedWarpPlate != null && attunedWarpPlate.isValid()) {
var textComponent = Component.translatable("tooltip.waystones.attuned_shard.attunement_crumbling");
textComponent.withStyle(ChatFormatting.WHITE).withStyle(ChatFormatting.ITALIC);

Player player = Balm.getProxy().getClientPlayer();
if (player != null && player.containerMenu instanceof WarpPlateContainer wpc) {
if (!attunedWarpPlate.getWaystoneUid().equals(wpc.getWaystone().getWaystoneUid())) {
list.add(textComponent);
}
} else {
list.add(textComponent);
}
}
}

}
Loading

0 comments on commit aba9b45

Please sign in to comment.