Skip to content

Commit

Permalink
deterministic heat dissipation
Browse files Browse the repository at this point in the history
  • Loading branch information
parzivail committed Oct 31, 2024
1 parent 181629b commit 663a101
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.pswg.mixin.client;

import dev.pswg.Blasters;
import dev.pswg.GalaxiesClient;
import dev.pswg.item.BlasterItem;
import dev.pswg.rendering.Drawables;
import net.minecraft.client.MinecraftClient;
Expand All @@ -16,7 +17,7 @@
public abstract class DrawContextMixin
{
/**
* Draw the current heat dissipation in blaster stack overlays
* Draw the current lastTotalHeat dissipation in blaster stack overlays
*/
@Inject(method = "drawStackOverlay(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawCooldownProgress(Lnet/minecraft/item/ItemStack;II)V", shift = At.Shift.AFTER))
public void drawStackOverlay(TextRenderer textRenderer, ItemStack stack, int x, int y, String stackCountText, CallbackInfo ci)
Expand All @@ -29,8 +30,11 @@ public void drawStackOverlay(TextRenderer textRenderer, ItemStack stack, int x,
assert client.world != null;

// TODO: better visual
BlasterItem.getFireCooldownProgress(client.world, stack)
BlasterItem.getFireCooldownProgress(client.world, stack, GalaxiesClient.getTickDelta())
.ifPresent(value -> Drawables.itemDurability(self, value, x, y - 13, 13, 0x0000FF));

var heat = BlasterItem.getHeat(client.world, stack, GalaxiesClient.getTickDelta());
Drawables.itemDurability(self, heat / 10, x, y - 10, 13, 0x0000FF);
}
}
}
122 changes: 106 additions & 16 deletions projects/pswg_blasters/src/main/java/dev/pswg/item/BlasterItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,18 @@ public class BlasterItem extends Item implements ILeftClickUsable
public record StateComponent(
boolean isAiming,
long lastFired,
long fireCooldown
long fireCooldown,
long lastHeated,
float lastTotalHeat
)
{
public static final Codec<BlasterItem.StateComponent> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
Codec.BOOL.fieldOf("isAiming").forGetter(BlasterItem.StateComponent::isAiming),
Codec.LONG.fieldOf("lastFired").forGetter(BlasterItem.StateComponent::lastFired),
Codec.LONG.fieldOf("fireCooldown").forGetter(BlasterItem.StateComponent::fireCooldown)
Codec.LONG.fieldOf("fireCooldown").forGetter(BlasterItem.StateComponent::fireCooldown),
Codec.LONG.fieldOf("lastHeated").forGetter(BlasterItem.StateComponent::fireCooldown),
Codec.FLOAT.fieldOf("lastTotalHeat").forGetter(BlasterItem.StateComponent::lastTotalHeat)
)
.apply(instance, BlasterItem.StateComponent::new)
);
Expand All @@ -70,24 +74,38 @@ public record StateComponent(
StateComponent::lastFired,
PacketCodecs.VAR_LONG,
StateComponent::fireCooldown,
PacketCodecs.VAR_LONG,
StateComponent::lastHeated,
PacketCodecs.FLOAT,
StateComponent::lastTotalHeat,
StateComponent::new
);

public static final StateComponent DEFAULT = new StateComponent(false, 0L, 0L);
public static final StateComponent DEFAULT = new StateComponent(false, 0, 0, 0, 0);

public StateComponent withIsAiming(boolean isAiming)
{
return new StateComponent(isAiming, lastFired, fireCooldown);
return new StateComponent(isAiming, lastFired, fireCooldown, lastHeated, lastTotalHeat);
}

public StateComponent withLastFired(long lastFired)
{
return new StateComponent(isAiming, lastFired, fireCooldown);
return new StateComponent(isAiming, lastFired, fireCooldown, lastHeated, lastTotalHeat);
}

public StateComponent withFireCooldown(long fireCooldown)
{
return new StateComponent(isAiming, lastFired, fireCooldown);
return new StateComponent(isAiming, lastFired, fireCooldown, lastHeated, lastTotalHeat);
}

public StateComponent withLastHeated(long lastHeated)
{
return new StateComponent(isAiming, lastFired, fireCooldown, lastHeated, lastTotalHeat);
}

public StateComponent withLastTotalHeat(float lastTotalHeat)
{
return new StateComponent(isAiming, lastFired, fireCooldown, lastHeated, lastTotalHeat);
}
}

