Skip to content

Commit

Permalink
feat: Add xpCostsFullLevels option to charge xp points instead of lev…
Browse files Browse the repository at this point in the history
…els #412
  • Loading branch information
BlayTheNinth committed Dec 13, 2023
1 parent 33b6e74 commit cec87a4
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public Either<IWaystoneTeleportContext, WaystoneTeleportError> createDefaultTele
context.setCooldown(PlayerWaystoneManager.getCooldownPeriod(warpMode, waystone));

// Use the context so far to determine the xp cost
context.setXpCost(PlayerWaystoneManager.getExperienceLevelCost(entity, waystone, warpMode, context));
context.setExperienceCost(PlayerWaystoneManager.getExperienceLevelCost(context));
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.blay09.mods.waystones.api;

import net.blay09.mods.waystones.xp.ExperienceLevelCost;
import net.blay09.mods.waystones.xp.ExperiencePointsCost;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;

public interface ExperienceCost {
boolean canAfford(Player player);

void consume(Player player);

int getCostAsLevels(Player player);

Component getCostAsTooltip(Player player);

boolean isEmpty();

static ExperienceCost fromLevels(int levels) {
return new ExperienceLevelCost(levels);
}

static ExperienceCost fromExperience(int experience) {
return new ExperiencePointsCost(experience);
}

class NoExperienceCost implements ExperienceCost {
public static final ExperienceCost INSTANCE = new NoExperienceCost();

@Override
public boolean canAfford(Player player) {
return true;
}

@Override
public void consume(Player player) {
}

@Override
public int getCostAsLevels(Player player) {
return 0;
}

@Override
public Component getCostAsTooltip(Player player) {
return Component.translatable("gui.waystones.waystone_selection.no_xp_requirement");
}

@Override
public boolean isEmpty() {
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ public interface IWaystoneTeleportContext {

boolean isDimensionalTeleport();

@Deprecated(forRemoval = true)
int getXpCost();

@Deprecated(forRemoval = true)
void setXpCost(int xpCost);

ExperienceCost getExperienceCost();

void setExperienceCost(ExperienceCost experienceCost);

void setCooldown(int cooldown);

int getCooldown();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ public IWaystoneTeleportContext getContext() {
return context;
}

public ExperienceCost getExperienceCost() {
return context.getExperienceCost();
}

public void setExperienceCost(ExperienceCost experienceCost) {
context.setExperienceCost(experienceCost);
}

@Deprecated
public int getXpCost() {
return context.getXpCost();
}

@Deprecated
public void setXpCost(int xpCost) {
context.setXpCost(xpCost);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.blay09.mods.waystones.core.PlayerWaystoneManager;
import net.blay09.mods.waystones.core.WarpMode;
import net.blay09.mods.waystones.network.message.InventoryButtonMessage;
import net.blay09.mods.waystones.api.ExperienceCost;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
Expand Down Expand Up @@ -99,11 +100,16 @@ public static void initialize() {

long timeLeft = PlayerWaystoneManager.getInventoryButtonCooldownLeft(player);
IWaystone waystone = PlayerWaystoneManager.getInventoryButtonWaystone(player);
int xpLevelCost = waystone != null ? PlayerWaystoneManager.predictExperienceLevelCost(player, waystone, WarpMode.INVENTORY_BUTTON, (IWaystone) null) : 0;
final ExperienceCost xpCost = waystone != null ? PlayerWaystoneManager.predictExperienceLevelCost(player,
waystone,
WarpMode.INVENTORY_BUTTON,
null) : ExperienceCost.NoExperienceCost.INSTANCE;
int secondsLeft = (int) (timeLeft / 1000);
if (inventoryButtonMode.hasNamedTarget()) {
tooltip.add(formatTranslation(ChatFormatting.YELLOW, "gui.waystones.inventory.return_to_waystone"));
tooltip.add(formatTranslation(ChatFormatting.GRAY, "tooltip.waystones.bound_to", ChatFormatting.DARK_AQUA + inventoryButtonMode.getNamedTarget()));
tooltip.add(formatTranslation(ChatFormatting.GRAY,
"tooltip.waystones.bound_to",
ChatFormatting.DARK_AQUA + inventoryButtonMode.getNamedTarget()));
if (secondsLeft > 0) {
tooltip.add(Component.empty());
}
Expand All @@ -125,8 +131,8 @@ public static void initialize() {
}
}

if (xpLevelCost > 0 && player.experienceLevel < xpLevelCost) {
tooltip.add(formatTranslation(ChatFormatting.RED, "tooltip.waystones.not_enough_xp", xpLevelCost));
if (!xpCost.canAfford(player)) {
tooltip.add(formatTranslation(ChatFormatting.RED, "tooltip.waystones.not_enough_xp", xpCost));
}

if (secondsLeft > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ private void updateList() {
private WaystoneButton createWaystoneButton(int y, final IWaystone waystone) {
IWaystone waystoneFrom = menu.getWaystoneFrom();
Player player = Minecraft.getInstance().player;
int xpLevelCost = Math.round(PlayerWaystoneManager.predictExperienceLevelCost(Objects.requireNonNull(player),
final var xpCost = PlayerWaystoneManager.predictExperienceLevelCost(Objects.requireNonNull(player),
waystone,
menu.getWarpMode(),
waystoneFrom));
WaystoneButton btnWaystone = new WaystoneButton(width / 2 - 100, y, waystone, xpLevelCost, button -> onWaystoneSelected(waystone));
waystoneFrom);
WaystoneButton btnWaystone = new WaystoneButton(width / 2 - 100, y, waystone, xpCost, button -> onWaystoneSelected(waystone));
if (waystoneFrom != null && waystone.getWaystoneUid().equals(waystoneFrom.getWaystoneUid())) {
btnWaystone.active = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package net.blay09.mods.waystones.client.gui.widget;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.core.PlayerWaystoneManager;
import net.blay09.mods.waystones.api.ExperienceCost;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class WaystoneButton extends Button {
Expand All @@ -26,17 +25,17 @@ public class WaystoneButton extends Button {
private static final ResourceLocation[] DISABLED_LEVEL_SPRITES = new ResourceLocation[]{new ResourceLocation("container/enchanting_table/level_1_disabled"), new ResourceLocation(
"container/enchanting_table/level_2_disabled"), new ResourceLocation("container/enchanting_table/level_3_disabled")};

private final int xpLevelCost;
private final ExperienceCost xpCost;
private final IWaystone waystone;

public WaystoneButton(int x, int y, IWaystone waystone, int xpLevelCost, OnPress pressable) {
public WaystoneButton(int x, int y, IWaystone waystone, ExperienceCost xpCost, OnPress pressable) {
super(x, y, 200, 20, getWaystoneNameComponent(waystone), pressable, Button.DEFAULT_NARRATION);
Player player = Minecraft.getInstance().player;
this.xpLevelCost = xpLevelCost;
this.xpCost = xpCost;
this.waystone = waystone;
if (player == null || !PlayerWaystoneManager.mayTeleportToWaystone(player, waystone)) {
active = false;
} else if (player.experienceLevel < xpLevelCost && !player.getAbilities().instabuild) {
} else if (!xpCost.canAfford(player) && !player.getAbilities().instabuild) {
active = false;
}
}
Expand Down Expand Up @@ -75,20 +74,23 @@ public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float
}

// render xp cost
if (xpLevelCost > 0) {
boolean canAfford = Objects.requireNonNull(mc.player).experienceLevel >= xpLevelCost || mc.player.getAbilities().instabuild;
final var spriteIndex = Math.min(xpLevelCost, 3) - 1;
if (!xpCost.isEmpty()) {
boolean canAfford = xpCost.canAfford(mc.player);
final var xpCostAsLevels = xpCost.getCostAsLevels(mc.player);
final var spriteIndex = Math.min(xpCostAsLevels, 3) - 1;
guiGraphics.blitSprite(canAfford ? ENABLED_LEVEL_SPRITES[spriteIndex] : DISABLED_LEVEL_SPRITES[spriteIndex], getX() + 2, getY() + 2, 16, 16);

if (xpLevelCost > 3) {
if (xpCostAsLevels > 3) {
guiGraphics.drawString(mc.font, "+", getX() + 17, getY() + 6, 0xC8FF8F);
}

if (isHovered && mouseX <= getX() + 16) {
final List<Component> tooltip = new ArrayList<>();
final var levelRequirementText = Component.translatable("gui.waystones.waystone_selection.level_requirement", xpLevelCost);
levelRequirementText.withStyle(canAfford ? ChatFormatting.GREEN : ChatFormatting.RED);
tooltip.add(levelRequirementText);
final var xpCostText = xpCost.getCostAsTooltip(mc.player);
if (xpCostText instanceof MutableComponent mutableComponent) {
mutableComponent.withStyle(canAfford ? ChatFormatting.GREEN : ChatFormatting.RED);
}
tooltip.add(xpCostText);
guiGraphics.renderTooltip(mc.font, tooltip, Optional.empty(), mouseX, mouseY + mc.font.lineHeight);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public class WaystonesConfigData implements BalmConfigData {
public Compatibility compatibility = new Compatibility();

public static class XpCost {
@Synced
@Comment("Set to true if experience cost should cost full levels rather than experience points. Make sure to also adjust blocksPerXpLevel, maximumBaseXpCost and dimensionalWarpXpCost appropriately.")
public boolean xpCostsFullLevels = true; // TODO default to false in 1.20.4

@Synced
@Comment("Set to true if experience cost should be inverted, meaning the shorter the distance, the more expensive. Can be used to encourage other methods for short-distance travel.")
public boolean inverseXpCost = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
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.blay09.mods.waystones.api.ExperienceCost;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand Down Expand Up @@ -126,13 +127,24 @@ public static void activateWaystone(Player player, IWaystone waystone) {
}
}

public static int predictExperienceLevelCost(Entity player, IWaystone waystone, WarpMode warpMode, @Nullable IWaystone fromWaystone) {
public static ExperienceCost predictExperienceLevelCost(Entity player, IWaystone waystone, WarpMode warpMode, @Nullable IWaystone fromWaystone) {
WaystoneTeleportContext context = new WaystoneTeleportContext(player, waystone, null);
context.getLeashedEntities().addAll(findLeashedAnimals(player));
context.setFromWaystone(fromWaystone);
return getExperienceLevelCost(player, waystone, warpMode, context);
context.setWarpMode(warpMode);
return getExperienceLevelCost(context);
}

public static ExperienceCost getExperienceLevelCost(IWaystoneTeleportContext context) {
final var xpCost = getExperienceLevelCost(context.getEntity(), context.getTargetWaystone(), context.getWarpMode(), context);
if (WaystonesConfig.getActive().xpCost.xpCostsFullLevels) {
return ExperienceCost.fromLevels(xpCost);
} else {
return ExperienceCost.fromExperience(xpCost);
}
}

@Deprecated
public static int getExperienceLevelCost(Entity entity, IWaystone waystone, WarpMode warpMode, IWaystoneTeleportContext context) {
if (!(entity instanceof Player player)) {
return 0;
Expand Down Expand Up @@ -186,8 +198,11 @@ public static IWaystone getInventoryButtonWaystone(Player player) {

public static boolean canUseInventoryButton(Player player) {
IWaystone waystone = getInventoryButtonWaystone(player);
int xpLevelCost = waystone != null ? predictExperienceLevelCost(player, waystone, WarpMode.INVENTORY_BUTTON, null) : 0;
return getInventoryButtonCooldownLeft(player) <= 0 && (xpLevelCost <= 0 || player.experienceLevel >= xpLevelCost);
final ExperienceCost xpCost = waystone != null ? predictExperienceLevelCost(player,
waystone,
WarpMode.INVENTORY_BUTTON,
null) : ExperienceCost.NoExperienceCost.INSTANCE;
return getInventoryButtonCooldownLeft(player) <= 0 && xpCost.canAfford(player);
}

public static boolean canUseWarpStone(Player player, ItemStack heldItem) {
Expand Down Expand Up @@ -258,7 +273,7 @@ public static Either<List<Entity>, WaystoneTeleportError> tryTeleport(IWaystoneT
}
}

if (entity instanceof Player && ((Player) entity).experienceLevel < context.getXpCost()) {
if (entity instanceof Player player && !context.getExperienceCost().canAfford(player)) {
return Either.right(new WaystoneTeleportError.NotEnoughXp());
}

Expand All @@ -269,7 +284,7 @@ public static Either<List<Entity>, WaystoneTeleportError> tryTeleport(IWaystoneT

if (entity instanceof Player player) {
applyCooldown(warpMode, player, context.getCooldown());
applyXpCost(player, context.getXpCost());
context.getExperienceCost().consume(player);
}

final var teleportedEntities = doTeleport(context);
Expand All @@ -286,18 +301,11 @@ private static void sendHackySyncPacketsAfterTeleport(Entity entity) {
}
}

private static void applyXpCost(Player player, int xpLevelCost) {
if (xpLevelCost > 0) {
player.giveExperienceLevels(-xpLevelCost);
}
}

private static void applyCooldown(WarpMode warpMode, Player player, int cooldown) {
if (cooldown > 0) {
final Level level = player.level();
switch (warpMode) {
case INVENTORY_BUTTON ->
getPlayerWaystoneData(level).setInventoryButtonCooldownUntil(player, System.currentTimeMillis() + cooldown * 1000L);
case INVENTORY_BUTTON -> getPlayerWaystoneData(level).setInventoryButtonCooldownUntil(player, System.currentTimeMillis() + cooldown * 1000L);
case WARP_STONE -> getPlayerWaystoneData(level).setWarpStoneCooldownUntil(player, System.currentTimeMillis() + cooldown * 1000L);
}
WaystoneSyncManager.sendWaystoneCooldowns(player);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import net.blay09.mods.waystones.api.IWaystone;
import net.blay09.mods.waystones.api.IWaystoneTeleportContext;
import net.blay09.mods.waystones.api.TeleportDestination;
import net.blay09.mods.waystones.api.ExperienceCost;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

Expand All @@ -24,8 +26,10 @@ public class WaystoneTeleportContext implements IWaystoneTeleportContext {
private WarpMode warpMode = WarpMode.CUSTOM;
private ItemStack warpItem = ItemStack.EMPTY;
@Nullable
private Boolean consumesWarpItem; // nullable for now so we can fallback to legacy warp mode implementation
private int xpCost;
private Boolean consumesWarpItem; // nullable for now so we can fall back to legacy warp mode implementation

private ExperienceCost xpCost = ExperienceCost.NoExperienceCost.INSTANCE;

private int cooldown;
private boolean playsSound = true;
private boolean playsEffect = true;
Expand Down Expand Up @@ -99,12 +103,25 @@ public boolean isDimensionalTeleport() {

@Override
public int getXpCost() {
return xpCost;
if (entity instanceof Player player) {
return xpCost.getCostAsLevels(player);
}
return 0;
}

@Override
public void setXpCost(int xpCost) {
this.xpCost = xpCost;
this.xpCost = ExperienceCost.fromLevels(xpCost);
}

@Override
public ExperienceCost getExperienceCost() {
return xpCost;
}

@Override
public void setExperienceCost(ExperienceCost experienceCost) {
this.xpCost = experienceCost;
}

@Override
Expand Down
Loading

0 comments on commit cec87a4

Please sign in to comment.