From d4d5a84bc40e276fea788c2bbdb47ce0871ede4b Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Fri, 13 Mar 2026 20:53:00 +0100 Subject: [PATCH] Fixed pistol and knife sprites for retail. Added firing mechanism. Signed-off-by: Hans Kokx --- .../difficulty/difficulty_screen.dart | 2 +- lib/features/player/player.dart | 5 +- lib/features/player/weapon.dart | 7 +-- lib/features/player/weapons/knife.dart | 3 +- lib/features/player/weapons/pistol.dart | 4 +- lib/features/renderer/renderer.dart | 53 ++++++++++++++++--- lib/features/renderer/weapon_painter.dart | 29 ++++++++++ 7 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 lib/features/renderer/weapon_painter.dart diff --git a/lib/features/difficulty/difficulty_screen.dart b/lib/features/difficulty/difficulty_screen.dart index 572de2a..0f36b34 100644 --- a/lib/features/difficulty/difficulty_screen.dart +++ b/lib/features/difficulty/difficulty_screen.dart @@ -16,7 +16,7 @@ class DifficultyScreen extends StatelessWidget { MaterialPageRoute( builder: (_) => const WolfRenderer( difficulty: Difficulty.bringEmOn, - // isDemo: true, + isDemo: false, showSpriteGallery: true, ), ), diff --git a/lib/features/player/player.dart b/lib/features/player/player.dart index 890dac1..12de74d 100644 --- a/lib/features/player/player.dart +++ b/lib/features/player/player.dart @@ -120,14 +120,11 @@ class Player { } void fire(int currentTime) { + // Only spend ammo if the weapon isn't a knife bool shotFired = currentWeapon.fire(currentTime, currentAmmo: ammo); - - // If it was a gun (not a knife) and it fired, consume ammo if (shotFired && currentWeapon is! Knife) { ammo--; } - - // TODO: We'll add Raycast hit detection here next! } void updateWeapon(int currentTime) { diff --git a/lib/features/player/weapon.dart b/lib/features/player/weapon.dart index 61f0e7b..2db4665 100644 --- a/lib/features/player/weapon.dart +++ b/lib/features/player/weapon.dart @@ -16,16 +16,13 @@ abstract class Weapon { required this.idleSprite, required this.fireFrames, required this.damage, - this.msPerFrame = 100, // Speed of animation + this.msPerFrame = 100, }); int get currentSprite => state == WeaponState.idle ? idleSprite : fireFrames[frameIndex]; - bool get isFiring => state == WeaponState.firing; - - /// The main entry point for the player to use the gun. - /// Returns true if a bullet was actually spent. + /// Core firing logic. Returns true if a bullet was spent. bool fire(int currentTime, {required int currentAmmo}) { if (state == WeaponState.idle && currentAmmo > 0) { state = WeaponState.firing; diff --git a/lib/features/player/weapons/knife.dart b/lib/features/player/weapons/knife.dart index bb85f76..5255947 100644 --- a/lib/features/player/weapons/knife.dart +++ b/lib/features/player/weapons/knife.dart @@ -5,14 +5,13 @@ class Knife extends Weapon { : super( name: "Knife", idleSprite: 416, - fireFrames: [417, 418, 419], + fireFrames: [417, 418, 419, 420], damage: 15, msPerFrame: 120, ); @override bool fire(int currentTime, {required int currentAmmo}) { - // Knife doesn't need ammo! if (state == WeaponState.idle) { state = WeaponState.firing; frameIndex = 0; diff --git a/lib/features/player/weapons/pistol.dart b/lib/features/player/weapons/pistol.dart index 03ab6bc..1a910cd 100644 --- a/lib/features/player/weapons/pistol.dart +++ b/lib/features/player/weapons/pistol.dart @@ -4,8 +4,8 @@ class Pistol extends Weapon { Pistol() : super( name: "Pistol", - idleSprite: 408, - fireFrames: [409, 410, 411, 412], + idleSprite: 421, + fireFrames: [422, 423, 424, 425], damage: 20, ); } diff --git a/lib/features/renderer/renderer.dart b/lib/features/renderer/renderer.dart index 4453181..118b5af 100644 --- a/lib/features/renderer/renderer.dart +++ b/lib/features/renderer/renderer.dart @@ -13,6 +13,7 @@ import 'package:wolf_dart/features/entities/entity_registry.dart'; import 'package:wolf_dart/features/map/wolf_map.dart'; import 'package:wolf_dart/features/player/player.dart'; import 'package:wolf_dart/features/renderer/raycast_painter.dart'; +import 'package:wolf_dart/features/renderer/weapon_painter.dart'; import 'package:wolf_dart/features/ui/hud.dart'; import 'package:wolf_dart/sprite_gallery.dart'; @@ -55,6 +56,9 @@ class _WolfRendererState extends State Map doorOffsets = {}; Map doorStates = {}; // 1 = opening, 2 = fully open + double moveStepX = 0; + double moveStepY = 0; + @override void initState() { super.initState(); @@ -191,6 +195,10 @@ class _WolfRendererState extends State const double moveSpeed = 0.12; const double turnSpeed = 0.08; + // Reset steps each tick before calculation + moveStepX = 0; + moveStepY = 0; + // 1. ANIMATE DOORS doorStates.forEach((key, state) { if (state == 1) { @@ -203,8 +211,6 @@ class _WolfRendererState extends State } }); - double moveStepX = 0; - double moveStepY = 0; final pressedKeys = HardwareKeyboard.instance.logicalKeysPressed; if (pressedKeys.contains(LogicalKeyboardKey.keyW)) { @@ -300,12 +306,12 @@ class _WolfRendererState extends State }); } - // 5. Fire weapon - bool isCtrlPressed = pressedKeys.contains(LogicalKeyboardKey.controlLeft); - if (isCtrlPressed) { + // 5. Weapon + player.currentWeapon.update(elapsed.inMilliseconds); + + if (pressedKeys.contains(LogicalKeyboardKey.controlLeft)) { player.fire(elapsed.inMilliseconds); } - player.updateWeapon(elapsed.inMilliseconds); // Fade out the damage flash smoothly if (damageFlashOpacity > 0) { @@ -357,6 +363,40 @@ class _WolfRendererState extends State sprites: gameMap.sprites, ), ), + // Weapon Viewmodel + Positioned( + bottom: -20, + left: 0, + right: 0, + child: Center( + child: Transform.translate( + offset: Offset( + 0, + // Bobbing math: only moves if velocity is > 0 + (moveStepX.abs() + moveStepY.abs()) > 0 + ? math.sin( + DateTime.now() + .millisecondsSinceEpoch / + 100, + ) * + 12 + : 0, + ), + child: SizedBox( + width: 500, + height: 500, + child: CustomPaint( + painter: WeaponPainter( + sprite: + gameMap.sprites[player + .currentWeapon + .currentSprite], + ), + ), + ), + ), + ), + ), if (damageFlashOpacity > 0) Positioned.fill( child: Container( @@ -370,6 +410,7 @@ class _WolfRendererState extends State }, ), ), + // HUD Hud(player: player), ], diff --git a/lib/features/renderer/weapon_painter.dart b/lib/features/renderer/weapon_painter.dart new file mode 100644 index 0000000..bbe2c7a --- /dev/null +++ b/lib/features/renderer/weapon_painter.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:wolf_dart/features/renderer/color_palette.dart'; + +class WeaponPainter extends CustomPainter { + final List> sprite; + WeaponPainter({required this.sprite}); + + @override + void paint(Canvas canvas, Size size) { + double pixelSize = size.width / 64; + final paint = Paint(); + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + int colorByte = sprite[x][y]; + if (colorByte != 255) { + // Transparency check + paint.color = ColorPalette.vga[colorByte]; + canvas.drawRect( + Rect.fromLTWH(x * pixelSize, y * pixelSize, pixelSize, pixelSize), + paint, + ); + } + } + } + } + + @override + bool shouldRepaint(covariant WeaponPainter oldDelegate) => true; +}