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

feat: Day/Night support for monster spawns #265

Merged
merged 10 commits into from
Apr 28, 2024
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Server.Network;
using Arrowgene.Ddon.Shared.Crypto;
Expand All @@ -7,12 +8,28 @@
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Logging;
using Arrowgene.Ddon.Shared.Csv;
using System.Diagnostics;

namespace Arrowgene.Ddon.GameServer.Handler
{
public class InstanceGetEnemySetListHandler : StructurePacketHandler<GameClient, C2SInstanceGetEnemySetListReq>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(InstanceGetEnemySetListHandler));
private static readonly long ORIGINAL_REAL_TIME_SEC = 0x55DDD470; // Taken from the pcaps. A few days before DDOn release. Wednesday, 26 August 2015 15:00:00


// Magical game time calculation obtained from the PS4 client, can be found in ServerGameTimeGetBaseinfoHandler
private long calcGameTimeMSec(DateTimeOffset realTime, long originalRealTimeSec, uint gameTimeOneDayMin, uint gameTimeDayHour)
{
long result = (1440 * (realTime.Millisecond + 1000 * (realTime.ToUnixTimeSeconds() - originalRealTimeSec)) / gameTimeOneDayMin)
% (3600000 * gameTimeDayHour);
return result;
}
// defining how many hours there are in a day (24)
const int gameDayLength = 24;
// defining how long a full 24 hours cycle is in real world time, (90 minutes)
const int gameDayLengthRealTime = 90;
Conner-Schaffer marked this conversation as resolved.
Show resolved Hide resolved

