Files
DayZ-Epoch/SQF/dayz_code/actions/player_buildVanilla.sqf
icomrade 7ea9a270f2 New building functions, vanilla building checks
DZE_RequiredItemsCheck returns bool, displays an error of all the tools
the player is missing to build the item.

DZE_BuildChecks returns array of bools, canBuild and if the item is a
plotPole.  Also has the ability to check if the player has required
tools to build using DZE_RequiredItemsCheck.

The vanilla build script still needs to be modified to work over water
and allow players to place floating objects, but now uses plot checks.
It would be easiest to just modify the configs to use the epoch building
scripts instead.

fnc_getSetPos now accepts a position as the second element of the input
array. the object will be set to this position if it is used. This
change requires no code updates

Note: scripts with build and/or tool checks should be updated to use the
new functions
2016-04-27 14:30:11 -04:00

415 lines
16 KiB
Plaintext

// (c) facoptere@gmail.com, licensed to DayZMod for the community
private ["_classType","_item","_action","_missingTools","_missingItem","_emergingLevel","_isClass","_classname","_requiredTools",
"_requiredParts","_ghost","_placement","_text","_onLadder","_isWater","_object","_string","_actionBuildHidden","_getBeams",
"_o","_offset","_rot","_r","_p","_bn","_bb","_h","_bx","_by","_minElevation","_maxElevation","_insideCheck","_building",
"_unit","_bbb","_ubb","_check","_min","_max","_myX","_myY","_checkBuildingCollision","_objColliding","_inside","_checkOnRoad",
"_roadCollide","_checkBeam2Magnet","_a","_beams","_best","_b","_d","_checkNotBuried","_elevation","_position","_delta","_overElevation",
"_maxplanting","_safeDistance","_dir","_angleRef","_tmp","_actionCancel","_sfx","_actionBuild","_byPassChecks","_keepOnSlope","_msg",
"_ok","_missing","_upgradeParts","_ownerID","_posReference"];
/*
Needs a full rewrite to keep up with the demand of everything we plan to add.
*/
call gear_ui_init;
closeDialog 1;
_item = _this select 0;
_action = _this select 1;
_classType = "ItemActions";
if (count _this > 2) then {
_classType = _this select 2;
};
_emergingLevel = 1.1;
r_action_count = 1;
_isClass = switch (1==1) do {
case (isClass (configFile >> "CfgMagazines" >> _item)): {"CfgMagazines"};
case (isClass (configFile >> "CfgWeapons" >> _item)): {"CfgWeapons"};
};
//need to swap all build systems to this "ItemActions"
_classname = getText (configFile >> _isClass >> _item >> _classType >> _action >> "create");
_requiredTools = getArray (configFile >> _isClass >> _item >> _classType >> _action >> "require");
_requiredParts = getArray (configFile >> _isClass >> _item >> _classType >> _action >> "consume");
_ghost = getText (configFile >> _isClass >> _item >> _classType >> _action >> "ghost");
//need to move to array and separate what checks need to be done.
_byPassChecks = getText (configFile >> _isClass >> _item >> _classType >> _action >> "byPass");
if (_byPassChecks == "") then { _byPassChecks = "BaseItems" };
if (_ghost == "") then { _ghost = _classname; };
_text = getText (configFile >> "CfgVehicles" >> _classname >> "displayName");
_keepOnSlope = 0 == (getNumber (configFile >> "CfgVehicles" >> _classname >> "canbevertical"));
_onLadder = {getNumber (configFile >> "CfgMovesMaleSdr" >> "States" >> (animationState player) >> "onLadder") == 1};
_isWater = {(surfaceIsWater (getPosATL _object)) or dayz_isSwimming};
if (0 != count Dayz_constructionContext) then {
r_action_count = 0;
//localize "str_already_building" call dayz_rollingMessages;
_msg = localize "str_already_building";
_msg call dayz_rollingMessages;
diag_log [ diag_ticktime, __FILE__, 'already building, exiting', Dayz_constructionContext, typeName Dayz_constructionContext];
};
// item is missin - this really is pointless but it aint broke so dont fix it
if (isClass (configFile >> _isClass >> _item)) then {
if ((!(_item IN magazines player))) exitWith {
_string = switch true do {
case (_item isKindOf "Land_A_tent"): {"str_player_31_pitch"};
default {"str_player_31_build"};
};
//format[localize "str_player_31",_text,(localize _string)] call dayz_rollingMessages;
_msg = format [localize "str_player_31",_text,(localize _string)];
_msg call dayz_rollingMessages;
//diag_log(format["player_build: item:%1 require:%2 Player items:%3 magazines:%4", _item, _requiredTools, (items player), (magazines player)]);
};
};
// lets check player has requiredTools for upgrade
_ok = true;
_missing = "";
{
if (!(_x IN items player)) exitWith {
//systemchat("Missing tools for upgrade." +str());
_missing = getText (configFile >> "CfgWeapons" >> _x >> "displayName");
_ok = false;
};
} count _requiredTools;
if (!_ok) exitWith {
r_action_count = 0;
//format[localize "str_player_31_missingtools",_text,_missing] call dayz_rollingMessages;
_msg = format [localize "str_player_31_missingtools",_text,_missing];
_msg call dayz_rollingMessages;
};
// lets check player has requiredParts for upgrade
_ok = true;
_upgradeParts = [];
{
if (!(_x IN magazines player)) exitWith {
_missing = getText (configFile >> "CfgMagazines" >> _x >> "displayName");
_ok = false;
};
if (_x IN magazines player) then {
_upgradeParts set [count _upgradeParts, _x];
player removeMagazine _x;
};
} count _requiredParts;
if (!_ok) exitWith {
{ player addMagazine _x; } foreach _upgradeParts;
r_action_count = 0;
// format[localize "str_player_31", _missing, localize "str_player_31_build"] call dayz_rollingMessages;
_msg = format [localize "str_player_31", _missing, localize "str_player_31_build"];
_msg call dayz_rollingMessages;
};
_posReference = [player] call FNC_GetPos;
_canBuild = [_posReference, _item, false] call DZE_BuildChecks;
if (!(_canBuild select 0)) exitWith {};
//localize "str_player_build_rotate" call dayz_rollingMessages;
_msg = localize "str_player_build_rotate";
_msg call dayz_rollingMessages;
//Get fence beams based on model
_getBeams = {
private [ "_p", "_r", "_bn", "_bb", "_bx", "_by" ];
_o = _this select 0;
_offset = _this select 1;
_rot = _this select 2;
_r = [];
for "_bn" from 1 to 4 do {
_p = _o selectionPosition Format [ "beam%1", _bn ];
if (_p distance [0,0,0] == 0) exitWith {
if (_bn == 1) then { // no memory points defined
_bb = boundingBox _o;
_h = _offset + ((_o worldToModel (getPosATL _o)) select 2);
_p = [ (_bb select 0) select 0, 0, _h ];
_r set [ 0, _o modelToWorld _p];
_p = [ (_bb select 1) select 0, 0, _h ];
_r set [ 1, _o modelToWorld _p];
};
};
if (_rot != 0) then {
_bx = _p select 0;
_by = _p select 1;
_p set [0, (_bx * cos _rot) - (_by * sin _rot)];
_p set [1, (_bx * sin _rot) + (_by * cos _rot)];
};
_p set [2, (_p select 2) + _offset];
_r set [ count _r, _o modelToWorld _p];
};
_r
};
_minElevation = {
private "_r";
_r = 400;
{ _r = _r min (_x select 2); } count _this;
_r
};
_maxElevation = {
private "_r";
_r = -400;
{ _r = _r max (_x select 2); } count _this;
_r
};
//Is the placed object inside another object
_insideCheck = {
private ["_bbb","_building","_ubb","_unit","_check","_min","_max","_myX","_p","_myY"];
_building = _this select 0;
_unit = _this select 1;
if ((typeOf _building != "") and {(
(sizeOf (typeOf _building) < 8) or {(_unit distance _building > (sizeOf (typeOf _building) + sizeOf (typeOf _unit))/2)}
)}) exitwith {false};
_bbb = boundingBox _building;
_ubb = boundingBox _unit;
_check = {
_min = _bbb select 0;
_max = _bbb select 1;
_myX = _p select 0;
_myY = _p select 1;
(((_myX > (_min select 0)) and {(_myX < (_max select 0))}) and {((_myY > (_min select 1)) and {(_myY < (_max select 1))})})
};
_p = _building worldToModel (_unit modelToWorld [ (_ubb select 0) select 0, (_ubb select 0) select 1, 0]);
if (call _check) exitWith {true};
_p = _building worldToModel (_unit modelToWorld [ (_ubb select 0) select 0, (_ubb select 1) select 1, 0]);
if (call _check) exitWith {true};
_p = _building worldToModel (_unit modelToWorld [ (_ubb select 1) select 0, (_ubb select 1) select 1, 0]);
if (call _check) exitWith {true};
_p = _building worldToModel (_unit modelToWorld [ (_ubb select 1) select 0, (_ubb select 0) select 1, 0]);
if (call _check) exitWith {true};
false
};
//check if building being placed and objects around placement is free to be built on.
//Fence owners must build all the foundations by one player anyone can still upgrade (pending lock build level)
_checkBuildingCollision = {
_objColliding = objNull;
{
_inside = false;
_ownerID = _x getVariable ["ownerArray",[]];
if (count _ownerID > 0) then { _ownerID = _ownerID select 0; } else { _ownerID = (getPlayerUID player); };
//and (!(_x isKindOf "DZ_buildables")) Not used
if(_ownerID != (getPlayerUID player)) then {
if ((!isNull _x) and (!(_x == player)) and (!(_x == _object)) and (!(_x IN DayZ_SafeObjects))
and (!((typeOf _x == "CamoNet_DZ") or {(_x isKindOf "Land_CamoNet_EAST")}))) then {
if ((_x isKindOf "Building") or (_x isKindOf "AllVehicles")) then {
_inside = [_x, _object] call _insideCheck;
/*
if (!_inside) then {
_inside = [_object, _x] call _insideCheck;
};
*/
};
};
};
if (_inside) exitWith { _objColliding = _x; };
} forEach (nearestObjects [_object, ["Building", "Air", "LandVehicle", "Ship", "DZ_buildables"], 35]);
(!isNull _objColliding)
// _objColliding contains the building that collides with the ghost object
};
//Is placement on a road?
_checkOnRoad = {
_roadCollide = false;
{
_x set [2,0];
if (isOnRoad _x) exitWith { _roadCollide = true;};
} forEach ([_object, 0,0] call _getBeams);
_roadCollide
};
//Make the object attach to beams if it can
_checkBeam2Magnet = {
_a = [];
{
if ((!isNull _x) and (_x != _object)) then { _a = _a + ([_x, 0,0] call _getBeams); };
} forEach (nearestObjects [getPosATL _object, ["DZ_buildables"], 15]);
_beams = [_object, 0,0] call _getBeams;
_best = [50,[0,0,0],[0,0,0]];
{
_b = _x;
{
_d = [_x, _b] call BIS_fnc_distance2D;
if (_d < _best select 0) then {
_best = [_d,_b,_x];
};
} forEach _a;
} count _beams;
// _best contains the best beam to dock to. [ distance, coor of beam found around, coor of beam of ghost object ]
};
_checkNotBuried = {
// lift up the object so that any beams are buried, but also don't lift further the planting level (straight placement only)
_elevation = _position select 2;
_delta = 0;
_overElevation = 0;
_beams = [_object, 0,0] call _getBeams;
if (_elevation < 0) then { _delta = -_elevation; }
else {
_overElevation = _beams call _minElevation;
if (_overElevation>0.05) then { // bury the object so that posATL is still positif and all beams are above the ground
_delta = - (_overElevation min _elevation);
};
if (_overElevation < -0.05) then { // lift up the object because a beam is burried
_delta = - _overElevation + 0.10;
};
};
_position set [ 2, _elevation + _delta ];
_maxplanting = _beams call _maxElevation;
// _maxplanting is the height of the emerging foundations, must not be so high because we don't want some "floating" foundations
};
_object = _ghost createVehicleLocal getMarkerpos "respawn_west";
_safeDistance = 0.5 + (sizeOf _ghost) * 0.5; // beware of hedgehogs
_dir = getDir player;
_object setDir _dir;
Dayz_constructionContext = [_object, round (_dir/5)*5, cameraView, false, true, _keepOnSlope];
// ghost, angle, previous camera, build view on/off, continue on/off, slope on/off
//_posReference = getPosATL player;
_objColliding = objNull;
_best = [50,[0,0,0],[0,0,0]];
_maxplanting = 10;
_position = getPosATL _object;
_actionBuildHidden = true;
_actionCancel = player addAction [localize "str_player_build_cancel", "\z\addons\dayz_code\actions\object_build.sqf", [_object, _requiredParts, _classname, _text, false, 0, "none"], 1, true, true, "", "0 != count Dayz_constructionContext"];
while {r_action_count != 0 and Dayz_constructionContext select 4} do {
// force the angle so that the ghost is showing always the same side
_angleRef=Dayz_constructionContext select 1;
_dir = _angleRef - (getDir player);
if (_dir > 180) then {_dir = _dir - 360};
if (_dir < -180) then {_dir = _dir + 360};
if (_dir < -75) then {
_angleRef = ceil(((getDir player) - 75)/5)*5;
Dayz_constructionContext set [ 1, _angleRef];
};
if (_dir > 75) then {
_angleRef = floor(((getDir player) + 75)/5)*5;
Dayz_constructionContext set [ 1, _angleRef];
};
// move object according to player position
if ((abs(([_object, player] call BIS_fnc_distance2D) - _safeDistance) > (if (_best select 0 < 0.50) then {0.50} else {0.05}))
or (abs([player, _object] call BIS_fnc_relativeDirTo) > (if (_best select 0 < 0.50) then {5} else {1})) or (r_interrupt)) then {
r_interrupt = false;
_object setDir _angleRef;
_tmp = player modelToWorld [0, _safeDistance,0];
if (Dayz_constructionContext select 5 or _keepOnSlope) then {
_tmp set [2, 0];
_object setVectorUp surfaceNormal _tmp;
}
else {
_tmp set [2, _position select 2];
_object setVectorUp [0,0,1];
};
_position = +(_tmp);
_object setPosATL _position;
// check now that ghost is not colliding
call _checkBuildingCollision;
};
// try to dock a beam from current ghost to another beams nearby
call _checkBeam2Magnet;
if (_best select 0 < 0.50) then {
_position = [
(_position select 0) + ((_best select 2) select 0) - ((_best select 1) select 0),
(_position select 1) + ((_best select 2) select 1) - ((_best select 1) select 1),
_position select 2
];
_object setPosATL _position;
};
if (Dayz_constructionContext select 5 or _keepOnSlope) then {
_maxplanting = 0;
_position set [2, 0];
}
else {
// adjust the elevation of the object according to slope and beams to keep them visible (straight placement only)
call _checkNotBuried;
};
_object setPosATL _position;
if ((((vehicle player) != player or _posReference distance player > 20 or 0 != player getVariable["startcombattimer",0]) or {(!alive player)}) or {((call _onLadder) or {(call _isWater)})}) exitWith {
[[],[],[],[_object, _requiredParts , _classname, _text, false, 0, "none"]] call object_build;
};
if (_byPassChecks == "byPassRoadCheck") then {
if (isNull _objColliding and _maxplanting <= _emergingLevel) then { // placement is fine, enable "Build" in the menu
if (_actionBuildHidden) then {
_actionBuildHidden = false;
player removeAction _actionCancel;
_sfx = if (_object isKindOf "Land_A_tent") then {"tentunpack"} else {"repair"};
_actionBuild = player addAction [localize "str_player_build_complete", "\z\addons\dayz_code\actions\object_build.sqf", [_object, _requiredParts , _classname, _text, true, 20, _sfx], 1, true, true, "", "0 != count Dayz_constructionContext"];
_actionCancel = player addAction [localize "str_player_build_cancel", "\z\addons\dayz_code\actions\object_build.sqf", [_object, _requiredParts , _classname, _text, false, 0, "none"], 1, true, true, "", "0 != count Dayz_constructionContext"];
};
} else {
if (!_actionBuildHidden) then {
_actionBuildHidden = true;
player removeAction _actionBuild;
};
};
};
if (_byPassChecks == "BaseItems") then {
if (isNull _objColliding and _maxplanting <= _emergingLevel and !(call _checkOnRoad)) then { // placement is fine, enable "Build" in the menu
if (_actionBuildHidden) then {
_actionBuildHidden = false;
player removeAction _actionCancel;
_sfx = if (_object isKindOf "Land_A_tent") then {"tentunpack"} else {"repair"};
_actionBuild = player addAction [localize "str_player_build_complete", "\z\addons\dayz_code\actions\object_build.sqf", [_object, _requiredParts , _classname, _text, true, 20, _sfx], 1, true, true, "", "0 != count Dayz_constructionContext"];
_actionCancel = player addAction [localize "str_player_build_cancel", "\z\addons\dayz_code\actions\object_build.sqf", [_object, _requiredParts , _classname, _text, false, 0, "none"], 1, true, true, "", "0 != count Dayz_constructionContext"];
};
} else {
if (!_actionBuildHidden) then {
_actionBuildHidden = true;
player removeAction _actionBuild;
};
};
};
uiSleep 0.03;
};
if (!_actionBuildHidden) then { // player can't build until all is fine
_actionBuildHidden = true;
player removeAction _actionBuild;
};
player removeAction _actionCancel;
if (Dayz_constructionContext select 3) then { // "build" camera was on, switch it off
call fn_buildCamera;
};
Dayz_constructionContext = [];
r_action_count = 0;
//systemChat "Dayz_constructionContext reset";