< Summary

Information
Class: RaidLoop.Core.RaidEngine
Assembly: RaidLoop.Core
File(s): /home/runner/work/RaidLoop/RaidLoop/src/RaidLoop.Core/RaidEngine.cs
Line coverage
82%
Covered lines: 129
Uncovered lines: 28
Coverable lines: 157
Total lines: 333
Line coverage: 82.1%
Branch coverage
73%
Covered branches: 59
Total branches: 80
Branch coverage: 73.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/RaidLoop/RaidLoop/src/RaidLoop.Core/RaidEngine.cs

#LineLine coverage
 1namespace RaidLoop.Core;
 2
 3public static class RaidEngine
 4{
 5    public static OpeningPhaseResult ResolveOpeningPhase(OpeningContactState contactState, int playerInitiative, int ene
 6    {
 37        return ResolveOpeningPhase(new OpeningPhaseContext(contactState, playerInitiative, enemyInitiative));
 8    }
 9
 10    public static OpeningPhaseResult ResolveOpeningPhase(OpeningPhaseContext context)
 11    {
 412        return context.ContactState switch
 413        {
 114            OpeningContactState.PlayerAmbush => new OpeningPhaseResult(
 115                context.ContactState,
 116                OpeningSide.Player,
 117                OpeningSide.None,
 118                OpeningActionsRemaining: 1,
 119                SurprisePersistenceEligible: false),
 120            OpeningContactState.EnemyAmbush => new OpeningPhaseResult(
 121                context.ContactState,
 122                OpeningSide.Enemy,
 123                OpeningSide.None,
 124                OpeningActionsRemaining: 1,
 125                SurprisePersistenceEligible: false),
 226            _ => new OpeningPhaseResult(
 227                context.ContactState,
 228                OpeningSide.None,
 229                context.PlayerInitiative >= context.EnemyInitiative ? OpeningSide.Player : OpeningSide.Enemy,
 230                OpeningActionsRemaining: 1,
 231                SurprisePersistenceEligible: false)
 432        };
 33    }
 34
 35    public static RaidState StartRaid(GameState game, List<Item> loadout, int backpackCapacity, int startingHealth)
 36    {
 1837        foreach (var item in loadout)
 38        {
 639            game.Stash.Remove(item);
 40        }
 41
 942        var equippedBackpack = loadout.FirstOrDefault(x => x.Type == ItemType.Backpack);
 343        var inventory = RaidInventory.FromItems([.. loadout], [], CombatBalance.GetBackpackCapacity(equippedBackpack));
 344        return new RaidState(startingHealth, inventory);
 45    }
 46
 47    public static void ApplyCombatDamage(RaidState state, int damage)
 48    {
 149        state.Health = Math.Max(0, state.Health - Math.Max(0, damage));
 150    }
 51
 52    public static bool TryAddLoot(RaidState state, Item item)
 53    {
 554        return TryAddCarriedItem(state, item);
 55    }
 56
 57    public static void FinalizeRaid(GameState game, RaidState raid, bool extracted)
 58    {
 259        if (!extracted || raid.IsDead)
 60        {
 161            return;
 62        }
 63
 164        game.Stash.AddRange(raid.Inventory.GetExtractableItems());
 165    }
 66
 67    public static void StartDiscoveredLootEncounter(RaidState state, IEnumerable<Item> items)
 68    {
 569        EncounterLoot.StartLootEncounter(state.Inventory.DiscoveredLoot, items);
 570    }
 71
 72    public static void AppendDiscoveredLoot(RaidState state, IEnumerable<Item> items)
 73    {
 074        EncounterLoot.AppendDiscoveredLoot(state.Inventory.DiscoveredLoot, items);
 075    }
 76
 77    public static void ClearDiscoveredLoot(RaidState state)
 78    {
 079        state.Inventory.DiscoveredLoot.Clear();
 080    }
 81
 82    public static bool TryLootFromDiscovered(RaidState state, Item item)
 83    {
 284        if (!state.Inventory.DiscoveredLoot.Remove(item))
 85        {
 086            return false;
 87        }
 88
 289        if (!TryAddCarriedItem(state, item))
 90        {
 191            state.Inventory.DiscoveredLoot.Add(item);
 192            return false;
 93        }
 94
 195        return true;
 96    }
 97
 98    public static bool TryDropCarriedToDiscovered(RaidState state, Item item)
 99    {
 0100        if (!state.Inventory.CarriedItems.Remove(item))
 101        {
 0102            return false;
 103        }
 104
 0105        state.Inventory.DiscoveredLoot.Add(item);
 0106        return true;
 107    }
 108
 109    public static bool TryDropEquippedToDiscovered(RaidState state, ItemType slotType)
 110    {
 1111        Item? item = slotType switch
 1112        {
 0113            ItemType.Weapon => state.Inventory.EquippedWeapon,
 0114            ItemType.Armor => state.Inventory.EquippedArmor,
 1115            ItemType.Backpack => state.Inventory.EquippedBackpack,
 0116            _ => null
 1117        };
 118
 1119        if (item is null)
 120        {
 0121            return false;
 122        }
 123
 124        switch (slotType)
 125        {
 126            case ItemType.Weapon:
 0127                state.Inventory.EquippedWeapon = null;
 0128                break;
 129            case ItemType.Armor:
 0130                state.Inventory.EquippedArmor = null;
 0131                break;
 132            case ItemType.Backpack:
 1133                state.Inventory.EquippedBackpack = null;
 1134                state.Inventory.BackpackCapacity = CombatBalance.GetBackpackCapacity((string?)null);
 135                // Requested behavior: dropping backpack drops everything carried.
 1136                state.Inventory.DiscoveredLoot.AddRange(state.Inventory.CarriedItems);
 1137                state.Inventory.CarriedItems.Clear();
 1138                break;
 139            default:
 0140                return false;
 141        }
 142
 1143        state.BackpackCapacity = state.Inventory.BackpackCapacity;
 1144        state.Inventory.DiscoveredLoot.Add(item);
 1145        return true;
 146    }
 147
 148    public static bool TryEquipFromDiscovered(RaidState state, Item item)
 149    {
 2150        if (!state.Inventory.DiscoveredLoot.Remove(item))
 151        {
 0152            return false;
 153        }
 154
 2155        if (!TryEquipItem(state, item))
 156        {
 1157            state.Inventory.DiscoveredLoot.Add(item);
 1158            return false;
 159        }
 160
 1161        return true;
 162    }
 163
 164    public static bool TryEquipFromCarried(RaidState state, Item item)
 165    {
 2166        if (!state.Inventory.CarriedItems.Remove(item))
 167        {
 0168            return false;
 169        }
 170
 2171        if (!TryEquipItem(state, item))
 172        {
 1173            state.Inventory.CarriedItems.Add(item);
 1174            return false;
 175        }
 176
 1177        return true;
 178    }
 179
 180    private static bool TryEquipItem(RaidState state, Item item)
 181    {
 4182        if (item.Type is not (ItemType.Weapon or ItemType.Armor or ItemType.Backpack))
 183        {
 0184            return false;
 185        }
 186
 4187        if (!CanEquipItemByWeight(state, item))
 188        {
 2189            return false;
 190        }
 191
 2192        Item? previous = item.Type switch
 2193        {
 1194            ItemType.Weapon => state.Inventory.EquippedWeapon,
 0195            ItemType.Armor => state.Inventory.EquippedArmor,
 1196            ItemType.Backpack => state.Inventory.EquippedBackpack,
 0197            _ => null
 2198        };
 199
 2200        switch (item.Type)
 201        {
 202            case ItemType.Weapon:
 1203                state.Inventory.EquippedWeapon = item;
 1204                break;
 205            case ItemType.Armor:
 0206                state.Inventory.EquippedArmor = item;
 0207                break;
 208            case ItemType.Backpack:
 1209                state.Inventory.EquippedBackpack = item;
 1210                state.Inventory.BackpackCapacity = CombatBalance.GetBackpackCapacity(item);
 211                break;
 212        }
 213
 2214        state.BackpackCapacity = state.Inventory.BackpackCapacity;
 2215        if (previous is not null)
 216        {
 2217            state.Inventory.DiscoveredLoot.Add(previous);
 218        }
 219
 2220        SpillOverflowToDiscovered(state);
 2221        return true;
 222    }
 223
 224    private static bool TryAddCarriedItem(RaidState state, Item item)
 225    {
 7226        if (CombatBalance.IsMedkit(item))
 227        {
 3228            if (!CanFitMedkitByWeight(state))
 229            {
 1230                return false;
 231            }
 232
 2233            state.Inventory.MedkitCount++;
 2234            return true;
 235        }
 236
 5237        var currentSlots = state.Inventory.CarriedItems.Sum(x => x.Slots);
 4238        if (currentSlots + item.Slots > state.Inventory.BackpackCapacity)
 239        {
 1240            return false;
 241        }
 242
 3243        if (!CanFitCarriedItemByWeight(state, item))
 244        {
 0245            return false;
 246        }
 247
 3248        state.Inventory.CarriedItems.Add(item);
 3249        return true;
 250    }
 251
 252    private static void SpillOverflowToDiscovered(RaidState state)
 253    {
 5254        var currentSlots = state.Inventory.CarriedItems.Sum(x => x.Slots);
 3255        while (currentSlots > state.Inventory.BackpackCapacity && state.Inventory.CarriedItems.Count > 0)
 256        {
 1257            var spill = state.Inventory.CarriedItems[^1];
 1258            state.Inventory.CarriedItems.RemoveAt(state.Inventory.CarriedItems.Count - 1);
 1259            state.Inventory.DiscoveredLoot.Add(spill);
 1260            currentSlots -= spill.Slots;
 261        }
 2262    }
 263
 264    private static bool CanFitMedkitByWeight(RaidState state)
 265    {
 3266        return GetEncumbranceWithAdditionalItems(state, [ItemCatalog.Create("Medkit")]) <= state.Inventory.MaxEncumbranc
 267    }
 268
 269    private static bool CanFitCarriedItemByWeight(RaidState state, Item item)
 270    {
 3271        return GetEncumbranceWithAdditionalItems(state, [item]) <= state.Inventory.MaxEncumbrance;
 272    }
 273
 274    private static bool CanEquipItemByWeight(RaidState state, Item item)
 275    {
 4276        var equippedItems = GetEquippedItems(state)
 7277            .Where(existing => existing.Type != item.Type)
 4278            .ToList();
 4279        equippedItems.Add(item);
 280
 4281        var carriedItems = state.Inventory.CarriedItems.ToList();
 4282        if (item.Type == ItemType.Backpack)
 283        {
 1284            var backpackCapacity = CombatBalance.GetBackpackCapacity(item);
 4285            var currentSlots = carriedItems.Sum(x => x.Slots);
 2286            while (currentSlots > backpackCapacity && carriedItems.Count > 0)
 287            {
 1288                var spill = carriedItems[^1];
 1289                carriedItems.RemoveAt(carriedItems.Count - 1);
 1290                currentSlots -= spill.Slots;
 291            }
 292        }
 293
 4294        var prospectiveItems = equippedItems.Concat(carriedItems).ToList();
 8295        for (var i = 0; i < state.Inventory.MedkitCount; i++)
 296        {
 0297            prospectiveItems.Add(ItemCatalog.Create("Medkit"));
 298        }
 299
 4300        return CombatBalance.GetTotalEncumbrance(prospectiveItems) <= state.Inventory.MaxEncumbrance;
 301    }
 302
 303    private static int GetEncumbranceWithAdditionalItems(RaidState state, IEnumerable<Item> additionalItems)
 304    {
 6305        var prospectiveItems = GetEquippedItems(state).ToList();
 6306        prospectiveItems.AddRange(state.Inventory.CarriedItems);
 12307        for (var i = 0; i < state.Inventory.MedkitCount; i++)
 308        {
 0309            prospectiveItems.Add(ItemCatalog.Create("Medkit"));
 310        }
 311
 6312        prospectiveItems.AddRange(additionalItems);
 6313        return CombatBalance.GetTotalEncumbrance(prospectiveItems);
 314    }
 315
 316    private static IEnumerable<Item> GetEquippedItems(RaidState state)
 317    {
 10318        if (state.Inventory.EquippedWeapon is not null)
 319        {
 7320            yield return state.Inventory.EquippedWeapon;
 321        }
 322
 10323        if (state.Inventory.EquippedArmor is not null)
 324        {
 1325            yield return state.Inventory.EquippedArmor;
 326        }
 327
 10328        if (state.Inventory.EquippedBackpack is not null)
 329        {
 4330            yield return state.Inventory.EquippedBackpack;
 331        }
 10332    }
 333}

Methods/Properties

ResolveOpeningPhase(RaidLoop.Core.OpeningContactState,System.Int32,System.Int32)
ResolveOpeningPhase(RaidLoop.Core.OpeningPhaseContext)
StartRaid(RaidLoop.Core.GameState,System.Collections.Generic.List`1<RaidLoop.Core.Item>,System.Int32,System.Int32)
ApplyCombatDamage(RaidLoop.Core.RaidState,System.Int32)
TryAddLoot(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
FinalizeRaid(RaidLoop.Core.GameState,RaidLoop.Core.RaidState,System.Boolean)
StartDiscoveredLootEncounter(RaidLoop.Core.RaidState,System.Collections.Generic.IEnumerable`1<RaidLoop.Core.Item>)
AppendDiscoveredLoot(RaidLoop.Core.RaidState,System.Collections.Generic.IEnumerable`1<RaidLoop.Core.Item>)
ClearDiscoveredLoot(RaidLoop.Core.RaidState)
TryLootFromDiscovered(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
TryDropCarriedToDiscovered(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
TryDropEquippedToDiscovered(RaidLoop.Core.RaidState,RaidLoop.Core.ItemType)
TryEquipFromDiscovered(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
TryEquipFromCarried(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
TryEquipItem(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
TryAddCarriedItem(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
SpillOverflowToDiscovered(RaidLoop.Core.RaidState)
CanFitMedkitByWeight(RaidLoop.Core.RaidState)
CanFitCarriedItemByWeight(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
CanEquipItemByWeight(RaidLoop.Core.RaidState,RaidLoop.Core.Item)
GetEncumbranceWithAdditionalItems(RaidLoop.Core.RaidState,System.Collections.Generic.IEnumerable`1<RaidLoop.Core.Item>)
GetEquippedItems()