Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encounter Adjustments #4114

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Source/ACE.Server/Command/Handlers/DeveloperCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2741,6 +2741,7 @@ public static void HandleGeneratorDump(Session session, params string[] paramete
msg += $"Generator WCID: {wo.WeenieClassId}\n";
msg += $"Generator WeenieClassName: {wo.WeenieClassName}\n";
msg += $"Generator WeenieType: {wo.WeenieType.ToString()}\n";
msg += $"Generator IsEncounter: {wo.IsEncounter}\n";
msg += $"Generator Status: {(wo.GeneratorDisabled ? "Disabled" : "Enabled")}\n";
msg += $"GeneratorType: {wo.GeneratorType.ToString()}\n";
msg += $"GeneratorTimeType: {wo.GeneratorTimeType.ToString()}\n";
Expand Down Expand Up @@ -2782,6 +2783,19 @@ public static void HandleGeneratorDump(Session session, params string[] paramete
msg += $"IsMaxed: {profile.IsMaxed}\n";
if (!profile.IsMaxed)
msg += $"IsAvailable: {profile.IsAvailable}{(profile.IsAvailable ? "" : $", NextAvailable: {profile.NextAvailable.ToLocalTime()}")}\n";
msg += $"AdjustedProbability: {wo.GetAdjustedProbability(activeProfile)} / {wo.GetTotalProbability()}\n";
msg += $"MostRecentSpawnTime: {profile.MostRecentSpawnTime.ToLocalTime()}\n";
if (wo.IsEncounter)
{
var hasNonWorldObjects = 0;
var hasAwakeCreatures = 0;
var hasOpenContainers = 0;
var hasUnlockedChests = 0;
var isAbleToBeMarkedStale = profile.IsAbleToBeMarkedStale(ref hasNonWorldObjects, ref hasAwakeCreatures, ref hasOpenContainers, ref hasUnlockedChests);
msg += $"IsAbleToBeMarkedStale: {isAbleToBeMarkedStale}\n";
if (isAbleToBeMarkedStale)
msg += $"StaleTime: {profile.StaleTime.ToLocalTime()}\n";
}
msg += $"--====--\n";
if (profile.Spawned.Count > 0)
{
Expand Down
88 changes: 88 additions & 0 deletions Source/ACE.Server/Entity/GeneratorProfile.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;

using log4net;

using ACE.Common;
using ACE.Database;
using ACE.Entity;
using ACE.Entity.Enum;
Expand Down Expand Up @@ -114,6 +116,16 @@ public float Delay
}
}

/// <summary>
/// DateTime for when the profile last spawned something
/// </summary>
public DateTime MostRecentSpawnTime { get; set; } = DateTime.MinValue;

/// <summary>
/// DateTime for when the profile is considered stale
/// </summary>
public DateTime StaleTime { get; set; } = DateTime.MinValue;

/// <summary>
/// DateTime for when the profile is available as a possible spawn choice
/// </summary>
Expand All @@ -129,6 +141,60 @@ public float Delay
/// </summary>
public bool IsMaxed => MaxCreate != -1 && CurrentCreate >= MaxCreate;

///// <summary>
///// Returns TRUE if this profile has any non-Creature WorldObjects, Creatures with IsAwake being false, any Containers with IsOpen being false,
///// any Chests with DefaultLocked true and IsLocked being true
///// </summary>
public bool IsAbleToBeMarkedStale(ref int hasNonWorldObjects, ref int hasAwakeCreatures, ref int hasOpenContainers, ref int hasUnlockedChests)
{
//var hasNonWorldObjects = 0;
//var hasAwakeCreatures = 0;
//var hasOpenContainers = 0;
//var hasUnlockedChests = 0;

if (Spawned.Count == 0)
return false;

foreach (var spawn in Spawned.Values)
{
var wo = spawn.TryGetWorldObject();
if (wo != null)
{
if (wo is not Creature && !wo.IsGenerator)
hasNonWorldObjects++;

if (wo.IsGenerator)
{
//if (wo is not Creature)
// hasNonWorldObjects++;

//if (wo is not Creature && wo is not GenericObject)
// hasNonWorldObjects++;

//if (wo is Switch)
// hasNonWorldObjects++;

foreach (var profile in wo.GeneratorProfiles)
{
if (profile.IsAbleToBeMarkedStale(ref hasNonWorldObjects, ref hasAwakeCreatures, ref hasOpenContainers, ref hasUnlockedChests))
hasNonWorldObjects++;
}
}

if (wo is Creature creature && creature.IsAwake)
hasAwakeCreatures++;

if (wo is Container container && container.IsOpen)
hasOpenContainers++;

if (wo is Chest chest && (chest.GetProperty(PropertyBool.DefaultLocked) ?? false) && !chest.IsLocked)
hasUnlockedChests++;
}
}

return hasNonWorldObjects > 0 && hasAwakeCreatures == 0 && hasOpenContainers == 0 && hasUnlockedChests == 0;
}

/// <summary>
/// The generator world object for this profile
/// </summary>
Expand Down Expand Up @@ -180,6 +246,16 @@ public void Enqueue(int numObjects = 1)
break;
}*/
SpawnQueue.Add(GetSpawnTime());

