Refactor collectible handling and scoring system

- Introduced `CollectiblePickupContext` and `CollectiblePickupEffect` to streamline the collectible pickup logic in the Player class.
- Updated the `tryPickup` method in the Player class to utilize the new effect system for health, ammo, score, keys, and weapons.
- Refactored collectible classes to implement the new `tryCollect` method, returning a `CollectiblePickupEffect`.
- Enhanced enemy classes to expose score values based on their type.
- Added unit tests for scoring ownership and shareware menu module functionality.
- Updated rendering logic in various renderer classes to use the new mapped picture retrieval system.
- Improved the `WolfClassicMenuArt` class to utilize a more structured approach for image retrieval based on registry keys.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 14:25:14 +01:00
parent 6e53da7095
commit 57dde0f31c
15 changed files with 584 additions and 223 deletions

View File

@@ -139,86 +139,58 @@ class Player {
///
/// Returns `null` when the item was not collected (for example: full health).
int? tryPickup(Collectible item) {
bool pickedUp = false;
int? pickupSfxId;
final effect = item.tryCollect(
CollectiblePickupContext(
health: health,
ammo: ammo,
hasGoldKey: hasGoldKey,
hasSilverKey: hasSilverKey,
hasMachineGun: hasMachineGun,
hasChainGun: hasChainGun,
currentWeaponType: currentWeapon.type,
),
);
if (effect == null) return null;
switch (item.type) {
case CollectibleType.health:
if (health >= 100) return null;
heal(item.mapId == MapObject.food ? 4 : 25);
pickupSfxId = item.mapId == MapObject.food
? WolfSound.healthSmall
: WolfSound.healthLarge;
pickedUp = true;
break;
case CollectibleType.ammo:
if (ammo >= 99) return null;
int previousAmmo = ammo;
addAmmo(8);
if (currentWeapon is Knife && previousAmmo <= 0) {
requestWeaponSwitch(WeaponType.pistol);
}
pickupSfxId = WolfSound.getAmmo;
pickedUp = true;
break;
case CollectibleType.treasure:
if (item.mapId == MapObject.cross) {
score += 100;
pickupSfxId = WolfSound.treasure1;
}
if (item.mapId == MapObject.chalice) {
score += 500;
pickupSfxId = WolfSound.treasure2;
}
if (item.mapId == MapObject.chest) {
score += 1000;
pickupSfxId = WolfSound.treasure3;
}
if (item.mapId == MapObject.crown) {
score += 5000;
pickupSfxId = WolfSound.treasure4;
}
if (item.mapId == MapObject.extraLife) {
heal(100);
addAmmo(25);
pickupSfxId = WolfSound.extraLife;
}
pickedUp = true;
break;
case CollectibleType.weapon:
if (item.mapId == MapObject.machineGun) {
if (weapons[WeaponType.machineGun] == null) {
weapons[WeaponType.machineGun] = MachineGun();
hasMachineGun = true;
}
addAmmo(8);
requestWeaponSwitch(WeaponType.machineGun);
pickupSfxId = WolfSound.getMachineGun;
pickedUp = true;
}
if (item.mapId == MapObject.chainGun) {
if (weapons[WeaponType.chainGun] == null) {
weapons[WeaponType.chainGun] = ChainGun();
hasChainGun = true;
}
addAmmo(8);
requestWeaponSwitch(WeaponType.chainGun);
pickupSfxId = WolfSound.getGatling;
pickedUp = true;
}
break;
case CollectibleType.key:
if (item.mapId == MapObject.goldKey) hasGoldKey = true;
if (item.mapId == MapObject.silverKey) hasSilverKey = true;
pickupSfxId = WolfSound.getAmmo;
pickedUp = true;
break;
if (effect.healthToRestore > 0) {
heal(effect.healthToRestore);
}
return pickedUp ? pickupSfxId : null;
if (effect.ammoToAdd > 0) {
addAmmo(effect.ammoToAdd);
}
if (effect.scoreToAdd > 0) {
score += effect.scoreToAdd;
}
if (effect.grantGoldKey) {
hasGoldKey = true;
}
if (effect.grantSilverKey) {
hasSilverKey = true;
}
if (effect.grantWeapon case final weaponType?) {
if (weapons[weaponType] == null) {
weapons[weaponType] = switch (weaponType) {
WeaponType.machineGun => MachineGun(),
WeaponType.chainGun => ChainGun(),
WeaponType.pistol => Pistol(),
WeaponType.knife => Knife(),
};
}
if (weaponType == WeaponType.machineGun) hasMachineGun = true;
if (weaponType == WeaponType.chainGun) hasChainGun = true;
}
if (effect.requestWeaponSwitch case final weaponType?) {
requestWeaponSwitch(weaponType);
}
return effect.pickupSfxId;
}
bool fire(int currentTime) {
@@ -259,26 +231,9 @@ class Player {
isWalkable: isWalkable,
currentTime: currentTime,
onEnemyKilled: (Enemy killedEnemy) {
// Dynamic scoring based on the enemy type!
int pointsToAdd = 0;
switch (killedEnemy.runtimeType.toString()) {
case 'BrownGuard':
pointsToAdd = 100;
break;
case 'Dog':
pointsToAdd = 200;
break;
// You can easily plug in future enemies here!
// case 'SSOfficer': pointsToAdd = 500; break;
default:
pointsToAdd = 100; // Fallback
}
score += pointsToAdd;
// Optional: Print to console so you can see it working
score += killedEnemy.scoreValue;
print(
"Killed ${killedEnemy.runtimeType}! +$pointsToAdd (Score: $score)",
"Killed ${killedEnemy.runtimeType}! +${killedEnemy.scoreValue} (Score: $score)",
);
},
);