private static void ToggleCreatureEvents(NWArea area) { AreaSpawn areaSpawn = AreaSpawns[area]; int playerCount = NWNXArea.GetNumberOfPlayersInArea(area); foreach (var creature in areaSpawn.Creatures) { if (creature.Spawn.IsValid) { bool eventsDisabled = creature.Spawn.GetLocalInt("SPAWN_EVENTS_DISABLED") == TRUE; bool isCreature = creature.Spawn.IsCreature; if (isCreature) { // Currently disabled, but players are in area. Enable them. if (eventsDisabled && playerCount > 0) { EnableCreatureEvents(creature.SpawnCreature); creature.SpawnCreature.SetLocalInt("SPAWN_EVENTS_DISABLED", FALSE); } // Currently enabled, but players are no longer in area. Disable them. else if (!eventsDisabled && playerCount <= 0) { DisableCreatureEvents(creature.SpawnCreature); creature.SpawnCreature.SetLocalInt("SPAWN_EVENTS_DISABLED", TRUE); } } } } }
private static void CopyAreaSpawns(string originalResref, NWArea copyArea) { NWArea originalArea = NWModule.Get().Areas.Single(x => x.Resref == originalResref && x.GetLocalInt("IS_AREA_INSTANCE") == FALSE); AreaSpawn originalAreaSpawn = AreaSpawns[originalArea]; AreaSpawns.Add(copyArea, originalAreaSpawn); }
private static void CopyAreaSpawns(string originalResref, NWArea copyArea) { NWArea originalArea = NWModule.Get().Areas.Single(x => x.Resref == originalResref && x.GetLocalBool("IS_AREA_INSTANCE") == false); AreaSpawn copyAreaSpawn = AreaSpawns[originalArea].Clone(); AreaSpawns.Add(copyArea, copyAreaSpawn); }
private static void ProcessSpawns() { using (new Profiler(nameof(SpawnService) + "." + nameof(ProcessSpawns))) { foreach (var spawn in AppCache.AreaSpawns) { // Check for a valid area - otherwise it causes hangs sometimes when the server shuts down. if (!spawn.Key.IsValid) { continue; } AreaSpawn areaSpawn = spawn.Value; bool forceSpawn = !areaSpawn.HasSpawned; foreach (var plc in areaSpawn.Placeables.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(plc, OBJECT_TYPE_PLACEABLE, spawn.Key, forceSpawn); } foreach (var creature in areaSpawn.Creatures.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(creature, OBJECT_TYPE_CREATURE, spawn.Key, forceSpawn); } areaSpawn.SecondsEmpty = 0.0f; areaSpawn.HasSpawned = true; } } }
private static void ProcessSpawns() { using (new Profiler(nameof(SpawnService) + "." + nameof(ProcessSpawns))) { // On module load, we want to populate all areas with NPCs and disable their AI. // The reason for this is because we don't want lag when players enter an area. // This'll use more memory but the CPU usage will be very limited as none of the // creatures will have scripts assigned. bool hasRunOnce = NWModule.Get().GetLocalInt("SPAWN_HAS_RUN_ONCE") == TRUE; foreach (var spawn in AreaSpawns) { // Check for a valid area - otherwise it causes hangs sometimes when the server shuts down. if (!spawn.Key.IsValid) { continue; } // Ignore empty areas. int playerCount = NWNXArea.GetNumberOfPlayersInArea(spawn.Key); if (playerCount <= 0 && hasRunOnce) { continue; } AreaSpawn areaSpawn = spawn.Value; bool forceSpawn = !areaSpawn.HasSpawned; foreach (var plc in areaSpawn.Placeables.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(plc, OBJECT_TYPE_PLACEABLE, spawn.Key, forceSpawn); } foreach (var creature in areaSpawn.Creatures.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(creature, OBJECT_TYPE_CREATURE, spawn.Key, forceSpawn); } areaSpawn.SecondsEmpty = 0.0f; areaSpawn.HasSpawned = true; // Toggle creature AI now, if this is the first time we're running this process. if (!hasRunOnce) { ToggleCreatureEvents(spawn.Key); } } NWModule.Get().SetLocalInt("SPAWN_HAS_RUN_ONCE", TRUE); } }
private static void InitializeAreaSpawns(NWArea area) { var areaSpawn = new AreaSpawn(); // Check for manually placed spawns NWObject obj = GetFirstObjectInArea(area.Object); while (obj.IsValid) { bool isSpawn = obj.ObjectType == OBJECT_TYPE_WAYPOINT && obj.GetLocalInt("IS_SPAWN") == TRUE; if (isSpawn) { int spawnType = obj.GetLocalInt("SPAWN_TYPE"); int objectType = spawnType == 0 || spawnType == OBJECT_TYPE_CREATURE ? OBJECT_TYPE_CREATURE : spawnType; int spawnTableID = obj.GetLocalInt("SPAWN_TABLE_ID"); int npcGroupID = obj.GetLocalInt("SPAWN_NPC_GROUP_ID"); string behaviourScript = obj.GetLocalString("SPAWN_BEHAVIOUR_SCRIPT"); if (string.IsNullOrWhiteSpace(behaviourScript)) { behaviourScript = obj.GetLocalString("SPAWN_BEHAVIOUR"); } string spawnResref = obj.GetLocalString("SPAWN_RESREF"); float respawnTime = obj.GetLocalFloat("SPAWN_RESPAWN_SECONDS"); string spawnRule = obj.GetLocalString("SPAWN_RULE"); int deathVFXID = obj.GetLocalInt("SPAWN_DEATH_VFX"); AIFlags aiFlags = (AIFlags)obj.GetLocalInt("SPAWN_AI_FLAGS"); bool useResref = true; // No resref specified but a table was, look in the database for a random record. if (string.IsNullOrWhiteSpace(spawnResref) && spawnTableID > 0) { // Pick a random record. var spawnObjects = DataService.SpawnObject.GetAllBySpawnTableID(spawnTableID).ToList(); int count = spawnObjects.Count; int index = count <= 0 ? 0 : RandomService.Random(count); var dbSpawn = spawnObjects[index]; if (dbSpawn != null) { spawnResref = dbSpawn.Resref; useResref = false; if (dbSpawn.NPCGroupID != null && dbSpawn.NPCGroupID > 0) { npcGroupID = Convert.ToInt32(dbSpawn.NPCGroupID); } if (!string.IsNullOrWhiteSpace(dbSpawn.BehaviourScript)) { behaviourScript = dbSpawn.BehaviourScript; } if (!string.IsNullOrWhiteSpace(dbSpawn.SpawnRule)) { spawnRule = dbSpawn.SpawnRule; } if (deathVFXID <= 0) { deathVFXID = dbSpawn.DeathVFXID; } if (aiFlags == AIFlags.None) { aiFlags = dbSpawn.AIFlags; } } } // If we found a resref, spawn the object and add it to the cache. if (!string.IsNullOrWhiteSpace(spawnResref)) { // Delay the creation so that the iteration through the area doesn't get thrown off by new entries. Location location = obj.Location; bool isInstance = area.IsInstance; ObjectSpawn newSpawn; if (useResref) { newSpawn = new ObjectSpawn(location, true, spawnResref, respawnTime); } else { newSpawn = new ObjectSpawn(location, true, spawnTableID, respawnTime); } if (npcGroupID > 0) { newSpawn.NPCGroupID = npcGroupID; } if (deathVFXID > 0) { newSpawn.DeathVFXID = deathVFXID; } if (!string.IsNullOrWhiteSpace(behaviourScript)) { newSpawn.BehaviourScript = behaviourScript; } if (!string.IsNullOrWhiteSpace(spawnRule)) { newSpawn.SpawnRule = spawnRule; } if (aiFlags == AIFlags.None) { newSpawn.AIFlags = aiFlags; } // Instance spawns are one-shot. if (isInstance) { newSpawn.Respawns = false; } if (objectType == OBJECT_TYPE_CREATURE) { areaSpawn.Creatures.Add(newSpawn); } else if (objectType == OBJECT_TYPE_PLACEABLE) { areaSpawn.Placeables.Add(newSpawn); } } } obj = GetNextObjectInArea(area.Object); } AreaSpawns.Add(area, areaSpawn); DelayCommand(1.0f, () => { SpawnResources(area, areaSpawn); }); }
private static void SpawnResources(NWArea area, AreaSpawn areaSpawn) { var dbArea = DataService.Area.GetByResref(area.Resref); if (dbArea.ResourceSpawnTableID <= 0 || !dbArea.AutoSpawnResources) { return; } var possibleSpawns = DataService.SpawnObject.GetAllBySpawnTableID(dbArea.ResourceSpawnTableID).ToList(); // 1024 size = 32x32 // 256 size = 16x16 // 64 size = 8x8 int size = area.Width * area.Height; int maxSpawns = 0; if (size <= 12) { maxSpawns = 2; } else if (size <= 32) { maxSpawns = 6; } else if (size <= 64) { maxSpawns = 10; } else if (size <= 256) { maxSpawns = 25; } else if (size <= 512) { maxSpawns = 40; } else if (size <= 1024) { maxSpawns = 50; } int[] weights = new int[possibleSpawns.Count()]; for (int x = 0; x < possibleSpawns.Count(); x++) { weights[x] = possibleSpawns.ElementAt(x).Weight; } for (int x = 1; x <= maxSpawns; x++) { int index = RandomService.GetRandomWeightedIndex(weights); var dbSpawn = possibleSpawns.ElementAt(index); Location location = GetRandomSpawnPoint(area); NWPlaceable plc = (CreateObject(OBJECT_TYPE_PLACEABLE, dbSpawn.Resref, location)); ObjectSpawn spawn = new ObjectSpawn(location, false, dbArea.ResourceSpawnTableID, 600.0f); spawn.Spawn = plc; ObjectVisibilityService.ApplyVisibilityForObject(plc); if (dbSpawn.NPCGroupID != null && dbSpawn.NPCGroupID > 0) { plc.SetLocalInt("NPC_GROUP", Convert.ToInt32(dbSpawn.NPCGroupID)); spawn.NPCGroupID = Convert.ToInt32(dbSpawn.NPCGroupID); } if (!string.IsNullOrWhiteSpace(dbSpawn.BehaviourScript) && string.IsNullOrWhiteSpace(plc.GetLocalString("BEHAVIOUR"))) { plc.SetLocalString("BEHAVIOUR", dbSpawn.BehaviourScript); spawn.BehaviourScript = dbSpawn.BehaviourScript; } if (!string.IsNullOrWhiteSpace(dbSpawn.SpawnRule)) { var rule = GetSpawnRule(dbSpawn.SpawnRule); rule.Run(plc); } areaSpawn.Placeables.Add(spawn); } }
public void Run(object[] args) { var spawns = _cache.AreaSpawns; foreach (var spawn in spawns) { // Check for a valid area - otherwise it causes hangs sometimes when the server shuts down. if (!spawn.Key.IsValid) { continue; } AreaSpawn areaSpawn = spawn.Value; int pcsInArea = NWModule.Get().Players.Count(x => x.Area.Equals(spawn.Key)); // No players in area. Process the despawner. if (pcsInArea <= 0 && areaSpawn.HasSpawned) { areaSpawn.SecondsEmpty += _processor.ProcessingTickInterval; if (areaSpawn.SecondsEmpty >= 1200) // 20 minutes have passed with no players in the area. { areaSpawn.SecondsEmpty = 0.0f; areaSpawn.HasSpawned = false; foreach (var plc in areaSpawn.Placeables) { if (plc.Spawn.IsValid) { plc.Spawn.Destroy(); } } foreach (var creature in areaSpawn.Creatures) { if (creature.Spawn.IsValid) { creature.Spawn.Destroy(); } } } } // Players in the area else if (pcsInArea > 0) { bool forceSpawn = !areaSpawn.HasSpawned; foreach (var plc in areaSpawn.Placeables.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(plc, OBJECT_TYPE_PLACEABLE, spawn.Key, forceSpawn); } foreach (var creature in areaSpawn.Creatures.Where(x => x.Respawns || !x.Respawns && !x.HasSpawnedOnce)) { ProcessSpawn(creature, OBJECT_TYPE_CREATURE, spawn.Key, forceSpawn); } areaSpawn.SecondsEmpty = 0.0f; areaSpawn.HasSpawned = true; } } }