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
2 changes: 2 additions & 0 deletions addons/casings/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
PREP(createCasing);
PREP(createLitter);
PREP(createMagazine);
2 changes: 2 additions & 0 deletions addons/casings/XEH_postInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ if (!hasInterface) exitWith {};
if (!GVAR(enabled)) exitWith {};

GVAR(cachedCasings) = createHashMap;
GVAR(cachedMagazines) = createHashMap;
GVAR(casings) = [];

["CAManBase", "FiredMan", LINKFUNC(createCasing)] call CBA_fnc_addClassEventHandler;
[QGVAR(reloaded), "Reloaded", LINKFUNC(createMagazine)] call CBA_fnc_addBISPlayerEventHandler;
}] call CBA_fnc_addEventHandler;
61 changes: 16 additions & 45 deletions addons/casings/functions/fnc_createCasing.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -22,55 +22,26 @@ if (!isNull objectParent _unit) exitWith {};


private _modelPath = GVAR(cachedCasings) getOrDefaultCall [_ammo, {
private _cartridge = getText (configFile >> "CfgAmmo" >> _ammo >> "cartridge");
if (_cartridge == "") then { // return (note: can't use exitWith)
""
} else {
private _cartridgeConfig = configFile >> "CfgVehicles" >> _cartridge;
private _cartridgeConfig = configNull; // private var in switch condition scope isn't available in the result block.
private _modelOverride = ""; // very annoying.

// if explicitly defined, use ACE's config
if (isText (_cartridgeConfig >> QGVAR(model))) exitWith {
getText (_cartridgeConfig >> QGVAR(model))
};
// use casing's default model
private _model = getText (_cartridgeConfig >> "model");
if ("a3\weapons_f\empty" in toLowerANSI _model) exitWith { "" };
private _cartridge = getText (configFile >> "CfgAmmo" >> _ammo >> "cartridge");
private _model = switch (true) do {
case (_cartridge == ""): { "" };

// Add file extension if missing (fileExists needs file extension)
if ((_model select [count _model - 4]) != ".p3d") then {
_model = _model + ".p3d";
};
_cartridgeConfig = configFile >> "CfgVehicles" >> _cartridge;
_modelOverride = getText (_cartridgeConfig >> QGVAR(model));

["", _model] select (fileExists _model)
case (_modelOverride != ""): { _modelOverride }; // Use the override if non-empty
default { getText (_cartridgeConfig >> "model") } // Use the casing's default model
};
}, true];

if (_modelPath isEqualTo "") exitWith {};

private _unitPos = getPosASL _unit;
// Distant shooters don't produce as many cases
if ((AGLToASL positionCameraToWorld [0,0,0]) vectorDistance _unitPos > 100 && {random 1 < 0.9}) exitWith {};

private _weapDir = _unit weaponDirection currentWeapon _unit;
private _ejectDir = _weapDir vectorCrossProduct [0, 0, 1];
private _pos = _unitPos
vectorAdd (_weapDir vectorMultiply (-0.5 + random 2))
vectorAdd (_ejectDir vectorMultiply (0.2 + random 2));

[
{
params ["_modelPath", "_pos"];
// Add file extension if missing (fileExists needs file extension)
if ((_model select [count _model - 4]) != ".p3d") then {
_model = _model + ".p3d";
};

private _lisPos = (lineIntersectsSurfaces [_pos, _pos vectorAdd [0,0,-1e11], objNull, objNull, true, 1, "ROADWAY", "FIRE"]) #0;
private _casing = createSimpleObject [_modelPath, (_lisPos #0 vectorAdd [0,0,0.005]), true];
_casing setDir (random 360);
_casing setVectorUp _lisPos #1;
private _idx = GVAR(casings) pushBack _casing;
["", _model] select (!("a3\weapons_f\empty" in toLowerANSI _model) && {fileExists _model})
}, true];

for "_" from 0 to (_idx - GVAR(maxCasings)) do {
deleteVehicle (GVAR(casings) deleteAt 0);
};
},
[_modelPath,_pos],
0.4
] call CBA_fnc_waitAndExecute;
[_unit, _modelPath] call FUNC(createLitter);
51 changes: 51 additions & 0 deletions addons/casings/functions/fnc_createLitter.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "..\script_component.hpp"
/*
* Author: esteldunedain / Cyruz / diwako
* Handles casing/dropped mag creation.
*
* Arguments:
* 0: Unit - Unit to create litter for <OBJECT>
* 1: Model - Path to litter model <STRING>
* 2: Force creation - Skip the distance + RNG check <BOOL> (default: false)
*
* Return Value:
* None
*
* Example:
* [player, "\a3\weapons_f\mag_univ.p3d"] call ace_casings_fnc_createLitter
*
* Public: No
*/

params ["_unit", "_modelPath", ["_force", false]];

if (_modelPath == "") exitWith {};

private _unitPos = getPosASL _unit;
// Distant shooters don't produce as many cases
if (!_force && {(AGLToASL positionCameraToWorld [0,0,0]) vectorDistance _unitPos > 100 && {random 1 < 0.9}}) exitWith {};

private _weapDir = _unit weaponDirection currentWeapon _unit;
private _ejectDir = _weapDir vectorCrossProduct [0, 0, 1];
private _pos = _unitPos
vectorAdd (_weapDir vectorMultiply (-0.5 + random 2))
vectorAdd (_ejectDir vectorMultiply (0.2 + random 2));

[
{
params ["_modelPath", "_pos"];
TRACE_2("creating litter",_modelPath,_pos);

private _lisPos = (lineIntersectsSurfaces [_pos, _pos vectorAdd [0,0,-1e11], objNull, objNull, true, 1, "ROADWAY", "FIRE"]) #0;
private _casing = createSimpleObject [_modelPath, (_lisPos #0 vectorAdd [0,0,0.010]), false]; // global
_casing setDir (random 360);
_casing setVectorUp _lisPos #1;
private _idx = GVAR(casings) pushBack _casing;

for "_" from 0 to (_idx - GVAR(maxCasings)) do {
deleteVehicle (GVAR(casings) deleteAt 0);
};
},
[_modelPath,_pos],
0.4
] call CBA_fnc_waitAndExecute;
51 changes: 51 additions & 0 deletions addons/casings/functions/fnc_createMagazine.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "..\script_component.hpp"
/*
* Author: GabrielPearce / esteldunedain / Cyruz / diwako / PabstMirror
* Produces a casing matching the reloaded and dropped magazine
*
* Arguments:
* 0: unit - Object the reloaded event handler is assigned to <OBJECT>
* 4: Old magazine (can be nil) - <ARRAY>
*
* Return Value:
* None
*
* Example:
* [player, "", "","", ["1Rnd_HE_Grenade_shell", 0]] call ace_casings_fnc_createMagazine
*
* Public: No
*/

params ["_unit", "", "", "", "_oldMagazine"];
TRACE_2("createMagazine",_unit,_oldMagazine);

if (isNil "_oldMagazine") exitWith {};
_oldMagazine params ["_mag", "_ammo"];
if (_ammo != 0) exitWith {};

private _modelPath = GVAR(cachedMagazines) getOrDefaultCall [_mag, {
private _magConfig = configNull; // private var in switch condition scope isn't available in the result block.
private _modelOverride = ""; // very annoying.

private _model = switch true do {
// Should cover most 40x36
case (_mag in compatibleMagazines ["arifle_Mk20_GL_F", "EGLM"]): { "A3\Weapons_F\MagazineProxies\mag_40x36_HE_1rnd.p3d" };

_magConfig = configFile >> "CfgMagazines" >> _mag;
_modelOverride = getText (_magConfig >> QGVAR(model));

case (_modelOverride != ""): { _modelOverride }; // Use the override if non-empty
case (getNumber (_magConfig >> "modelSpecialIsProxy") == 1): {getText (_magConfig >> "modelSpecial")}; // Use the magazine's proxy

default { getText (_magConfig >> "model") }; // Use the magazine's dropped model
};

// Add file extension if missing (fileExists needs file extension)
if ((_model select [count _model - 4]) != ".p3d") then {
_model = _model + ".p3d";
};

["", _model] select (!(_model regexMatch "(?:\\)?a3\\weapons_f\\(?:empty|ammo\\mag_univ).p3d") && {fileExists _model})
}, true];

[_unit, _modelPath, true] call FUNC(createLitter);
4 changes: 2 additions & 2 deletions docs/wiki/feature/casings.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: wiki
title: Casings
component: casings
description: Adds infantry bullet casings on the ground when weapons are fired.
description: Adds infantry bullet casings on the ground when weapons are fired and drops empty magazines when reloading.
group: feature
category: realism
parent: wiki
Expand All @@ -14,4 +14,4 @@ version:
---

## 1. Overview
Spits out casings from infantry weapons when fired.
Spits out casings from infantry weapons when fired and drops empty magazines when reloading.
42 changes: 42 additions & 0 deletions docs/wiki/framework/casings-framework.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
layout: wiki
title: Casings Framework
description: Explains how to set-up dropped magazine models with ACE3 casings system.
group: framework
order: 5
parent: wiki
mod: ace
version:
major: 3
minor: 0
patch: 0
---

## 1. Config Values

```cpp
class CfgAmmo { // In order of priority
class MyAmmo {
cartridge = "CartridgeClassnameInCfgVehicles"; // if empty (""), no casing will be created
ace_casings_model = "path\to\casing\model.p3d"; // Dropped casing will have this model if not an empty string
};
};

class CfgVehicles {
class MyCartridge {
model = "path\to\cartridge\model.p3d"; // If empty string or model, no casing will be created
};
};

class CfgMagazines {
class MyMagazine { // In order of priority
// If magazine is compatible with vanilla Mk20's EGLM, model will always be "A3\Weapons_F\MagazineProxies\mag_40x36_HE_1rnd.p3d"
ace_casings_model = "path\to\magazine\model.p3d"; //Dropped magazine will have this model if not an empty string
modelSpecialIsProxy = 1;
modelSpecial = "path\to\magazine\proxy\model.p3d"; // Proxy model
model = "path\to\magazine\dropped\model.p3d"; // Dropped model

// If model doesn't exist, is the default dropped magazine model (pouch), or is empty, no dropped magazine will be created.
};
};
```