mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Merge pull request #1007 from acemod/userActionPrototype
Add Actions to buildings (UserActions - Ladders)
This commit is contained in:
commit
cabddf89b7
BIN
ace_parse_imagepath.dll
Normal file
BIN
ace_parse_imagepath.dll
Normal file
Binary file not shown.
@ -76,4 +76,11 @@ class ACE_Settings {
|
||||
displayName = CSTRING(background);
|
||||
values[] = {"$STR_A3_OPTIONS_DISABLED", CSTRING(backgroundBlur), CSTRING(backgroundBlack)};
|
||||
};
|
||||
class GVAR(addBuildingActions) {
|
||||
value = 0;
|
||||
typeName = "BOOL";
|
||||
isClientSettable = 1;
|
||||
displayName = CSTRING(addBuildingActions);
|
||||
description = CSTRING(addBuildingActionsDescription);
|
||||
};
|
||||
};
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
if (!hasInterface) exitWith {};
|
||||
|
||||
GVAR(cachedBuildingTypes) = [];
|
||||
GVAR(cachedBuildingActionPairs) = [];
|
||||
|
||||
GVAR(ParsedTextCached) = [];
|
||||
|
||||
//Setup text/shadow/size/color settings matrix
|
||||
@ -17,6 +20,9 @@ GVAR(ParsedTextCached) = [];
|
||||
// Install the render EH on the main display
|
||||
addMissionEventHandler ["Draw3D", DFUNC(render)];
|
||||
|
||||
//Add Actions to Houses:
|
||||
["interactMenuOpened", {_this call FUNC(userActions_addHouseActions)}] call EFUNC(common,addEventHandler);
|
||||
|
||||
// This spawn is probably worth keeping, as pfh don't work natively on the briefing screen and IDK how reliable the hack we implemented for them is.
|
||||
// The thread dies as soon as the mission start, so it's not really compiting for scheduler space.
|
||||
[] spawn {
|
||||
|
@ -25,6 +25,8 @@ PREP(renderMenu);
|
||||
PREP(renderSelector);
|
||||
PREP(setupTextColors);
|
||||
PREP(splitPath);
|
||||
PREP(userActions_addHouseActions);
|
||||
PREP(userActions_getHouseActions);
|
||||
|
||||
// Event handlers for all interact menu controls
|
||||
DFUNC(handleMouseMovement) = {
|
||||
|
@ -21,5 +21,5 @@ class CfgPatches {
|
||||
#include "ACE_Settings.hpp"
|
||||
|
||||
class ACE_Extensions {
|
||||
extensions[] += {"ace_break_line"};
|
||||
extensions[] += {"ace_break_line", "ace_parse_imagepath"};
|
||||
};
|
||||
|
@ -90,7 +90,7 @@ _recurseFnc = {
|
||||
[],
|
||||
_position,
|
||||
_distance,
|
||||
[_showDisabled,_enableInside,_canCollapse,_runOnHover],
|
||||
[_showDisabled,_enableInside,_canCollapse,_runOnHover, false],
|
||||
_modifierFunction
|
||||
],
|
||||
_children
|
||||
|
@ -74,7 +74,7 @@ _recurseFnc = {
|
||||
{},
|
||||
[0,0,0],
|
||||
10, //distace
|
||||
[_showDisabled,_enableInside,_canCollapse,_runOnHover],
|
||||
[_showDisabled,_enableInside,_canCollapse,_runOnHover, true],
|
||||
_modifierFunction
|
||||
],
|
||||
_children
|
||||
|
@ -68,7 +68,7 @@ _distance = if (count _this > 8) then {
|
||||
_params = if (count _this > 9) then {
|
||||
_this select 9
|
||||
} else {
|
||||
[false,false,false,false]
|
||||
[false,false,false,false,false]
|
||||
};
|
||||
|
||||
_modifierFunction = if (count _this > 10) then {
|
||||
|
@ -44,7 +44,7 @@ if (GVAR(openedMenuType) == 0 && (vehicle ACE_player == ACE_player) && (isNull c
|
||||
|
||||
if (_actualDistance > _distance) exitWith {true};
|
||||
|
||||
if (_actualDistance > 1.5) exitWith {
|
||||
if ((_actualDistance > 1.5) && {!((_actionData select 9) select 4)}) exitWith {
|
||||
// If distance to action is greater than 1.5 m, check LOS
|
||||
_line = [_headPos call EFUNC(common,positionToASL), _pos call EFUNC(common,positionToASL), _object, ACE_player];
|
||||
lineIntersects _line
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Scans for nearby "Static" objects (buildings) and adds the UserActions to them.
|
||||
* Called when interact_menu starts rendering (from "interact_keyDown" event)
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Interact Menu Type (0 - world, 1 - self) <NUMBER>
|
||||
*
|
||||
* Return Value:
|
||||
* Nothing
|
||||
*
|
||||
* Example:
|
||||
* [0] call ace_interact_menu_fnc_addHouseActions
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
PARAMS_1(_interactionType);
|
||||
|
||||
//Ignore if not enabled:
|
||||
if (!GVAR(addBuildingActions)) exitWith {};
|
||||
//Ignore self-interaction menu:
|
||||
if (_interactionType != 0) exitWith {};
|
||||
//Ignore when mounted:
|
||||
if ((vehicle ACE_player) != ACE_player) exitWith {};
|
||||
|
||||
[{
|
||||
private ["_nearBuidlings", "_typeOfHouse", "_houseBeingScaned", "_actionSet", "_memPoints", "_memPointsActions", "_helperPos", "_helperObject"];
|
||||
PARAMS_2(_args,_pfID);
|
||||
EXPLODE_4_PVT(_args,_setPosition,_addedHelpers,_housesScaned,_housesToScanForActions);
|
||||
|
||||
if (!EGVAR(interact_menu,keyDown)) then {
|
||||
{deleteVehicle _x;} forEach _addedHelpers;
|
||||
[_pfID] call CBA_fnc_removePerFrameHandler;
|
||||
} else {
|
||||
// Prevent Rare Error when ending mission with interact key down:
|
||||
if (isNull ace_player) exitWith {};
|
||||
|
||||
//Make the common case fast (cursorTarget is looking at a door):
|
||||
if ((!isNull cursorTarget) && {cursorTarget isKindOf "Static"} && {!(cursorTarget in _housesScaned)}) then {
|
||||
if (((count (configFile >> "CfgVehicles" >> (typeOf cursorTarget) >> "UserActions")) > 0) || {(count (getArray (configFile >> "CfgVehicles" >> (typeOf cursorTarget) >> "ladders"))) > 0}) then {
|
||||
_housesToScanForActions = [cursorTarget];
|
||||
} else {
|
||||
_housesScaned pushBack cursorTarget;
|
||||
};
|
||||
};
|
||||
|
||||
//For performance, we only do 1 thing per frame,
|
||||
//-either do a wide scan and search for houses with actions
|
||||
//-or scan one house at a time and add the actions for that house
|
||||
|
||||
if (_housesToScanForActions isEqualTo []) then {
|
||||
//If player moved >2 meters from last pos, then rescan
|
||||
if (((getPosASL ace_player) distance _setPosition) < 2) exitWith {};
|
||||
|
||||
_nearBuidlings = nearestObjects [ace_player, ["Static"], 30];
|
||||
{
|
||||
_typeOfHouse = typeOf _x;
|
||||
if (((count (configFile >> "CfgVehicles" >> _typeOfHouse >> "UserActions")) == 0) && {(count (getArray (configFile >> "CfgVehicles" >> _typeOfHouse >> "ladders"))) == 0}) then {
|
||||
_housesScaned pushBack _x;
|
||||
} else {
|
||||
_housesToScanForActions pushBack _x;
|
||||
};
|
||||
} forEach (_nearBuidlings - _housesScaned);
|
||||
|
||||
_args set [0, (getPosASL ace_player)];
|
||||
} else {
|
||||
_houseBeingScaned = _housesToScanForActions deleteAt 0;
|
||||
_typeOfHouse = typeOf _houseBeingScaned;
|
||||
//Skip this house for now if we are outside of it's radius
|
||||
//(we have to scan far out for the big houses, but we don't want to waste time adding actions on every little shack)
|
||||
if ((_houseBeingScaned != cursorTarget) && {((ACE_player distance _houseBeingScaned) - ((sizeOf _typeOfHouse) / 2)) > 4}) exitWith {};
|
||||
|
||||
_housesScaned pushBack _houseBeingScaned;
|
||||
|
||||
_actionSet = [_typeOfHouse] call FUNC(userActions_getHouseActions);
|
||||
EXPLODE_2_PVT(_actionSet,_memPoints,_memPointsActions);
|
||||
|
||||
// systemChat format ["Add Actions for [%1] (count %2) @ %3", _typeOfHouse, (count _memPoints), diag_tickTime];
|
||||
{
|
||||
_helperPos = (_houseBeingScaned modelToWorld (_houseBeingScaned selectionPosition _x)) call EFUNC(common,positionToASL);
|
||||
_helperObject = "Sign_Sphere25cm_F" createVehicleLocal _helperPos;
|
||||
_addedHelpers pushBack _helperObject;
|
||||
_helperObject setVariable [QGVAR(building), _houseBeingScaned];
|
||||
_helperObject setPosASL _helperPos;
|
||||
_helperObject hideObject true;
|
||||
TRACE_3("Making New Helper",_helperObject,_x,_houseBeingScaned);
|
||||
|
||||
{
|
||||
[_helperObject, 0, [], _x] call EFUNC(interact_menu,addActionToObject);
|
||||
} forEach (_memPointsActions select _forEachIndex);
|
||||
|
||||
} forEach _memPoints;
|
||||
};
|
||||
};
|
||||
}, 0, [((getPosASL ace_player) vectorAdd [-100,0,0]), [], [], []]] call CBA_fnc_addPerFrameHandler;
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Scans the buidling type for UserActions and Ladder mount points.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Building Classname <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* [[Array of MemPoints], [Array Of Actions]]
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
PARAMS_1(_typeOfBuilding);
|
||||
|
||||
private["_action", "_actionDisplayName", "_actionDisplayNameDefault", "_actionMaxDistance", "_actionOffset", "_actionPath", "_actionPosition", "_building", "_configPath", "_endIndex", "_iconImage", "_index", "_ladders", "_memPointIndex", "_memPoints", "_memPointsActions", "_startIndex"];
|
||||
|
||||
_searchIndex = GVAR(cachedBuildingTypes) find _typeOfBuilding;
|
||||
if (_searchIndex != -1) exitWith {GVAR(cachedBuildingActionPairs) select _searchIndex};
|
||||
|
||||
_memPoints = [];
|
||||
_memPointsActions = [];
|
||||
|
||||
//Get the offset for a memory point:
|
||||
_fnc_getMemPointOffset = {
|
||||
PARAMS_1(_memoryPoint);
|
||||
_memPointIndex = _memPoints find _memoryPoint;
|
||||
_actionOffset = [0,0,0];
|
||||
if (_memPointIndex == -1) then {
|
||||
_memPoints pushBack _memoryPoint;
|
||||
_memPointsActions pushBack [];
|
||||
} else {
|
||||
_actionOffset set [2, 0.0254 * (count (_memPointsActions select _memPointIndex))];
|
||||
};
|
||||
_actionOffset
|
||||
};
|
||||
|
||||
// Add UserActions for the building:
|
||||
_fnc_userAction_Statement = {
|
||||
PARAMS_3(_target,_player,_variable);
|
||||
EXPLODE_2_PVT(_variable,_actionStatement,_actionCondition);
|
||||
this = _target getVariable [QGVAR(building), objNull];
|
||||
call _actionStatement;
|
||||
};
|
||||
_fnc_userAction_Condition = {
|
||||
PARAMS_3(_target,_player,_variable);
|
||||
EXPLODE_2_PVT(_variable,_actionStatement,_actionCondition);
|
||||
this = _target getVariable [QGVAR(building), objNull];
|
||||
if (isNull this) exitWith {false};
|
||||
call _actionCondition;
|
||||
};
|
||||
|
||||
_configPath = configFile >> "CfgVehicles" >> _typeOfBuilding >> "UserActions";
|
||||
for "_index" from 0 to ((count _configPath) - 1) do {
|
||||
_actionPath = _configPath select _index;
|
||||
|
||||
_actionDisplayName = getText (_actionPath >> "displayName");
|
||||
_actionDisplayNameDefault = getText (_actionPath >> "displayNameDefault");
|
||||
_actionPosition = getText (_actionPath >> "position");
|
||||
_actionCondition = getText (_actionPath >> "condition");
|
||||
_actionStatement = getText (_actionPath >> "statement");
|
||||
_actionMaxDistance = getNumber (_actionPath >> "radius");
|
||||
|
||||
if (_actionDisplayName == "") then {_actionDisplayName = (configName _x);};
|
||||
if (_actionPosition == "") then {ERROR("Bad Position");};
|
||||
if (_actionCondition == "") then {_actionCondition = "true";};
|
||||
if (_actionStatement == "") then {ERROR("No Statement");};
|
||||
|
||||
_actionStatement = compile _actionStatement;
|
||||
_actionCondition = compile _actionCondition;
|
||||
_actionMaxDistance = _actionMaxDistance + 0.1; //increase range slightly
|
||||
_iconImage = "";
|
||||
|
||||
//extension ~4x as fast:
|
||||
_iconImage = "ace_parse_imagepath" callExtension _actionDisplayNameDefault;
|
||||
|
||||
_actionOffset = [_actionPosition] call _fnc_getMemPointOffset;
|
||||
_memPointIndex = _memPoints find _actionPosition;
|
||||
|
||||
_action = [(configName _actionPath), _actionDisplayName, _iconImage, _fnc_userAction_Statement, _fnc_userAction_Condition, {}, [_actionStatement, _actionCondition], _actionOffset, _actionMaxDistance, [false,false,false,false,true]] call EFUNC(interact_menu,createAction);
|
||||
(_memPointsActions select _memPointIndex) pushBack _action;
|
||||
};
|
||||
|
||||
// Add Ladder Actions for the building:
|
||||
_fnc_ladder_ladderUp = {
|
||||
PARAMS_3(_target,_player,_variable);
|
||||
EXPLODE_1_PVT(_variable,_ladderIndex);
|
||||
_building = _target getVariable [QGVAR(building), objNull];
|
||||
TRACE_3("Ladder Action - UP",_player,_building,_ladderIndex);
|
||||
_player action ["LadderUp", _building, _ladderIndex, 0];
|
||||
};
|
||||
_fnc_ladder_ladderDown = {
|
||||
PARAMS_3(_target,_player,_variable);
|
||||
EXPLODE_1_PVT(_variable,_ladderIndex);
|
||||
_building = _target getVariable [QGVAR(building), objNull];
|
||||
TRACE_3("Ladder Action - Down",_player,_building,_ladderIndex);
|
||||
_player action ["LadderDown", _building, _ladderIndex, 1];
|
||||
};
|
||||
|
||||
_fnc_ladder_conditional = {
|
||||
PARAMS_2(_target,_player);
|
||||
//(Check distance < 2) and (Don't show actions if on a ladder)
|
||||
((_target distance _player) < 2) && {((getNumber (configFile >> "CfgMovesMaleSdr" >> "States" >> (animationState _player) >> "onLadder")) == 0)}
|
||||
};
|
||||
|
||||
_ladders = getArray (configFile >> "CfgVehicles" >> _typeOfBuilding >> "ladders");
|
||||
{
|
||||
EXPLODE_2_PVT(_x,_ladderBottomMemPoint,_ladderTopMemPoint);
|
||||
|
||||
_actionMaxDistance = 3; //interact_menu will check head -> target's offset; leave this high and do a precice distance check in condition
|
||||
|
||||
_actionDisplayName = localize "str_action_ladderup";
|
||||
_iconImage = "\A3\ui_f\data\igui\cfg\actions\ladderup_ca.paa";
|
||||
//Ladder Up Action:
|
||||
_actionOffset = [_ladderBottomMemPoint] call _fnc_getMemPointOffset;
|
||||
_actionOffset = _actionOffset vectorAdd [0,0,1];
|
||||
_memPointIndex = _memPoints find _ladderBottomMemPoint;
|
||||
_action = [format ["LadderUp_%1", _forEachIndex], _actionDisplayName, _iconImage, _fnc_ladder_ladderUp, _fnc_ladder_conditional, {}, [_forEachIndex], _actionOffset, _actionMaxDistance, [false,false,false,false,true]] call EFUNC(interact_menu,createAction);
|
||||
(_memPointsActions select _memPointIndex) pushBack _action;
|
||||
|
||||
_actionDisplayName = localize "str_action_ladderdown";
|
||||
_iconImage = "\A3\ui_f\data\igui\cfg\actions\ladderdown_ca.paa";
|
||||
//Ladder Down Action:
|
||||
_actionOffset = [_ladderTopMemPoint] call _fnc_getMemPointOffset;
|
||||
_actionOffset = _actionOffset vectorAdd [0,0,0.25];
|
||||
_memPointIndex = _memPoints find _ladderTopMemPoint;
|
||||
_action = [format ["LadderDown_%1", _forEachIndex], _actionDisplayName, _iconImage, _fnc_ladder_ladderDown, _fnc_ladder_conditional, {}, [_forEachIndex], _actionOffset, _actionMaxDistance, [false,false,false,false,true]] call EFUNC(interact_menu,createAction);
|
||||
(_memPointsActions select _memPointIndex) pushBack _action;
|
||||
|
||||
} forEach _ladders;
|
||||
|
||||
GVAR(cachedBuildingTypes) pushBack _typeOfBuilding;
|
||||
GVAR(cachedBuildingActionPairs) pushBack [_memPoints, _memPointsActions];
|
||||
|
||||
|
||||
[_memPoints, _memPointsActions]
|
@ -248,5 +248,11 @@
|
||||
<Czech>Černý obraz</Czech>
|
||||
<Portuguese>Preto</Portuguese>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Interact_Menu_addBuildingActions">
|
||||
<English>Show actions for buildings</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Interact_Menu_addBuildingActionsDescription">
|
||||
<English>Adds interaction actions for opening doors and mounting ladders on buildings. (Note: There is a performance cost when opening interaction menu, especially in towns)</English>
|
||||
</Key>
|
||||
</Package>
|
||||
</Project>
|
||||
|
@ -126,6 +126,7 @@ add_subdirectory(break_line)
|
||||
add_subdirectory(clipboard)
|
||||
add_subdirectory(advanced_ballistics)
|
||||
add_subdirectory(medical)
|
||||
add_subdirectory(parse_imagepath)
|
||||
|
||||
# Test Extension for dynamically loading/unloading built extensions; does not build in release
|
||||
if (DEVEL)
|
||||
|
12
extensions/parse_imagepath/CMakeLists.txt
Normal file
12
extensions/parse_imagepath/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
set(ACE_EXTENSION_NAME "ace_parse_imagepath")
|
||||
|
||||
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
|
||||
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
|
||||
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
|
||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
|
||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
|
||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
|
||||
endif()
|
54
extensions/parse_imagepath/ace_parse_imagepath.cpp
Normal file
54
extensions/parse_imagepath/ace_parse_imagepath.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* ace_parse_imagepath.cpp
|
||||
* Author: PabstMirror
|
||||
* Gets raw image path from structured text input.
|
||||
*
|
||||
* Takes:
|
||||
* Structured text that usualy has an image:
|
||||
* Example: "<img image='\A3\Ui_f\data\IGUI\Cfg\Actions\open_door_ca.paa' size='2.5' />";
|
||||
*
|
||||
* Returns:
|
||||
* Just the image path or "" if none
|
||||
*/
|
||||
|
||||
#include "shared.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
__declspec (dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function);
|
||||
};
|
||||
|
||||
std::string getImagePathFromStructuredText(const std::string & input) {
|
||||
std::string returnValue = "";
|
||||
std::size_t endIndex = input.find(".paa");
|
||||
std::size_t startIndex = endIndex - 1;
|
||||
if ((endIndex != std::string::npos) && (endIndex > 1)) {
|
||||
endIndex = endIndex + 4;
|
||||
while ((startIndex > 0) && (returnValue == "")) {
|
||||
if ((input[startIndex]) == '\'') {
|
||||
returnValue = input.substr((startIndex + 1), (endIndex - startIndex - 1));
|
||||
};
|
||||
startIndex = startIndex - 1;
|
||||
};
|
||||
};
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// i like to live dangerously. jk, fix strncpy sometime pls.
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4996 )
|
||||
|
||||
void __stdcall RVExtension(char *output, int outputSize, const char *function) {
|
||||
ZERO_OUTPUT();
|
||||
if (!strcmp(function, "version")) {
|
||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize);
|
||||
} else {
|
||||
strncpy(output, getImagePathFromStructuredText(function).c_str(), outputSize);
|
||||
output[outputSize - 1] = '\0';
|
||||
}
|
||||
EXTENSION_RETURN();
|
||||
}
|
||||
|
||||
#pragma warning( pop )
|
Loading…
Reference in New Issue
Block a user