//if (Generator.IsEncounter)
//{
// Generator.CurrentLandblock?.NotifyEncounterGenerators(Generator, (int)Id);
// foreach (var landblock in Generator.CurrentLandblock?.Adjacents)
// landblock.NotifyEncounterGenerators(Generator, (int)Id);
//}

//if (Generator.IsEncounter)
// Generator.CurrentLandblock?.RegisterEncounterProfileUsed(Generator, Id, Delay);
}
}

Expand Down Expand Up @@ -214,6 +290,16 @@ public void ProcessQueue()

Spawned.Add(obj.Guid.Full, woi);
}

MostRecentSpawnTime = DateTime.UtcNow;

if (Generator.IsEncounter)
{
var variance = ThreadSafeRandom.Next(0, Delay);
StaleTime = DateTime.UtcNow.AddSeconds(Delay * MaxCreate + variance);

//Generator.CurrentLandblock?.NotifyEncounterGenerators(Generator, (int)Id);
}
}
}
else
Expand Down Expand Up @@ -618,6 +704,8 @@ private void CleanupProfile()
Spawned.Clear();
SpawnQueue.Clear();

MostRecentSpawnTime = DateTime.MinValue;
StaleTime = DateTime.MinValue;
NextAvailable = DateTime.UtcNow;

GeneratedTreasureItem = false;
Expand Down
49 changes: 49 additions & 0 deletions Source/ACE.Server/Entity/Landblock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ private void SpawnEncounters()

if (wo == null) continue;

wo.IsEncounter = true;

actionQueue.EnqueueAction(new ActionEventDelegate(() =>
{
var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f);
Expand Down Expand Up @@ -1349,5 +1351,52 @@ public void DoEnvironChange(EnvironChangeType environChangeType)
else
SendEnvironSound(environChangeType);
}

//public void NotifyEncounterGenerators(WorldObject generator, int profileId)
//{
// //if (string.IsNullOrWhiteSpace(message)) return;

// //foreach (var wo in worldObjects.Values.Where(w => w.HearLocalSignals).ToList())
// //{
// // if (emitter == wo) continue;

// // if (emitter.IsWithinUseRadiusOf(wo, wo.HearLocalSignalsRadius))
// // {
// // //Console.WriteLine($"{wo.Name}.EmoteManager.OnLocalSignal({emitter.Name}, {message})");
// // wo.EmoteManager.OnLocalSignal(emitter, message);
// // }
// //}

// var nextAvailable = DateTime.UtcNow.AddSeconds(generator.GeneratorProfiles[profileId].Delay);

// var encounterGenerators = worldObjects.Values.Where(w => w.WeenieClassId == generator.WeenieClassId && w.Guid != generator.Guid).ToList();
// foreach (var encounterGenerator in encounterGenerators)
// {
// encounterGenerator.GeneratorProfiles[profileId].NextAvailable = nextAvailable;
// }
//}

//private readonly Dictionary<uint, Dictionary<int, DateTime>> EncounterSomething = new();

//public void RegisterEncounterProfileUsed(WorldObject generator, uint profileId, float delay)
//{
// EncounterSomething.TryAdd(generator.WeenieClassId, new());

// EncounterSomething[generator.WeenieClassId][(int)profileId] = DateTime.UtcNow.AddSeconds(delay);
//}

//public bool CheckEncounterProfileIsAvailable(WorldObject generator, uint profileId)
//{
// if (!EncounterSomething.TryGetValue(generator.WeenieClassId, out var encounter))
// return true;

// if (!encounter.TryGetValue((int)profileId, out var nextAvailable))
// return true;

// if (DateTime.UtcNow > nextAvailable)
// return true;

// return false;
//}
}
}
71 changes: 63 additions & 8 deletions Source/ACE.Server/WorldObjects/WorldObject_Generators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ partial class WorldObject
/// (spawns other world objects)
/// </summary>
public bool IsGenerator { get => GeneratorProfiles != null && GeneratorProfiles.Count > 0; }


/// <summary>
/// Is this WorldObject created from an Encounter, Set by Landblock
/// </summary>
public bool IsEncounter { get; set; }

//public List<string> History = new List<string>();

/// <summary>
Expand Down Expand Up @@ -116,8 +121,9 @@ public void SelectAProfile()

//var totalProbability = rng_selected ? GetTotalProbability() : 1.0f;
//var rng = ThreadSafeRandom.Next(0.0f, totalProbability);
//var rng = ThreadSafeRandom.Next(0.0f, 1.0f);
var rng = ThreadSafeRandom.Next(0.0f, GetTotalProbability());
var rng = ThreadSafeRandom.Next(0.0f, 1.0f);
//var rng = ThreadSafeRandom.Next(0.0f, GetTotalProbability());
//var rng = ThreadSafeRandom.Next(0.0f, GetMaxProbability());

