Merge pull request #1007 from acemod/userActionPrototype

Add Actions to buildings (UserActions - Ladders)
This commit is contained in:
Nicolás Badano 2015-07-01 18:11:38 -03:00
commit cabddf89b7
15 changed files with 327 additions and 5 deletions

BIN
ace_parse_imagepath.dll Normal file

Binary file not shown.

View File

@ -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);
};
};

View File

@ -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 {

View File

@ -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) = {

View File

@ -21,5 +21,5 @@ class CfgPatches {
#include "ACE_Settings.hpp"
class ACE_Extensions {
extensions[] += {"ace_break_line"};
extensions[] += {"ace_break_line", "ace_parse_imagepath"};
};

View File

@ -90,7 +90,7 @@ _recurseFnc = {
[],
_position,
_distance,
[_showDisabled,_enableInside,_canCollapse,_runOnHover],
[_showDisabled,_enableInside,_canCollapse,_runOnHover, false],
_modifierFunction
],
_children

View File

@ -74,7 +74,7 @@ _recurseFnc = {
{},
[0,0,0],
10, //distace
[_showDisabled,_enableInside,_canCollapse,_runOnHover],
[_showDisabled,_enableInside,_canCollapse,_runOnHover, true],
_modifierFunction
],
_children

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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]

View File

@ -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>

View File

@ -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)

View 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()

View 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 )