Created
July 25, 2022 11:13
-
-
Save Seb105/ed34305b9f156c5d17f1695c551060fa to your computer and use it in GitHub Desktop.
Giant Death Spider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
params ["_core"]; | |
if !(isServer) exitwith {}; | |
// 1 = normal, 2 = 2x, 3 = 3x, 4 = 4x, 5 = 5x | |
spider_overall_speed = 1; | |
// per point, not per "leg" | |
spider_arm_length = 13; | |
spider_num_segments = 4; | |
spider_point_max_rot_speed = (360/90)*(spider_overall_speed/(spider_num_segments)); | |
spider_total_leg_length = spider_arm_length * spider_num_segments; | |
spider_point_max_angle = 135/90; | |
spider_segment_object = "Land_Cargo40_grey_F"; | |
spider_point_object = "Land_Cargo10_white_F"; | |
spider_target_object = "ProtectionZone_Invisible_F"; | |
spider_walk_height = 20; | |
// Lifted off ground | |
spider_leg_neutral_position = [-spider_arm_length*0.5, 2*spider_arm_length, spider_walk_height]; | |
// Placed on ground | |
spider_leg_active_position = [-spider_arm_length*0.5, 2*spider_arm_length, 0]; | |
// max fabrik iterations per frame | |
spider_max_iterations = 5; | |
// Threshhold for IK success | |
spider_minimum_distance = 0.1; | |
spider_max_rot_speed = (5/90)*spider_overall_speed; | |
spider_move_speed = 4*spider_overall_speed; | |
spider_leg_sleep = 2/spider_overall_speed; | |
spider_fnc_createLeg = { | |
params ["_base", "_target"]; | |
private _segments = []; | |
// Create segments | |
for "_i" from 1 to spider_num_segments do { | |
_segments pushBack (spider_segment_object createvehicle [0, 0, 0]); | |
}; | |
// Create points before segments | |
private _points = []; | |
{ | |
private _pointobj = createvehicle [spider_point_object, [0, 0, 0], [], 0, "NONE"]; | |
_points pushBack _pointobj | |
} forEach _segments; | |
// Create last point | |
private _lastPoint = createvehicle [spider_point_object, [0, 0, 0], [], 0, "NONE"]; | |
_points pushBack _lastPoint; | |
// Attach segments to points at correct offset | |
{ | |
_x attachto [_points#_forEachindex, [0, spider_arm_length/2, 0]]; | |
_x setDir 90; | |
} forEach _segments; | |
_base setVariable ["spider_points", _points]; | |
private _objects = [_base] + _segments + _points + [_target]; | |
_base setVariable ["spider_objects", _objects]; | |
_base setVariable ["spider_target", _target]; | |
_base setVariable ["spider_currentEndPoint", [0, 0, 0]]; | |
// PFH handler to move leg | |
_handle = [{ | |
if (isGamePaused) exitWith {}; | |
params ["_args", "_handle"]; | |
_args params ["_base"]; | |
private _points = _base getVariable "spider_points"; | |
private _target = _base getVariable "spider_target"; | |
[_base, ASLtoAGL (getPosASL _target)] call spider_fnc_moveLeg; | |
}, 0, [_base]] call CBA_fnc_addPerFrameHandler; | |
_base setVariable ["spider_handle", _handle]; | |
}; | |
spider_fnc_moveLeg = { | |
params ["_base", "_targetPos"]; | |
// origin is base position, always [0, 0, 0] in model space | |
private _origin = [0, 0, 0]; | |
private _points = _base getVariable "spider_points"; | |
// Get world position of points to model space | |
private _pointpositions = _points apply { | |
_base worldToModel (ASLtoAGL (getPosWorld _x)) | |
}; | |
private _targetPosmodelSpace = _base worldToModel _targetPos; | |
private _targetDirto = _origin vectorFromTo _targetPosmodelSpace; | |
// set target position to a point that is reachable if it is too far away, so IK can actually solve | |
if (_origin vectorDistance _targetPosmodelSpace > spider_total_leg_length) then { | |
_targetPosmodelSpace = _targetDirto vectorMultiply (spider_total_leg_length-spider_minimum_distance); | |
}; | |
// Calculate segment directions using FABRIK algorithm | |
private _desiredDirs = [_base, _pointpositions, _origin, _targetPosmodelSpace] call spider_fnc_fabrik; | |
private _currentDirs = _points apply { | |
_base vectorworldToModel (vectorDir _x) | |
}; | |
// Using calculated desired rotation angle, do foward kinematics | |
[_base, _points, _currentDirs, _desiredDirs] call spider_fnc_IKmove; | |
}; | |
spider_fnc_fabrik = { | |
params ["_base", "_pointpositions", "_basePos", "_targetPos"]; | |
// https://www.youtube.com/watch?v=UNoX65PRehA | |
private _fnc_fabrikBackward = { | |
params ["_pointpositions", "_targetPos"]; | |
private _prevDir = [0, 0, 0]; | |
// model space direction of base will always be 0 | |
{ | |
private _i = spider_num_segments - _forEachindex; | |
if (_forEachindex == 0) then { | |
_pointpositions set [_i, _targetPos]; | |
continue | |
}; | |
private _point = _pointpositions#_i; | |
private _prevPoint = _pointpositions#(_i + 1); | |
private _dir = _prevPoint vectorFromTo _point; | |
// Angle constraint | |
if (_forEachindex > 0) then { | |
_dir = [_prevDir, _dir] call fnc_angleConstraint; | |
}; | |
private _newPos = _prevPoint vectorAdd (_dir vectorMultiply spider_arm_length); | |
_pointpositions set [_i, _newPos]; | |
_prevDir = _dir; | |
} forEach _pointpositions; | |
_pointpositions | |
}; | |
private _fnc_fabrikforward ={ | |
params ["_pointpositions", "_basePos"]; | |
private _prevDir = [0, 0, 0]; | |
// model space direction of base will always be 0 | |
{ | |
private _i = _forEachindex; | |
if (_forEachindex == 0) then { | |
_pointpositions set [_i, _basePos]; | |
continue | |
}; | |
private _point = _pointpositions#_i; | |
private _prevPoint = _pointpositions#(_i - 1); | |
private _dir = _prevPoint vectorFromTo _point; | |
// Angle constraint | |
if (_forEachindex > 0) then { | |
_dir = [_prevDir, _dir] call fnc_angleConstraint; | |
}; | |
private _newPos = _prevPoint vectorAdd (_dir vectorMultiply spider_arm_length); | |
_pointpositions set [_i, _newPos]; | |
_prevDir = _dir; | |
} forEach _pointpositions; | |
_pointpositions | |
}; | |
private _fnc_getPointdirections = { | |
params ["_pointpositions"]; | |
private _pointDirs = []; | |
{ | |
if (_forEachindex == spider_num_segments) then { | |
_pointDirs pushBack [0, 0, 0]; | |
continue | |
}; | |
private _pos = _x; | |
private _nextPos = _pointpositions#(_forEachindex+1); | |
private _dir = _pos vectorFromTo _nextPos; | |
_pointDirs pushBack _dir; | |
_prevPos = _pointpositions#_forEachindex; | |
} forEach _pointpositions; | |
_pointDirs | |
}; | |
// Check if calculation necessary | |
private _movedistancetoTarget = (_base getVariable "spider_currentEndPoint") vectorDistance _targetPos; | |
if (_movedistancetoTarget > spider_minimum_distance) then { | |
private _iterations = 0; | |
while {_iterations < spider_max_iterations} do { | |
_iterations = _iterations + 1; | |
// Run 1 full fabrik iteration | |
_pointpositions = [_pointpositions, _targetPos] call _fnc_fabrikBackward; | |
_pointpositions = [_pointpositions, _basePos] call _fnc_fabrikforward; | |
// Check if we are close enough to target | |
private _error = (_pointpositions#spider_num_segments) vectorDistance _targetPos; | |
if (_error < spider_minimum_distance) then { | |
break; | |
}; | |
}; | |
// Cache end point and point positions for retrieval if needed | |
_base setVariable ["spider_currentEndPoint", _pointpositions#spider_num_segments]; | |
_base setVariable ["spider_pointpositions", _pointpositions]; | |
} else { | |
_pointpositions = _base getVariable "spider_pointpositions"; | |
}; | |
// Point positions are not used for calculating IK, the directions of the nodes are used. | |
// So we need to convert the point positions to directions. | |
private _pointDirs = [_pointpositions] call _fnc_getPointdirections; | |
_pointDirs | |
}; | |
fnc_angleConstraint = { | |
params ["_prevDir", "_targetDir"]; | |
// Get vector difference of angles and calculate the magnitude of this vector. 1=90 etc | |
private _constDiff = _targetDir vectorDiff _prevDir; | |
private _constMagnitude = vectorMagnitude _constDiff; | |
if (_constMagnitude > spider_point_max_angle) then { | |
// Clamps the magnitude of the vectorDiff to the max angle | |
_targetDir = _prevDir vectorAdd (_constDiff vectorMultiply spider_point_max_angle/_constMagnitude); | |
}; | |
_targetDir | |
}; | |
spider_fnc_IKmove = { | |
params ["_base", "_points", "_currentDirs", "_targetDirs"]; | |
// Given an array of target directions for points, and their current directions, run forward IK smoothly. | |
private _fnc_setVelTransformation = { | |
params ["_base", "_point", "_deltaT", "_offsetCurrent", "_offsetTarget", "_currentDir", "_newDir"]; | |
// Convert from model space to world space | |
private _pivot = _point worldToModel (ASLtoAGL (getPosASL _point)); | |
// Pos is set in ASL but by default pivot is worldspace | |
private _currentPos = _base modeltoWorldWorld (_offsetCurrent vectorMultiply spider_arm_length); | |
private _newPos = _base modeltoWorldWorld (_offsetTarget vectorMultiply spider_arm_length); | |
private _velocity = (_newPos vectorDiff _currentPos) vectorMultiply _deltaT; | |
private _currentDir = _base vectorModelToWorld _currentDir; | |
private _newDir = _base vectorModelToWorld _newDir; | |
// private _up = [0, 0, 1]; | |
private _up = surfaceNormal _newPos; | |
// Add upwards velocity, as clients will simulate their own gravity we don't want | |
_velocity = _velocity vectorAdd [0, 0, 9.81*_deltaT]; | |
_point setvelocityTransformation [ | |
_currentPos, | |
_newPos, | |
_velocity, | |
_velocity, | |
_currentDir, | |
_newDir, | |
_up, | |
_up, | |
1, | |
_pivot | |
] | |
}; | |
// Point positions always start at [0, 0, 0] in modelspace | |
private _offsetCurrent = [0, 0, 0]; | |
private _offsetTarget = [0, 0, 0]; | |
private _prevDir = [0, 0, 0]; | |
private _deltaT = 1/diag_fps; | |
{ | |
private _point = _x; | |
private _currentDir = _currentDirs#_forEachindex; | |
private _targetDir = _targetDirs#_forEachindex; | |
// speed constraint. Similar algorithm to the angle constraint | |
_targetDir = [_currentDir, _targetDir, _deltaT, spider_point_max_rot_speed] call spider_fnc_speedConstraint; | |
// Angle constraint | |
_targetDir = [_prevDir, _targetDir] call fnc_angleConstraint; | |
[_base, _point, _deltaT, _offsetCurrent, _offsetTarget, _currentDir, _targetDir] call _fnc_setVelTransformation; | |
// Update position of current node | |
_offsetCurrent = _offsetCurrent vectorAdd _currentDir; | |
_offsetTarget = _offsetTarget vectorAdd _targetDir; | |
_prevDir = _targetDir; | |
} forEach _points; | |
}; | |
spider_fnc_speedConstraint = { | |
// Constrains the rotational speed magnitude | |
params ["_currentDir", "_targetDir", "_deltaT", "_clampVal"]; | |
private _rotDiff = _targetDir vectorDiff _currentDir; | |
private _rotMagnitude = vectorMagnitude _rotDiff; | |
private _maxRot = _clampVal * _deltaT; | |
if (_rotMagnitude > _maxRot) then { | |
_targetDir = [_currentDir, _targetDir, _maxRot/_rotMagnitude] call BIS_fnc_slerp; | |
}; | |
_targetDir | |
}; | |
spider_fnc_legPlacedEffect = { | |
params ["_tip", "_target"]; | |
[ | |
{ | |
params ["_tip", "_target"]; | |
private _distancetoTarget = (getPosASL _tip) vectorDistance (getPosASL _target); | |
_distancetoTarget < spider_minimum_distance | |
}, | |
{ | |
params ["_tip", "_target"]; | |
private _targetPos = getPos _target; | |
_targetPos set [2, -5]; | |
private _shell = createVehicle ["R_60mm_HE", _target, [], 0, "CAN_COLLIDE"]; | |
_shell setDamage 1; | |
{ | |
_x setDamage 1; | |
} forEach (nearestTerrainObjects [_target, [], 10, false]); | |
}, | |
[_tip, _target], | |
spider_leg_sleep, | |
{} | |
] call CBA_fnc_waitUntilAndExecute | |
}; | |
spider_fnc_placeLegGroup = { | |
params [ | |
"_legGroup", | |
["_doEffect", true] | |
]; | |
{ | |
private _leg = _x; | |
private _target = _leg getVariable "spider_target"; | |
private _pos = +spider_leg_active_position; | |
_pos set [0, (_pos#0) * (_leg getVariable "spider_side")]; | |
// Flip legs on the x axis depending on side | |
detach _target; | |
private _pos = (_leg modeltoWorldWorld _pos); | |
_pos set [2, 0]; | |
_target setPos _pos; | |
_target setvectorDirAndUp [[0, 1, 0], [0, 0, 1]]; | |
if (_doEffect) then { | |
[(_leg getVariable "spider_points")#spider_num_segments, _target] call spider_fnc_legPlacedEffect; | |
}; | |
} forEach _legGroup; | |
}; | |
spider_fnc_liftLegGroup = { | |
params ["_leggroup"]; | |
{ | |
private _leg = _x; | |
private _pos = +spider_leg_neutral_position; | |
// Set leg position to neutral, Z off of ground | |
_pos = _leg modeltoWorld _pos; | |
_pos set [0, (_pos#0) * (_leg getVariable "spider_side")]; | |
_pos set [2, spider_leg_neutral_position#2]; | |
// Flip legs on the x axis depending on side | |
_pos set [0, (_pos#0) * (_leg getVariable "spider_side")]; | |
private _target = _leg getVariable "spider_target"; | |
_target setPosASL (AGLtoASL _pos); | |
} forEach _leggroup; | |
}; | |
spider_fnc_createSpider = { | |
params ["_core"]; | |
private _van = createVehicle ["C_Van_02_medevac_F", _core, [], 0, "NONE"]; | |
private _group = createGroup [civilian, true]; | |
private _man = _group createUnit ["C_man_1", _core, [], 0, "NONE"]; | |
_man moveInDriver _van; | |
_core allowDamage false; | |
_man disableAI "all"; | |
_van allowDamage false; | |
_man allowDamage false; | |
_van attachto [_core, [0, -15, 6.5]]; | |
_core setVariable ["spider_group", _group]; | |
_core setVariable ["spider_driver", _man]; | |
private _bodyLength = 110; | |
private _bodyWidth = 5; | |
private _height = -5; | |
private _handles = []; | |
private _objects = [_core, _van, _man]; | |
_core setVariable ["spider_handles", _handles]; | |
_core setVariable ["spider_objects", _objects]; | |
_core setVariable ["spider_phase", 0]; | |
_core setVariable ["spider_nextUpdate", time+5]; | |
private _legs = []; | |
for "_pair" from -1.5 to 1.5 step 1 do { | |
private _posY = _pair * _bodyLength/4; | |
for "_side" from -1 to 1 step 2 do { | |
private _posX = _side * _bodyWidth/2; | |
private _legBasePos = [_posX, _posY, _height]; | |
private _legDir = _side*90; | |
private _legBase = spider_target_object createvehicle [0, 0, 0]; | |
_legBase attachto [_core, _legBasePos]; | |
_legBase setDir _legDir; | |
private _legTarget = spider_target_object createvehicle [0, 0, 0]; | |
private _legTargetPos = _legBase modelToWorldWorld [0, 2*spider_total_leg_length, 2*spider_total_leg_length]; | |
_legTarget setPosASL _legTargetPos; | |
_legs pushBack _legBase; | |
_legBase setVariable ["spider_side", _side]; | |
[_legBase, _legTarget] call spider_fnc_createLeg; | |
private _handle = _legBase getVariable "spider_handle"; | |
_handles pushBack _handle; | |
_objects append (_legBase getVariable "spider_objects"); | |
}; | |
}; | |
private _legGroup1 = [_legs#0, _legs#3, _legs#4, _legs#7]; | |
private _legGroup2 = [_legs#1, _legs#2, _legs#5, _legs#6]; | |
_core setVariable ["spider_legGroup1", _legGroup1]; | |
_core setVariable ["spider_legGroup2", _legGroup2]; | |
[_van, _core] spawn { | |
params ["_van", "_core"]; | |
waitUntil {sleep 1; time > 5}; | |
{ | |
_x addCuratorEditableObjects [[_van, _core], true]; | |
} forEach (allCurators); | |
}; | |
// Brain and leg shit | |
private _handle = [{ | |
if (isGamePaused) exitWith {}; | |
params ["_args", "_handle"]; | |
_args params ["_core"]; | |
[_core] call spider_fnc_shouldExit; | |
private _fnc_handlePosUpdate = { | |
params ["_core"]; | |
private _group = _core getVariable "spider_group"; | |
private _start = getPosASL _core; | |
// fix retarded arma bullshit in setvelocityTransformation | |
private _pivot = _core worldToModel (ASLtoAGL _start); | |
_start set [2, (getTerrainHeightASL _start) + spider_walk_height]; | |
private _currentWaypointindex = (currentWaypoint _group); | |
private _hasWaypoint = count (waypoints _group) > 1; | |
// Has waypoint? | |
private _end = if (_hasWaypoint) then { | |
private _wpPos = waypointPosition [_group, _currentWaypointindex]; | |
AGLtoASL _wpPos | |
} else { | |
_start | |
}; | |
private _distancetoTarget = _start distance2D _end; | |
private _active = _distancetoTarget > 20; | |
if (!_active && _hasWaypoint) then { | |
deleteWaypoint [_group, (currentWaypoint _group)]; | |
}; | |
_core setVariable ["spider_active", _active]; | |
private _deltaT = 1/diag_fps; | |
private _velocityAdd = [0, 0, 9.81*_deltaT]; | |
private _currentDir = vectorDir _core; | |
private _currentUp = vectorUp _core; | |
if !(_active) exitwith { | |
_core setvelocityTransformation [ | |
_start, | |
_start, | |
_velocityAdd, | |
_velocityAdd, | |
_currentDir, | |
_currentDir, | |
_currentUp, | |
_currentUp, | |
1, | |
_pivot | |
] | |
}; | |
private _dirtoTarget = _start vectorFromTo _end; | |
private _newDir = [_currentDir, _dirtoTarget, _deltaT, spider_max_rot_speed] call spider_fnc_speedConstraint; | |
private _velocity = (_newDir vectorMultiply (spider_move_speed * _deltaT)); | |
private _velocityNetwork = _velocity vectorAdd _velocityAdd; | |
private _newPos = _start vectorAdd _velocity; | |
_newPos set [2, (getTerrainHeightASL _newPos) + spider_walk_height]; | |
// Draw a line from front to back of the spider, get the angle of the line | |
private _offset = _newDir vectorMultiply 40; | |
private _front = _newPos vectorAdd _offset; | |
private _back = _newPos vectorAdd (_offset vectorMultiply -1); | |
{ | |
_x set [2, getTerrainHeightASL _x]; | |
} forEach [_front, _back]; | |
private _actualnewDir = _back vectorFromTo _front; | |
private _targetUp = surfaceNormal _newPos; | |
private _newUp = [_currentUp, _targetUp, _deltaT, spider_max_rot_speed] call spider_fnc_speedConstraint; | |
_core setvelocityTransformation [ | |
_start, | |
_newPos, | |
_velocityNetwork, | |
_velocityNetwork, | |
_currentDir, | |
_actualnewDir, | |
_currentUp, | |
_newUp, | |
1, | |
_pivot | |
]; | |
}; | |
private _fnc_handleLegUpdate = { | |
params ["_core"]; | |
private _nextUpdate = _core getVariable "spider_nextUpdate"; | |
if (time > _nextUpdate) then { | |
_core setVariable ["spider_nextUpdate", time + spider_leg_sleep]; | |
private _phase = _core getVariable "spider_phase"; | |
private _legGroup1 = _core getVariable "spider_legGroup1"; | |
private _legGroup2 = _core getVariable "spider_legGroup2"; | |
if !(_core getVariable "spider_active") exitWith { | |
{ | |
[_x, false] call spider_fnc_placeLegGroup; | |
} forEach [_legGroup1, _legGroup2]; | |
_core setVariable ["spider_phase", 1]; | |
}; | |
switch _phase do { | |
case 0: { | |
[_legGroup2] call spider_fnc_placeLegGroup; | |
_core setVariable ["spider_phase", 1]; | |
}; | |
case 1: { | |
[_legGroup1] call spider_fnc_liftLegGroup; | |
_core setVariable ["spider_phase", 2]; | |
}; | |
case 2: { | |
[_legGroup1] call spider_fnc_placeLegGroup; | |
_core setVariable ["spider_phase", 3]; | |
}; | |
case 3: { | |
[_legGroup2] call spider_fnc_liftLegGroup; | |
_core setVariable ["spider_phase", 0]; | |
}; | |
} | |
}; | |
}; | |
[_core] call _fnc_handlePosUpdate; | |
[_core] call _fnc_handleLegUpdate; | |
}, 0, [_core]] call CBA_fnc_addPerFrameHandler; | |
_handles pushBack _handle; | |
_core addEventHandler ["Deleted", { | |
params ["_core"]; | |
[_core, true] call spider_fnc_shouldExit; | |
}] | |
}; | |
spider_fnc_shouldExit = { | |
// Tear down the spider if an object is deleted or the driver is dead | |
params ["_core", "_override"]; | |
private _objects = _core getVariable "spider_objects"; | |
private _driver = _core getVariable "spider_driver"; | |
private _objectsMissing = _objects find objNull != -1; | |
private _driverDead = !(alive _driver); | |
if (_objectsMissing || {_driverDead} || {_override}) then {; | |
private _handles = _core getVariable "spider_handles"; | |
{ | |
systemChat str _x; | |
_x call CBA_fnc_removePerFrameHandler; | |
} forEach _handles; | |
{ | |
detach _x; | |
} forEach _objects; | |
}; | |
}; | |
isNil { | |
[_core] call spider_fnc_createSpider; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment