diff --git a/addons/ai/functions/fnc_suppressiveFire.sqf b/addons/ai/functions/fnc_suppressiveFire.sqf index 635317ffb..ef06f265a 100644 --- a/addons/ai/functions/fnc_suppressiveFire.sqf +++ b/addons/ai/functions/fnc_suppressiveFire.sqf @@ -51,25 +51,10 @@ if (_unit isEqualType grpNull) exitWith { }; // If a vehicle is given directly, use its gunner as the unit -if !(_unit isKindOf "CAManBase") then { - _unit = gunner _unit; -}; - -if ( - !alive _unit - || {isPlayer _unit} - || {!(lifeState _unit in ["HEALTHY", "INJURED"])} - || {_unit getVariable [QGVAR(isSuppressing), false]} - || { - private _vehicle = vehicle _unit; +private _unit = _unit call EFUNC(common,getEffectiveGunner); - if (_vehicle == _unit || {_unit call EFUNC(common,isUnitFFV)}) then { - currentWeapon _unit == "" - } else { - _vehicle weaponsTurret (_vehicle unitTurret _unit) isEqualTo [] - }; - } -) exitWith {}; +// Exit if the unit cannot fire or is already suppressing +if (!([_unit, true, true] call EFUNC(common,canFire)) || {_unit getVariable [QGVAR(isSuppressing), false]}) exitWith {}; // Prevent the unit from performing other suppressive fire tasks while this one is active _unit setVariable [QGVAR(isSuppressing), true, true]; @@ -177,47 +162,11 @@ private _endTime = CBA_missionTime + _duration + TARGETING_DELAY; }; if (CBA_missionTime >= _shotTime) then { + [_unit, true] call EFUNC(common,fireWeapon); + private _vehicle = vehicle _unit; private _turretPath = _vehicle unitTurret _unit; - switch (true) do { - // On foot - case (_vehicle == _unit): { - weaponState _unit params ["_weapon", "_muzzle", "_fireMode"]; - - _unit setAmmo [_weapon, 1e6]; - _unit forceWeaponFire [_muzzle, _fireMode]; - }; - - // FFV - case (_unit call EFUNC(common,isUnitFFV)): { - // Using UseMagazine action since forceWeaponFire command does not work for FFV units - // UseMagazine action doesn't seem to work with currently loaded magazine (currentMagazineDetail) - // Therefore, this relies on the unit having an extra magazine in their inventory - // but should be fine in most situations - private _weapon = currentWeapon _unit; - private _compatibleMagazines = _weapon call CBA_fnc_compatibleMagazines; - private _index = magazines _unit findAny _compatibleMagazines; - if (_index == -1) exitWith {}; - - private _magazine = magazinesDetail _unit select _index; - _magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"]; - - _unit setAmmo [_weapon, 1e6]; - CBA_logic action ["UseMagazine", _unit, _unit, _owner, _id]; - }; - - // Vehicle gunner - default { - private _muzzle = weaponState [_vehicle, _turretPath] select 1; - _unit setAmmo [_muzzle, 1e6]; - - private _magazine = _vehicle currentMagazineDetailTurret _turretPath; - _magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"]; - _vehicle action ["UseMagazine", _vehicle, _unit, _owner, _id]; - }; - }; - // Set time until the next shot based on the weapon's ammo reloading time and whether the current burst is finished private _reloadTime = [_vehicle, _turretPath] call EFUNC(common,getWeaponReloadTime); _currentBurstRounds = _currentBurstRounds + 1; diff --git a/addons/attributes/initAttributes.sqf b/addons/attributes/initAttributes.sqf index 24038252d..94613aaf5 100644 --- a/addons/attributes/initAttributes.sqf +++ b/addons/attributes/initAttributes.sqf @@ -185,6 +185,19 @@ {alive _entity && {_entity isKindOf "LandVehicle" || {_entity isKindOf "Air"} || {_entity isKindOf "Ship"}}} ] call FUNC(addAttribute); +[ + "Object", + [ELSTRING(building_markers,BuildingMarker), ELSTRING(building_markers,BuildingMarker_Tooltip)], + QGVAR(toolbox), + [1, 2, [ELSTRING(common,Disabled), ELSTRING(common,Enabled)]], + { + private _buildings = SELECTED_OBJECTS select {_x isKindOf "Building"}; + [_buildings, _value] call EFUNC(building_markers,set); + }, + {_entity getVariable [QEGVAR(building_markers,marker), ""] != ""}, + {_entity isKindOf "Building"} +] call FUNC(addAttribute); + [ "Object", LSTRING(PlateNumber), diff --git a/addons/building_markers/$PBOPREFIX$ b/addons/building_markers/$PBOPREFIX$ new file mode 100644 index 000000000..bd1072a15 --- /dev/null +++ b/addons/building_markers/$PBOPREFIX$ @@ -0,0 +1 @@ +x\zen\addons\building_markers diff --git a/addons/building_markers/CfgEventHandlers.hpp b/addons/building_markers/CfgEventHandlers.hpp new file mode 100644 index 000000000..f6503c247 --- /dev/null +++ b/addons/building_markers/CfgEventHandlers.hpp @@ -0,0 +1,17 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; + +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; + +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/building_markers/XEH_PREP.hpp b/addons/building_markers/XEH_PREP.hpp new file mode 100644 index 000000000..d31889c12 --- /dev/null +++ b/addons/building_markers/XEH_PREP.hpp @@ -0,0 +1,3 @@ +PREP(handleObjectEdited); +PREP(handleObjectPlaced); +PREP(set); diff --git a/addons/building_markers/XEH_postInit.sqf b/addons/building_markers/XEH_postInit.sqf new file mode 100644 index 000000000..38fa35da9 --- /dev/null +++ b/addons/building_markers/XEH_postInit.sqf @@ -0,0 +1,15 @@ +#include "script_component.hpp" + +if (isServer) then { + [QGVAR(set), LINKFUNC(set)] call CBA_fnc_addEventHandler; +}; + +if (hasInterface) then { + EGVAR(area_markers,blacklist) pushBack QUOTE(ADDON); + + [QEGVAR(placement,done), { + params ["_object"]; + + [nil, _object] call FUNC(handleObjectPlaced); + }] call CBA_fnc_addEventHandler; +}; diff --git a/addons/building_markers/XEH_preInit.sqf b/addons/building_markers/XEH_preInit.sqf new file mode 100644 index 000000000..cfac7f501 --- /dev/null +++ b/addons/building_markers/XEH_preInit.sqf @@ -0,0 +1,18 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +#include "initSettings.sqf" + +["ModuleCurator_F", "Init", { + params ["_logic"]; + + _logic addEventHandler ["CuratorObjectPlaced", {call FUNC(handleObjectPlaced)}]; + _logic addEventHandler ["CuratorObjectEdited", {call FUNC(handleObjectEdited)}]; +}, true, [], true] call CBA_fnc_addClassEventHandler; + +ADDON = true; diff --git a/addons/building_markers/XEH_preStart.sqf b/addons/building_markers/XEH_preStart.sqf new file mode 100644 index 000000000..022888575 --- /dev/null +++ b/addons/building_markers/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/building_markers/config.cpp b/addons/building_markers/config.cpp new file mode 100644 index 000000000..6e7f8a521 --- /dev/null +++ b/addons/building_markers/config.cpp @@ -0,0 +1,19 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"zen_common"}; + author = ECSTRING(main,Author); + authors[] = {"Ampersand"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +PRELOAD_ADDONS; + +#include "CfgEventHandlers.hpp" diff --git a/addons/building_markers/functions/fnc_handleObjectEdited.sqf b/addons/building_markers/functions/fnc_handleObjectEdited.sqf new file mode 100644 index 000000000..044a4766b --- /dev/null +++ b/addons/building_markers/functions/fnc_handleObjectEdited.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Handles editing of an object by Zeus. + * + * Arguments: + * 0: Curator (not used) + * 1: Edited Object + * + * Return Value: + * None + * + * Example: + * [_curator, _object] call zen_building_markers_fnc_handleObjectEdited + * + * Public: No + */ + +params ["", "_object"]; + +private _marker = _object getVariable [QGVAR(marker), ""]; + +if (_marker != "") then { + [QGVAR(set), [_object, true]] call CBA_fnc_serverEvent; +}; diff --git a/addons/building_markers/functions/fnc_handleObjectPlaced.sqf b/addons/building_markers/functions/fnc_handleObjectPlaced.sqf new file mode 100644 index 000000000..639dca651 --- /dev/null +++ b/addons/building_markers/functions/fnc_handleObjectPlaced.sqf @@ -0,0 +1,23 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Handles placement of an object by Zeus. + * + * Arguments: + * 0: Curator (not used) + * 1: Placed Object + * + * Return Value: + * None + * + * Example: + * [_curator, _object] call zen_building_markers_fnc_handleObjectPlaced + * + * Public: No + */ + +params ["", "_object"]; + +if (GVAR(enabled) && {_object isKindOf "Building"}) then { + [QGVAR(set), [_object, true]] call CBA_fnc_serverEvent; +}; diff --git a/addons/building_markers/functions/fnc_set.sqf b/addons/building_markers/functions/fnc_set.sqf new file mode 100644 index 000000000..94f9dbbbc --- /dev/null +++ b/addons/building_markers/functions/fnc_set.sqf @@ -0,0 +1,79 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Creates or deletes the given object's building marker. + * + * Building markers are grey rectangle area markers for the object's bounding box + * that mimic those of terrain buildings. + * + * When called in create mode for an object that already has a building marker, + * the object's marker is updated to reflect its current position and direction. + * + * The marker is automatically updated if the object is edited by Zeus and + * deleted when the object is deleted. However, the marker will not update if the + * object is edited by some other means (e.g., through script). + * + * Arguments: + * 0: Object(s) + * 1: Set (create or delete) + * + * Return Value: + * None + * + * Example: + * [_object, true] call zen_building_markers_fnc_set + * + * Public: No + */ + +if (!isServer) exitWith { + [QGVAR(set), _this] call CBA_fnc_serverEvent; +}; + +params [["_object", objNull, [objNull, []]], ["_set", true, [true]]]; + +if (_object isEqualType []) exitWith { + { + [_x, _set] call FUNC(set); + } forEach _object; +}; + +if (isNull _object) exitWith {}; + +private _marker = _object getVariable [QGVAR(marker), ""]; + +if (_set) then { + // Only update marker if it already exists + if (_marker != "") exitWith { + _marker setMarkerPosLocal getPos _object; + _marker setMarkerDir getDir _object; + }; + + // Create marker for the given object + 0 boundingBoxReal _object params ["_p0", "_p1"]; + private _size = _p1 vectorDiff _p0 vectorMultiply 0.5 select [0, 2]; + + _marker = createMarker [format [QGVAR(%1), _object call BIS_fnc_netId], _object]; + _marker setMarkerShapeLocal "RECTANGLE"; + _marker setMarkerColorLocal "ColorGrey"; + _marker setMarkerBrushLocal "SolidFull"; + _marker setMarkerSizeLocal _size; + _marker setMarkerDir getDir _object; + _object setVariable [QGVAR(marker), _marker, true]; + + // Delete marker when the object is deleted + private _eventID = _object addEventHandler ["Deleted", { + params ["_object"]; + + deleteMarker (_object getVariable [QGVAR(marker), ""]); + }]; + + _object setVariable [QGVAR(eventID), _eventID]; +} else { + if (_marker == "") exitWith {}; + + private _eventID = _object getVariable [QGVAR(eventID), -1]; + _object removeEventHandler ["Deleted", _eventID]; + _object setVariable [QGVAR(marker), nil, true]; + deleteMarker _marker; +}; diff --git a/addons/building_markers/functions/script_component.hpp b/addons/building_markers/functions/script_component.hpp new file mode 100644 index 000000000..e1910a447 --- /dev/null +++ b/addons/building_markers/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\x\zen\addons\building_markers\script_component.hpp" diff --git a/addons/building_markers/initSettings.sqf b/addons/building_markers/initSettings.sqf new file mode 100644 index 000000000..96d83a8f7 --- /dev/null +++ b/addons/building_markers/initSettings.sqf @@ -0,0 +1,8 @@ +[ + QGVAR(enabled), + "CHECKBOX", + [LSTRING(Enabled), LSTRING(Enabled_Description)], + [ELSTRING(main,DisplayName), LSTRING(DisplayName)], + false, + false +] call CBA_fnc_addSetting; diff --git a/addons/building_markers/script_component.hpp b/addons/building_markers/script_component.hpp new file mode 100644 index 000000000..cbd332152 --- /dev/null +++ b/addons/building_markers/script_component.hpp @@ -0,0 +1,17 @@ +#define COMPONENT building_markers +#define COMPONENT_BEAUTIFIED Building Markers +#include "\x\zen\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_EDITOR + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_EDITOR + #define DEBUG_SETTINGS DEBUG_SETTINGS_EDITOR +#endif + +#include "\x\zen\addons\main\script_macros.hpp" diff --git a/addons/building_markers/stringtable.xml b/addons/building_markers/stringtable.xml new file mode 100644 index 000000000..689e7dc05 --- /dev/null +++ b/addons/building_markers/stringtable.xml @@ -0,0 +1,20 @@ + + + + + Building Markers + + + Enable Automatic Building Markers + + + Enables automatically creating grey rectangle area markers (similarly to terrain structures) for buildings placed through Zeus. + + + Building Marker + + + Adds a grey rectangle area marker for the building, similarly to terrain structures. + + + diff --git a/addons/common/XEH_PREP.hpp b/addons/common/XEH_PREP.hpp index 698d88fb0..5fc2d6691 100644 --- a/addons/common/XEH_PREP.hpp +++ b/addons/common/XEH_PREP.hpp @@ -1,3 +1,4 @@ +PREP(canFire); PREP(changeGroupSide); PREP(collapseTree); PREP(createZeus); @@ -6,6 +7,7 @@ PREP(deserializeInventory); PREP(deserializeObjects); PREP(displayCuratorLoad); PREP(displayCuratorUnload); +PREP(drawHint); PREP(dumpPerformanceCounters); PREP(earthquake); PREP(ejectPassengers); @@ -13,6 +15,8 @@ PREP(exportMissionSQF); PREP(exportText); PREP(fireArtillery); PREP(fireVLS); +PREP(fireWeapon); +PREP(forceFire); PREP(formatDegrees); PREP(getActiveTree); PREP(getAllTurrets); @@ -20,6 +24,8 @@ PREP(getArtilleryETA); PREP(getCargoPositionsCount); PREP(getDefaultInventory); PREP(getDLC); +PREP(getEffectiveGunner); +PREP(getGunnerName); PREP(getLightingSelections); PREP(getPhoneticName); PREP(getPlayers); @@ -39,8 +45,10 @@ PREP(initListNBoxSorting); PREP(initOwnersControl); PREP(initSidesControl); PREP(initSliderEdit); +PREP(isCursorOnMouseArea); PREP(isInScreenshotMode); PREP(isPlacementActive); +PREP(isReloading); PREP(isRemoteControlled); PREP(isSwimming); PREP(isUnitFFV); @@ -62,3 +70,4 @@ PREP(setVehicleAmmo); PREP(showMessage); PREP(spawnLargeObject); PREP(teleportIntoVehicle); +PREP(updateEditableObjects); diff --git a/addons/common/XEH_postInit.sqf b/addons/common/XEH_postInit.sqf index f00ef720b..dd77cdc26 100644 --- a/addons/common/XEH_postInit.sqf +++ b/addons/common/XEH_postInit.sqf @@ -178,6 +178,11 @@ _unit selectWeapon _muzzle; }] call CBA_fnc_addEventHandler; +[QGVAR(selectWeaponTurret), { + params ["_vehicle", "_weapon", "_turretPath", ["_muzzle", ""], ["_fireMode", ""]]; + _vehicle selectWeaponTurret [_weapon, _turretPath, _muzzle, _fireMode]; +}] call CBA_fnc_addEventHandler; + [QGVAR(setPilotLight), { params ["_vehicle", "_lights"]; _vehicle setPilotLight _lights; @@ -319,6 +324,9 @@ [QGVAR(earthquake), LINKFUNC(earthquake)] call CBA_fnc_addEventHandler; [QGVAR(fireArtillery), LINKFUNC(fireArtillery)] call CBA_fnc_addEventHandler; +[QGVAR(fireWeapon), LINKFUNC(fireWeapon)] call CBA_fnc_addEventHandler; +[QGVAR(forceFire), LINKFUNC(forceFire)] call CBA_fnc_addEventHandler; +[QGVAR(loadMagazineInstantly), LINKFUNC(loadMagazineInstantly)] call CBA_fnc_addEventHandler; [QGVAR(setLampState), LINKFUNC(setLampState)] call CBA_fnc_addEventHandler; [QGVAR(setMagazineAmmo), LINKFUNC(setMagazineAmmo)] call CBA_fnc_addEventHandler; [QGVAR(setTurretAmmo), LINKFUNC(setTurretAmmo)] call CBA_fnc_addEventHandler; @@ -360,30 +368,6 @@ if (isServer) then { _waypoint setWaypointSpeed _speedMode; }] call CBA_fnc_addEventHandler; - [QGVAR(addObjects), { - params ["_objects", ["_curator", objNull]]; - - if (!isNull _curator) exitWith { - _curator addCuratorEditableObjects [_objects, true]; - }; - - { - _x addCuratorEditableObjects [_objects, true]; - } forEach allCurators; - }] call CBA_fnc_addEventHandler; - - [QGVAR(removeObjects), { - params ["_objects", ["_curator", objNull]]; - - if (!isNull _curator) exitWith { - _curator removeCuratorEditableObjects [_objects, true]; - }; - - { - _x removeCuratorEditableObjects [_objects, true]; - } forEach allCurators; - }] call CBA_fnc_addEventHandler; - { ["AllVehicles", "InitPost", { params ["_object"]; @@ -408,4 +392,5 @@ if (isServer) then { [QGVAR(createZeus), LINKFUNC(createZeus)] call CBA_fnc_addEventHandler; [QGVAR(deserializeObjects), LINKFUNC(deserializeObjects)] call CBA_fnc_addEventHandler; + [QGVAR(updateEditableObjects), LINKFUNC(updateEditableObjects)] call CBA_fnc_addEventHandler; }; diff --git a/addons/common/XEH_preInit.sqf b/addons/common/XEH_preInit.sqf index c39019352..ba495c029 100644 --- a/addons/common/XEH_preInit.sqf +++ b/addons/common/XEH_preInit.sqf @@ -6,6 +6,8 @@ PREP_RECOMPILE_START; #include "XEH_PREP.hpp" PREP_RECOMPILE_END; +GVAR(mousePos) = [0, 0]; + GVAR(aceMedical) = isClass (configFile >> "CfgPatches" >> "ace_medical"); GVAR(aceMedicalTreatment) = isClass (configFile >> "CfgPatches" >> "ace_medical_treatment"); diff --git a/addons/common/functions/fnc_canFire.sqf b/addons/common/functions/fnc_canFire.sqf new file mode 100644 index 000000000..0c1d333d6 --- /dev/null +++ b/addons/common/functions/fnc_canFire.sqf @@ -0,0 +1,43 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Ampersand + * Checks if the given unit or vehicle can fire its current weapon. + * + * Arguments: + * 0: Unit or Vehicle + * 1: Ignore Ammo (default: false) + * 2: Ignore Reload (default: false) + * + * Return Value: + * Can Fire + * + * Example: + * [_unit] call zen_common_fnc_canFire + * + * Public: No + */ + +params [["_unit", objNull, [objNull]], ["_ignoreAmmo", false, [false]], ["_ignoreReload", false, [false]]]; + +private _unit = _unit call FUNC(getEffectiveGunner); + +alive _unit +&& {!isPlayer _unit} +&& {lifeState _unit in ["HEALTHY", "INJURED"]} +&& { + private _vehicle = vehicle _unit; + + if (_vehicle == _unit || {_unit call FUNC(isUnitFFV)}) then { + currentWeapon _unit != "" + && {_ignoreAmmo || {_unit ammo currentMuzzle _unit > 0}} + && {_ignoreReload || {!(_unit call FUNC(isReloading))}} + } else { + private _turretPath = _vehicle unitTurret _unit; + weaponState [_vehicle, _turretPath] params ["_weapon", "", "", "", "_ammoCount"]; + + _weapon != "" + && {!("fake" in toLower _weapon)} + && {_ignoreAmmo || {_ammoCount > 0} || {_weapon isKindOf ["CarHorn", configFile >> "CfgWeapons"]}} + && {_ignoreReload || {!([_vehicle, _turretPath] call FUNC(isReloading))}} + }; +} diff --git a/addons/common/functions/fnc_deserializeObjects.sqf b/addons/common/functions/fnc_deserializeObjects.sqf index 96fd59fe6..f62e9bdba 100644 --- a/addons/common/functions/fnc_deserializeObjects.sqf +++ b/addons/common/functions/fnc_deserializeObjects.sqf @@ -285,7 +285,7 @@ private _fnc_deserializeObject = { } forEach _objectData; if (_makeEditable) then { - [QGVAR(addObjects), [_objects]] call CBA_fnc_serverEvent; + [_objects] call FUNC(updateEditableObjects); }; _objects diff --git a/addons/common/functions/fnc_displayCuratorLoad.sqf b/addons/common/functions/fnc_displayCuratorLoad.sqf index 5e9009bb5..a0a06dfed 100644 --- a/addons/common/functions/fnc_displayCuratorLoad.sqf +++ b/addons/common/functions/fnc_displayCuratorLoad.sqf @@ -22,5 +22,17 @@ if (GVAR(disableGearAnim) && {vehicle player == player}) then { [{player switchMove _this}, animationState player] call CBA_fnc_execNextFrame; }; +// Track mouse position from mouse area control to handle the mouse being over other UI elements +// RscDisplayCurator_mousePos from base game attempts to do this but for some reason also updates when +// the mouse is over the mission controls group +private _fnc_updateMousePos = { + params ["", "_posX", "_posY"]; + GVAR(mousePos) = [_posX, _posY]; +}; + +private _ctrlMouseArea = _display displayCtrl IDC_RSCDISPLAYCURATOR_MOUSEAREA; +_ctrlMouseArea ctrlAddEventHandler ["MouseMoving", _fnc_updateMousePos]; +_ctrlMouseArea ctrlAddEventHandler ["MouseHolding", _fnc_updateMousePos]; + // Emit display load event ["zen_curatorDisplayLoaded", _display] call CBA_fnc_localEvent; diff --git a/addons/common/functions/fnc_drawHint.sqf b/addons/common/functions/fnc_drawHint.sqf new file mode 100644 index 000000000..448364b68 --- /dev/null +++ b/addons/common/functions/fnc_drawHint.sqf @@ -0,0 +1,198 @@ +#include "script_component.hpp" +/* + * Author: Ampersand, mharis001 + * Draws a hint that contains icon and line elements in 2D (Zeus display map) + * and 3D (in world) for the given duration. + * + * Will overwrite an existing hint when called using the same ID. Position + * arguments can be given as OBJECTs, in which case the hint elements will + * follow objects as they move and will be hidden if the object is deleted. + * + * The visual properties for "ICON" elements are: + * 0: Position + * 1: Icon Texture + * 2: Color (RGBA) (default: [1, 1, 1, 1]) + * 3: Scale (default: 1) + * 4: Angle (default: 0) + * 5: Text (default: "") + * 6: Shadow (default: 0) + * 7: Text Size (default: 0.05) + * 8: Font (default: "RobotoCondensed") + * 9: Align (default: "center") + * + * The visual properties for "LINE" elements are: + * 0: Start Position + * 1: End Position + * 2: Color (RGBA) (default: [1, 1, 1, 1]) + * + * Arguments: + * 0: Elements + * 0: Type + * - either "ICON" or "LINE". + * 1: Visual Properties + * - depends on element type (see above for details). + * 1: Duration (in seconds) + * 2: ID (default: "") + * - an ID is generated when an empty string is given. + * - in the case of an OBJECT, the hash value is used. + * + * Return Value: + * ID + * + * Example: + * [["ICON", [_unit, _texture]], 3] call zen_common_fnc_drawHint + * + * Public: No + */ + +#define MAP_ICON_SIZE 24 + +params [ + ["_elements", [], [[]]], + ["_duration", 0, [0]], + ["_id", "", ["", objNull]] +]; + +private _ctrlMap = findDisplay IDD_RSCDISPLAYCURATOR displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; + +// Map of hint IDs and their corresponding draw (2D and 3D) event handler IDs +if (isNil QGVAR(drawHintMap)) then { + GVAR(drawHintMap) = createHashMap; +}; + +// Use an object's hash value as its hint ID +if (_id isEqualType objNull) then { + _id = hashValue _id; +}; + +// Generate a hint ID if one is not given +if (_id isEqualTo "") then { + if (isNil QGVAR(drawHintCounter)) then { + GVAR(drawHintCounter) = -1; + }; + + GVAR(drawHintCounter) = GVAR(drawHintCounter) + 1; + + _id = [CBA_clientID, GVAR(drawHintCounter)] joinString ":"; +}; + +// Remove an existing hint with the same ID +if (_id in GVAR(drawHintMap)) then { + GVAR(drawHintMap) deleteAt _id params ["_id2D", "_id3D"]; + + _ctrlMap ctrlRemoveEventHandler ["Draw", _id2D]; + removeMissionEventHandler ["Draw3D", _id3D]; +}; + +// Validate the given hint elements and separate them by type +private _icons = []; +private _lines = []; + +{ + _x params [["_type", "", [""]], ["_args", [], [[]]]]; + + switch (_type) do { + case "ICON": { + _args params [ + ["_position", [0, 0, 0], [[], objNull], 3], + ["_icon", "", [""]], + ["_color", [1, 1, 1, 1], [[]], 4], + ["_scale", 1, [0]], + ["_angle", 0, [0]], + ["_text", "", [""]], + ["_shadow", 0, [0, false]], + ["_textSize", 0.05, [0]], + ["_font", "RobotoCondensed", [""]], + ["_align", "center", [""]] + ]; + + _icons pushBack [_position, _icon, _color, _scale, _angle, _text, _shadow, _textSize, _font, _align]; + }; + case "LINE": { + _args params [ + ["_begPos", [0, 0, 0], [[], objNull], 3], + ["_endPos", [0, 0, 0], [[], objNull], 3], + ["_color", [1, 1, 1, 1], [[]], 4] + ]; + + _lines pushBack [_begPos, _endPos, _color]; + }; + default { + ERROR_1("Invalid hint element type - %1.",_type); + }; + }; +} forEach _elements; + +// Add event handlers to draw the hint elements +private _fnc_draw2D = { + params ["_ctrlMap"]; + _thisArgs params ["_icons", "_lines"]; + + { + _x params ["_position", "_icon", "_color", "_scale", "_angle", "_text", "_shadow", "_textSize", "_font", "_align"]; + + if (_position isEqualTo objNull) then {continue}; + + _ctrlMap drawIcon [_icon, _color, _position, _scale * MAP_ICON_SIZE, _scale * MAP_ICON_SIZE, _angle, _text, _shadow, _textSize, _font, _align]; + } forEach _icons; + + { + _x params ["_begPos", "_endPos", "_color"]; + + if (objNull in [_begPos, _endPos]) then {continue}; + + _ctrlMap drawLine [_begPos, _endPos, _color]; + } forEach _lines; +}; + +private _fnc_draw3D = { + _thisArgs params ["_icons", "_lines", "_endTime", "_id"]; + + // Exit if the Zeus display is closed or hint duration is complete + if (isNull curatorCamera || {CBA_missionTime >= _endTime}) exitWith { + GVAR(drawHintMap) deleteAt _id params ["_id2D", "_id3D"]; + + private _ctrlMap = findDisplay IDD_RSCDISPLAYCURATOR displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; + _ctrlMap ctrlRemoveEventHandler ["Draw", _id2D]; + removeMissionEventHandler ["Draw3D", _id3D]; + }; + + // No 3D drawing needed if the map is visible + if (visibleMap) exitWith {}; + + { + _x params ["_position", "_icon", "_color", "_scale", "_angle", "_text", "_shadow", "_textSize", "_font", "_align"]; + + if (_position isEqualTo objNull) then {continue}; + + if (_position isEqualType objNull) then { + _position = unitAimPositionVisual _position; + }; + + drawIcon3D [_icon, _color, _position, _scale, _scale, _angle, _text, _shadow, _textSize, _font, _align]; + } forEach _icons; + + { + _x params ["_begPos", "_endPos", "_color"]; + + if (objNull in [_begPos, _endPos]) then {continue}; + + if (_begPos isEqualType objNull) then { + _begPos = unitAimPositionVisual _begPos; + }; + + if (_endPos isEqualType objNull) then { + _endPos = unitAimPositionVisual _endPos; + }; + + drawLine3D [_begPos, _endPos, _color]; + } forEach _lines; +}; + +private _args = [_icons, _lines, CBA_missionTime + _duration, _id]; +private _id2D = [_ctrlMap, "Draw", _fnc_draw2D, _args] call CBA_fnc_addBISEventHandler; +private _id3D = [missionNamespace, "Draw3D", _fnc_draw3D, _args] call CBA_fnc_addBISEventHandler; +GVAR(drawHintMap) set [_id, [_id2D, _id3D]]; + +// Return the hint ID (in case a generated one was used) +_id diff --git a/addons/common/functions/fnc_fireWeapon.sqf b/addons/common/functions/fnc_fireWeapon.sqf new file mode 100644 index 000000000..82181c706 --- /dev/null +++ b/addons/common/functions/fnc_fireWeapon.sqf @@ -0,0 +1,78 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Ampersand + * Makes the given unit fire their current or their vehicle turret's weapon. + * + * Arguments: + * 0: Unit + * 1: Infinite Ammo (default: false) + * + * Return Value: + * None + * + * Example: + * [_unit] call zen_common_fnc_fireWeapon + * + * Public: No + */ + +params [["_unit", objNull, [objNull]], ["_infiniteAmmo", false, [false]]]; + +if (!local _unit) exitWith { + [QGVAR(fireWeapon), _this, _unit] call CBA_fnc_targetEvent; +}; + +private _unit = _unit call FUNC(getEffectiveGunner); +private _vehicle = vehicle _unit; + +switch (true) do { + // On foot + case (_vehicle == _unit): { + weaponState _unit params ["_weapon", "_muzzle", "_fireMode"]; + + if (_infiniteAmmo) then { + _unit setAmmo [_weapon, 1e6]; + }; + + _unit forceWeaponFire [_muzzle, _fireMode]; + }; + + // FFV + case (_unit call EFUNC(common,isUnitFFV)): { + // Using UseMagazine action since forceWeaponFire command does not work for FFV units + // UseMagazine action doesn't seem to work with currently loaded magazine (currentMagazineDetail) + // Therefore, this relies on the unit having an extra magazine in their inventory + // but should be fine in most situations + private _weapon = currentWeapon _unit; + private _compatibleMagazines = _weapon call CBA_fnc_compatibleMagazines; + private _index = magazines _unit findAny _compatibleMagazines; + if (_index == -1) exitWith {}; + + private _magazine = magazinesDetail _unit select _index; + _magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"]; + + if (_infiniteAmmo) then { + _unit setAmmo [_weapon, 1e6]; + }; + + CBA_logic action ["UseMagazine", _unit, _unit, _owner, _id]; + }; + + // Vehicle gunner + default { + private _turretPath = _vehicle unitTurret _unit; + weaponState [_vehicle, _turretPath] params ["_weapon", "_muzzle", "_fireMode"]; + + if (_weapon isKindOf ["CarHorn", configFile >> "CfgWeapons"]) exitWith { + _unit forceWeaponFire [_muzzle, _fireMode]; + }; + + if (_infiniteAmmo) then { + _unit setAmmo [_muzzle, 1e6]; + }; + + private _magazine = _vehicle currentMagazineDetailTurret _turretPath; + _magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"]; + _vehicle action ["UseMagazine", _vehicle, _unit, _owner, _id]; + }; +}; diff --git a/addons/common/functions/fnc_forceFire.sqf b/addons/common/functions/fnc_forceFire.sqf new file mode 100644 index 000000000..e55304ec7 --- /dev/null +++ b/addons/common/functions/fnc_forceFire.sqf @@ -0,0 +1,55 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Makes the given units or vehicles fire their current weapons. + * + * Arguments: + * 0: Units + * 1: ID + * + * Return Value: + * None + * + * Example: + * [_units, 0] call zen_common_fnc_forceFire + * + * Public: No + */ + +#define FORCE_FIRE_TIMEOUT 10 + +params ["_units", "_id"]; + +if (isNil QGVAR(forceFiringCurators)) then { + GVAR(forceFiringCurators) = []; +}; + +// If no units are given, then stop firing +if (_units isEqualTo []) exitWith { + GVAR(forceFiringCurators) deleteAt (GVAR(forceFiringCurators) find _id); +}; + +// Track which curators are forcing fire on local machine +GVAR(forceFiringCurators) pushBackUnique _id; + +// Repeatedly fire weapons of local units +private _units = _units apply { + _x call EFUNC(common,getEffectiveGunner) +} select { + local _x +}; + +[{ + params ["_args", "_pfhID"]; + _args params ["_units", "_id", "_endTime"]; + + if (CBA_missionTime >= _endTime || {!(_id in GVAR(forceFiringCurators))}) exitWith { + [_pfhID] call CBA_fnc_removePerFrameHandler; + }; + + { + if (_x call EFUNC(common,canFire)) then { + _x call EFUNC(common,fireWeapon); + }; + } forEach _units; +}, 0.05, [_units, _id, CBA_missionTime + FORCE_FIRE_TIMEOUT]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/common/functions/fnc_getAllTurrets.sqf b/addons/common/functions/fnc_getAllTurrets.sqf index 951bd29e9..2ce53487e 100644 --- a/addons/common/functions/fnc_getAllTurrets.sqf +++ b/addons/common/functions/fnc_getAllTurrets.sqf @@ -18,9 +18,9 @@ params ["_vehicle"]; private _turrets = if (_vehicle isEqualType objNull) then { - allTurrets _vehicle; + allTurrets _vehicle } else { - [_vehicle] call BIS_fnc_allTurrets; + [_vehicle] call BIS_fnc_allTurrets }; // Add the driver turret diff --git a/addons/common/functions/fnc_getEffectiveGunner.sqf b/addons/common/functions/fnc_getEffectiveGunner.sqf new file mode 100644 index 000000000..a81242ed3 --- /dev/null +++ b/addons/common/functions/fnc_getEffectiveGunner.sqf @@ -0,0 +1,40 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Return the given vehicle's effective gunner (unit seated in a turret with weapons). + * Priority order is the gunner, commander, other turrets (excluding FFV), and the driver. + * + * Arguments: + * 0: Vehicle + * + * Return Value: + * Gunner + * + * Example: + * [_vehicle] call zen_common_fnc_getEffectiveGunner + * + * Public: No + */ + +params [["_vehicle", objNull, [objNull]]]; + +// If a infantry unit is given, return it directly +if (_vehicle isKindOf "CAManBase") exitWith {_vehicle}; + +// Get units in the vehicle occupying turrets with wepaons +private _units = [gunner _vehicle, commander _vehicle]; +_units insert [-1, _vehicle call FUNC(getAllTurrets) apply {_vehicle turretUnit _x}, true]; + +private _index = _units findIf { + alive _x && {_vehicle weaponsTurret (_vehicle unitTurret _x) isNotEqualTo []} +}; + +// Select the first available gunner +// Fall back to effective commander if one in not found and manual fire is enabled +private _gunner = _units param [_index, objNull]; + +if (isNull _gunner && {isManualFire _vehicle}) then { + _gunner = effectiveCommander _vehicle; +}; + +_gunner diff --git a/addons/common/functions/fnc_getGunnerName.sqf b/addons/common/functions/fnc_getGunnerName.sqf new file mode 100644 index 000000000..6a5ac203e --- /dev/null +++ b/addons/common/functions/fnc_getGunnerName.sqf @@ -0,0 +1,27 @@ +#include "script_component.hpp" +/* + * Author: NeilZar + * Returns the (gunner) name of the given vehicle's specified turret. + * + * Arguments: + * 0: Vehicle + * 1: Turret Path + * + * Return Value: + * Gunner Name + * + * Example: + * ["B_MRAP_01_hmg_F", [0]] call zen_common_fnc_getGunnerName + * + * Public: No + */ + +params [["_vehicle", "", ["", objNull, configNull]], ["_turretPath", [], [[]]]]; + +private _name = getText ([_vehicle, _turretPath] call CBA_fnc_getTurret >> "gunnerName"); + +if (_name == "") then { + _name = localize (["str_driver", "str_pilot"] select (_vehicle isKindOf "Air")); +}; + +_name diff --git a/addons/common/functions/fnc_isCursorOnMouseArea.sqf b/addons/common/functions/fnc_isCursorOnMouseArea.sqf new file mode 100644 index 000000000..17f5feea5 --- /dev/null +++ b/addons/common/functions/fnc_isCursorOnMouseArea.sqf @@ -0,0 +1,22 @@ +#include "script_component.hpp" +/* + * Author: mharis001 + * Checks if the Zeus display's cursor is currently hovering over + * the mouse area (or the map) without any other overlapping controls + * such as the create or edit trees. + * + * Arguments: + * None + * + * Return Value: + * On Mouse Area + * + * Example: + * [] call zen_common_fnc_isCursorOnMouseArea + * + * Public: No + */ + +private _display = findDisplay IDD_RSCDISPLAYCURATOR; +private _ctrlIDC = ctrlIDC (_display ctrlAt getMousePosition); +_ctrlIDC in [IDC_RSCDISPLAYCURATOR_MOUSEAREA, IDC_RSCDISPLAYCURATOR_MAINMAP] diff --git a/addons/common/functions/fnc_isReloading.sqf b/addons/common/functions/fnc_isReloading.sqf new file mode 100644 index 000000000..850b8aa6b --- /dev/null +++ b/addons/common/functions/fnc_isReloading.sqf @@ -0,0 +1,38 @@ +#include "script_component.hpp" +/* + * Author: mharis001 + * Checks if the given unit or vehicle's weapon or muzzle is reloading. + * + * Arguments: + * 0: Unit or Vehicle + * 1: Turret Path (default: [0]) + * - Only used if the given entity is a vehicle. + * 2: Weapon or Muzzle (default: "") + * - Use "" for current weapon. + * + * Return Value: + * Is Reloading + * + * Example: + * [_unit, "arifle_MX_F"] call zen_common_fnc_isReloading + * + * Public: No + */ + +params [ + ["_entity", objNull, [objNull]], + ["_turretPath", [0], [[]]], + ["_weaponOrMuzzle", "", [""]] +]; + +// Get given weapon or muzzle's current state +private _state = if (_entity isKindOf "CAManBase") then { + _entity weaponState _weaponOrMuzzle +} else { + weaponState [_entity, _turretPath, _weaponOrMuzzle] +}; + +_state params ["", "", "", "", "", "_roundReloadPhase", "_magazineReloadPhase"]; + +// Is reloading if round or magazine reload phase is > 0 +_roundReloadPhase > 0 || {_magazineReloadPhase > 0} diff --git a/addons/common/functions/fnc_isUnitFFV.sqf b/addons/common/functions/fnc_isUnitFFV.sqf index c26e03245..db4475ba7 100644 --- a/addons/common/functions/fnc_isUnitFFV.sqf +++ b/addons/common/functions/fnc_isUnitFFV.sqf @@ -18,3 +18,4 @@ params [["_unit", objNull, [objNull]]]; fullCrew vehicle _unit select {_x select 0 == _unit} param [0, []] param [4, false] +&& {_unit call CBA_fnc_isTurnedOut} // Tank commander is only FFV when turned out diff --git a/addons/common/functions/fnc_loadMagazineInstantly.sqf b/addons/common/functions/fnc_loadMagazineInstantly.sqf index 931d8c794..a90c0906f 100755 --- a/addons/common/functions/fnc_loadMagazineInstantly.sqf +++ b/addons/common/functions/fnc_loadMagazineInstantly.sqf @@ -1,51 +1,69 @@ #include "script_component.hpp" /* * Author: Kex - * Instantly loads the given magazine into the specified weapon. + * Instantly loads the given magazine into the specified vehicle's turret weapon. * * Arguments: * 0: Vehicle - * 1: Turret path - * 2: Muzzle class - * 3: Magazine class + * 1: Turret Path + * 2: Weapon + * 3: Magazine * * Return Value: * None * * Example: - * [_vehicle, [0], "HE", "60Rnd_40mm_GPR_Tracer_Red_shells"] call zen_common_fnc_loadMagazineInstantly + * [_vehicle, [0, 0], "HMG_127_MBT", "200Rnd_127x99_mag_Tracer_Red"] call zen_common_fnc_loadMagazineInstantly * * Public: No */ -params [["_vehicle", objNull, [objNull]], ["_turretPath", [], [[]]], ["_muzzle", "", [""]], ["_magazine", "", [""]]]; +params [["_vehicle", objNull, [objNull]], ["_turretPath", [], [[]]], ["_weapon", "", [""]], ["_magazine", "", [""]]]; + +if !(_vehicle turretLocal _turretPath) exitWith { + [QGVAR(loadMagazineInstantly), _this, _vehicle, _turretPath] call CBA_fnc_turretEvent; +}; + +// Get all magazines compatible with the given weapon on the given turret +private _magazines = createHashMap; +private _compatibleMagazines = [_weapon, true] call CBA_fnc_compatibleMagazines; -private _magazines = []; -private _ammoCounts = []; { - _x params ["_currentMagazine", "_currentTurretPath", "_currentAmmoCount"]; - if (_turretPath isEqualTo _currentTurretPath) then { - _magazines pushBack _currentMagazine; - _ammoCounts pushBack _currentAmmoCount; + _x params ["_xMagazine", "_xTurretPath", "_xAmmoCount"]; + + if (_turretPath isEqualTo _xTurretPath && {_xMagazine in _compatibleMagazines}) then { + _magazines getOrDefault [_xMagazine, [], true] pushBack _xAmmoCount; }; -} forEach (magazinesAllTurrets _vehicle); +} forEach magazinesAllTurrets _vehicle; + +// Ensure that the vehicle has the desired magazine to load +if (_magazine in _magazines) then { + // Remove given weapon and all compatible magazines + _vehicle removeWeaponTurret [_weapon, _turretPath]; -private _magIdx = _magazines findIf {_x == _magazine}; -if (_magIdx != -1) then { - // Remove weapon and magazines { - _vehicle removeMagazineTurret [_x, _turretPath]; + _vehicle removeMagazinesTurret [_x, _turretPath]; } forEach _magazines; - _vehicle removeWeaponTurret [_muzzle, _turretPath]; - // Add desired magazine first - _vehicle addMagazineTurret [_magazine, _turretPath, _ammoCounts select _magIdx]; - _magazines deleteAt _magIdx; - _ammoCounts deleteAt _magIdx; + // Add magazines of selected type back first (load magazine with most ammo) + private _selectedMagazine = _magazines deleteAt _magazine; + _selectedMagazine sort false; - // Restore weapon and magazines - _vehicle addWeaponTurret [_muzzle, _turretPath]; { - _vehicle addMagazineTurret [_x, _turretPath, _ammoCounts select _forEachIndex]; + _vehicle addMagazineTurret [_magazine, _turretPath, _x]; + } forEach _selectedMagazine; + + // Restore weapon and other magazines (will load desired magazine since it was added first) + _vehicle addWeaponTurret [_weapon, _turretPath]; + + { + private _magazine = _x; + + { + _vehicle addMagazineTurret [_magazine, _turretPath, _x]; + } forEach _y; } forEach _magazines; + + // Select the weapon that the magazine was loaded into + _vehicle selectWeaponTurret [_weapon, _turretPath]; }; diff --git a/addons/common/functions/fnc_selectPosition.sqf b/addons/common/functions/fnc_selectPosition.sqf index 19cf3f2ed..ea4f80e17 100644 --- a/addons/common/functions/fnc_selectPosition.sqf +++ b/addons/common/functions/fnc_selectPosition.sqf @@ -79,9 +79,9 @@ private _visuals = [_text, _icon, _angle, _color]; private _mouseEH = [_display, "MouseButtonDown", { params ["", "_button", "", "", "_shift", "_ctrl", "_alt"]; - if (_button != 0) exitWith {}; + if (_button != 0 || {!call FUNC(isCursorOnMouseArea)}) exitWith {}; - private _position = [nil, 2] call FUNC(getPosFromScreen); + private _position = [GVAR(mousePos), 2] call FUNC(getPosFromScreen); _thisArgs params ["_objects", "_function", "_args"]; [true, _objects, _position, _args, _shift, _ctrl, _alt] call _function; @@ -94,7 +94,7 @@ private _keyboardEH = [_display, "KeyDown", { if (_key != DIK_ESCAPE) exitWith {false}; - private _position = [nil, 2] call FUNC(getPosFromScreen); + private _position = [GVAR(mousePos), 2] call FUNC(getPosFromScreen); _thisArgs params ["_objects", "_function", "_args"]; [false, _objects, _position, _args, _shift, _ctrl, _alt] call _function; @@ -108,7 +108,7 @@ private _drawEH = [_ctrlMap, "Draw", { params ["_ctrlMap"]; _thisArgs params ["_objects", "_args", "_visuals", "_modifierFunction"]; - private _position = [nil, 2] call FUNC(getPosFromScreen); + private _position = [GVAR(mousePos), 2] call FUNC(getPosFromScreen); [_objects, _position, _args, _visuals] call _modifierFunction; _visuals params ["_text", "_icon", "_angle", "_color"]; @@ -155,7 +155,7 @@ private _drawEH = [_ctrlMap, "Draw", { // No 3D drawing needed if the map is visible if (visibleMap) exitWith {}; - private _position = [nil, 2] call FUNC(getPosFromScreen); + private _position = [GVAR(mousePos), 2] call FUNC(getPosFromScreen); [_objects, _position, _args, _visuals] call _modifierFunction; _visuals params ["_text", "_icon", "_angle", "_color"]; diff --git a/addons/common/functions/fnc_updateEditableObjects.sqf b/addons/common/functions/fnc_updateEditableObjects.sqf new file mode 100644 index 000000000..addc3de26 --- /dev/null +++ b/addons/common/functions/fnc_updateEditableObjects.sqf @@ -0,0 +1,54 @@ +#include "script_component.hpp" +/* + * Author: mharis001 + * Updates editable objects for the given (or all) curators. + * + * Arguments: + * 0: Object(s) + * 1: Mode (default: true) + * - true: add objects, false: remove objects. + * 2: Curator(s) (default: []) + * - When given [] or objNull, changes are applied to all curators. + * 3: Include Crew (default: true) + * + * Return Value: + * None + * + * Example: + * [_objects, true] call zen_common_fnc_updateEditableObjects + * + * Public: No + */ + +if (!isServer) exitWith { + [QGVAR(updateEditableObjects), _this] call CBA_fnc_serverEvent; +}; + +params [ + ["_objects", [], [[], objNull]], + ["_mode", true, [true]], + ["_curators", [], [[], objNull]], + ["_includeCrew", true, [true]] +]; + +if (_objects isEqualType objNull) then { + _objects = [_objects]; +}; + +if (_curators in [[], objNull]) then { + _curators = allCurators; +}; + +if (_curators isEqualType objNull) then { + _curators = [_curators]; +}; + +if (_mode) then { + { + _x addCuratorEditableObjects [_objects, _includeCrew]; + } forEach _curators; +} else { + { + _x removeCuratorEditableObjects [_objects, _includeCrew]; + } forEach _curators; +}; diff --git a/addons/context_actions/CfgContext.hpp b/addons/context_actions/CfgContext.hpp index 503a26c4c..892ab9aba 100644 --- a/addons/context_actions/CfgContext.hpp +++ b/addons/context_actions/CfgContext.hpp @@ -343,6 +343,11 @@ class EGVAR(context_menu,actions) { statement = QUOTE(_objects call FUNC(refuelVehicles)); icon = "\a3\ui_f\data\igui\cfg\simpleTasks\types\refuel_ca.paa"; }; + class SwitchWeapon { + displayName = "$STR_A3_Switch1"; + icon = "\a3\ui_f\data\GUI\Cfg\Hints\VehicleAmmo_CA.paa"; + insertChildren = QUOTE(_hoveredEntity call FUNC(getVehicleWeaponActions)); + }; class UnloadViV { displayName = "$STR_A3_ModuleDepot_Unload"; condition = QUOTE(_objects call FUNC(canUnloadViV)); diff --git a/addons/context_actions/XEH_PREP.hpp b/addons/context_actions/XEH_PREP.hpp index 18a6c6190..0443e4190 100644 --- a/addons/context_actions/XEH_PREP.hpp +++ b/addons/context_actions/XEH_PREP.hpp @@ -12,6 +12,7 @@ PREP(canUnloadViV); PREP(copyVehicleAppearance); PREP(compileGrenades); PREP(getArtilleryActions); +PREP(getVehicleWeaponActions); PREP(getGrenadeActions); PREP(healUnits); PREP(openEditableObjectsDialog); @@ -26,6 +27,7 @@ PREP(setCombatMode); PREP(setFormation); PREP(setSpeedMode); PREP(setStance); +PREP(switchVehicleWeapon); PREP(switchWeaponModifier); PREP(switchWeapon); PREP(teleportPlayers); diff --git a/addons/context_actions/functions/fnc_getVehicleWeaponActions.sqf b/addons/context_actions/functions/fnc_getVehicleWeaponActions.sqf new file mode 100644 index 000000000..3d1a6f9fe --- /dev/null +++ b/addons/context_actions/functions/fnc_getVehicleWeaponActions.sqf @@ -0,0 +1,108 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Returns children actions for switching between the given vehicle's weapons, muzzles, and magazines. + * + * Arguments: + * 0: Vehicle + * + * Return Value: + * Actions + * + * Example: + * [_vehicle] call zen_context_actions_fnc_getVehicleWeaponActions + * + * Public: No + */ + +#define AMMO_SIMULATION_BLACKLIST ["shotcm", "laserdesignate"] + +params ["_vehicle"]; + +if !(_vehicle isEqualType objNull && {alive _vehicle}) exitWith {[]}; + +private _actions = []; +private _cfgAmmo = configFile >> "CfgAmmo"; +private _cfgMagazines = configFile >> "CfgMagazines"; +private _cfgWeapons = configFile >> "CfgWeapons"; + +{ + private _turretPath = _x; + private _turretMagazines = _vehicle magazinesTurret _turretPath; + + weaponState [_vehicle, _turretPath] params ["_currentWeapon", "_currentMuzzle", "", "_currentMagazine"]; + + { + private _weapon = _x; + private _weaponConfig = _cfgWeapons >> _weapon; + + { + private _muzzle = _x; + private _muzzleConfig = _weaponConfig; + + if (_muzzle == "this") then { + _muzzle = _weapon; + } else { + _muzzleConfig = _muzzleConfig >> _muzzle; + }; + + private _compatibleMagazines = [_muzzleConfig] call CBA_fnc_compatibleMagazines; + private _magazines = _turretMagazines arrayIntersect _compatibleMagazines; + + { + private _magazine = _x; + private _magazineConfig = _cfgMagazines >> _magazine; + + // Skip current weapon, muzzle, and magazine combination + if ( + _weapon == _currentWeapon + && {_muzzle == _currentMuzzle} + && {_magazine == _currentMagazine} + ) then {continue}; + + // Skip blacklisted ammo simulation types + private _ammoConfig = _cfgAmmo >> getText (_magazineConfig >> "ammo"); + private _ammoSimulation = toLower getText (_ammoConfig >> "simulation"); + if (_ammoSimulation in AMMO_SIMULATION_BLACKLIST) then {continue}; + + private _name = format [ + "%1 - %2 %3- %4", + [_vehicle, _turretPath] call EFUNC(common,getGunnerName), + getText (_weaponConfig >> "displayName"), + [format ["%1 ", _muzzle], ""] select (_muzzle == _weapon), + getText (_magazineConfig >> "displayName") + ]; + + private _icon = switch (_ammoSimulation) do { + case "shotbullet": { + "\a3\ui_f_jets\data\gui\cfg\hints\weaponsguns_ca.paa" + }; + case "shotmissile": { + "\a3\ui_f_jets\data\gui\cfg\hints\weaponsmissiles_ca.paa" + }; + case "shotilluminating": { + "\a3\ui_f\data\gui\cfg\hints\flares_ca.paa" + }; + default { + QPATHTOF(ui\ammo_ca.paa) + }; + }; + + private _action = [ + [_weapon, _muzzle, _magazine] joinString "", + _name, + _icon, + { + _args call FUNC(switchVehicleWeapon) + }, + {true}, + [_vehicle, _turretPath, _weapon, _muzzle, _magazine] + ] call EFUNC(context_menu,createAction); + + _actions pushBack [_action, [], 0]; + } forEach _magazines; + } forEach getArray (_weaponConfig >> "muzzles"); + } forEach (_vehicle weaponsTurret _turretPath); +} forEach (_vehicle call EFUNC(common,getAllTurrets)); + +_actions diff --git a/addons/context_actions/functions/fnc_openEditableObjectsDialog.sqf b/addons/context_actions/functions/fnc_openEditableObjectsDialog.sqf index efaa57c3c..9df478d7d 100644 --- a/addons/context_actions/functions/fnc_openEditableObjectsDialog.sqf +++ b/addons/context_actions/functions/fnc_openEditableObjectsDialog.sqf @@ -19,5 +19,4 @@ params ["_position"]; private _logic = QEGVAR(modules,moduleEditableObjects) createVehicleLocal [0, 0, 0]; _logic setPosASL _position; - _logic call BIS_fnc_showCuratorAttributes; diff --git a/addons/context_actions/functions/fnc_switchVehicleWeapon.sqf b/addons/context_actions/functions/fnc_switchVehicleWeapon.sqf new file mode 100644 index 000000000..587811712 --- /dev/null +++ b/addons/context_actions/functions/fnc_switchVehicleWeapon.sqf @@ -0,0 +1,31 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Switches the given vehicle turret's weapon, muzzle, and magazine. + * + * Arguments: + * 0: Vehicle + * 1: Turret Path + * 2: Weapon + * 3: Muzzle + * 4: Magazine + * + * Return Value: + * None + * + * Example: + * [_vehicle, _turretPath, _weapon, _muzzle, _magazine] call zen_context_actions_fnc_switchVehicleWeapon + * + * Public: No + */ + +params ["_vehicle", "_turretPath", "_weapon", "_muzzle", "_magazine"]; + +// For regular (non-pylon) magazines, load the magazine instantly into the selected weapon +// Pylon magazines work differently and only need to have the corresponding pylon weapon selected +if !(_magazine in getPylonMagazines _vehicle) then { + [_vehicle, _turretPath, _weapon, _magazine] call EFUNC(common,loadMagazineInstantly); +}; + +// Select the given weapon and muzzle on the vehicle's turret +[QEGVAR(common,selectWeaponTurret), [_vehicle, _weapon, _turretPath, _muzzle], _vehicle, _turretPath] call CBA_fnc_turretEvent; diff --git a/addons/context_actions/functions/fnc_updateEditableObjects.sqf b/addons/context_actions/functions/fnc_updateEditableObjects.sqf index f6bb82c56..838ed2c06 100644 --- a/addons/context_actions/functions/fnc_updateEditableObjects.sqf +++ b/addons/context_actions/functions/fnc_updateEditableObjects.sqf @@ -12,7 +12,7 @@ * None * * Example: - * [true, [0, 0, 0], 100] call zen_context_actions_fnc_editableObjects + * [true, [0, 0, 0], 100] call zen_context_actions_fnc_updateEditableObjects * * Public: No */ @@ -21,6 +21,4 @@ params ["_mode", "_position", "_radius"]; private _curator = getAssignedCuratorLogic player; private _objects = nearestObjects [ASLtoAGL _position, ["All"], _radius, true]; -private _eventName = [QEGVAR(common,removeObjects), QEGVAR(common,addObjects)] select _mode; - -[_eventName, [_objects, _curator]] call CBA_fnc_serverEvent; +[_objects, _mode, _curator] call EFUNC(common,updateEditableObjects); diff --git a/addons/context_menu/functions/fnc_createContextGroup.sqf b/addons/context_menu/functions/fnc_createContextGroup.sqf index 8c5bec186..4dc1b456f 100644 --- a/addons/context_menu/functions/fnc_createContextGroup.sqf +++ b/addons/context_menu/functions/fnc_createContextGroup.sqf @@ -138,8 +138,8 @@ private _posH = POS_H(count _actions); _ctrlName ctrlCommit 0; private _ctrlMouse = _ctrlContextRow controlsGroupCtrl IDC_CONTEXT_MOUSE; - _ctrlHighlight ctrlSetPositionW _posW; - _ctrlHighlight ctrlCommit 0; + _ctrlMouse ctrlSetPositionW _posW; + _ctrlMouse ctrlCommit 0; private _ctrlExpandable = _ctrlContextRow controlsGroupCtrl IDC_CONTEXT_EXPANDABLE; _ctrlExpandable ctrlSetPositionX (_posW - POS_W(1)); diff --git a/addons/editor/XEH_preInit.sqf b/addons/editor/XEH_preInit.sqf index caa413a4b..3a9860073 100644 --- a/addons/editor/XEH_preInit.sqf +++ b/addons/editor/XEH_preInit.sqf @@ -9,7 +9,6 @@ PREP_RECOMPILE_END; #include "initSettings.sqf" #include "initKeybinds.sqf" -GVAR(mousePos) = [0, 0]; GVAR(clipboard) = []; GVAR(includeCrew) = true; diff --git a/addons/editor/functions/fnc_handleLoad.sqf b/addons/editor/functions/fnc_handleLoad.sqf index 04d37f0cf..58525ce2d 100644 --- a/addons/editor/functions/fnc_handleLoad.sqf +++ b/addons/editor/functions/fnc_handleLoad.sqf @@ -110,18 +110,6 @@ _ctrlTreeRecent ctrlAddEventHandler ["TreeSelChanged", { }; }]; -// Track mouse position from mouse area control to handle the mouse being over other UI elements -// RscDisplayCurator_mousePos from base game attempts to do this but for some reason also updates when -// the mouse is over the mission controls group -private _fnc_updateMousePos = { - params ["", "_posX", "_posY"]; - GVAR(mousePos) = [_posX, _posY]; -}; - -private _ctrlMouseArea = _display displayCtrl IDC_RSCDISPLAYCURATOR_MOUSEAREA; -_ctrlMouseArea ctrlAddEventHandler ["MouseMoving", _fnc_updateMousePos]; -_ctrlMouseArea ctrlAddEventHandler ["MouseHolding", _fnc_updateMousePos]; - // Initially open the map fully zoomed out and centered if (isNil QGVAR(previousMapState)) then { GVAR(previousMapState) = [1, [worldSize / 2, worldSize / 2]]; diff --git a/addons/editor/initKeybinds.sqf b/addons/editor/initKeybinds.sqf index c0ad0c57e..9c3a49a92 100644 --- a/addons/editor/initKeybinds.sqf +++ b/addons/editor/initKeybinds.sqf @@ -7,27 +7,7 @@ }; }, {}, [DIK_B, [false, false, false]]] call CBA_fnc_addKeybind; // Default: B -[ELSTRING(main,DisplayName), QGVAR(deployCountermeasures), [LSTRING(DeployCountermeasures), LSTRING(DeployCountermeasures_Description)], { - if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { - { - [_x] call EFUNC(common,deployCountermeasures); - } forEach SELECTED_OBJECTS; - - true // handled - }; -}, {}, [DIK_C, [true, false, false]]] call CBA_fnc_addKeybind; // Default: SHIFT + C - -[ELSTRING(main,DisplayName), QGVAR(ejectPassengers), [LSTRING(EjectPassengers), LSTRING(EjectPassengers_Description)], { - if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { - { - [_x] call EFUNC(common,ejectPassengers); - } forEach SELECTED_OBJECTS; - - true // handled, prevents vanilla eject - }; -}, {}, [DIK_G, [false, true, false]]] call CBA_fnc_addKeybind; // Default: CTRL + G - -[ELSTRING(main,DisplayName), QGVAR(unloadViV), [localize "STR_A3_ModuleDepot_Unload", LSTRING(UnloadViV_Description)], { +[ELSTRING(main,DisplayName), QGVAR(unloadViV), ["STR_A3_ModuleDepot_Unload", LSTRING(UnloadViV_Description)], { if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { { if (isNull isVehicleCargo _x) then { @@ -87,6 +67,30 @@ }; }, {}, [DIK_X, [false, true, false]]] call CBA_fnc_addKeybind; // Default: CTRL + X +[ELSTRING(main,DisplayName), QGVAR(toggleEditability), [LSTRING(ToggleEditability), LSTRING(ToggleEditability_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + curatorMouseOver params ["_type", "_entity"]; + + private _object = if (_type != "OBJECT") then { + if (!call EFUNC(common,isCursorOnMouseArea)) exitWith {objNull}; + + private _begPos = getPosASL curatorCamera; + private _endPos = AGLToASL screenToWorld getMousePosition; + lineIntersectsSurfaces [_begPos, _endPos, curatorCamera] param [0, []] param [2, objNull] + } else { + _entity + }; + + if (!isNull _object) then { + private _curator = getAssignedCuratorLogic player; + private _isEditable = _object in curatorEditableObjects _curator; + [_object, !_isEditable, _curator] call EFUNC(common,updateEditableObjects); + }; + + true // handled + }; +}, {}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound + [ELSTRING(main,DisplayName), QGVAR(toggleIcons), [LSTRING(ToggleIcons), LSTRING(ToggleIcons_Description)], { if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { GVAR(iconsVisible) = !GVAR(iconsVisible); @@ -108,3 +112,131 @@ true // handled }; }, {}, [DIK_R, [true, true, false]]] call CBA_fnc_addKeybind; // Default: CTRL + SHIFT + R + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(ejectPassengers), [LSTRING(EjectPassengers), LSTRING(EjectPassengers_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + { + [_x] call EFUNC(common,ejectPassengers); + } forEach SELECTED_OBJECTS; + + true // handled, prevents vanilla eject + }; +}, {}, [DIK_G, [false, true, false]]] call CBA_fnc_addKeybind; // Default: CTRL + G + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(deployCountermeasures), [LSTRING(DeployCountermeasures), LSTRING(DeployCountermeasures_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + { + [_x] call EFUNC(common,deployCountermeasures); + } forEach SELECTED_OBJECTS; + + true // handled + }; +}, {}, [DIK_C, [true, false, false]]] call CBA_fnc_addKeybind; // Default: SHIFT + C + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(watchCursor), [LSTRING(WatchCursor), LSTRING(WatchCursor_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + curatorMouseOver params ["_type", "_target"]; + + if (_type != "OBJECT") then { + _target = ASLToAGL ([] call EFUNC(common,getPosFromScreen)); + }; + + { + if (!isNull group _x && {!isPlayer _x}) then { + // Cancel if target is self + private _isSelf = _x isEqualTo _target; + private _target = [_target, objNull] select _isSelf; + [QEGVAR(common,doWatch), [[_x, gunner _x], _target], _x] call CBA_fnc_targetEvent; + if (_isSelf) then {continue}; + + [[ + ["ICON", [_target, "\a3\ui_f\data\igui\cfg\simpletasks\types\scout_ca.paa"]], + ["LINE", [_x, _target]] + ], 3, _x] call EFUNC(common,drawHint); + }; + } forEach SELECTED_OBJECTS; + + true // handled + }; +}, {}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(watchCuratorCamera), [LSTRING(WatchCuratorCamera), LSTRING(WatchCuratorCamera_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _position = ASLToAGL getPosASL curatorCamera; + + { + if (!isNull group _x && {!isPlayer _x}) then { + [QEGVAR(common,doWatch), [_x, _position], _x] call CBA_fnc_targetEvent; + + [[ + ["ICON", [_position, "\a3\ui_f\data\igui\cfg\simpletasks\types\scout_ca.paa"]], + ["LINE", [_x, _position]] + ], 3, _x] call EFUNC(common,drawHint); + }; + } forEach SELECTED_OBJECTS; + + true // handled + }; +}, {}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(forceFire), [LSTRING(ForceFire), LSTRING(ForceFire_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _units = SELECTED_OBJECTS select {!isPlayer _x && {!isNull group _x}}; + [QEGVAR(common,forceFire), [_units, CBA_clientID]] call CBA_fnc_globalEvent; + + true // handled + }; +}, { + [QEGVAR(common,forceFire), [[], CBA_clientID]] call CBA_fnc_globalEvent; +}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(moveToCursor), [LSTRING(MoveToCursor), LSTRING(MoveToCursor_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _position = ASLToAGL ([] call EFUNC(common,getPosFromScreen)); + + { + if (!isNull driver _x && {!isPlayer _x}) then { + [QEGVAR(common,doMove), [_x, _position], _x] call CBA_fnc_targetEvent; + + [[ + ["ICON", [_position, "\a3\ui_f\data\igui\cfg\simpletasks\types\walk_ca.paa"]], + ["LINE", [_x, _position]] + ], 3, _x] call EFUNC(common,drawHint); + }; + } forEach SELECTED_OBJECTS; + + true // handled + }; +}, {}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound + +[[ELSTRING(main,DisplayName), LSTRING(AIControl)], QGVAR(toggleAIPATH), [LSTRING(ToggleAIPATH), LSTRING(ToggleAIPATH_Description)], { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _enabled = 0; + private _disabled = 0; + + { + if (!isPlayer _x && {_x == vehicle _x || {_x == driver vehicle _x}}) then { + private _isPathEnabled = _x checkAIFeature "PATH"; + private _eventName = [QEGVAR(common,enableAI), QEGVAR(common,disableAI)] select _isPathEnabled; + [_eventName, [_x, "PATH"], _x] call CBA_fnc_globalEvent; + + if (_isPathEnabled) then { + _disabled = _disabled + 1; + } else { + _enabled = _enabled + 1; + }; + }; + } forEach SELECTED_OBJECTS; + + [ + "%1 - %2: %3 - %4: %5", + LLSTRING(AIPathToggled), + LELSTRING(common,Enabled), + _enabled, + LELSTRING(common,Disabled), + _disabled + ] call EFUNC(common,showMessage); + + true // handled + }; +}, {}, [0, [false, false, false]]] call CBA_fnc_addKeybind; // Default: Unbound diff --git a/addons/editor/stringtable.xml b/addons/editor/stringtable.xml index 2405fb9ad..c9aa2ccb2 100644 --- a/addons/editor/stringtable.xml +++ b/addons/editor/stringtable.xml @@ -220,27 +220,6 @@ 切換包含乘員 Toggle Includi equipaggio - - Eject Passengers - Ejecter les passagers - Passagiere rauswerfen - Wysadź pasażerów - 後部座席を脱出 - 승객 탈출 - 弹出乘客 - 彈出乘客 - Espellere i passeggeri - - - Ejects the passengers of all selected vehicles. If the vehicle is currently flying, the units will be given parachutes.\nUnlike vanilla eject, units will disembark one after another (not immediately, with a short delay in-between). - Éjecte les passagers de tous les véhicules sélectionnés. Si le véhicule est actuellement en vol, les unités recevront des parachutes. Contrairement à l'éjection standard, les unités débarqueront les unes après les autres (pas immédiatement, avec un court délai entre les deux). - Wirft die Fahrgäste aller ausgewählten Fahrzeuge raus. Wenn das Fahrzeug gerade fliegt, erhalten die Einheiten Fallschirme.\nIm Gegensatz zu "Vanilla" sitzen die Einheiten nacheinander ab (nicht alle sofort, sondern mit einer kurzen Verzögerung dazwischen). - Wysadza pasażerów ze wszystkich wybranych pojazdów. Jeżeli pojazd jest w powietrzu, pasażerowie dostaną spadochron.\nJednostki zostaną wysadzone jedna po drugiej (nie wszystkie na raz). - 選択された全車両から後部座席のユニットを脱出させます。もし車両が飛行中の場合はパラシュートが与えられます。\nなお脱出は一人づつ降りるようになっています (即時ではなく数秒づつ)。 - 弹出所有选定载具的乘客。如果载具正在飞行,这些单位将获得降落伞。与原版弹射不同的是,单位将一个接一个地离开(而不是立即离开,中间会有短暂间隙)。 - 彈出所有選定載具的乘客。如果載具正在飛行,這些單位將獲得降落傘。與原版彈射不同的是,單位將一個接一個地離開(而不是立即離開,中間會有短暫間隙)。 - Espelle i passeggeri di tutti i veicoli selezionati. Se il veicolo è attualmente in volo, alle unità verranno forniti paracadute.\nA differenza dell'espulsione vaniglia, le unità sbarcheranno una dopo l'altra (non immediatamente, con un breve ritardo nel mezzo). - Unloads all selected vehicle-in-vehicle cargo and carriers. A vehicle that is both cargo and carrying cargo will only be unloaded from its carrier. Entladet alle ausgewählte Fahrzeug-in-Fahrzeug Fracht und Transporter. Fahrzeuge, die sowohl Fracht als auch Transporter sind, werden nur als Fracht entladen. @@ -249,26 +228,6 @@ 卸下所有選定的載具中的貨物和車輛。既有貨物又有運載車輛的載具將只卸下貨物。 Scarica tutte le merci e i vettori selezionati veicolo-in-veicolo. Un veicolo che è sia un carico che un carico verrà scaricato solo dal suo vettore. - - Deploy Countermeasures - Déployer des contre-mesures - Gegenmaßnahmen einleiten - 欺瞞装置作動 - Odpal środki przeciw namierzające - 发射反制措施 - 發射反制措施 - Implementare contromisure - - - Makes all selected vehicles deploy countermeasures such as smokes and flares. - Tous les véhicules sélectionnés déploient des contre-mesures telles que des fumées et des fusées éclairantes. - Alle ausgewählten Fahrzeuge setzen Gegenmaßnahmen wie Rauch und Leuchtfackeln ein. - 選択された全車両から煙幕やフレアといった欺瞞装置を作動させます。 - Powoduje że wszystkie wybrane pojazdy odpalają środki przeciw namierzające, czyli flary, dymy itp. - 使所有选定的车辆发射反制措施,如烟雾和诱饵弹。 - 使所有選定的車輛發射反制措施,如煙霧和誘餌彈。 - Fa in modo che tutti i veicoli selezionati utilizzino contromisure come fumogeni e razzi. - Deep Copy Komplette Kopie @@ -332,6 +291,12 @@ 調整所有被選中的物體的方向,使之與它們位置上的地形坡度一致。 Orienta tutti gli oggetti selezionati in modo che corrispondano alla pendenza del terreno nella loro posizione. + + Toggle Object Editability + + + Toggles the editability (i.e., whether an object is editable through Zeus) of the object currently under the cursor. + Toggle Editable Icons Bearbeitbare Symbole umschalten @@ -366,6 +331,83 @@ 重新加載Zeus界面。可用於修復鎖定問題。 Ricarica l'interfaccia di Zeus. Può essere utilizzato per risolvere i problemi di blocco. + + AI Control + + + Eject Passengers + Ejecter les passagers + Passagiere rauswerfen + Wysadź pasażerów + 後部座席を脱出 + 승객 탈출 + 弹出乘客 + 彈出乘客 + Espellere i passeggeri + + + Ejects the passengers of all selected vehicles. If the vehicle is currently flying, the units will be given parachutes.\nUnlike vanilla eject, units will disembark one after another (not immediately, with a short delay in-between). + Éjecte les passagers de tous les véhicules sélectionnés. Si le véhicule est actuellement en vol, les unités recevront des parachutes. Contrairement à l'éjection standard, les unités débarqueront les unes après les autres (pas immédiatement, avec un court délai entre les deux). + Wirft die Fahrgäste aller ausgewählten Fahrzeuge raus. Wenn das Fahrzeug gerade fliegt, erhalten die Einheiten Fallschirme.\nIm Gegensatz zu "Vanilla" sitzen die Einheiten nacheinander ab (nicht alle sofort, sondern mit einer kurzen Verzögerung dazwischen). + Wysadza pasażerów ze wszystkich wybranych pojazdów. Jeżeli pojazd jest w powietrzu, pasażerowie dostaną spadochron.\nJednostki zostaną wysadzone jedna po drugiej (nie wszystkie na raz). + 選択された全車両から後部座席のユニットを脱出させます。もし車両が飛行中の場合はパラシュートが与えられます。\nなお脱出は一人づつ降りるようになっています (即時ではなく数秒づつ)。 + 弹出所有选定载具的乘客。如果载具正在飞行,这些单位将获得降落伞。与原版弹射不同的是,单位将一个接一个地离开(而不是立即离开,中间会有短暂间隙)。 + 彈出所有選定載具的乘客。如果載具正在飛行,這些單位將獲得降落傘。與原版彈射不同的是,單位將一個接一個地離開(而不是立即離開,中間會有短暫間隙)。 + Espelle i passeggeri di tutti i veicoli selezionati. Se il veicolo è attualmente in volo, alle unità verranno forniti paracadute.\nA differenza dell'espulsione vaniglia, le unità sbarcheranno una dopo l'altra (non immediatamente, con un breve ritardo nel mezzo). + + + Deploy Countermeasures + Déployer des contre-mesures + Gegenmaßnahmen einleiten + 欺瞞装置作動 + Odpal środki przeciw namierzające + 发射反制措施 + 發射反制措施 + Implementare contromisure + + + Makes all selected vehicles deploy countermeasures such as smokes and flares. + Tous les véhicules sélectionnés déploient des contre-mesures telles que des fumées et des fusées éclairantes. + Alle ausgewählten Fahrzeuge setzen Gegenmaßnahmen wie Rauch und Leuchtfackeln ein. + 選択された全車両から煙幕やフレアといった欺瞞装置を作動させます。 + Powoduje że wszystkie wybrane pojazdy odpalają środki przeciw namierzające, czyli flary, dymy itp. + 使所有选定的车辆发射反制措施,如烟雾和诱饵弹。 + 使所有選定的車輛發射反制措施,如煙霧和誘餌彈。 + Fa in modo che tutti i veicoli selezionati utilizzino contromisure come fumogeni e razzi. + + + Watch Cursor + + + Makes selected AI units watch the cursor's position. Order a unit to watch itself (i.e., with the cursor hovering over the unit) to cancel the watch target. + + + Watch Curator Camera + + + Makes selected AI units watch the Zeus camera's position. + + + Force Fire + + + Makes selected AI units fire their current weapon while the key is held down. Vehicles will fire their first available turret with a weapon. + + + Move To Cursor + + + Makes selected AI units move to the cursor's position. Afterwards, AI units will return to formation and resume waypoints. + + + Toggle AI Pathing + + + Makes selected AI units start or stop moving. Does not affect aiming or shooting. Similar to "Hold Position" in RTS games. + + + AI Pathing Toggled + Focus Object Mode diff --git a/addons/loadout/functions/fnc_getWeaponName.sqf b/addons/loadout/functions/fnc_getWeaponName.sqf index e0dae730c..739e158cd 100644 --- a/addons/loadout/functions/fnc_getWeaponName.sqf +++ b/addons/loadout/functions/fnc_getWeaponName.sqf @@ -20,10 +20,5 @@ params ["_vehicle", "_weapon", "_turretPath"]; private _weaponName = getText (configFile >> "CfgWeapons" >> _weapon >> "displayName"); -private _gunnerName = getText ([_vehicle, _turretPath] call CBA_fnc_getTurret >> "gunnerName"); - -if (_gunnerName == "") then { - _gunnerName = localize (["str_driver", "str_pilot"] select (_vehicle isKindOf "Air")); -}; - +private _gunnerName = [_vehicle, _turretPath] call EFUNC(common,getGunnerName); format ["%1 (%2)", _weaponName, _gunnerName] diff --git a/addons/main/script_mod.hpp b/addons/main/script_mod.hpp index c574be741..a8234f1d7 100644 --- a/addons/main/script_mod.hpp +++ b/addons/main/script_mod.hpp @@ -10,7 +10,7 @@ #define VERSION_AR MAJOR,MINOR,PATCHLVL,BUILD // MINIMAL required version for the Mod. Components can specify others.. -#define REQUIRED_VERSION 2.10 +#define REQUIRED_VERSION 2.12 #define REQUIRED_CBA_VERSION {3,15,8} #ifdef COMPONENT_BEAUTIFIED diff --git a/addons/modules/functions/fnc_moduleAmbientFlyby.sqf b/addons/modules/functions/fnc_moduleAmbientFlyby.sqf index 11f6a1712..4e27cf500 100644 --- a/addons/modules/functions/fnc_moduleAmbientFlyby.sqf +++ b/addons/modules/functions/fnc_moduleAmbientFlyby.sqf @@ -114,4 +114,4 @@ _waypoint setWaypointSpeed _speed; _waypoint setWaypointStatements ["true", "private _group = group this; private _aircrafts = []; {_aircrafts pushBackUnique vehicle _x; deleteVehicle _x} forEach thisList; {deleteVehicle _x} forEach _aircrafts; deleteGroup _group"]; // Add aircrafts to curators -[QEGVAR(common,addObjects), [_aircrafts]] call CBA_fnc_serverEvent; +[_aircrafts] call EFUNC(common,updateEditableObjects); diff --git a/addons/modules/functions/fnc_moduleCreateIntel.sqf b/addons/modules/functions/fnc_moduleCreateIntel.sqf index 141eb1d81..7f36ccfe5 100644 --- a/addons/modules/functions/fnc_moduleCreateIntel.sqf +++ b/addons/modules/functions/fnc_moduleCreateIntel.sqf @@ -105,7 +105,7 @@ if (isNull _object) then { if (_object isEqualType []) then { _object = createVehicle [_values deleteAt 0, _object, [], 0, "NONE"]; - [QEGVAR(common,addObjects), [[_object]]] call CBA_fnc_serverEvent; + _object call EFUNC(common,updateEditableObjects); }; // Handle no action type option when ACE is not loaded diff --git a/addons/modules/functions/fnc_moduleCreateMinefield.sqf b/addons/modules/functions/fnc_moduleCreateMinefield.sqf index 4f0e4da2a..49503ef0d 100644 --- a/addons/modules/functions/fnc_moduleCreateMinefield.sqf +++ b/addons/modules/functions/fnc_moduleCreateMinefield.sqf @@ -79,7 +79,7 @@ _minesCache params ["_configNames", "_displayNames"]; }; // Add created mines to editable objects - [QEGVAR(common,addObjects), [_mines]] call CBA_fnc_serverEvent; + [_mines] call EFUNC(common,updateEditableObjects); }, {}, ASLtoAGL getPosASL _logic] call EFUNC(dialog,create); deleteVehicle _logic; diff --git a/addons/modules/functions/fnc_moduleCreateTeleporterServer.sqf b/addons/modules/functions/fnc_moduleCreateTeleporterServer.sqf index 41f986497..ea41f10df 100644 --- a/addons/modules/functions/fnc_moduleCreateTeleporterServer.sqf +++ b/addons/modules/functions/fnc_moduleCreateTeleporterServer.sqf @@ -22,7 +22,7 @@ params ["_object", "_position", "_name"]; // Create a flag pole object if an object wasn't given if (isNull _object) then { _object = createVehicle ["FlagPole_F", _position, [], 0, "NONE"]; - [QEGVAR(common,addObjects), [[_object]]] call CBA_fnc_localEvent; + _object call EFUNC(common,updateEditableObjects); }; // Add teleport action to new teleporter object diff --git a/addons/modules/functions/fnc_moduleEditableObjects.sqf b/addons/modules/functions/fnc_moduleEditableObjects.sqf index 37a6cb94c..eebfcabc5 100644 --- a/addons/modules/functions/fnc_moduleEditableObjects.sqf +++ b/addons/modules/functions/fnc_moduleEditableObjects.sqf @@ -62,5 +62,4 @@ if (_range == -1) then { _objects = nearestObjects [_position, _types, _range, true]; }; -private _eventName = [QEGVAR(common,removeObjects), QEGVAR(common,addObjects)] select _editingMode; -[_eventName, [_objects, _curator]] call CBA_fnc_serverEvent; +[_objects, _editingMode, _curator] call EFUNC(common,updateEditableObjects); diff --git a/addons/modules/functions/fnc_moduleEffectFireLocal.sqf b/addons/modules/functions/fnc_moduleEffectFireLocal.sqf index ece5a26f9..d2c5f2579 100644 --- a/addons/modules/functions/fnc_moduleEffectFireLocal.sqf +++ b/addons/modules/functions/fnc_moduleEffectFireLocal.sqf @@ -28,7 +28,7 @@ _color params ["_colorRed", "_colorGreen", "_colorBlue"]; if (isServer) then { // Add logic object to all curators for QOL - [QEGVAR(common,addObjects), [[_logic]]] call CBA_fnc_localEvent; + [_logic] call EFUNC(common,updateEditableObjects); // Create global fire sound effect if not created yet private _sound = _logic getVariable QGVAR(fireSound); diff --git a/addons/modules/functions/fnc_moduleLightSource.sqf b/addons/modules/functions/fnc_moduleLightSource.sqf index 4b30b3fc8..83763aab2 100644 --- a/addons/modules/functions/fnc_moduleLightSource.sqf +++ b/addons/modules/functions/fnc_moduleLightSource.sqf @@ -47,7 +47,7 @@ _display closeDisplay IDC_CANCEL; // Close helper display _logic setVariable [QGVAR(lightpoint), _lightpoint, true]; // Add logic object to all curators once it has lightpoint for QOL - [QEGVAR(common,addObjects), [[_logic]]] call CBA_fnc_serverEvent; + [_logic] call EFUNC(common,updateEditableObjects); // Add event handler to delete lightpoint if logic is deleted [QEGVAR(common,execute), [{ diff --git a/addons/modules/functions/fnc_moduleSitOnChair.sqf b/addons/modules/functions/fnc_moduleSitOnChair.sqf index 40bba00be..a00350402 100644 --- a/addons/modules/functions/fnc_moduleSitOnChair.sqf +++ b/addons/modules/functions/fnc_moduleSitOnChair.sqf @@ -139,7 +139,7 @@ private _displayNames = _configNames apply {getText (_cfgVehicles >> _x >> "disp [QEGVAR(common,setDir), [_unit, _sitDirection], _unit] call CBA_fnc_targetEvent; // Add chair to editable objects so it can be moved around - [QEGVAR(common,addObjects), [[_chair]]] call CBA_fnc_serverEvent; + [_chair] call EFUNC(common,updateEditableObjects); // Flag unit as sitting so module will make it stand up next time _unit setVariable [QGVAR(isSitting), true, true]; diff --git a/addons/modules/functions/fnc_moduleSpawnCarrier.sqf b/addons/modules/functions/fnc_moduleSpawnCarrier.sqf index 6adae5c60..1b87088dc 100644 --- a/addons/modules/functions/fnc_moduleSpawnCarrier.sqf +++ b/addons/modules/functions/fnc_moduleSpawnCarrier.sqf @@ -31,7 +31,7 @@ params ["_logic"]; private _jipID = [QGVAR(carrierInit), _carrier] call CBA_fnc_globalEventJIP; [_jipID, _carrier] call CBA_fnc_removeGlobalEventJIP; - [QEGVAR(common,addObjects), [[_carrier]]] call CBA_fnc_serverEvent; + [_carrier] call EFUNC(common,updateEditableObjects); { deleteVehicle _x; diff --git a/addons/modules/functions/fnc_moduleSpawnDestroyer.sqf b/addons/modules/functions/fnc_moduleSpawnDestroyer.sqf index 09599666e..c70547477 100644 --- a/addons/modules/functions/fnc_moduleSpawnDestroyer.sqf +++ b/addons/modules/functions/fnc_moduleSpawnDestroyer.sqf @@ -31,7 +31,7 @@ params ["_logic"]; private _jipID = [QGVAR(destroyerInit), _destroyer] call CBA_fnc_globalEventJIP; [_jipID, _destroyer] call CBA_fnc_removeGlobalEventJIP; - [QEGVAR(common,addObjects), [[_destroyer]]] call CBA_fnc_serverEvent; + [_destroyer] call EFUNC(common,updateEditableObjects); { deleteVehicle _x; diff --git a/addons/modules/functions/fnc_moduleSpawnReinforcements.sqf b/addons/modules/functions/fnc_moduleSpawnReinforcements.sqf index da63d732e..f44ff252d 100644 --- a/addons/modules/functions/fnc_moduleSpawnReinforcements.sqf +++ b/addons/modules/functions/fnc_moduleSpawnReinforcements.sqf @@ -139,4 +139,4 @@ private _objects = [_vehicle]; _objects append units _vehicleGroup; _objects append units _infantryGroup; -[QEGVAR(common,addObjects), [_objects]] call CBA_fnc_serverEvent; +[_objects] call EFUNC(common,updateEditableObjects); diff --git a/addons/placement/functions/fnc_handleObjectPlaced.sqf b/addons/placement/functions/fnc_handleObjectPlaced.sqf index 6de95e1b2..137353316 100644 --- a/addons/placement/functions/fnc_handleObjectPlaced.sqf +++ b/addons/placement/functions/fnc_handleObjectPlaced.sqf @@ -38,7 +38,10 @@ _object allowDamage false; _object setVectorDirAndUp _dirAndUp; _object setVelocity [0, 0, 0]; - [{_this allowDamage true}, _object] call CBA_fnc_execNextFrame; + [{ + _this allowDamage true; + [QGVAR(done), _this] call CBA_fnc_localEvent; + }, _object] call CBA_fnc_execNextFrame; }, [_object, getPosASL GVAR(helper), [vectorDir GVAR(helper), vectorUp GVAR(helper)]]] call CBA_fnc_execNextFrame; // Do not cancel the preview if the control key is held diff --git a/addons/placement/functions/fnc_updatePreview.sqf b/addons/placement/functions/fnc_updatePreview.sqf index 68a47a655..f4d7d2867 100644 --- a/addons/placement/functions/fnc_updatePreview.sqf +++ b/addons/placement/functions/fnc_updatePreview.sqf @@ -26,19 +26,19 @@ if (inputAction "curatorRotateMod" > 0) then { // Otherwise, fall back to using the mouse's world positon, which can behave weirdly at certain // camera angles but should not be the case with the object offscreen private _direction = if (_screenPos isNotEqualTo []) then { - private _vector = _screenPos vectorDiff EGVAR(editor,mousePos); + private _vector = _screenPos vectorDiff EGVAR(common,mousePos); _vector params ["_vectorX", "_vectorY"]; // Translate to north as 0 degrees and account for camera view direction _vectorY atan2 _vectorX - 90 + getDir curatorCamera } else { - GVAR(helper) getDir screenToWorld EGVAR(editor,mousePos) + GVAR(helper) getDir screenToWorld EGVAR(common,mousePos) }; GVAR(helper) setDir _direction; } else { // Get terrain position and normal - private _position = AGLToASL screenToWorld EGVAR(editor,mousePos); + private _position = AGLToASL screenToWorld EGVAR(common,mousePos); private _vectorUp = surfaceNormal _position; // Check if a surface other than the terrain exists diff --git a/addons/position_logics/functions/fnc_add.sqf b/addons/position_logics/functions/fnc_add.sqf index 25792ab57..08ca16b05 100644 --- a/addons/position_logics/functions/fnc_add.sqf +++ b/addons/position_logics/functions/fnc_add.sqf @@ -40,7 +40,7 @@ if (isServer) then { [_jipID, _logic] call CBA_fnc_removeGlobalEventJIP; // Make the logic editable for all curators - [QEGVAR(common,addObjects), [[_logic]]] call CBA_fnc_localEvent; + [_logic] call EFUNC(common,updateEditableObjects); // Disable attributes for the logic _logic setVariable [QEGVAR(attributes,disabled), true, true]; diff --git a/docs/user_guide/attributes.md b/docs/user_guide/attributes.md index 4303250f3..f36596366 100644 --- a/docs/user_guide/attributes.md +++ b/docs/user_guide/attributes.md @@ -48,3 +48,6 @@ _* Only for vehicles that are currently occupied._ ### Markers - Text - Color + +### Others +- Building Marker