mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Add findUnloadPosition integration
This commit is contained in:
parent
1a98726716
commit
17a882fe4d
@ -21,9 +21,9 @@
|
||||
}] call EFUNC(common,addEventHandler);
|
||||
|
||||
["UnloadCargo", {
|
||||
(_this select 0) params ["_item","_vehicle"];
|
||||
(_this select 0) params ["_item","_vehicle", ["_unloader", objNull]];
|
||||
|
||||
private _unloaded = [_item, _vehicle] call FUNC(unloadItem);
|
||||
private _unloaded = [_item, _vehicle, _player] call FUNC(unloadItem);
|
||||
|
||||
private _itemClass = if (_item isEqualType "") then {_item} else {typeOf _item};
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Arguments:
|
||||
* 0: loaded Object <OBJECT>
|
||||
* 1: Object <OBJECT>
|
||||
* 2: Unloader (player) <OPTIONAL><OBJECT>
|
||||
*
|
||||
* Return value:
|
||||
* Can be unloaded <BOOL>
|
||||
@ -16,7 +17,7 @@
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
params ["_item", "_vehicle"];
|
||||
params ["_item", "_vehicle", ["_unloader", objNull]];
|
||||
TRACE_2("params",_item,_vehicle);
|
||||
|
||||
private _loaded = _vehicle getVariable [QGVAR(loaded), []];
|
||||
@ -24,22 +25,6 @@ if !(_item in _loaded) exitWith {false};
|
||||
|
||||
private _itemClass = if (_item isEqualType "") then {_item} else {typeOf _item};
|
||||
|
||||
private _validVehiclestate = true;
|
||||
private _emptyPos = [];
|
||||
if (_vehicle isKindOf "Ship" ) then {
|
||||
if !(speed _vehicle <1 && {(((getPosATL _vehicle) select 2) < 2)}) then {_validVehiclestate = false};
|
||||
_emptyPos = ((getPosASL _vehicle) call EFUNC(common,ASLtoPosition) findEmptyPosition [0, 15, _itemClass]); // TODO: if spot is underwater pick another spot.
|
||||
} else {
|
||||
if (_vehicle isKindOf "Air" ) then {
|
||||
if !(speed _vehicle <1 && {isTouchingGround _vehicle}) then {_validVehiclestate = false};
|
||||
_emptyPos = (getPosASL _vehicle) call EFUNC(common,ASLtoPosition);
|
||||
_emptyPos = [(_emptyPos select 0) + random(5), (_emptyPos select 1) + random(5), _emptyPos select 2 ];
|
||||
} else {
|
||||
if !(speed _vehicle <1 && {(((getPosATL _vehicle) select 2) < 2)}) then {_validVehiclestate = false};
|
||||
_emptyPos = ((getPosASL _vehicle) call EFUNC(common,ASLtoPosition) findEmptyPosition [0, 15, _itemClass]);
|
||||
};
|
||||
};
|
||||
private _emptyPos = [_vehicle, _itemClass, _unloader] call EFUNC(common,findUnloadPosition);
|
||||
|
||||
if (!_validVehiclestate) exitWith {false};
|
||||
|
||||
(count _emptyPos != 0)
|
||||
(count _emptyPos) == 3
|
||||
|
@ -43,7 +43,7 @@ if (getNumber (configFile >> "CfgVehicles" >> _type >> QGVAR(hasCargo)) != 1) ex
|
||||
TRACE_1("Adding unload cargo action to class", _type);
|
||||
|
||||
private _condition = {
|
||||
GVAR(enable) && {locked _target < 2} && {alive _target} && {[_player, _target, []] call EFUNC(common,canInteractWith)}
|
||||
GVAR(enable) && {locked _target < 2} && {alive _target} && {[_player, _target, ["isNotSwimming"]] call EFUNC(common,canInteractWith)}
|
||||
};
|
||||
private _statement = {
|
||||
GVAR(interactionVehicle) = _target;
|
||||
|
@ -31,10 +31,10 @@ if (count _loaded <= _selected) exitWith {};
|
||||
private _item = _loaded select _selected; //This can be an object or a classname string
|
||||
|
||||
// Start progress bar
|
||||
if ([_item, GVAR(interactionVehicle)] call FUNC(canUnloadItem)) then {
|
||||
if ([_item, GVAR(interactionVehicle), ACE_player] call FUNC(canUnloadItem)) then {
|
||||
private _size = [_item] call FUNC(getSizeItem);
|
||||
|
||||
[5 * _size, [_item, GVAR(interactionVehicle)], "UnloadCargo", {}, localize LSTRING(UnloadingItem)] call EFUNC(common,progressBar);
|
||||
[5 * _size, [_item, GVAR(interactionVehicle), ACE_player], "UnloadCargo", {}, localize LSTRING(UnloadingItem)] call EFUNC(common,progressBar);
|
||||
} else {
|
||||
private _itemClass = if (_item isEqualType "") then {_item} else {typeOf _item};
|
||||
private _displayName = getText (configFile >> "CfgVehicles" >> _itemClass >> "displayName");
|
||||
|
@ -16,38 +16,31 @@
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
params ["_item", "_vehicle"];
|
||||
TRACE_2("params",_item,_vehicle);
|
||||
params ["_item", "_vehicle", ["_unloader", objNull]];
|
||||
TRACE_3("params",_item,_vehicle,_unloader);
|
||||
|
||||
if !([_item, _vehicle] call FUNC(canUnloadItem)) exitWith {
|
||||
private _itemClass = if (_item isEqualType "") then {_item} else {typeOf _item};
|
||||
|
||||
//This covers testing vehicle stability and finding a safe position
|
||||
private _emptyPosAGL = [_vehicle, _itemClass, _unloader] call EFUNC(common,findUnloadPosition);
|
||||
TRACE_1("findUnloadPosition",_emptyPosAGL);
|
||||
|
||||
if ((count _emptyPosAGL) != 3) exitWith {
|
||||
TRACE_4("Could not find unload pos",_vehicle,getPosASL _vehicle,isTouchingGround _vehicle,speed _vehicle);
|
||||
if ((!isNull _unloader) && {_unloader == ACE_player}) then {
|
||||
//display text saying there are no safe places to exit the vehicle
|
||||
["displayTextStructured", [localize ELSTRING(common,NoRoomToUnload)]] call EFUNC(common,localEvent);
|
||||
};
|
||||
false
|
||||
};
|
||||
|
||||
_itemClass = if (_item isEqualType "") then {_item} else {typeOf _item};
|
||||
private _loaded = _vehicle getVariable [QGVAR(loaded), []];
|
||||
|
||||
private _validVehiclestate = true;
|
||||
private _emptyPos = [];
|
||||
if (_vehicle isKindOf "Ship" ) then {
|
||||
if !(speed _vehicle <1 && {(((getPosATL _vehicle) select 2) < 2)}) then {_validVehiclestate = false};
|
||||
TRACE_1("SHIP Ground Check", getPosATL _vehicle );
|
||||
_emptyPos = ((getPosASL _vehicle) call EFUNC(common,ASLtoPosition) findEmptyPosition [0, 15, _itemClass]); // TODO: if spot is underwater pick another spot.
|
||||
} else {
|
||||
if (_vehicle isKindOf "Air" ) then {
|
||||
if !(speed _vehicle <1 && {isTouchingGround _vehicle}) then {_validVehiclestate = false};
|
||||
TRACE_1("Vehicle Ground Check", isTouchingGround _vehicle);
|
||||
_emptyPos = (getPosASL _vehicle) call EFUNC(common,ASLtoPosition);
|
||||
_emptyPos = [(_emptyPos select 0) + random(5), (_emptyPos select 1) + random(5), _emptyPos select 2 ];
|
||||
} else {
|
||||
if !(speed _vehicle <1 && {(((getPosATL _vehicle) select 2) < 2)}) then {_validVehiclestate = false};
|
||||
TRACE_1("Vehicle Ground Check", isTouchingGround _vehicle);
|
||||
_emptyPos = ((getPosASL _vehicle) call EFUNC(common,ASLtoPosition) findEmptyPosition [0, 13, _itemClass]);
|
||||
};
|
||||
if !(_item in _loaded) exitWith {
|
||||
ACE_LOGERROR_3("Tried to unload item [%1] not in vehicle[%2] cargo[%3]", _item, _vehicle, _loaded);
|
||||
false
|
||||
};
|
||||
|
||||
TRACE_1("getPosASL Vehicle Check", getPosASL _vehicle);
|
||||
if ((!_validVehiclestate) || {_emptyPos isEqualTo []}) exitWith {false};
|
||||
|
||||
private _loaded = _vehicle getVariable [QGVAR(loaded), []];
|
||||
_loaded deleteAt (_loaded find _item);
|
||||
_vehicle setVariable [QGVAR(loaded), _loaded, true];
|
||||
|
||||
@ -57,10 +50,11 @@ _vehicle setVariable [QGVAR(space), (_space + _itemSize), true];
|
||||
|
||||
if (_item isEqualType objNull) then {
|
||||
detach _item;
|
||||
_item setPosASL (_emptyPos call EFUNC(common,PositiontoASL));
|
||||
_item setPosASL (AGLtoASL _emptyPosAGL);
|
||||
["hideObjectGlobal", [_item, false]] call EFUNC(common,serverEvent);
|
||||
} else {
|
||||
createVehicle [_item, _emptyPos, [], 0, ""];
|
||||
private _newItem = createVehicle [_item, _emptyPosAGL, [], 0, ""];
|
||||
_newItem setPosASL (AGLtoASL _emptyPosAGL);
|
||||
};
|
||||
|
||||
true
|
||||
|
@ -47,6 +47,7 @@ PREP(execPersistentFnc);
|
||||
PREP(execRemoteFnc);
|
||||
PREP(executePersistent);
|
||||
PREP(filter);
|
||||
PREP(findUnloadPosition);
|
||||
PREP(fixCollision);
|
||||
PREP(fixFloating);
|
||||
PREP(fixLoweredRifleAnimation);
|
||||
|
102
addons/common/functions/fnc_findUnloadPosition.sqf
Normal file
102
addons/common/functions/fnc_findUnloadPosition.sqf
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Author: PabstMirror, ViperMaul
|
||||
* Find a safe place near a vehicle to unload something
|
||||
* Handles Normal Terrain, In Water or On Buildings (Pier, StaticShip)
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Source Vehicle <OBJECT>
|
||||
* 1: Cargo Classname <STRING>
|
||||
* 2: Unloader (player) <OBJECT><OPTIONAL>
|
||||
* 3: Max Distance (meters) <NUMBER><OPTIONAL>
|
||||
* 4: Check Vehicle is Stable <BOOL><OPTIONAL>
|
||||
*
|
||||
* Return Value:
|
||||
* Unload PositionAGL (Can Be [] if no valid pos found) <ARRAY>
|
||||
*
|
||||
* Example:
|
||||
* [theCar, "CAManBase", player, 10, true] call ace_common_fnc_findUnloadPosition;
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
#define DEBUG_MODE_FULL
|
||||
#include "script_component.hpp"
|
||||
|
||||
//Number of tests run
|
||||
#define MAX_TESTS 50
|
||||
|
||||
//Manual collision tests (count and radius):
|
||||
#define COL_TEST_COUNT 12
|
||||
|
||||
params ["_vehicle", "_typeOfCargo", ["_theUnloader", objNull], ["_maxDistance", 10], ["_checkVehicleIsStable", true]];
|
||||
TRACE_5("params",_vehicle,_typeOfCargo,_theUnloader,_maxDistance,_checkVehicleIsStable);
|
||||
|
||||
scopeName "main";
|
||||
|
||||
if (_checkVehicleIsStable) then {
|
||||
if (((vectorMagnitude (velocity _vehicle)) > 1.5) || {(!(_vehicle isKindOf "Ship")) && {(!isTouchingGround _vehicle) && {((getPos _vehicle) select 2) > 1.5}}}) then {
|
||||
TRACE_4("bad vehicle state",_vehicle,velocity _vehicle,isTouchingGround _vehicle,getPos _vehicle);
|
||||
[] breakOut "main";
|
||||
};
|
||||
};
|
||||
|
||||
private _radiusOfItem = 1.1;
|
||||
//`sizeOf` is unreliable, and does not work with object types that don't exist on map, so estimate size based on cargo size
|
||||
if (isNumber (configFile >> "CfgVehicles" >> _typeOfCargo >> QEGVAR(cargo,size))) then {
|
||||
_radiusOfItem = (((getNumber (configFile >> "CfgVehicles" >> _typeOfCargo >> QEGVAR(cargo,size))) ^ 0.35) max 1.1);
|
||||
};
|
||||
|
||||
if (isNull _theUnloader) then {_theUnloader = _vehicle;};
|
||||
|
||||
//Ideal unload pos is halfway between unloader and vehicle
|
||||
private _originASL = ((getPosASL _theUnloader) vectorAdd (getPosASL _vehicle)) vectorMultiply 0.5;
|
||||
_originASL set [2, (getPosASL _theUnloader) select 2];
|
||||
private _originAGL = ASLtoAGL _originASL;
|
||||
|
||||
//Do a manual search for empty pos (handles underwater, buildings or piers)
|
||||
TRACE_2("Checking for unload",_originAGL,_radiusOfItem);
|
||||
private _rangeToCheck = 0.5;
|
||||
while {_rangeToCheck < (sqrt _maxDistance)} do {
|
||||
private _roundAGL = _originAGL vectorAdd [(-1 * _rangeToCheck + (random 2 * _rangeToCheck)), (-1 * _rangeToCheck + (random 2 * _rangeToCheck)), 0];
|
||||
private _belowRoundArray = lineIntersectsSurfaces [(AGLtoASL _roundAGL) vectorAdd [0,0,0.5], (AGLtoASL _roundAGL) vectorAdd [0,0,-2]];
|
||||
TRACE_2("Testing Pos",_roundAGL,_belowRoundArray);
|
||||
if (!(_belowRoundArray isEqualTo [])) then {
|
||||
private _aboveBuilding = (_belowRoundArray select 0) select 2;
|
||||
|
||||
//Point is above something: Terrain(null) or Building
|
||||
if ((isNull _aboveBuilding) || {_aboveBuilding isKindOf "Building"}) then {
|
||||
//Get the real intersection point:
|
||||
_roundAGL = ASLtoAGL ((_belowRoundArray select 0) select 0);
|
||||
|
||||
//Do a fast check for people in the way
|
||||
private _roundPointIsValid = (_roundAGL nearEntities ["Man", _radiusOfItem]) isEqualTo [];
|
||||
if (_roundPointIsValid) then {
|
||||
for "_index" from 0 to (COL_TEST_COUNT -1) do {
|
||||
private _angle = _index * (360 / COL_TEST_COUNT);
|
||||
private _point1ASL = (AGLtoASL _roundAGL) vectorAdd [_radiusOfItem * cos _angle, _radiusOfItem * sin _angle, 0.1];
|
||||
private _point2ASL = (AGLtoASL _roundAGL) vectorAdd [-_radiusOfItem * cos _angle, -_radiusOfItem * sin _angle, (_radiusOfItem + 0.5)];
|
||||
private _testIntersections = lineIntersectsSurfaces [_point1ASL, _point2ASL];
|
||||
if (!(_testIntersections isEqualTo [])) exitWith {
|
||||
TRACE_2("test point not valid",_roundAGL,_testIntersections);
|
||||
_roundPointIsValid = false;
|
||||
};
|
||||
_point1ASL = (AGLtoASL _roundAGL) vectorAdd [_radiusOfItem * cos _angle, _radiusOfItem * sin _angle, 0.5];
|
||||
_point2ASL = (AGLtoASL _roundAGL) vectorAdd [-_radiusOfItem * cos _angle, -_radiusOfItem * sin _angle, 1];
|
||||
_testIntersections = lineIntersectsSurfaces [_point1ASL, _point2ASL];
|
||||
if (!(_testIntersections isEqualTo [])) exitWith {
|
||||
TRACE_2("test point not valid",_roundAGL,_testIntersections);
|
||||
_roundPointIsValid = false;
|
||||
};
|
||||
};
|
||||
if (_roundPointIsValid) then {
|
||||
TRACE_3("Valid point found", _rangeToCheck,_roundAGL, (_originAGL distance _roundAGL));
|
||||
//Add just a little so we don't sink through the floor:
|
||||
(_roundAGL vectorAdd [0,0,0.05]) breakOut "main";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
_rangeToCheck = _rangeToCheck + ((sqrt _maxDistance) / MAX_TESTS);
|
||||
};
|
||||
|
||||
TRACE_1("no valid spots found",_rangeToCheck);
|
||||
[] //return empty array
|
@ -8,6 +8,9 @@
|
||||
* Return Value:
|
||||
* Returns true if succesfully unloaded person <BOOL>
|
||||
*
|
||||
* Example:
|
||||
* [hurtGuy] call ace_common_fnc_unloadPerson
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
@ -20,14 +23,10 @@ private _vehicle = vehicle _unit;
|
||||
|
||||
if (_vehicle == _unit) exitWith {false};
|
||||
|
||||
if (speed _vehicle > 1 || getPos _vehicle select 2 > 2) exitWith {false};
|
||||
|
||||
private _emptyPos = (getPos _vehicle) findEmptyPosition [0, 10, typeOf _unit]; // @todo to small?
|
||||
|
||||
if (count _emptyPos == 0) exitWith {false};
|
||||
if (speed _vehicle > 1 || {((getPos _vehicle) select 2) > 2}) exitWith {false};
|
||||
|
||||
if (!isNull _vehicle) then {
|
||||
[[_unit], QUOTE(FUNC(unloadPersonLocal)), _unit, false] call FUNC(execRemoteFnc);
|
||||
["unloadPersonEvent", [_unit], [_unit, _vehicle]] call EFUNC(common,targetEvent);
|
||||
};
|
||||
|
||||
true
|
||||
|
@ -3,7 +3,9 @@
|
||||
* Unload a person from a vehicle, local
|
||||
*
|
||||
* Arguments:
|
||||
* 0: unit <OBJECT>
|
||||
* 0: unit to unload <OBJECT>
|
||||
* 1: Vehicle <OBJECT>
|
||||
* 2: Unloader (player) <OBJECT><OPTIONAL>
|
||||
*
|
||||
* Return Value:
|
||||
* Returns true if succesfully unloaded person <BOOL>
|
||||
@ -14,53 +16,22 @@
|
||||
|
||||
#define GROUP_SWITCH_ID QUOTE(FUNC(loadPerson))
|
||||
|
||||
params ["_unit", "_vehicle"];
|
||||
TRACE_2("params",_unit,_vehicle);
|
||||
params ["_unit", "_vehicle", ["_unloader", objNull]];
|
||||
TRACE_3("params",_unit,_vehicle,_unloader);
|
||||
|
||||
private _validVehiclestate = true;
|
||||
private _emptyPos = [];
|
||||
//This covers testing vehicle stability and finding a safe position
|
||||
private _emptyPos = [_vehicle, (typeOf _unit), _unloader] call EFUNC(common,findUnloadPosition);
|
||||
TRACE_1("findUnloadPosition",_emptyPos);
|
||||
|
||||
if (_vehicle isKindOf "Ship") then {
|
||||
if (speed _vehicle > 1 || {getPos _vehicle select 2 > 2}) then {
|
||||
_validVehiclestate = false;
|
||||
if (count _emptyPos != 3) exitwith {
|
||||
ACE_LOGWARNING_4("Could not find unload pos %1-ASL: %2 isTouchingGround: %3 Speed: %4",_vehicle, getPosASL _vehicle, isTouchingGround _vehicle, speed _vehicle);
|
||||
if ((!isNull _unloader) && {[_unloader] call FUNC(isPlayer)}) then {
|
||||
//display text saying there are no safe places to exit the vehicle
|
||||
["displayTextStructured", [_unloader], [localize LSTRING(NoRoomToUnload)]] call FUNC(targetEvent);
|
||||
};
|
||||
|
||||
TRACE_1("SHIP Ground Check",getPos _vehicle);
|
||||
|
||||
_emptyPos = (ASLToAGL getPosASL _vehicle) findEmptyPosition [0, 13, typeOf _unit]; // TODO: if spot is underwater pick another spot.
|
||||
} else {
|
||||
if (_vehicle isKindOf "Air") then {
|
||||
if (speed _vehicle > 1 || {!isTouchingGround _vehicle}) then {
|
||||
_validVehiclestate = false;
|
||||
};
|
||||
|
||||
TRACE_1("Vehicle Ground Check",isTouchingGround _vehicle);
|
||||
|
||||
_emptyPos = ASLToAGL getPosASL _vehicle;
|
||||
_emptyPos = _emptyPos vectorAdd [random 10 - 5, random 10 - 5, 0];
|
||||
} else {
|
||||
if (speed _vehicle > 1 || {getPos _vehicle select 2 > 2}) then {
|
||||
_validVehiclestate = false;
|
||||
};
|
||||
|
||||
TRACE_1("Vehicle Ground Check", isTouchingGround _vehicle);
|
||||
|
||||
_emptyPos = (ASLToAGL getPosASL _vehicle) findEmptyPosition [0, 13, typeOf _unit];
|
||||
};
|
||||
};
|
||||
|
||||
TRACE_1("getPosASL Vehicle Check", getPosASL _vehicle);
|
||||
|
||||
if !(_validVehiclestate) exitWith {
|
||||
ACE_LOGWARNING_4("Unable to unload patient because invalid (%1) vehicle state. Either moving or Not close enough on the ground. position: %2 isTouchingGround: %3 Speed: %4",_vehicle,getPos _vehicle,isTouchingGround _vehicle,speed _vehicle);
|
||||
false
|
||||
};
|
||||
|
||||
if (count _emptyPos == 0) exitWith {
|
||||
ACE_LOGWARNING_1("No safe empty spots to unload patient. %1",_emptyPos);
|
||||
false
|
||||
}; //consider displaying text saying there are no safe places to exit the vehicle
|
||||
|
||||
unassignVehicle _unit;
|
||||
[_unit] orderGetIn false;
|
||||
|
||||
@ -87,16 +58,16 @@ _unit action ["Eject", vehicle _unit];
|
||||
if ((_unit getVariable "ACE_isUnconscious") and (animationState _unit != _anim)) then {
|
||||
[_unit, _anim, 2, true] call FUNC(doAnimation);
|
||||
};
|
||||
}, [_unit, _anim], 0.5, 0] call FUNC(waitAndExecute);
|
||||
}, [_unit, _anim], 0.5] call FUNC(waitAndExecute);
|
||||
};
|
||||
};
|
||||
}, [_unit, _emptyPos], 0.5, 0] call FUNC(waitAndExecute);
|
||||
}, [_unit, _emptyPos], 0.5] call FUNC(waitAndExecute);
|
||||
|
||||
[_unit, false, GROUP_SWITCH_ID, side group _unit] call FUNC(switchToGroupSide);
|
||||
|
||||
private _loaded = _vehicle getVariable [QGVAR(loaded_persons),[]];
|
||||
private _loaded = _vehicle getvariable [QGVAR(loaded_persons),[]];
|
||||
_loaded deleteAt (_loaded find _unit);
|
||||
|
||||
_vehicle setVariable [QGVAR(loaded_persons), _loaded, true];
|
||||
_vehicle setvariable [QGVAR(loaded_persons), _loaded, true];
|
||||
|
||||
true
|
||||
|
@ -762,5 +762,8 @@
|
||||
<Czech>ACE3 Vozidla</Czech>
|
||||
<Spanish>ACE3 Vehículos</Spanish>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Common_NoRoomToUnload">
|
||||
<English>No Room to unload</English>
|
||||
</Key>
|
||||
</Package>
|
||||
</Project>
|
||||
|
@ -21,4 +21,4 @@ params ["_caller", "_target", ["_drag", false]];
|
||||
if (vehicle _target == _target) exitWith {};
|
||||
if (([_target] call EFUNC(common,isAwake))) exitWith {};
|
||||
|
||||
["unloadPersonEvent", _target, [_target, vehicle _target]] call EFUNC(common,targetEvent)
|
||||
["unloadPersonEvent", _target, [_target, vehicle _target, _caller]] call EFUNC(common,targetEvent);
|
||||
|
Loading…
Reference in New Issue
Block a user