import 'dart:math' as math; import 'package:wolf_dart/classes/linear_coordinates.dart'; import 'package:wolf_dart/features/entities/collectible.dart'; import 'package:wolf_dart/features/weapon/weapon.dart'; import 'package:wolf_dart/features/weapon/weapons/knife.dart'; import 'package:wolf_dart/features/weapon/weapons/machine_gun.dart'; import 'package:wolf_dart/features/weapon/weapons/pistol.dart'; enum WeaponSwitchState { idle, lowering, raising } class Player { // Spatial double x; double y; double angle; // Stats int health = 100; int ammo = 8; int score = 0; // Inventory bool hasGoldKey = false; bool hasSilverKey = false; bool hasMachineGun = false; bool hasChainGun = false; // Weapon System late Weapon currentWeapon; int currentWeaponIndex = 1; // Starts with Pistol (Index 1) // Fixed indices: 0 = Knife, 1 = Pistol, 2 = Machine Gun, 3 = Chain Gun final List availableWeapons = [null, null, null, null]; WeaponSwitchState switchState = WeaponSwitchState.idle; int? pendingWeaponIndex; // 0.0 is resting, 500.0 is fully off-screen double weaponAnimOffset = 0.0; // How fast the weapon drops/raises per tick final double switchSpeed = 30.0; Player({ required this.x, required this.y, required this.angle, }) { // Start with Knife and Pistol availableWeapons[0] = Knife(); availableWeapons[1] = Pistol(); currentWeapon = availableWeapons[1]!; } // Helper getter to interface with the RaycasterPainter LinearCoordinates get position => (x: x, y: y); // --- Weapon Switching & Animation Logic --- void updateWeaponSwitch() { if (switchState == WeaponSwitchState.lowering) { weaponAnimOffset += switchSpeed; if (weaponAnimOffset >= 500.0) { weaponAnimOffset = 500.0; currentWeaponIndex = pendingWeaponIndex!; currentWeapon = availableWeapons[currentWeaponIndex]!; switchState = WeaponSwitchState.raising; } } else if (switchState == WeaponSwitchState.raising) { weaponAnimOffset -= switchSpeed; if (weaponAnimOffset <= 0) { weaponAnimOffset = 0.0; switchState = WeaponSwitchState.idle; } } } void requestWeaponSwitch(int index) { // Prevent switching if animating, firing, picking the same gun, or if slot is empty if (switchState != WeaponSwitchState.idle) return; if (currentWeapon.state != WeaponState.idle) return; if (index == currentWeaponIndex) return; if (index < 0 || index >= availableWeapons.length) return; if (availableWeapons[index] == null) return; // Don't switch to a firearm if out of ammo if (index > 0 && ammo <= 0) return; pendingWeaponIndex = index; switchState = WeaponSwitchState.lowering; } // --- Health & Damage --- void takeDamage(int damage) { health = math.max(0, health - damage); if (health <= 0) { print("YOU DIED!"); } else { print("Ouch! ($health)"); } } void heal(int amount) { final int newHealth = math.min(100, health + amount); if (health < 100) { print("Feelin' better. ($newHealth)"); } health = newHealth; } void addAmmo(int amount) { final int newAmmo = math.min(99, ammo + amount); if (ammo < 99) { print("Hell yeah. ($newAmmo)"); } ammo = newAmmo; } // --- Interaction & Firing --- bool tryPickup(Collectible item) { bool pickedUp = false; switch (item.type) { case CollectibleType.health: if (health >= 100) return false; // Map IDs 47 (Dog Food) and 48 (Medkit) heal(item.mapId == 47 ? 4 : 25); pickedUp = true; break; case CollectibleType.ammo: if (ammo >= 99) return false; int previousAmmo = ammo; addAmmo(8); // Auto-switch back to Pistol if holding Knife and just got ammo if (currentWeaponIndex == 0 && previousAmmo <= 0) { requestWeaponSwitch(1); } pickedUp = true; break; case CollectibleType.treasure: if (item.mapId == 52) score += 100; if (item.mapId == 53) score += 500; if (item.mapId == 54) score += 1000; if (item.mapId == 55) score += 5000; if (item.mapId == 56) { // 1-Up heal(100); addAmmo(25); } pickedUp = true; break; case CollectibleType.weapon: if (item.mapId == 50) { if (!hasMachineGun) { hasMachineGun = true; availableWeapons[2] = MachineGun(); } requestWeaponSwitch(2); pickedUp = true; } // Assuming mapId 51 is Chain Gun for later if (item.mapId == 51) { if (!hasChainGun) { hasChainGun = true; // availableWeapons[3] = ChainGun(); // Uncomment when you add the class } requestWeaponSwitch(3); pickedUp = true; } break; case CollectibleType.key: if (item.mapId == 43) hasGoldKey = true; if (item.mapId == 44) hasSilverKey = true; pickedUp = true; break; } return pickedUp; } void fire(int currentTime) { if (switchState != WeaponSwitchState.idle) { return; // No shooting while switching } bool shotFired = currentWeapon.fire(currentTime, currentAmmo: ammo); if (shotFired && currentWeaponIndex > 0) { // If it's a gun ammo--; if (ammo <= 0) { // Auto-switch to knife when out of bullets requestWeaponSwitch(0); } } } /// Returns true only on the specific frame where the hit should be calculated bool updateWeapon(int currentTime) { int oldFrame = currentWeapon.frameIndex; currentWeapon.update(currentTime); if (currentWeapon.state == WeaponState.firing && oldFrame == 0 && currentWeapon.frameIndex == 1) { return true; } return false; } }