Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions addons/captives/Configs/ACE/Settings.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ACE_SettingsConfig {
m_aInitialModSettings {
ACE_Captives_Settings "{66540638C1BB804D}" {
}
}
}
17 changes: 17 additions & 0 deletions addons/captives/Configs/ACE/Settings.conf.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
MetaFileClass {
Name "{A305FEB7400A2965}Configs/ACE/Settings.conf"
Configurations {
CONFResourceClass PC {
}
CONFResourceClass XBOX_ONE : PC {
}
CONFResourceClass XBOX_SERIES : PC {
}
CONFResourceClass PS4 : PC {
}
CONFResourceClass PS5 : PC {
}
CONFResourceClass HEADLESS : PC {
}
}
}
1 change: 1 addition & 0 deletions addons/captives/Prefabs/Characters/Core/Character_Base.et
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ SCR_ChimeraCharacter {
m_sInteractionIcon "{2EFEA2AF1F38E7F0}UI/Textures/Icons/icons_wrapperUI-64.imageset"
m_sIconName "connection"
}
VisibilityRange 1.5
"Sort Priority" 1
}
ACE_Captives_ReleaseCaptiveUserAction "{64D908D36A98FF2D}" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ modded class SCR_CharacterControllerComponent : CharacterControllerComponent
[RplProp()]
protected bool m_bACE_Captives_IsCaptive = false;

[RplProp()]
protected ACE_Captives_ETakeCaptiveRequirement m_eACE_Captives_TakePlayerCaptiveRequirements;
[RplProp()]
protected ACE_Captives_ETakeCaptiveRequirement m_eACE_Captives_TakeAICaptiveRequirements;

//------------------------------------------------------------------------------------------------
override protected void OnInit(IEntity owner)
{
super.OnInit(owner);

if (!Replication.IsServer())
return;

ACE_Captives_Settings settings = ACE_SettingsHelperT<ACE_Captives_Settings>.GetModSettings();
if (!settings)
return;

m_eACE_Captives_TakePlayerCaptiveRequirements = settings.m_eTakePlayerCaptiveRequirements;
m_eACE_Captives_TakeAICaptiveRequirements = settings.m_eTakeAICaptiveRequirements;
Replication.BumpMe();
}

//------------------------------------------------------------------------------------------------
void ACE_Captives_SetSurrender(bool hasSurrendered)
{
Expand Down Expand Up @@ -71,6 +93,50 @@ modded class SCR_CharacterControllerComponent : CharacterControllerComponent
Replication.BumpMe();
}

//------------------------------------------------------------------------------------------------
bool ACE_Captives_CanBeTakenCaptive(IEntity captor)
{
if (m_bACE_Captives_IsCaptive || m_bACE_IsCarried)
return false;

// Incapacitated and surrendered characters can always be tied
if (GetLifeState() == ECharacterLifeState.INCAPACITATED || m_bACE_Captives_HasSurrendered)
return true;

ACE_Captives_ETakeCaptiveRequirement requirements = m_eACE_Captives_TakeAICaptiveRequirements;

if (ACE_EntityUtils.IsPlayer(GetOwner()))
requirements = m_eACE_Captives_TakePlayerCaptiveRequirements;

if (requirements == ACE_Captives_ETakeCaptiveRequirement.SURRENDER)
return false;

if (!GetWeaponManagerComponent().GetCurrentWeapon())
return true;

if (requirements == ACE_Captives_ETakeCaptiveRequirement.UNARMED)
return false;

// For FROM_BEHIND_FOR_ARMED, armed units can be captured from behind.
Animation ownerAnim = GetOwner().GetAnimation();
vector rightHandTransform[4], leftHandTransform[4];
ownerAnim.GetBoneMatrix(ownerAnim.GetBoneIndex("RightHand"), rightHandTransform);
ownerAnim.GetBoneMatrix(ownerAnim.GetBoneIndex("LeftHand"), leftHandTransform);
vector ownerDir = GetOwner().GetWorldTransformAxis(2);
ownerDir[1] = 0;
// Take the mean of hand positions as owner origin
vector relCaptorPos = captor.GetOrigin() - GetOwner().CoordToParent(0.5 * (rightHandTransform[3] + leftHandTransform[3]));
relCaptorPos[1] = 0;
float dot = vector.DotXZ(ownerDir, relCaptorPos);

// Dot product has to be negative to be behind owner
if (dot >= 0)
return false;

// 0.4 corresponds to a sector angle of 101.54°
return dot * dot / ownerDir.LengthSq() / relCaptorPos.LengthSq() >= 0.4;
}

//------------------------------------------------------------------------------------------------
//! Prevent captives from equipping gadgets in vehicles
override bool GetCanEquipGadget(IEntity gadget)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------------------------
//! Add mod settings to mission header
[BaseContainerProps()]
modded class ACE_MissionHeaderSettings
{
[Attribute()]
protected ref ACE_Captives_Settings m_ACE_Captives_Settings;

//------------------------------------------------------------------------------------------------
//! Applies settings from mission header to config
override void ApplyToSettingsConfig(notnull ACE_SettingsConfig config)
{
super.ApplyToSettingsConfig(config);

if (m_ACE_Captives_Settings)
config.SetModSettings(m_ACE_Captives_Settings);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//------------------------------------------------------------------------------------------------
enum ACE_Captives_ETakeCaptiveRequirement
{
SURRENDER,
UNARMED,
FROM_BEHIND_FOR_ARMED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//------------------------------------------------------------------------------------------------
[BaseContainerProps()]
class ACE_Captives_Settings : ACE_ModSettings
{
[Attribute(defvalue: "SURRENDER", desc: "Requirements for taking a player captive.", uiwidget: UIWidgets.ComboBox, enums: ParamEnumArray.FromEnum(ACE_Captives_ETakeCaptiveRequirement))]
ACE_Captives_ETakeCaptiveRequirement m_eTakePlayerCaptiveRequirements;

[Attribute(defvalue: "SURRENDER", desc: "Requirements for taking AI captive.", uiwidget: UIWidgets.ComboBox, enums: ParamEnumArray.FromEnum(ACE_Captives_ETakeCaptiveRequirement))]
ACE_Captives_ETakeCaptiveRequirement m_eTakeAICaptiveRequirements;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,7 @@ class ACE_Captives_TakeCaptiveUserAction : ACE_InstantGadgetUserAction
if (!ownerCharController)
return false;

if (ownerCharController.ACE_IsCarried())
return false;

if (ownerCharController.ACE_Captives_IsCaptive())
return false;

// Incapacitated characters can be tied
if (ownerCharController.GetLifeState() == ECharacterLifeState.INCAPACITATED)
return true;

return ownerCharController.ACE_Captives_HasSurrendered();
return ownerCharController.ACE_Captives_CanBeTakenCaptive(user);
}

//------------------------------------------------------------------------------------------------
Expand All @@ -55,7 +45,7 @@ class ACE_Captives_TakeCaptiveUserAction : ACE_InstantGadgetUserAction
return;

SCR_CharacterControllerComponent ownerCharController = SCR_CharacterControllerComponent.Cast(ownerChar.GetCharacterController());
if (!ownerCharController)
if (!ownerCharController || !ownerCharController.ACE_Captives_CanBeTakenCaptive(pUserEntity))
return;

ownerCharController.ACE_Captives_SetCaptive(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ modded class SCR_RemoveCasualtyUserAction : SCR_CompartmentUserAction
return;

// We just eject AI for now. Trying to carrying them immediatly breaks their animation
if (!EntityUtils.IsPlayer(casualtyChar))
if (!ACE_EntityUtils.IsPlayer(casualtyChar))
return;

string cannotPerformReason;
Expand Down
14 changes: 14 additions & 0 deletions addons/core/scripts/Game/ACE_Core/Global/ACE_EntityUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//------------------------------------------------------------------------------------------------
class ACE_EntityUtils
{
//------------------------------------------------------------------------------------------------
//! Returns true if the character is a player. Also returns false for GM-possessed AI.
static bool IsPlayer(IEntity entity)
{
SCR_ECharacterControlType controlType = SCR_CharacterHelper.GetCharacterControlType(entity);
if (controlType == SCR_ECharacterControlType.AI || controlType == SCR_ECharacterControlType.POSSESSED_AI)
return false;

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ACE_Medical_CardiacArrestState : ACE_Medical_IVitalState
super.OnEnter(context);

//! Kill AI when cardiac arrest is not allowed
if (!s_pCirculationSettings.m_bCardiacArrestForAIEnabled && !EntityUtils.IsPlayer(context.m_pObject))
if (!s_pCirculationSettings.m_bCardiacArrestForAIEnabled && !ACE_EntityUtils.IsPlayer(context.m_pObject))
{
context.m_pDamageManager.Kill(context.m_pDamageManager.GetInstigator());
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ modded class SCR_CharacterBloodHitZone : SCR_RegeneratingHitZone
//! Whether the character can bleed out
protected bool ACE_Medical_CanBleedOut()
{
if (!EntityUtils.IsPlayer(GetOwner()))
if (!ACE_EntityUtils.IsPlayer(GetOwner()))
return true;

ACE_Medical_Core_Settings settings = ACE_SettingsHelperT<ACE_Medical_Core_Settings>.GetModSettings();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,8 @@ modded class SCR_CharacterDamageManagerComponent : SCR_DamageManagerComponent
bool ACE_Medical_IsSecondChanceEnabled()
{
// Check for AI - if AI setting is disabled, block second chance for AI and possessed AI
if (!s_ACE_Medical_Core_Settings.m_bSecondChanceForAIEnabled)
{
SCR_ECharacterControlType controlType = SCR_CharacterHelper.GetCharacterControlType(GetOwner());
if (controlType == SCR_ECharacterControlType.AI || controlType == SCR_ECharacterControlType.POSSESSED_AI)
return false;
}
if (!s_ACE_Medical_Core_Settings.m_bSecondChanceForAIEnabled && !ACE_EntityUtils.IsPlayer(GetOwner()))
return false;

// Check for fall damage
if (s_ACE_Medical_Core_Settings.m_bSecondChanceForFallDamageEnabled || m_fACE_Medical_SecondChanceDeactivationTimeMS < 0)
Expand Down
23 changes: 23 additions & 0 deletions docs/src/content/docs/components/captives.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,26 @@ Adds the ability to surrender and adds zip cuffs to arsenal, which can be used t

- Captives have a user action on them for escorting
- Escorted captives can be loaded in vehicles with the user action on the door

## Settings

Certain aspects of this mod can be configured in the mission header section of a server config file. An overview of available fields is given below in the table:

| Field | Value | Description |
| ------------------------------------- | -------------------- | -------------------------------------------------- |
| `m_eTakePlayerCaptiveRequirements` | Type: `int`*<br />Default: `SURRENDER` | Requirements for taking a player captive. |
| `m_eTakeAICaptiveRequirements` | Type: `int`*<br />Default: `SURRENDER` | Requirements for taking AI captive. |

\* Note that requirements have to be provided as integers in the config: 0 (SURRENDER), 1 (UNARMED) or 2 (FROM_BEHIND_FOR_ARMED).

Example for the `missionHeader` in a server config:
```
"missionHeader": {
"m_ACE_Settings": {
"m_ACE_Captives_Settings": {
"m_eTakePlayerCaptiveRequirements": 0,
"m_eTakeAICaptiveRequirements": 0
}
}
}
```