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 ProcessAreaAI() { using (new Profiler(nameof(AIService) + "." + nameof(ProcessAreaAI))) { foreach (var area in NWModule.Get().Areas) { int lastTickPlayerCount = area.GetLocalInt("AI_PLAYER_COUNT"); int thisTickPlayerCount = NWNXArea.GetNumberOfPlayersInArea(area); area.SetLocalInt("AI_PLAYER_COUNT", thisTickPlayerCount); // AI gets processed one more time after an area becomes empty. // We do this so that behaviours can clean up properly. if (thisTickPlayerCount <= 0 && lastTickPlayerCount <= 0) { continue; } // Safety check - If the area isn't in the cache, report it. if (!_areaAICreatures.ContainsKey(area)) { Console.WriteLine("Area " + area.Name + " not registered with AI service. Tag: " + area.Tag + ", Resref = " + area.Resref); continue; } var creatures = _areaAICreatures[area]; ProcessCreatureAI(area, ref creatures); } } }
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 OnAreaExit() { NWArea area = NWGameObject.OBJECT_SELF; int playerCount = NWNXArea.GetNumberOfPlayersInArea(area); if (playerCount > 0) { _.SetEventScript(area, _.EVENT_SCRIPT_AREA_ON_HEARTBEAT, "area_on_hb"); } else { _.SetEventScript(area, _.EVENT_SCRIPT_AREA_ON_HEARTBEAT, string.Empty); } }
private static void OnAreaExit() { NWArea area = _.OBJECT_SELF; int playerCount = NWNXArea.GetNumberOfPlayersInArea(area); if (playerCount > 0) { _.SetEventScript(area, EventScript.Area_OnHeartbeat, "area_on_hb"); } else { _.SetEventScript(area, EventScript.Area_OnHeartbeat, string.Empty); } }
public virtual void OnHeartbeat(NWCreature self) { // No sense processing for empty and invalid (limbo) areas. if (!self.Area.IsValid || NWNXArea.GetNumberOfPlayersInArea(self.Area) <= 0) { return; } var flags = GetAIFlags(self); if ((flags & AIFlags.RandomWalk) != 0) { RandomWalk(self); } }
private static void ProcessAreaAI() { using (new Profiler(nameof(AIService) + "." + nameof(ProcessAreaAI))) { foreach (var area in NWModule.Get().Areas) { // We don't process AI for empty areas. if (NWNXArea.GetNumberOfPlayersInArea(area) <= 0) { continue; } var creatures = _areaAICreatures[area]; ProcessCreatureAI(ref creatures); } } }
private void AttemptCleanup() { NWPlaceable exit = NWGameObject.OBJECT_SELF; NWArea mainLevel = exit.Area.GetLocalObject("MAIN_LEVEL"); NWArea restrictedLevel = exit.Area.GetLocalObject("RESTRICTED_LEVEL"); NWArea directorsChambers = exit.Area.GetLocalObject("DIRECTORS_CHAMBERS"); int playersInAreas = NWNXArea.GetNumberOfPlayersInArea(mainLevel) + NWNXArea.GetNumberOfPlayersInArea(restrictedLevel) + NWNXArea.GetNumberOfPlayersInArea(directorsChambers); // There are still players in the areas. We can't clean up yet. if (playersInAreas > 0) { return; } // Otherwise, everyone has left. Do the cleanup now. AreaService.DestroyAreaInstance(mainLevel); AreaService.DestroyAreaInstance(restrictedLevel); AreaService.DestroyAreaInstance(directorsChambers); }
private static void ProcessCreatureAI(ref HashSet <NWCreature> creatures) { // Iterate backwards so we can remove the creature if it's no longer valid. for (int x = creatures.Count - 1; x >= 0; x--) { NWCreature creature = creatures.ElementAt(x); NWArea area = creature.Area; // Limbo check. if (!area.IsValid) { continue; } bool areaHasPCs = NWNXArea.GetNumberOfPlayersInArea(area) > 0; // Is this creature invalid or dead? If so, remove it and move to the next one. if (!creature.IsValid || creature.IsDead) { creatures.Remove(creature); continue; } // Are there no players in the area? Is the creature being possessed? If so, don't execute AI this frame. Move to the next one. if (creature.IsPossessedFamiliar || creature.IsDMPossessed || !areaHasPCs) { continue; } string script = GetBehaviourScript(creature); if (string.IsNullOrWhiteSpace(script)) { continue; } IAIBehaviour behaviour = GetAIBehaviour(script); behaviour.OnProcessObject(creature); } }
private static void ProcessAreaAI() { using (new Profiler(nameof(AIService) + "." + nameof(ProcessAreaAI))) { foreach (var area in NWModule.Get().Areas) { // We don't process AI for empty areas. if (NWNXArea.GetNumberOfPlayersInArea(area) <= 0) { continue; } // Safety check - If the area isn't in the cache, report it. if (!_areaAICreatures.ContainsKey(area)) { Console.WriteLine("Area " + area.Name + " not registered with AI service. Tag: " + area.Tag + ", Resref = " + area.Resref); return; } var creatures = _areaAICreatures[area]; ProcessCreatureAI(ref creatures); } } }