Skip to content

Commit

Permalink
starting hud
Browse files Browse the repository at this point in the history
  • Loading branch information
parzivail committed Nov 15, 2024
1 parent b30805d commit 57827a4
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 68 deletions.
102 changes: 102 additions & 0 deletions projects/pswg_blasters/src/client/java/dev/pswg/BlastersClient.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package dev.pswg;

import dev.pswg.api.GalaxiesClientAddon;
import dev.pswg.events.HudRenderEvents;
import dev.pswg.item.BlasterItem;
import dev.pswg.renderer.BlasterBoltEntityRenderer;
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.util.Identifier;

/**
* The main entrypoint for PSWG client-side blaster features
Expand All @@ -16,6 +23,101 @@ public void onGalaxiesClientReady()
EntityRendererRegistry.register(Blasters.BLASTER_BOLT_ENTITY, BlasterBoltEntityRenderer::new);
EntityModelLayerRegistry.registerModelLayer(BlasterBoltEntityRenderer.MODEL_LAYER, BlasterBoltEntityRenderer.Model::getTexturedModelData);

HudRenderEvents.CROSSHAIR.register(BlastersClient::renderCrosshair);

Blasters.LOGGER.info("Client module initialized");
}

private static final Identifier HUD_ELEMENTS_TEXTURE = Blasters.id("textures/gui/hud_elements.png");

private static void renderCrosshair(DrawContext context, RenderTickCounter tickCounter)
{
var client = MinecraftClient.getInstance();
if (client.player == null)
return;

var stack = client.player.getMainHandStack();
if (!stack.isOf(Blasters.BLASTER_ITEM))
return;

var stats = BlasterItem.getStats(stack);
var coolingStatus = BlasterItem.getCoolingStatus(client.world, stack, tickCounter.getTickDelta(false));

var m = context.getMatrices();
m.push();

var left = (int)(context.getScaledWindowWidth() / 2f);
var top = (int)(context.getScaledWindowHeight() / 2f);

final var cooldownWidth = 61;
var cooldownOffset = 30;
var cooldownBarX = left - cooldownWidth / 2;

// translucent background
context.drawTexture(
RenderLayer::getGuiTexturedOverlay,
HUD_ELEMENTS_TEXTURE,
cooldownBarX, top + cooldownOffset,
0, 0,
cooldownWidth, 3,
256, 256,
-1
);

if (coolingStatus.coolingMode() == BlasterItem.CoolingMode.PASSIVE)
{
// passive heat accumulator
context.drawTexture(
RenderLayer::getGuiTexturedOverlay,
HUD_ELEMENTS_TEXTURE,
cooldownBarX, top + cooldownOffset,
0, 4,
(int)(cooldownWidth * coolingStatus.totalHeat() / stats.heat().capacity()), 3,
256, 256,
-1
);
}
else
{
// cooldown background
context.drawTexture(
RenderLayer::getGuiTexturedOverlay,
HUD_ELEMENTS_TEXTURE,
cooldownBarX, top + cooldownOffset,
0, 16,
cooldownWidth, 3,
256, 256,
-1
);

var heat = coolingStatus.totalHeat() / (stats.heat().capacity() + stats.heat().overheatPenalty());

// cursor
m.push();
m.translate(cooldownBarX + heat * (cooldownWidth - 3), 0, 0);
context.drawTexture(
RenderLayer::getGuiTexturedOverlay,
HUD_ELEMENTS_TEXTURE,
0, top + cooldownOffset - 2,
0, 24,
3, 7,
256, 256,
-1
);
m.pop();
}

// endcaps
context.drawTexture(
RenderLayer::getGuiTexturedOverlay,
HUD_ELEMENTS_TEXTURE,
cooldownBarX, top + cooldownOffset,
0, 20,
cooldownWidth, 3,
256, 256,
-1
);

m.pop();
}
}
Empty file.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"package": "dev.pswg.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"DrawContextMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down
73 changes: 56 additions & 17 deletions projects/pswg_blasters/src/main/java/dev/pswg/item/BlasterItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ public boolean canBypass()
public static final PacketCodec<RegistryByteBuf, CoolingMode> PACKET_CODEC = GalaxiesPacketCodecs.forEnum(CoolingMode.class);
}

/**
* Represents the cooling status of a blaster
*
* @param coolingMode The current cooling mode of the blaster.
* @param totalHeat The total amount of heat accumulated in the blaster, passively or otherwise.
*/
public record CoolingStatus(CoolingMode coolingMode, float totalHeat)
{
}

