Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions lib/replay/replay_weapons.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import 'package:icarus/const/weapons.dart';

/// Icarus weapons by the equippable folder Valorant keeps each gun in, e.g.
/// `/Game/Equippables/Guns/Rifles/AK/...` is the Vandal. Checked against the
/// equippable paths replays carry; an unknown path shows no weapon.
/// `/Game/Equippables/Guns/Rifles/AK/...` is the Vandal. The folders are
/// valorant-api.com's asset paths, checked against the economy weapons and
/// kill sources of 13.0x replays; an unknown path shows no weapon. Matched
/// ignoring case: replays carry both `/SniperRifles/DMR/` and `/Dmr/`.
const List<(String, WeaponType)> _weaponsByPathPart = [
('/Sidearms/BasePistol/', WeaponType.classic),
('/Sidearms/Shotgun/', WeaponType.shorty),
('/Sidearms/AutomaticPistol/', WeaponType.frenzy),
('/Sidearms/Luger/', WeaponType.ghost),
('/Sidearms/Revolver/', WeaponType.sheriff),
('/Sidearms/Slim/', WeaponType.bandit),
('/SubMachineGuns/Vector/', WeaponType.stinger),
('/SubMachineGuns/MP5/', WeaponType.spectre),
('/Shotguns/PumpShotgun/', WeaponType.bucky),
('/Shotguns/AutomaticShotgun/', WeaponType.judge),
('/Rifles/Burst/', WeaponType.bulldog),
('/Rifles/DMR/', WeaponType.guardian),
('/Rifles/Carbine/', WeaponType.phantom),
('/Rifles/AK/', WeaponType.vandal),
('/Rifles/BattleRifle/', WeaponType.warden),
('/Sniper/LeverSniperRifle/', WeaponType.marshal),
('/Sniper/DoubleSniper/', WeaponType.outlaw),
('/Sniper/BoltSniper/', WeaponType.operator),
('/HvyMachineGuns/LMG/', WeaponType.ares),
('/HvyMachineGuns/HMG/', WeaponType.odin),
('/sidearms/basepistol/', WeaponType.classic),
('/sidearms/slim/', WeaponType.shorty),
('/sidearms/autopistol/', WeaponType.frenzy),
('/sidearms/luger/', WeaponType.ghost),
('/sidearms/revolver/', WeaponType.sheriff),
('/sidearms/compact/', WeaponType.bandit),
('/submachineguns/vector/', WeaponType.stinger),
('/submachineguns/mp5/', WeaponType.spectre),
('/shotguns/pumpshotgun/', WeaponType.bucky),
('/shotguns/autoshotgun/', WeaponType.judge),
('/rifles/burst/', WeaponType.bulldog),
('/rifles/carbine/', WeaponType.phantom),
('/rifles/ak/', WeaponType.vandal),
('/rifles/battlerifle/', WeaponType.warden),
('/sniperrifles/dmr/', WeaponType.guardian),
('/sniperrifles/leversniper/', WeaponType.marshal),
('/sniperrifles/doublesniper/', WeaponType.outlaw),
('/sniperrifles/boltsniper/', WeaponType.operator),
('/hvymachineguns/lmg/', WeaponType.ares),
('/hvymachineguns/hmg/', WeaponType.odin),
];

WeaponType replayWeaponType(String? equippablePath) {
if (equippablePath == null) return WeaponType.none;
final path = equippablePath.toLowerCase();
for (final (part, weapon) in _weaponsByPathPart) {
if (equippablePath.contains(part)) return weapon;
if (path.contains(part)) return weapon;
}
return WeaponType.none;
}
50 changes: 50 additions & 0 deletions test/replay_weapons_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:icarus/const/weapons.dart';
import 'package:icarus/replay/replay_weapons.dart';

void main() {
test('every gun path seen in replays is its weapon', () {
// Economy weapons and kill sources from three 13.0x replays, plus the
// Frenzy from valorant-api.com's asset paths (no replay had one).
const guns = {
'Sidearms/BasePistol/BasePistol.BasePistol_C': WeaponType.classic,
'Sidearms/Slim/SawedOffShotgun.SawedOffShotgun_C': WeaponType.shorty,
'Sidearms/AutoPistol/AutoPistol.AutoPistol_C': WeaponType.frenzy,
'Sidearms/Luger/LugerPistol.LugerPistol_C': WeaponType.ghost,
'Sidearms/Revolver/RevolverPistol.RevolverPistol_C': WeaponType.sheriff,
'Sidearms/Compact/CompactPistol.CompactPistol_C': WeaponType.bandit,
'SubMachineGuns/Vector/Vector.Vector_C': WeaponType.stinger,
'SubMachineGuns/MP5/SubMachineGun_MP5.SubMachineGun_MP5_C':
WeaponType.spectre,
'Shotguns/PumpShotgun/PumpShotgun.PumpShotgun_C': WeaponType.bucky,
'Shotguns/AutoShotgun/AutomaticShotgun.AutomaticShotgun_C':
WeaponType.judge,
'Rifles/Burst/AssaultRifle_Burst.AssaultRifle_Burst_C':
WeaponType.bulldog,
'SniperRifles/DMR/DMR.DMR_C': WeaponType.guardian,
'SniperRifles/Dmr/DMR.DMR_C': WeaponType.guardian,
'Rifles/Carbine/AssaultRifle_ACR.AssaultRifle_ACR_C': WeaponType.phantom,
'Rifles/AK/AssaultRifle_AK.AssaultRifle_AK_C': WeaponType.vandal,
'Rifles/BattleRifle/BattleRifle.BattleRifle_C': WeaponType.warden,
'SniperRifles/Leversniper/LeverSniperRifle.LeverSniperRifle_C':
WeaponType.marshal,
'SniperRifles/Doublesniper/DS_Gun.DS_Gun_C': WeaponType.outlaw,
'SniperRifles/Boltsniper/BoltSniper.BoltSniper_C': WeaponType.operator,
'HvyMachineGuns/LMG/LightMachineGun.LightMachineGun_C': WeaponType.ares,
'HvyMachineGuns/HMG/HeavyMachineGun.HeavyMachineGun_C': WeaponType.odin,
};
for (final MapEntry(key: path, value: weapon) in guns.entries) {
expect(replayWeaponType('/Game/Equippables/Guns/$path'), weapon,
reason: path);
}
});

test('an ability or unknown path is no weapon', () {
expect(
replayWeaponType('/Game/Characters/Deadeye/S0/Ability_Q/Gun/'
'Gun_Deadeye_Q_Pistol.Gun_Deadeye_Q_Pistol_C'),
WeaponType.none,
);
expect(replayWeaponType(null), WeaponType.none);
});
}
Loading