From 877183d690fc51ce5e5eca3b741c56aea9a1f63f Mon Sep 17 00:00:00 2001 From: BaerMitUmlaut Date: Tue, 6 Aug 2019 00:27:06 +0200 Subject: [PATCH] Add weapon on back functionality --- addons/weapononback/$PBOPREFIX$ | 1 + addons/weapononback/CfgEventHandlers.hpp | 31 ++++++ addons/weapononback/CfgVehicles.hpp | 14 +++ addons/weapononback/CfgWeapons.hpp | 16 +++ addons/weapononback/README.md | 11 ++ addons/weapononback/RscDisplayInventory.hpp | 16 +++ addons/weapononback/XEH_PREP.hpp | 14 +++ addons/weapononback/XEH_postInit.sqf | 43 ++++++++ addons/weapononback/XEH_preInit.sqf | 28 +++++ addons/weapononback/XEH_preStart.sqf | 3 + addons/weapononback/config.cpp | 20 ++++ addons/weapononback/data/holder.p3d | Bin 0 -> 2986 bytes addons/weapononback/data/model.cfg | 27 +++++ addons/weapononback/data/weapononback_ca.paa | Bin 0 -> 22016 bytes addons/weapononback/functions/fnc_add.sqf | 79 ++++++++++++++ addons/weapononback/functions/fnc_get.sqf | 25 +++++ .../functions/fnc_getIDCContainer.sqf | 35 ++++++ addons/weapononback/functions/fnc_onDrag.sqf | 94 ++++++++++++++++ .../weapononback/functions/fnc_onDragWOB.sqf | 100 ++++++++++++++++++ addons/weapononback/functions/fnc_onDrop.sqf | 84 +++++++++++++++ .../weapononback/functions/fnc_onDropWOB.sqf | 86 +++++++++++++++ .../functions/fnc_onInventoryOpened.sqf | 48 +++++++++ .../weapononback/functions/fnc_onWHSInit.sqf | 27 +++++ .../functions/fnc_onWeaponChange.sqf | 19 ++++ addons/weapononback/functions/fnc_remove.sqf | 40 +++++++ .../weapononback/functions/fnc_renderPFH.sqf | 51 +++++++++ addons/weapononback/functions/fnc_swap.sqf | 85 +++++++++++++++ .../functions/fnc_updateInventory.sqf | 39 +++++++ .../functions/script_component.hpp | 1 + addons/weapononback/initSettings.sqf | 13 +++ addons/weapononback/script_component.hpp | 46 ++++++++ addons/weapononback/stringtable.xml | 21 ++++ 32 files changed, 1117 insertions(+) create mode 100644 addons/weapononback/$PBOPREFIX$ create mode 100644 addons/weapononback/CfgEventHandlers.hpp create mode 100644 addons/weapononback/CfgVehicles.hpp create mode 100644 addons/weapononback/CfgWeapons.hpp create mode 100644 addons/weapononback/README.md create mode 100644 addons/weapononback/RscDisplayInventory.hpp create mode 100644 addons/weapononback/XEH_PREP.hpp create mode 100644 addons/weapononback/XEH_postInit.sqf create mode 100644 addons/weapononback/XEH_preInit.sqf create mode 100644 addons/weapononback/XEH_preStart.sqf create mode 100644 addons/weapononback/config.cpp create mode 100644 addons/weapononback/data/holder.p3d create mode 100644 addons/weapononback/data/model.cfg create mode 100644 addons/weapononback/data/weapononback_ca.paa create mode 100644 addons/weapononback/functions/fnc_add.sqf create mode 100644 addons/weapononback/functions/fnc_get.sqf create mode 100644 addons/weapononback/functions/fnc_getIDCContainer.sqf create mode 100644 addons/weapononback/functions/fnc_onDrag.sqf create mode 100644 addons/weapononback/functions/fnc_onDragWOB.sqf create mode 100644 addons/weapononback/functions/fnc_onDrop.sqf create mode 100644 addons/weapononback/functions/fnc_onDropWOB.sqf create mode 100644 addons/weapononback/functions/fnc_onInventoryOpened.sqf create mode 100644 addons/weapononback/functions/fnc_onWHSInit.sqf create mode 100644 addons/weapononback/functions/fnc_onWeaponChange.sqf create mode 100644 addons/weapononback/functions/fnc_remove.sqf create mode 100644 addons/weapononback/functions/fnc_renderPFH.sqf create mode 100644 addons/weapononback/functions/fnc_swap.sqf create mode 100644 addons/weapononback/functions/fnc_updateInventory.sqf create mode 100644 addons/weapononback/functions/script_component.hpp create mode 100644 addons/weapononback/initSettings.sqf create mode 100644 addons/weapononback/script_component.hpp create mode 100644 addons/weapononback/stringtable.xml diff --git a/addons/weapononback/$PBOPREFIX$ b/addons/weapononback/$PBOPREFIX$ new file mode 100644 index 00000000000..6c80c84ab44 --- /dev/null +++ b/addons/weapononback/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\weapononback diff --git a/addons/weapononback/CfgEventHandlers.hpp b/addons/weapononback/CfgEventHandlers.hpp new file mode 100644 index 00000000000..f5826ce696e --- /dev/null +++ b/addons/weapononback/CfgEventHandlers.hpp @@ -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)); + }; + }; +}; diff --git a/addons/weapononback/CfgVehicles.hpp b/addons/weapononback/CfgVehicles.hpp new file mode 100644 index 00000000000..ff831c13dab --- /dev/null +++ b/addons/weapononback/CfgVehicles.hpp @@ -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"; + }; +}; diff --git a/addons/weapononback/CfgWeapons.hpp b/addons/weapononback/CfgWeapons.hpp new file mode 100644 index 00000000000..df009bf059b --- /dev/null +++ b/addons/weapononback/CfgWeapons.hpp @@ -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; + }; + }; +}; diff --git a/addons/weapononback/README.md b/addons/weapononback/README.md new file mode 100644 index 00000000000..d6d7fc484bc --- /dev/null +++ b/addons/weapononback/README.md @@ -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) diff --git a/addons/weapononback/RscDisplayInventory.hpp b/addons/weapononback/RscDisplayInventory.hpp new file mode 100644 index 00000000000..044aaab0d53 --- /dev/null +++ b/addons/weapononback/RscDisplayInventory.hpp @@ -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"; + }; + }; +}; diff --git a/addons/weapononback/XEH_PREP.hpp b/addons/weapononback/XEH_PREP.hpp new file mode 100644 index 00000000000..995d5686e9b --- /dev/null +++ b/addons/weapononback/XEH_PREP.hpp @@ -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); diff --git a/addons/weapononback/XEH_postInit.sqf b/addons/weapononback/XEH_postInit.sqf new file mode 100644 index 00000000000..31e7de7404d --- /dev/null +++ b/addons/weapononback/XEH_postInit.sqf @@ -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; diff --git a/addons/weapononback/XEH_preInit.sqf b/addons/weapononback/XEH_preInit.sqf new file mode 100644 index 00000000000..43d3c140991 --- /dev/null +++ b/addons/weapononback/XEH_preInit.sqf @@ -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; diff --git a/addons/weapononback/XEH_preStart.sqf b/addons/weapononback/XEH_preStart.sqf new file mode 100644 index 00000000000..022888575ed --- /dev/null +++ b/addons/weapononback/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/weapononback/config.cpp b/addons/weapononback/config.cpp new file mode 100644 index 00000000000..7ecf732af86 --- /dev/null +++ b/addons/weapononback/config.cpp @@ -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" diff --git a/addons/weapononback/data/holder.p3d b/addons/weapononback/data/holder.p3d new file mode 100644 index 0000000000000000000000000000000000000000..f222852897f8f20dc57dab54e52b01820b4740a3 GIT binary patch literal 2986 zcmdT_&1(};5Z|>{TP;0!P!GMWN^47u#4l=1D)z%eYYMH>VxVldTU&_Pu#MJ&haif2 z(VNhdAYK&e#Se05vlsE)Kft4S7ZpM9V}5x%$;*UFDhP3w_jcyZy!m?X&5VvD2Tap2 zwi|{q78@Atpua)%Zu(*D1WnLCc)vG4*(ZU(7W+J#&_cBKFC6)`eMKmOK^ z%o}n9+YHjK^b2zJ#3S=0?MOdl9_zIu{grm4A2N^i*qJyxJZxrt@5XX(%8sR+dE2+0 zoa--6&!w=u;4Y??Ts!M!BGIG99#Uzdh$sjV)A6p7fSbvMB1(z`NwhSZrZwh!S=Y}k zTSlJP_O+bn&<596nzlMex4J>do7Tlk<8IC}-j^AbSF4u(^h(Q{8XhsN!OU!OZfGIx zTI_5>FaPDk47p~1$B;wAz#u4H5Nq5`yL2sPEp!Yz(^)T@&Un}D%tE-p`NSOr)YyGC z?M&)+@yIc7$pL5+$uFx_ZA7P;&us&327IQ`CZZeFZA7P;&us&327IQq^8Zg-1QNrs zmI9^;g+b3h9(bjVs3MI=UJcNMU7Tv>VmAYL`cA1?$w5l`fL*$u($eOd^j*-8NT8iT zYL=Fj97m-`BQ;4>MqJX;W%~q;5$@t2C9~fL`Rt<8 literal 0 HcmV?d00001 diff --git a/addons/weapononback/data/model.cfg b/addons/weapononback/data/model.cfg new file mode 100644 index 00000000000..a17a0063010 --- /dev/null +++ b/addons/weapononback/data/model.cfg @@ -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 + { + }; + }; +}; diff --git a/addons/weapononback/data/weapononback_ca.paa b/addons/weapononback/data/weapononback_ca.paa new file mode 100644 index 0000000000000000000000000000000000000000..621fcfa132f7f0cb501b1218586812d859060ce0 GIT binary patch literal 22016 zcmeHP3w%_?xu3I-WH%&n2?-KNbORgmAZV~5f{2_5aZ4{_3l;_y3*Uv%5J7SL{Wl zz05CU^X<%hGxNRXn{U2B?-hg``&6MYN5>eOHf@?&hKq9Y3HG88Eac3bPB4dm1kas5 zeKwE3UHJD9{@7w;tjW$;tH{{9_;>NeML772Z-1rb6$)ITz!eJot5e`R@&Z!b-HeH% zs27;E!Scc&=7Sjv6Wq?;nAV*>m}lWsZ>_TU+r!jeDgR0okokMKUfWFkj?;63E?%C# zyBps~(&vKUTa6#yuY&gqtVioY6XHLQ&+AA&o=NPH@rib9zgNoTM1frB>uF;>Tiq%1 zTQBIzbz-c!dv)iYj`MJ;e6uB3lJ@q2cS!2`&ja;tYJcAQJ5D&W;tPPR?j*9YDqeb@)CQNcT;v#R=l zJjnQVN#_M_aaFQMI|104&1Nh4w$}acGOk8&myY>+S!Nda7pK?jje;?;&(ENUPwwfgddWJ zA`~^cuxxkf8h7O+Q#E5J;^N|j3Tolf+uLK?I}UhPDE!I-jQ#$+3O>*W-X`ts16Qfq zZ<7w>3FHqp==}H&6ht+QOlycfoYh`s;(3ot$7UU};3#Ee#`vN1@q%u z55~61{9mBPqnnrQcq+SW)bUo>3zS78bg4D>|EXd~o0*v{mNHZDShO>dxb>2M%~2d}s)N_lHxS?1^nRGuP(iZUdXq1^%a)G0}M_ z>iJ%Q`%>U~W__aC+NkSl^zlzfPLms)Yc*95!(T#5kCwhT*P2mIMwJtuOYB|ScJLhd zZMWMOB(RP3KZZ)0QP+(FUD$#u%~RQnrk-6x_)ds|lVvi_jQ;NzGp6Mf{-0}w#Dw_& zemGzsWVl8O*av^T*CaZBU6?OM!|NZ;#@z$=!8YrZ~FM`1@?tSU>6K|aqH zhQogtQTR?W*#ohO*1M|PDv4Qv<&_@{_34P3BIv0yDcQsn`KqQ_Ms>XUWqbz96#M|_G&Ap~|&z_z> zi|EO1P4=C67Bof8TRXX=tk=Dw7WPN4Wut_!M~MIOc<9*&Urq4*AmdN( zx1NA}``7#VaA95BG(SEHE@%aJRBuvig}gpHk8l3HO4v-=za68F$o4vJyh6W*#uc^O zLiCSd=;Qg>!x}Q)jv1NM&{%39e_JpZ>$GVm4bjJlA{gP1SOMQMWk zrTK)~CxRnX^WCL_X+1Oi{v)-QBx%YcGF%X`y0ZcDCbG>kT=iGc2YW*4f%aa3N$$Hw41qrv7uO_;mfvk~dWJ4-Xf)H*1Nyk4 zc1|rz5BlRWj00R#%=)=gzW?D%&D}|cmGh8XKQd6gyX^KSWBF^)&`}~s@+W$U5 zhrwIQM_E}(?+VO+4B#E%MU`bQwEUL*9cFA2g~#zZ*cG#)w#))T})Mi$P>KDNyC7izuGhfop^ytXH zi$ziNgX(4d@bjPaA(a0FgZ%;jFVH?@yik9D>Qh7XmtggLsJ&C>_hY8m_Br5p)6X|0 zb{78Xqj!Hu{H|GP?-neTpD}o_nbl_21Oswi{*q&~Yeu>QQR-M?HSq=XnZmGHP=84PSsV4iu+ZvOFYpx=_ zWHg>JWWSK{lYiltH-cRVI1594_9(OZFY7#Pmjr+DBg>4c6zVV9`^S?JU#cT}r!aL` z-G;KM?$TcJKgL5IM%0XGJ^X%T49OeM-^yudf0%Lk#3N!6FMm9{EQRzJ_c3~f9;WKE zYqYz9^dUHu|1u2v|Ex3C z^4hVTQ*UzrB*&0Yi!Tj+fV*+BKZgQZ#*86O!p)1yJY_k z*R6W?-C=1rRJ^*px!cS-(qCIyKjD+J-cO(OuI@Z--qq33q0sxP2ac@4eT&~;YJ|N% z0uH99>x6YTq^`RmgPSW?010a%_7CFR8W z^wm_oxzYjqeSg@*zmNDw{`n(o%uF9^<5R!a`?B-x4fmKJ4=92CCeXjncms^DV_Gwd zV6;A-hyPFdn}6~88|L3%X>ckAw)mZt5PRYK_M7`1|B!o8Wp>$A5&q8l_Be6lkpDh@ zlXH;!Ea+8K0{<%x&UrY)dT~DIw1M-}ku~s$u))q0taUnT-8576o>hpqd8>2}f4cSI zPZKh$lMILtj^8QUoK1`?ZJQH zYM(qDS(8!DgRFz^vqhxprfL4N4)QRxs6>{BKW$h}>qQC3gFGJf_she8utAcp!G7M` z(&i!m&^~~FKu9m=Vn%%KCCHx{lWYfr5*~7dkK3?FOXu;8aA1}Z|8n2N`8l%O>;165QLtDKcQXgJJ&-%R*KIC%K#8!ZMJf9MAm zd3-U4OT3lrdt!vMlJ*yoMfuO8e%P*_+*PtXJR!?N6FS%iH{C<$Gs36U2L1CbSTBPE zuFyk5^Ly6ceu~b^Hv7V2HGMBNO#Wj< z$o(qL+dC{X4q2R)8^&?k;Qi|6%KS=6`phd#aA{}`J{bF@<^wr???k-^`g;@Zb5o(u zvV5SOkm$?La8{<}`I20HE1>qI@5=gp_&)aPN!xy}N_Q75+Cm8Fe`RT`l zR9pu15BW$$EvQpJee+TCpZ~3A)~(a91uGa5Hl2FfIU`~--Oou9>)uZGd)BR`t2-g@ z;=)09`}X6>S?vUi_bL2~UbPhVJxGr)gU41bE#CwEQTNZ)mu&gN;w^um_4f4qwpGzD zH9qQpCZs>X`}Xz6r{Jf02^|zzjqkFB*?pR1UTYpJ>jUpm_;jg0;eXcc1dA1XTxz9e zNp?xE8DFGhL(gT8ztw#BNsBHC@jNhmM|f*V@25{%=zc5$@FS*Sy~Y<>eoW_6*Ps*K zWrFn`y04j2n^7(nigtzGGs`2(X}?$CjLMAi^IpV<{Q4@_L!V~qmPfv_JmQu7%2Gok zic(MQ4|s9k=Wm|@e4+M(>i71m5BlE&wcqdkO%NaY@AJ+J!2RYckC(ptg5%5eLH+mj z59Xt&$CvB(1nJRlJbAsK&v%$?Z=)(_%!jy3QMtG;cs{Ww1FJ{;V1dH7Z|l>_S?f(3 z>kPyVr1R(<*Oz?WT{_~0hQ=~e=y~tYjzRN7?ZX?6F)}>02;uF9kw#N>WI4jAO|!Ji z(|>s1%h%F*4(xBY3f8V<){q_M)$0nO!P+l;2ZZ3)p8alOfRE+9=d%e{@+r$^(FhQ$=21KccyvY`8c5OAC$dc zyw~f!U7_E9migrY@?8R?k>xc7WsvXr+ASXsd*kO1(D{0Oa`Ufq1)c=N@{6*Fxg`Jn z`upuc0G}T|Q}#h|sJzkmXT2W>^jqCt4G+}+{g8aJFKe&#$N2{HkFluRUo7lE|I^kw zb5{E+6TRLkv+vUMuDj)L$5ZJ1QDFb(^AAS);ysqv!EeNSIDs8rPJSK1$@o4E%ArQRmfXDi}AuNPxGiOUO{`O>t!qX`sm{dIF=w}S8W;@t(I_Fm~9 zQV;w=`|BGo6yA;cp#1py2lIh}e}MX+eO8Y*Fx($seM~t43}wT+brkVj43}7X?V?5p%<%|B5SLCV|^zPn7mD{u&7JkR$ zWiPh;mf{&5#uMW93En%r33V2_50tiP#Q4qge?GI-e30&!j;t&cb!qW8EBr17!@pK$ z13AT;bs_#>gt&N9d!gg`Evw9I`{woQx5)C@d@#1X2K=0mr?(a_^(vpD3J4YuLJY(ypg@uKe{QUlP z3y1ptNu3Y(W#e-RN6XeBUz76p#^XZOnP5p{`nuldVE@ju-QwMG7Hx*7Y)V68j^S?J zAuiL7i`TwbxBqx+66B>*99P-fY(ah<*-I~zzIC`Q)tw(>@xtEGd>mra(BzFycN_9^ zV8!4OC2a5QS>B*SKA@RJyWk;>ufP4|x~Se){Pyt6==JYbkNBb-k5$&inPzAh>_rmf z;S1O|&Scdq@PjgJD<{~D4s@S@`apYx3oZxPpv+Ige8SkU7P8mM`VdIl8VY|^eNpgS zs!yKrNLC17GvhBZOUf5l;ID@s9F9rz!`6+aXQg5s$C3nhrx9Wz!yrDMK}F%(@2Cui zT>Jg2@*@iC-Je}{Jw5A*qqL73eaD36ZhN~1=RjrKl%Hi%*w3G)=k69e&tMxiOh=j& zo>|Jzz(UeuE*E+X&2g9Rcb89+-5@+eiJgFJ+h-Gd$?e561v`$xb!=jthQj7})E01C zR(Hm#o}mqCgBMHJo)p$Vnh`xy6UCvz+QgGBiKi&-D>STgxvU@8g5{H@mnYLPCJqNe z&28&%@A*^fQG)Gup0~3!)5=d{^&dgE_i|akz|!h8_#uAFX^d%1!($2OTIW!jufs6k?R@GP z&AsFnoX{t&Eloo3q^t4iafsiJUf}0GG8+0b zb*3-PT+}l*TyT#idqMQ2Ge8X43uBWHM$nJTp=+VGlhhx_>OdIqQqe0?zUHl)M(d@p z>?nl!k})4TrW&EzDGMyO6;$v@%U(3j#rSQ4k$b?Si}W-!rrx@CU6o51zR;GTFJyCS zUzup)87no9is*UqWL+29dm54rLWa*itiUw^{e?6TSs#39)84qdlAs+4Y+^&>ZS-EK zry{&}G(W1zLi)kjPesSDom0D5OBu-vN)(=HN5OuaqWR&K_Wzk4irYfiX}=T_!%U1T zL7Hbx0bPrZ5R8o_#%832?JnN7ZCf4LGdjl3sXelWU+i#TeQ0mO`j4#9h8%n4v{dMO zfq%VSjhWQ|G#!coa|ctg@KV_n+DC=iu2^g|%Ko9>H)2 zg=fMfJz_|h^kV687kfLwF|q!~lxBC3LmtcIC6BwEo7_j%I4e1=v`dKM!*qNU(wcG% zCevHY5pU7n5ckNN$6KEs2l8|dufVI(8CCT%ENR4w9CEtqEIyd^hleelmd$d}f5W)P z9PGp$(Y+g-$C{mc5H`VT18K9qG$PQCQ&L;0=X&W9xh{3{a+k98=h*Evq8Sxk0#w4`+t=T$3Ws6QiE-mX^lh6~fS! z7@mVYm!xf-%J})XmP6L4C`5QP8J_45BFimFJnwQT^QC%ue*UnU z4w!f?%YBE!3iNuK!s=gjeP+^hozm-qYOgdvV_9u882Olrbbk4@>%E%yd@!#x zzi50)2Q&T{NG(T_8BZFL`kd@`=HwWJ!m`oW$>~^#>|Tvf zI(E@9jOwVE@oymfR7b^(e*@JEY{@E5cm<+&jO8$mM&QCs+HoZuZ4>18&Q59=y|H1W zC$=4oIpeq=uk}8%yf{{FkGnbF{}%L2+iTPxyAY3FaxvrgK;zNtW}Wp33$a~{!?Ab> zr*r9U_N=Id+vKUN6f!p+up@nP4qzVXl47d)>Sd7ou0|OC4oR9f#r@|U6|Ft=+Ksuk z?v1lse>nabQGQ*<1~uAZ=HOVpHtH0K5weT>nZ>Eh+L~akpGIPfQ+u3nEqqu_wCgOg zojrV&WXs(2!hJvZ?$w#d&PbJZbxxbxU^X^1BHDRmjl(N?ZR7rH=+ejycbcm6E7?;| zpNbG#+D>h3)sdRoY&_!vBY?*$?f~)4a9lOK!Xa8j#+<@ecbYZ0I#=n<=Y=oLKXTPs(>)3SrnDq<6MK>W? zwxY#@+-BSkKA{Z*Jx+E<$?Lb-?ETbMH+SaFgP_kItp8AvD5lxQJR1{zXD{8BNxL{v zF9II|phO>J&jvaxy?BPM*+_dSCMU3G|7P57noH8m nv5xIw^8Zcnt3!}=DkVBtRM7LPoDClg&8?nJMk0sUB$xjIzmTxp literal 0 HcmV?d00001 diff --git a/addons/weapononback/functions/fnc_add.sqf b/addons/weapononback/functions/fnc_add.sqf new file mode 100644 index 00000000000..8204ffe9de0 --- /dev/null +++ b/addons/weapononback/functions/fnc_add.sqf @@ -0,0 +1,79 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Adds a weapon to the units back (global effect). + * + * Arguments: + * 0: Unit + * 1: Weapons items (weaponsItems format) + * + * 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; +}; diff --git a/addons/weapononback/functions/fnc_get.sqf b/addons/weapononback/functions/fnc_get.sqf new file mode 100644 index 00000000000..a6c15c22e9f --- /dev/null +++ b/addons/weapononback/functions/fnc_get.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Returns the weapon on a units back. + * + * Arguments: + * 0: Unit + * + * Return Value: + * Weapon, attachments and magazines (weaponsItems format) or empty 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 +}; diff --git a/addons/weapononback/functions/fnc_getIDCContainer.sqf b/addons/weapononback/functions/fnc_getIDCContainer.sqf new file mode 100644 index 00000000000..7ebd47d10b6 --- /dev/null +++ b/addons/weapononback/functions/fnc_getIDCContainer.sqf @@ -0,0 +1,35 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Gets the container associated with the given inventory display IDC. + * + * Arguments: + * 0: Control IDC + * + * Return Value: + * Container + * + * 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 + }; +}; diff --git a/addons/weapononback/functions/fnc_onDrag.sqf b/addons/weapononback/functions/fnc_onDrag.sqf new file mode 100644 index 00000000000..01baf4b0b86 --- /dev/null +++ b/addons/weapononback/functions/fnc_onDrag.sqf @@ -0,0 +1,94 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Handles dragging weapons onto the secondary slot. + * + * Arguments: + * 0: Drag origin + * 1: Information about the dragged item + * + * 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]; diff --git a/addons/weapononback/functions/fnc_onDragWOB.sqf b/addons/weapononback/functions/fnc_onDragWOB.sqf new file mode 100644 index 00000000000..2effcb285d7 --- /dev/null +++ b/addons/weapononback/functions/fnc_onDragWOB.sqf @@ -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 + * 1: Mouse button ID + * + * 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]; diff --git a/addons/weapononback/functions/fnc_onDrop.sqf b/addons/weapononback/functions/fnc_onDrop.sqf new file mode 100644 index 00000000000..530405b7c7f --- /dev/null +++ b/addons/weapononback/functions/fnc_onDrop.sqf @@ -0,0 +1,84 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Handles dropping a weapon onto the secondary slot. + * + * Arguments: + * 0: Drop catcher control + * 1: Ignored + * 2: Ignored + * 3: Control that the dragging operation started from + * 4: Information about the dragged item + * + * 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); +}; diff --git a/addons/weapononback/functions/fnc_onDropWOB.sqf b/addons/weapononback/functions/fnc_onDropWOB.sqf new file mode 100644 index 00000000000..f426b5a93be --- /dev/null +++ b/addons/weapononback/functions/fnc_onDropWOB.sqf @@ -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 + * + * 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; +}; diff --git a/addons/weapononback/functions/fnc_onInventoryOpened.sqf b/addons/weapononback/functions/fnc_onInventoryOpened.sqf new file mode 100644 index 00000000000..6e29083d706 --- /dev/null +++ b/addons/weapononback/functions/fnc_onInventoryOpened.sqf @@ -0,0 +1,48 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Adds event handlers when opening the inventory display. + * + * Arguments: + * 0: Inventory 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; + }; +}]; diff --git a/addons/weapononback/functions/fnc_onWHSInit.sqf b/addons/weapononback/functions/fnc_onWHSInit.sqf new file mode 100644 index 00000000000..69b2a2b14a6 --- /dev/null +++ b/addons/weapononback/functions/fnc_onWHSInit.sqf @@ -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 + * + * 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); +}; diff --git a/addons/weapononback/functions/fnc_onWeaponChange.sqf b/addons/weapononback/functions/fnc_onWeaponChange.sqf new file mode 100644 index 00000000000..1abf7f5a6e6 --- /dev/null +++ b/addons/weapononback/functions/fnc_onWeaponChange.sqf @@ -0,0 +1,19 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Handles switching to the weapon on the back. + * + * Arguments: + * 0: Player + * 1: Weapon that was swapped to + * + * Return Value: + * None + * + * Public: No + */ +params ["", "_weapon"]; + +if (_weapon != QGVAR(weapon)) exitWith {}; + +[FUNC(swap), [], 2] call CBA_fnc_waitAndExecute; diff --git a/addons/weapononback/functions/fnc_remove.sqf b/addons/weapononback/functions/fnc_remove.sqf new file mode 100644 index 00000000000..30589f53219 --- /dev/null +++ b/addons/weapononback/functions/fnc_remove.sqf @@ -0,0 +1,40 @@ +#include "script_component.hpp" +/* + * Author: BaerMitUmlaut + * Removes the weapon on the back of a unit (global effect). + * + * Arguments: + * 0: Unit + * + * 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); +}; diff --git a/addons/weapononback/functions/fnc_renderPFH.sqf b/addons/weapononback/functions/fnc_renderPFH.sqf new file mode 100644 index 00000000000..c27c722e1fe --- /dev/null +++ b/addons/weapononback/functions/fnc_renderPFH.sqf @@ -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; diff --git a/addons/weapononback/functions/fnc_swap.sqf b/addons/weapononback/functions/fnc_swap.sqf new file mode 100644 index 00000000000..454cd775dee --- /dev/null +++ b/addons/weapononback/functions/fnc_swap.sqf @@ -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); +}; diff --git a/addons/weapononback/functions/fnc_updateInventory.sqf b/addons/weapononback/functions/fnc_updateInventory.sqf new file mode 100644 index 00000000000..ada7746a836 --- /dev/null +++ b/addons/weapononback/functions/fnc_updateInventory.sqf @@ -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) + * + * 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; diff --git a/addons/weapononback/functions/script_component.hpp b/addons/weapononback/functions/script_component.hpp new file mode 100644 index 00000000000..07c8cab8367 --- /dev/null +++ b/addons/weapononback/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\weapononback\script_component.hpp" diff --git a/addons/weapononback/initSettings.sqf b/addons/weapononback/initSettings.sqf new file mode 100644 index 00000000000..1b2409c0780 --- /dev/null +++ b/addons/weapononback/initSettings.sqf @@ -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; diff --git a/addons/weapononback/script_component.hpp b/addons/weapononback/script_component.hpp new file mode 100644 index 00000000000..886a1ee66ef --- /dev/null +++ b/addons/weapononback/script_component.hpp @@ -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) diff --git a/addons/weapononback/stringtable.xml b/addons/weapononback/stringtable.xml new file mode 100644 index 00000000000..f528290adbd --- /dev/null +++ b/addons/weapononback/stringtable.xml @@ -0,0 +1,21 @@ + + + + + Weapon on back + Waffe auf dem Rücken + + + Render limit + Renderlimit + + + Limit the amount of weapons on backs to be rendered (-1 for no limit). + Limitiert die Anzahl der Waffen auf dem Rücken die gerendert werden (-1 bedeutet kein Limit). + + + Could not fit %1 into %2. + %1 passt nicht in %2. + + +