From bd43c32334bc228b0f02263910f0c1daa2a5d8db Mon Sep 17 00:00:00 2001 From: PabstMirror Date: Wed, 4 Oct 2023 13:21:50 -0500 Subject: [PATCH] Trenches - Terrain Trenching (#9148) * Common - Use hashmap variable for canDig surfaces * Update XEH_preInit.sqf * Trenches - Terrain Trenching (simple blocks) * Update fnc_canDig.sqf * Update fnc_canDig.sqf * Handle locality * move code around, stringtable * use third block for floor * cleanup debug * update includes, add tooltip --- addons/trenches/XEH_PREP.hpp | 1 + addons/trenches/XEH_postInit.sqf | 11 ++ .../functions/fnc_blockTrench_place.sqf | 164 ++++++++++++++++++ addons/zeus/CfgVehicles.hpp | 5 + addons/zeus/XEH_PREP.hpp | 1 + addons/zeus/config.cpp | 6 + addons/zeus/functions/fnc_moduleLayTrench.sqf | 50 ++++++ addons/zeus/stringtable.xml | 6 + 8 files changed, 244 insertions(+) create mode 100644 addons/trenches/functions/fnc_blockTrench_place.sqf create mode 100644 addons/zeus/functions/fnc_moduleLayTrench.sqf diff --git a/addons/trenches/XEH_PREP.hpp b/addons/trenches/XEH_PREP.hpp index 05eb403fd8..340cfe482d 100644 --- a/addons/trenches/XEH_PREP.hpp +++ b/addons/trenches/XEH_PREP.hpp @@ -1,4 +1,5 @@ +PREP(blockTrench_place); PREP(camouflageTrench); PREP(canCamouflageTrench); PREP(canContinueDiggingTrench); diff --git a/addons/trenches/XEH_postInit.sqf b/addons/trenches/XEH_postInit.sqf index 1fbfa24116..ea8ff7e24f 100644 --- a/addons/trenches/XEH_postInit.sqf +++ b/addons/trenches/XEH_postInit.sqf @@ -3,6 +3,17 @@ if (isServer) then { // Cancel dig on hard disconnection. Function is identical to killed addMissionEventHandler ["HandleDisconnect", {_this call FUNC(handleKilled)}]; + + // Wrapper for blockTrench_place, on failure send hint back to source + [QGVAR(layTrenchline), { + params [["_source", objNull, [objNull]], ["_args", [], [[]]]]; + private _return = _args call FUNC(blockTrench_place); + TRACE_3("layTrenchline EH",_source,_args,_return); + _return params ["_success", "_reason", ["_info", ""]]; + if ((!_success) && {!isNull _source}) then { + [QEGVAR(common,displayTextStructured), [["%1:
%2
%3", "str_mis_state_failed", _reason, _info], 3], [_source]] call CBA_fnc_targetEvent; + }; + }] call CBA_fnc_addEventHandler; }; if (!hasInterface) exitWith {}; diff --git a/addons/trenches/functions/fnc_blockTrench_place.sqf b/addons/trenches/functions/fnc_blockTrench_place.sqf new file mode 100644 index 0000000000..f2aa062fe8 --- /dev/null +++ b/addons/trenches/functions/fnc_blockTrench_place.sqf @@ -0,0 +1,164 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Dig trenchline + * + * Arguments: + * 0: Position + * 1: Position + * 2: Force - ignoring saftey checks (optional: false) + * 3: Cut Grass (optional: false) + * + * Return Value: + * + * 0: Success + * 1: Failure reason + * 2: Extra info + * + * Example: + * [a, b] call ace_trenches_fnc_blockTrench_place + * + * Public: No + */ + +if (!isServer) exitWith { ERROR("function must be called on server"); [false, "server-only"]; }; + +params [["_start2d", [], [[]]], ["_end2d", [], [[]]], ["_force", false, [false]], ["_cutGrass", false, [false]]]; +TRACE_3("blockTrench_place",_start2d,_end2d,_force); + +scopeName "main"; + +// get maths +getTerrainInfo params ["", "", "_cellsize"]; +if ((_cellsize < 1) || {_cellsize > 10}) exitWith { [false, "world cellsize"] breakOut "main" }; // malden is 12.5 + +// for Land_Trench_01_forest_F +private _modelX = 2.1; +private _modelZ = 1.1; +private _modelSize = 3.75; + +private _landAdjust = -1.7; // how deep we dig into the terrain +private _trenchDepth = -1; // how deep the floor is +private _trenchWidth = 1; // offset for each side from center +private _blockAdjust = -0.45; // get block to sit flush +private _blockScale = _cellsize / _modelSize; // scale up block to fit cellsize + +private _xOffset = _trenchWidth + _blockScale * _modelX; +private _zOffset = _blockAdjust - (_blockScale - 1) * _modelZ; +private _testRadius = 1 * _blockScale * _modelSize; + +// convert to terrain grid +_start2d = (_start2d select [0,2]) apply {_cellsize * round (_x / _cellsize)}; +_end2d = (_end2d select [0,2]) apply {_cellsize * round (_x / _cellsize)}; +_start2d params ["_ax", "_ay"]; +_end2d params ["_bx", "_by"]; +{ // make sure points aren't outside terrain + if (_x < _cellsize || {_x > (worldSize - _cellsize)}) exitWith { [false, "outside map boundry"] breakOut "main" }; +} forEach [_ax, _ay, _bx, _by]; +TRACE_3("adjusted",_cellsize,_start2d,_end2d); + +// get direction and start/end +private _east = (abs (_ax - _bx)) >= (abs (_ay - _by)); +private _origin2D = []; +private _length = 0; +if (_east) then { + _origin2D = if (_ax < _bx) then { _start2d } else { _end2d }; + _length = (abs (_ax - _bx)) / _cellsize; +} else { + _origin2D = if (_ay < _by) then { _start2d } else { _end2d }; + _length = (abs (_ay - _by)) / _cellsize; +}; +TRACE_3("",_east,_origin2D,_length); +if (_length < 2) exitWith { [false, "too short"] breakOut "main" }; + + +// Test and get block data +private _blockData = []; +for "_i" from 0 to _length do { // intentionally inclusive + private _posCenter = _origin2D; + private _posLeft = _origin2D; + private _posRight = _origin2D; + private _direction = []; + if (_east) then { + _posCenter = _posCenter vectorAdd [(_i + 0.5) * _cellsize, 0]; + _posLeft = _posCenter vectorAdd [0, _xOffset]; + _posRight = _posCenter vectorAdd [0, -_xOffset]; + _direction = [0,-1,0]; + } else { + _posCenter = _posCenter vectorAdd [0, (_i + 0.5) * _cellsize]; + _posLeft = _posCenter vectorAdd [_xOffset, 0]; + _posRight = _posCenter vectorAdd [-_xOffset, 0]; + _direction = [-1,0,0]; + }; + + { // Test if each point is valid + private _pos2d = _x; + // check water + if ((!_force) && {(getTerrainHeightASL _pos2D) < 0}) then { [false, "water"] breakOut "main" }; + // check canDig (surface type) + if ((!_force) && {!([_pos2d] call EFUNC(common,canDig))}) then { [false, "canDig surface"] breakOut "main" }; + // check canDig (surface type) + if ((!_force) && {isOnRoad _pos2D}) then { [false, "road"] breakOut "main" }; + // check terrain objects + private _terrainObjects = nearestTerrainObjects [_pos2d, [], _testRadius, false, true]; + // todo: want to avoid touching trees and large rocks but could allow some small shrubs to be overlapped + if (_terrainObjects isNotEqualTo []) then { + if (_force) then { + WARNING_1("overlapping terrainObjects %1",_terrainObjects); + } else { + [false, "terrain object", _terrainObjects] breakOut "main"; + }; + }; + // check mission objects + private _missionObjects = nearestObjects [_origin2D, ["All"], _testRadius, true]; + _missionObjects = _missionObjects select { !(_x isKindOf "Logic") }; + if (_missionObjects isNotEqualTo []) then { + _missionObjects = _missionObjects apply {typeOf _x}; + if (_force) then { + WARNING_1("blocking missionObjects %1",_missionObjects); + } else { + [false, "mission object", _missionObjects] breakOut "main"; + }; + }; + } forEach [_posCenter, _posLeft, _posRight]; + + _posCenter set [2, (getTerrainHeightASL _posCenter) + _zOffset + _trenchDepth]; + _posLeft set [2, (getTerrainHeightASL _posLeft) + _zOffset]; + _posRight set [2, (getTerrainHeightASL _posRight) + _zOffset]; + + if (_cutGrass && {_i != 0} && {_i != _length}) then { + _blockData pushBack ["Land_ClutterCutter_medium_F", _blockScale, _posCenter, [0,1,0], surfaceNormal _posCenter]; + }; + // todo: there also is a snow textured block or do it right and make our own re-texturable model + _blockData pushBack ["Land_Trench_01_forest_F", _blockScale, _posCenter, _direction, surfaceNormal _posCenter]; + _blockData pushBack ["Land_Trench_01_forest_F", _blockScale, _posLeft, _direction, surfaceNormal _posLeft]; + _blockData pushBack ["Land_Trench_01_forest_F", _blockScale, _posRight, _direction vectorMultiply -1, surfaceNormal _posRight]; +}; + + +// Adjust terrain heights +private _terrainData = []; +for "_i" from 1 to (_length - 1) do { // skip first and last + private _posCenter = _origin2D; + if (_east) then { + _posCenter = _posCenter vectorAdd [_i * _cellsize, 0]; + } else { + _posCenter = _posCenter vectorAdd [0, _i * _cellsize]; + }; + + _posCenter set [2, (getTerrainHeight _posCenter) + _landAdjust]; + _terrainData pushBack _posCenter +}; +TRACE_1("setTerrainHeight",count _terrainData); +setTerrainHeight [_terrainData, true]; + + +// Place blocks +{ + _x params ["_xClass", "_xScale", "_xPosASL", "_xDir", "_xUp"]; + private _block = createSimpleObject [_xClass, _xPosASL]; + _block setVectorDirAndUp [_xDir, _xUp]; + if (_xScale != 1) then { _block setObjectScale _xScale; }; +} forEach _blockData; + +[true, "", _length] diff --git a/addons/zeus/CfgVehicles.hpp b/addons/zeus/CfgVehicles.hpp index 737f2acc09..4ae07fdedc 100644 --- a/addons/zeus/CfgVehicles.hpp +++ b/addons/zeus/CfgVehicles.hpp @@ -179,6 +179,11 @@ class CfgVehicles { function = QFUNC(moduleHeal); icon = QPATHTOF(ui\Icon_Module_Zeus_Heal_ca.paa); }; + class GVAR(moduleLayTrench): GVAR(moduleBase) { + category = QGVAR(Utility); + displayName = CSTRING(ModuleLayTrenchline_DisplayName); + function = QFUNC(moduleLayTrench); + }; class GVAR(moduleLoadIntoCargo): GVAR(moduleBase) { curatorCanAttach = 1; category = QGVAR(Utility); diff --git a/addons/zeus/XEH_PREP.hpp b/addons/zeus/XEH_PREP.hpp index c92c98b211..46cbbeb446 100644 --- a/addons/zeus/XEH_PREP.hpp +++ b/addons/zeus/XEH_PREP.hpp @@ -21,6 +21,7 @@ PREP(moduleGarrison); PREP(moduleGlobalSetSkill); PREP(moduleGroupSide); PREP(moduleHeal); +PREP(moduleLayTrench); PREP(moduleLoadIntoCargo); PREP(moduleRemoveArsenal); PREP(moduleRemoveAceArsenal); diff --git a/addons/zeus/config.cpp b/addons/zeus/config.cpp index 3856828230..8b752771b7 100644 --- a/addons/zeus/config.cpp +++ b/addons/zeus/config.cpp @@ -92,6 +92,11 @@ class CfgPatches { QGVAR(moduleBurn) }; }; + class GVAR(trenches): ADDON { + units[] = { + QGVAR(moduleLayTrench) + }; + }; }; class ACE_Curator { @@ -104,6 +109,7 @@ class ACE_Curator { GVAR(pylons) = "ace_pylons"; GVAR(arsenal) = "ace_arsenal"; GVAR(fire) = "ace_fire"; + GVAR(trenches) = "ace_trenches"; }; #include "CfgFactionClasses.hpp" diff --git a/addons/zeus/functions/fnc_moduleLayTrench.sqf b/addons/zeus/functions/fnc_moduleLayTrench.sqf new file mode 100644 index 0000000000..ae9d60f2f2 --- /dev/null +++ b/addons/zeus/functions/fnc_moduleLayTrench.sqf @@ -0,0 +1,50 @@ +#include "..\script_component.hpp" +/* + * PabstMirror + * Dig trenchline + * + * Arguments: + * 0: Module logic + * 1: Synchronized units + * 2: Activated + * + * Return Value: + * None + * + * Example: + * [LOGIC, [], true] call ace_zeus_fnc_moduleLayTrench + * + * Public: No + */ +if (canSuspend) exitWith {[FUNC(moduleLayTrench), _this] call CBA_fnc_directCall;}; + +params ["_logic", "_units", "_activated"]; +if !(_activated && {local _logic}) exitWith {}; +TRACE_1("",_logic); + +if !(["ace_trenches"] call EFUNC(common,isModLoaded)) exitWith { + deleteVehicle _logic; + [LSTRING(RequiresAddon)] call FUNC(showMessage); +}; + +private _startPos = getPosASL _logic; // change position of logic to be aligned with grid +getTerrainInfo params ["", "", "_cellsize"]; +_startPos = (_startPos select [0,2]) apply {_cellsize * round (_x / _cellsize)}; +_logic setPos _startPos; + + +// todo: it would be nice to show visually that trenches can only be dug north/south or east/west +private _text = format ["%1 %2", LELSTRING(trenches,ConfirmDig), LLSTRING(ModuleLayTrenchline_Tooltip)]; +[_logic, { + params ["_successful", "_logic", "_mousePosASL", "_shift"]; + TRACE_4("getModuleDestination",_successful,_logic,_mousePosASL,_shift); + + private _startPosASL = getPosASL _logic; + deleteVehicle _logic; + if (isNull _logic) exitWith { TRACE_1("exit",isNull _logic); }; + if (!_successful) exitWith { TRACE_1("exit",_successful); }; + + private _args = [_startPosASL, _mousePosASL, _shift]; + TRACE_1("sending event",_args); + [QEGVAR(trenches,layTrenchline), [ace_player, _args]] call CBA_fnc_serverEvent; +}, _text, "\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa", [0, 1, 0, 1], 45] call FUNC(getModuleDestination); diff --git a/addons/zeus/stringtable.xml b/addons/zeus/stringtable.xml index 4769f897ca..b32b09692b 100644 --- a/addons/zeus/stringtable.xml +++ b/addons/zeus/stringtable.xml @@ -1933,5 +1933,11 @@ Поджечь юнита Quemar a unidad + + Lay Trenchline + + + +SHIFT to force (Can only lay N/S or E/W) +