diff --git a/packages/wolf_3d_engine/lib/src/rasterizer.dart b/packages/wolf_3d_engine/lib/src/rasterizer.dart index ea018f2..290b777 100644 --- a/packages/wolf_3d_engine/lib/src/rasterizer.dart +++ b/packages/wolf_3d_engine/lib/src/rasterizer.dart @@ -10,18 +10,18 @@ class SoftwareRasterizer { final int floorColor = ColorPalette.vga32Bit[29]; void render(WolfEngine engine, FrameBuffer buffer) { - // 1. Wipe the screen clean with ceiling and floor colors _clearScreen(buffer); - // 2. We need a Z-Buffer (1D array mapping to screen width) so sprites - // know if they are hiding behind a wall slice. List zBuffer = List.filled(buffer.width, 0.0); - // 3. Do the math and draw the walls, filling the Z-Buffer as we go _castWalls(engine, buffer, zBuffer); - - // 4. Draw the entities/sprites _castSprites(engine, buffer, zBuffer); + + // NEW: Draw the weapon on top of the 3D world + _drawWeapon(engine, buffer); + + // NEW: Apply the full-screen damage tint last + _drawDamageFlash(engine, buffer); } void _clearScreen(FrameBuffer buffer) { @@ -355,4 +355,69 @@ class SoftwareRasterizer { } } } + + void _drawWeapon(WolfEngine engine, FrameBuffer buffer) { + int spriteIndex = engine.player.currentWeapon.getCurrentSpriteIndex( + engine.data.sprites.length, + ); + Sprite weaponSprite = engine.data.sprites[spriteIndex]; + + // Dropped the scale from 4 to 2 (a 50% reduction in size) + const int scale = 2; + const int weaponWidth = 64 * scale; + const int weaponHeight = 64 * scale; + + int startX = (buffer.width ~/ 2) - (weaponWidth ~/ 2); + + // Kept the grounding to the bottom of the screen + int startY = + buffer.height - weaponHeight + (engine.player.weaponAnimOffset ~/ 2); + + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + int colorByte = weaponSprite.pixels[x * 64 + y]; + + if (colorByte != 255) { + int color32 = ColorPalette.vga32Bit[colorByte]; + + for (int sx = 0; sx < scale; sx++) { + for (int sy = 0; sy < scale; sy++) { + int drawX = startX + (x * scale) + sx; + int drawY = startY + (y * scale) + sy; + + if (drawX >= 0 && + drawX < buffer.width && + drawY >= 0 && + drawY < buffer.height) { + buffer.pixels[drawY * buffer.width + drawX] = color32; + } + } + } + } + } + } + } + + void _drawDamageFlash(WolfEngine engine, FrameBuffer buffer) { + if (engine.damageFlashOpacity <= 0) return; + + int alpha = (engine.damageFlashOpacity * 256).toInt().clamp(0, 256); + int invAlpha = 256 - alpha; + + for (int i = 0; i < buffer.pixels.length; i++) { + int color = buffer.pixels[i]; + + int r = color & 0xFF; + int g = (color >> 8) & 0xFF; + int b = (color >> 16) & 0xFF; + int a = (color >> 24) & 0xFF; + + // Blend with Red + r = ((r * invAlpha) + (255 * alpha)) >> 8; + g = (g * invAlpha) >> 8; + b = (b * invAlpha) >> 8; + + buffer.pixels[i] = (a << 24) | (b << 16) | (g << 8) | r; + } + } } diff --git a/packages/wolf_3d_renderer/lib/wolf_3d_renderer.dart b/packages/wolf_3d_renderer/lib/wolf_3d_renderer.dart index c4fc8b1..416863e 100644 --- a/packages/wolf_3d_renderer/lib/wolf_3d_renderer.dart +++ b/packages/wolf_3d_renderer/lib/wolf_3d_renderer.dart @@ -6,7 +6,6 @@ import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_engine/wolf_3d_engine.dart'; import 'package:wolf_3d_input/wolf_3d_input.dart'; import 'package:wolf_3d_renderer/hud.dart'; -import 'package:wolf_3d_renderer/weapon_painter.dart'; class WolfRenderer extends StatefulWidget { const WolfRenderer( @@ -125,57 +124,9 @@ class _WolfRendererState extends State return Center( child: AspectRatio( aspectRatio: 16 / 10, - child: Stack( - children: [ - // --- 3D WORLD (PIXEL BUFFER) --- - CustomPaint( - size: Size( - constraints.maxWidth, - constraints.maxHeight, - ), - painter: BufferPainter(_renderedFrame), - ), - - // --- FIRST PERSON WEAPON --- - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Center( - child: Transform.translate( - offset: Offset( - 0, - engine.player.weaponAnimOffset, - ), - child: SizedBox( - width: 500, - height: 500, - child: CustomPaint( - painter: WeaponPainter( - sprite: - widget.data.sprites[engine - .player - .currentWeapon - .getCurrentSpriteIndex( - widget.data.sprites.length, - )], - ), - ), - ), - ), - ), - ), - - // --- DAMAGE FLASH --- - if (engine.damageFlashOpacity > 0) - Positioned.fill( - child: Container( - color: Colors.red.withValues( - alpha: engine.damageFlashOpacity, - ), - ), - ), - ], + child: CustomPaint( + size: Size(constraints.maxWidth, constraints.maxHeight), + painter: BufferPainter(_renderedFrame), ), ), ); @@ -190,8 +141,6 @@ class _WolfRendererState extends State } } -// --- DEAD SIMPLE PAINTER --- -// It literally just stretches the 320x200 image to fill the screen class BufferPainter extends CustomPainter { final ui.Image? frame; @@ -201,7 +150,6 @@ class BufferPainter extends CustomPainter { void paint(Canvas canvas, Size size) { if (frame == null) return; - // FilterQuality.none guarantees the classic, chunky, un-blurred pixels! final Paint paint = Paint()..filterQuality = FilterQuality.none; final Rect srcRect = Rect.fromLTWH(