public InstanceGetEnemySetListHandler(DdonGameServer server) : base(server)
{
Expand All @@ -24,7 +41,6 @@ public override void Handle(GameClient client, StructurePacket<C2SInstanceGetEne
byte subGroupId = request.Structure.SubGroupId;
client.Character.Stage = stageId;


List<Enemy> spawns = Server.AssetRepository.EnemySpawnAsset.Enemies.GetValueOrDefault((stageId, subGroupId)) ?? new List<Enemy>();

// TODO test
Expand All @@ -34,16 +50,37 @@ public override void Handle(GameClient client, StructurePacket<C2SInstanceGetEne
response.LayoutId = stageId.ToStageLayoutId();
response.SubGroupId = subGroupId;
response.RandomSeed = CryptoRandom.Instance.GetRandomUInt32();

for (byte i = 0; i < spawns.Count; i++)
{
Enemy spawn = spawns[i];
CDataLayoutEnemyData enemy = new CDataLayoutEnemyData
{
PositionIndex = i,
EnemyInfo = spawn.asCDataStageLayoutEnemyPresetEnemyInfoClient()

};
response.EnemyList.Add(enemy);

// Calculate current game time
long gameTimeMSec = calcGameTimeMSec(DateTimeOffset.Now, ORIGINAL_REAL_TIME_SEC, gameDayLengthRealTime, gameDayLength);

// getting the start and end times from enemy.cs and defining them here for handling the spawnchecks
long startMilliseconds, endMilliseconds;
startMilliseconds = spawn.SpawnTimeStart;
endMilliseconds = spawn.SpawnTimeEnd;

// If end < start, it spans past midnight and needs special range handling
if(endMilliseconds < startMilliseconds)
{
if(gameTimeMSec <= endMilliseconds // Morning range is 0 (midnight) to end time
|| gameTimeMSec >= startMilliseconds) // Evening range is start time and onwards
{
response.EnemyList.Add(enemy);
}
}
else if(gameTimeMSec >= startMilliseconds && gameTimeMSec <= endMilliseconds)
{
response.EnemyList.Add(enemy);
}
}

client.Send(response);
Expand Down
42 changes: 40 additions & 2 deletions Arrowgene.Ddon.Shared/AssetReader/EnemySpawnAssetDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class EnemySpawnAssetDeserializer : IAssetDeserializer<EnemySpawnAsset>
{
private static readonly ILogger Logger = LogProvider.Logger(typeof(EnemySpawnAssetDeserializer));

private static readonly string[] ENEMY_HEADERS = new string[]{"StageId", "LayerNo", "GroupId", "SubGroupId", "EnemyId", "NamedEnemyParamsId", "RaidBossId", "Scale", "Lv", "HmPresetNo", "StartThinkTblNo", "RepopNum", "RepopCount", "EnemyTargetTypesId", "MontageFixNo", "SetType", "InfectionType", "IsBossGauge", "IsBossBGM", "IsManualSet", "IsAreaBoss", "BloodOrbs", "HighOrbs", "Experience", "DropsTableId"};
private static readonly string[] ENEMY_HEADERS = new string[]{"StageId", "LayerNo", "GroupId", "SubGroupId", "EnemyId", "NamedEnemyParamsId", "RaidBossId", "Scale", "Lv", "HmPresetNo", "StartThinkTblNo", "RepopNum", "RepopCount", "EnemyTargetTypesId", "MontageFixNo", "SetType", "InfectionType", "IsBossGauge", "IsBossBGM", "IsManualSet", "IsAreaBoss", "BloodOrbs", "HighOrbs", "Experience", "DropsTableId", "SpawnTime"};
private static readonly string[] DROPS_TABLE_HEADERS = new string[]{"ItemId", "ItemNum", "MaxItemNum", "Quality", "IsHidden", "DropChance"};

public EnemySpawnAsset ReadPath(string path)
Expand Down Expand Up @@ -99,6 +99,24 @@ public EnemySpawnAsset ReadPath(string path)
HighOrbs = row[enemySchemaIndexes["HighOrbs"]].GetUInt32(),
Experience = row[enemySchemaIndexes["Experience"]].GetUInt32(),
};

//checking if the file has spawntime, if yes we convert the time and pass it along to enemy.cs
if(enemySchemaIndexes.ContainsKey("SpawnTime"))
{
string SpawnTimeGet = row[enemySchemaIndexes["SpawnTime"]].GetString();
ConvertSpawnTimeToMilliseconds(SpawnTimeGet, out long start, out long end);
enemy.SpawnTimeStart = start;
enemy.SpawnTimeEnd = end;
}
else
{
// if no, we define it to the "allday" spawn time range and pass this along to the enemy.cs instead.
ConvertSpawnTimeToMilliseconds("00:00,23:59", out long start, out long end);
enemy.SpawnTimeStart = start;
enemy.SpawnTimeEnd = end;
}


int dropsTableId = row[enemySchemaIndexes["DropsTableId"]].GetInt32();
if(dropsTableId >= 0)
{
Expand All @@ -110,6 +128,26 @@ public EnemySpawnAsset ReadPath(string path)

return asset;
}
// this converts the time (07:00,18:00) as example, down into milliseconds, via splitting into hours/minutes and then combining each respect time into start/end
public void ConvertSpawnTimeToMilliseconds(string SpawnTime, out long startMilliseconds, out long endMilliseconds)
{
// Split the spawnTime string at the comma to get start and end times
string[] spawnTimes = SpawnTime.Split(',');

// Split the start time at the colon to get hours and minutes
string[] startTimeComponents = spawnTimes[0].Split(':');
int startHours = int.Parse(startTimeComponents[0]);
int startMinutes = int.Parse(startTimeComponents[1]);

// Split the end time at the colon to get hours and minutes
string[] endTimeComponents = spawnTimes[1].Split(':');
int endHours = int.Parse(endTimeComponents[0]);
int endMinutes = int.Parse(endTimeComponents[1]);

// Convert hours and minutes into milliseconds
startMilliseconds = (startHours * 3600000) + (startMinutes * 60000);
endMilliseconds = (endHours * 3600000) + (endMinutes * 60000);
}

protected uint ParseHexUInt(string str)
{
Expand All @@ -131,4 +169,4 @@ private Dictionary<string, int> findSchemaIndexes(string[] reference, List<strin
return schemaIndexes;
}
}
}
}
Loading
Loading