using DiIiS_NA.GameServer.MessageSystem;
using GameBalance = DiIiS_NA.Core.MPQ.FileFormats.GameBalance;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing;
using DiIiS_NA.Core.Logging;
using DiIiS_NA.Core.MPQ;
using DiIiS_NA.Core.Storage;
using DiIiS_NA.Core.Helpers.Hash;
using DiIiS_NA.GameServer.ClientSystem;
using DiIiS_NA.GameServer.GSSystem.TickerSystem;
using DiIiS_NA.GameServer.GSSystem.QuestSystem;
using DiIiS_NA.GameServer.Core.Types.Math;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Game;
using DiIiS_NA.GameServer.MessageSystem.Message.Fields;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Act;
using DiIiS_NA.GameServer.Core.Types.SNO;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Player;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Team;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Text;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Base;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Quest;
using DiIiS_NA.GameServer.Core.Types.TagMap;
using DiIiS_NA.GameServer.GSSystem.PlayerSystem;
using DiIiS_NA.GameServer.GSSystem.ActorSystem.Implementations.Hirelings;
using DiIiS_NA.GameServer.GSSystem.GeneratorsSystem;
using DiIiS_NA.GameServer.GSSystem.AISystem.Brains;
using System.Diagnostics;
using DiIiS_NA.Core.MPQ.FileFormats;
using DiIiS_NA.D3_GameServer.Core.Types.SNO;
using DiIiS_NA.D3_GameServer.GSSystem.GameSystem;
using Actor = DiIiS_NA.GameServer.GSSystem.ActorSystem.Actor;
using Monster = DiIiS_NA.GameServer.GSSystem.ActorSystem.Monster;
using Scene = DiIiS_NA.GameServer.GSSystem.MapSystem.Scene;
using World = DiIiS_NA.GameServer.GSSystem.MapSystem.World;
using DiIiS_NA.Core.MPQ.FileFormats;
namespace DiIiS_NA.GameServer.GSSystem.GameSystem
{
public enum ActEnum
{
Act1 = 0,
Act2 = 100,
Act3 = 200,
Act4 = 300,
Act5 = 400,
OpenWorld = 3000
}
public class Game : IMessageConsumer
{
private static readonly Logger Logger = LogManager.CreateLogger();
///
/// The game id.
///
public int GameId { get; private set; }
///
/// Dictionary that maps gameclient's to players.
///
public ConcurrentDictionary Players { get; private set; }
public ImmutableArray ConnectedPlayers => Players.Where(s => s.Value != null && s.Key.Connection.IsOpen() && !s.Key.IsLoggingOut)
.Select(s=>s.Value).ToImmutableArray();
public bool QuestSetup = false;
public int LoadedPlayers = 0;
public int CurrentPvPRound = 0;
public TickTimer PvPTimer;
public TickTimer GlobalPvPTimer;
public TickTimer QuestTimer;
public WorldSno WorldOfPortalNephalem = WorldSno.__NONE;
public WorldSno WorldOfPortalNephalemSec = WorldSno.__NONE;
public int NephalemGreaterLevel = -1;
public bool NephalemGreater = false;
public bool NephalemBuff = false;
public bool ActiveNephalemPortal = false;
public bool ActiveNephalemTimer = false;
public float ActiveNephalemProgress = 0f;
public bool ActiveNephalemKilledMobs = false;
public bool ActiveNephalemKilledBoss = false;
public SecondsTickTimer TiredRiftTimer;
public int LastTieredRiftTimeout = 0;
public TickTimer LockdownTimer;
public Actor SideQuestGizmo = null;
public int RedTeamWins = 0;
public int BlueTeamWins = 0;
///
/// DynamicId counter for objects.
///
private uint _lastObjectId = 10001;
///
/// Returns a new dynamicId for objects.
///
private readonly object _obj = new();
public uint NewActorGameId
{
get
{
lock (_obj)
{
_lastObjectId++;
return _lastObjectId;
}
}
}
///
/// Dictionary that tracks world.
/// NOTE: This tracks by WorldSNO rather than by DynamicID; this.Objects _does_ still contain the world since it is a DynamicObject
///
private readonly ConcurrentDictionary _worlds;
public List Worlds => _worlds.Values.ToList();
public Mode GameMode = Mode.Campaign;
public enum Mode
{
Campaign = 0,
Bounties = 1,
Portals = 6 //6?
}
public struct BossEncounter
{
public int SnoId;
public bool Activated;
public int AcceptedPlayers;
};
public readonly Dictionary> OnLoadWorldActions = new();
public readonly Dictionary> OnLoadSceneActions = new();
public BossEncounter CurrentEncounter = new() { SnoId = -1, Activated = false, AcceptedPlayers = 0 };
///
/// Starting world's sno.
///
public WorldSno StartingWorldSno { get; private set; }
///
/// Starting world's monster level
///
public int InitialMonsterLevel { get; set; }
public int MonsterLevel { get; private set; }
///
/// Is it a world without players?
///
public bool Empty { get; private set; }
///
/// Paused game state (for single-player only)
///
public bool Paused { get; private set; }
private bool _updateEnabled = true;
///
/// Starting world for the game.
///
public World StartingWorld => GetWorld(StartingWorldSno);
///
/// Player index counter.
///
public int PlayerIndexCounter = -1;
public int PlayerGroupIndexCounter = 0;
///
/// Current quest SNOid.
///
public int CurrentQuest = -1;
public int CurrentSideQuest = -1;
///
/// Current quest step SNOid.
///
public int DestinationEnterQuest = -1;
public int DestinationEnterQuestStep = -1;
///
/// Current act system id.
///
public int CurrentAct = -1;
public ActEnum CurrentActEnum => CurrentAct != -1 ? (ActEnum)CurrentAct : ActEnum.Act1;
private int _difficulty = 0;
///
/// Current difficulty system id.
/// Min: 0, Max: 19
///
public int Difficulty { get => _difficulty; set => _difficulty = Math.Clamp(value, 0, 19); }
public float HpModifier { get; set; } = 1f;
public float DmgModifier { get; set; } = 1f;
public float XpModifier { get; set; } = 1f;
public float GoldModifier { get; set; } = 1f;
///
/// Hardcore mode flag.
///
public bool IsHardcore = false;
public bool IsSeasoned = false;
public List OpenedWaypoints = new();
public readonly Dictionary BountiesCompleted = new()
{
{BountyData.ActT.A1, 0},
{BountyData.ActT.A2, 0},
{BountyData.ActT.A3, 0},
{BountyData.ActT.A4, 0},
{BountyData.ActT.A5, 0}
};
///
/// Current act SNO id.
///
public int CurrentActSnoId =>
CurrentActEnum switch
{
ActEnum.Act1 => 70015,
ActEnum.Act2 => 70016,
ActEnum.Act3 => 70017,
ActEnum.Act4 => 70018,
ActEnum.Act5 => 236915,
ActEnum.OpenWorld => 70015,
_ => throw new ArgumentOutOfRangeException()
};
///
/// Last completed quest SNOid.
///
public int LastCompletedQuest = -1;
///
/// Current quest step SNOid.
///
public int CurrentStep = -1;
public int CurrentSideStep = -1;
///
/// Current quest order (for auto-advance).
///
public int[] QuestsOrder;
///
/// Current quest progress handler.
///
public QuestRegistry QuestProgress;
public QuestRegistry SideQuestProgress;
///
/// World generator for this game
///
public WorldGenerator WorldGenerator;
///
/// Database connection for this game
///
public GameDBSession GameDbSession;
///
/// Update frequency for the game - 100 ms.
///
public readonly long UpdateFrequency = 100;
///
/// Incremented tick value on each Game.Update().
///
public readonly int TickRate = 6;
///
/// Tick counter.
///
private int _tickCounter;
///
/// Returns the latest tick count.
///
public int TickCounter => _tickCounter;
// ///
// /// Stopwatch that measures time takent to get a full Game.Update().
// ///
//private readonly Stopwatch _tickWatch;
///
/// DynamicId counter for scene.
///
private uint _lastSceneId = 0x04000000;
///
/// Returns a new dynamicId for scenes.
///
public uint NewSceneId
{
get
{
lock (_obj)
{
_lastSceneId++;
return _lastSceneId;
}
}
}
public int WaypointFlags
{
get
{
if (CurrentAct == 3000) return 0x0000ffff;
int flags = 0;
for (int i = 16; i >= 0; i--)
{
flags = flags << 1;
if (OpenedWaypoints.Contains(i)) flags++;
}
return flags;
}
}
public Vector3D StartPosition =>
CurrentActEnum switch
{
ActEnum.Act1 => StartingWorld.GetStartingPointById(24).Position,
ActEnum.Act2 => StartingWorld.GetStartingPointById(59).Position,
ActEnum.Act3 => StartingWorld.GetStartingPointById(172).Position,
ActEnum.Act4 => StartingWorld.GetStartingPointById(172).Position,
ActEnum.Act5 => StartingWorld.GetStartingPointById(172).Position,
ActEnum.OpenWorld => StartingWorld.GetStartingPointById(24).Position,
_ => StartingWorld.StartingPoints.First().Position
};
///
/// DynamicId counter for worlds.
///
private uint _lastWorldId = 0x07000000;
public int WeatherSeed = DiIiS_NA.Core.Helpers.Math.FastRandom.Instance.Next();
///
/// Returns a new dynamicId for worlds.
///
public uint NewWorldId => _lastWorldId++;
public QuestManager QuestManager { get; private set; }
//public AI.Pather Pathfinder { get; private set; }
public bool Working = true;
public bool PvP = false;
///
/// Creates a new game with given gameId.
///
///
public Game(int gameId, int initalLevel, bool endless = false)
{
GameId = gameId;
_lastObjectId = (uint)gameId * 100000;
Empty = true;
Players = new ConcurrentDictionary();
_worlds = new ConcurrentDictionary();
StartingWorldSno = WorldSno.pvp_caout_arena_01;// FIXME: track the player's save point and toss this stuff.
InitialMonsterLevel = initalLevel;
MonsterLevel = initalLevel;
QuestManager = new QuestManager(this);
CurrentAct = -1;
QuestsOrder = null;
CurrentQuest = -1;
CurrentStep = -1;
CurrentSideQuest = -1;
CurrentSideStep = -1;
QuestProgress = null;
SideQuestProgress = new Events(this);
var loopThread = new Thread(Update) { Name = "GameLoopThread", IsBackground = true }; ; // create the game update thread.
loopThread.Start();
WorldGenerator = new WorldGenerator(this);
GameDbSession = new GameDBSession();
LockdownTimer = TickTimer.WaitSeconds(this, 60f, new Action((q) =>
{
if (Empty || Players.IsEmpty)
{
Logger.Warn("All players disconnected, closing game session.");
Dispose();
GameManager.Games.Remove(GameId);
}
}));
}
///
/// Executes an action to all players in the game.
///
/// Action to execute
public void BroadcastPlayers(Action action)
{
foreach (var player in Players)
{
action(player.Key, player.Value);
}
}
///
/// Executes an action to all players in the game where the predicate is true.
///
/// Predicate to check
/// Action to execute
public void BroadcastPlayers(Func predicate, Action action)
{
foreach (var player in Players.Where(s=>predicate(s.Key, s.Value)))
{
action(player.Key, player.Value);
}
}
///
/// Executes an action to all worlds in the game.
///
/// Action to execute
public void BroadcastWorlds(Action action)
{
foreach (var world in Worlds)
{
action(world);
}
}
///
/// Executes an action to all worlds in the game.
///
/// Predicate to check
/// Action to execute
public void BroadcastWorlds(Func predicate, Action action)
{
foreach (var world in Worlds.Where(predicate))
{
action(world);
}
}
#region update & tick managment
private readonly object _updateLock = new();
public int MissedTicks = 0;
public bool UpdateInProgress = false;
///
/// The main game loop.
///
public void Update()
{
while (Working)
{
Stopwatch tickWatch = new Stopwatch();
tickWatch.Restart();
if (Players.Count == 0 && !Empty)
{
Logger.Info("All players disconnected, game session closed");
Dispose();
GameManager.Games.Remove(GameId);
return;
}
Interlocked.Add(ref _tickCounter, (TickRate + MissedTicks)); // +6 ticks per 100ms. Verified by setting LogoutTickTimeMessage.Ticks to 600 which eventually renders a 10 sec logout timer on client. /raist
MissedTicks = 0;
if (_updateEnabled && !Paused)
{
// Lock Game instance to prevent incoming messages from modifying state while updating
// only update worlds with active players in it - so mob brain()'s in empty worlds doesn't get called and take actions for nothing. /raist.
lock (_updateLock)
{
foreach (var pair in _worlds.Where(pair => pair.Value.HasPlayersIn))
{
try
{
pair.Value.Update(_tickCounter);
}
catch (Exception e)
{
Logger.WarnException(e, "update worlds exception: ");
}
}
PvPTimer?.Update(_tickCounter);
GlobalPvPTimer?.Update(_tickCounter);
LockdownTimer?.Update(_tickCounter);
QuestTimer?.Update(_tickCounter);
}
}
tickWatch.Stop();
Stopwatch calcWatch = new();
calcWatch.Start();
var compensation = (int)(UpdateFrequency - tickWatch.ElapsedMilliseconds); // the compensation value we need to sleep in order to get consistent 100 ms Game.Update().
if (tickWatch.ElapsedMilliseconds > UpdateFrequency)
{
if (tickWatch.ElapsedMilliseconds >= UpdateFrequency * 2)
{
Logger.Error($"took [{tickWatch.ElapsedMilliseconds}ms] more than Game.UpdateFrequency [{UpdateFrequency}ms].");
}
else if (tickWatch.ElapsedMilliseconds >= UpdateFrequency * 1.5)
{
Logger.Warn(
$"took [{tickWatch.ElapsedMilliseconds}ms] more than Game.UpdateFrequency [{UpdateFrequency}ms].");
}
else
{
Logger.Trace(
$"took [{tickWatch.ElapsedMilliseconds}ms] more than Game.UpdateFrequency [{UpdateFrequency}ms].");
}
compensation = (int)(UpdateFrequency - (tickWatch.ElapsedMilliseconds % UpdateFrequency));
MissedTicks = TickRate * (int)(tickWatch.ElapsedMilliseconds / UpdateFrequency);
}
calcWatch.Stop();
Thread.Sleep(Math.Max(0, compensation - (int)calcWatch.ElapsedMilliseconds)); // sleep until next Update().
}
}
#endregion
#region game-message handling & routing
///
/// Routers incoming GameMessage to it's proper consumer.
///
///
///
public void Route(GameClient client, GameMessage message)
{
_updateEnabled = false;
try
{
switch (message.Consumer)
{
case Consumers.Game:
Consume(client, message);
break;
case Consumers.Inventory:
client.Player.Inventory.Consume(client, message);
break;
case Consumers.Player:
client.Player.Consume(client, message);
break;
case Consumers.Conversations:
client.Player.Conversations.Consume(client, message);
break;
case Consumers.SelectedNPC:
if (client.Player.SelectedNPC != null)
client.Player.SelectedNPC.Consume(client, message);
break;
}
}
catch (Exception e)
{
Logger.WarnException(e, "Unhandled exception caught:");
}
finally
{
_updateEnabled = true;
}
}
public void Consume(GameClient client, GameMessage message)
{
lock (_updateLock)
{
if (message is PauseGameMessage) OnPause(client, (PauseGameMessage)message);
else if (message is RaiseGameDifficulty) RaiseDifficulty(client, (RaiseGameDifficulty)message);
else if (message is LowGameDifficulty) LowDifficulty(client, (LowGameDifficulty)message);
}
}
#endregion
#region player-handling
public void LowDifficulty(GameClient client, LowGameDifficulty message)
{
Difficulty--;
SetDifficulty(Difficulty);
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Misc.HandicapMessage(Opcodes.HandicapMessage) { Difficulty = (uint)Difficulty });
}
public void RaiseDifficulty(GameClient client, RaiseGameDifficulty message)
{
Difficulty++;
SetDifficulty(Difficulty);
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Misc.HandicapMessage(Opcodes.HandicapMessage) { Difficulty = (uint)Difficulty });
}
///
/// Allows a player to join the game.
///
/// The new player.
public void Enter(Player joinedPlayer)
{
if (IsHardcore && !joinedPlayer.Toon.DBToon.isHardcore)
{
return;
}
Task.Run(() =>
{
lock (Players)
{
Players.TryAdd(joinedPlayer.InGameClient, joinedPlayer);
// send all players in the game to new player that just joined (including him)
foreach (var pair in Players)
{
if (pair.Value.PlayerGroupIndex == joinedPlayer.PlayerGroupIndex)
SendNewPlayerMessage(joinedPlayer, pair.Value);
}
foreach (var pair in Players.Where(pair => pair.Value != joinedPlayer))
{
if (pair.Value.PlayerGroupIndex == joinedPlayer.PlayerGroupIndex)
SendNewPlayerMessage(pair.Value, joinedPlayer);
}
joinedPlayer.LoadShownTutorials();
joinedPlayer.LoadCrafterData();
joinedPlayer.LoadCurrencyData();
//joinedPlayer.LoadMailData();
joinedPlayer.LoadStashIconsData();
if (!PvP)
{
joinedPlayer.InGameClient.TickingEnabled = true; // it seems bnet-servers only start ticking after player is completely in-game. /raist
joinedPlayer.InGameClient.SendMessage(new GameSyncedDataMessage
{
SyncedData = new GameSyncedData
{
GameSyncedFlags = 6,
Act = CurrentAct, //act id
InitialMonsterLevel = InitialMonsterLevel, //InitialMonsterLevel
MonsterLevel = 0x6FEA8DF5, //MonsterLevel
RandomWeatherSeed = 0, //RandomWeatherSeed
OpenWorldMode = CurrentAct == 3000 ? -1 : 0, //OpenWorldMode
OpenWorldModeAct = -1, //OpenWorldModeAct
OpenWorldModeParam = -1, //OpenWorldModeParam
OpenWorldTransitionTime = 0, //OpenWorldTransitionTime
OpenWorldDefaultAct = -1, //OpenWorldDefaultAct
OpenWorldBonusAct = -1, //OpenWorldBonusAct
SNODungeonFinderLevelArea = 0, //SNODungeonFinderLevelArea
LootRunOpen = GameMode == Mode.Portals ? 0 : -1, //LootRunOpen //0 - Великий Портал
OpenLootRunLevel = 0, //OpenLootRunLevel
LootRunBossDead = 0, //LootRunBossDead
HunterPlayerIdx = -1, //HunterPlayerIdx
LootRunBossActive = 0, //LootRunBossActive
TieredLootRunFailed = 0, //TieredLootRunFailed
LootRunChallengeCompleted = 0, //LootRunChallengeCompleted
SetDungeonActive = -1, //SetDungeonActive
Pregame = 0, //Pregame
PregameEnd = 0, //PregameEnd
RoundStart = 0, //RoundStart
RoundEnd = 0, //RoundEnd
PVPGameOver = 0x0, //PVPGameOver
field_v273 = 0x0,
TeamWins = new[] { 0x0, 0x0 }, //TeamWins
TeamScore = new[] { 0x0, 0x0 }, //TeamScore
PVPGameResult = new[] { -1, -1 }, //PVPGameResult
PartyGuideHeroId = 0x0, //PartyGuideHeroId //new EntityId() { High = 0, Low = (long)this.Players.Values.First().Toon.PersistentID }
TiredRiftPaticipatingHeroID = new long[] { 0x0, 0x0, 0x0, 0x0 }, //TiredRiftPaticipatingHeroID
}
});
if ((CurrentStep == -1 || CurrentAct == 400) && (CurrentQuest == QuestsOrder[0]) && CurrentAct != 3000)
{
switch (CurrentAct)
{
case 0:
joinedPlayer.EnterWorld(StartingWorld.GetStartingPointById(0).Position);
break;
case 100:
joinedPlayer.EnterWorld(StartingWorld.GetStartingPointById(130).Position);
break;
case 200:
joinedPlayer.ChangeWorld(GetWorld(WorldSno.a3dun_hub_adria_tower_intro), GetWorld(WorldSno.a3dun_hub_adria_tower_intro).GetStartingPointById(206).Position);
break;
case 300:
joinedPlayer.ChangeWorld(GetWorld(WorldSno.a4dun_heaven_1000_monsters_fight_entrance), GetWorld(WorldSno.a4dun_heaven_1000_monsters_fight_entrance).StartingPoints.First().Position);
break;
case 400:
joinedPlayer.ChangeWorld(GetWorld(WorldSno.x1_westmarch_overlook_d), GetWorld(WorldSno.x1_westmarch_overlook_d).StartingPoints.First().Position);
break;
default:
break;
}
joinedPlayer.PlayCutscene(0);
}
else
{
joinedPlayer.EnterWorld(StartPosition);
}
}
else
{
joinedPlayer.EnterWorld(StartingWorld.GetStartingPointById(288 + joinedPlayer.PlayerIndex).Position);
}
Empty = false;
foreach (var portal in StartingWorld.GetActorsBySNO(ActorSno._x1_openworld_lootrunportal, ActorSno._x1_openworld_tiered_rifts_portal, ActorSno._x1_openworld_tiered_rifts_challenge_portal))
{
portal.Destroy();
}
ClientSystem.GameServer.GSBackend.PlayerJoined(GameId);
//joinedPlayer.InGameClient.SendTick();
/*
if (this.Players.Count < 2)
{
int? hirelingId = joinedPlayer.Toon.DBToon.ActiveHireling;
if (hirelingId != null)
{
Hireling hireling = null;
switch (hirelingId)
{
case 1:
hireling = new Templar(joinedPlayer.World, 52693, new TagMap());
hireling.GBHandle.GBID = StringHashHelper.HashItemName("Templar");
break;
case 2:
hireling = new Scoundrel(joinedPlayer.World, 52694, new TagMap());
hireling.GBHandle.GBID = StringHashHelper.HashItemName("Scoundrel");
break;
case 3:
hireling = new Enchantress(joinedPlayer.World, 4482, new TagMap());
hireling.GBHandle.GBID = StringHashHelper.HashItemName("Enchantress");
break;
default:
hireling = new Templar(joinedPlayer.World, 52693, new TagMap());
hireling.GBHandle.GBID = StringHashHelper.HashItemName("Templar");
break;
}
hireling.SetUpAttributes(joinedPlayer);
hireling.GBHandle.Type = 4;
hireling.Attributes[GameAttribute.Pet_Creator] = joinedPlayer.PlayerIndex;
hireling.Attributes[GameAttribute.Pet_Type] = 0;
hireling.Attributes[GameAttribute.Pet_Owner] = joinedPlayer.PlayerIndex;
hireling.Attributes[GameAttribute.Untargetable] = false;
hireling.Attributes[GameAttribute.NPC_Is_Escorting] = true;
hireling.RotationW = joinedPlayer.RotationW;
hireling.RotationAxis = joinedPlayer.RotationAxis;
if (hireling.Brain == null)
hireling.Brain = new AISystem.Brains.HirelingBrain(hireling, joinedPlayer);
hireling.Brain.DeActivate();
hireling.EnterWorld(joinedPlayer.Position);
hireling.Brain = new HirelingBrain(hireling, joinedPlayer);
(hireling.Brain as HirelingBrain).Activate();
joinedPlayer.ActiveHireling = hireling;
}
}
else
/**/
{
foreach (var plr in Players.Values)
if (plr.ActiveHireling != null)
{
plr.ActiveHireling.Dismiss();
plr.ActiveHireling = null;
}
}
/**/
if (!PvP && !((CurrentStep == -1) && (CurrentQuest == QuestsOrder[0])))
{
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage()
{
snoQuest = CurrentQuest,
snoLevelArea = -1,
StepID = CurrentStep,
DisplayButton = true,
Failed = false
});
}
if (joinedPlayer.PlayerIndex == 0)
joinedPlayer.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Misc.HandicapMessage(Opcodes.HandicapMessage)
{
Difficulty = (uint)Difficulty
});
UpdateLevel();
joinedPlayer.NotifyMaintenance();
if (CurrentAct == 3000 && !joinedPlayer.InGameClient.OpenWorldDefined)
{
joinedPlayer.InGameClient.OpenWorldDefined = true;
joinedPlayer.InGameClient.SendMessage(new ActTransitionMessage
{
Act = 3000,
OnJoin = true
});
foreach (var bounty in QuestManager.Bounties)
{
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage()
{
snoQuest = bounty.BountySNOid,
snoLevelArea = bounty.LevelArea,
StepID = -1,
DisplayButton = true,
Failed = false
});
}
CurrentQuest = 0x0004C46D;
QuestManager.Advance();
joinedPlayer.InGameClient.SendMessage(new IntDataMessage(Opcodes.DungeonFinderSeedMessage)
{
Field0 = 0x3E0FC64C
});
joinedPlayer.InGameClient.SendMessage(new IntDataMessage(Opcodes.DungeonFinderParticipatingPlayerCount)
{
Field0 = 0
});
joinedPlayer.InGameClient.SendMessage(new FloatDataMessage(Opcodes.DungeonFinderProgressMessage)
{
Field0 = 0
});
joinedPlayer.InGameClient.SendMessage(new SNODataMessage(Opcodes.DungeonFinderSetTimedEvent)
{
Field0 = 0
});
joinedPlayer.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Misc.SavePointInfoMessage()
{
snoLevelArea = joinedPlayer.CurrentScene.Specification.SNOLevelAreas[0],//102362,
});
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage
{
snoQuest = 0x0005727C,
snoLevelArea = -1,
StepID = -1,
DisplayButton = true,
Failed = false
});
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage
{
snoQuest = 0x00057282,
snoLevelArea = -1,
StepID = -1,
DisplayButton = true,
Failed = false
});
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage
{
snoQuest = 0x00057284,
snoLevelArea = -1,
StepID = -1,
DisplayButton = true,
Failed = false
});
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage
{
snoQuest = 0x00057287,
snoLevelArea = -1,
StepID = -1,
DisplayButton = true,
Failed = false
});
joinedPlayer.InGameClient.SendMessage(new QuestUpdateMessage
{
snoQuest = 0x00057289,
snoLevelArea = -1,
StepID = -1,
DisplayButton = true,
Failed = false
});
}
}
});
}
public void UpdateLevel()
{
if (Players.Count < 1)
return;
MonsterLevel = Players.Values.Select(p => p.Level).Max();
foreach (var wld in _worlds)
foreach (var monster in wld.Value.Monsters)
monster.UpdateStats();
}
public void EnablePerfTest(int charId)
{
}
private readonly int[] _questsOrderA1 = new[] { 87700, 72095, 72221, 72061, 117779, 72738, 73236, 72546, 72801, 136656 };
private readonly int[] _questsOrderA2 = new[] { 80322, 93396, 74128, 57331, 78264, 78266, 57335, 57337, 121792, 57339 };
private readonly int[] _questsOrderA3 = new[] { 93595, 93684, 93697, 203595, 101756, 101750, 101758 };
private readonly int[] _questsOrderA4 = new[] { 112498, 113910, 114795, 114901 };
private readonly int[] _questsOrderA5 = new[] { 251355, 284683, 285098, 257120, 263851, 273790, 269552, 273408 };
private readonly int[] _questsOrderOpenWorld = new[] { 312429 };
public void SetQuestProgress(int currQuest, int step)
{
if (PvP) return;
if (!QuestSetup)
{
QuestManager.SetQuests();
DestinationEnterQuest = currQuest;
DestinationEnterQuestStep = step;
Logger.Trace("SetQuestProgress: quest {0}, step {1}", currQuest, step);
CurrentQuest = QuestsOrder[0];
CurrentStep = -1;
if (CurrentAct is 3000)
{
QuestManager.Quests[CurrentQuest].Steps[-1].OnAdvance.Invoke();
return;
}
if (!(currQuest == QuestsOrder[0] && step == -1))
{
if (currQuest == -1 && step == -1)
{
currQuest = CurrentQuest;
QuestManager.Quests[currQuest].Steps[-1].OnAdvance.Invoke();
return;
}
QuestManager.AdvanceTo(currQuest, step);
return;
}
if (CurrentQuest == QuestsOrder[0] && CurrentStep == -1)
QuestManager.Quests[CurrentQuest].Steps[-1].OnAdvance.Invoke();
}
}
public void SetGameMode(Mode mode)
{
GameMode = mode;
switch (GameMode)
{
case Mode.Portals:
QuestsOrder = new int[] { -1 };
StartingWorldSno = WorldSno.weekly_challenge_hub;
QuestProgress = new QuestRegistry(this);
break;
}
}
public void SetAct(int act)
{
if (PvP)
{
CurrentAct = 0;
QuestsOrder = _questsOrderA1;
QuestProgress = new QuestRegistry(this);
StartingWorldSno = WorldSno.pvp_caout_arena_01;
return;
}
if (CurrentAct != act)
{
CurrentAct = act;
switch (act)
{
case 0:
QuestsOrder = _questsOrderA1;
StartingWorldSno = WorldSno.trout_town;
QuestProgress = new ActI(this);
break;
case 100:
QuestsOrder = _questsOrderA2;
StartingWorldSno = WorldSno.caout_refugeecamp;
QuestProgress = new ActII(this);
break;
case 200:
QuestsOrder = _questsOrderA3;
StartingWorldSno = WorldSno.a3dun_hub_keep;
QuestProgress = new ActIII(this);
break;
case 300:
QuestsOrder = _questsOrderA4;
StartingWorldSno = WorldSno.a4dun_heaven_hub_keep;
QuestProgress = new ActIV(this);
break;
case 400:
QuestsOrder = _questsOrderA5;
StartingWorldSno = WorldSno.x1_westmarch_hub;
QuestProgress = new ActV(this);
break;
case 3000:
QuestsOrder = _questsOrderOpenWorld;
StartingWorldSno = WorldSno.x1_tristram_adventure_mode_hub;
QuestProgress = new OpenWorld(this);
QuestManager.SetBounties();
break;
default:
QuestsOrder = _questsOrderA1;
StartingWorldSno = WorldSno.trout_town;
QuestProgress = new QuestRegistry(this);
break;
}
}
}
public void ChangeAct(int act)
{
foreach(var plr in Players.Values)
plr.InGameClient.SendMessage(new SimpleMessage(Opcodes.LoadingWarping));
SetAct(act);
CurrentQuest = QuestsOrder[0];
CurrentStep = -1;
QuestManager.ReloadQuests();
OpenedWaypoints = new List() { };
foreach (var plr in Players)
{
plr.Key.SendMessage(new ActTransitionMessage
{
Act = act,
OnJoin = true, //with cutscenes
});
plr.Value.UpdateHeroState();
if (act == 3000)
{
plr.Key.SendMessage(new IntDataMessage(Opcodes.DungeonFinderSeedMessage)
{
Field0 = 0x3E0FC64C
});
plr.Key.SendMessage(new IntDataMessage(Opcodes.DungeonFinderParticipatingPlayerCount)
{
Field0 = 0
});
plr.Key.SendMessage(new FloatDataMessage(Opcodes.DungeonFinderProgressMessage)
{
Field0 = 0
});
plr.Key.SendMessage(new SNODataMessage(Opcodes.DungeonFinderSetTimedEvent)
{
Field0 = 0
});
}
plr.Key.SendMessage(new GameSyncedDataMessage
{
SyncedData = new GameSyncedData
{
GameSyncedFlags = IsSeasoned ? IsHardcore ? 6 : 4 : IsHardcore == true ? 4 : 6,
Act = Math.Min(CurrentAct, 3000), //act id
InitialMonsterLevel = InitialMonsterLevel, //InitialMonsterLevel
MonsterLevel = 0x7044248F, //MonsterLevel
RandomWeatherSeed = 0, //RandomWeatherSeed
OpenWorldMode = CurrentAct == 3000 ? -1 : 0, //OpenWorldMode
OpenWorldModeAct = -1, //OpenWorldModeAct
OpenWorldModeParam = -1, //OpenWorldModeParam
OpenWorldTransitionTime = 200, //OpenWorldTransitionTime
OpenWorldDefaultAct = -1, //OpenWorldDefaultAct
OpenWorldBonusAct = -1, //OpenWorldBonusAct
SNODungeonFinderLevelArea = 0, //SNODungeonFinderLevelArea
LootRunOpen = GameMode == Mode.Portals ? 0 : -1, //LootRunOpen //0 - Великий Портал
OpenLootRunLevel = 0, //OpenLootRunLevel
LootRunBossDead = 0, //LootRunBossDead
HunterPlayerIdx = -1, //HunterPlayerIdx
LootRunBossActive = 0, //LootRunBossActive
TieredLootRunFailed = 0, //TieredLootRunFailed
LootRunChallengeCompleted = 0, //LootRunChallengeCompleted
SetDungeonActive = -1, //SetDungeonActive
Pregame = 0, //Pregame
PregameEnd = 0, //PregameEnd
RoundStart = 0, //RoundStart
RoundEnd = 0, //RoundEnd
PVPGameOver = 0x0, //PVPGameOver
field_v273 = 0x0,
TeamWins = new[] { 0x0, 0x0 }, //TeamWins
TeamScore = new[] { 0x0, 0x0 }, //TeamScore
PVPGameResult = new[] { -1, -1 }, //PVPGameResult
PartyGuideHeroId = 0x0, //PartyGuideHeroId //new EntityId() { High = 0, Low = (long)this.Players.Values.First().Toon.PersistentID }
TiredRiftPaticipatingHeroID = new long[] { 0x0, 0x0, 0x0, 0x0 }, //TiredRiftPaticipatingHeroID
}
});
switch (act)
{
case 0:
plr.Value.ChangeWorld(StartingWorld, StartingWorld.GetStartingPointById(0).Position);
break;
case 100:
plr.Value.ChangeWorld(StartingWorld, StartingWorld.GetStartingPointById(130).Position);
break;
case 200:
plr.Value.ChangeWorld(GetWorld(WorldSno.a3dun_hub_adria_tower_intro), GetWorld(WorldSno.a3dun_hub_adria_tower_intro).GetStartingPointById(206).Position);
break;
case 300:
plr.Value.ChangeWorld(GetWorld(WorldSno.a4dun_heaven_1000_monsters_fight_entrance), GetWorld(WorldSno.a4dun_heaven_1000_monsters_fight_entrance).StartingPoints.First().Position);
break;
case 400:
plr.Value.ChangeWorld(GetWorld(WorldSno.x1_westmarch_overlook_d), GetWorld(WorldSno.x1_westmarch_overlook_d).StartingPoints.First().Position);
break;
default:
break;
}
for (int i = 0; i < 10; i++)
{
plr.Key.SendMessage(new PlayerLoadoutTabIconMessage(Opcodes.PlayerLoadoutTabIconMessage)
{
Field0 = i,
TabIcon = i
});
}
plr.Key.SendMessage(new RevealTeamMessage() { Team = 0, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 1, TeamFlags = 0, TeamColoring = 2 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 2, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 3, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 4, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 5, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 6, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 7, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 8, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 9, TeamFlags = 0, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 10, TeamFlags = 2, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 11, TeamFlags = 2, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 12, TeamFlags = 2, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 13, TeamFlags = 2, TeamColoring = -1 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 14, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 15, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 16, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 17, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 18, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 19, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 20, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 21, TeamFlags = 0, TeamColoring = 0 });
plr.Key.SendMessage(new RevealTeamMessage() { Team = 22, TeamFlags = 0, TeamColoring = 0 });
plr.Value.PlayCutscene(0);
}
try
{
QuestManager.Quests[QuestsOrder[0]].Steps[-1].OnAdvance.Invoke();
}
catch (Exception e)
{
Logger.WarnException(e, "onAdvance():");
}
}
public void UpdateLevel(int level)
{
MonsterLevel = level;
foreach (var wld in _worlds)
foreach (var monster in wld.Value.Monsters)
monster.UpdateStats();
}
public void SetDifficulty(int diff)
{
Difficulty = diff;
if (Difficulty < 0) Difficulty = 0;
if (Difficulty > 19) Difficulty = 19;
diff++;
if (diff > 0)
{
var handicapLevels = (GameBalance)MPQStorage.Data.Assets[SNOGroup.GameBalance][256027].Data;
HpModifier = handicapLevels.HandicapLevelTables[diff].HPMod;
DmgModifier = handicapLevels.HandicapLevelTables[diff].DmgMod;
XpModifier = (1f + handicapLevels.HandicapLevelTables[diff].XPMod);
GoldModifier = (1f + handicapLevels.HandicapLevelTables[diff].GoldMod);
}
foreach (var wld in _worlds)
foreach (var monster in wld.Value.Monsters)
monster.UpdateStats();
foreach(var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Misc.HandicapMessage(Opcodes.HandicapMessage) { Difficulty = (uint)Difficulty });
}
public void UnlockTeleport(int waypointId)
{
OpenedWaypoints.Add(waypointId);
}
public Actor GetHearthPortal()
{
return StartingWorld.Actors.Values.Where(x => x.SNO == ActorSno._hearthportal).First();
}
private void OnPause(GameClient client, PauseGameMessage message)
{
if (Players.Count == 1)
{
Logger.Trace("Game state is paused: {0}", message.Field0);
Players.First().Value.Attributes[GameAttribute.Disabled] = message.Field0;
Players.First().Value.Attributes[GameAttribute.Immobolize] = message.Field0;
//this.Players.First().Value.Attributes[GameAttribute.Stunned] = message.Field0;
Players.First().Value.Attributes.BroadcastChangedIfRevealed();
//this.Players.First().Key.TickingEnabled = !message.Field0;
Paused = message.Field0;
Players.First().Key.SendMessage(new FreezeGameMessage
{
Field0 = message.Field0
});
}
}
///
/// Sends NewPlayerMessage to players when a new player joins the game.
///
/// Target player to send the message.
/// The new joined player.
private void SendNewPlayerMessage(Player target, Player joinedPlayer)
{
target.InGameClient.SendMessage(new NewPlayerMessage
{
PlayerIndex = joinedPlayer.PlayerIndex,
NewToonId = (long)joinedPlayer.Toon.D3EntityID.IdLow,
GameAccountId = new GameAccountHandle() { ID = (uint)joinedPlayer.Toon.GameAccount.BnetEntityId.Low, Program = 0x00004433, Region = 1 },
ToonName = joinedPlayer.Toon.Name,
Team = 0x00000002,
Class = joinedPlayer.ClassSNO,
snoActorPortrait = joinedPlayer.Toon.DBToon.Cosmetic4,
Level = joinedPlayer.Toon.Level,
AltLevel = (ushort)joinedPlayer.Toon.ParagonLevel,
HighestHeroSoloRiftLevel = 0,
StateData = joinedPlayer.GetStateData(),
JustJoined = false,
Field9 = 0x77EA0000,
ActorID = joinedPlayer.DynamicID(target),
});
if (PvP)
target.InGameClient.SendMessage(new RevealTeamMessage
{
Team = joinedPlayer.PlayerIndex + 2,
TeamFlags = 0,
TeamColoring = 0
});
target.InGameClient.SendMessage(joinedPlayer.GetPlayerBanner()); // send player banner proto - D3.GameMessage.PlayerBanner
}
public void BroadcastMessage(string message)
{
lock (Players)
{
foreach (var plr in Players.Keys)
plr.SendMessage(new BroadcastTextMessage() { Field0 = message });
}
}
public void StartPvPRound()
{
CurrentPvPRound++;
var winner = Players.Values.Where(p => !p.Dead).FirstOrDefault();
if (winner != null && CurrentPvPRound > 1)
{
BroadcastMessage("Round is over! Winner: " + winner.Toon.Name);
if (winner.Attributes[GameAttribute.TeamID] == 2)
RedTeamWins++;
else
BlueTeamWins++;
}
if (CurrentPvPRound > 5 || Math.Abs(RedTeamWins - BlueTeamWins) > (5 - CurrentPvPRound))
{
BroadcastMessage("Battle is over!");
try
{
var totalWinner = Players.Values.Where(p => p.Attributes[GameAttribute.TeamID] == (RedTeamWins > BlueTeamWins ? 2 : 3)).FirstOrDefault();
BroadcastMessage("Winner: " + totalWinner.Toon.Name);
}
catch { Logger.Warn("Exception on FindWinner()"); }
//foreach (var player in this.Players.Values)
//player.World.BuffManager.AddBuff(player, player, new Mooege.Core.GS.Powers.Implementations.PVPRoundEndBuff(TickTimer.WaitSeconds(this, 1200.0f)));
foreach (var plr in Players.Keys)
plr.SendMessage(new DataIDDataMessage(Opcodes.PVPArenaWin) { Field0 = (RedTeamWins == BlueTeamWins ? 0 : (RedTeamWins < BlueTeamWins ? 2 : 3)) });
return;
}
if (CurrentPvPRound == 1)
{
GlobalPvPTimer = TickTimer.WaitSeconds(this, 600f, new Action((z) =>
{
BroadcastMessage("Time is up, battle is over!");
if (RedTeamWins == BlueTeamWins)
{
BroadcastMessage("Draw!");
}
else
{
var totalWinner = Players.Values.Where(p => p.Attributes[GameAttribute.TeamID] == (RedTeamWins > BlueTeamWins ? 2 : 3)).FirstOrDefault();
BroadcastMessage("Winner: " + totalWinner.Toon.Name);
}
//foreach (var player in this.Players.Values)
//player.World.BuffManager.AddBuff(player, player, new Mooege.Core.GS.Powers.Implementations.PVPRoundEndBuff(TickTimer.WaitSeconds(this, 1200.0f)));
foreach (var plr in Players.Keys)
plr.SendMessage(new DataIDDataMessage(Opcodes.PVPArenaWin) { Field0 = (RedTeamWins == BlueTeamWins ? 0 : (RedTeamWins < BlueTeamWins ? 2 : 3)) });
}));
}
PvPTimer = TickTimer.WaitSeconds(this, 3f, new Action((x) =>
{
foreach (var player in Players.Values)
{
player.Revive(player.CheckPointPosition);
player.GeneratePrimaryResource(player.Attributes[GameAttribute.Resource_Max_Total, player.Attributes[GameAttribute.Resource_Type_Primary]]);
player.World.BuffManager.AddBuff(player, player, new PowerSystem.Implementations.PVPSkirmishBuff(TickTimer.WaitSeconds(this, 15.0f)));
}
BroadcastMessage("Round " + CurrentPvPRound + ". Battle will commence in 15 seconds!");
BroadcastMessage("Score: " + RedTeamWins + ":" + BlueTeamWins);
PvPTimer = TickTimer.WaitSeconds(this, 15f, new Action((y) =>
{
BroadcastMessage("Fight!");
foreach (var player in Players.Keys)
{
//player.SendMessage(new FightAnnounceMessage());
}
}));
}));
}
///
/// Disposes all memory defore destroying game.
///
public void Dispose()
{
Working = false;
if (Players.Count > 0)
foreach (var plr in Players.Keys)
plr.Connection.Disconnect();
_worlds.Clear();
Thread.Sleep(1000);
GameDbSession.SessionDispose();
GameManager.Games.Remove(GameId);
}
public void TeleportToBossEncounter(int snoId)
{
foreach (var player in Players.Values)
{
player.ClearDoorAnimations();
}
Paused = true;
/*foreach (var player in this.Players.Values)
{
player.InGameClient.SendMessage(new ACDTranslateSyncMessage()
{
ActorId = player.DynamicID(player),
Position = player.Position
});
/*player.InGameClient.SendMessage(new FreezeGameMessage
{
Field0 = true
});
}*/
var encAsset = (DiIiS_NA.Core.MPQ.FileFormats.BossEncounter)MPQStorage.Data.Assets[SNOGroup.BossEncounter][snoId].Data;
World encWorld = GetWorld((WorldSno)encAsset.Worlds[0]);
Logger.Debug("TeleportToBossEncounter, worldId: {0}", encAsset.Worlds[0]);
Vector3D startPoint = null;
switch (snoId)
{
case 168925: //CainIntro
startPoint = encWorld.GetStartingPointById(172).Position;
break;
case 159592: //Leoric
startPoint = encWorld.GetStartingPointById(23).Position;
break;
case 181436: //SpiderQueen
startPoint = encWorld.GetStartingPointById(30).Position;
break;
case 159591: //Cain Death
startPoint = encWorld.GetStartingPointById(172).Position;
break;
case 158915: //Butcher
startPoint = encWorld.GetStartingPointById(191).Position;
break;
case 195234: //Maghda
startPoint = encWorld.StartingPoints.First().Position;
break;
case 226716: //SiegeBreaker
startPoint = encWorld.GetStartingPointById(172).Position;
break;
case 188021: //Cydaea
startPoint = encWorld.GetStartingPointById(172).Position;
break;
case 182960: //Iskatu
startPoint = encWorld.GetStartingPointById(287).Position;
break;
case 220541: //Imperius_Spire
startPoint = encWorld.GetStartingPointById(172).Position;
break;
case 161280: //Diablo
startPoint = encWorld.GetStartingPointById(172).Position;
break;
default:
startPoint = encWorld.StartingPoints.First().Position;
break;
}
var proximity = new RectangleF(startPoint.X - 1f, startPoint.Y - 1f, 2f, 2f);
var scenes = encWorld.QuadTree.Query(proximity);
if (scenes.Count == 0) return; // cork (is it real?)
var scene = scenes[0]; // Parent scene /fasbat
if (scenes.Count == 2) // What if it's a subscene?
{
if (scenes[1].ParentChunkID != 0xFFFFFFFF)
scene = scenes[1];
}
var levelArea = scene.Specification.SNOLevelAreas[0];
foreach (var gPlayer in Players)
{
if (gPlayer.Value.World == encWorld)
gPlayer.Value.Teleport(startPoint);
else
gPlayer.Value.ChangeWorld(encWorld, startPoint);
}
Paused = false;
//handling quest triggers
if (QuestProgress.QuestTriggers.ContainsKey(levelArea)) //EnterLevelArea
{
var trigger = QuestProgress.QuestTriggers[levelArea];
if (trigger.triggerType == QuestStepObjectiveType.EnterLevelArea)
{
try
{
trigger.questEvent.Execute(encWorld); // launch a questEvent
}
catch (Exception e)
{
Logger.WarnException(e, "questEvent()");
}
}
}
//Исполнение скриптов катсцены
if (GameMode == Mode.Campaign)
switch (snoId)
{
case 168925: //CainIntro
//if (this.CurrentAct == 0)
Task.Delay(1000).ContinueWith(delegate
{
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Camera.CameraCriptedSequenceStartMessage() { Activate = true });
Task.Delay(1000).ContinueWith(delegate
{
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Camera.CameraFocusMessage()
{
ActorID = (int)encWorld.GetActorBySNO(ActorSno._test_cainintro_greybox_bridge_trout_tempworking).DynamicID(plr), Duration = 1f, Snap = false
});
Actor cainRun = null;
Actor cainQuest = null;
//Убираем лишнего каина.
foreach (var cain in encWorld.GetActorsBySNO(ActorSno._cain_intro))
if (cain.Position.Y > 140)
{
cain.SetVisible(false);
foreach (var plr in Players.Values) cain.Unreveal(plr);
cainQuest = cain;
}
else
{
cain.SetVisible(true);
foreach (var plr in Players.Values) cain.Reveal(plr);
cainRun = cain;
}
//Скелеты
var skeletons = encWorld.GetActorsBySNO(ActorSno._skeleton_cain);
//Камни
//var Rocks = encWorld.GetActorsBySNO(176);
//Берем позицию для леорика, а самого на мороз
Vector3D fakeLeoricPosition = new Vector3D(0f, 0f, 0f);
foreach (var fake in encWorld.GetActorsBySNO(ActorSno._skeletonking_ghost))
{
fakeLeoricPosition = fake.Position;
fake.Destroy();
}
//Берем каина
var firstPoint = new Vector3D(120.92718f, 121.26151f, 0.099973306f);
var secondPoint = new Vector3D(120.73298f, 160.61829f, 0.31863004f);
var sceletonPoint = new Vector3D(120.11514f, 140.77332f, 0.31863004f);
var firstfacingAngle = ActorSystem.Movement.MovementHelpers.GetFacingAngle(cainRun, firstPoint);
var secondfacingAngle = ActorSystem.Movement.MovementHelpers.GetFacingAngle(firstPoint, secondPoint);
var thirdfacingAngle = ActorSystem.Movement.MovementHelpers.GetFacingAngle(secondPoint, fakeLeoricPosition);
//Подготовления завершены - НАЧИНАЕМ ТЕАТР=)
Task.Delay(3000).ContinueWith(delegate
{
cainRun.Move(firstPoint, firstfacingAngle);
foreach (var plr in Players.Values)
plr.Conversations.StartConversation(80920);//Запуск диалога - 80920 //Фраза Каина, бежит первым до начала мостика, оглядывается. //"Cain_Run_CainIntro", 81080 - Анимация
Task.Delay(5000).ContinueWith(delegate
{
foreach (var skeleton in skeletons)
{
skeleton.Move(sceletonPoint, ActorSystem.Movement.MovementHelpers.GetFacingAngle(skeleton, sceletonPoint));
}
cainRun.Move(secondPoint, secondfacingAngle);
Task.Delay(7000).ContinueWith(delegate
{
//foreach (var rock in Rocks)
//{
//{[1013103213, {[Actor] [Type: Gizmo] SNOId:78439 GlobalId: 1013103213 Position: x:119.54008 y:140.65799 z:-4.535186 Name: Test_CainIntro_greybox_bridge_trOut_TempWorking}]}
//Обрушиваем мостик //EffectGroup "CainIntro_shake", 81546
var bridge = encWorld.GetActorBySNO(ActorSno._test_cainintro_greybox_bridge_trout_tempworking);
bridge.PlayAnimation(5, (AnimationSno)bridge.AnimationSet.TagMapAnimDefault[AnimationSetKeys.DeathDefault]);
//}
foreach (var skeleton in skeletons)
{
//Убиваем скелетов
skeleton.Destroy();
}
});
Task.Delay(5000).ContinueWith(delegate
{
cainRun.Move(secondPoint, thirdfacingAngle);
//(Должен быть диалог Король скилет.)
var leoric = encWorld.SpawnMonster(ActorSno._skeletonking_ghost, fakeLeoricPosition);
leoric.PlayActionAnimation(AnimationSno.skeletonking_ghost_spawn);
Task.Delay(1000).ContinueWith(delegate
{
foreach (var plr in Players.Values)
plr.Conversations.StartConversation(17692); //Фраза Леорика
Task.Delay(14000).ContinueWith(delegate
{
//Leoric.PlayActionAnimation(9854); //Леорик призывает скелетов
leoric.PlayActionAnimation(AnimationSno.skeletonking_ghost_despawn); //Себаса
Task.Delay(1000).ContinueWith(delegate
{
foreach (var plr in Players.Values)
{
plr.InGameClient.SendMessage(new BoolDataMessage(Opcodes.CameraTriggerFadeToBlackMessage) { Field0 = true });
plr.InGameClient.SendMessage(new SimpleMessage(Opcodes.CameraSriptedSequenceStopMessage) { });
}
cainQuest.SetVisible(true);
cainRun.SetVisible(false);
foreach (var fake in encWorld.GetActorsBySNO(ActorSno._skeletonking_ghost))
{
fakeLeoricPosition = fake.Position;
fake.Destroy();
}
});
});
});
});
});
});
});
});
break;
case 159592: //Leoric
break;
case 158915: //ButcherLair
//if (this.CurrentAct == 0)
var butcher = encWorld.GetActorBySNO(ActorSno._butcher);
if (butcher != null)
(butcher as Monster).Brain.DeActivate();
else
{
butcher = encWorld.SpawnMonster(ActorSno._butcher, new Vector3D { X = 93.022f, Y = 89.86f, Z = 0.1f });
(butcher as Monster).Brain.DeActivate();
}
Task.Delay(1000).ContinueWith(delegate
{
//Butcher - 3526
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Camera.CameraCriptedSequenceStartMessage() { Activate = true });
Task.Delay(1000).ContinueWith(delegate
{
if (butcher != null)
(butcher as Monster).Brain.DeActivate();
foreach (var plr in Players.Values)
plr.InGameClient.SendMessage(new MessageSystem.Message.Definitions.Camera.CameraFocusMessage() { ActorID = (int)butcher.DynamicID(plr), Duration = 1f, Snap = false });
foreach (var plr in Players.Values)
plr.Conversations.StartConversation(211980); //ФРЭШ МИТ
// StartConversation(ButcherLair, 211980);
Task.Delay(3000).ContinueWith(delegate
{
foreach (var plr in Players.Values)
{
plr.InGameClient.SendMessage(new BoolDataMessage(Opcodes.CameraTriggerFadeToBlackMessage) { Field0 = true });
plr.InGameClient.SendMessage(new SimpleMessage(Opcodes.CameraSriptedSequenceStopMessage) { });
}
Task.Delay(1500).ContinueWith(delegate
{
(butcher as Monster).Brain.Activate();
});
});
});
});
break;
}
foreach (var bounty in QuestManager.Bounties)
bounty.CheckLevelArea(levelArea);
CurrentEncounter.AcceptedPlayers = 0;
CurrentEncounter.Activated = false;
}
public void AcceptBossEncounter()
{
CurrentEncounter.AcceptedPlayers++;
if (CurrentEncounter.AcceptedPlayers >= Players.Count)
TeleportToBossEncounter(CurrentEncounter.SnoId);
}
public void DeclineBossEncounter()
{
CurrentEncounter.Activated = false;
CurrentEncounter.AcceptedPlayers = 0;
}
public void AddOnLoadWorldAction(WorldSno worldSno, Action action)
{
Logger.Trace("AddOnLoadWorldAction: {0}", worldSno);
if (Players.Values.Any(p => p.World?.SNO == worldSno))
{
action.Invoke();
}
else
{
if (!OnLoadWorldActions.ContainsKey(worldSno))
OnLoadWorldActions.Add(worldSno, new List());
OnLoadWorldActions[worldSno].Add(action);
}
}
public void AddOnLoadSceneAction(int sceneSno, Action action)
{
Logger.Trace("AddOnLoadSceneAction: {0}", sceneSno);
if (!OnLoadSceneActions.ContainsKey(sceneSno))
OnLoadSceneActions.Add(sceneSno, new List());
OnLoadSceneActions[sceneSno].Add(action);
}
#endregion
#region world collection
public void AddWorld(World world)
{
if (world.SNO == WorldSno.__NONE || WorldExists(world.SNO))
Logger.Error(String.Format("World has an invalid SNO or was already being tracked (ID = {0}, SNO = {1})", world.GlobalID, world.SNO));
else
_worlds.TryAdd(world.SNO, world);
}
public void RemoveWorld(World world)
{
World removed;
if (world.SNO == WorldSno.__NONE || !WorldExists(world.SNO))
Logger.Error(String.Format("World has an invalid SNO or was not being tracked (ID = {0}, SNO = {1})", world.GlobalID, world.SNO));
else
_worlds.TryRemove(world.SNO, out removed);
}
public World GetWorld(WorldSno worldSno)
{
if (worldSno == WorldSno.__NONE)
return null;
World world;
if (CurrentAct != 3000 && worldSno == WorldSno.x1_tristram_adventure_mode_hub) //fix for a1 Tristram
worldSno = WorldSno.trout_town;
if (!WorldExists(worldSno)) // If it doesn't exist, try to load it
{
//Task loading = Task.Run(() => {world = this.WorldGenerator.Generate(worldSNO);});
//if (!loading.Wait(TimeSpan.FromSeconds(30)))
//Logger.Warn("Failed to generate world with sno: {0}", worldSNO);
//bool loaded = false;
//Action action = (() => {
// world = this.WorldGenerator.Generate(worldSNO);
//});
//this.WorldGenerator.Actions.Enqueue(action);
//while (!loaded && this.Working)
//{
/*var timer = new System.Timers.Timer(1);
timer.Elapsed += (src, args) => { if (!this.WorldGenerator.Actions.Contains(action)) loaded = true; };
timer.AutoReset = false;
timer.Start();*/
//Task.Delay(1000).ContinueWith(t => { if (!this.WorldGenerator.Actions.Contains(action)) loaded = true; }).Wait();
//}
world = WorldGenerator.Generate(worldSno);
if (world == null) Logger.Warn("Failed to generate world with sno: {0}", worldSno);
}
_worlds.TryGetValue(worldSno, out world);
return world;
}
public bool WorldExists(WorldSno worldSno)
{
return _worlds.ContainsKey(worldSno);
}
public bool WorldCleared(WorldSno worldSno)
{
return _worlds[worldSno].Actors.Values.OfType().Count(m => m.OriginalLevelArea != -1 && !m.Dead) < 5;
}
///
/// Returns World with given Waypoint id.
///
/// The id of the WayPoint
///
public World GetWayPointWorldById(int id)
{
Logger.MethodTrace($"id {id}");
bool isOpenWorld = CurrentAct == 3000;
ImmutableArray actData;
if (isOpenWorld)
actData = ((Act)MPQStorage.Data.Assets[SNOGroup.Act][70015].Data).WayPointInfo
.Union(((Act)MPQStorage.Data.Assets[SNOGroup.Act][70016].Data).WayPointInfo)
.Union(((Act)MPQStorage.Data.Assets[SNOGroup.Act][70017].Data).WayPointInfo)
.Union(((Act)MPQStorage.Data.Assets[SNOGroup.Act][70018].Data).WayPointInfo)
.Union(((Act)MPQStorage.Data.Assets[SNOGroup.Act][236915].Data).WayPointInfo)
.Where(w => w.SNOWorld != -1).ToImmutableArray();
else
{
actData = ((Act)MPQStorage.Data.Assets[SNOGroup.Act][CurrentActSnoId].Data).WayPointInfo.ToImmutableArray();
}
var wayPointInfo = actData.Where(w => w.Flags == 3 || (isOpenWorld ? (w.Flags == 2) : (w.Flags == 1))).ToList();
//Logger.Debug("GetWayPointWorldById: world id {0}", wayPointInfo[id].SNOWorld);
return GetWorld((WorldSno)wayPointInfo[id].SNOWorld);
}
#endregion
}
}