Skip to content
Closed
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
14 changes: 14 additions & 0 deletions common/src/main/java/com/lambda/mixin/render/ChatHudMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@

@Mixin(ChatHud.class)
public class ChatHudMixin {
/**
* Redirects the chat HUD text rendering to apply custom text parsing and color adjustments.
*
* <p>This method intercepts calls to {@code DrawContext#drawTextWithShadow} during chat rendering.
* It processes the text using {@code LambdaMoji.INSTANCE.parse} to incorporate custom formatting (e.g., emoji support),
* overrides the x-coordinate by rendering at x = 0, and adjusts the text color by combining a white base (0xFFFFFF)
* with an alpha value derived from the provided {@code color} parameter.
*
* @param text the text to be rendered, processed for custom formatting
* @param x the original x-coordinate (ignored as rendering occurs at x = 0)
* @param y the y-coordinate for rendering the text
* @param color the original text color used to compute the alpha channel for the final rendered color
* @return the result of the underlying text rendering call
*/
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/OrderedText;III)I"))
int redirectRenderCall(DrawContext instance, TextRenderer textRenderer, OrderedText text, int x, int y, int color) {
return instance.drawTextWithShadow(textRenderer, LambdaMoji.INSTANCE.parse(text, x, y, color), 0, y, 16777215 + (color << 24));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,56 @@ public abstract class ChatInputSuggestorMixin {
@Shadow
private @Nullable CompletableFuture<Suggestions> pendingSuggestions;

/**
* Displays the current suggestion list.
*
* <p>This shadowed method should be overridden to update the suggestion display. If
* {@code narrateFirstSuggestion} is {@code true}, the method should also provide narration
* for the first suggestion to enhance accessibility.
*
* @param narrateFirstSuggestion if {@code true}, the first suggestion is narrated; otherwise, it is not
*/
@Shadow
public abstract void show(boolean narrateFirstSuggestion);

/**
* Determines if the current chat input text should be treated as a command.
*
* <p>This method overrides the provided <code>showCompletions</code> flag during the refresh
* process by evaluating the text currently entered in the chat input field. It returns
* <code>true</code> if the input is recognized as a command, and <code>false</code> otherwise.</p>
*
* @param showCompletions the initial flag indicating whether completions should be shown (its value is ignored)
* @return <code>true</code> if the chat input text is recognized as a command, <code>false</code> otherwise
*/
@ModifyVariable(method = "refresh", at = @At(value = "STORE"), index = 3)
private boolean refreshModify(boolean showCompletions) {
return CommandManager.INSTANCE.isCommand(textField.getText());
}

/**
* Redirects the command dispatcher retrieval to use a custom dispatcher based on the current chat input.
* <p>
* Instead of invoking the default dispatcher from the network handler during a refresh, this method
* fetches a dispatcher tailored to the chat input text from the CommandManager.
*
* @return a command dispatcher that reflects the current chat input context
*/
@Redirect(method = "refresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;getCommandDispatcher()Lcom/mojang/brigadier/CommandDispatcher;"))
private CommandDispatcher<CommandSource> refreshRedirect(ClientPlayNetworkHandler instance) {
return CommandManager.INSTANCE.currentDispatcher(textField.getText());
}

/**
* Injects emoji suggestion logic at the end of the chat refresh process.
*
* <p>This method verifies that emoji suggestions are enabled and that the current input is not a command.
* It extracts text up to the cursor, identifies the last colon to determine the start of the emoji query,
* and filters available emojis based on the subsequent substring. The resulting suggestions are then
* prepared and, once completed, are displayed.
*
* @param ci the callback information for the injection point
*/
@Inject(method = "refresh", at = @At("TAIL"))
private void refreshEmojiSuggestion(CallbackInfo ci) {
if (!LambdaMoji.INSTANCE.isEnabled() ||
Expand Down Expand Up @@ -105,6 +142,16 @@ private void refreshEmojiSuggestion(CallbackInfo ci) {
@Unique
private static final Pattern COLON_PATTERN = Pattern.compile("(:[a-zA-Z0-9_]+)");

/**
* Returns the index of the last occurrence of a colon that precedes valid emoji key characters.
*
* <p>The method uses a regular expression to identify sequences starting with a colon
* followed by alphanumeric characters or underscores. If the input is null, empty, or no
* matching sequence is found, it returns -1.</p>
*
* @param input the string to be searched for a colon pattern
* @return the index of the last colon matching the pattern, or -1 if none is found
*/
@Unique
private int neoLambda$getLastColon(String input) {
if (Strings.isNullOrEmpty(input)) return -1;
Expand Down
10 changes: 10 additions & 0 deletions common/src/main/java/com/lambda/mixin/render/ChatScreenMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@

@Mixin(ChatScreen.class)
public abstract class ChatScreenMixin {
/**
* Intercepts the chat message sending process to execute Lambda commands.
*
* <p>If the chat text is recognized as a Lambda command, the command is executed via the CommandManager,
* and the default message sending is canceled by setting the callback's return value to true.</p>
*
* @param chatText the text of the chat message being processed
* @param addToHistory flag indicating whether the message should be added to the chat history (not used for Lambda commands)
* @param cir callback used to override the normal sending behavior of the chat message
*/
@Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true)
void sendMessageInject(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cir) {
if (!CommandManager.INSTANCE.isLambdaCommand(chatText)) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,35 @@

@Mixin(DrawContext.class)
public abstract class DrawContextMixin {
@Shadow protected abstract void drawTooltip(TextRenderer textRenderer, List<TooltipComponent> components, int x, int y, TooltipPositioner positioner);
/**
* Renders a tooltip using the provided text renderer, tooltip components, and positioning strategy.
*
* <p>This shadowed method is implemented by the target class to draw tooltips at the specified coordinates.
* The tooltip components determine the content displayed, while the positioner defines how the tooltip is positioned on the screen.</p>
*
* @param textRenderer the renderer used for drawing the tooltip's text
* @param components the list of components forming the tooltip's content
* @param x the x-coordinate for the tooltip's starting position
* @param y the y-coordinate for the tooltip's starting position
* @param positioner the strategy that determines the tooltip's placement
*/
@Shadow protected abstract void drawTooltip(TextRenderer textRenderer, List<TooltipComponent> components, int x, int y, TooltipPositioner positioner);

/**
* Overrides the default tooltip rendering to include additional components.
*
* <p>This injected method converts a list of text components into tooltip components. If optional tooltip data
* is present, it is inserted into the list, and if the currently focused item is a filled map, a map preview
* component is added. It then calls the shadowed tooltip drawing method with the modified components and cancels
* further execution of the original method.</p>
*
* @param textRenderer the renderer used to draw text
* @param text the list of text elements to be converted into tooltip components
* @param data an optional tooltip data element to be incorporated into the tooltip
* @param x the x-coordinate for tooltip positioning
* @param y the y-coordinate for tooltip positioning
* @param ci the callback information used to cancel the original tooltip rendering
*/
@Inject(method = "drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V", at = @At("HEAD"), cancellable = true)
void drawItemTooltip(TextRenderer textRenderer, List<Text> text, Optional<TooltipData> data, int x, int y, CallbackInfo ci) {
List<TooltipComponent> list = text.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ public LogoTextureMixin(Identifier location) {
super(location);
}

/**
* Redirects the default texture loading to supply a custom banner texture.
*
* <p>This method intercepts the call to load texture data from the default resource pack and returns
* an InputSupplier that provides an InputStream for the custom texture located at "textures/lambda_banner.png".
* The provided resource pack, resource type, and identifier are ignored.</p>
*
* @return an InputSupplier that supplies the custom banner texture's input stream
*/
@Redirect(method = "loadTextureData", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/DefaultResourcePack;open(Lnet/minecraft/resource/ResourceType;Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/InputSupplier;"))
InputSupplier<InputStream> loadTextureData(DefaultResourcePack instance, ResourceType type, Identifier id) {
return () -> LambdaResourceKt.getStream("textures/lambda_banner.png");
Expand Down
23 changes: 23 additions & 0 deletions common/src/main/kotlin/com/lambda/graphics/RenderMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ object RenderMain {
var screenSize = Vec2d.ZERO
var scaleFactor = 1.0

/**
* Renders the 2D interface elements.
*
* This function resets the transformation matrices with a translation along the z-axis to position the 2D
* layer appropriately, then sets up the OpenGL state and posts rendering events for fixed GUI elements,
* the HUD, and scaled elements using the default and current GUI scaling factors.
*/
@JvmStatic
fun render2D() {
resetMatrices(Matrix4f().translate(0f, 0f, -3000f))
Expand All @@ -50,6 +57,12 @@ object RenderMain {
}
}

/**
* Prepares the 3D rendering context by resetting matrices with the given transformation,
* updating the projection matrix, and posting a world render event.
*
* @param matrix the transformation matrix used to reset the rendering matrices.
*/
@JvmStatic
fun render3D(matrix: Matrix4f) {
resetMatrices(matrix)
Expand All @@ -60,6 +73,16 @@ object RenderMain {
}
}

/**
* Recalculates the screen dimensions and updates the orthographic projection matrix based on a scaling factor.
*
* This function retrieves the current framebuffer dimensions, computes the scaled width and height
* by dividing them by the provided factor, and then updates the global screen size and scale factor.
* It sets the projection matrix to an orthographic projection using the calculated dimensions with a
* near plane of 1000f and a far plane of 21000f.
*
* @param factor the scale factor used to adjust the framebuffer dimensions.
*/
private fun rescale(factor: Double) {
val width = mc.window.framebufferWidth.toFloat()
val height = mc.window.framebufferHeight.toFloat()
Expand Down
38 changes: 35 additions & 3 deletions common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,43 @@ class Animation(initialValue: Double, val update: (Double) -> Double) {
private var prevValue = initialValue
private var currValue = initialValue

operator fun getValue(thisRef: Any?, property: KProperty<*>) = value()
operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Double) = setValue(valueIn)
/**
* Retrieves the current interpolated animation value.
*
* This operator function enables property delegation by returning the animation's
* current value as computed by the [value] method, which interpolates between the previous
* and current states.
*/
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value()
/**
* Delegated setter that updates the animation's value.
*
* This operator function is triggered when a delegated property is assigned a new value. It sets both the previous
* and current values of the animation to the provided [valueIn].
*
* @param thisRef The object owning the delegated property (unused).
* @param property Metadata for the property being assigned (unused).
* @param valueIn The new value to set for the animation.
*/
operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Double) = setValue(valueIn)

fun value(): Double = lerp(mc.partialTicks, prevValue, currValue)
/**
* Computes and returns the current interpolated animation value.
*
* This function uses linear interpolation between the previous and current animation values,
* leveraging the partial tick value from the rendering context (mc.partialTicks) to ensure smooth transitions.
*
* @return the interpolated animation value.
*/
fun value(): Double = lerp(mc.partialTicks, prevValue, currValue)

/**
* Resets the animation state by updating both the previous and current values.
*
* This ensures that the animation starts from a consistent value without any interpolation.
*
* @param valueIn The new value to set as both the previous and current animation state.
*/
fun setValue(valueIn: Double) {
prevValue = valueIn
currValue = valueIn
Expand Down
Loading