diff --git a/Source/Framework/Constants/VehicleConst.cs b/Source/Framework/Constants/VehicleConst.cs
index aac5d0cb2..e000fcb07 100644
--- a/Source/Framework/Constants/VehicleConst.cs
+++ b/Source/Framework/Constants/VehicleConst.cs
@@ -61,4 +61,12 @@ namespace Framework.Constants
AdjustAimPower = 0x800, // LuaIsvehicleaimpoweradjustable
FixedPosition = 0x200000 // Used for cannons, when they should be rooted
}
+
+ public enum VehicleExitParameters
+ {
+ VehicleExitParamNone = 0, // provided parameters will be ignored
+ VehicleExitParamOffset = 1, // provided parameters will be used as offset values
+ VehicleExitParamDest = 2, // provided parameters will be used as absolute destination
+ VehicleExitParamMax
+ }
}
diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs
index ad3fdab02..61eb2fc24 100644
--- a/Source/Game/Entities/Unit/Unit.cs
+++ b/Source/Game/Entities/Unit/Unit.cs
@@ -1025,6 +1025,7 @@ namespace Game.Entities
return;
// This should be done before dismiss, because there may be some aura removal
+ VehicleSeatAddon seatAddon = m_vehicle.GetSeatAddonForSeatOfPassenger(this);
Vehicle vehicle = m_vehicle.RemovePassenger(this);
Player player = ToPlayer();
@@ -1049,7 +1050,18 @@ namespace Game.Entities
// Set exit position to vehicle position and use the current orientation
pos = vehicle.GetBase().GetPosition();
pos.SetOrientation(GetOrientation());
+
+ // To-do: snap this hook out of existance
Global.ScriptMgr.ModifyVehiclePassengerExitPos(this, vehicle, pos);
+
+ // Change exit position based on seat entry addon data
+ if (seatAddon != null)
+ {
+ if (seatAddon.ExitParameter == VehicleExitParameters.VehicleExitParamOffset)
+ pos.RelocateOffset(new Position(seatAddon.ExitParameterX, seatAddon.ExitParameterY, seatAddon.ExitParameterZ, seatAddon.ExitParameterO));
+ else if (seatAddon.ExitParameter == VehicleExitParameters.VehicleExitParamDest)
+ pos.Relocate(new Position(seatAddon.ExitParameterX, seatAddon.ExitParameterY, seatAddon.ExitParameterZ, seatAddon.ExitParameterO));
+ }
}
float height = pos.GetPositionZ() + vehicle.GetBase().GetCollisionHeight();
diff --git a/Source/Game/Entities/Vehicle.cs b/Source/Game/Entities/Vehicle.cs
index c7115ab61..b02e009c6 100644
--- a/Source/Game/Entities/Vehicle.cs
+++ b/Source/Game/Entities/Vehicle.cs
@@ -45,7 +45,8 @@ namespace Game.Entities
VehicleSeatRecord veSeat = CliDB.VehicleSeatStorage.LookupByKey(seatId);
if (veSeat != null)
{
- Seats.Add((sbyte)i, new VehicleSeat(veSeat));
+ VehicleSeatAddon addon = Global.ObjectMgr.GetVehicleSeatAddon(seatId);
+ Seats.Add((sbyte)i, new VehicleSeat(veSeat, addon));
if (veSeat.CanEnterOrExit())
++UsableSeatNum;
}
@@ -253,6 +254,20 @@ namespace Game.Entities
return seat;
}
+ ///
+ /// Gets the vehicle seat addon data for the seat of a passenger
+ ///
+ /// Identifier for the current seat user
+ /// The seat addon data for the currently used seat of a passenger
+ public VehicleSeatAddon GetSeatAddonForSeatOfPassenger(Unit passenger)
+ {
+ foreach (var pair in Seats)
+ if (!pair.Value.IsEmpty() && pair.Value.Passenger.Guid == passenger.GetGUID())
+ return pair.Value.SeatAddon;
+
+ return null;
+ }
+
void InstallAccessory(uint entry, sbyte seatId, bool minion, byte type, uint summonTime)
{
// @Prevent adding accessories when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.)
@@ -625,6 +640,7 @@ namespace Game.Entities
Passenger.RemoveAurasByType(AuraType.Mounted);
VehicleSeatRecord veSeat = Seat.Value.SeatInfo;
+ VehicleSeatAddon veSeatAddon = Seat.Value.SeatAddon;
Player player = Passenger.ToPlayer();
if (player != null)
@@ -644,10 +660,12 @@ namespace Game.Entities
if (veSeat.HasFlag(VehicleSeatFlags.DisableGravity))
Passenger.SetDisableGravity(true);
- if (Seat.Value.SeatInfo.HasFlag(VehicleSeatFlags.PassengerNotSelectable))
- Passenger.AddUnitFlag(UnitFlags.NotSelectable);
+ float o = veSeatAddon != null ? veSeatAddon.SeatOrientationOffset : 0.0f;
+ float x = veSeat.AttachmentOffset.X;
+ float y = veSeat.AttachmentOffset.Y;
+ float z = veSeat.AttachmentOffset.Z;
- Passenger.m_movementInfo.transport.pos.Relocate(veSeat.AttachmentOffset.X, veSeat.AttachmentOffset.Y, veSeat.AttachmentOffset.Z);
+ Passenger.m_movementInfo.transport.pos.Relocate(x, y, z, o);
Passenger.m_movementInfo.transport.time = 0;
Passenger.m_movementInfo.transport.seat = Seat.Key;
Passenger.m_movementInfo.transport.guid = Target.GetBase().GetGUID();
@@ -669,8 +687,8 @@ namespace Game.Entities
MoveSplineInit init = new(Passenger);
init.DisableTransportPathTransformations();
- init.MoveTo(veSeat.AttachmentOffset.X, veSeat.AttachmentOffset.Y, veSeat.AttachmentOffset.Z, false, true);
- init.SetFacing(0.0f);
+ init.MoveTo(x, y, z, false, true);
+ init.SetFacing(o);
init.SetTransportEnter();
Passenger.GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleBoard, MovementGeneratorPriority.Highest);
@@ -736,15 +754,17 @@ namespace Game.Entities
public class VehicleSeat
{
- public VehicleSeat(VehicleSeatRecord seatInfo)
+ public VehicleSeat(VehicleSeatRecord seatInfo, VehicleSeatAddon seatAddon)
{
SeatInfo = seatInfo;
+ SeatAddon = seatAddon;
Passenger.Reset();
}
public bool IsEmpty() { return Passenger.Guid.IsEmpty(); }
public VehicleSeatRecord SeatInfo;
+ public VehicleSeatAddon SeatAddon;
public PassengerInfo Passenger;
}
@@ -769,4 +789,24 @@ namespace Game.Entities
{
public TimeSpan DespawnDelay;
}
+
+ public class VehicleSeatAddon
+ {
+ public float SeatOrientationOffset;
+ public float ExitParameterX;
+ public float ExitParameterY;
+ public float ExitParameterZ;
+ public float ExitParameterO;
+ public VehicleExitParameters ExitParameter;
+
+ public VehicleSeatAddon(float orientatonOffset, float exitX, float exitY, float exitZ, float exitO, byte param)
+ {
+ SeatOrientationOffset = orientatonOffset;
+ ExitParameterX = exitX;
+ ExitParameterY = exitY;
+ ExitParameterZ = exitZ;
+ ExitParameterO = exitO;
+ ExitParameter = (VehicleExitParameters)param;
+ }
+ }
}
diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs
index 61f743df0..81b291f00 100644
--- a/Source/Game/Globals/ObjectManager.cs
+++ b/Source/Game/Globals/ObjectManager.cs
@@ -10505,7 +10505,6 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Vehicle Template Accessories in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
-
public void LoadVehicleAccessories()
{
uint oldMSTime = Time.GetMSTime();
@@ -10546,6 +10545,57 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Vehicle Accessories in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
+ public void LoadVehicleSeatAddon()
+ {
+ uint oldMSTime = Time.GetMSTime();
+
+ _vehicleSeatAddonStore.Clear(); // needed for reload case
+
+ // 0 1 2 3 4 5 6
+ SQLResult result = DB.World.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
+ if (result.IsEmpty())
+ {
+ Log.outError(LogFilter.ServerLoading, "Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
+ return;
+ }
+
+ uint count = 0;
+ do
+ {
+ uint seatID = result.Read(0);
+ float orientation = result.Read(1);
+ float exitX = result.Read(2);
+ float exitY = result.Read(3);
+ float exitZ = result.Read(4);
+ float exitO = result.Read(5);
+ byte exitParam = result.Read(6);
+
+ if (!CliDB.VehicleSeatStorage.ContainsKey(seatID))
+ {
+ Log.outError(LogFilter.Sql, $"Table `vehicle_seat_addon`: SeatID: {seatID} does not exist in VehicleSeat.dbc. Skipping entry.");
+ continue;
+ }
+
+ // Sanitizing values
+ if (orientation > MathF.PI * 2)
+ {
+ Log.outError(LogFilter.Sql, $"Table `vehicle_seat_addon`: SeatID: {seatID} is using invalid angle offset value ({orientation}). Set Value to 0.");
+ orientation = 0.0f;
+ }
+
+ if (exitParam >= (byte)VehicleExitParameters.VehicleExitParamMax)
+ {
+ Log.outError(LogFilter.Sql, $"Table `vehicle_seat_addon`: SeatID: {seatID} is using invalid exit parameter value ({exitParam}). Setting to 0 (none).");
+ continue;
+ }
+
+ _vehicleSeatAddonStore[seatID] = new VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
+
+ ++count;
+ } while (result.NextRow());
+
+ Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} Vehicle Seat Addon entries in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
+ }
public VehicleTemplate GetVehicleTemplate(Vehicle veh)
{
@@ -10565,7 +10615,11 @@ namespace Game
// Otherwise return entry-based
return _vehicleTemplateAccessoryStore.LookupByKey(veh.GetCreatureEntry());
}
-
+ public VehicleSeatAddon GetVehicleSeatAddon(uint seatId)
+ {
+ return _vehicleSeatAddonStore.LookupByKey(seatId);
+ }
+
#region Fields
//General
Dictionary CypherStringStorage = new();
@@ -10684,6 +10738,7 @@ namespace Game
Dictionary _vehicleTemplateStore = new();
MultiMap _vehicleTemplateAccessoryStore = new();
MultiMap _vehicleAccessoryStore = new();
+ Dictionary _vehicleSeatAddonStore = new();
//Locales
Dictionary _creatureLocaleStorage = new();
diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs
index f01f42f9a..240d4cf6e 100644
--- a/Source/Game/Server/WorldManager.cs
+++ b/Source/Game/Server/WorldManager.cs
@@ -706,6 +706,9 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Accessories...");
Global.ObjectMgr.LoadVehicleAccessories(); // must be after LoadCreatureTemplates() and LoadNPCSpellClickSpells()
+ Log.outInfo(LogFilter.ServerLoading, "Loading Vehicle Seat Addon Data...");
+ Global.ObjectMgr.LoadVehicleSeatAddon(); // must be after loading DBC
+
Log.outInfo(LogFilter.ServerLoading, "Loading SpellArea Data..."); // must be after quest load
Global.SpellMgr.LoadSpellAreas();
diff --git a/Source/Scripts/World/NpcSpecial.cs b/Source/Scripts/World/NpcSpecial.cs
index 3d4a59bc1..960031721 100644
--- a/Source/Scripts/World/NpcSpecial.cs
+++ b/Source/Scripts/World/NpcSpecial.cs
@@ -2350,32 +2350,6 @@ namespace Scripts.World.NpcSpecial
creature.SetDisplayFromModel(0);
}
}
-
- [Script]
- class npc_traveler_tundra_mammoth_exit_pos : UnitScript
- {
- public npc_traveler_tundra_mammoth_exit_pos() : base("npc_traveler_tundra_mammoth_exit_pos") { }
-
- public override void ModifyVehiclePassengerExitPos(Unit passenger, Vehicle vehicle, Position pos)
- {
- if (passenger.IsCreature())
- {
- switch (passenger.GetEntry())
- {
- // Right side
- case CreatureIds.DrixBlackwrench:
- case CreatureIds.Gnimo:
- pos.RelocateOffset(new Position(-2.0f, -2.0f, 0.0f, 0.0f));
- break;
- // Left side
- case CreatureIds.Mojodishu:
- case CreatureIds.HakmudOfArgus:
- pos.RelocateOffset(new Position(-2.0f, 2.0f, 0.0f, 0.0f));
- break;
- }
- }
- }
- }
class CastFoodSpell : BasicEvent
{
diff --git a/sql/updates/world/master/2021_12_21_00_world_2020_02_08_01_world.sql b/sql/updates/world/master/2021_12_21_00_world_2020_02_08_01_world.sql
new file mode 100644
index 000000000..00bf33bb8
--- /dev/null
+++ b/sql/updates/world/master/2021_12_21_00_world_2020_02_08_01_world.sql
@@ -0,0 +1,18 @@
+--
+DROP TABLE IF EXISTS `vehicle_seat_addon`;
+CREATE TABLE `vehicle_seat_addon` (
+ `SeatEntry` INT(4) UNSIGNED NOT NULL COMMENT 'VehicleSeatEntry.dbc identifier',
+ `SeatOrientation` FLOAT(6) DEFAULT 0 COMMENT 'Seat Orientation override value',
+ `ExitParamX` FLOAT(10) DEFAULT 0,
+ `ExitParamY` FLOAT(10) DEFAULT 0,
+ `ExitParamZ` FLOAT(10) DEFAULT 0,
+ `ExitParamO` FLOAT(10) DEFAULT 0,
+ `ExitParamValue` TINYINT(1) DEFAULT 0,
+ PRIMARY KEY (`SeatEntry`)
+);
+
+INSERT INTO `vehicle_seat_addon` (`SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue`) VALUES
+(2764, 0, -2, 2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 2
+(2765, 0, -2, -2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 3
+(2767, 0, -2, 2, 0, 0, 1), -- Traveler's Tundra Mammoth Seat 2
+(2768, 0, -2, -2, 0, 0, 1); -- Traveler's Tundra Mammoth Seat 3