Files
forge/src/main/java/com/rejahtavi/rfp2/RenderPlayerDummy.java
2025-05-10 11:37:44 +03:00

334 lines
17 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.rejahtavi.rfp2;
import com.rejahtavi.rfp2.compat.handlers.RFP2CompatHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Mouse;
/*
* This class handles calls to draw the PlayerDummy object (except not really.)
*
* When doRender() is called, we don't draw anything for the dummy itself.
* Instead, we prepare some things, then call a vanilla player renderer at a very precise position relative to the camera.
*
* In other words: The original doRender() call is *indirectly* triggering a vanilla player render operation,
* instead of rendering our dummy entity (which is invisible anyway).
*
* Using the vanilla renderer means we will automatically inherit any changes *other* mods have made to the player character,
* but it also means that we have to deal with anything they add to the head that could block the view.
*/
public class RenderPlayerDummy extends Render<EntityPlayerDummy>
{
// Объявляем lastLeftClickTime как поле класса
private long lastLeftClickTime = 0; // В миллисекундах
// Constructor
public RenderPlayerDummy(RenderManager renderManager)
{
// Call parent constructor
super(renderManager);
}
// Handles requests for texture of the player dummy
@Override
protected ResourceLocation getEntityTexture(EntityPlayerDummy entity)
{
// The PlayerDummy entity is only for tracking the player's position, so it does not have a texture.
return null;
}
// Implements linear interpolation for animation smoothing
private float linearInterpolate(float current, float target, float partialTicks)
{
// Explanation for linear interpolation math can be found here:
// https://en.wikipedia.org/wiki/Linear_interpolation
return ((1 - partialTicks) * current) + (partialTicks * target);
}
// Called when the game wants to draw our PlayerDummy entity
@Override
public void doRender(EntityPlayerDummy renderEntity, double renderPosX, double renderPosY, double renderPosZ, float renderYaw, float partialTicks)
{
/*
* NO-OP Checklist: We want to use as few CPU cycles as possible if we aren't
* going to do anything useful. The following checks abort the render *as soon
* as possible* if any of those conditions are true.
*/
// Grab a reference to the local player entity, null-check it, and abort if it fails.
EntityPlayerSP player = Minecraft.getMinecraft().player;
if (player == null) return;
// Grab a backup of any items we might possibly touch, so that we can be guaranteed
// to be able to restore them when it comes time for the finally{} block to run.
ItemStack itemMainHand = player.inventory.getCurrentItem();
ItemStack itemOffHand = player.inventory.offHandInventory.get(0);
ItemStack itemHelmetSlot = player.inventory.armorInventory.get(3);
// Make quick per-frame compatibility checks based on current configuration and player state
// Implement config option for disabling when sneaking
if (RFP2Config.compatibility.disableWhenSneaking && player.isSneaking()) return;
// Grab a reference to the vanilla player renderer, null check, and abort if it fails
Render<AbstractClientPlayer> render = (RenderPlayer) this.renderManager.<AbstractClientPlayer>getEntityRenderObject(player);
RenderPlayer playerRenderer = (RenderPlayer) render;
if (playerRenderer == null) return;
// Grab a reference to the local player's model, null check, and abort if it fails
ModelPlayer playerModel = (ModelPlayer) playerRenderer.getMainModel();
if (playerModel == null) return;
RFP2.logger.log(RFP2.LOGGING_LEVEL_HIGH, playerModel.getClass().getCanonicalName());
// Grab a backup of the various player model layers we might adjust
// This way we aren't making any assumptions about what other mods might be doing with these options
// and we can restore everything when we are finished.
boolean[] modelState = { playerModel.bipedHead.isHidden,
playerModel.bipedHead.showModel,
playerModel.bipedHeadwear.isHidden,
playerModel.bipedHeadwear.showModel,
playerModel.bipedLeftArm.isHidden,
playerModel.bipedLeftArm.showModel,
playerModel.bipedLeftArmwear.isHidden,
playerModel.bipedLeftArmwear.showModel,
playerModel.bipedRightArm.isHidden,
playerModel.bipedRightArm.showModel,
playerModel.bipedRightArmwear.isHidden,
playerModel.bipedRightArmwear.showModel
};
/*
* With the routine, unlikely-to-fail stuff out of the way, try to make the remainder
* of this routine as fail-safe as possible. It runs every frame, so we want to be able
* to stop it from running anymore if we encounter a problem, to avoid slowing down the
* game and consuming disk space with useless error logs.
*/
try
{
// Note: thirdPersonView can be: 0 = First Person, 1 = Third Person Rear, 2 = Third Person
// If the player is NOT in first person, do nothing
if (Minecraft.getMinecraft().gameSettings.thirdPersonView != 0) return;
// If the player is flying with an Elytra, do nothing
if (player.isElytraFlying()) return;
// (Keep this NO-OP check last, it can be more expensive than the others, due to the mount check.)
// If mod is not enabled this frame, do nothing
if (!RFP2.state.isModEnabled(player)) return;
if (!RFP2.state.isRealArmsEnabled(player)) return;
// Check if any of the compatibility handlers want us to skip this frame
for (RFP2CompatHandler handler : RFP2.compatHandlers)
{
if (handler.getDisableRFP2(player))
{
return;
}
}
/*
* Initialization: Pull in state info and set up local variables, now that we
* know we actually need to do some rendering.
*/
// Initialize remaining local variables
double playerRenderPosX = 0;
double playerRenderPosZ = 0;
double playerRenderPosY = 0;
float playerRenderAngle = 0;
// Get local copies of config & state
float playerModelOffset = (float) RFP2Config.preferences.playerModelOffset;
boolean isRealArmsEnabled = RFP2.state.isRealArmsEnabled(player);
boolean isHeadRotationEnabled = RFP2.state.isHeadRotationEnabled(player);
playerModelOffset += -0.2f*(1-player.rotationPitch/90);
// Check if the player is sprinting
if (player.isSprinting()) {
// Increase playerModelOffset by 0.2 when sprinting
playerModelOffset += 0.2f;
}
// Check if the player is falling
if (!player.onGround && player.motionY < -0.5f) { // Если игрок не на земле и движется вниз
playerModelOffset += 0.4f; // Increase offset when falling
}
// Check if the player is holding down the Shift key
if (Minecraft.getMinecraft().gameSettings.keyBindSneak.isKeyDown()) {
// Increase playerModelOffset by 0.2
playerModelOffset += 0.75f;
}
// Проверяем, была ли нажата ЛКМ
if (Mouse.isButtonDown(0)) {
lastLeftClickTime = System.currentTimeMillis(); // Сохраняем время нажатия
}
// Проверяем, прошло ли 1 секунду с момента нажатия ЛКМ
long currentTime = System.currentTimeMillis();
if (lastLeftClickTime != 0 && (currentTime - lastLeftClickTime >= 400)) { // 1000 миллисекунд = 1 секунда
playerModelOffset += 0.5f; // Увеличиваем смещение
if (currentTime - lastLeftClickTime >= 2100 ) { // 1000 миллисекунд = 1 секунда
lastLeftClickTime = 0;
playerModelOffset -= 0.5f; }// Увеличиваем смещение
}
/*
* Adjust Player Model:
* Strip unwanted items and layers from the player model to avoid obstructing the camera
*/
// Remove the player's helmet, so that it does not obstruct the camera.
player.inventory.armorInventory.set(3, ItemStack.EMPTY);
// Hide the player model's head layers, again so they do not obstruct the camera.
playerModel.bipedHead.isHidden = true;
playerModel.bipedHead.showModel = false;
playerModel.bipedHeadwear.isHidden = true;
playerModel.bipedHeadwear.showModel = false;
// Instruct compatibility handlers hide head models (handlers are responsible for caching state for later restoration)
for (RFP2CompatHandler handler : RFP2.compatHandlers)
{
handler.hideHead(player, true);
}
// Check if we need to hide the arms
if (!isRealArmsEnabled)
{
// The real arms feature is not enabled, so we should NOT render the arms in 3D.
// That means we need to hide them before we draw the player model.
// Remove the player's currently held main and off hand items, so that they do not obstruct the camera.
player.inventory.removeStackFromSlot(player.inventory.currentItem);
player.inventory.offHandInventory.set(0, ItemStack.EMPTY);
// Hide the player model's arm layers
playerModel.bipedLeftArm.isHidden = true;
playerModel.bipedLeftArm.showModel = false;
playerModel.bipedRightArm.isHidden = true;
playerModel.bipedRightArm.showModel = false;
playerModel.bipedLeftArmwear.isHidden = true;
playerModel.bipedLeftArmwear.showModel = false;
playerModel.bipedRightArmwear.isHidden = true;
playerModel.bipedRightArmwear.showModel = false;
// Instruct compatibility handlers hide arm models (handlers are responsible for caching state for later restoration)
for (RFP2CompatHandler handler : RFP2.compatHandlers)
{
handler.hideArms(player, true);
}
}
/*
* Calculate Rendering Coordinates:
* Determine the precise location and angle the player should be rendered this frame, then render it.
*
* Notes:
* player.rotationYaw = The direction the player's camera is facing.
* player.renderYawOffset = The direction the player's 3D model is facing.
*
* (In vanilla minecraft, the player's body lags behind the head to provide a more natural look to movement.
* This can lead to renderYawOffset being off from the main camera by up to 75 degrees!)
*/
// Generate default rendering coordinates for player body
playerRenderPosX = player.posX - renderEntity.posX + renderPosX;
playerRenderPosY = player.posY - renderEntity.posY + renderPosY;
playerRenderPosZ = player.posZ - renderEntity.posZ + renderPosZ;
// If the player IS sleeping, we can skip any extra calculations and proceed directly to rendering.
if (!player.isPlayerSleeping())
{
// The player is NOT sleeping, so we are going to need to make some adjustments.
// If head rotation IS enabled, then we DO NOT need to counteract it and can skip the next adjustment.
if (!isHeadRotationEnabled)
{
// Head rotation is NOT enabled, so we have to prevent the vanilla rotation behavior!
// We can do this by updating the player's body's rendering values to match the camera's
// position before rendering each frame. It is *critical* that we update the data for both
// this frame and the previous one, or else our linear interpolation will be fed bad
// data and the result will not look smooth!
player.renderYawOffset = player.rotationYaw;
player.prevRenderYawOffset = player.prevRotationYaw;
}
// Interpolate to get final rendering position
playerRenderAngle = this.linearInterpolate(player.prevRenderYawOffset, player.renderYawOffset, partialTicks);
// Update position of rendered body to include interpolation and model offset
playerRenderPosX += (playerModelOffset * Math.sin(Math.toRadians(playerRenderAngle)));
playerRenderPosZ -= (playerModelOffset * Math.cos(Math.toRadians(playerRenderAngle)));
}
/*
* Trigger rendering of the fake player model: this is done by calling the
* vanilla player renderer with our adjusted coordinates.
*
* Note that we do NOT pass the playerModel here -- the vanilla renderer already
* has it! That's why we had to actually remove the player's helmet and possibly
* other inventory items. That also means that it is *critical* that we undo all
* of those changes immediately after this, before anything outside of RFP2 can
* interact with these objects and invalidate our cached state.
*
* (That is also why those reversions are implemented within a finally block.)
*/
playerRenderer.doRender(player, playerRenderPosX, playerRenderPosY, playerRenderPosZ, playerRenderAngle, partialTicks);
}
catch (Exception e)
{
// If anything goes wrong, shut the mod off and write an error to the logs.
RFP2.errorDisableMod(this.getClass().getName() + ".doRender()", e);
}
finally
{
/*false
* Cleanup Phase:
* Revert all temporary changes we made to the model for rendering purposes, so that we don't cause any side effects.
*
* Whether or not something went wrong, we want to make ABSOLUTELY SURE to give
* back any items we took and re-enable all player model rendering layers.
*
* If we fail to do this, we could glitch out the player's rendering, or worse,
* accidentally delete someone's hard won inventory items!
*/
// restore the player's inventory to the state we found it in
player.inventory.armorInventory.set(3, itemHelmetSlot);
player.inventory.setInventorySlotContents(player.inventory.currentItem, itemMainHand);
player.inventory.offHandInventory.set(0, itemOffHand);
// restore the player model's rendering layers
playerModel.bipedHead.isHidden = modelState[0];
playerModel.bipedHead.showModel = modelState[1];
playerModel.bipedHeadwear.isHidden = modelState[2];
playerModel.bipedHeadwear.showModel = modelState[3];
playerModel.bipedLeftArm.isHidden = modelState[4];
playerModel.bipedLeftArm.showModel = modelState[5];
playerModel.bipedLeftArmwear.isHidden = modelState[6];
playerModel.bipedLeftArmwear.showModel = modelState[7];
playerModel.bipedRightArm.isHidden = modelState[8];
playerModel.bipedRightArm.showModel = modelState[9];
playerModel.bipedRightArmwear.isHidden = modelState[10];
playerModel.bipedRightArmwear.showModel = modelState[11];
// Instruct compatibility handlers restore head models
for (RFP2CompatHandler handler : RFP2.compatHandlers)
{
handler.restoreHead(player, true);
handler.restoreArms(player, true);
}
}
}
}