public int Validate(ValidateErrorDelegate dgt) { int cError = 0; // Validate switch count int cSwitchesMax = 16; if (m_swm.Items.Count > cSwitchesMax) { dgt(this, ValidateError.Error, 0, 0, null, String.Format("Max switches is {0}, this mission has {1}", cSwitchesMax, m_swm.Items.Count)); cError++; } // Validate area count int cAreas = 0; foreach (IMapItem mi in m_alsmi) { if (mi is Area) cAreas++; } int cAreasMax = 32; if (cAreas > cAreasMax) { dgt(this, ValidateError.Error, 0, 0, null, String.Format("Max areas is {0}, this mission has {1}", cAreasMax, cAreas)); cError++; } // Validate SideInfo int cHuman = 0; foreach (SideInfo sidi in m_alsidi) { if (sidi.Intelligence == Intelligence.Human) cHuman++; #if false if (sidi.InitialCredits == 0) dgt(this, ValidateError.Warning, 0, 0, sidi, String.Format("Side {0} starts with no credits", sidi.Side)); #endif } #if false // Don't allow multi-player yet if (MaxPlayers != 1) { dgt(this, ValidateError.Error, 0, 0, null, "Multi-player not supported yet: MaxPlayers should be 1"); cError++; } // Must have 1 human side if (cHuman != 1) { dgt(this, ValidateError.Error, 0, 0, null, "Must have 1 human side!"); cError++; } #endif if (cHuman > MaxPlayers) { dgt(this, ValidateError.Error, 0, 0, null, String.Format("MaxPlayers is {0}, # of human sides is {1}", MaxPlayers, cHuman)); cError++; } // Validate gob inside/outside of boundaries foreach (IMapItem mi in m_alsmi) { if (mi is Galaxite) continue; if (mi is Wall) continue; if (mi is Tile) continue; // Gobs can be inside or outside of boundaries. If gobs are intersected by the boundaries // that is not ok and will send the game into fits Rectangle rcT = new Rectangle(Bounds.Location, Bounds.Size); if (!Bounds.Contains(new Rectangle((int)mi.tx, (int)mi.ty, mi.ctx, mi.cty))) { if (mi is Area) { Area area = (Area)mi; dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, area, String.Format("Area '{0}' out of bounds", area.Name)); cError++; } else { // If we have a gob that is "partially" outside, and gets compiled into the game, // errors will occur at runtime, so make this an error dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, mi, mi.ToString() + " out of bounds"); cError++; } } } // Collect placement information. // enum ItemMask { None = 0, Galaxite = 1, Wall = 2, Unreachable = 4, Scenery = 8, Structure = 16, MobileUnit = 32 }; // Initialize with all "unreachable areas" appropriately TerrainTypes[,] aterMap = GetTerrainMap(m_tmpd.TileSize, m_tmpd, false); ItemMask[,] aimMap = new ItemMask[Bounds.Height, Bounds.Width]; for (int ty = 0; ty < Bounds.Height; ty++) { for (int tx = 0; tx < Bounds.Width; tx++) { switch (aterMap[ty, tx]) { case TerrainTypes.Blocked: case TerrainTypes.Wall: aimMap[ty, tx] |= ItemMask.Unreachable; break; } } } // Validate placement foreach (IMapItem mi in m_alsmi) { ItemMask im = ItemMask.None; ItemMask imInvalid = ItemMask.None; if (mi is Galaxite) { im = ItemMask.Galaxite; imInvalid = ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure; } else if (mi is Wall) { im = ItemMask.Wall; imInvalid = ItemMask.Galaxite | ItemMask.Wall | ItemMask.Structure | ItemMask.MobileUnit; } else if (mi is Scenery) { im = ItemMask.Scenery; imInvalid = ItemMask.None; } else if (mi is Structure) { im = ItemMask.Structure; imInvalid = ItemMask.Galaxite | ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure | ItemMask.MobileUnit; } else if (mi is MobileUnit) { im = ItemMask.MobileUnit; imInvalid = ItemMask.Wall | ItemMask.Unreachable | ItemMask.Structure | ItemMask.MobileUnit; } if (im == ItemMask.None) continue; // Check each tile occupied by this mi ItemMask imError = ItemMask.None; for (int ty = (int)mi.ty; ty < (int)mi.ty + mi.cty; ty++) { for (int tx = (int)mi.tx; tx < (int)mi.tx + mi.ctx; tx++) { int txT = tx - Bounds.Left; int tyT = ty - Bounds.Top; if (txT < 0 || tyT < 0) continue; if (txT >= Bounds.Width || tyT >= Bounds.Height) continue; ItemMask imInvalidOverlap = aimMap[tyT, txT] & imInvalid; aimMap[tyT, txT] |= im; ItemMask imErrorNew = (ItemMask)(imInvalidOverlap & ~imError); if (imErrorNew != ItemMask.None) { imError |= imErrorNew; // Build up the error string ItemMask[] aimValues = (ItemMask[])Enum.GetValues(typeof(ItemMask)); string strT = ""; for (int n = 0; n < aimValues.Length; n++) { if ((aimValues[n] & imErrorNew) != 0) strT += aimValues[n].ToString() + ","; } if (strT != "") strT = strT.Substring(0, strT.Length - 1); dgt(this, ValidateError.Error, (int)mi.tx, (int)mi.ty, mi, mi.ToString() + " is on top of: " + strT); cError++; } } } } // Validate that structures don't make terrain inaccessible // Everywhere there is a blocked section in aterMapStructs that isn't // blocked in aterMap and isn't a structure is now inaccessible because // of a structure block TerrainTypes[,] aterMapStructs = GetTerrainMap(m_tmpd.TileSize, m_tmpd, true); for (int ty = 0; ty < aterMap.GetLength(0); ty++) { for (int tx = 0; tx < aterMap.GetLength(1); tx++) { // Check bool fSrcOpen = (aimMap[ty, tx] & (ItemMask.Unreachable | ItemMask.Structure)) == 0; if (fSrcOpen && aterMapStructs[ty, tx] == TerrainTypes.Blocked) { dgt(this, ValidateError.Error, tx + Bounds.Left, ty + Bounds.Top, null, String.Format("Terrain at {0},{1} is inaccessible due to structure blockage", tx + Bounds.Left, ty + Bounds.Top)); cError++; } } } // Validate areas in triggers exist StringCollection strc = CaTypeArea.GetAreaNames(); foreach (Side side in Enum.GetValues(typeof(Side))) { Trigger[] atgr = m_tgrm.GetTriggerList(side); foreach (Trigger tgr in atgr) { foreach (CaBase cab in tgr.Conditions) { foreach (CaType cat in cab.GetTypes()) { if (cat is CaTypeArea) { CaTypeArea catArea = (CaTypeArea)cat; if (strc.IndexOf(catArea.Area) < 0) { dgt(this, ValidateError.Error, 0, 0, null, "Area " + catArea.Area + " in trigger " + tgr.ToString() + " doesn't exist!"); cError++; } } } } foreach (CaBase cab in tgr.Actions) { foreach (CaType cat in cab.GetTypes()) { if (cat is CaTypeArea) { CaTypeArea catArea = (CaTypeArea)cat; if (strc.IndexOf(catArea.Area) < 0) { dgt(this, ValidateError.Error, 0, 0, null, "Area " + catArea.Area + " in trigger " + tgr.ToString() + " doesn't exist!"); cError++; } } } } } } // Validate unit groups in triggers foreach (Side side in Enum.GetValues(typeof(Side))) { Trigger[] atgr = m_tgrm.GetTriggerList(side); foreach (Trigger tgr in atgr) { foreach (CaBase cab in tgr.Conditions) { foreach (CaType cat in cab.GetTypes()) { if (cat is CaTypeUnitGroup) { CaTypeUnitGroup catUg = (CaTypeUnitGroup)cat; if (Array.IndexOf(atgr, tgr) < 0) { dgt(this, ValidateError.Error, 0, 0, null, "Orphaned UnitGroup in trigger " + tgr.ToString()); cError++; } } } } foreach (CaBase cab in tgr.Actions) { foreach (CaType cat in cab.GetTypes()) { if (cat is CaTypeArea) { CaTypeArea catArea = (CaTypeArea)cat; if (strc.IndexOf(catArea.Area) < 0) { dgt(this, ValidateError.Error, 0, 0, null, "Orphaned UnitGroup in trigger " + tgr.ToString()); cError++; } } } } } } // Validate triggers per side limit int cTriggersPerSideMax = 128; foreach (Side side in Enum.GetValues(typeof(Side))) { Trigger[] atgr = m_tgrm.GetTriggerList(side); if (atgr == null) continue; if (atgr.Length > cTriggersPerSideMax) { string strT = String.Format("Triggers per side {0}. Side {1} has {2} triggers", cTriggersPerSideMax, side.ToString(), atgr.Length); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } } // Validate gob limits int[] acStructures = new int[Enum.GetNames(typeof(Side)).Length]; int[] acMunts = new int[Enum.GetNames(typeof(Side)).Length]; int cScenery = 0; foreach (IMapItem mi in m_alsmi) { if (mi is Unit) { Unit unit = (Unit)mi; if (unit is Structure) { acStructures[(int)unit.Side]++; } else { acMunts[(int)unit.Side]++; } continue; } if (mi is Scenery) { cScenery++; continue; } } // Scenery Limit int cSceneryMax = 100; if (cScenery > cSceneryMax) { string strT = String.Format("{0} scenery; {1} allowed", cScenery, cSceneryMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } // Unit counts if (MaxPlayers == 1) { // Single player - asymmetric: one human count, shared computer counts // #define kcStructGobsHumanMin 39 // #define kcStructGobsComputerMin 52 // #define kcMuntGobsHumanMin 60 // #define kcMuntGobsComputerMin 80 int cStructsHumanMax = 39; int cMuntsHumanMax = 60; int cStructsComputerMax = 52; int cMuntsComputerMax = 80; int cStructsHuman = 0; int cMuntsHuman = 0; int cStructsComputer = 0; int cMuntsComputer = 0; foreach (SideInfo sidi in m_alsidi) { if (sidi.Intelligence == Intelligence.Human) { cStructsHuman += acStructures[(int)sidi.Side]; cMuntsHuman += acMunts[(int)sidi.Side]; } else { cStructsComputer += acStructures[(int)sidi.Side]; cMuntsComputer += acMunts[(int)sidi.Side]; } } // HACK ALERT: Sometimes levels have more human structures than the human limit. In this // case, take some from the computer side if possible. Hack: Reserve 5 structs for computer building int cStructuresAvailable = cStructsComputerMax - cStructsComputer - 5; if (cStructuresAvailable < 0) cStructuresAvailable = 0; cStructsHumanMax += cStructuresAvailable; cStructsComputerMax -= cStructuresAvailable; // Check if (cStructsHuman > cStructsHumanMax) { string strT = String.Format("Human Side has {0} structures; {1} allowed", cStructsHuman, cStructsHumanMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } if (cMuntsHuman > cMuntsHumanMax) { string strT = String.Format("Human Side has {0} mobile units; {1} allowed", cMuntsHuman, cMuntsHumanMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } if (cStructsComputer > cStructsComputerMax) { string strT = String.Format("Computer Side has {0} structures; {1} allowed", cStructsComputer, cStructsComputerMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } if (cMuntsComputer > cMuntsComputerMax) { string strT = String.Format("Computer Side has {0} mobile units; {1} allowed", cMuntsComputer, cMuntsComputerMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } } else { // Multi-player - symmetric: same counts for each side // #define kcStructGobsMax 55 // #define kcMuntGobsMax 88 int cStructsMax = 55; int cMuntsMax = 88; foreach (Side side in Enum.GetValues(typeof(Side))) { if (acStructures[(int)side] > cStructsMax) { string strT = String.Format("Side {0} has {1} structures; {2} allowed", side.ToString(), acStructures[(int)side], cStructsMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } if (acMunts[(int)side] > cMuntsMax) { string strT = String.Format("Side {0} has {1} mobile units; {2} allowed", side.ToString(), acMunts[(int)side], cMuntsMax); dgt(this, ValidateError.Error, 0, 0, null, strT); cError++; } } } // UNDONE: Validate legal multiplayer triggers // UNDONE: Validate computer sides have enough power // UNDONE: Validate computer sides have a surveillance center if they have towers // UNDONE: info -- total credits value of on-map Galaxite // UNDONE: info -- power supply/demand for each side return cError; }
public int Validate(ValidateErrorDelegate dgt) { return 0; }