Skip to content

Commit

Permalink
[Game] Changed gimmick implementation so that Sunken Treasure works (A…
Browse files Browse the repository at this point in the history
…AEmu#1129)

* [Game] Small Gimmicks rewrite to support sunken treasure

- Updated some of the offset routines for Gimmick spawning.
- Moved Gimmick reference to Unit instead of NPC so that a character can spawn one.
- Added `GetSpawnGimmickEffect` helper function.
- Added basic `/gimmick spawn` command.
- Improved Gimmick Tick handling.
- Added support for Gimmick LifeTime and SkillDelay functions.
- Added gimmick movement handler classes
- Changed how gimmick movement is handled for Elevators.
- Smoother gimmick movement by calculating "fake" velocity.
- Fixed issue of Gimmicks not showing on the client-side.
- Fixed some typos.
- Changed skill effect targeting to no longer allow duplicates. Also made it so you can only have one valid target if targeting a position.
- Updated `AddToCharacter` function to do special handling for Gimmicks.

* [Game] Added /findobject command and changed loot

- Small change to loot by loot-group (group 1 now also only drops 1 of them)
- Added a GM command to find nearby doodads, NPCs or gimmicks.
- Added a failsafe to skill targeting.
- Fixed a possible issue with the leap controller.
  • Loading branch information
ZeromusXYZ authored Dec 29, 2024
1 parent e8cc780 commit 41e1251
Show file tree
Hide file tree
Showing 29 changed files with 768 additions and 252 deletions.
6 changes: 6 additions & 0 deletions AAEmu.Game/AAEmu.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@
<Compile Update="Scripts\Commands\DeSpawnAll.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
<Compile Update="Scripts\Commands\Around.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
<Compile Update="Scripts\Commands\FindObject.cs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
125 changes: 37 additions & 88 deletions AAEmu.Game/Core/Managers/GimmickManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

using AAEmu.Commons.Utils;
using AAEmu.Game.Core.Managers.Id;
using AAEmu.Game.Core.Packets.G2C;
using AAEmu.Game.Models.Game.Faction;
using AAEmu.Game.Models.Game.Gimmicks;
using AAEmu.Game.Models.Game.Units;
Expand All @@ -28,6 +27,7 @@ public class GimmickManager : Singleton<GimmickManager>
private const double Delay = 50;
//private const double DelayInit = 1;
private Task GimmickTickTask { get; set; }
private DateTime LastCheck { get; set; } = DateTime.MinValue;

public bool Exist(uint templateId)
{
Expand Down Expand Up @@ -70,12 +70,15 @@ public Gimmick Create(uint objectId, uint templateId, GimmickSpawner spawner)
}

gimmick.ObjId = objectId > 0 ? objectId : ObjectIdManager.Instance.GetNextId();
gimmick.GimmickId = (ushort)GimmickIdManager.Instance.GetNextId();
gimmick.Spawner = spawner;
gimmick.TemplateId = templateId;
gimmick.Faction = new SystemFaction();
gimmick.Transform.ApplyWorldSpawnPosition(spawner.Position);
gimmick.Vel = new Vector3(0f, 0f, 0f);
gimmick.Rot = new Quaternion(spawner.RotationX, spawner.RotationY, spawner.RotationZ, spawner.RotationW);
var spawnRotation = new Quaternion(spawner.RotationX, spawner.RotationY, spawner.RotationZ, spawner.RotationW);
// Apply Gimmick setting's rotation to the GameObject.Transform
gimmick.Transform.Local.ApplyFromQuaternion(spawnRotation);
gimmick.ModelParams = new UnitCustomModelParams();
gimmick.SetScale(spawner.Scale);

Expand All @@ -86,11 +89,36 @@ public Gimmick Create(uint objectId, uint templateId, GimmickSpawner spawner)
}

gimmick.Spawn(); // adding to the world
_activeGimmicks.Add(gimmick.ObjId, gimmick);
AddActiveGimmick(gimmick);

return gimmick;
}

