Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ dependencies {
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))

runtimeOnlyNonPublishable(rfg.deobf("CoreTweaks:CoreTweaks:0.3.3.2"))
runtimeOnlyNonPublishable(rfg.deobf(files("libs/plugeditor-1.3.jar")))

// Hodgepodge
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
Expand Down
Binary file added libs/plugeditor-1.3.jar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.gtnewhorizons.angelica.client.font;

/**
* Thread-local flags controlling how Angelica's font renderer interprets formatting codes.
*/
public final class AngelicaFontRenderContext {

private static final ThreadLocal<Integer> RAW_TEXT_DEPTH = ThreadLocal.withInitial(() -> 0);

private AngelicaFontRenderContext() {
}

public static void pushRawTextRendering() {
RAW_TEXT_DEPTH.set(RAW_TEXT_DEPTH.get() + 1);
}

public static void popRawTextRendering() {
int depth = RAW_TEXT_DEPTH.get() - 1;
if (depth <= 0) {
RAW_TEXT_DEPTH.set(0);
} else {
RAW_TEXT_DEPTH.set(depth);
}
}

public static boolean isRawTextRendering() {
return RAW_TEXT_DEPTH.get() > 0;
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package com.gtnewhorizons.angelica.client.font;

/**
* Utility class for parsing RGB color codes in text.
* Supports multiple formats:
* - Traditional: §0-9a-f (handled elsewhere)
* - Ampersand: &RRGGBB (6 hex digits)
* - Tag style: <RRGGBB>text</RRGGBB>
*/
public class ColorCodeUtils {

/**
* Check if a character is a valid hexadecimal digit (0-9, a-f, A-F)
*/
public static boolean isValidHexChar(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

/**
* Check if a string contains exactly 6 valid hexadecimal characters
*/
public static boolean isValidHexString(String hex) {
if (hex == null || hex.length() != 6) {
return false;
}
for (int i = 0; i < 6; i++) {
if (!isValidHexChar(hex.charAt(i))) {
return false;
}
}
return true;
}

/**
* Check if a character represents a traditional Minecraft formatting code (0-9, a-f, k-o, r)
* Also includes custom codes: g (rainbow), h (dinnerbone)
*/
public static boolean isFormattingCode(char c) {
char lower = Character.toLowerCase(c);
return (lower >= '0' && lower <= '9')
|| (lower >= 'a' && lower <= 'f')
|| lower == 'g' // rainbow
|| lower == 'h' // dinnerbone
|| (lower >= 'k' && lower <= 'o')
|| lower == 'r';
}

/**
* Check if 6 characters starting at position are valid hex
*/
public static boolean isValidHexString(CharSequence str, int start) {
if (str == null || start < 0 || start + 6 > str.length()) {
return false;
}
for (int i = 0; i < 6; i++) {
if (!isValidHexChar(str.charAt(start + i))) {
return false;
}
}
return true;
}

/**
* Parse a 6-digit hexadecimal string to an RGB integer (0xRRGGBB)
*
* @param hex String containing exactly 6 hex digits
* @return RGB value as integer, or -1 if invalid
*/
public static int parseHexColor(String hex) {
if (!isValidHexString(hex)) {
return -1;
}
try {
return Integer.parseInt(hex, 16) & 0x00FFFFFF;
} catch (NumberFormatException e) {
return -1;
}
}

/**
* Parse 6 hex characters from a CharSequence starting at position
*
* @param str The string to parse
* @param start Starting position
* @return RGB value as integer, or -1 if invalid
*/
public static int parseHexColor(CharSequence str, int start) {
if (!isValidHexString(str, start)) {
return -1;
}
try {
String hex = str.subSequence(start, start + 6).toString();
return Integer.parseInt(hex, 16) & 0x00FFFFFF;
} catch (NumberFormatException | IndexOutOfBoundsException e) {
return -1;
}
}

/**
* Detect the length of a color code starting at position, or 0 if none.
*
* @param str The string to check
* @param pos Position to check
* @return Length of color code:
* - 7 for &RRGGBB format (& + 6 hex)
* - 8 for <RRGGBB> format (< + 6 hex + >)
* - 9 for </RRGGBB> format (</ + 6 hex + >)
* - 2 for §X format (handled elsewhere, but counted here)
* - 0 for no color code
*/
public static int detectColorCodeLength(CharSequence str, int pos) {
return detectColorCodeLengthInternal(str, pos, AngelicaFontRenderContext.isRawTextRendering());
}

public static int detectColorCodeLengthIgnoringRaw(CharSequence str, int pos) {
return detectColorCodeLengthInternal(str, pos, false);
}

private static int detectColorCodeLengthInternal(CharSequence str, int pos, boolean skipDueToRaw) {
if (str == null || pos < 0 || pos >= str.length())
return 0;

if (skipDueToRaw)
return 0;

final int len = str.length();
char c = str.charAt(pos);

// §x (traditional)
if (c == 167 && pos + 1 < len) {
return 2;
}

// &RRGGBB
if (c == '&' && pos + 7 <= len) {
if (isValidHexString(str, pos + 1)) {
return 7; // & + 6 hex
}
}

// &x (alias for traditional formatting)
if (c == '&' && pos + 1 < len && isFormattingCode(str.charAt(pos + 1))) {
return 2;
}

// </RRGGBB> -> 9 chars total
// indices: pos:'<', pos+1:'/', pos+2..pos+7: 6 hex, pos+8:'>'
if (c == '<' && pos + 9 <= len && pos + 8 < len
&& str.charAt(pos + 1) == '/' && str.charAt(pos + 8) == '>'
&& isValidHexString(str, pos + 2)) {
return 9;
}

// <RRGGBB> -> 8 chars total
// indices: pos:'<', pos+1..pos+6: 6 hex, pos+7:'>'
if (c == '<' && pos + 8 <= len && pos + 7 < len
&& str.charAt(pos + 7) == '>' && isValidHexString(str, pos + 1)) {
return 8;
}

return 0;
}

/**
* Calculate the shadow color for a given RGB color.
* Shadow is typically darker (divided by 4 per component).
*
* @param rgb The base RGB color (0xRRGGBB)
* @return Shadow RGB color (0xRRGGBB)
*/
public static int calculateShadowColor(int rgb) {
return (rgb & 0xFCFCFC) >> 2;
}

/**
* Convert HSV (Hue, Saturation, Value) color to RGB.
*
* @param hue Hue in degrees (0-360)
* @param saturation Saturation (0.0-1.0)
* @param value Value/Brightness (0.0-1.0)
* @return RGB color as integer (0xRRGGBB)
*/
public static int hsvToRgb(float hue, float saturation, float value) {
// Normalize hue to 0-360 range
hue = hue % 360.0f;
if (hue < 0) hue += 360.0f;

// If saturation is 0, it's grayscale
if (saturation == 0) {
int gray = (int) (value * 255);
return (gray << 16) | (gray << 8) | gray;
}

// Calculate which sector (0-5) of the color wheel we're in
float h = hue / 60.0f;
int sector = (int) Math.floor(h);
float fractionalSector = h - sector;

float p = value * (1.0f - saturation);
float q = value * (1.0f - saturation * fractionalSector);
float t = value * (1.0f - saturation * (1.0f - fractionalSector));

float r, g, b;
switch (sector) {
case 0:
r = value;
g = t;
b = p;
break;
case 1:
r = q;
g = value;
b = p;
break;
case 2:
r = p;
g = value;
b = t;
break;
case 3:
r = p;
g = q;
b = value;
break;
case 4:
r = t;
g = p;
b = value;
break;
default:
r = value;
g = p;
b = q;
break; // sector 5
}

int red = (int) (r * 255);
int green = (int) (g * 255);
int blue = (int) (b * 255);

return (red << 16) | (green << 8) | blue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ public interface FontProvider {
* For use with §k. Should fetch a character of the same width as provided.
*/
char getRandomReplacement(char chr);

boolean isGlyphAvailable(char chr);

float getUStart(char chr);

float getVStart(char chr);

float getXAdvance(char chr);

float getGlyphW(char chr);

float getUSize(char chr);

float getVSize(char chr);

float getShadowOffset();

ResourceLocation getTexture(char chr);
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,19 @@ private FontProviderCustom(byte id) {
}
font = availableFonts[fontPos].deriveFont(currentFontQuality);
}

private static class InstLoader {
static final FontProviderCustom instance0 = new FontProviderCustom((byte)0);
static final FontProviderCustom instance1 = new FontProviderCustom((byte)1);
static final FontProviderCustom instance0 = new FontProviderCustom((byte) 0);
static final FontProviderCustom instance1 = new FontProviderCustom((byte) 1);
}

public static FontProviderCustom getPrimary() {
return InstLoader.instance0;
}

public static FontProviderCustom getFallback() {
return InstLoader.instance1;
}
public static FontProviderCustom getPrimary() { return InstLoader.instance0; }
public static FontProviderCustom getFallback() { return InstLoader.instance1; }

public void reloadFont(int fontID) {
currentFontQuality = FontConfig.customFontQuality;
Expand Down Expand Up @@ -150,7 +157,7 @@ void construct(Font font) {
int width = 0;
int actualChars = 0;
for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
final char ch = (char) (i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
Expand All @@ -171,7 +178,7 @@ void construct(Font font) {
maxRowWidth = Math.max(maxRowWidth, width);
width = 0;
}
final char ch = (char)(i + ATLAS_SIZE * this.id);
final char ch = (char) (i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
Expand Down Expand Up @@ -201,8 +208,10 @@ void construct(Font font) {
int imgX = (int) separator; // position in pixels

for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (!font.canDisplay(ch)) { continue; }
final char ch = (char) (i + ATLAS_SIZE * this.id);
if (!font.canDisplay(ch)) {
continue;
}

if (tileX >= atlasTilesX) {
tileX = 0;
Expand Down Expand Up @@ -249,7 +258,9 @@ private FontAtlas getAtlas(char chr) {

@Override
public boolean isGlyphAvailable(char chr) {
if (font == null) { return false; }
if (font == null) {
return false;
}
return (getAtlas(chr).glyphData[chr % ATLAS_SIZE] != null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ public final class FontProviderMC implements FontProvider {
@SuppressWarnings("UnnecessaryUnicodeEscape")
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";

private FontProviderMC() {}
private FontProviderMC() {
}

private static class InstLoader {
static final FontProviderMC instance = new FontProviderMC();
static final FontProviderMC instanceSGA = new FontProviderMC();
}

public static FontProviderMC get(boolean isSGA) {
return (isSGA ? InstLoader.instanceSGA : InstLoader.instance);
}
Expand Down Expand Up @@ -45,7 +48,7 @@ public boolean isGlyphAvailable(char chr) {
return MCFONT_ASCII_MAP.containsKey(chr);
}

public char getRandomReplacement(char chr){
public char getRandomReplacement(char chr) {
int lutIndex = lookupMcFontPosition(chr);
if (lutIndex != -1) {
int randomReplacementIndex;
Expand Down
Loading
Loading