public Point3D GetSpawnPosition(bool requiresurface, int packrange, Point3D packcoord, SpawnPositionInfo spawnpositioning) { Map map = Map; if (map == null) return Location; // random positioning by default SpawnPositionType positioning = SpawnPositionType.Random; Mobile trigmob = null; string[] positionargs = null; if(spawnpositioning != null) { positioning = spawnpositioning.positionType; trigmob = spawnpositioning.trigMob; positionargs = spawnpositioning.positionArgs; } // parse the possible args to the spawn position control keywords int fillinc = 1; int positionrange = 0; string prefix = null; ArrayList WayList = null; int xinc = 0; int yinc = 0; int zinc = 0; switch (positioning) { case SpawnPositionType.RowFill: case SpawnPositionType.ColFill: case SpawnPositionType.Perimeter: // syntax XFILL[,inc] // syntax YFILL[,inc] // syntax EDGE[,inc] if (positionargs != null && positionargs.Length > 1) { try { fillinc = int.Parse(positionargs[1]); } catch { } } break; case SpawnPositionType.RelXY: case SpawnPositionType.DeltaLocation: case SpawnPositionType.Location: // syntax RELXY,xinc,yinc[,zinc] // syntax XY,x,y[,z] // syntax DXY,dx,dy[,dz] if (positionargs != null && positionargs.Length > 2) { try { xinc = int.Parse(positionargs[1]); yinc = int.Parse(positionargs[2]); } catch { } } if (positionargs != null && positionargs.Length > 3) { try { zinc = int.Parse(positionargs[3]); } catch { } } break; case SpawnPositionType.Waypoint: // syntax WAYPOINT,prefix[,range] if (positionargs != null && positionargs.Length > 1) { prefix = positionargs[1]; } if (positionargs != null && positionargs.Length > 2) { try { positionrange = int.Parse(positionargs[2]); } catch { } } // find a list of items that match the waypoint prefix if (prefix != null) { WayList = new ArrayList(); foreach (Item i in World.Items.Values) { if (i is WayPoint && i.Name != null && i.Name.StartsWith(prefix)) { // add it to the list of items WayList.Add(i); } } } break; case SpawnPositionType.Player: // syntax PLAYER[,range] if (positionargs != null && positionargs.Length > 1) { try { positionrange = int.Parse(positionargs[1]); } catch { } } break; } // Try 10 times to find a Spawnable location. // trace profiling indicates that this is a major bottleneck for (int i = 0; i < 10; i++) { // Take the top left position of the spawn box and // add a random distance based on the width and // height of the spawn area. // be careful of the Utility.Random function. It will fail with a zero arg int x = m_X; int y = m_Y; int z = Z; int defaultZ = this.Z; if (packrange >= 0 && packcoord != Point3D.Zero) { defaultZ = packcoord.Z; } try { if (packrange >= 0 && packcoord != Point3D.Zero) { // find a random coord relative to the packcoord x = packcoord.X - packrange + Utility.Random(packrange * 2 + 1); y = packcoord.Y - packrange + Utility.Random(packrange * 2 + 1); } else if (m_Region != null && HasRegionPoints(m_Region)) // 2004.02.08 :: Omega Red { // if region spawning is selected then use that to find an x,y loc instead of the spawn box Point2D p = GetRandomRegionPoint(m_Region); x = p.X; y = p.Y; } else { switch (positioning) { case SpawnPositionType.Random: if (m_Width > 0) x = m_X + Utility.Random(m_Width + 1); if (m_Height > 0) y = m_Y + Utility.Random(m_Height + 1); break; case SpawnPositionType.RelXY: x = mostRecentSpawnPosition.X + xinc; y = mostRecentSpawnPosition.Y + yinc; defaultZ = mostRecentSpawnPosition.Z + zinc; break; case SpawnPositionType.DeltaLocation: x = this.X + xinc; y = this.Y + yinc; defaultZ = this.Z + zinc; break; case SpawnPositionType.Location: x = xinc; y = yinc; defaultZ = zinc; break; case SpawnPositionType.RowFill: x = mostRecentSpawnPosition.X + fillinc; y = mostRecentSpawnPosition.Y; if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } if (x > m_X + m_Width) { x = m_X + (x - m_X - m_Width - 1); y++; } if (y > m_Y + m_Height) { y = m_Y; } break; case SpawnPositionType.ColFill: x = mostRecentSpawnPosition.X; y = mostRecentSpawnPosition.Y + fillinc; if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } if (y > m_Y + m_Height) { y = m_Y + (y - m_Y - m_Height - 1); x++; } if (x > m_X + m_Width) { x = m_X; } break; case SpawnPositionType.Perimeter: x = mostRecentSpawnPosition.X; y = mostRecentSpawnPosition.Y; // if the point is not on the perimeter, reset it to the corner if (x != m_X && x != m_X + m_Width && y != m_Y && y != m_Y + m_Height) { x = m_X; y = m_Y; } if (y == m_Y && x < m_X + m_Width) x += fillinc; else if (y == m_Y + m_Height && x > m_X) x -= fillinc; else if (x == m_X && y > m_Y ) y -= fillinc; else if (x == m_X + m_Width && y < m_Y + m_Height) y += fillinc; if (x > m_X + m_Width) { x = m_X + m_Width; } if (y > m_Y + m_Height) { y = m_Y + m_Height; } if (x < m_X) { x = m_X; } if (y < m_Y) { y = m_Y; } break; case SpawnPositionType.Player: if (trigmob != null) { x = trigmob.Location.X; y = trigmob.Location.Y; if (positionrange > 0) { x += Utility.Random(positionrange*2 + 1) - positionrange; y += Utility.Random(positionrange * 2 + 1) - positionrange; } } break; case SpawnPositionType.Waypoint: // pick an item randomly from the waylist if (WayList != null && WayList.Count > 0) { int index = Utility.Random(WayList.Count); Item waypoint = (Item)WayList[index]; if (waypoint != null) { x = waypoint.Location.X; y = waypoint.Location.Y; if (positionrange > 0) { x += Utility.Random(positionrange * 2 + 1) - positionrange; y += Utility.Random(positionrange * 2 + 1) - positionrange; } } } break; } mostRecentSpawnPosition = new Point3D(x, y, defaultZ); } // try to find a valid spawn location using the z coord of the spawner // relax the normal surface requirement for mobiles if the flag is set bool fit = false; if (requiresurface) { fit = Map.CanSpawnMobile(x, y, defaultZ); } else { fit = Map.CanFit(x, y, defaultZ, SpawnFitSize, true, false, false); } // if that fails then try to find a valid z coord if (fit) { return new Point3D(x, y, defaultZ); } else { z = Map.GetAverageZ(x, y); if (requiresurface) { fit = Map.CanSpawnMobile(x, y, z); } else fit = Map.CanFit(x, y, z, SpawnFitSize, true, false, false); if (fit) { return new Point3D(x, y, z); } } } catch { } } if (packrange >= 0 && packcoord != Point3D.Zero) { return packcoord; } else { return this.Location; } }
// spawn an individual entry by index public bool Spawn(int index, bool smartspawn, int packrange, Point3D packcoord) { Map map = this.Map; // Make sure everything is ok to spawn an object if ((map == null) || (map == Map.Internal) || (m_SpawnObjects == null) || (m_SpawnObjects.Count == 0) || (index < 0) || (index >= m_SpawnObjects.Count) ) return false; // Remove any spawns that don't belong to the spawner any more. Defrag(false); // Get the spawn object at the required index SpawnObject TheSpawn = m_SpawnObjects[index] as SpawnObject; // Check if the object retrieved is a valid SpawnObject if (TheSpawn != null) { // check the nextspawn time to see if it is available if (TheSpawn.NextSpawn > DateTime.Now) return false; int CurrentCreatureMax = TheSpawn.MaxCount; int CurrentCreatureCount = TheSpawn.SpawnedObjects.Count; // Check that the current object to be spawned has not reached its maximum allowed // and make sure that the maximum spawner count has not been exceeded as well if ((CurrentCreatureCount >= CurrentCreatureMax) || (TotalSpawnedObjects >= m_Count)) { return false; } // check for string substitions string substitutedtypeName = BaseXmlSpawner.ApplySubstitution(this, this, m_mob_who_triggered, TheSpawn.TypeName); // random positioning is the default SpawnPositionInfo spawnpositioning = null; // require valid surfaces by default bool requiresurface = true; // parse the # function specification for the entry while (substitutedtypeName.StartsWith("#")) { string[] args = BaseXmlSpawner.ParseSemicolonArgs(substitutedtypeName, 2); if (args.Length > 0) { // parse any comma args string[] keyvalueargs = BaseXmlSpawner.ParseCommaArgs(args[0], 10); if (keyvalueargs.Length > 0) { switch (keyvalueargs[0]) { case "#XFILL": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.RowFill, m_mob_who_triggered, keyvalueargs); break; case "#YFILL": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.ColFill, m_mob_who_triggered, keyvalueargs); break; case "#EDGE": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.Perimeter, m_mob_who_triggered, keyvalueargs); break; case "#PLAYER": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.Player, m_mob_who_triggered, keyvalueargs); break; case "#WAYPOINT": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.Waypoint, m_mob_who_triggered, keyvalueargs); break; case "#RELXY": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.RelXY, m_mob_who_triggered, keyvalueargs); break; case "#DXY": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.DeltaLocation, m_mob_who_triggered, keyvalueargs); break; case "#XY": spawnpositioning = new SpawnPositionInfo(SpawnPositionType.Location, m_mob_who_triggered, keyvalueargs); break; case "#CONDITION": // test the specified condition string // syntax is #CONDITION,proptest // reparse with only one arg after the comma, this allows property tests that use commas as well string[] ckeyvalueargs = BaseXmlSpawner.ParseCommaArgs(args[0], 2); if (ckeyvalueargs.Length > 1) { // dont spawn if it fails the test if(!BaseXmlSpawner.CheckPropertyString(this, this, ckeyvalueargs[1], m_mob_who_triggered, out this.status_str)) return false; } else { this.status_str = "invalid #CONDITION specification: " + args[0]; } break; default: this.status_str = "invalid # specification: " + args[0]; break; } } } // get the rest of the spawn entry if (args.Length > 1) { substitutedtypeName = args[1].Trim(); } else { substitutedtypeName = String.Empty; } } if (substitutedtypeName.StartsWith("*")) { requiresurface = false; substitutedtypeName = substitutedtypeName.TrimStart('*'); } TheSpawn.RequireSurface = requiresurface; string typeName = BaseXmlSpawner.ParseObjectType(substitutedtypeName); if (BaseXmlSpawner.IsTypeOrItemKeyword(typeName)) { string status_str = null; bool completedtypespawn = BaseXmlSpawner.SpawnTypeKeyword(this, TheSpawn, typeName, substitutedtypeName, requiresurface, spawnpositioning, m_mob_who_triggered, this.Location, this.Map, new XmlGumpCallback(SpawnerGumpCallback), out status_str); if (status_str != null) { this.status_str = status_str; } if (completedtypespawn) { // successfully spawned the keyword // note that returning true means that Spawn will assume that it worked and will not try to respawn something else // added the duration timer that begins on spawning DoTimer2(m_Duration); InvalidateProperties(); return true; } else { return false; } } else { // its a regular type descriptor so find out what it is Type type = null; if (typeName != null) { type = SpawnerType.GetType(typeName); } // dont try to spawn invalid types, or Mobile type spawns in containers if (type != null && !(this.Parent != null && (type == typeof(Mobile) || type.IsSubclassOf(typeof(Mobile))))) { string[] arglist = BaseXmlSpawner.ParseString(substitutedtypeName, 3, "/"); object o = CreateObject(type, arglist[0]); if (o == null) { this.status_str = "invalid type specification: " + arglist[0]; return true; } try { if (o is Mobile) { // if this is in any container such as a pack the xyz values are invalid as map coords so dont spawn the mob if (this.Parent is Container) { ((Mobile)o).Delete(); return true; } Mobile m = (Mobile)o; // add the mobile to the spawned list TheSpawn.SpawnedObjects.Add(m); Point3D loc; /* if( ( m is BaseVendor ) && ( this.Map.CanFit( this.Location, SpawnFitSize, true, false ) == true ) ) { loc = this.Location; } else { loc = GetSpawnPosition(requiresurface); } */ loc = GetSpawnPosition(requiresurface, packrange, packcoord, spawnpositioning); if (!smartspawn) { m.OnBeforeSpawn(loc, map); } m.MoveToWorld(loc, map); if (m is BaseCreature) { BaseCreature c = (BaseCreature)m; c.RangeHome = m_HomeRange; c.CurrentWayPoint = m_WayPoint; if (m_Team > 0) c.Team = m_Team; // Check if this spawner uses absolute (from spawnER location) // or relative (from spawnED location) as the mobiles home point if (m_HomeRangeIsRelative == true) c.Home = m.Location; // Mobiles spawned location is the home point else c.Home = this.Location; // Spawners location is the home point } // if the object has an OnSpawned method, then invoke it if (!smartspawn) { m.OnAfterSpawn(); } // apply the parsed arguments from the typestring using setcommand // be sure to do this after setting map and location so that errors dont place the mob on the internal map string status_str; BaseXmlSpawner.ApplyObjectStringProperties(this, substitutedtypeName, m, m_mob_who_triggered, this, out status_str); // if the object has an OnAfterSpawnAndModify method, then invoke it //BaseXmlSpawner.InvokeOnAfterSpawnAndModify(o); if (status_str != null) { this.status_str = status_str; } InvalidateProperties(); // added the duration timer that begins on spawning DoTimer2(m_Duration); return true; } else if (o is Item) { Item item = (Item)o; string status_str; BaseXmlSpawner.AddSpawnItem(this, TheSpawn, item, this.Location, map, m_mob_who_triggered, requiresurface, spawnpositioning, substitutedtypeName, smartspawn, out status_str); if (status_str != null) { this.status_str = status_str; } InvalidateProperties(); // added the duration timer that begins on spawning DoTimer2(m_Duration); return true; } } catch (Exception ex) { Console.WriteLine("When spawning {0}, {1}", o, ex); } } else { this.status_str = "invalid type specification: " + typeName; return true; } } } return false; }