Add weapon on back functionality

This commit is contained in:
BaerMitUmlaut 2019-08-06 00:27:06 +02:00
parent cd65c2f196
commit 877183d690
32 changed files with 1117 additions and 0 deletions

View File

@ -0,0 +1 @@
z\ace\addons\weapononback

View File

@ -0,0 +1,31 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_postInit));
};
};
class Extended_DisplayLoad_EventHandlers {
class RscDisplayInventory {
ADDON = QUOTE(call FUNC(onInventoryOpened));
};
};
class Extended_Init_EventHandlers {
class WeaponHolderSimulated {
class ADDON {
init = QUOTE(_this call FUNC(onWHSInit));
};
};
};

View File

@ -0,0 +1,14 @@
class CfgVehicles {
class ReammoBox;
class GVAR(weaponHolder): ReammoBox {
author = ECSTRING(common,ACETeam);
model = QPATHTOF(data\holder.p3d);
showWeaponCargo = 1;
scope = 1;
transportMaxWeapons = 1;
transportMaxMagazines = 0;
transportMaxItems = 0;
transportMaxBackpacks = 0;
destrType = "DestructNo";
};
};

View File

@ -0,0 +1,16 @@
class CfgWeapons {
class Launcher_Base_F;
class GVAR(weapon): Launcher_Base_F {
scope = 1;
scopeCurator = 1;
scopeArsenal = 1;
model = "\A3\Weapons_f\empty";
picture = "\A3\Weapons_F\Data\clear_empty.paa";
magazines[] = {"ACE_FakeMagazine"};
displayName = CSTRING(FakeWeaponDisplayName);
type = TYPE_WEAPON_SECONDARY;
class WeaponSlotsInfo {
mass = 0;
};
};
};

View File