/**
* The available cooling bypass categories
*/
Expand Down Expand Up @@ -436,20 +446,22 @@ public static Optional<CoolingBypass> getCoolingBypass(World world, ItemStack st
return Optional.empty();

var stats = getStats(stack);
var progress = getAccumulatedHeat(world, stack, tickDelta) / stats.heat().capacity();
if (progress == 0)
var potentialVentingHeat = getVentingHeat(world, stack, tickDelta);
if (potentialVentingHeat.isEmpty())
return Optional.empty();

var ventingHeat = potentialVentingHeat.get() / stats.heat().capacity();

var attachments = getAttachments(stack);

var primaryBypassTime = stats.cooling().primaryBypassTime();
var primaryBypassTolerance = getScaledPrimaryBypassTolerance(stats, attachments);
if (Math.abs(progress - primaryBypassTime) <= primaryBypassTolerance)
if (Math.abs(ventingHeat - primaryBypassTime) <= primaryBypassTolerance)
return Optional.of(CoolingBypass.PRIMARY);

var secondaryBypassTime = stats.cooling().secondaryBypassTime();
var secondaryBypassTolerance = getScaledSecondaryBypassTolerance(stats, attachments);
if (Math.abs(progress - secondaryBypassTime) <= secondaryBypassTolerance)
if (Math.abs(ventingHeat - secondaryBypassTime) <= secondaryBypassTolerance)
return Optional.of(CoolingBypass.SECONDARY);

return Optional.empty();
Expand Down Expand Up @@ -487,19 +499,25 @@ public static boolean canFire(World world, LivingEntity user, ItemStack stack)
*
* @return The current heat of the blaster
*/
public static float getAccumulatedHeat(World world, ItemStack stack, float tickDelta)
public static Optional<Float> getAccumulatedHeat(World world, ItemStack stack, float tickDelta)
{
var state = getState(stack);
if (state.coolingMode() != CoolingMode.PASSIVE)
return Optional.empty();

var stats = getStats(stack);
var attachments = getAttachments(stack);
var state = getState(stack);

var time = world.getTime() + tickDelta;

var lastCommittedHeat = state.lastTotalHeat();
var dissipationPerTick = getScaledHeatDrainSpeed(stats, attachments);

var dissipation = dissipationPerTick * (time - state.cooldownStart());
return MathHelper.clamp(lastCommittedHeat - dissipation, 0, lastCommittedHeat);
if (dissipation > lastCommittedHeat)
return Optional.empty();

return Optional.of(Math.min(lastCommittedHeat - dissipation, lastCommittedHeat));
}

/**
Expand All @@ -511,11 +529,11 @@ public static float getAccumulatedHeat(World world, ItemStack stack, float tickD
*
* @return The current heat of the blaster
*/
public static float getVentingHeat(World world, ItemStack stack, float tickDelta)
public static Optional<Float> getVentingHeat(World world, ItemStack stack, float tickDelta)
{
var state = getState(stack);
if (state.coolingMode() == CoolingMode.PASSIVE)
return 0;
return Optional.empty();

var stats = getStats(stack);
var attachments = getAttachments(stack);
Expand All @@ -526,7 +544,27 @@ public static float getVentingHeat(World world, ItemStack stack, float tickDelta
var dissipationPerTick = getScaledOverheatDrainSpeed(stats, attachments);

var dissipation = dissipationPerTick * (time - state.cooldownStart());
return MathHelper.clamp(lastVentingHeat - dissipation, 0, lastVentingHeat);
if (dissipation > lastVentingHeat)
return Optional.empty();

return Optional.of(Math.min(lastVentingHeat - dissipation, lastVentingHeat));
}

/**
* Determines the cooling status of a blaster, whether passively or actively cooling
*
* @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 cooling status of the blaster, including its current cooling mode and total accumulated or venting heat
*/
public static CoolingStatus getCoolingStatus(World world, ItemStack stack, float tickDelta)
{
var state = getState(stack);
return getVentingHeat(world, stack, tickDelta)
.map(ventingHeat -> new CoolingStatus(state.coolingMode(), ventingHeat))
.orElseGet(() -> new CoolingStatus(CoolingMode.PASSIVE, getAccumulatedHeat(world, stack, tickDelta).orElse(0f)));
}

/**
Expand Down Expand Up @@ -683,14 +721,15 @@ public ActionResult useLeft(World world, LivingEntity user, Hand hand)

var timestamp = world.getTime();

var totalVentingHeat = getVentingHeat(world, itemStack, 0);
if (totalVentingHeat == 0 && state.coolingMode != CoolingMode.PASSIVE)
var coolingStatus = getCoolingStatus(world, itemStack, 0);

if (coolingStatus.coolingMode() == CoolingMode.PASSIVE && state.coolingMode() != CoolingMode.PASSIVE)
state = state.withCooling(CoolingMode.PASSIVE, timestamp);

if (state.coolingMode.isCooling())
if (state.coolingMode().isCooling())
{
var isRepeatEvent = false; // TODO: value ultimately comes from #usageTickLeft
if (world.isClient() || !state.coolingMode.canBypass() || isRepeatEvent)
if (world.isClient() || !state.coolingMode().canBypass() || isRepeatEvent)
{
itemStack.set(STATE, state);
return ActionResult.FAIL;
Expand All @@ -699,7 +738,7 @@ public ActionResult useLeft(World world, LivingEntity user, Hand hand)
var bypass = getCoolingBypass(world, itemStack, 0);
if (bypass.isEmpty())
{
state = state.withLastVentingHeat(totalVentingHeat)
state = state.withLastVentingHeat(coolingStatus.totalHeat())
.withCooling(CoolingMode.FAILED_OVERCHARGE, timestamp);

// TODO: play sound - failed bypass
Expand Down Expand Up @@ -735,7 +774,7 @@ else if (bypass.get() == CoolingBypass.SECONDARY)
.withCooling(CoolingMode.PASSIVE, timestamp + getScaledPassiveCooldownDelay(stats, attachments))
.withFireCooldown(timestamp + getScaledAutoRepeatDelay(stats, attachments));

var totalHeat = getAccumulatedHeat(world, itemStack, 0);
var totalHeat = coolingStatus.totalHeat();

if (overchargeTimeRemaining(world, itemStack, 0) == 0)
totalHeat += stats.heat().perRound();
Expand All @@ -758,7 +797,7 @@ else if (bypass.get() == CoolingBypass.SECONDARY)
{
// TODO: play sound - overheat

state = state.withLastVentingHeat(totalHeat + stats.heat().overheatPenalty())
state = state.withLastVentingHeat(stats.heat().capacity() + stats.heat().overheatPenalty())
.withCooling(CoolingMode.OVERHEAT, timestamp)
.withBurstBoltsRemaining(0);

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dev.pswg.events;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderTickCounter;

public final class HudRenderEvents
{
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Crosshair
{
void crosshair(DrawContext context, RenderTickCounter tickCounter);
}

public static final Event<Crosshair> CROSSHAIR = EventFactory.createArrayBacked(
Crosshair.class,
(context, tickCounter) -> {
},
(callbacks) -> (context, tickCounter) -> {
for (Crosshair callback : callbacks)
callback.crosshair(context, tickCounter);
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.pswg.mixin.client.events;

import dev.pswg.events.HudRenderEvents;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter;
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;

@Mixin(InGameHud.class)
public class InGameHudMixin
{
@Inject(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawGuiTexture(Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIII)V", ordinal = 0))
void renderCrosshair(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci)
{
var matrix = context.getMatrices();

matrix.push();
HudRenderEvents.CROSSHAIR.invoker().crosshair(context, tickCounter);
matrix.pop();
}
}
Loading

0 comments on commit 57827a4

Please sign in to comment.