public void AddActiveGimmick(Gimmick gimmick)
{
// Attach movement handlers based on settings
if ((gimmick.TemplateId == 0) && (gimmick.EntityGuid > 0))
{
// Elevators defined in gimmick_spawns.json
gimmick.MovementHandler = new GimmickMovementElevator(gimmick);
}
else
// TODO: Add decent Physics system to handle movement
if (gimmick.TemplateId == 37)
{
// Recovered Treasure Chest
gimmick.MovementHandler = new GimmickMovementFloatToSurface(gimmick);
}

gimmick.Time = (uint)(DateTime.UtcNow - DateTime.UtcNow.Date).TotalMilliseconds;
_activeGimmicks.TryAdd(gimmick.ObjId, gimmick);
}

public void RemoveActiveGimmick(Gimmick gimmick)
{
_activeGimmicks.Remove(gimmick.ObjId);
}

/// <summary>
/// Create for spawning projectiles
/// </summary>
Expand All @@ -103,6 +131,7 @@ public Gimmick Create(uint templateId)

var gimmick = new Gimmick();
gimmick.ObjId = ObjectIdManager.Instance.GetNextId();
gimmick.GimmickId = (ushort)GimmickIdManager.Instance.GetNextId();
gimmick.Spawner = new GimmickSpawner();
gimmick.Template = template;
gimmick.TemplateId = template.Id;
Expand Down Expand Up @@ -181,31 +210,20 @@ public void Load()
public void Initialize()
{
Logger.Warn("GimmickTickTask: Started");

//GimmickTickTask = new GimmickTickStartTask();
//TaskManager.Instance.Schedule(GimmickTickTask, TimeSpan.FromMinutes(DelayInit));
TickManager.Instance.OnTick.Subscribe(GimmickTick, TimeSpan.FromMilliseconds(Delay), true);
}

/// <summary>
/// Callback function for global gimmick ticks
/// </summary>
/// <param name="delta"></param>
private void GimmickTick(TimeSpan delta)
{
var activeGimmicks = GetActiveGimmicks();
foreach (var gimmick in activeGimmicks)
{
GimmickTick(gimmick);
gimmick.GimmickTick(delta);
}

//TaskManager.Instance.Schedule(GimmickTickTask, TimeSpan.FromMilliseconds(Delay));
}
internal void GimmickTick()
{
var activeGimmicks = GetActiveGimmicks();
foreach (var gimmick in activeGimmicks)
{
GimmickTick(gimmick);
}

TaskManager.Instance.Schedule(GimmickTickTask, TimeSpan.FromMilliseconds(Delay));
}

private Gimmick[] GetActiveGimmicks()
Expand All @@ -215,74 +233,5 @@ private Gimmick[] GetActiveGimmicks()
return _activeGimmicks.Values.ToArray();
}
}