Expand Down Expand Up @@ -121,7 +139,7 @@ public StateComponent withFireCooldown(long fireCooldown)
/**
* The component that contains the mutable gameplay state of the blaster
*/
public static final ComponentType<StateComponent> STATE = Registry.register(
private static final ComponentType<StateComponent> STATE = Registry.register(
Registries.DATA_COMPONENT_TYPE,
Blasters.id("state"),
ComponentType.<StateComponent>builder().codec(StateComponent.CODEC).packetCodec(StateComponent.PACKET_CODEC).build()
Expand Down Expand Up @@ -208,14 +226,56 @@ public static long getLastFired(ItemStack stack)
*
* @param stack The stack to modify
* @param lastFired The world tick
*
* @see #getLastFired
*/
public static void setLastFired(ItemStack stack, long lastFired)
{
applyState(stack, state -> state.withLastFired(lastFired));
}

/**
* Gets the timestamp when heat was last added, and therefore the
* time from which all heat calculations (e.g. cooldowns) are made.
*
* @param stack The stack to query
*
* @return The world tick when heat was last applied
*/
public static long getLastHeated(ItemStack stack)
{
return stack.getOrDefault(STATE, StateComponent.DEFAULT).lastHeated();
}

/**
* Gets the amount of heat the blaster contained the last time
* heat was added. To get the current amount of heat, taking
* into account cooling and other parameters, see {@link #getHeat(World, ItemStack, float)}.
*
* @param stack The stack to query
*
* @return The total amount of heat at the last time heat was added
*
* @see #getLastHeated
*/
public static float getLastTotalHeat(ItemStack stack)
{
return stack.getOrDefault(STATE, StateComponent.DEFAULT).lastTotalHeat();
}

/**
* Sets the amount of heat the blaster contained the last time
* heat was added.
*
* @param stack The stack to modify
* @param heat The total amount of heat in the blaster
*/
public static void setLastTotalHeat(ItemStack stack, long timestamp, float heat)
{
applyState(stack, state -> state
.withLastTotalHeat(heat)
.withLastHeated(timestamp)
);
}

/**
* Determines the next world tick when the blaster is able to be
* fired again. It is derived from the global timestamp {@link World#getTime()}.
Expand Down Expand Up @@ -246,22 +306,23 @@ public static void setFireCooldown(ItemStack stack, long cooldownEnd)
* If currently waiting to be able to fire again, gets the current
* progress [0,1) of the cooldown process
*
* @param world The world to the stack's timestamps are referenced
* @param stack The stack to query
* @param world The world to the stack's timestamps are referenced
* @param stack The stack to query
* @param tickDelta The partial tick to evaluate at
*
* @return A float [0,1) if currently waiting to be able to fire, empty otherwise
*/
public static Optional<Float> getFireCooldownProgress(World world, ItemStack stack)
public static Optional<Float> getFireCooldownProgress(World world, ItemStack stack, float tickDelta)
{
var lastFired = getLastFired(stack);
var cooldown = getFireCooldown(stack);
var time = world.getTime();
var time = world.getTime() + tickDelta;

if (cooldown <= lastFired || cooldown < time)
return Optional.empty();

var cooldownLength = cooldown - lastFired;
var cooldownProgress = (float)(time - lastFired) / cooldownLength;
var cooldownProgress = (time - lastFired) / cooldownLength;
return Optional.of(cooldownProgress);
}

Expand All @@ -286,6 +347,30 @@ public static boolean canFire(World world, LivingEntity user, ItemStack stack)
return true;
}

/**
* Calculates the current heat of the blaster based on the dissipation rate and the time passed since the last shot.
*
* @param world The world to the stack's timestamps are referenced
* @param stack The stack to query
* @param tickDelta The partial tick to evaluate at
*
* @return The current heat of the blaster
*/
public static float getHeat(World world, ItemStack stack, float tickDelta)
{
var time = world.getTime() + tickDelta;

var lastCommittedHeat = getLastTotalHeat(stack);
var lastCommittedHeatTime = getLastHeated(stack);

// TODO: pull these values from a default component
var dissipationDelayTicks = 30;
var dissipationPerTick = 2;

var dissipation = dissipationPerTick * (time - lastCommittedHeatTime - dissipationDelayTicks);
return MathHelper.clamp(lastCommittedHeat - dissipation, 0, lastCommittedHeat);
}

@Override
public boolean canMine(BlockState state, World world, BlockPos pos, PlayerEntity miner)
{
Expand Down Expand Up @@ -362,8 +447,13 @@ public ActionResult useLeft(World world, LivingEntity user, Hand hand)
0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
);

setLastFired(itemStack, world.getTime());
// TODO: pull this value from a default component
var currentHeat = getHeat(world, itemStack, 0);

var timestamp = world.getTime();
setLastFired(itemStack, timestamp);

// TODO: pull these values from a default component
setLastTotalHeat(itemStack, timestamp, currentHeat + 100);
setFireCooldown(itemStack, world.getTime() + TickConstants.ONE_SECOND);

if (world instanceof ServerWorld serverWorld)
Expand Down

0 comments on commit 663a101

Please sign in to comment.