Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions changelog/v_4.0.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@
- Pole Climbing
- Hang Down Swinging
- Creative Fly
- New Items
- *ParCool Guide*
- Builtin in-game Guide and Skill Tree
- Equipments
- *Traceur's Gloves*
- *Traceur's Boots*
- Grappling Hook
- Physics based Rope Swinging
- Aim Assist Cone, used only when the crosshair itself misses
- Reworked Existing Animations
- Powered Zipline
- Some Equipment Items
- Builtin in-game Guide and Skill Tree
- Additional APIs
- Some Attributes
- Sounds for Some Actions

### Modified

- ParCool API
- Re-implement Internal Base System
- Re-implement Actions
- Re-implement Zipline
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/alrex/parcool/api/action/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ public void startExplicitly() {

protected final boolean isPossible() {
var player = parkourability.player();
if (parkourability.player().isSpectator() || (parkourability.getStamina().isExhausted())) return false;
var option = entry.option();
if (player.isSpectator()) return false;
if (!option.availableWhileExhausted() && parkourability.getStamina().isExhausted()) return false;
if ((option.neededPose() != null && option.neededPose() != player.getPose())
|| (!option.availableInFluid() && player.isInFluidType())
|| (!option.availableNotInFluid() && !player.isInFluidType())
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/alrex/parcool/api/action/ActionOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public record Value(
boolean availableInFluid,
boolean availableNotInFluid,
boolean availableWithFallFlying,
boolean availableWhileExhausted,
boolean needLearning,
LogicalSide triggeredSide
) {
}
Expand All @@ -34,13 +36,15 @@ public record Value(
private boolean availableInFluid = false;
private boolean availableNotInFluid = true;
private boolean availableWithFallFlying = false;
private boolean availableWhileExhausted = false;
private boolean needLearning = true;
private boolean needOnGround = false;
private boolean needNotOnGround = false;
private LogicalSide triggeredSide = LogicalSide.CLIENT;

public Value build() {
return new Value(
staminaConsumption, learningCost, parent, neededPose, beforeProcessedActions, needOnGround, needNotOnGround, availableInFluid, availableNotInFluid, availableWithFallFlying, triggeredSide
staminaConsumption, learningCost, parent, neededPose, beforeProcessedActions, needOnGround, needNotOnGround, availableInFluid, availableNotInFluid, availableWithFallFlying, availableWhileExhausted, needLearning, triggeredSide
);
}

Expand Down Expand Up @@ -93,6 +97,16 @@ public ActionOption availableWithFallFlying(boolean availableWithFallFlying) {
return this;
}

public ActionOption availableWhileExhausted(boolean availableWhileExhausted) {
this.availableWhileExhausted = availableWhileExhausted;
return this;
}

public ActionOption needLearning(boolean needLearning) {
this.needLearning = needLearning;
return this;
}

public ActionOption triggeredSide(LogicalSide side) {
this.triggeredSide = side;
return this;
Expand Down
91 changes: 91 additions & 0 deletions src/main/java/com/alrex/parcool/client/GrappleCameraHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.alrex.parcool.client;

import com.alrex.parcool.ParCool;
import com.alrex.parcool.common.Parkourability;
import com.alrex.parcool.common.action.ParCoolActions;
import com.alrex.parcool.common.action.impl.Grapple;
import com.alrex.parcool.util.EntityUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.ComputeFovModifierEvent;
import net.minecraftforge.client.event.ViewportEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;

@OnlyIn(Dist.CLIENT)
public class GrappleCameraHandler {
private static final double REFERENCE_SPEED = 1.6;

private static final float MAX_FOV_GAIN = 0.18f;

private static final float MAX_ROLL_DEGREES = 8f;

private static final double SMOOTHING = 0.18;

private static float previousIntensity = 0;
private static float currentIntensity = 0;
private static float previousRoll = 0;
private static float currentRoll = 0;

@SubscribeEvent
public static void onClientTick(TickEvent.ClientTickEvent event) {
if (event.phase == TickEvent.Phase.START) return;
previousIntensity = currentIntensity;
previousRoll = currentRoll;

float targetIntensity = 0;
float targetRoll = 0;

LocalPlayer player = Minecraft.getInstance().player;
if (player != null && !Minecraft.getInstance().isPaused()) {
Parkourability parkourability = Parkourability.get(player);
if (parkourability != null) {
Grapple grapple = parkourability.get(ParCoolActions.GRAPPLE);
if (grapple.isAttached() || grapple.isMomentumActive()) {
Vec3 movement = player.getDeltaMovement();
double speed = Math.sqrt(movement.x * movement.x + movement.z * movement.z);
targetIntensity = (float) Mth.clamp(speed / REFERENCE_SPEED, 0, 1);
targetRoll = grapple.isAttached() ? bankAngle(player, grapple, targetIntensity) : 0;
}
}
}
currentIntensity = (float) Mth.lerp(SMOOTHING, currentIntensity, targetIntensity);
currentRoll = (float) Mth.lerp(SMOOTHING, currentRoll, targetRoll);
}

private static float bankAngle(LocalPlayer player, Grapple grapple, float intensity) {
Vec3 pivot = grapple.getPivot();
if (pivot == null) return 0;
Vec3 toPivot = pivot.subtract(player.position());
double horizontal = Math.sqrt(toPivot.x * toPivot.x + toPivot.z * toPivot.z);
if (horizontal < 1.0e-4) return 0;

Vec3 forward = EntityUtil.getHorizontalLookAngle(player);
Vec3 right = new Vec3(-forward.z, 0, forward.x);
double lateral = (toPivot.x * right.x + toPivot.z * right.z) / horizontal;
return (float) (lateral * MAX_ROLL_DEGREES * intensity);
}

@SubscribeEvent
public static void onComputeFov(ComputeFovModifierEvent event) {
double configured = ParCool.getConfig().client().grapplingHook.fovIntensity().get();
if (configured <= 0) return;
float partialTick = Minecraft.getInstance().getFrameTime();
float intensity = Mth.lerp(partialTick, previousIntensity, currentIntensity);
if (intensity <= 1.0e-4) return;
event.setNewFovModifier(event.getNewFovModifier() * (1f + MAX_FOV_GAIN * intensity * (float) configured));
}

@SubscribeEvent
public static void onComputeCameraAngles(ViewportEvent.ComputeCameraAngles event) {
double configured = ParCool.getConfig().client().grapplingHook.cameraRollIntensity().get();
if (configured <= 0) return;
float roll = Mth.lerp((float) event.getPartialTick(), previousRoll, currentRoll);
if (Math.abs(roll) <= 1.0e-4) return;
event.setRoll(event.getRoll() + roll * (float) configured);
}
}
83 changes: 83 additions & 0 deletions src/main/java/com/alrex/parcool/client/GrappleTargetOverlay.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.alrex.parcool.client;

import com.alrex.parcool.ParCool;
import com.alrex.parcool.common.Parkourability;
import com.alrex.parcool.common.action.ParCoolActions;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.RenderGuiEvent;
import net.minecraftforge.client.event.RenderLevelStageEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;

@OnlyIn(Dist.CLIENT)
public class GrappleTargetOverlay {
public static final ResourceLocation TEXTURE = ParCool.resourceLocation("textures/misc/grapple_target.png");
private static final int TEXTURE_SIZE = 15;

private static float screenX;
private static float screenY;
private static boolean visible = false;

@SubscribeEvent
public static void onRenderLevel(RenderLevelStageEvent event) {
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_PARTICLES) return;
visible = false;

Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null || minecraft.options.hideGui) return;
Parkourability parkourability = Parkourability.get(minecraft.player);
if (parkourability == null) return;
Vec3 target = parkourability.get(ParCoolActions.GRAPPLE).getPreviewTarget();
if (target == null) return;

Vec3 camera = event.getCamera().getPosition();
Vector4f point = new Vector4f(
(float) (target.x - camera.x),
(float) (target.y - camera.y),
(float) (target.z - camera.z),
1f
);
point.transform(event.getPoseStack().last().pose());
point.transform(event.getProjectionMatrix());

if (point.w() <= 1.0e-4) return;

float normalisedX = point.x() / point.w();
float normalisedY = point.y() / point.w();
if (Math.abs(normalisedX) > 1.5f || Math.abs(normalisedY) > 1.5f) return;

var window = minecraft.getWindow();
screenX = (normalisedX * 0.5f + 0.5f) * window.getGuiScaledWidth();
screenY = (1 - (normalisedY * 0.5f + 0.5f)) * window.getGuiScaledHeight();
visible = true;
}

@SubscribeEvent
public static void onRenderGui(RenderGuiEvent.Post event) {
if (!visible) return;
int size = ParCool.getConfig().client().targetIndicatorSize.get();
PoseStack poseStack = event.getPoseStack();

RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, TEXTURE);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();

GuiComponent.blit(
poseStack,
Math.round(screenX - size / 2f), Math.round(screenY - size / 2f),
size, size,
0, 0, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE
);

RenderSystem.disableBlend();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ private static LogicalInput listen(BooleanSupplier keyDownSupplier) {
public static final Input HIDE_IN_BLOCK = register(new KeyMapping("key.parcool.hide_in_block", GLFW.GLFW_KEY_C, KEY_CATEGORY));

public static final LogicalInput JUMP = listen(Minecraft.getInstance().options.keyJump::isDown);
public static final LogicalInput USE_ITEM = listen(Minecraft.getInstance().options.keyUse::isDown);
public static final LogicalInput ATTACK = listen(Minecraft.getInstance().options.keyAttack::isDown);
public static final LogicalInput SHIFT = listen(Minecraft.getInstance().options.keyShift::isDown);
public static final LogicalInput MOVEMENT_FORWARD = listen(Minecraft.getInstance().options.keyUp::isDown);
public static final LogicalInput MOVEMENT_BACK = listen(Minecraft.getInstance().options.keyDown::isDown);
Expand Down
Loading