private static void GimmickTick(Gimmick gimmick)
{
if (gimmick.TimeLeft > 0)
return;

const float maxVelocity = 4.5f;
const float deltaTime = 0.05f;
const float movingDistance = 0.27f;

var position = gimmick.Transform.World.Position;
var velocityZ = gimmick.Vel.Z;

var middleTarget = position with { Z = gimmick.Spawner.MiddleZ };
var topTarget = position with { Z = gimmick.Spawner.TopZ };
var bottomTarget = position with { Z = gimmick.Spawner.BottomZ };

var isMovingDown = gimmick.moveDown;
var isInMiddleZ = gimmick.Spawner.MiddleZ > 0;

if (isInMiddleZ)
{
if (position.Z < gimmick.Spawner.MiddleZ && gimmick.Vel.Z >= 0 && !isMovingDown)
MoveAlongZAxis(gimmick, ref position, middleTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
else if (position.Z < gimmick.Spawner.TopZ && gimmick.Vel.Z >= 0 && !isMovingDown)
MoveAlongZAxis(gimmick, ref position, topTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
else if (position.Z > gimmick.Spawner.MiddleZ && gimmick.Vel.Z <= 0 && isMovingDown)
MoveAlongZAxis(gimmick, ref position, middleTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
else
MoveAlongZAxis(gimmick, ref position, bottomTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
}
else
{
if (position.Z < gimmick.Spawner.TopZ && gimmick.Vel.Z >= 0)
MoveAlongZAxis(gimmick, ref position, topTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
else
MoveAlongZAxis(gimmick, ref position, bottomTarget, maxVelocity, deltaTime, movingDistance, ref velocityZ, ref isMovingDown);
}

gimmick.Transform.Local.SetHeight(position.Z);

var isMoving = Math.Abs(gimmick.Vel.Z) > 0;
gimmick.Time += 50;
gimmick.BroadcastPacket(new SCGimmickMovementPacket(gimmick), true);

if (isMoving)
return;

gimmick.WaitTime = DateTime.UtcNow.AddSeconds(gimmick.Spawner.WaitTime);
gimmick.moveDown = !gimmick.moveDown;
}

private static void MoveAlongZAxis(Gimmick gimmick, ref Vector3 position, Vector3 target, float maxVelocity, float deltaTime, float movingDistance, ref float velocityZ, ref bool isMovingDown)
{
var distance = target - position;
velocityZ = maxVelocity * Math.Sign(distance.Z);
movingDistance = velocityZ * deltaTime;

if (Math.Abs(distance.Z) >= Math.Abs(movingDistance))
{
position.Z += movingDistance;
gimmick.Vel = gimmick.Vel with { Z = velocityZ };
}
else
{
position.Z = target.Z;
gimmick.Vel = Vector3.Zero;
}
}
}

18 changes: 18 additions & 0 deletions AAEmu.Game/Core/Managers/Id/GimmickIdManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using AAEmu.Game.Utils;

namespace AAEmu.Game.Core.Managers.Id;

public class GimmickIdManager : IdManager
{
private static GimmickIdManager _instance;
private const uint FirstId = 0x0001;
private const uint LastId = 0xFFFE;
private static readonly uint[] Exclude = [];
private static readonly string[,] ObjTables = { { } };

public static GimmickIdManager Instance => _instance ?? (_instance = new GimmickIdManager());

public GimmickIdManager() : base("GimmickIdManager", FirstId, LastId, ObjTables, Exclude)
{
}
}
22 changes: 22 additions & 0 deletions AAEmu.Game/Core/Managers/SkillManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,4 +1833,26 @@ public ActabilityType GetSkillActAbility(uint skillId)
return ActabilityType.None;
return (ActabilityType)value.ActabilityGroupId;
}

/// <summary>
/// Gets the first spawn effect for a given Gimmick TemplateId
/// </summary>
/// <param name="gimmickTemplateId"></param>
/// <returns></returns>
public SpawnGimmickEffect GetSpawnGimmickEffect(uint gimmickTemplateId)
{
if (!_effects.TryGetValue("SpawnGimmickEffect", out var spawnGimmickEffects))
return null;

foreach (var effect in spawnGimmickEffects.Values)
{
if (effect is not SpawnGimmickEffect spawnGimmickEffect)
continue;
if (spawnGimmickEffect.GimmickId == gimmickTemplateId)
return spawnGimmickEffect;
}

return null;
}

}
6 changes: 3 additions & 3 deletions AAEmu.Game/Core/Managers/World/SpawnManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ public void Load()
entry++;
if (spawner.UnitId != 0 && !GimmickManager.Instance.Exist(spawner.UnitId))
{
Logger.Warn($"Gimmick Template {spawner.UnitId} (file entry {entry}) doesn't exist - {jsonFileName}");
continue; // TODO ... so mb warn here?
Logger.Error($"Gimmick Template {spawner.UnitId} (file entry {entry}) doesn't exist - {jsonFileName}");
continue;
}
spawner.Id = _nextId;
//spawner.UnitId = 0; // EntityGuid is used for elevators
// spawner.EntityGuid = 0; // EntityGuid is used for elevators
spawner.Position.WorldId = world.Id;
spawner.Position.ZoneId = WorldManager.Instance.GetZoneId(world.Id, spawner.Position.X, spawner.Position.Y);
if (gimmickSpawners.TryAdd(_nextId, spawner))
Expand Down
30 changes: 29 additions & 1 deletion AAEmu.Game/Core/Managers/World/WorldManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,10 @@ public Character GetCharacterById(uint id)
return null;
}

/// <summary>
/// Adds a GameObject to the list of existing objects on the server
/// </summary>
/// <param name="obj"></param>
public void AddObject(GameObject obj)
{
if (obj == null)
Expand Down Expand Up @@ -871,6 +875,11 @@ public void AddObject(GameObject obj)
_mates.TryAdd(mate.ObjId, mate);
}

/// <summary>
/// Removes a GameObject from the list of "existing" objects on the server
/// </summary>
/// <param name="ObjId"></param>
/// <returns></returns>
public bool RemoveObject(uint ObjId)
{
if (ObjId == 0)
Expand Down Expand Up @@ -911,6 +920,12 @@ public bool RemoveObject(uint ObjId)

return res;
}

/// <summary>
/// Removes a GameObject from the list of "existing" objects on the server
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public void RemoveObject(GameObject obj)
{
if (obj == null)
Expand Down Expand Up @@ -938,6 +953,10 @@ public void RemoveObject(GameObject obj)
_mates.TryRemove(mate.ObjId, out _);
}

/// <summary>
/// Adds or updates a GameObject of its region object list
/// </summary>
/// <param name="obj"></param>
public void AddVisibleObject(GameObject obj)
{
if (obj == null)
Expand Down Expand Up @@ -976,7 +995,7 @@ public void AddVisibleObject(GameObject obj)

// Add this obj to the new region
region.AddObject(obj);
// Update it's region
// Update its region
obj.Region = region;

// remove the obj from the old region
Expand All @@ -992,6 +1011,10 @@ public void AddVisibleObject(GameObject obj)
//Logger.Warn($" objects={_objects.Count}, doodads={_doodads.Count}, npcs={_npcs.Count}, characters={_characters.Count}");
}

/// <summary>
/// Removes a GameObject from its region object list
/// </summary>
/// <param name="obj"></param>
public static void RemoveVisibleObject(GameObject obj)
{
if (obj?.Region == null)
Expand Down Expand Up @@ -1218,6 +1241,11 @@ public List<Doodad> GetAllDoodads()
return _doodads.Values.ToList();
}

public List<Gimmick> GetAllGimmicks()
{
return _gimmicks.Values.ToList();
}

public List<Slave> GetAllSlavesFromWorld(uint worldId)
{
return _slaves.Values.Where(n => n.Transform.WorldId == worldId).ToList();
Expand Down
22 changes: 7 additions & 15 deletions AAEmu.Game/Core/Packets/G2C/SCGimmickJointsBrokenPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,19 @@

namespace AAEmu.Game.Core.Packets.G2C;

public class SCGimmickJointsBrokenPacket : GamePacket
public class SCGimmickJointsBrokenPacket(Gimmick[] gimmicks) : GamePacket(SCOffsets.SCGimmickJointsBrokenPacket, 1)
{
private readonly Gimmick[] _gimmick;
private readonly int _jointId;
private readonly int _epicentr;

public SCGimmickJointsBrokenPacket(Gimmick[] gimmick) : base(SCOffsets.SCGimmickJointsBrokenPacket, 1)
{
_gimmick = gimmick;
_jointId = 0;
_epicentr = 0;
}
private const int JointId = 0;
private const int Epicenter = 0;

public override PacketStream Write(PacketStream stream)
{
stream.Write((byte)_gimmick.Length); // TODO max length 200
foreach (var gimmick in _gimmick)
stream.Write((byte)gimmicks.Length); // TODO max length 200
foreach (var gimmick in gimmicks)
{
stream.Write(gimmick.ObjId); // gimmickId
stream.Write(_jointId); // jointId
stream.Write(_epicentr); // epicentr
stream.Write(JointId); // jointId
stream.Write(Epicenter); // epicenter
}

return stream;
Expand Down
Loading

0 comments on commit 41e1251

Please sign in to comment.