public SCLogic(FF1Rom _rom, SCMain _main, List <IRewardSource> _itemPlacement, IVictoryConditionFlags _victoryConditions, bool _excludeBridge) { rom = _rom; main = _main; itemPlacement = _itemPlacement; victoryConditions = _victoryConditions; excludeBridge = _excludeBridge; locations = new OwLocationData(rom); locations.LoadData(); GetShipDockArea(); BuildTunnels(); DoPathing(); DoAirShipPathing(); RemoveEmptyAreas(); ProcessTreasures(); ProcessShopSlot(); ProcessNPCs(); RewardSources = rewardSourceDic.Values.ToList(); ProcessFreebies(); }
private void BuildInitialRequirements(IVictoryConditionFlags victoryConditions) { airShipLocationAccessible = false; requirements = AccessRequirement.None; if ((bool)victoryConditions.FreeLute) { requirements |= AccessRequirement.Lute; } changes = MapChange.None; if (victoryConditions.IsBridgeFree ?? false) { changes |= MapChange.Bridge; } if (victoryConditions.IsShipFree ?? false) { changes |= MapChange.Ship; SetShipDock(255); } if (victoryConditions.IsAirshipFree ?? false) { changes |= MapChange.Airship; airShipLocationAccessible = true; } if (victoryConditions.IsCanalFree ?? false) { changes |= MapChange.Canal; } if (victoryConditions.IsCanoeFree ?? false) { changes |= MapChange.Canoe; } }
public static bool CheckSanity(List <IRewardSource> treasurePlacements, Dictionary <MapLocation, List <MapChange> > mapLocationRequirements, IVictoryConditionFlags victoryConditions) { const int maxIterations = 20; var currentIteration = 0; var currentAccess = AccessRequirement.None; if (victoryConditions.ShardHunt) { currentAccess |= AccessRequirement.Lute; } var currentMapChanges = MapChange.None; Func <IEnumerable <MapLocation> > currentMapLocations = () => mapLocationRequirements .Where(x => x.Value .Any(y => currentMapChanges.HasFlag(y))).Select(x => x.Key); Func <IEnumerable <IRewardSource> > currentItemLocations = () => treasurePlacements .Where(x => { var locations = currentMapLocations().ToList(); return(locations.Contains(x.MapLocation) && currentAccess.HasFlag(x.AccessRequirement) && (!(x is MapObject) || locations.Contains(((MapObject)x).SecondLocation))); }); var accessibleLocationCount = currentItemLocations().Count(); var requiredAccess = AccessRequirement.All; var requiredMapChanges = new List <MapChange> { MapChange.All }; if (victoryConditions.OnlyRequireGameIsBeatable) { var winTheGameAccess = ItemLocations.ChaosReward.AccessRequirement; var winTheGameLocation = ItemLocations.ChaosReward.MapLocation; requiredAccess = winTheGameAccess; requiredMapChanges = mapLocationRequirements[winTheGameLocation]; } while (!currentAccess.HasFlag(requiredAccess) || !requiredMapChanges.Any(x => currentMapChanges.HasFlag(x))) { if (currentIteration > maxIterations) { throw new InvalidOperationException($"Sanity Check hit max iterations: {currentIteration}"); } currentIteration++; var currentItems = currentItemLocations().Select(x => x.Item).ToList(); if (!currentAccess.HasFlag(AccessRequirement.Key) && currentItems.Contains(Item.Key)) { currentAccess |= AccessRequirement.Key; } if (!currentMapChanges.HasFlag(MapChange.Bridge) && currentItems.Contains(Item.Bridge) && currentMapLocations().Contains(MapLocation.BridgeLocation)) { currentMapChanges |= MapChange.Bridge; } if (!currentAccess.HasFlag(AccessRequirement.Crown) && currentItems.Contains(Item.Crown)) { currentAccess |= AccessRequirement.Crown; } if (!currentAccess.HasFlag(AccessRequirement.Crystal) && currentItems.Contains(Item.Crystal)) { currentAccess |= AccessRequirement.Crystal; } if (!currentAccess.HasFlag(AccessRequirement.Herb) && currentItems.Contains(Item.Herb)) { currentAccess |= AccessRequirement.Herb; } if (!currentMapChanges.HasFlag(MapChange.Canoe) && currentItems.Contains(Item.Canoe)) { currentMapChanges |= MapChange.Canoe; } if (!currentMapChanges.HasFlag(MapChange.Ship) && currentItems.Contains(Item.Ship)) { currentMapChanges |= MapChange.Ship; } if (!currentAccess.HasFlag(AccessRequirement.Tnt) && currentItems.Contains(Item.Tnt)) { currentAccess |= AccessRequirement.Tnt; } if (!currentAccess.HasFlag(AccessRequirement.Adamant) && currentItems.Contains(Item.Adamant)) { currentAccess |= AccessRequirement.Adamant; } if (!currentMapChanges.HasFlag(MapChange.Canal) && currentItems.Contains(Item.Canal) && currentMapChanges.HasFlag(MapChange.Ship)) { currentMapChanges |= MapChange.Canal; } if (!currentMapChanges.HasFlag(MapChange.TitanFed) && currentItems.Contains(Item.Ruby) && currentMapLocations().Contains(MapLocation.TitansTunnelEast)) { currentMapChanges |= MapChange.TitanFed; } if (!currentAccess.HasFlag(AccessRequirement.Rod) && currentItems.Contains(Item.Rod)) { currentAccess |= AccessRequirement.Rod; } if (!currentAccess.HasFlag(AccessRequirement.Slab) && currentItems.Contains(Item.Slab)) { currentAccess |= AccessRequirement.Slab; } if (!currentMapChanges.HasFlag(MapChange.Airship) && (currentItems.Contains(Item.Floater)) && // || currentItems.Contains(Item.Airship)) && currentMapLocations().Contains(MapLocation.AirshipLocation)) { currentMapChanges |= MapChange.Airship; } if (!currentAccess.HasFlag(AccessRequirement.Bottle) && currentItems.Contains(Item.Bottle)) { currentAccess |= AccessRequirement.Bottle; } if (!currentAccess.HasFlag(AccessRequirement.Oxyale) && currentItems.Contains(Item.Oxyale)) { currentAccess |= AccessRequirement.Oxyale; } if (!currentMapChanges.HasFlag(MapChange.Chime) && currentItems.Contains(Item.Chime)) { currentMapChanges |= MapChange.Chime; } if (!currentAccess.HasFlag(AccessRequirement.Cube) && currentItems.Contains(Item.Cube)) { currentAccess |= AccessRequirement.Cube; } if (!currentAccess.HasFlag(AccessRequirement.EarthOrb) && currentItems.Contains(Item.EarthOrb)) { currentAccess |= AccessRequirement.EarthOrb; } if (!currentAccess.HasFlag(AccessRequirement.FireOrb) && currentItems.Contains(Item.FireOrb)) { currentAccess |= AccessRequirement.FireOrb; } if (!currentAccess.HasFlag(AccessRequirement.WaterOrb) && currentItems.Contains(Item.WaterOrb)) { currentAccess |= AccessRequirement.WaterOrb; } if (!currentAccess.HasFlag(AccessRequirement.AirOrb) && currentItems.Contains(Item.AirOrb)) { currentAccess |= AccessRequirement.AirOrb; } if (!currentAccess.HasFlag(AccessRequirement.Lute) && currentItems.Contains(Item.Lute)) { currentAccess |= AccessRequirement.Lute; } var newCount = currentItemLocations().Count(); if (newCount <= accessibleLocationCount) { return(false); } accessibleLocationCount = newCount; } return(true); }
public (bool complete, IEnumerable <IRewardSource> rewardSources, AccessRequirement requirements, MapChange changes) Crawl(IVictoryConditionFlags victoryConditions) { Stopwatch w = Stopwatch.StartNew(); immediateAreas = new HashSet <short>(256); processedAreas = new HashSet <short>(2048); processedDungeons = new HashSet <OverworldTeleportIndex>(32); deferredAreas = new HashSet <SCDeferredAreaQueueEntry>(64, new SCDeferredAreaQueueEntryEqualityComparer()); deferredPointOfInterests = new Queue <SCPointOfInterest>(); rewardSources = new HashSet <IRewardSource>(256, new RewardSourceEqualityComparer()); shipDockAreaIndex = -1; slabTranslated = false; herbCheckedIn = false; princessRescued = false; vampireAccessible = false; airShipLiftOff = false; BuildInitialRequirements(victoryConditions); SetAirShipPoi(); short areaIndex = Main.Overworld.Tiles[locations.StartingLocation.X, locations.StartingLocation.Y].Area; SCOwArea area = Main.Overworld.Areas[areaIndex]; CrawlOwArea(area); ProcessImmediateAreas(); while (ProcessDeferredAreas()) { ProcessImmediateAreas(); } //that for just means, that there's a fault in the function. Actually the mere existence of the call is a fault in the above functions for (int i = 0; i < 10; i++) { ProcessDeferredPointsOfInterest(); } w.Stop(); var requiredAccess = AccessRequirement.All; var requiredMapChanges = MapChange.All; if ((bool)victoryConditions.IsFloaterRemoved) { requiredMapChanges &= ~MapChange.Airship; } bool complete = changes.HasFlag(requiredMapChanges) && requirements.HasFlag(requiredAccess); return(complete, rewardSources, requirements, changes); }
public (bool Complete, List <MapLocation> MapLocations, AccessRequirement Requirements) CheckSanity(List <IRewardSource> _treasurePlacements, Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements, IVictoryConditionFlags victoryConditions) { treasurePlacements = _treasurePlacements; //kids, don't try this at home. Calculating an index from an address is usually not the way to go. chests = treasurePlacements.Select(r => r as TreasureChest).Where(r => r != null).ToDictionary(r => (byte)(r.Address - 0x3100)); npcs = treasurePlacements.Select(r => r as MapObject).Where(r => r != null).ToDictionary(r => r.ObjectId); shopslot = (ItemShopSlot)treasurePlacements.FirstOrDefault(r => r is ItemShopSlot); var result = Crawl(victoryConditions); var mapLocations = result.rewardSources.Select(r => r.MapLocation).Distinct().ToList(); return(result.complete, mapLocations, result.requirements); }
public (bool Complete, List <MapLocation> MapLocations, AccessRequirement Requirements) CheckSanity(List <IRewardSource> treasurePlacements, Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements, IVictoryConditionFlags victoryConditions) { const int maxIterations = 20; var currentIteration = 0; var currentAccess = AccessRequirement.None; if ((bool)victoryConditions.FreeLute) { currentAccess |= AccessRequirement.Lute; } var currentMapChanges = MapChange.None; if (victoryConditions.IsBridgeFree ?? false) { currentMapChanges |= MapChange.Bridge; } if (victoryConditions.IsShipFree ?? false) { currentMapChanges |= MapChange.Ship; } if (victoryConditions.IsAirshipFree ?? false) { currentMapChanges |= MapChange.Airship; } if (victoryConditions.IsCanalFree ?? false) { currentMapChanges |= MapChange.Canal; } if (victoryConditions.IsCanoeFree ?? false) { currentMapChanges |= MapChange.Canoe; } IEnumerable <MapLocation> currentMapLocations() { return(fullLocationRequirements.Where(x => x.Value.Item1.Any(y => currentMapChanges.HasFlag(y) && currentAccess.HasFlag(x.Value.Item2))).Select(x => x.Key)); } IEnumerable <IRewardSource> currentItemLocations() { var locations = currentMapLocations().ToList(); return(treasurePlacements.Where(x => { return locations.Contains(x.MapLocation) && currentAccess.HasFlag(x.AccessRequirement) && locations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation); })); } var requiredAccess = AccessRequirement.All; var requiredMapChanges = MapChange.All; if ((bool)victoryConditions.IsFloaterRemoved) { requiredMapChanges &= ~MapChange.Airship; } var accessibleLocationCount = 0; while (!currentAccess.HasFlag(requiredAccess) || !currentMapChanges.HasFlag(requiredMapChanges)) { if (currentIteration > maxIterations) { throw new InvalidOperationException($"Sanity Check hit max iterations: {currentIteration}"); } currentIteration++; var accessibleLocations = currentItemLocations().ToList(); if (accessibleLocations.Count <= accessibleLocationCount) { return(false, currentMapLocations().ToList(), currentAccess); } accessibleLocationCount = accessibleLocations.Count; var currentItems = accessibleLocations.Select(x => x.Item).ToList(); if (!currentAccess.HasFlag(AccessRequirement.Key) && currentItems.Contains(Item.Key)) { currentAccess |= AccessRequirement.Key; } if (!currentMapChanges.HasFlag(MapChange.Bridge) && currentItems.Contains(Item.Bridge)) { currentMapChanges |= MapChange.Bridge; } if (!currentAccess.HasFlag(AccessRequirement.Crown) && currentItems.Contains(Item.Crown)) { currentAccess |= AccessRequirement.Crown; } if (!currentAccess.HasFlag(AccessRequirement.Crystal) && currentItems.Contains(Item.Crystal)) { currentAccess |= AccessRequirement.Crystal; } if (!currentAccess.HasFlag(AccessRequirement.Herb) && currentItems.Contains(Item.Herb)) { currentAccess |= AccessRequirement.Herb; } if (!currentMapChanges.HasFlag(MapChange.Canoe) && currentItems.Contains(Item.Canoe)) { currentMapChanges |= MapChange.Canoe; } if (!currentMapChanges.HasFlag(MapChange.Ship) && currentItems.Contains(Item.Ship)) { currentMapChanges |= MapChange.Ship; } if (!currentAccess.HasFlag(AccessRequirement.Tnt) && currentItems.Contains(Item.Tnt)) { currentAccess |= AccessRequirement.Tnt; } if (!currentAccess.HasFlag(AccessRequirement.Adamant) && currentItems.Contains(Item.Adamant)) { currentAccess |= AccessRequirement.Adamant; } if (!currentMapChanges.HasFlag(MapChange.Canal) && currentItems.Contains(Item.Canal) && currentMapChanges.HasFlag(MapChange.Ship)) { currentMapChanges |= MapChange.Canal; } if (!currentMapChanges.HasFlag(MapChange.TitanFed) && currentItems.Contains(Item.Ruby) && (currentMapLocations().Contains(MapLocation.TitansTunnelEast) || currentMapLocations().Contains(MapLocation.TitansTunnelWest))) { currentMapChanges |= MapChange.TitanFed; currentAccess |= AccessRequirement.Ruby; } if (!currentAccess.HasFlag(AccessRequirement.Rod) && currentItems.Contains(Item.Rod)) { currentAccess |= AccessRequirement.Rod; } if (!currentAccess.HasFlag(AccessRequirement.Slab) && currentItems.Contains(Item.Slab)) { currentAccess |= AccessRequirement.Slab; } if (!currentMapChanges.HasFlag(MapChange.Airship) && (currentItems.Contains(Item.Floater)) && currentMapLocations().Contains(MapLocation.AirshipLocation)) { currentMapChanges |= MapChange.Airship; } if (!currentAccess.HasFlag(AccessRequirement.Bottle) && currentItems.Contains(Item.Bottle)) { currentAccess |= AccessRequirement.Bottle; } if (!currentAccess.HasFlag(AccessRequirement.Oxyale) && currentItems.Contains(Item.Oxyale)) { currentAccess |= AccessRequirement.Oxyale; } if (!currentMapChanges.HasFlag(MapChange.Chime) && currentItems.Contains(Item.Chime)) { currentMapChanges |= MapChange.Chime; } if (!currentAccess.HasFlag(AccessRequirement.Cube) && currentItems.Contains(Item.Cube)) { currentAccess |= AccessRequirement.Cube; } if (!currentAccess.HasFlag(AccessRequirement.EarthOrb) && currentItems.Contains(Item.EarthOrb)) { currentAccess |= AccessRequirement.EarthOrb; } if (!currentAccess.HasFlag(AccessRequirement.FireOrb) && currentItems.Contains(Item.FireOrb)) { currentAccess |= AccessRequirement.FireOrb; } if (!currentAccess.HasFlag(AccessRequirement.WaterOrb) && currentItems.Contains(Item.WaterOrb)) { currentAccess |= AccessRequirement.WaterOrb; } if (!currentAccess.HasFlag(AccessRequirement.AirOrb) && currentItems.Contains(Item.AirOrb)) { currentAccess |= AccessRequirement.AirOrb; } if (!currentAccess.HasFlag(AccessRequirement.Lute) && currentItems.Contains(Item.Lute)) { currentAccess |= AccessRequirement.Lute; } } return(true, currentMapLocations().ToList(), currentAccess); }