mirror of
https://github.com/EpochModTeam/DayZ-Epoch.git
synced 2025-12-14 04:02:37 +03:00
1631 lines
61 KiB
Plaintext
1631 lines
61 KiB
Plaintext
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DayZ Base Building
|
|
//
|
|
// Author: vbawol@veteranbastards.com
|
|
// Updated by: Victor the Cleaner
|
|
// Date: August 2021
|
|
//
|
|
// - Players may not build objects outside the plot boundary.
|
|
// - Distance from plot pole is calculated using the model center, not its ATL/ASL value.
|
|
// - Players may move an object any distance or height within the plot boundary.
|
|
//
|
|
// - Players may move a small distance outside the plot boundary, provided the object remains inside. Enabled with DZE_PlotOzone. Default: 10 meters.
|
|
// - A line of helpers now appears through the plot's vertical axis to aid line-of-sight to the pole. Enabled with DZE_AxialHelper.
|
|
// This may be useful for nearby plots that are grouped together, or where their radii overlap.
|
|
//
|
|
// - Players may now cancel the build by fast movement, i.e. running, or fast walking on steep terrain.
|
|
// This only applies when the player is holding the object. They may press F to release it, then run without cancelling.
|
|
// Crouch-walking or slow-walking will not cancel the build.
|
|
// Pressing ESC will also cancel without opening the exit menu.
|
|
//
|
|
// - A collision check is performed only if the held object is collidable, i.e. it has no ghost preview.
|
|
// Now the player will let go of the object, preventing them from being knocked across the map.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (dayz_actionInProgress) exitWith {localize "str_epoch_player_40" call dayz_rollingMessages;}; // Building already in progress.
|
|
dayz_actionInProgress = true;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Miscellaneous Checks
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _misc_checks = {
|
|
|
|
local _inCombat = (player getVariable["combattimeout",0] >= diag_tickTime);
|
|
local _inVehicle = ((vehicle player) != player);
|
|
local _onLadder = (getNumber (configFile >> "CfgMovesMaleSdr" >> "States" >> (animationState player) >> "onLadder")) == 1;
|
|
local _reason = "";
|
|
|
|
call {
|
|
if (_inCombat) exitWith {_reason = "str_epoch_player_43"}; // You cannot build while in combat.
|
|
if (_inVehicle) exitWith {_reason = "str_epoch_player_42"}; // You cannot build while in a vehicle.
|
|
if (_onLadder) exitWith {_reason = "str_player_21"}; // You cannot do this while you are on a ladder.
|
|
if (dayz_isSwimming) exitWith {_reason = "str_player_26"}; // You cannot do this while you are in the water.
|
|
};
|
|
|
|
_reason;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _reason = call _misc_checks;
|
|
|
|
if (_reason != "") exitWith {
|
|
dayz_actionInProgress = false;
|
|
localize _reason call dayz_rollingMessages;
|
|
};
|
|
|
|
call gear_ui_init;
|
|
closeDialog 1;
|
|
|
|
DZE_buildItem = _this; // CfgMagazines class, e.g. "full_cinder_wall_kit"
|
|
local _playerPos = [player] call FNC_GetPos;
|
|
local _buildCheck = [_playerPos, DZE_buildItem, true] call dze_buildChecks; // input: [player position, class, toolcheck] // return value: [_canBuild, _isPole, _nearestPole];
|
|
local _canBuild = _buildCheck select 0; // bool
|
|
|
|
if (_canBuild) then {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialise
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _wasStanding = ["perc", animationState player] call fnc_inString;
|
|
|
|
player playActionNow "PlayerCrouch"; // prevent instant cancel from moving too fast
|
|
|
|
local _isPole = _buildCheck select 1; // bool
|
|
local _nearestPole = _buildCheck select 2; // object or objNull
|
|
local _findNearestPole = [];
|
|
local _inRadius = (!_isPole && !(isNull _nearestPole)); // is the player attempting to place an object within a plot boundary?
|
|
local _radius = DZE_PlotPole select 0; // max distance from plot pole an object may be built
|
|
local _minDistance = DZE_PlotPole select 1; // minimum distance between plot poles
|
|
local _diameter = _radius * 2; // plot diameter
|
|
local _ozone = _radius + DZE_PlotOzone; // zone outside plot radius where a player may stand before placing an object, but the object must remain within the plot radius
|
|
local _isBHL = DZE_BuildHeightLimit > 0; // is build height limit enabled
|
|
local _BHL = DZE_BuildHeightLimit; // short name
|
|
local _sphere = "Sign_sphere100cm_EP1"; // axial helper class name
|
|
local _polePos = [];
|
|
local _pArray = []; // used to store axial helper objects
|
|
local _isAdmin = dayz_playerUID in DZE_PlotManagementAdmins;
|
|
|
|
local _dir = 0; // object direction
|
|
local _vector = []; // object vector
|
|
local _object = objNull;
|
|
local _objectHelper = objNull;
|
|
local _objectHelperPos = [];
|
|
local _noColor = [0,"#(argb,8,8,3)color(0,0,0,0,ca)"]; // transparent helper color
|
|
|
|
local _boundingCenter = []; // model center offset
|
|
local _modelOffset = 0; // model center Z offset
|
|
local _modelBase = objNull; // Attached to logical base of model
|
|
local _modelBasePos = [];
|
|
local _modelCenter = objNull; // Attached to logical center of model
|
|
local _modelCenterPos = [];
|
|
local _modelCenterPrevPos = [];
|
|
useModelCenter = 0; // use visual center of model instead of base when determining transforms and ground collision
|
|
modelSelect = objNull;
|
|
|
|
local _tooLow = false; // warn player object cannot go below ground
|
|
local _isOnWater = false; // object placed in or above water
|
|
local _heightASL = 0; // test if object is below max sea level
|
|
local _minHeight = 0; // used for nounderground
|
|
local _startPos = [];
|
|
local _playerASL = 0;
|
|
local _startHeight = 0; // raise object up on creation if necessary
|
|
local _staticOffset = 0;
|
|
local _staticOffsetSet = false;
|
|
local _isStaticWeapon = false;
|
|
local _vectoringEnabled = false;
|
|
local _snappingEnabled = false;
|
|
local _snapList = []; // helper panel array of valid snapping points
|
|
local _snapTabMax = 0; // hotkey index
|
|
local _snapSelMax = 0; // snapping point index
|
|
local _points = []; // snapping point array
|
|
|
|
local _distFromPlot = "-";
|
|
if (!isNull _nearestPole) then {_distFromPlot = "0";};
|
|
|
|
local _degreeCount = count DZE_vectorDegrees; // index count of degree array
|
|
local _refreshDist = 0; // init snap auto-refresh distance
|
|
local _OFF = localize "STR_EPOCH_ACTION_SNAP_OFF";
|
|
DZR_snapRefresh = false; // notify snap functions an auto-refresh is in progress
|
|
skipUpdates = false; // skip over multiple snapActionState updates from single keypress
|
|
distanceFromPlot = 0; // realtime updates on snap building panel
|
|
DZE_snapRadius = 0;
|
|
DZE_SnapTabIdx = 0; // tab hotkey array index
|
|
DZE_SnapSelIdx = -2; // array of object snapping points
|
|
DZE_nowBuilding = false; // notify snap build so it can clean up helpers
|
|
snapGizmosNearby = [];
|
|
|
|
local _walk = "amovpercmwlk"; // animation state substrings
|
|
local _run = "amovpercmrun";
|
|
local _sprint = "meva"; // evasive manoeuvre
|
|
local _collisionCheck = false;
|
|
local _hitSfx = [[0,1,3,5],4] call fn_shuffleArray; // used in collision check
|
|
local _hitIdx = 0;
|
|
local _hitCount = 4;
|
|
local _scream = "z_scream_"; // male Sfx class
|
|
local _scrSfx = [[0,1,2,3],4] call fn_shuffleArray; // male scream index
|
|
local _scrIdx = 0;
|
|
local _scrCount = 4;
|
|
local _isWoman = getText(configFile >> "cfgVehicles" >> (typeOf player) >> "TextSingular") == "Woman";
|
|
if (_isWoman) then {
|
|
_scream = _scream + "w_"; // female Sfx class
|
|
_scrSfx = [[1,3,4],3] call fn_shuffleArray; // female scream index
|
|
_scrCount = 3;
|
|
};
|
|
|
|
local _isOk = true;
|
|
local _cancel = false;
|
|
|
|
DZE_Q = false; // PgUp
|
|
DZE_Z = false; // PgDn
|
|
DZE_Q_alt = false; // Alt-PgUp
|
|
DZE_Z_alt = false; // Alt-PgDn
|
|
DZE_Q_ctrl = false; // Ctrl-PgUp
|
|
DZE_Z_ctrl = false; // Ctrl-PgDn
|
|
DZE_5 = false; // space bar - build
|
|
DZE_4 = false; // Q Key - rotate left
|
|
DZE_6 = false; // E Key - rotate right
|
|
DZE_F = false; // F Key - hold/release
|
|
DZE_P = false; // P Key - show/hide plot pole
|
|
DZE_T = false; // T Key - terrain align
|
|
DZE_L = false; // L Key - local mode
|
|
DZE_H = false; // H Key - hide/unhide panel
|
|
DZE_LEFT = false; // Left Arrow Key - Bank Left
|
|
DZE_RIGHT = false; // Right Arrow Key - Bank Right
|
|
DZE_UP = false; // Up Arrow Key - Pitch Forward
|
|
DZE_DOWN = false; // Down Arrow Key - Pitch Back
|
|
DZE_MINUS = false; // Minus Key - Decrease Degrees
|
|
DZE_PLUS = false; // Plus Key (=+) - Increase Degrees
|
|
DZE_BACK = false; // Backspace Key - reset vectors
|
|
DZE_TAB = false; // Tab Key - Next Snap
|
|
DZE_TAB_SHIFT = false; // Shift-Tab - Prev Snap
|
|
DZE_cancelBuilding = false; // ESC Key
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Axial Helper
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _axial_helper = { // Create vertical helpers at the plot center to which the object will be assigned
|
|
|
|
if !(DZE_AxialHelper && _inRadius) exitWith {};
|
|
|
|
local _density = 4; // minimum distance between helpers
|
|
local _segments = floor (_diameter / _density); // total helpers = _segments + 1
|
|
local _segments = _segments - (_segments % 2); // get even number of segments
|
|
local _spacing = -(_diameter / _segments); // actual distance between helpers
|
|
local _zenith = _radius;
|
|
local _color = [DZE_plotGreen, DZE_plotGreen];
|
|
if (_isBHL && DZE_HeightLimitColor) then {
|
|
_color set [1, DZE_plotRed]; // red helpers above building height limit
|
|
};
|
|
|
|
if (!isNil "PP_Marks") then {
|
|
_zenith = _zenith + _spacing; // subtract upper and lower positions
|
|
};
|
|
|
|
for "_i" from _zenith to -_zenith step _spacing do { // decrement from zenith to nadir
|
|
local _helper = _sphere createVehicleLocal [0,0,0]; // create helper
|
|
_helper setObjectTexture _noColor;
|
|
|
|
_helper attachTo [_nearestPole, [0,0,_i]]; // pre-position
|
|
uiSleep 0.001;
|
|
local _height = ([_helper] call FNC_GetPos) select 2; // test ATL/ASL
|
|
|
|
if (_height < 0) exitWith {deleteVehicle _helper}; // prevent placing below ground/sea
|
|
|
|
local _texture = _color select (_height > _BHL); // red if too high
|
|
_helper setObjectTexture _texture; // or green
|
|
|
|
_pArray = _pArray + [_helper]; // record helper
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Setup Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _setup_object = {
|
|
|
|
if (!_staticOffsetSet) then {
|
|
_object setVectorUp [0,0,1];
|
|
_staticOffset = ((getPosASL _object) select 2) - ((getPosASL _modelBase) select 2);
|
|
_staticOffsetSet = true;
|
|
};
|
|
|
|
DZE_updateVec = false; // trigger update on true
|
|
DZE_memDir = 0; // object rotation (Q/E keys)
|
|
DZE_memForBack = 0; // pitch forward/back
|
|
DZE_memLeftRight = 0; // bank left/right
|
|
|
|
_tooLow = false;
|
|
|
|
_dir = getDir player;
|
|
_objectHelper setDir _dir;
|
|
|
|
modelSelect = [_modelBase, _modelCenter] select useModelCenter; // substitute base with center if required
|
|
_visualBase = [0,0,[_modelOffset,0] select useModelCenter]; // update visual position
|
|
_object attachTo [_objectHelper, _visualBase]; // align to helper
|
|
|
|
_minHeight = _offset select 2; // min Z height allowed
|
|
_startPos = player modelToWorld _offset; // AGL = ATL/ASLW (variable height over ocean waves)
|
|
_playerASL = (getPosASL player) select 2; // player ASL height
|
|
|
|
if (surfaceIsWater _startPos) then { // if object in/over water
|
|
_startPos set [2, _playerASL + _minHeight]; // match player height + min height
|
|
};
|
|
_startHeight = _startPos select 2; // current start height
|
|
|
|
if ((_isAllowedUnderGround == 0) && (_startHeight < _minHeight)) then { // if too low
|
|
_startPos set [2, _minHeight]; // raise up
|
|
};
|
|
|
|
if (surfaceIsWater _startPos) then { // adjust for land and sea
|
|
_objectHelper setPosASL _startPos;
|
|
} else {
|
|
_objectHelper setPosATL _startPos;
|
|
};
|
|
|
|
_objectHelper attachTo [player];
|
|
helperDetach = false;
|
|
|
|
uiSleep 0.05;
|
|
|
|
_modelCenterPrevPos = getPosASL _modelCenter; // used in snap auto-refresh check
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Player is Stopped
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_isStopped = {
|
|
local _stopped = ["mstp", animationState player] call fnc_inString;
|
|
|
|
_stopped;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Move Check
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_moveCheck = {
|
|
// if the object is attached and the player is still moving in some way
|
|
local _isMoving = (!helperDetach && (speed player != 0 || {!(call _isStopped)}));
|
|
|
|
_isMoving;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Change Height
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _change_height = {
|
|
if (call _moveCheck) exitWith {};
|
|
|
|
local _distance = _this; // height vector
|
|
local _level = ""; // ground/sea level message
|
|
local _notify = false; // notify player that object is too low
|
|
|
|
local _zHeightOld = 0;
|
|
local _zHeightNew = 0;
|
|
local _terrainOld = true;
|
|
local _terrainNew = true;
|
|
|
|
local _helperPos = getPosASL _objectHelper; // helper ASL
|
|
local _helperPosZ = _helperPos select 2; // helper ASL height
|
|
local _modelOldASL = getPosASL modelSelect; // old pos
|
|
local _modelOldASLZ = _modelOldASL select 2; // old pos height
|
|
|
|
local _vector1 = []; // safe vector
|
|
local _vector2 = []; // terrain intersect
|
|
|
|
if (DZE_LOCAL_MODE) then {
|
|
_vector1 = vectorUp _object; // local vectorUp
|
|
} else {
|
|
_vector1 = [0,0,1]; // world vectorUp
|
|
};
|
|
{_vector1 set [_forEachIndex, _x * _distance];} forEach _vector1; // vector distance
|
|
|
|
local _modelNewASL = [_modelOldASL, _vector1] call BIS_fnc_vectorAdd; // new pos
|
|
local _modelNewASLZ = _modelNewASL select 2; // new pos height
|
|
local _modelNewATL = ASLToATL _modelNewASL; // new ATL
|
|
local _modelNewATLZ = _modelNewATL select 2; // new ATL height
|
|
|
|
if (surfaceIsWater _modelNewASL) then {
|
|
_terrainNew = false;
|
|
_zHeightNew = _modelNewASLZ; // sea
|
|
} else {
|
|
_zHeightNew = _modelNewATLZ; // terrain
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Object is below ground/sea
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if ((_isAllowedUnderGround == 0) && {_zHeightNew < _minHeight}) then { // if new height too low, raise up along vector
|
|
|
|
if (!_tooLow && {_distance != 0}) then {
|
|
_tooLow = true;
|
|
_notify = true; // notify once only while building
|
|
};
|
|
|
|
local _modelOldATL = ASLToATL _modelOldASL; // old ATL
|
|
local _modelOldATLZ = _modelOldATL select 2; // old ATL height
|
|
|
|
if (surfaceIsWater _modelOldASL) then {
|
|
_terrainOld = false;
|
|
_zHeightOld = _modelOldASLZ; // sea
|
|
} else {
|
|
_zHeightOld = _modelOldATLZ; // terrain
|
|
};
|
|
|
|
if (_zHeightOld < _minHeight) exitWith { // if both points are too low
|
|
_helperPos set [2, _helperPosZ + _minHeight - _zHeightOld]; // set to ground/sea level
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Simulate terrainIntersectAtASL from Arma 3
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_modelNewASL = +_modelOldASL; // start pos
|
|
_modelNewASLZ = _modelNewASL select 2; // current height
|
|
local _vector2 = +_vector1; // vector distance
|
|
|
|
local _water = !_terrainOld && !_terrainNew; // water only
|
|
local _terrain = _terrainOld && _terrainNew; // land only
|
|
local _coast = (_terrainOld || _terrainNew) && !(_terrainOld && _terrainNew);// coastline (water and land)
|
|
local _heightZ = _minHeight + 0.001; // prevent asymptote curve
|
|
|
|
local _count = 0; // loop counter
|
|
|
|
if (_terrain) then {_modelNewASLZ = (ASLToATL _modelNewASL) select 2;}; // ignore loop if already touching ground/sea level
|
|
|
|
while {_modelNewASLZ > _heightZ} do { // while object is above ground/sea level
|
|
|
|
{_vector2 set [_forEachIndex, _x * 0.5]} forEach _vector2; // get half the previous vector distance
|
|
|
|
local _newPos = [_modelNewASL, _vector2] call BIS_fnc_vectorAdd; // test start position + half previous distance
|
|
local _newPosZ = _newPos select 2; // ASL height
|
|
|
|
call {
|
|
if (_water) exitWith {}; // transform is entirely over water
|
|
|
|
local _ATLZ = (ASLToATL _newPos) select 2; // prepare for terrain check
|
|
|
|
if (_terrain) exitWith { // transform is entirely over land
|
|
_newPosZ = _ATLZ; // ATL
|
|
};
|
|
if (_coast) exitWith { // transform crosses threshold between land and water
|
|
if !(surfaceIsWater _newPos) then {
|
|
_newPosZ = _ATLZ; // ATL
|
|
};
|
|
};
|
|
};
|
|
if (_newPosZ > _minHeight) then { // test new height
|
|
_modelNewASL = +_newPos; // move closer
|
|
_modelNewASLZ = _newPosZ; // update height
|
|
_helperPos = [_helperPos, _vector2] call BIS_fnc_vectorAdd; // vector aggregate
|
|
};
|
|
_count = _count + 1;
|
|
if (_count > 15) exitWith {}; // prevent endless looping (usually resolves within 10 loops)
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
} else {
|
|
_helperPos = [_helperPos, _vector1] call BIS_fnc_vectorAdd; // safe vector
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!helperDetach) then {detach _objectHelper;};
|
|
|
|
_objectHelper setPosASL _helperPos;
|
|
uiSleep 0.04;
|
|
|
|
if (!helperDetach) then {_objectHelper attachTo [player];}; // re-attach helper
|
|
|
|
_modelNewASL = getPosASL modelSelect;
|
|
_modelNewASLZ = _modelNewASL select 2; // get current ASL height
|
|
|
|
if (surfaceIsWater _modelNewASL) then {
|
|
_level = localize "STR_EPOCH_PLAYER_NO_UGROUND_03"; // object cannot be placed below sea level
|
|
} else {
|
|
_level = localize "STR_EPOCH_PLAYER_NO_UGROUND_02"; // object cannot be placed below ground
|
|
};
|
|
|
|
local _floodArea = (_distance == 0 && {_modelNewASLZ < DZE_maxSeaLevel}); // No need to check surfaceIsWater. The surface area will change significantly near coastlines.
|
|
|
|
if (DZE_buildOnWater && _floodArea) then {
|
|
localize "STR_EPOCH_BEWARE_RISING_TIDE" call dayz_rollingMessages; // Warn only when player tries to place the object so this message does not coincide with the next one.
|
|
};
|
|
|
|
if (_notify) then {
|
|
format[localize "STR_EPOCH_PLAYER_NO_UGROUND_01", _text, _level] call dayz_rollingMessages;
|
|
};
|
|
|
|
[true] call _update; // update with collision check
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Degree Change
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _degrees = {
|
|
local _index = DZE_vectorDegrees find DZE_curDegree; // get current degree index
|
|
_index = (_index + _this + _degreeCount) % _degreeCount; // get adjacent value
|
|
DZE_curDegree = DZE_vectorDegrees select _index; // update degrees
|
|
|
|
if (_vectoringEnabled) then {
|
|
degreeActionState = localize "STR_EPOCH_VECTORS_CLOSE";
|
|
[1,1] call fnc_degreeActionCleanup; // keep action menu in sync
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Rotate Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _rotate = {
|
|
if (call _moveCheck) exitWith {};
|
|
|
|
DZE_memDir = DZE_memDir + _this;
|
|
[true] call _update;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Bank Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _bank = {
|
|
if (!_vectoringEnabled || (call _moveCheck)) exitWith {};
|
|
|
|
DZE_memLeftRight = DZE_memLeftRight + _this;
|
|
DZE_updateVec = true;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Pitch Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _pitch = {
|
|
if (!_vectoringEnabled || (call _moveCheck)) exitWith {};
|
|
|
|
DZE_memForBack = DZE_memForBack + _this;
|
|
DZE_updateVec = true;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Vector Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _vector = {
|
|
vectorActionState = localize "STR_EPOCH_VECTORS_CLOSE";
|
|
[1,1] call fnc_vectorActionCleanup;
|
|
|
|
[true] call _update;
|
|
DZE_updateVec = false;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Hold / Release Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _hold_release = {
|
|
if (!r_drag_sqf && !r_player_unconscious) then {
|
|
|
|
if (helperDetach) then {
|
|
|
|
call _attach;
|
|
} else {
|
|
call _detach;
|
|
};
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Attach Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _attach = {
|
|
detach _objectHelper;
|
|
_objectHelper attachTo [player];
|
|
DZE_memDir = DZE_memDir - (getDir player);
|
|
|
|
[false] call _update; // update, without collision check
|
|
|
|
helperDetach = false;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Detach Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _detach = {
|
|
detach _objectHelper; // release object
|
|
|
|
DZE_memDir = getDir _objectHelper; // get current Z rotation
|
|
|
|
if (DZE_memLeftRight == 0) then { // if object is not banked (left/right)
|
|
|
|
local _absMem = abs DZE_memForBack;
|
|
|
|
if ((_absMem >= 90) && (_absMem < 270)) then { // but is pitched upside down (forward/back)
|
|
DZE_memDir = DZE_memDir + 180; // prevent flipping around X axis
|
|
};
|
|
} else { // if object is banked (left/right)
|
|
if (DZE_memForBack != 0) then { // and object is pitched (forward/back)
|
|
|
|
local _dX = (sin DZE_memForBack) * (sin DZE_memLeftRight); // Pre-calculate Z rotation
|
|
local _dY = cos DZE_memForBack;
|
|
local _delta = _dX atan2 _dY; // convert to degrees on world axis
|
|
|
|
DZE_memDir = DZE_memDir - _delta; // prevent Z rotation bug on sloping terrain
|
|
};
|
|
};
|
|
if (!_collisionCheck) then {
|
|
[false] call _update; // update rotations, without collision check
|
|
};
|
|
|
|
_objectHelper setVelocity [0,0,0];
|
|
|
|
helperDetach = true;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update Pitch/Bank/Yaw
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _update = {
|
|
DZE_memForBack = DZE_memForBack % 360; // clamp rotation angles
|
|
DZE_memLeftRight = DZE_memLeftRight % 360;
|
|
DZE_memDir = DZE_memDir % 360;
|
|
|
|
[_objectHelper, [DZE_memForBack, DZE_memLeftRight, DZE_memDir]] call fnc_SetPitchBankYaw;
|
|
|
|
if ((_this select 0) && !helperDetach) then {
|
|
call _collision_check;
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Collision Check
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Small movements can occur from turning on the spot, or the beginning phase of a
|
|
// new animation state. Therefore the _tolerance variable is the distance in meters
|
|
// the player can move before a collision check is considered valid.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _collision_check = {
|
|
|
|
local _prevPos = _playerPos; // previous position
|
|
uiSleep 0.05; // allow time for player to be knocked back
|
|
_playerPos = getPosASL player; // current position
|
|
|
|
local _tolerance = 0.01; // movement threshold
|
|
local _distance = _prevPos distance _playerPos; // distance moved
|
|
local _hasMoved = _distance > _tolerance; // collision check valid
|
|
|
|
if (_hasMoved && (call _isStopped)) then { // if player is hit
|
|
_collisionCheck = true; // disable updates
|
|
call _detach; // let go of object
|
|
_collisionCheck = false; // enable updates
|
|
|
|
_objectHelper setPosASL _objectHelperPos; // reposition object
|
|
|
|
player say ["z_hit_" + str(_hitSfx select _hitIdx), 10]; // hit sound (local only)
|
|
uiSleep 0.2; // wait
|
|
player say [_scream + str(_scrSfx select _scrIdx), 70]; // scream sound (local only)
|
|
|
|
[player, 70, true, _playerPos] spawn player_alertZombies; // check if zombies hear the scream
|
|
_hitIdx = (_hitIdx + 1) % _hitCount; // cue next hit
|
|
_scrIdx = (_scrIdx + 1) % _scrCount; // cue next scream
|
|
|
|
format[localize "STR_EPOCH_PLAYER_COLLISION_01", _text] call dayz_rollingMessages;
|
|
};
|
|
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Plot boundary
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _plot_pole = {
|
|
if (_inRadius) then {
|
|
[_nearestPole] call PlotPreview;
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Terrain Align
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _terrain = {
|
|
if (call _moveCheck) exitWith {};
|
|
|
|
skipUpdates = true; // prevent temporary values updating on the snap panel
|
|
|
|
detach _objectHelper;
|
|
detach _object;
|
|
|
|
local _pos = getPosATL modelSelect;
|
|
_objectHelper setPosATL _pos; // the helper must align with the model base or center
|
|
_object attachTo [_objectHelper];
|
|
|
|
_pos = [_objectHelper] call FNC_GetPos; // ATL/ASL
|
|
local _vector = surfaceNormal _pos; // get terrain vector
|
|
_pos set [2, _minHeight]; // set default height to touch the ground/water
|
|
|
|
if (surfaceIsWater _pos) then { // place on ground or at sea level
|
|
_objectHelper setPosASL _pos;
|
|
_objectHelperPos = _pos;
|
|
} else {
|
|
_objectHelper setPosATL _pos;
|
|
_objectHelperPos = ATLToASL _pos;
|
|
};
|
|
|
|
if (_vectoringEnabled || _isStaticWeapon) then {
|
|
_objectHelper setVectorUp _vector; // align
|
|
};
|
|
|
|
local _pb = _objectHelper call BIS_fnc_getPitchBank; // not fully accurate according to the wiki, but for this purpose it will do
|
|
DZE_memForBack = _pb select 0; // pitch
|
|
DZE_memLeftRight= _pb select 1; // bank
|
|
DZE_memDir = getDir _objectHelper; // rotation
|
|
|
|
if (DZE_memForBack != 0) then {
|
|
|
|
local _dX = (sin DZE_memForBack) * (sin DZE_memLeftRight); // Pre-calculate Z rotation
|
|
local _dY = cos DZE_memForBack;
|
|
local _delta = _dX atan2 _dY; // convert to degrees on world axis
|
|
|
|
DZE_memDir = DZE_memDir - _delta; // prevent Z rotation bug on sloping terrain
|
|
};
|
|
|
|
if (!helperDetach) then {
|
|
call _attach; // reattach if object was attached prior
|
|
};
|
|
[true] call _update; // update with collision check
|
|
|
|
call _resetMenu; // snap and vector settings must yield to the terrain vector
|
|
|
|
skipUpdates = false;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Snap Next / Prev
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _snap = {
|
|
if (_snappingEnabled) then {
|
|
|
|
DZE_SnapTabIdx = DZE_SnapTabIdx + _this;
|
|
DZE_SnapSelIdx = DZE_SnapSelIdx + _this;
|
|
|
|
call {
|
|
if (DZE_SnapTabIdx < 0) exitWith { // selection was Shift-Tabbed left and looped around
|
|
DZE_SnapTabIdx = _snapTabMax;
|
|
DZE_SnapSelIdx = _snapSelMax;
|
|
};
|
|
if (DZE_SnapTabIdx > _snapTabMax) exitWith { // selection was Tabbed right and looped around
|
|
DZE_SnapTabIdx = 0;
|
|
DZE_SnapSelIdx = -2;
|
|
};
|
|
};
|
|
|
|
local _snapParams = [];
|
|
|
|
local _ON = [localize "STR_EPOCH_ACTION_SNAP_ON", _object, _classname, _objectHelper]; // Click ON to Turn OFF
|
|
local _OFF = [localize "STR_EPOCH_ACTION_SNAP_OFF", _object, _classname, _objectHelper]; // From OFF to ON/Auto
|
|
local _MANUAL = [localize "STR_EPOCH_ACTION_SNAP_POINT_MANUAL", _object, _classname, _objectHelper]; // From Manual to ON/Auto
|
|
local _AUTO = ["Auto", _object, _classname, _objectHelper]; // From Auto to Manual (nothing selected)
|
|
local _SELECT = ["Selected", _object, _classname, _objectHelper, DZE_SnapSelIdx]; // Select snapping point
|
|
|
|
//
|
|
// keep action menu in sync with hotkeys
|
|
//
|
|
call {
|
|
if (DZE_SnapTabIdx == 0) exitWith {
|
|
_snapParams = [_ON]; // ON to OFF
|
|
};
|
|
if (DZE_SnapTabIdx == 1) exitWith {
|
|
if (_this == 1) then {
|
|
_snapParams = [_OFF]; // From OFF to ON/Auto
|
|
} else {
|
|
_snapParams = [_MANUAL]; // Shift-Tab left from Manual to ON/Auto
|
|
};
|
|
};
|
|
if (DZE_SnapTabIdx > 1) exitWith {
|
|
if (_this == -1) then {
|
|
if (DZE_SnapTabIdx == _snapTabMax) then {
|
|
_snapParams = [_OFF, _AUTO]; // Shift-Tab left and loop back to select the last snapping point
|
|
};
|
|
} else {
|
|
if (DZE_SnapSelIdx == 0) then {
|
|
_snapParams = [_AUTO]; // Tab right from Auto to first snapping point
|
|
};
|
|
};
|
|
_snapParams = _snapParams + [_SELECT]; // Tab/Shift-Tab through snapping points
|
|
};
|
|
};
|
|
|
|
skipUpdates = true; // prevent display of multiple snapping states on helper panel until complete
|
|
|
|
{
|
|
["", "", "", _x] spawn snap_build;
|
|
uiSleep 0.04;
|
|
} count _snapParams;
|
|
|
|
skipUpdates = false; // re-enable snapping state display
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Snap Auto-Refresh
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _refresh = {
|
|
if !(_snappingEnabled && DZE_snapAutoRefresh && {snapActionState != _OFF}) exitWith {};
|
|
|
|
_modelCenterPos = getPosASL _modelCenter;
|
|
local _objectMove = _modelCenterPrevPos distance _modelCenterPos;
|
|
|
|
if (_objectMove > _refreshDist) then {
|
|
_modelCenterPrevPos = _modelCenterPos;
|
|
|
|
DZR_snapRefresh = true; // suspend fnc_snapDistanceCheck
|
|
|
|
[_object] call fnc_initSnapPointsNearby; // create new snap point radius
|
|
|
|
DZR_snapRefresh = false; // resume fnc_snapDistanceCheck
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset functions
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _resetMenu = {
|
|
if (_snappingEnabled) then {
|
|
snapActionState = localize "STR_EPOCH_ACTION_SNAP_OFF"; // close Snap menu
|
|
[1,0,0, _object, _classname, _objectHelper, _points] call fnc_snapActionCleanup;
|
|
call fnc_initSnapPointsCleanup;
|
|
};
|
|
if (_vectoringEnabled) then {
|
|
vectorActionState = localize "STR_EPOCH_VECTORS_OPEN"; // close Vectors menu
|
|
[1,0] call fnc_vectorActionCleanup;
|
|
|
|
degreeActionState = localize "STR_EPOCH_VECTORS_OPEN"; // close Degrees menu
|
|
[1,0] call fnc_degreeActionCleanup;
|
|
};
|
|
};
|
|
|
|
local _reset = {
|
|
detach _objectHelper;
|
|
detach _object;
|
|
|
|
call _setup_object; // reset object
|
|
call _resetMenu;
|
|
DZE_LOCAL_MODE = false;
|
|
DZE_HIDE_PANEL = false;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cancel Build
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_cancel_build = {
|
|
|
|
DZE_cancelBuilding = true; // snapping point cleanup
|
|
|
|
_isOk = false;
|
|
_cancel = true;
|
|
|
|
deleteVehicle _modelBase;
|
|
deleteVehicle _modelCenter;
|
|
deleteVehicle _object;
|
|
deleteVehicle _objectHelper;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get config data
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _classname = getText (configFile >> "CfgMagazines" >> DZE_buildItem >> "ItemActions" >> "Build" >> "create"); // e.g. "CinderWall_DZ"
|
|
local _classnameBuild = _classname;
|
|
local _text = getText (configFile >> "CfgVehicles" >> _classname >> "displayName"); // e.g. "Cinder Wall Full"
|
|
local _ghost = getText (configFile >> "CfgVehicles" >> _classname >> "ghostpreview"); // e.g. "CinderWall_Preview_DZ"
|
|
local _lockable = getNumber (configFile >> "CfgVehicles" >> _classname >> "lockable"); // defaults to 0
|
|
|
|
local _offset = getArray (configFile >> "CfgVehicles" >> _classname >> "offset");
|
|
if (count _offset == 0) then {
|
|
_offset = [0, abs (((boundingBox _object) select 0) select 1), 0];
|
|
};
|
|
|
|
local _isAllowedUnderGround = 1;
|
|
|
|
if (isNumber (configFile >> "CfgVehicles" >> _classname >> "nounderground")) then {
|
|
_isAllowedUnderGround = getNumber(configFile >> "CfgVehicles" >> _classname >> "nounderground");
|
|
};
|
|
|
|
local _requireplot = 1;
|
|
|
|
if (isNumber (configFile >> "CfgVehicles" >> _classname >> "requireplot")) then {
|
|
_requireplot = getNumber(configFile >> "CfgVehicles" >> _classname >> "requireplot");
|
|
};
|
|
if (_classname in DZE_requirePlotOverride) then {_requireplot = 1;};
|
|
|
|
useModelCenter = 0; // global
|
|
|
|
if (isNumber (configFile >> "CfgVehicles" >> _classname >> "useModelCenter")) then {
|
|
useModelCenter = getNumber(configFile >> "CfgVehicles" >> _classname >> "useModelCenter");
|
|
};
|
|
|
|
if (_ghost != "") then {
|
|
_classname = _ghost;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create ghost preview and object helpers
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Some models have a non-zero boundingCenter, i.e. values other than [0,0,0], so this
|
|
// needs to be offset from their ATL/ASL position. They use only the Z value returned
|
|
// from "boundingCenter _object" for this purpose. Furthermore, these objects require
|
|
// a second offset position value saved to the database to correctly handle vectoring
|
|
// and rotation data.
|
|
//
|
|
// To improve accuracy, we can attach a dummy object to the base of the model, and to
|
|
// the model center, and use these position values when performing calculations.
|
|
//
|
|
// This setup works for any model type.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_object = _classname createVehicle [0,0,0];
|
|
|
|
_boundingCenter = boundingCenter _object; // model center offset
|
|
_modelOffset = _boundingCenter select 2; // Z offset
|
|
|
|
_modelBase = "Sign_sphere10cm_EP1" createVehicleLocal [0,0,0];
|
|
_modelBase setObjectTexture _noColor;
|
|
_modelBase attachTo [_object, [0,0,-_modelOffset]]; // getPosATL/ASL position
|
|
|
|
_modelCenter = "Sign_sphere25cm_EP1" createVehicleLocal [0,0,0];
|
|
_modelCenter setObjectTexture _noColor;
|
|
_modelCenter attachTo [_object, [0,0,0]]; // model center
|
|
|
|
_objectHelper = "Sign_sphere10cm_EP1" createVehicle [0,0,0]; // main helper during building
|
|
_objectHelper setObjectTexture _noColor;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Initialize Snapping and Vectoring
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (isClass (configFile >> "SnapBuilding" >> _classname)) then {
|
|
_points = getArray(configFile >> "SnapBuilding" >> _classname >> "points"); // get all snapping points
|
|
|
|
_snapList = [localize "STR_EPOCH_ACTION_SNAP_OFF", localize "STR_EPOCH_ACTION_SNAP_POINT_AUTO"]; // initialize
|
|
{_snapList = _snapList + [_x select 3];} count _points; // append
|
|
|
|
_snappingEnabled = true;
|
|
snapActionState = "";
|
|
snapActionStateSelect = "";
|
|
_snapTabMax = (count _snapList) - 1;
|
|
_snapSelMax = (count _points) - 1;
|
|
|
|
["", "", "", ["Init", _object, _classname, _objectHelper]] spawn snap_build;
|
|
|
|
local _box = boundingBox _object;
|
|
local _b0 = _box select 0; // lower diagonal
|
|
local _b1 = _box select 1; // upper diagonal
|
|
local _bx = abs (_b0 select 0) + abs (_b1 select 0);
|
|
local _by = abs (_b0 select 1) + abs (_b1 select 1);
|
|
local _bz = abs (_b0 select 2) + abs (_b1 select 2);
|
|
local _diag = sqrt (_bx^2 + _by^2 + _bz^2); // get diagonal of boundingBox
|
|
|
|
DZE_snapRadius = _diag * 0.5 + 9; // 9 is half the largest bounding box diagonal (rounded up) of the largest snappable objects in the game; currently the Land_WarfareBarrier10xTall_DZ and the MetalContainer2D_DZ.
|
|
_refreshDist = DZE_snapRadius * 0.5; // distance object moves before the snap auto-refresh triggers
|
|
};
|
|
|
|
_isStaticWeapon = ((_object isKindof "StaticWeapon") || {_classname in DZE_StaticWeapons});
|
|
|
|
if (!(DZE_buildItem in DZE_noRotate) && !_isStaticWeapon) then {
|
|
_vectoringEnabled = true;
|
|
["","","",["Init", "Init", 0]] spawn build_vectors;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Main Loop
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
call _axial_helper;
|
|
call _setup_object;
|
|
[_distFromPlot, _radius, _snappingEnabled, _vectoringEnabled, _isStaticWeapon, _snapList, _object] spawn dze_snap_building;
|
|
|
|
while {_isOk} do {
|
|
|
|
_playerPos = getPosASL player; // current position used in collision check
|
|
_objectHelperPos = getPosASL _objectHelper; // used to reposition object after a collision
|
|
|
|
// scan for key press
|
|
|
|
call {
|
|
// adjust height of object
|
|
if (DZE_Q) exitWith {DZE_Q = false; 0.10 call _change_height;}; // +10cm
|
|
if (DZE_Z) exitWith {DZE_Z = false; -0.10 call _change_height;}; // -10cm
|
|
if (DZE_Q_alt) exitWith {DZE_Q_alt = false; 1.00 call _change_height;}; // +1m
|
|
if (DZE_Z_alt) exitWith {DZE_Z_alt = false; -1.00 call _change_height;}; // -1m
|
|
if (DZE_Q_ctrl) exitWith {DZE_Q_ctrl = false; 0.01 call _change_height;}; // +1cm
|
|
if (DZE_Z_ctrl) exitWith {DZE_Z_ctrl = false; -0.01 call _change_height;}; // -1cm
|
|
|
|
// rotate object
|
|
if (DZE_4) exitWith {DZE_4 = false; -DZE_curDegree call _rotate;}; // Q Key / CCW
|
|
if (DZE_6) exitWith {DZE_6 = false; DZE_curDegree call _rotate;}; // E Key / CW
|
|
|
|
// vector object using arrow keys
|
|
if (DZE_LEFT) exitWith {DZE_LEFT = false; -DZE_curDegree call _bank;}; // Left Arrow Key
|
|
if (DZE_RIGHT) exitWith {DZE_RIGHT = false; DZE_curDegree call _bank;}; // Right Arrow Key
|
|
if (DZE_UP) exitWith {DZE_UP = false; -DZE_curDegree call _pitch;}; // Up Arrow Key
|
|
if (DZE_DOWN) exitWith {DZE_DOWN = false; DZE_curDegree call _pitch;}; // Down Arrow Key
|
|
|
|
// adjust degrees
|
|
if (DZE_MINUS) exitWith {DZE_MINUS = false; -1 call _degrees;}; // Minus Key
|
|
if (DZE_PLUS) exitWith {DZE_PLUS = false; 1 call _degrees;}; // Plus Key (=+)
|
|
|
|
// snapping points
|
|
if (DZE_TAB) exitWith {DZE_TAB = false; 1 call _snap;}; // Tab Key
|
|
if (DZE_TAB_SHIFT) exitWith {DZE_TAB_SHIFT = false; -1 call _snap;}; // Shift-Tab
|
|
|
|
// hold or release object
|
|
if (DZE_F) exitWith {DZE_F = false; call _hold_release;}; // F Key
|
|
|
|
// terrain align
|
|
if (DZE_T) exitWith {DZE_T = false; call _terrain;}; // T Key
|
|
|
|
// show plot boundary
|
|
if (DZE_P) exitWith {DZE_P = false; call _plot_pole;}; // P Key
|
|
|
|
// local mode
|
|
if (DZE_L) exitWith {DZE_L = false; DZE_LOCAL_MODE = !DZE_LOCAL_MODE;}; // L Key
|
|
|
|
// hide panel
|
|
if (DZE_H) exitWith {DZE_H = false; DZE_HIDE_PANEL = !DZE_HIDE_PANEL;}; // H Key
|
|
|
|
// reset object
|
|
if (DZE_BACK) exitWith {DZE_BACK = false; call _reset;}; // Backspace Key
|
|
};
|
|
|
|
// vector object
|
|
if (DZE_updateVec) then {call _vector;};
|
|
|
|
// Swimming, in vehicle, on ladder, in combat
|
|
_reason = call _misc_checks;
|
|
if (_reason != "") exitWith {
|
|
call _cancel_build;
|
|
_reason = localize _reason;
|
|
};
|
|
|
|
// auto-refresh snap radius
|
|
call _refresh;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Player has plot pole
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
_modelCenterPos = [_modelCenter] call FNC_GetPos;
|
|
|
|
if (_isPole) then {
|
|
_findNearestPole = _modelCenterPos nearEntities ["Plastic_Pole_EP1_DZ", _minDistance]; // check for nearby plots within range of current pole
|
|
_findNearestPole = _findNearestPole - [_object]; // exclude current pole
|
|
};
|
|
if (count _findNearestPole > 0) exitWith { // pole is too close to another plot
|
|
call _cancel_build;
|
|
_reason = format[localize "str_epoch_player_44", _minDistance];
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Object was initially within plot radius
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (_inRadius) then {
|
|
distanceFromPlot = _nearestPole distance _object; // distance is calculated from model center, not getPosATL/ASL
|
|
|
|
if (_requireplot != 0) then {
|
|
call {
|
|
if ((_nearestPole distance player) > _ozone) exitWith {_reason = localize "STR_EPOCH_BUILD_MOVE_TOO_FAR"}; // You moved too far!
|
|
if (distanceFromPlot > _radius) exitWith {_reason = localize "STR_EPOCH_BUILD_OBJ_MOVE_TOO_FAR"}; // Object moved too far!
|
|
};
|
|
};
|
|
};
|
|
if (_reason != "") exitWith {
|
|
call _cancel_build;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Other checks
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _anim = animationState player;
|
|
local _speed = floor(abs(speed player));
|
|
local _isWalking = ([_walk, _anim] call fnc_inString);
|
|
local _isRunning = ([_run, _anim] call fnc_inString);
|
|
local _isSprinting = ([_sprint, _anim] call fnc_inString);
|
|
local _isFastWalking = (_isWalking && (_speed >= 10)); // fast walking on steep incline
|
|
local _tooFast = (!helperDetach && (_isRunning || _isSprinting || _isFastWalking)); // fast movement on level ground
|
|
local _tooHigh = (_isBHL && {(_modelCenterPos select 2) > _BHL});
|
|
|
|
call {
|
|
if (_tooFast) exitWith {_reason = localize "STR_EPOCH_BUILD_MOVE_TOO_FAST"}; // You moved too fast!
|
|
if (_tooHigh) exitWith {_reason = format[localize "STR_EPOCH_PLAYER_168", _BHL]}; // object moved above height limit
|
|
if (!canbuild) exitWith {_reason = format[localize "STR_EPOCH_PLAYER_136", localize "STR_EPOCH_TRADER"]}; // trader nearby
|
|
if (DZE_cancelBuilding) exitWith {_reason = localize "STR_EPOCH_PLAYER_46"}; // ESC Key
|
|
};
|
|
if (_reason != "") exitWith {
|
|
call _cancel_build;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Space Bar - Place Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (DZE_5 && !(call _moveCheck)) exitWith {
|
|
DZE_nowBuilding = true;
|
|
_isOk = false;
|
|
|
|
0 call _change_height; // raise up to ground/sea level if necessary
|
|
uiSleep 0.01;
|
|
|
|
_modelBasePos = [_modelBase] call FNC_GetPos;
|
|
_modelCenterPos = [_modelCenter] call FNC_GetPos;
|
|
_isOnWater = surfaceIsWater _modelBasePos;
|
|
_heightASL = (getPosASL _modelBase) select 2;
|
|
|
|
detach _object;
|
|
detach _objectHelper;
|
|
|
|
_dir = getDir _object;
|
|
_vector = [vectorDir _object, vectorUp _object];
|
|
|
|
deleteVehicle _modelBase;
|
|
deleteVehicle _modelCenter;
|
|
deleteVehicle _object;
|
|
deleteVehicle _objectHelper;
|
|
};
|
|
|
|
uiSleep 0.02;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
helperDetach = false; // set false to terminate fnc_snapDistanceCheck
|
|
|
|
// Delete Helper Array
|
|
{deleteVehicle _x;} count _pArray;
|
|
_pArray = [];
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Check that the auto-adjusted height does not violate distance requirements.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
if (_isPole) then { // check for nearby plots within range
|
|
local _findNearestPole = [];
|
|
_findNearestPole = _modelCenterPos nearEntities ["Plastic_Pole_EP1_DZ", _minDistance];
|
|
_findNearestPole = _findNearestPole - [_object];
|
|
|
|
if (count _findNearestPole > 0) then {
|
|
_cancel = true;
|
|
_reason = format[localize "str_epoch_player_44", _minDistance]; // pole is too close to another plot
|
|
};
|
|
} else {
|
|
if (_inRadius && {_requireplot != 0 && {(_nearestPole distance _modelCenterPos) > _radius}}) then {
|
|
_cancel = true;
|
|
_reason = localize "STR_EPOCH_BUILD_OBJ_MOVE_TOO_FAR"; // object has moved outside radius
|
|
};
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You cannot build on a road.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel && !DZE_BuildOnRoads && (isOnRoad _modelCenterPos)) then {
|
|
_cancel = true;
|
|
_reason = localize "STR_EPOCH_BUILD_FAIL_ROAD";
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You do not have access to build on this plot.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
|
|
_findNearestPole = _modelCenterPos nearEntities ["Plastic_Pole_EP1_DZ", _minDistance]; // check for nearby plots within range of current pole
|
|
_findNearestPole = _findNearestPole - [_object]; // exclude current pole
|
|
|
|
if (count _findNearestPole > 0) then { // is near plot
|
|
|
|
_nearestPole = _findNearestPole select 0; // get first entry
|
|
_buildcheck = [player, _nearestPole] call FNC_check_access;
|
|
_isowner = _buildcheck select 0;
|
|
_isfriendly = ((_buildcheck select 1) || (_buildcheck select 3));
|
|
if (!_isowner && !_isfriendly) then {
|
|
_cancel = true;
|
|
};
|
|
if (_cancel) then {
|
|
_reason = localize "STR_EPOCH_PLAYER_134"; // You do not have access to build on this plot.
|
|
};
|
|
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You are not allowed to build over sea water.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel && !DZE_buildOnWater && (_isOnWater || {_heightASL < DZE_maxSeaLevel})) then {
|
|
_cancel = true;
|
|
_reason = localize "STR_EPOCH_BUILD_FAIL_WATER";
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You cannot build. There are too many objects within the maintain range.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
_buildables = DZE_maintainClasses + DZE_LockableStorage + ["DZ_storage_base"];
|
|
|
|
if (_isPole && ((count (nearestObjects [_modelCenterPos, _buildables, DZE_maintainRange])) >= DZE_BuildingLimit)) then {
|
|
_cancel = true;
|
|
_reason = format[localize "str_epoch_player_41", floor DZE_maintainRange]; // You cannot build. There are too many objects within %1m.
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You cannot build within X meters of a restricted zone.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
{
|
|
local _dis = (_x select 2); // minimum distance
|
|
local _chk = _modelCenterPos distance (_x select 1); // current distance
|
|
|
|
if (_chk <= _dis) then { // object is within restricted zone
|
|
_cancel = true;
|
|
_reason = format[localize "STR_EPOCH_PLAYER_RES_ZONE", _dis, (_x select 0), floor _chk];
|
|
};
|
|
} count DZE_RestrictedBuildingZones;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You cannot build within X meters of a blacklisted building.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
{
|
|
local _dis = (_x select 2); // minimum distance
|
|
local _chk = count (nearestObjects [_modelCenterPos, [(_x select 1)], _dis]); // blacklisted buildings
|
|
|
|
if (_chk > 0) exitWith { // object is too close
|
|
_cancel = true;
|
|
_reason = format[localize "STR_EPOCH_PLAYER_RES_BUILDING", _dis, (_x select 0)];
|
|
};
|
|
} count DZE_BlacklistedBuildings;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You can't build an object within X meters of a safe zone.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
|
|
local _checkOK = false;
|
|
local _distance = DZE_SafeZoneNoBuildDistance;
|
|
{
|
|
if (typeName _x == "ARRAY") then {
|
|
if (_x select 0 == _classname) then {
|
|
_checkOK = true;
|
|
_distance = _x select 1;
|
|
};
|
|
} else {
|
|
if (_x == _classname) then {
|
|
_checkOK = true;
|
|
};
|
|
};
|
|
if (_checkOK) exitWith {};
|
|
} count DZE_SafeZoneNoBuildItems;
|
|
|
|
if (_checkOK && !_isAdmin) then {
|
|
_canBuild = !([_modelCenterPos, _distance] call DZE_SafeZonePosCheck);
|
|
};
|
|
if (!_canBuild) then {
|
|
_cancel = true;
|
|
_reason = format [localize "STR_EPOCH_PLAYER_166", _text, _distance]; // You can't build a %1 within %2 meters of a safe zone.
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// You can't build an object within X meters of a specified building.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
if ((count DZE_NoBuildNear > 0) && !_isAdmin) then {
|
|
_near = (nearestObjects [_modelCenterPos, DZE_NoBuildNear, DZE_NoBuildNearDistance]);
|
|
|
|
if ((count _near) > 0) then {
|
|
_cancel = true;
|
|
_reason = format [localize "STR_EPOCH_PLAYER_167", _text, DZE_NoBuildNearDistance, typeOf (_near select 0)]; // You can't build a %1 within %2 meters of a %3.
|
|
};
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_cancel) then {
|
|
|
|
_classname = _classnameBuild;
|
|
|
|
local _builtObject = _classname createVehicle [0,0,0];
|
|
|
|
//_builtObject setDir _dir; // setDir is incompatible with setVectorDirAndUp and should not be used together on the same object https://community.bistudio.com/wiki/setVectorDirAndUp
|
|
_builtObject setVariable["memDir", _dir, true];
|
|
_builtObject setVectorDirAndUp _vector;
|
|
|
|
local _position = _modelBasePos; // ATL/ASL
|
|
local _vectorUp = _vector select 1;
|
|
local _isWater = surfaceIsWater _position;
|
|
|
|
if (_isStaticWeapon) then { // handle static weapons
|
|
local _positionASL = _position;
|
|
|
|
if (!_isWater) then {_positionASL = ATLToASL _position;}; // must be ASL
|
|
|
|
for "_i" from 0 to 2 do {
|
|
_positionASL set [_i, (_positionASL select _i) + ((_vectorUp select _i) * _staticOffset)]; // add static weapon vectorUp offset to ASL position (world coordinates)
|
|
};
|
|
if (!_isWater) then { // convert back to
|
|
_position = ASLToATL _positionASL; // ATL
|
|
} else {
|
|
_position = _positionASL; // or ASL
|
|
};
|
|
};
|
|
|
|
if (_isWater) then {
|
|
_position = ASLToATL _position; // position must be ATL
|
|
};
|
|
_builtObject setPosATL _position;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
if (!_isStaticWeapon) then {
|
|
_position = _modelCenterPos; // ATL/ASL. Update db position in case model center is non-zero
|
|
_position set [2, (_position select 2) - _modelOffset]; // adjust world Z-height
|
|
|
|
if (surfaceIsWater _position) then {
|
|
_position = ASLToATL _position; // ensure position passed to db is ATL
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
local _limit = 3;
|
|
|
|
if (DZE_StaticConstructionCount > 0) then {
|
|
_limit = DZE_StaticConstructionCount;
|
|
} else {
|
|
if (isNumber (configFile >> "CfgVehicles" >> _classname >> "constructioncount")) then {
|
|
_limit = getNumber(configFile >> "CfgVehicles" >> _classname >> "constructioncount");
|
|
};
|
|
};
|
|
|
|
_isOk = true;
|
|
local _counter = 0;
|
|
local _proceed = false;
|
|
|
|
while {_isOk} do {
|
|
format[localize "str_epoch_player_139", _text, (_counter + 1), _limit] call dayz_rollingMessages; // Constructing %1 stage %2 of %3, move to cancel.
|
|
|
|
[player, (getPosATL player), 40, "repair"] spawn fnc_alertZombies;
|
|
|
|
local _finished = ["Medic", 1, {player getVariable["combattimeout", 0] >= diag_tickTime or DZE_cancelBuilding}] call fn_loopAction;
|
|
|
|
if (!_finished) exitWith {
|
|
_isOk = false;
|
|
_proceed = false;
|
|
};
|
|
|
|
if (_finished) then {
|
|
_counter = _counter + 1;
|
|
};
|
|
|
|
if (_counter == _limit) exitWith {
|
|
_isOk = false;
|
|
_proceed = true;
|
|
};
|
|
};
|
|
|
|
if (_proceed) then {
|
|
|
|
if (_wasStanding) then {player playActionNow "PlayerStand";}; // once the action has completed, return player to a standing pose if they were standing before the action
|
|
|
|
local _num_removed = ([player, DZE_buildItem] call BIS_fnc_invRemove); // remove item's magazine from inventory
|
|
|
|
if (_num_removed == 1) then {
|
|
|
|
local _friendsArr = [];
|
|
|
|
["Working", 0, [20,10,5,0]] call dayz_NutritionSystem;
|
|
call player_forceSave;
|
|
|
|
[format[localize "str_build_01", _text], 1] call dayz_rollingMessages;
|
|
|
|
if (_lockable > 1) then { //if item has code lock on it
|
|
|
|
local _combination = "";
|
|
local _combinationDisplay = ""; //define new display
|
|
local _combination_1_Display = "";
|
|
local _combination_1 = 0;
|
|
local _combination_2 = 0;
|
|
local _combination_3 = 0;
|
|
local _combination_4 = 0;
|
|
|
|
dayz_combination = "";
|
|
dayz_selectedVault = objNull;
|
|
|
|
call { // generate random combinations depending on item type
|
|
|
|
///////////////////////////////////////////////////
|
|
//
|
|
// 2 Lockbox
|
|
//
|
|
///////////////////////////////////////////////////
|
|
|
|
if (_lockable == 2) exitWith {
|
|
|
|
createDialog "KeyPadUI";
|
|
waitUntil {!dialog};
|
|
|
|
_combinationDisplay = dayz_combination call fnc_lockCode;
|
|
|
|
if (keypadCancel || {typeName _combinationDisplay == "SCALAR"}) then {
|
|
|
|
_combination_1 = (floor(random 3)) + 100; // 100=red / 101=green / 102=blue
|
|
_combination_2 = floor(random 10);
|
|
_combination_3 = floor(random 10);
|
|
|
|
_combination = format["%1%2%3",_combination_1,_combination_2,_combination_3];
|
|
dayz_combination = _combination;
|
|
|
|
call {
|
|
if (_combination_1 == 100) exitWith {_combination_1_Display = localize "STR_TEAM_RED"};
|
|
if (_combination_1 == 101) exitWith {_combination_1_Display = localize "STR_TEAM_GREEN"};
|
|
if (_combination_1 == 102) exitWith {_combination_1_Display = localize "STR_TEAM_BLUE"};
|
|
};
|
|
_combinationDisplay = format["%1%2%3", _combination_1_Display, _combination_2, _combination_3];
|
|
} else {
|
|
_combination = dayz_combination;
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////
|
|
//
|
|
// 3 Combo Lock
|
|
//
|
|
///////////////////////////////////////////////////
|
|
|
|
if (_lockable == 3) exitWith {
|
|
|
|
DZE_topCombo = 0;
|
|
DZE_midCombo = 0;
|
|
DZE_botCombo = 0;
|
|
DZE_Lock_Door = "";
|
|
dayz_selectedDoor = objNull;
|
|
|
|
dayz_actionInProgress = false;
|
|
createDialog "ComboLockUI";
|
|
waitUntil {!dialog};
|
|
dayz_actionInProgress = true;
|
|
|
|
if (keypadCancel || {parseNumber DZE_Lock_Door == 0}) then {
|
|
|
|
_combination_1 = floor(random 10);
|
|
_combination_2 = floor(random 10);
|
|
_combination_3 = floor(random 10);
|
|
|
|
_combination = format["%1%2%3", _combination_1, _combination_2, _combination_3];
|
|
|
|
DZE_Lock_Door = _combination;
|
|
} else {
|
|
_combination = DZE_Lock_Door;
|
|
};
|
|
if (_classname in DZE_LockedGates) then {
|
|
GateMethod = DZE_Lock_Door;
|
|
};
|
|
|
|
_combinationDisplay = _combination;
|
|
};
|
|
|
|
///////////////////////////////////////////////////
|
|
//
|
|
// 4 Safe
|
|
//
|
|
///////////////////////////////////////////////////
|
|
|
|
if (_lockable == 4) exitWith {
|
|
|
|
createDialog "SafeKeyPad";
|
|
waitUntil {!dialog};
|
|
|
|
if (keypadCancel || {(parseNumber dayz_combination) > 9999} || {count (toArray (dayz_combination)) < 4}) then {
|
|
|
|
_combination_1 = floor(random 10);
|
|
_combination_2 = floor(random 10);
|
|
_combination_3 = floor(random 10);
|
|
_combination_4 = floor(random 10);
|
|
|
|
_combination = format["%1%2%3%4", _combination_1, _combination_2, _combination_3, _combination_4];
|
|
|
|
dayz_combination = _combination;
|
|
} else {
|
|
_combination = dayz_combination;
|
|
};
|
|
_combinationDisplay = _combination;
|
|
};
|
|
};
|
|
|
|
_builtObject setVariable ["CharacterID", _combination, true]; // set combination as a character ID
|
|
|
|
// call publish precompiled function with given args and send public variable to server to save item to database
|
|
|
|
_builtObject setVariable ["ownerPUID", dayz_playerUID, true];
|
|
|
|
PVDZ_obj_Publish = [_combination, _builtObject, [_dir, _position, dayz_playerUID, _vector], [], player, dayz_authKey];
|
|
|
|
if (_lockable == 3) then {
|
|
|
|
_friendsArr = [[dayz_playerUID, toArray (name player)]];
|
|
_builtObject setVariable ["doorfriends", _friendsArr, true];
|
|
|
|
PVDZ_obj_Publish = [_combination, _builtObject, [_dir, _position, dayz_playerUID, _vector], _friendsArr, player, dayz_authKey];
|
|
};
|
|
|
|
publicVariableServer "PVDZ_obj_Publish";
|
|
|
|
[format[localize "str_epoch_player_140", _combinationDisplay, _text], 1] call dayz_rollingMessages; // display new combination
|
|
systemChat format[localize "str_epoch_player_140", _combinationDisplay, _text]; // You have setup your %2. The combination is %1
|
|
|
|
} else { // if not lockable item
|
|
local _charID = "0";
|
|
_builtObject setVariable ["CharacterID", _charID];
|
|
|
|
// fireplace
|
|
if (_builtObject isKindOf "Land_Fire_DZ") then { // if campfire, then spawn, but do not publish to database
|
|
|
|
[_builtObject, true] call dayz_inflame;
|
|
_builtObject spawn player_fireMonitor;
|
|
} else {
|
|
_builtObject setVariable ["ownerPUID", dayz_playerUID, true];
|
|
|
|
if (_isPole) then {
|
|
|
|
_friendsArr = [[dayz_playerUID, toArray (name player)]];
|
|
_builtObject setVariable ["plotfriends", _friendsArr, true];
|
|
|
|
PVDZ_obj_Publish = [_charID, _builtObject, [_dir, _position, dayz_playerUID, _vector], _friendsArr, player, dayz_authKey];
|
|
} else {
|
|
PVDZ_obj_Publish = [_charID, _builtObject, [_dir, _position, dayz_playerUID, _vector], [], player, dayz_authKey];
|
|
};
|
|
publicVariableServer "PVDZ_obj_Publish";
|
|
};
|
|
if (_isStaticWeapon) then {
|
|
[_builtObject,DZE_clearStaticAmmo,false] call fn_vehicleAddons;
|
|
};
|
|
};
|
|
if (DZE_GodModeBase && {!(_classname in DZE_GodModeBaseExclude)}) then {
|
|
_builtObject addEventHandler ["HandleDamage", {0}];
|
|
};
|
|
} else { // if magazine was not removed, cancel publish
|
|
deleteVehicle _builtObject;
|
|
localize "str_epoch_player_46" call dayz_rollingMessages; // Canceled building.
|
|
};
|
|
} else { // if player was interrupted cancel publish
|
|
deleteVehicle _builtObject;
|
|
localize "str_epoch_player_46" call dayz_rollingMessages; // Canceled building.
|
|
};
|
|
} else { // cancel build if passed _cancel arg was true or building on roads/trader city
|
|
format[localize "str_epoch_player_47", _text, _reason] call dayz_rollingMessages; // Canceled construction of %1, %2.
|
|
};
|
|
};
|
|
DZE_buildItem = nil;
|
|
dayz_actionInProgress = false;
|