for (var i = 0; i < GeneratorProfiles.Count; i++)
{
Expand All @@ -135,6 +141,30 @@ public void SelectAProfile()
if (!profile.IsAvailable)
continue;

//if (IsEncounter && CurrentLandblock != null && !CurrentLandblock.CheckEncounterProfileIsAvailable(this, profile.Id))
// continue;
//if (IsEncounter)
//{
// if (CurrentLandblock != null)
// {
// if (!CurrentLandblock.CheckEncounterProfileIsAvailable(this, profile.Id))
// continue;

// //var adjacentIsAvailable = true;
// //foreach(var adjacent in CurrentLandblock.Adjacents)
// //{
// // if (!adjacent.CheckEncounterProfileIsAvailable(this, profile.Id))
// // {
// // adjacentIsAvailable = false;
// // break;
// // }
// //}

// //if (!adjacentIsAvailable)
// // continue;
// }
//}

if (profile.RegenLocationType.HasFlag(RegenLocationType.Treasure))
{
if (profile.Biota.InitCreate > 1)
Expand All @@ -151,8 +181,8 @@ public void SelectAProfile()
}

//var probability = rng_selected ? GetAdjustedProbability(i) : profile.Biota.Probability;
//var probability = profile.Biota.Probability;
var probability = GetAdjustedProbability(i);
var probability = profile.Biota.Probability;
//var probability = GetAdjustedProbability(i);

if (rng < probability || probability == -1)
{
Expand Down Expand Up @@ -308,7 +338,7 @@ public int GetSpawnObjectsForProfile(GeneratorProfile profile)
if (numObjects == 0 && initCreate == 0)
log.Warn($"[GENERATOR] 0x{Guid}:{WeenieClassId} {Name}.GetSpawnObjectsForProfile(profile[{profile.LinkId}]): profile.InitCreate = {profile.InitCreate} | profile.MaxCreate = {profile.MaxCreate} | profile.WeenieClassId = {profile.WeenieClassId} | Profile Init invalid, cannot spawn.");
else if (numObjects == 0)
log.Warn($"[GENERATOR] 0x{Guid}:{WeenieClassId} {Name}.GetSpawnObjectsForProfile(profile[{profile.LinkId}]): profile.InitCreate = {profile.InitCreate} | profile.MaxCreate = {profile.MaxCreate} | profile.WeenieClassId = {profile.WeenieClassId} | genSlotsAvailable = {genSlotsAvailable} | profileSlotsAvailable = {profileSlotsAvailable} | numObjects = {numObjects}, cannot spawn.");
log.Warn($"[GENERATOR] 0x{Guid}:{WeenieClassId} {Name}.GetSpawnObjectsForProfile(profile[{profile.LinkId}]): profile.InitCreate = {profile.InitCreate} | profile.MaxCreate = {profile.MaxCreate} | profile.WeenieClassId = {profile.WeenieClassId} | genSlotsAvailable = {genSlotsAvailable} | profileSlotsAvailable = {profileSlotsAvailable} | numObjects = {numObjects}, cannot spawn.");

return numObjects;
}
Expand Down Expand Up @@ -355,7 +385,7 @@ public void CheckGeneratorStatus()
case GeneratorTimeType.Day:
CheckTimeOfDayStatus();
break;
}
}
}

/// <summary>
Expand All @@ -364,7 +394,7 @@ public void CheckGeneratorStatus()
public void CheckTimeOfDayStatus()
{
var prevDisabled = GeneratorDisabled;

var isDay = Timers.CurrentInGameTime.IsDay;
var isDayGenerator = GeneratorTimeType == GeneratorTimeType.Day;

Expand Down Expand Up @@ -656,6 +686,8 @@ public void Generator_Generate()
{
//Console.WriteLine($"{Name}.Generator_Generate({RegenerationInterval})");

CheckForStaleEncounters();

if (!GeneratorDisabled)
{
if (CurrentlyPoweringUp)
Expand Down Expand Up @@ -684,6 +716,8 @@ public void Generator_Generate()

foreach (var profile in GeneratorProfiles)
profile.Spawn_HeartBeat();

//CheckForStaleEncounters();
}

public virtual void ResetGenerator()
Expand All @@ -703,5 +737,26 @@ public virtual void ResetGenerator()
}
return null;
}

/// <summary>
/// If Generator has been marked an Encounter by Landblock, check for idle, stale profiles and reset them for long lived landblocks.
/// </summary>
public void CheckForStaleEncounters()
{
if (!IsEncounter) return;

var hasNonWorldObjects = 0;
var hasAwakeCreatures = 0;
var hasOpenContainers = 0;
var hasUnlockedChests = 0;

var idleStaleProfiles = GeneratorProfiles.Where(p => p.IsMaxed && (DateTime.UtcNow > p.StaleTime) && p.IsAbleToBeMarkedStale(ref hasNonWorldObjects, ref hasAwakeCreatures, ref hasOpenContainers, ref hasUnlockedChests));

foreach (var profile in idleStaleProfiles)
{
profile.Reset();
profile.NextAvailable = DateTime.UtcNow.AddSeconds(profile.Delay);
}
}
}
}