Rewrite mod menu drawing to use GTA IV natives with Vec2

- Move menu to right side of screen (x=0.73) to avoid chat
- Use normalized coordinates (0-1) for screen positions
- Use natives.drawRect(Vec2 pos, Vec2 size, r, g, b, a)
- Use natives.displayText(Vec2) with addTextComponentString/drawText
- Separate RGBA color components instead of toColour integer
- Fix text rendering with proper native text function sequence
This commit is contained in:
Claude
2026-01-12 15:51:25 +00:00
parent 7bc52ef9d4
commit 3c68c3a4a4

View File

@@ -10,28 +10,24 @@ let currentMenu = "main";
let selectedIndex = 0; let selectedIndex = 0;
let menuStack = []; // For back navigation let menuStack = []; // For back navigation
// Menu colors (RGBA) // Menu colors (RGBA components)
const colors = { const colors = {
background: toColour(20, 20, 20, 200), bgR: 20, bgG: 20, bgB: 20, bgA: 200,
header: toColour(200, 50, 50, 255), headerR: 200, headerG: 50, headerB: 50, headerA: 255,
headerText: toColour(255, 255, 255, 255), itemR: 40, itemG: 40, itemB: 40, itemA: 200,
itemBg: toColour(40, 40, 40, 200), selectedR: 200, selectedG: 50, selectedB: 50, selectedA: 220,
itemBgSelected: toColour(200, 50, 50, 220), textR: 255, textG: 255, textB: 255, textA: 255,
itemText: toColour(255, 255, 255, 255), footerR: 30, footerG: 30, footerB: 30, footerA: 200
itemTextSelected: toColour(255, 255, 255, 255),
footer: toColour(30, 30, 30, 200),
footerText: toColour(180, 180, 180, 255),
subText: toColour(150, 150, 150, 255)
}; };
// Menu dimensions // Menu dimensions - positioned on RIGHT side to avoid chat
const menu = { const menu = {
x: 50, x: 0.73, // Normalized (0-1) - right side of screen
y: 100, y: 0.15, // Normalized (0-1) - top area
width: 300, width: 0.24,
headerHeight: 40, headerHeight: 0.045,
itemHeight: 35, itemHeight: 0.038,
footerHeight: 30, footerHeight: 0.035,
maxVisibleItems: 12 maxVisibleItems: 12
}; };
@@ -421,7 +417,6 @@ addEventHandler("OnKeyUp", function(event, key, scanCode, mods) {
selectedIndex = 0; selectedIndex = 0;
scrollOffset = 0; scrollOffset = 0;
menuStack = []; menuStack = [];
// Show cursor - use gui if available
if (typeof gui !== "undefined" && gui.showCursor) { if (typeof gui !== "undefined" && gui.showCursor) {
gui.showCursor(true, true); gui.showCursor(true, true);
} }
@@ -526,7 +521,6 @@ function getNetworkMenuItems() {
// ACTION HANDLING // ACTION HANDLING
// ============================================================================ // ============================================================================
// Selected player for network options
let selectedPlayer = null; let selectedPlayer = null;
function selectItem() { function selectItem() {
@@ -767,7 +761,7 @@ addNetworkHandler("ModMenu:Notification", function(message) {
}); });
// ============================================================================ // ============================================================================
// RENDERING - Using correct GTAC drawing API // RENDERING - Using GTA IV Native Drawing Functions with Vec2
// ============================================================================ // ============================================================================
addEventHandler("OnDrawnHUD", function(event) { addEventHandler("OnDrawnHUD", function(event) {
@@ -782,22 +776,28 @@ addEventHandler("OnDrawnHUD", function(event) {
let totalHeight = menu.headerHeight + (visibleCount * menu.itemHeight) + menu.footerHeight; let totalHeight = menu.headerHeight + (visibleCount * menu.itemHeight) + menu.footerHeight;
// Draw background // Draw background
drawRect(menu.x - 5, menu.y - 5, menu.width + 10, totalHeight + 10, colors.background); drawMenuRect(menu.x - 0.005, menu.y - 0.005, menu.width + 0.01, totalHeight + 0.01,
colors.bgR, colors.bgG, colors.bgB, colors.bgA);
// Draw header // Draw header
drawRect(menu.x, menu.y, menu.width, menu.headerHeight, colors.header); drawMenuRect(menu.x, menu.y, menu.width, menu.headerHeight,
drawText(title, menu.x + menu.width / 2, menu.y + 10, colors.headerText, true, 1.0); colors.headerR, colors.headerG, colors.headerB, colors.headerA);
drawMenuText(title, menu.x + menu.width / 2, menu.y + 0.012, 0.45, true);
// Draw items // Draw items
let yPos = menu.y + menu.headerHeight; let yPos = menu.y + menu.headerHeight;
for (let i = scrollOffset; i < scrollOffset + visibleCount && i < items.length; i++) { for (let i = scrollOffset; i < scrollOffset + visibleCount && i < items.length; i++) {
let item = items[i]; let item = items[i];
let isSelected = (i === selectedIndex); let isSelected = (i === selectedIndex);
let bgColor = isSelected ? colors.itemBgSelected : colors.itemBg;
let textColor = isSelected ? colors.itemTextSelected : colors.itemText;
// Draw item background // Draw item background
drawRect(menu.x, yPos, menu.width, menu.itemHeight, bgColor); if (isSelected) {
drawMenuRect(menu.x, yPos, menu.width, menu.itemHeight,
colors.selectedR, colors.selectedG, colors.selectedB, colors.selectedA);
} else {
drawMenuRect(menu.x, yPos, menu.width, menu.itemHeight,
colors.itemR, colors.itemG, colors.itemB, colors.itemA);
}
// Build label with state indicators // Build label with state indicators
let label = item.label; let label = item.label;
@@ -815,46 +815,55 @@ addEventHandler("OnDrawnHUD", function(event) {
label += " >>"; label += " >>";
} }
drawText(label, menu.x + 15, yPos + 8, textColor, false, 0.9); drawMenuText(label, menu.x + 0.01, yPos + 0.008, 0.35, false);
yPos += menu.itemHeight; yPos += menu.itemHeight;
} }
// Draw footer // Draw footer
drawRect(menu.x, yPos, menu.width, menu.footerHeight, colors.footer); drawMenuRect(menu.x, yPos, menu.width, menu.footerHeight,
drawText("UP/DOWN: Navigate | ENTER: Select | BACKSPACE: Back", menu.x + menu.width / 2, yPos + 8, colors.footerText, true, 0.6); colors.footerR, colors.footerG, colors.footerB, colors.footerA);
drawMenuText("UP/DOWN: Nav | ENTER: Select | BACKSPACE: Back",
menu.x + menu.width / 2, yPos + 0.008, 0.25, true);
// Draw scroll indicator // Draw scroll indicator
if (items.length > menu.maxVisibleItems) { if (items.length > menu.maxVisibleItems) {
let scrollText = (scrollOffset + 1) + "-" + Math.min(scrollOffset + visibleCount, items.length) + " / " + items.length; let scrollText = (scrollOffset + 1) + "-" + Math.min(scrollOffset + visibleCount, items.length) + "/" + items.length;
drawText(scrollText, menu.x + menu.width - 50, menu.y + 12, colors.subText, false, 0.7); drawMenuText(scrollText, menu.x + menu.width - 0.03, menu.y + 0.012, 0.3, false);
} }
}); });
// Helper drawing functions using GTAC graphics API // Drawing functions using GTA IV natives with Vec2
function drawRect(x, y, width, height, colour) { function drawMenuRect(x, y, w, h, r, g, b, a) {
// Use graphics.drawRectangle with Vec2 objects // natives.drawRect expects: Vec2 position (center), Vec2 size, r, g, b, a
// Signature: graphics.drawRectangle(surface, Vec2 position, Vec2 size, colour1, colour2, colour3, colour4, rotation) // Position is center-based, so adjust x and y
try { let centerX = x + w / 2;
let pos = new Vec2(x, y); let centerY = y + h / 2;
let size = new Vec2(width, height);
graphics.drawRectangle(null, pos, size, colour, colour, colour, colour, 0); let pos = new Vec2(centerX, centerY);
} catch(e) { let size = new Vec2(w, h);
// Silent fail
} natives.drawRect(pos, size, r, g, b, a);
} }
function drawText(text, x, y, colour, centered, scale) { function drawMenuText(text, x, y, scale, centered) {
// Use font.render with Vec2 position // Set up text properties
// Signature: font.render(text, Vec2 position, width, align, justify, size, colour, wordWrap, colourCodes, ignoreColourCodes, shadow) natives.setTextFont(0);
try { natives.setTextScale(scale, scale);
let pos = new Vec2(x, y); natives.setTextColour(255, 255, 255, 255);
let textScale = (scale || 1.0) * 14; // Convert scale to pixel size natives.setTextDropshadow(2, 0, 0, 0, 255);
let align = centered ? 0.5 : 0.0; // 0.5 = center, 0.0 = left
font.render(text, pos, 500, align, 0.0, textScale, colour, false, false, false, true); if (centered) {
} catch(e) { natives.setTextCentre(true);
// Silent fail } else {
natives.setTextCentre(false);
} }
// displayText expects Vec2 position
let pos = new Vec2(x, y);
natives.displayText(pos, "STRING");
natives.addTextComponentString(text);
natives.drawText();
} }
// ============================================================================ // ============================================================================
@@ -874,7 +883,7 @@ function showNotification(text) {
// Draw notifications // Draw notifications
addEventHandler("OnDrawnHUD", function(event) { addEventHandler("OnDrawnHUD", function(event) {
let now = Date.now(); let now = Date.now();
let yPos = 200; let yPos = 0.25;
for (let i = 0; i < notifications.length; i++) { for (let i = 0; i < notifications.length; i++) {
let notif = notifications[i]; let notif = notifications[i];
@@ -882,13 +891,23 @@ addEventHandler("OnDrawnHUD", function(event) {
if (elapsed < notif.duration) { if (elapsed < notif.duration) {
let alpha = elapsed < notif.duration - 500 ? 200 : Math.floor(200 * (notif.duration - elapsed) / 500); let alpha = elapsed < notif.duration - 500 ? 200 : Math.floor(200 * (notif.duration - elapsed) / 500);
let bgColor = toColour(20, 20, 20, alpha);
let textColor = toColour(255, 255, 100, alpha + 55);
drawRect(10, yPos, 300, 30, bgColor); // Draw notification background
drawText(notif.text, 20, yPos + 6, textColor, false, 0.8); drawMenuRect(0.01, yPos, 0.25, 0.035, 20, 20, 20, alpha);
yPos += 35; // Draw notification text
natives.setTextFont(0);
natives.setTextScale(0.3, 0.3);
natives.setTextColour(255, 255, 100, Math.min(255, alpha + 55));
natives.setTextDropshadow(2, 0, 0, 0, 255);
natives.setTextCentre(false);
let pos = new Vec2(0.015, yPos + 0.008);
natives.displayText(pos, "STRING");
natives.addTextComponentString(notif.text);
natives.drawText();
yPos += 0.04;
} }
} }