|
|
|
|
@@ -1,5 +1,6 @@
|
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
|
|
import 'package:arcane_helper_utils/arcane_helper_utils.dart';
|
|
|
|
|
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
|
|
|
|
import 'package:wolf_3d_engine/wolf_3d_engine.dart';
|
|
|
|
|
|
|
|
|
|
@@ -222,53 +223,97 @@ class AsciiRasterizer extends Rasterizer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _drawSimpleHud(WolfEngine engine) {
|
|
|
|
|
// 1. Clear the HUD area so stray wall characters don't bleed in
|
|
|
|
|
for (int y = viewHeight; y < height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
_screen[y][x] = ColoredChar(' ', 0xFF000000);
|
|
|
|
|
// 1. Pull Retro Colors
|
|
|
|
|
final int vgaStatusBarBlue = ColorPalette.vga32Bit[153];
|
|
|
|
|
final int vgaPanelDark = ColorPalette.vga32Bit[0];
|
|
|
|
|
final int white = ColorPalette.vga32Bit[15];
|
|
|
|
|
final int yellow = ColorPalette.vga32Bit[11];
|
|
|
|
|
final int red = ColorPalette.vga32Bit[4];
|
|
|
|
|
|
|
|
|
|
// 2. Setup Centered Layout
|
|
|
|
|
// The total width of our standard HUD elements is roughly 120 chars
|
|
|
|
|
const int hudContentWidth = 120;
|
|
|
|
|
final int offsetX = ((width - hudContentWidth) ~/ 2).clamp(0, width);
|
|
|
|
|
|
|
|
|
|
// 3. Clear HUD Base
|
|
|
|
|
_fillRect(0, viewHeight, width, height - viewHeight, ' ', vgaStatusBarBlue);
|
|
|
|
|
_writeString(0, viewHeight, "═" * width, white);
|
|
|
|
|
|
|
|
|
|
// 4. Panel Drawing Helper
|
|
|
|
|
void drawBorderedPanel(int startX, int startY, int w, int h) {
|
|
|
|
|
_fillRect(startX, startY, w, h, ' ', vgaPanelDark);
|
|
|
|
|
// Horizontal lines
|
|
|
|
|
_writeString(startX, startY, "┌${"─" * (w - 2)}┐", white);
|
|
|
|
|
_writeString(startX, startY + h - 1, "└${"─" * (w - 2)}┘", white);
|
|
|
|
|
// Vertical sides
|
|
|
|
|
for (int i = 1; i < h - 1; i++) {
|
|
|
|
|
_writeString(startX, startY + i, "│", white);
|
|
|
|
|
_writeString(startX + w - 1, startY + i, "│", white);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Draw a decorative border to separate the 3D view from the HUD
|
|
|
|
|
int borderColor = 0xFF555555; // Dark Gray
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
_screen[viewHeight][x] = ColoredChar('=', borderColor);
|
|
|
|
|
}
|
|
|
|
|
// 5. Draw the Panels
|
|
|
|
|
// FLOOR
|
|
|
|
|
drawBorderedPanel(offsetX + 4, viewHeight + 2, 12, 5);
|
|
|
|
|
_writeString(offsetX + 7, viewHeight + 3, "FLOOR", white);
|
|
|
|
|
_writeString(
|
|
|
|
|
offsetX + 9,
|
|
|
|
|
viewHeight + 5,
|
|
|
|
|
engine.activeLevel.name.split(' ').last,
|
|
|
|
|
white,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 3. Define some clean terminal colors
|
|
|
|
|
int white = 0xFFFFFFFF;
|
|
|
|
|
int yellow = 0xFFFFFF55;
|
|
|
|
|
int red = 0xFFFF5555;
|
|
|
|
|
// SCORE
|
|
|
|
|
drawBorderedPanel(offsetX + 18, viewHeight + 2, 24, 5);
|
|
|
|
|
_writeString(offsetX + 27, viewHeight + 3, "SCORE", white);
|
|
|
|
|
_writeString(
|
|
|
|
|
offsetX + 27,
|
|
|
|
|
viewHeight + 5,
|
|
|
|
|
engine.player.score.toString().padLeft(6, '0'),
|
|
|
|
|
white,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Turn health text red if dying
|
|
|
|
|
int healthColor = engine.player.health > 25 ? white : red;
|
|
|
|
|
// LIVES
|
|
|
|
|
drawBorderedPanel(offsetX + 44, viewHeight + 2, 12, 5);
|
|
|
|
|
_writeString(offsetX + 47, viewHeight + 3, "LIVES", white);
|
|
|
|
|
_writeString(offsetX + 49, viewHeight + 5, "3", white);
|
|
|
|
|
|
|
|
|
|
// Format the strings nicely
|
|
|
|
|
String score = "SCORE: ${engine.player.score.toString().padLeft(6, '0')}";
|
|
|
|
|
String health = "HEALTH: ${engine.player.health}%";
|
|
|
|
|
String ammo = "AMMO: ${engine.player.ammo}";
|
|
|
|
|
|
|
|
|
|
int textY = viewHeight + 4; // Center it vertically in the HUD area
|
|
|
|
|
|
|
|
|
|
// 4. Print the stats evenly spaced across the bottom
|
|
|
|
|
_writeString(6, textY, "FLOOR 1", white);
|
|
|
|
|
_writeString(22, textY, score, white);
|
|
|
|
|
_writeString(44, textY, "LIVES: 3", white);
|
|
|
|
|
|
|
|
|
|
// 5. Reactive ASCII Face
|
|
|
|
|
String face = " :-) ";
|
|
|
|
|
// FACE (With Reactive BJ Logic)
|
|
|
|
|
drawBorderedPanel(offsetX + 58, viewHeight + 1, 14, 7);
|
|
|
|
|
String face = " :-)";
|
|
|
|
|
if (engine.player.health <= 0) {
|
|
|
|
|
face = " X-x ";
|
|
|
|
|
face = " X-x";
|
|
|
|
|
} else if (engine.player.damageFlash > 0.1) {
|
|
|
|
|
face = " :-O"; // Mouth open in pain!
|
|
|
|
|
} else if (engine.player.health <= 25) {
|
|
|
|
|
face = " :-( ";
|
|
|
|
|
face = " :-(";
|
|
|
|
|
} else if (engine.player.health <= 60) {
|
|
|
|
|
face = " :-| ";
|
|
|
|
|
face = " :-|";
|
|
|
|
|
}
|
|
|
|
|
_writeString(offsetX + 63, viewHeight + 4, face, yellow);
|
|
|
|
|
|
|
|
|
|
_writeString(62, textY, "[$face]", yellow);
|
|
|
|
|
// HEALTH
|
|
|
|
|
int healthColor = engine.player.health > 25 ? white : red;
|
|
|
|
|
drawBorderedPanel(offsetX + 74, viewHeight + 2, 16, 5);
|
|
|
|
|
_writeString(offsetX + 78, viewHeight + 3, "HEALTH", white);
|
|
|
|
|
_writeString(
|
|
|
|
|
offsetX + 79,
|
|
|
|
|
viewHeight + 5,
|
|
|
|
|
"${engine.player.health}%",
|
|
|
|
|
healthColor,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
_writeString(78, textY, health, healthColor);
|
|
|
|
|
_writeString(100, textY, ammo, white);
|
|
|
|
|
// AMMO
|
|
|
|
|
drawBorderedPanel(offsetX + 92, viewHeight + 2, 12, 5);
|
|
|
|
|
_writeString(offsetX + 95, viewHeight + 3, "AMMO", white);
|
|
|
|
|
_writeString(offsetX + 97, viewHeight + 5, "${engine.player.ammo}", white);
|
|
|
|
|
|
|
|
|
|
// WEAPON
|
|
|
|
|
drawBorderedPanel(offsetX + 106, viewHeight + 2, 14, 5);
|
|
|
|
|
String weapon = engine.player.currentWeapon.type.name.spacePascalCase!
|
|
|
|
|
.toUpperCase();
|
|
|
|
|
if (weapon.length > 12) weapon = weapon.substring(0, 12);
|
|
|
|
|
_writeString(offsetX + 107, viewHeight + 4, weapon, white);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _drawFullVgaHud(WolfEngine engine) {
|
|
|
|
|
@@ -355,6 +400,19 @@ class AsciiRasterizer extends Rasterizer {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Helper to fill a rectangular area with a specific char and background color
|
|
|
|
|
void _fillRect(int startX, int startY, int w, int h, String char, int color) {
|
|
|
|
|
for (int dy = 0; dy < h; dy++) {
|
|
|
|
|
for (int dx = 0; dx < w; dx++) {
|
|
|
|
|
int x = startX + dx;
|
|
|
|
|
int y = startY + dy;
|
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height) {
|
|
|
|
|
_screen[y][x] = ColoredChar(char, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
dynamic finalizeFrame() {
|
|
|
|
|
if (_engine.player.damageFlash > 0.0) {
|
|
|
|
|
|