Add front-passenger handbrake control; record Workshop 0.0.132
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
// Front-seat passenger assistance is an extra held input, not a parking-brake
|
||||
// toggle. The driver's persistent brake, gears and service brake stay native.
|
||||
class ERR_PassengerHandbrake
|
||||
{
|
||||
static bool Beside(vector delta)
|
||||
{
|
||||
float lateral = Math.AbsFloat(delta[0]);
|
||||
return lateral >= 0.35 && lateral <= 1.7 && Math.AbsFloat(delta[1]) <= 0.5 && Math.AbsFloat(delta[2]) <= 0.65;
|
||||
}
|
||||
static bool FrontSeat(IEntity vehicle, BaseCompartmentSlot slot)
|
||||
{
|
||||
if (!vehicle || !slot || slot.GetType() != ECompartmentType.CARGO) return false;
|
||||
CarControllerComponent car = CarControllerComponent.Cast(vehicle.FindComponent(CarControllerComponent));
|
||||
if (!car) return false;
|
||||
BaseCompartmentSlot pilot = car.GetPilotCompartmentSlot();
|
||||
if (!pilot || pilot == slot || !slot.GetOwner() || slot.GetOwner().GetRootParent() != vehicle.GetRootParent()) return false;
|
||||
PointInfo passengerPoint = slot.GetPassengerPointInfo(), pilotPoint = pilot.GetPassengerPointInfo();
|
||||
if (!passengerPoint || !pilotPoint) return false;
|
||||
vector passenger = vehicle.CoordToLocal(passengerPoint.GetWorldTransformAxis(3));
|
||||
vector driver = vehicle.CoordToLocal(pilotPoint.GetWorldTransformAxis(3));
|
||||
return Beside(passenger - driver);
|
||||
}
|
||||
static IEntity VehicleFor(IEntity actor)
|
||||
{
|
||||
if (!ERR_Actors.CanFight(actor)) return null;
|
||||
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(actor.FindComponent(CompartmentAccessComponent));
|
||||
if (!access) return null;
|
||||
BaseCompartmentSlot slot = access.GetCompartment();
|
||||
IEntity vehicle = CompartmentAccessComponent.GetVehicleIn(actor);
|
||||
if (!slot || slot.GetOccupant() != actor || !FrontSeat(vehicle, slot)) return null;
|
||||
return vehicle;
|
||||
}
|
||||
static bool Requested(IEntity driver)
|
||||
{
|
||||
IEntity vehicle = CompartmentAccessComponent.GetVehicleIn(driver);
|
||||
if (!vehicle) return false;
|
||||
CarControllerComponent car = CarControllerComponent.Cast(vehicle.FindComponent(CarControllerComponent));
|
||||
if (!car || !car.GetPilotCompartmentSlot() || car.GetPilotCompartmentSlot().GetOccupant() != driver) return false;
|
||||
// Native held-handbrake input releases the parking latch. A parked car
|
||||
// already satisfies the passenger's demand, so leave that latch untouched.
|
||||
if (car.GetPersistentHandBrake()) return false;
|
||||
BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(vehicle.FindComponent(BaseCompartmentManagerComponent));
|
||||
if (!manager) return false;
|
||||
array<BaseCompartmentSlot> slots = {};
|
||||
manager.GetCompartments(slots);
|
||||
foreach (BaseCompartmentSlot slot : slots)
|
||||
{
|
||||
IEntity actor = slot.GetOccupant();
|
||||
if (!actor || actor == driver) continue;
|
||||
SCR_CharacterControllerComponent character = SCR_CharacterControllerComponent.Cast(actor.FindComponent(SCR_CharacterControllerComponent));
|
||||
if (character && character.ERR_PassengerBrakeHeld() && VehicleFor(actor) == vehicle) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_PlayerController
|
||||
{
|
||||
protected SCR_CharacterControllerComponent m_ERRBrakingPassenger;
|
||||
// No caller-supplied body, seat or vehicle: authority derives all three.
|
||||
void ERR_SendPassengerBrake(bool held)
|
||||
{
|
||||
if (this != GetGame().GetPlayerController()) return;
|
||||
if (Replication.IsServer()) ERR_RequestPassengerBrake(held);
|
||||
else Rpc(ERR_RequestPassengerBrake, held);
|
||||
}
|
||||
[RplRpc(RplChannel.Reliable, RplRcver.Server)]
|
||||
protected void ERR_RequestPassengerBrake(bool held)
|
||||
{
|
||||
if (!Replication.IsServer()) return;
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
IEntity actor = GetControlledEntity();
|
||||
SCR_CharacterControllerComponent character;
|
||||
if (actor) character = SCR_CharacterControllerComponent.Cast(actor.FindComponent(SCR_CharacterControllerComponent));
|
||||
if (m_ERRBrakingPassenger && (m_ERRBrakingPassenger != character || !held)) m_ERRBrakingPassenger.ERR_SetPassengerBrake(false);
|
||||
m_ERRBrakingPassenger = null;
|
||||
if (!character) return;
|
||||
bool allowed = held && mode && mode.Active() && mode.Players() && mode.Players().CurrentActor(actor) && ERR_PassengerHandbrake.VehicleFor(actor);
|
||||
character.ERR_SetPassengerBrake(allowed);
|
||||
if (allowed) m_ERRBrakingPassenger = character;
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_CharacterControllerComponent
|
||||
{
|
||||
[RplProp()] protected bool m_ERRPassengerBrake;
|
||||
protected int m_ERRPassengerBrakeHeartbeat;
|
||||
protected bool m_ERRPassengerBrakeSent;
|
||||
protected int m_ERRPassengerBrakeNextSend;
|
||||
protected IEntity m_ERRPassengerBrakeHintVehicle;
|
||||
|
||||
bool ERR_PassengerBrakeHeld() { return m_ERRPassengerBrake; }
|
||||
void ERR_SetPassengerBrake(bool held)
|
||||
{
|
||||
if (!Replication.IsServer()) return;
|
||||
m_ERRPassengerBrakeHeartbeat = System.GetTickCount();
|
||||
if (held == m_ERRPassengerBrake) return;
|
||||
m_ERRPassengerBrake = held;
|
||||
Replication.BumpMe();
|
||||
GetGame().GetCallqueue().Remove(ERR_ExpirePassengerBrake);
|
||||
if (held) GetGame().GetCallqueue().CallLater(ERR_ExpirePassengerBrake, 250, true);
|
||||
}
|
||||
protected void ERR_ExpirePassengerBrake()
|
||||
{
|
||||
if (!Replication.IsServer() || !m_ERRPassengerBrake) return;
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
int age = System.GetTickCount() - m_ERRPassengerBrakeHeartbeat;
|
||||
if (!mode || !mode.Active() || age < 0 || age > 1000 || !ERR_PassengerHandbrake.VehicleFor(GetOwner())) ERR_SetPassengerBrake(false);
|
||||
}
|
||||
void ~SCR_CharacterControllerComponent()
|
||||
{
|
||||
if (GetGame() && GetGame().GetCallqueue()) GetGame().GetCallqueue().Remove(ERR_ExpirePassengerBrake);
|
||||
}
|
||||
override void OnPrepareControls(IEntity owner, ActionManager am, float dt, bool player)
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
// Feed the actor's native action before it prepares driving controls. The
|
||||
// device/AI refreshes this value each tick; only add a held demand, never
|
||||
// write a release over the driver's own handbrake or parking-brake input.
|
||||
if (am && mode && mode.Active() && !am.GetActionTriggered("CarHandBrakePersistent") && ERR_PassengerHandbrake.Requested(owner)) am.SetActionValue("CarHandBrake", 1);
|
||||
super.OnPrepareControls(owner, am, dt, player);
|
||||
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
|
||||
if (!controller || controller.GetControlledEntity() != owner) return;
|
||||
IEntity vehicle;
|
||||
if (mode && mode.Active()) vehicle = ERR_PassengerHandbrake.VehicleFor(owner);
|
||||
bool held;
|
||||
InputManager input = GetGame().GetInputManager();
|
||||
if (vehicle && input && GetGame().GetMenuManager() && !GetGame().GetMenuManager().GetTopMenu() && !ERR_ObserverChatInput.Capture())
|
||||
{
|
||||
input.ActivateContext("ERR_PassengerBrakeContext", 1);
|
||||
held = input.GetActionValue("ERR_PassengerBrake") > 0.5;
|
||||
if (m_ERRPassengerBrakeHintVehicle != vehicle)
|
||||
{
|
||||
SCR_HintManagerComponent hints = SCR_HintManagerComponent.GetInstance();
|
||||
if (hints) hints.ShowCustom("Hold Space to apply the handbrake. Release Space to let go.", "Passenger handbrake", 5);
|
||||
m_ERRPassengerBrakeHintVehicle = vehicle;
|
||||
}
|
||||
}
|
||||
if (!vehicle) m_ERRPassengerBrakeHintVehicle = null;
|
||||
int now = System.GetTickCount();
|
||||
if (held != m_ERRPassengerBrakeSent || (held && now >= m_ERRPassengerBrakeNextSend))
|
||||
{
|
||||
controller.ERR_SendPassengerBrake(held);
|
||||
m_ERRPassengerBrakeSent = held;
|
||||
m_ERRPassengerBrakeNextSend = now + 250;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user