@ -0,0 +1,11 @@
ace_weapononback
===================
Adds the possibility to have a second primary weapon.
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [BaerMitUmlaut](https://github.com/BaerMitUmlaut)

View File

@ -0,0 +1,16 @@
class RscActiveText;
class RscDisplayInventory {
class controls {
class GVAR(weaponImage): RscActiveText {
idc = IDC_WEAPON_IMAGE;
style = "0x30 + 0x800";
color[] = {1, 1, 1, 1};
colorFocused[] = {1, 1, 1, 1};
colorText[] = {1, 1, 1, 1};
colorBackground[] = {1, 1, 1, 0.1};
colorBackgroundSelected[] = {1, 1, 1, 0.1};
onLoad = "_this#0 ctrlEnable false";
};
};
};

View File

@ -0,0 +1,14 @@
PREP(add);
PREP(get);
PREP(getIDCContainer);
PREP(onDrag);
PREP(onDragWOB);
PREP(onDrop);
PREP(onDropWOB);
PREP(onInventoryOpened);
PREP(onWeaponChange);
PREP(onWHSInit);
PREP(remove);
PREP(renderPFH);
PREP(swap);
PREP(updateInventory);

View File

@ -0,0 +1,43 @@
#include "script_component.hpp"
["weapon", FUNC(onWeaponChange)] call CBA_fnc_addPlayerEventHandler;
["unit", {
params ["_newUnit", "_oldUnit"];
private _inventoryEHs = _oldUnit getVariable [QGVAR(inventoryEHs), [-1, -1]];
_oldUnit removeEventHandler ["InventoryOpened", _inventoryEHs#0];
_oldUnit removeEventHandler ["InventoryClosed", _inventoryEHs#1];
_inventoryEHs = [
_newUnit addEventHandler ["InventoryOpened", {
params ["", "_firstContainer", "_secondContainer"];
// If only one container was opened, pretend it's both box and ground
// WOB holder sometimes gets opened for a frame, but is immediately
// closed and should never be accessed anyways
if (isNull _secondContainer || {typeOf _secondContainer == QGVAR(weaponHolder)}) then {
_secondContainer = _firstContainer;
};
GVAR(openedContainers) = [_firstContainer, _secondContainer];
}],
_newUnit addEventHandler ["InventoryClosed", {
GVAR(openedContainers) = [objNull, objNull];
}]
];
_newUnit setVariable [QGVAR(inventoryEHs), _inventoryEHs];
}, true] call CBA_fnc_addPlayerEventHandler;
["loadout", {
// One frame delay, because dropping from inventory isn't instant but still handled by us
// See also fnc_onDragWOB
[{
private _hasWeaponHolder = !isNull (ACE_player getVariable [QGVAR(weaponHolder), objNull]);
private _hasFakeWeapon = secondaryWeapon ACE_player == QGVAR(weapon);
if !(_hasWeaponHolder isEqualTo _hasFakeWeapon) then {
[ACE_player] call FUNC(remove);
};
}] call CBA_fnc_execNextFrame;
}] call CBA_fnc_addPlayerEventHandler;

View File

@ -0,0 +1,28 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
GVAR(units) = [];
GVAR(openedContainers) = [objNull, objNull];
// Add "add" event handler in preInit to catch JIP events
[QGVAR(add), {
params ["_unit", "_weaponsItems"];
[_unit, _weaponsItems, true] call FUNC(add);
}] call CBA_fnc_addEventHandler;
[QGVAR(remove), {
params ["_unit"];
[_unit, true] call FUNC(remove);
}] call CBA_fnc_addEventHandler;
ADDON = true;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,20 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_common"};
author = ECSTRING(common,ACETeam);
authors[] = {"BaerMitUmlaut"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "CfgEventHandlers.hpp"
#include "CfgVehicles.hpp"
#include "CfgWeapons.hpp"
#include "RscDisplayInventory.hpp"

Binary file not shown.

View File

@ -0,0 +1,27 @@
class CfgSkeletons
{
class Skeleton
{
isDiscrete=0;
skeletonInherit="";
skeletonBones[]={};
};
};
class CfgModels
{
class holder
{
htMin=0;
htMax=0;
afMax=0;
mfMax=0;
mFact=0;
tBody=0;
skeletonName="Skeleton";
sectionsInherit="";
sections[]={};
class Animations
{
};
};
};

Binary file not shown.

View File

@ -0,0 +1,79 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Adds a weapon to the units back (global effect).
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapons items (weaponsItems format) <ARRAY>
*
* Return Value:
* None
*
* Example:
* [player, weaponsItems player select 1] call ace_weapononback_fnc_add
*
* Public: Yes
*/
params ["_unit", "_weaponsItems", ["_calledFromEvent", false]];
if (!_calledFromEvent) exitWith {
[QGVAR(add), [_unit, _weaponsItems], EVENT_ID(_unit)] call CBA_fnc_globalEventJIP;
};
// Replace old weapon holder
private _weaponHolder = _unit getVariable [QGVAR(weaponHolder), objNull];
private _oldWeapon = "";
if (!isNull _weaponHolder) then {
deleteVehicle _weaponHolder;
};
_weaponHolder = QGVAR(weaponHolder) createVehicleLocal [0, 0, 0];
_weaponHolder attachTo [_unit, [0, 0, 0], "proxy:\a3\characters_f\proxies\launcher.001"];
// Add weapon to weapon holder
_weaponHolder addWeaponWithAttachmentsCargo [_weaponsItems, 1];
// Disable simulation to lock
_weaponHolder enableSimulation false;
_unit setVariable [QGVAR(weaponHolder), _weaponHolder];
GVAR(units) pushBack _unit;
if (local _unit) then {
private _oldMass = _unit getVariable [QGVAR(weaponMass), 0];
private _newMass = 0;
// Calculate weapon weight with all items
_weaponsItems params ["_weapon", "_muzzle", "_side", "_optic", "_magPrimary", "_magSecondary", "_bipod"];
// Weapon itself
_newMass = _newMass + getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass");
// Attachments
{
if (_x != "") then {
_newMass = _newMass + getNumber (configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "mass");
};
} forEach [_muzzle, _side, _optic, _bipod];
// Magazines
{
if !(_x isEqualTo []) then {
_newMass = _newMass + getNumber (configFile >> "CfgMagazines" >> _x#0 >> "mass");
};
} forEach [_magPrimary, _magSecondary];
[_unit, _unit, _newMass - _oldMass] call EFUNC(movement,addLoadToUnitContainer);
_unit setVariable [QGVAR(weaponMass), _newMass, true];
// Reset saved zeroing, if this weapon was swapped it will be restored in FUNC(swap)
ACE_player setVariable [QGVAR(scopeAdjustment), [0, 0, 0]];
_unit addWeapon QGVAR(weapon);
[] call FUNC(updateInventory);
};
if (isNil QGVAR(renderPFH)) then {
GVAR(renderPFH) = [FUNC(renderPFH), 0, []] call CBA_fnc_addPerFrameHandler;
};

View File

@ -0,0 +1,25 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Returns the weapon on a units back.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* Weapon, attachments and magazines (weaponsItems format) or empty array <ARRAY>
*
* Example:
* [player] call ace_weapononback_fnc_get
*
* Public: Yes
*/
params ["_unit"];
private _weaponHolder = _unit getVariable [QGVAR(weaponHolder), objNull];
if (isNull _weaponHolder) then {
[]
} else {
(weaponsItemsCargo _weaponHolder)#0
};

View File

@ -0,0 +1,35 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Gets the container associated with the given inventory display IDC.
*
* Arguments:
* 0: Control IDC <NUMBER>
*
* Return Value:
* Container <OBJECT>
*
* Public: No
*/
params ["_ctrlIDC"];
switch (_ctrlIDC) do {
case IDC_GROUND_CONTAINER: {
GVAR(openedContainers)#1
};
case IDC_SOLDIER_CONTAINER: {
GVAR(openedContainers)#0
};
case IDC_UNIFORM_CONTAINER: {
uniformContainer ACE_player
};
case IDC_VEST_CONTAINER: {
vestContainer ACE_player
};
case IDC_BACKPACK_CONTAINER: {
backpackContainer ACE_player
};
default {
objNull
};
};

View File

@ -0,0 +1,94 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles dragging weapons onto the secondary slot.
*
* Arguments:
* 0: Drag origin <CONTROL>
* 1: Information about the dragged item <ARRAY>
*
* Return Value:
* None
*
* Public: No
*/
params ["_ctrl", "_info"];
if !(secondaryWeapon ACE_player in ["", QGVAR(weapon)]) exitWith {};
// Check if dragged item is a primary weapon
private _ctrlIDC = ctrlIDC _ctrl;
private _isPrimary = if (_ctrlIDC == IDC_PRIMARY_SLOT) then {
(primaryWeapon ACE_player != "") && {_info == 0}
} else {
// Dragged from container
_info#0 params ["_displayName"];
private _container = [_ctrlIDC] call FUNC(getIDCContainer);
if (_container isKindOf "CAManBase") then {
_container = _container getVariable [QGVAR(droppedWeaponHolder), _container];
};
private _index = (weaponCargo _container) findIf {
private _cfg = configFile >> "CfgWeapons" >> _x;
(getText (_cfg >> "displayName") == _displayName)
&& {getNumber (_cfg >> "type") == TYPE_WEAPON_PRIMARY}
};
_index != -1
};
if (!_isPrimary) exitWith {
TRACE_1("Dragged item is not a primary",_info);
};
// Set up drag and drop catcher
private _display = ctrlParent _ctrl;
private _slotSecondary = _display displayCtrl IDC_SECONDARY_SLOT;
// RscPicture does not trigger LBDrop
private _dropCatcher = _display ctrlCreate ["ctrlActivePictureKeepAspect", IDC_DROP_CATCHER];
if (secondaryWeapon ACE_player != QGVAR(weapon)) then {
_dropCatcher ctrlSetText QPATHTOF(data\weapononback_ca.paa);
} else {
_dropCatcher ctrlSetText "#(rgb,8,8,3)color(1,1,1,0)";
};
_dropCatcher ctrlSetPosition ctrlPosition _slotSecondary;
_dropCatcher ctrlSetTextColor [1, 1, 1, 1];
_dropCatcher ctrlSetBackgroundColor [1, 1, 1, 0];
_dropCatcher ctrlCommit 0;
_dropCatcher ctrlAddEventHandler ["LBDrop", FUNC(onDrop)];
// Hide red "you can't drop this here"
_slotSecondary ctrlSetFade 1;
_slotSecondary ctrlCommit 0;
// Create white "you can drop this here"
private _secondaryBG = _display displayCtrl IDC_SECONDARY_BG;
_secondaryBG ctrlSetText "#(rgb,8,8,3)color(1,1,1,0.25)";
// Remove drop catcher when drag and drop action was completed
private _eventHandler = _display displayAddEventHandler ["MouseButtonUp", {
params ["_display"];
// Reset everything to its original state
private _eventHandler = _display getVariable QGVAR(mouseUpEH);
_display displayRemoveEventHandler ["MouseButtonUp", _eventHandler];
private _slotSecondary = _display displayCtrl IDC_SECONDARY_SLOT;
_slotSecondary ctrlSetFade 0;
_slotSecondary ctrlCommit 0;
private _secondaryBG = _display displayCtrl IDC_SECONDARY_BG;
_secondaryBG ctrlSetText "";
// Delay drop catcher destruction by one frame or drop event doesn't trigger
private _dropCatcher = _display displayCtrl IDC_DROP_CATCHER;
_dropCatcher ctrlSetText "#(rgb,8,8,3)color(1,1,1,0)";
[{
params ["_display"];
ctrlDelete (_display displayCtrl IDC_DROP_CATCHER);
}, _this] call CBA_fnc_execNextFrame;
}];
_display setVariable [QGVAR(mouseUpEH), _eventHandler];

View File

@ -0,0 +1,100 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles dragging the weapon on the back onto a container.
*
* Arguments:
* 0: Secondary slot control <CONTROL>
* 1: Mouse button ID <NUMBER>
*
* Return Value:
* None
*
* Public: No
*/
params ["_ctrl", "_mouseButton"];
if (_mouseButton != 0 || {secondaryWeapon ACE_player != QGVAR(weapon)}) exitWith {};
// Replace primary slot with drop catcher, make background white
private _display = ctrlParent _ctrl;
private _primarySlot = _display displayCtrl IDC_PRIMARY_SLOT;
_primarySlot ctrlSetFade 1;
_primarySlot ctrlCommit 0;
private _primaryBG = _display displayCtrl IDC_PRIMARY_BG;
_primaryBG ctrlSetText "#(rgb,8,8,3)color(1,1,1,0.25)";
// RscPicture does not trigger LBDrop
private _dropCatcher = _display ctrlCreate ["ctrlActivePictureKeepAspect", IDC_DROP_CATCHER];
if (primaryWeapon ACE_player == "") then {
_dropCatcher ctrlSetText "a3\ui_f\data\GUI\Rsc\RscDisplayGear\ui_gear_primary_gs.paa";
} else {
_dropCatcher ctrlSetText getText (configFile >> "CfgWeapons" >> primaryWeapon ACE_player >> "picture");
};
_dropCatcher ctrlSetPosition ctrlPosition _primarySlot;
_dropCatcher ctrlSetTextColor [1, 1, 1, 1];
_dropCatcher ctrlSetBackgroundColor [1, 1, 1, 1];
_dropCatcher ctrlCommit 0;
_dropCatcher ctrlAddEventHandler ["LBDrop", {[true] call FUNC(onDropWOB)}];
// Create image for the currently dragged weapon under the cursor
private _imgContainer = _display displayCtrl IDC_WEAPON_IMAGE;
private _dragImage = _display ctrlCreate ["RscPictureKeepAspect", -1];
_dragImage ctrlSetPosition ctrlPosition _imgContainer;
_dragImage ctrlSetText ctrlText _imgContainer;
_dragImage ctrlEnable false;
_dragImage ctrlCommit 0;
(ctrlPosition _dragImage) params ["", "", "_w", "_h"];
private _widthOffset = _w / 2;
private _heightOffset = _h / 2;
_dragImage ctrlSetPosition [getMousePosition#0 - _widthOffset, getMousePosition#1 - _heightOffset];
_dragImage ctrlCommit 0;
private _eventHandlers = [
_display displayAddEventHandler ["MouseMoving", {
params ["_display"];
private _dragImage = _display getVariable [QGVAR(dragImage), ctrlNull];
(ctrlPosition _dragImage) params ["", "", "_w", "_h"];
private _widthOffset = _w / 2;
private _heightOffset = _h / 2;
_dragImage ctrlSetPosition [getMousePosition#0 - _widthOffset, getMousePosition#1 - _heightOffset];
_dragImage ctrlCommit 0;
}],
_display displayAddEventHandler ["MouseButtonUp", {
params ["_display"];
private _dragImage = _display getVariable [QGVAR(dragImage), ctrlNull];
ctrlDelete _dragImage;
// Clean up event handlers
private _eventHandlers = _display getVariable [QGVAR(dragWOBEHs), []];
_eventHandlers params ["_mouseMoving", "_mouseButtonUp"];
_display displayRemoveEventHandler ["MouseMoving", _mouseMoving];
_display displayRemoveEventHandler ["MouseButtonUp", _mouseButtonUp];
// LBDrop triggers after MouseButtonUp, so delay by one frame
[{
params ["_display"];
ctrlDelete (_display displayCtrl IDC_DROP_CATCHER);
}, _this] call CBA_fnc_execNextFrame;
private _primarySlot = _display displayCtrl IDC_PRIMARY_SLOT;
_primarySlot ctrlSetFade 0;
_primarySlot ctrlCommit 0;
private _primaryBG = _display displayCtrl IDC_PRIMARY_BG;
_primaryBG ctrlSetText "";
// Inventory has some delay, timeout in case player didn't actually drop weapon somewhere
[{
secondaryWeapon ACE_player != QGVAR(weapon)
}, {
[false] call FUNC(onDropWOB);
}, [], 5] call CBA_fnc_waitUntilAndExecute;
}]
];
_display setVariable [QGVAR(dragImage), _dragImage];
_display setVariable [QGVAR(dragWOBEHs), _eventHandlers];

View File

@ -0,0 +1,84 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles dropping a weapon onto the secondary slot.
*
* Arguments:
* 0: Drop catcher control <CONTROL>
* 1: Ignored
* 2: Ignored
* 3: Control that the dragging operation started from <CONTROL>
* 4: Information about the dragged item <ARRAY>
*
* Return Value:
* None
*
* Public: No
*/
params ["_dropCatcher", "", "", "_originIDC", "_info"];
private _weaponsItems = [];
if (_originIDC == IDC_PRIMARY_SLOT) then {
[] call FUNC(swap);
} else {
_info#0 params ["_displayName", "_index"];
private _display = ctrlParent _dropCatcher;
private _listBox = _display displayCtrl _originIDC;
// First index is 127, followed by 1.
// Of course, the 128th item is then index -1, followed by 128.
// Thanks BI!
if (_index == 127) then {
_index = 0;
};
if (_index == -1) then {
_index = 127;
};
private _weaponIndex = -1;
for "_i" from 0 to _index do {
if (_listBox lbText _i == _displayName) then {
_weaponIndex = _weaponIndex + 1;
};
};
private _container = [_originIDC] call FUNC(getIDCContainer);
if (_container isKindOf "CAManBase") then {
_container = _container getVariable [QGVAR(droppedWeaponHolder), _container];
};
private _allWeaponsItems = weaponsItemsCargo _container;
// Get all weapons in container that have the name of the weapon that was taken
private _weaponsItemsWithName = _allWeaponsItems select {
getText (configFile >> "CfgWeapons" >> _x#0 >> "displayName") == _displayName
};
// Get unique weapon attachment setups because identical ones stack
private _allWeaponsItemsNoMags = _weaponsItemsWithName apply {_x select {!(_x isEqualType [])}};
private _allWeaponsItemsUnique = _allWeaponsItemsNoMags arrayIntersect _allWeaponsItemsNoMags;
private _weaponAttachmentSetup = _allWeaponsItemsUnique#_weaponIndex;
private _weaponsItemsIndex = _allWeaponsItems findIf {
(_x select {!(_x isEqualType [])}) isEqualTo _weaponAttachmentSetup
};
// Choose first occurance of this weapon setup from container
private _weaponsItems = _allWeaponsItems#_weaponsItemsIndex;
_allWeaponsItems deleteAt _weaponsItemsIndex;
// Add weapon on back to container if there is one
if (secondaryWeapon ACE_player == QGVAR(weapon)) then {
private _weaponsItems = [ACE_player] call FUNC(get);
_allWeaponsItems pushBack _weaponsItems;
};
// Remove setup from container
clearWeaponCargoGlobal _container;
{
_container addWeaponWithAttachmentsCargoGlobal [_x, 1];
} forEach _allWeaponsItems;
[ACE_player, _weaponsItems] call FUNC(add);
};

View File

@ -0,0 +1,86 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles dropping the weapon on the back onto a container.
*
* Arguments:
* 0: Dropped onto primary weapon slot <BOOL>
*
* Return Value:
* None
*
* Public: No
*/
params [["_droppedOntoPrimary", false]];
if (_droppedOntoPrimary) then {
[] call FUNC(swap);
} else {
// Find container the weapon was dropped into
private _possibleContainers = if (QGVAR(weapon) in weapons ACE_player) then {
[
// Ordered by likelyhood
backpackContainer ACE_player,
vestContainer ACE_player,
uniformContainer ACE_player
]
} else {
if (GVAR(openedContainers)#0 isKindOf "CAManBase") then {
// Weapons cannot be dropped onto dead bodies
[
// If the dropped primary of the unit wasn't taken yet, that holder is used
GVAR(openedContainers)#0 getVariable [QGVAR(droppedWeaponHolder), objNull],
// Otherwise a new one is created dynamically
nearestObject [ACE_player, "GroundWeaponHolder"]
]
} else {
GVAR(openedContainers)
}
};
private _container = {
if (QGVAR(weapon) in weaponCargo _x) exitWith { _x };
} forEach _possibleContainers;
// Remove and replace fake weapon with actual weapon
private _weaponsItems = [ACE_player] call FUNC(get);
private _allWeaponsItems = weaponsItemsCargo _container;
_allWeaponsItems deleteAt (_allWeaponsItems findIf { _x#0 == QGVAR(weapon) });
if (_container canAdd _weaponsItems#0) then {
_allWeaponsItems pushBack _weaponsItems;
[ACE_player] call FUNC(remove);
} else {
// If container cannot fit weapon on back, readd fake weapon to player, show hint
ACE_player addWeapon QGVAR(weapon);
private _containerCfg = switch (_container) do {
case (backpackContainer ACE_player): {
configFile >> "CfgWeapons" >> backpack ACE_player
};
case (vestContainer ACE_player): {
configFile >> "CfgWeapons" >> vest ACE_player
};
case (uniformContainer ACE_player): {
configFile >> "CfgWeapons" >> uniform ACE_player
};
default {
configFile >> "CfgVehicles" >> typeOf _container
};
};
[
format [
LLSTRING(CouldNotFit),
getText (configFile >> "CfgWeapons" >> _weaponsItems#0 >> "displayName"),
getText (_containerCfg >> "displayName")
],
true, 5
] call EFUNC(common,displayText);
};
clearWeaponCargoGlobal _container;
{
_container addWeaponWithAttachmentsCargoGlobal [_x, 1];
} forEach _allWeaponsItems;
};

View File

@ -0,0 +1,48 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Adds event handlers when opening the inventory display.
*
* Arguments:
* 0: Inventory display <DISPLAY>
*
* Return Value:
* None
*
* Public: No
*/
params ["_display"];
[_display] call FUNC(updateInventory);
// Add event handlers for dragging something onto the secondary slot
private _primarySlot = _display displayCtrl IDC_PRIMARY_SLOT;
_primarySlot ctrlAddEventHandler ["MouseButtonDown", FUNC(onDrag)];
{
private _ctrl = _display displayCtrl _x;
_ctrl ctrlAddEventHandler ["LBDrag", FUNC(onDrag)];
} forEach [
IDC_GROUND_CONTAINER,
IDC_SOLDIER_CONTAINER,
IDC_UNIFORM_CONTAINER,
IDC_VEST_CONTAINER,
IDC_BACKPACK_CONTAINER
];
// Add event handler for dragging the weapon on back to a container
private _secondarySlot = _display displayCtrl IDC_SECONDARY_SLOT;
_secondarySlot ctrlAddEventHandler ["MouseButtonDown", FUNC(onDragWOB)];
// Add event handler for right click dropping a weapon into a container
_secondarySlot ctrlAddEventHandler ["MouseButtonClick", {
params ["", "_mouseButton"];
if (_mouseButton == 1 && {ACE_player in GVAR(units)}) then {
[{
secondaryWeapon ACE_player != QGVAR(weapon)
}, {
[] call FUNC(onDropWOB);
}, [], 5] call CBA_fnc_waitUntilAndExecute;
};
}];

View File

@ -0,0 +1,27 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Associates unit and weapon holder on death and moves weapon on back into
* the dropped weapon holder.
*
* Arguments:
* 0: Weapon holder <OBJECT>
*
* Return Value:
* None
*
* Public: No
*/
params ["_weaponHolder"];
private _unit = nearestObject [_weaponHolder, "CAManBase"];
if (isNull _unit || {alive _unit}) exitWith {};
_unit setVariable [QGVAR(droppedWeaponHolder), _weaponHolder];
if (local _unit && {_unit in GVAR(units)}) then {
private _weaponsItems = [_unit] call FUNC(get);
_weaponHolder addWeaponWithAttachmentsCargoGlobal [_weaponsItems, 1];
[_unit] call FUNC(remove);
};

View File

@ -0,0 +1,19 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles switching to the weapon on the back.
*
* Arguments:
* 0: Player <OBJECT>
* 1: Weapon that was swapped to <STRING>
*
* Return Value:
* None
*
* Public: No
*/
params ["", "_weapon"];
if (_weapon != QGVAR(weapon)) exitWith {};
[FUNC(swap), [], 2] call CBA_fnc_waitAndExecute;

View File

@ -0,0 +1,40 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Removes the weapon on the back of a unit (global effect).
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [player] call ace_weapononback_fnc_remove
*
* Public: Yes
*/
params ["_unit", ["_calledFromEvent", false]];
if !(_unit in GVAR(units)) exitWith {};
if (!_calledFromEvent) exitWith {
[EVENT_ID(_unit)] call CBA_fnc_removeGlobalEventJIP;
[QGVAR(remove), [_unit]] call CBA_fnc_globalEvent;
};
private _weaponHolder = _unit getVariable [QGVAR(weaponHolder), objNull];
deleteVehicle _weaponHolder;
// There is a delay when deleting the weapon holder, so ensure the variable is null
_unit setVariable [QGVAR(weaponHolder), objNull];
GVAR(units) deleteAt (GVAR(units) find _unit);
if (local _unit) then {
private _oldMass = _unit getVariable [QGVAR(weaponMass), 0];
[_unit, _unit, -_oldMass] call EFUNC(movement,addLoadToUnitContainer);
_unit setVariable [QGVAR(weaponMass), 0, true];
_unit removeWeapon QGVAR(weapon);
[] call FUNC(updateInventory);
};

View File

@ -0,0 +1,51 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Renders weapons on the backs of players.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Public: No
*/
private _units = GVAR(units);
if (_units isEqualTo []) exitWith {};
// Sort units by distance if there is a render limit greater than 0
if (GVAR(renderLimit) > 0) then {
private _unitsWithDistance = GVAR(units) apply {[ACE_player distance _x, _x]};
_unitsWithDistance sort true;
_units = _unitsWithDistance apply {_x#1};
};
private _renderedUnits = 0;
{
private _weaponHolder = _x getVariable [QGVAR(weaponHolder), objNull];
if (_renderedUnits != GVAR(renderLimit) && {isNull objectParent _x}) then {
_weaponHolder hideObject false;
// Don't update orientation if not on screen, always update for player
if (_x == player || {!(worldToScreen ASLToAGL getPosWorld _weaponHolder isEqualTo [])}) then {
private _lShoulder = _x selectionPosition "leftshoulder";
private _rShoulder = _x selectionPosition "rightshoulder";
private _lUpLeg = _x selectionPosition "leftupleg";
private _ab = _lShoulder vectorFromTo _rShoulder;
private _bc = _lShoulder vectorFromTo _lUpLeg;
private _vectorDir = _ab vectorCrossProduct _bc;
private _vectorUp = _ab;
_weaponHolder setVectorDirAndUp [_vectorUp, _vectorDir];
_renderedUnits = _renderedUnits + 1;
};
} else {
_weaponHolder hideObject true;
};
} forEach _units;

View File

@ -0,0 +1,85 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Swaps primary and weapon on back. Can handle no primary or no weapon on back.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Public: No
*/
private _oldPrimary = (getUnitLoadout ACE_player)#0;
private _newPrimary = [ACE_player] call FUNC(get);
// Save scope adjustment
private _adjustments = ACE_player getVariable [QEGVAR(scopes,adjustment), [[0, 0, 0], [0, 0, 0], [0, 0, 0]]];
private _oldAdjustment = _adjustments#0;
private _newAdjustement = ACE_player getVariable [QGVAR(scopeAdjustment), [0, 0, 0]];
EGVAR(scopes,guns) set [0, ""];
ACE_player removeWeapon (_oldPrimary param [0, ""]);
if !(_newPrimary isEqualTo []) then {
_newPrimary params ["_weapon"];
// addWeapon is going to eat a mag for each muzzle before we can fill the weapon with addWeaponItem
// Because of that, we need to save where that mag was taken from and readd it
private _containerMags = [
uniformContainer ACE_player,
vestContainer ACE_player,
backpackContainer ACE_player
] apply {magazinesAmmo _x};
ACE_player addWeapon _weapon;
// Readd lost magazines
{
private _container = _x;
private _before = _containerMags#_forEachIndex;
private _after = magazinesAmmo _container;
{
_before deleteAt (_before find _x);
} forEach _after;
{
_container addMagazineAmmoCargo [_x#0, 1, _x#1];
} forEach _before;
} forEach [
uniformContainer ACE_player,
vestContainer ACE_player,
backpackContainer ACE_player
];
removeAllPrimaryWeaponItems ACE_player;
{
ACE_player addWeaponItem [_weapon, _x, true];
} forEach (_newPrimary select [1, 6]);
ACE_player selectWeapon _weapon;
// Restore scope adjustment
[{
EGVAR(scopes,guns)#0 != ""
}, {
params ["_oldAdjustment", "_newAdjustement"];
if !(_newAdjustement isEqualTo [0, 0, 0]) then {
private _scopeAdjustmentParams = [ACE_player];
_scopeAdjustmentParams append _newAdjustement;
_scopeAdjustmentParams call EFUNC(scopes,applyScopeAdjustment);
};
ACE_player setVariable [QGVAR(scopeAdjustment), _oldAdjustment];
}, [_oldAdjustment, _newAdjustement]] call CBA_fnc_waitUntilAndExecute;
};
if (_oldPrimary isEqualTo []) then {
[ACE_player] call FUNC(remove);
} else {
[ACE_player, _oldPrimary] call FUNC(add);
};

View File

@ -0,0 +1,39 @@
#include "script_component.hpp"
/*
* Author: BaerMitUmlaut
* Updates the inventory display by hiding / showing the weapon on the back.
*
* Arguments:
* 0: Inventory display (optional, for UI EH) <DISPLAY>
*
* Return Value:
* None
*
* Public: No
*/
params [["_display", findDisplay IDD_INVENTORY]];
if (isNull _display) exitWith {};
private _weaponHolder = ACE_player getVariable [QGVAR(weaponHolder), objNull];
private _hasWeaponOnBack = !isNull _weaponHolder;
// Hide attachment slots if player has weapon on their back
{
private _ctrl = _display displayCtrl _x;
_ctrl ctrlSetFade ([0, 1] select _hasWeaponOnBack);
_ctrl ctrlCommit 0;
} forEach IDCS_SECONDARY_ATTACHMENTS;
// Show/hide image of weapon on back
private _imgContainer = _display displayCtrl IDC_WEAPON_IMAGE;
private _secondarySlot = _display displayCtrl IDC_SECONDARY_SLOT;
_imgContainer ctrlSetPosition ctrlPosition _secondarySlot;
_imgContainer ctrlSetBackgroundColor [1, 1, 1, 0];
if (_hasWeaponOnBack) then {
private _weapon = (weaponCargo _weaponHolder)#0;
_imgContainer ctrlSetText getText (configFile >> "CfgWeapons" >> _weapon >> "picture");
} else {
_imgContainer ctrlSetText "";
};
_imgContainer ctrlCommit 0;

View File

@ -0,0 +1 @@
#include "\z\ace\addons\weapononback\script_component.hpp"

View File

@ -0,0 +1,13 @@
[
QGVAR(enabled), "CHECKBOX",
LELSTRING(common,Enabled),
LELSTRING(common,ACEKeybindCategoryWeapons),
true, false
] call CBA_settings_fnc_init;
[
QGVAR(renderLimit), "SLIDER",
[LSTRING(RenderLimit), LSTRING(RenderLimitDescription)],
LELSTRING(common,ACEKeybindCategoryWeapons),
[-1, 100, -1, -1], false
] call CBA_settings_fnc_init;

View File

@ -0,0 +1,46 @@
#define COMPONENT weapononback
#define COMPONENT_BEAUTIFIED Weapon on Back
#include "\z\ace\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_WEAPONONBACK
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_WEAPONONBACK
#define DEBUG_SETTINGS DEBUG_SETTINGS_WEAPONONBACK
#endif
#include "\z\ace\addons\main\script_macros.hpp"
// Inventory IDD
#define IDD_INVENTORY 602
// Relevant slot IDCs
#define IDC_PRIMARY_SLOT 610
#define IDC_SECONDARY_SLOT 611
// Custom controls
#define IDC_WEAPON_IMAGE 135001
#define IDC_DROP_CATCHER 135002
#define IDC_DROP_CATCHER_BG 135003
// Container IDCs
#define IDC_GROUND_CONTAINER 632
#define IDC_SOLDIER_CONTAINER 640
#define IDC_UNIFORM_CONTAINER 633
#define IDC_VEST_CONTAINER 638
#define IDC_BACKPACK_CONTAINER 619
// Attachement IDCs
#define IDCS_SECONDARY_ATTACHMENTS [1248, 1266, 1249, 1250, 1251, 624, 642, 626, 625, 627]
// Background IDCs
#define IDC_PRIMARY_BG 1242
#define IDC_SECONDARY_BG 1247
// ID generator for JIP events
#define EVENT_ID(obj) (QGVAR(eventID_) + netId obj)

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="WeaponOnBack">
<Key ID="STR_ACE_WeaponOnBack_FakeWeaponDisplayName">
<English>Weapon on back</English>
<German>Waffe auf dem Rücken</German>
</Key>
<Key ID="STR_ACE_WeaponOnBack_RenderLimit">
<English>Render limit</English>
<German>Renderlimit</German>
</Key>
<Key ID="STR_ACE_WeaponOnBack_RenderLimitDescription">
<English>Limit the amount of weapons on backs to be rendered (-1 for no limit).</English>
<German>Limitiert die Anzahl der Waffen auf dem Rücken die gerendert werden (-1 bedeutet kein Limit).</German>
</Key>
<Key ID="STR_ACE_WeaponOnBack_CouldNotFit">
<English>Could not fit %1 into %2.</English>
<German>%1 passt nicht in %2.</German>
</Key>
</Package>
</Project>