diff --git a/Server_Install_Pack/@epochhive/epochconfig.hpp b/Server_Install_Pack/@epochhive/epochconfig.hpp index 4fa8612e..2203fa02 100644 --- a/Server_Install_Pack/@epochhive/epochconfig.hpp +++ b/Server_Install_Pack/@epochhive/epochconfig.hpp @@ -25,7 +25,8 @@ antagonistChancePDeath = 0.33; //33% chance when player was killed from a other antagonistChanceLoot = 0.09; //9% chance when player click "SEARCH" on a loot object // Player Related -cloneCost = 100; // debt incurred on player death +cloneCost = 100; // debt incurred on player death +MaxBankDebitforTrade = -50000 // If Player has less money on Bank, Crypto from Trade goes directly to Bank instead to Player // vehicles - Max vehicle slots is calculated from per vehicle limits below. Warning! Higher the number lower the performance. simulationHandlerOld = "false"; // When enabled this feature disables simulation on vehicles that are not nea players. Can help improve client fps at the cost of server fps. diff --git a/Sources/epoch_code/compile/traders/EPOCH_NpcTrade_return.sqf b/Sources/epoch_code/compile/traders/EPOCH_NpcTrade_return.sqf new file mode 100644 index 00000000..0b0a9c9a --- /dev/null +++ b/Sources/epoch_code/compile/traders/EPOCH_NpcTrade_return.sqf @@ -0,0 +1,86 @@ +/* + Author: Aaron Clark - EpochMod.com + + Contributors: He-Man + + Description: + NPC trade code + + Licence: + Arma Public License Share Alike (APL-SA) - https://www.bistudio.com/community/licenses/arma-public-license-share-alike + + Github: + https://github.com/EpochModTeam/Epoch/tree/release/Sources/epoch_code/traders/EPOCH_startNpcTrade.sqf + + Example: + cursorTarget call EPOCH_startNpcTrade; + + Parameter(s): + _this: OBJECT + + Returns: + NOTHING +*/ +//[[[cog import generate_private_arrays ]]] +private ["_addWeaponToHands","_arrayIn","_arrayOut","_config","_current_crypto","_errorMsg","_item","_itemTax","_itemWorth","_sizeOut","_tax","_type","_vehSlot","_vehicle","_vehicles"]; +//[[[end]]] +params [["_returnIn",[]],["_returnOut",[]],["_message",""]]; + +if (isNil "EPOCH_TRADE_STARTED") exitWith{}; + +if !(_returnOut isEqualTo[]) then { + // add purchased items + { + if ([_x, "CfgWeapons"] call EPOCH_fnc_isAny) then { + _type = getNumber(configfile >> "CfgWeapons" >> (_x) >> "type"); + _addWeaponToHands = false; + switch (_type) do { + case 1: { + if (primaryWeapon player == "") then { + _addWeaponToHands = true; + }; + }; + case 4: { + if (secondaryWeapon player == "") then { + _addWeaponToHands = true; + }; + }; + case 2: { + if (handgunWeapon player == "") then { + _addWeaponToHands = true; + }; + }; + }; + if (_addWeaponToHands) then { + player addWeapon _x; + } + else { + _x call EPOCH_fnc_addItemOverflow; + }; + } + else { + if ([_x, "CfgMagazines"] call EPOCH_fnc_isAny) then { + _x call EPOCH_fnc_addItemOverflow; + }; + }; + } forEach _returnOut; +}; + +if !(_message isequalto "") then { + [_message, 5] call Epoch_message; +}; + +EPOCH_TRADE_STARTED = nil; + +// Ignatz ... +if (isnil 'Ignatz_LastTrade') then { + Ignatz_LastTrade = diag_ticktime; + ['Tradings',1,'add'] call Ignatz_Client_PlayerStatsUpdate; +} +else { + if (diag_ticktime > Ignatz_LastTrade+300) then { + ['Tradings',1,'add'] call Ignatz_Client_PlayerStatsUpdate; + Ignatz_LastTrade = diag_ticktime; + }; +}; +// ... Ignatz diff --git a/Sources/epoch_code/compile/traders/EPOCH_startNpcTrade.sqf b/Sources/epoch_code/compile/traders/EPOCH_startNpcTrade.sqf index 31092954..469ed6a9 100644 --- a/Sources/epoch_code/compile/traders/EPOCH_startNpcTrade.sqf +++ b/Sources/epoch_code/compile/traders/EPOCH_startNpcTrade.sqf @@ -25,7 +25,6 @@ private ["_addWeaponToHands","_arrayIn","_arrayOut","_config","_current_crypto","_errorMsg","_item","_itemTax","_itemWorth","_sizeOut","_tax","_type","_vehSlot","_vehicle","_vehicles"]; //[[[end]]] -if (!isNil "EPOCH_TRADE_COMPLETE") exitWith {}; if (!isNil "EPOCH_TRADE_STARTED") exitWith{}; if (isNull _this) exitWith{}; @@ -119,74 +118,5 @@ if (alive _this) then { // close menu closeDialog 0; - - [_arrayIn, _arrayOut] spawn{ - - waitUntil{ uiSleep 0.1; !isNil "EPOCH_TRADE_COMPLETE" }; - - // SOLD ITEMS ARRAY - if !((EPOCH_TRADE_COMPLETE select 0) isEqualTo[]) then { - if ((EPOCH_TRADE_COMPLETE select 0) isEqualTo(_this select 0)) then { - ['Items Sold', 5] call Epoch_message; - } - else { - ['Failed To Sell Items', 5] call Epoch_message; - }; - }; - - // PURCHASED ITEMS ARRAY - if !((EPOCH_TRADE_COMPLETE select 1) isEqualTo[]) then { - if ((EPOCH_TRADE_COMPLETE select 1) isEqualTo(_this select 1)) then { - - _errorMsg = 'Items Purchased: '; - // add purchased items - { - if ([_x, "CfgWeapons"] call EPOCH_fnc_isAny) then { - _errorMsg = _errorMsg + format["%1, ", getText(configfile >> "CfgWeapons" >> (_x) >> "displayName")]; - _type = getNumber(configfile >> "CfgWeapons" >> (_x) >> "type"); - _addWeaponToHands = false; - switch (_type) do { - case 1: { - if (primaryWeapon player == "") then { - _addWeaponToHands = true; - }; - }; - case 4: { - if (secondaryWeapon player == "") then { - _addWeaponToHands = true; - }; - }; - case 2: { - if (handgunWeapon player == "") then { - _addWeaponToHands = true; - }; - }; - }; - if (_addWeaponToHands) then { - player addWeapon _x; - } - else { - _x call EPOCH_fnc_addItemOverflow; - }; - } else { - if ([_x, "CfgMagazines"] call EPOCH_fnc_isAny) then { - _errorMsg = _errorMsg + format["%1, ", getText(configfile >> "CfgMagazines" >> (_x) >> "displayName")]; - _x call EPOCH_fnc_addItemOverflow; - } else { - _errorMsg = _errorMsg + format["%1, ", getText(configfile >> "CfgVehicles" >> (_x) >> "displayName")]; - }; - }; - } forEach(_this select 1); - - [_errorMsg, 5] call Epoch_message; - } - else { - ['Failed To Purchase Items', 5] call Epoch_message; - }; - }; - - EPOCH_TRADE_COMPLETE = nil; - EPOCH_TRADE_STARTED = nil; - }; }; }; diff --git a/Sources/epoch_config/Configs/CfgClientFunctions.hpp b/Sources/epoch_config/Configs/CfgClientFunctions.hpp index b43cd670..c4405dd6 100644 --- a/Sources/epoch_config/Configs/CfgClientFunctions.hpp +++ b/Sources/epoch_config/Configs/CfgClientFunctions.hpp @@ -60,6 +60,7 @@ class CfgClientFunctions }; class traders { + class NpcTrade_return {}; class startInteract {}; class startInteractNPC {}; class npcTraderAdd {}; diff --git a/Sources/epoch_server/compile/epoch_trading/EPOCH_server_makeNPCTrade.sqf b/Sources/epoch_server/compile/epoch_trading/EPOCH_server_makeNPCTrade.sqf index 36cf0845..571f98a1 100644 --- a/Sources/epoch_server/compile/epoch_trading/EPOCH_server_makeNPCTrade.sqf +++ b/Sources/epoch_server/compile/epoch_trading/EPOCH_server_makeNPCTrade.sqf @@ -12,9 +12,11 @@ Github: https://github.com/EpochModTeam/Epoch/tree/release/Sources/epoch_server/compile/epoch_trading/EPOCH_server_makeNPCTrade.sqf */ -private ["_vehicleSold","_vehHiveKey","_VAL","_makeTradeIn","_vehSlot","_playerNetID","_vehicle","_vehicles","_tradeTotal","_current_crypto","_tradeQtyTotal","_currQty","_qtyIndex","_itemWorth","_item","_itemQty","_position","_foundSmoke","_objOwner","_tmpposition","_lockOwner","_helipad","_helipads","_smoke","_vehicleBought","_playerGroup","_vehObj","_final_location","_group","_wp","_wHPos","_wH","_nearByHolder","_itemTax","_tax","_objHiveKey","_playerCryptoLimit","_config","_cIndex","_vars","_current_cryptoRaw","_aiItems","_itemClasses","_itemQtys","_returnIn","_returnOut","_slot"]; +private ["_SkipOut","_tradeIn","_tradeOut","_message","_vehicleSold","_vehHiveKey","_VAL","_makeTradeIn","_vehSlot","_playerNetID","_vehicle","_vehicles","_tradeTotal","_current_crypto","_tradeQtyTotal","_currQty","_qtyIndex","_itemWorth","_item","_itemQty","_position","_foundSmoke","_objOwner","_tmpposition","_lockOwner","_helipad","_helipads","_smoke","_vehicleBought","_playerGroup","_vehObj","_final_location","_group","_wp","_wHPos","_wH","_nearByHolder","_itemTax","_tax","_objHiveKey","_playerCryptoLimit","_config","_cIndex","_vars","_current_cryptoRaw","_aiItems","_itemClasses","_itemQtys","_returnIn","_returnOut","_slot"]; params ["_trader","_itemsIn","_itemsOut","_player",["_token","",[""]]]; +_playerUID = getplayeruid _player; + _vehicleSold = false; _vehicleBought = false; @@ -30,7 +32,11 @@ _slot = _trader getVariable["AI_SLOT", -1]; if (_slot != -1) then { _tradeTotal = 0; + _tradeIn = 0; + _tradeOut = 0; _tradeQtyTotal = 0; + _message = ""; + _SkipOut = false; _config = 'CfgPricing' call EPOCH_returnConfig; @@ -96,185 +102,241 @@ if (_slot != -1) then { if (_qtyIndex == -1) then { _itemClasses pushBack _item; _itemQtys pushBack _itemQty; - _tradeTotal = _tradeTotal + _itemWorth; + _tradeIn = _tradeIn + _itemWorth; _current_crypto = _current_crypto + _itemWorth; _tradeQtyTotal = _tradeQtyTotal + _itemQty; } else { _currQty = _itemQtys select _qtyIndex; _itemQtys set[_qtyIndex, (_currQty + _itemQty)]; - _tradeTotal = _tradeTotal + _itemWorth; + _tradeIn = _tradeIn + _itemWorth; _current_crypto = _current_crypto + _itemWorth; _tradeQtyTotal = _tradeQtyTotal + _itemQty; }; }; }; } forEach _itemsIn; + + _response = ["Bank", _playerUID] call EPOCH_fnc_server_hiveGETRANGE; + if ((_response select 0) == 1 && (_response select 1) isEqualType []) then { + _bankData = _response select 1; + if !(_bankData isEqualTo[]) then { + _serverSettingsConfig = configFile >> "CfgEpochServer"; + _MaxBankDebit = [_serverSettingsConfig, "MaxBankDebitforTrade", -999999] call EPOCH_fnc_returnConfigEntry; + _bankBalance = _bankData select 0; + if (_bankBalance < _MaxBankDebit) then { + if (_tradeIn > 0) then { + _bankBalance = _bankBalance + _tradeIn; + _return = ["Bank", _playerUID, EPOCH_expiresBank, [_bankBalance]] call EPOCH_fnc_server_hiveSETEX; + _message = _message + "Items sold, but the Money goes to your Bank - to much Bank-Debit"; + } + else { + _message = _message + "Putchase not possible - to much Bank-Debit"; + }; + _current_crypto = _current_cryptoRaw; + _tradeIn = 0; + _itemsIn = []; + _itemsOut = []; + _SkipOut = true; + }; + ['Bank',_bankBalance,'set'] remoteexec ['Ignatz_Client_PlayerStatsUpdate',_player]; + }; + }; - { - _item = _x; - _itemQty = 1; - if (isClass (_config >> _item)) then{ - _itemWorth = getNumber(_config >> _item >> "price"); - _itemTax = getNumber(_config >> _item >> "tax"); - _tax = _itemWorth * (EPOCH_taxRate + _itemTax); - _itemWorth = ceil(_itemWorth + _tax); - _qtyIndex = _itemClasses find _item; - // add items to array - if (_qtyIndex != -1) then { + if (!_SkipOut) then { + { + _item = _x; + _itemQty = 1; + if (isClass (_config >> _item)) then{ + _itemWorth = getNumber(_config >> _item >> "price"); + _itemTax = getNumber(_config >> _item >> "tax"); + _tax = _itemWorth * (EPOCH_taxRate + _itemTax); + _itemWorth = ceil(_itemWorth + _tax); + _qtyIndex = _itemClasses find _item; + // add items to array + if (_qtyIndex != -1) then { - _currQty = _itemQtys select _qtyIndex; - if (_currQty >= _itemQty) then { + _currQty = _itemQtys select _qtyIndex; + if (_currQty >= _itemQty) then { - if (_current_crypto >= _itemWorth) then { + if (_current_crypto >= _itemWorth) then { - if (_item isKindOf "Air" || _item isKindOf "Ship" || _item isKindOf "LandVehicle" || _item isKindOf "Tank") then{ + if (_item isKindOf "Air" || _item isKindOf "Ship" || _item isKindOf "LandVehicle" || _item isKindOf "Tank") then{ - if (!_vehicleBought) then { + if (!_vehicleBought) then { - if !(EPOCH_VehicleSlots isEqualTo[]) then { - _position = getPosATL _player; + if !(EPOCH_VehicleSlots isEqualTo[]) then { + _position = getPosATL _player; - _helipad = nearestObjects[_position, ["Land_HelipadEmpty_F", "Land_HelipadCircle_F"], 100]; - _helipads = []; - _smoke = nearestObject[_position, "SmokeShell"]; - if (!isNull _smoke) then { - _helipad pushBack _smoke; - }; - - // water check - if (_item isKindOf "Ship") then { - { - if (surfaceIsWater (getposATL _x)) then { - _helipads pushBack _x; - } - } forEach _helipad; - } else { - { - if !(surfaceIsWater (getposATL _x)) then { - _helipads pushBack _x; - } - } forEach _helipad; - }; - - if !(_helipads isEqualTo[]) then { - - _foundSmoke = false; - { - if (_x isKindOf "SmokeShell") then { - _objOwner = owner _x; - if (_objOwner == owner _player) then { - _position = getPosATL _x; - _foundSmoke = true; - } else { - { - if (_objOwner == owner _x) exitWith{ - _position = getPosATL _x; - _foundSmoke = true; - }; - } forEach (units _player); - }; - }; - if (_foundSmoke) exitWith {}; - } forEach _helipads; - if !(_foundSmoke) then { - _position = getPosATL (_helipads select 0); + _helipad = nearestObjects[_position, ["Land_HelipadEmpty_F", "Land_HelipadCircle_F"], 100]; + _helipads = []; + _smoke = nearestObject[_position, "SmokeShell"]; + if (!isNull _smoke) then { + _helipad pushBack _smoke; }; - } else { - _tmpposition = []; + + // water check if (_item isKindOf "Ship") then { - _tmpposition = [_position, 20, 120, 10, 0, 1000, 1] call BIS_fnc_findSafePos; - _tmpposition = [_tmpposition, 0, 60, 10, 2, 1000, 0] call BIS_fnc_findSafePos; + { + if (surfaceIsWater (getposATL _x)) then { + _helipads pushBack _x; + } + } forEach _helipad; } else { - _tmpposition = [_position, 20, 120, 20, 0, 2000, 0] call BIS_fnc_findSafePos; + { + if !(surfaceIsWater (getposATL _x)) then { + _helipads pushBack _x; + } + } forEach _helipad; }; - if ((count _tmpposition) == 2) then { - _tmpposition set [2, 0]; - if (surfaceIsWater _tmpposition) then { - _tmpposition = ATLtoASL _tmpposition; + + if !(_helipads isEqualTo[]) then { + + _foundSmoke = false; + { + if (_x isKindOf "SmokeShell") then { + _objOwner = owner _x; + if (_objOwner == owner _player) then { + _position = getPosATL _x; + _foundSmoke = true; + } else { + { + if (_objOwner == owner _x) exitWith{ + _position = getPosATL _x; + _foundSmoke = true; + }; + } forEach (units _player); + }; + }; + if (_foundSmoke) exitWith {}; + } forEach _helipads; + if !(_foundSmoke) then { + _position = getPosATL (_helipads select 0); + }; + } else { + _tmpposition = []; + if (_item isKindOf "Ship") then { + _tmpposition = [_position, 20, 150, 5, 0, 1000, 1] call BIS_fnc_findSafePos; + _tmpposition = [_tmpposition, 0, 60, 10, 2, 1000, 0] call BIS_fnc_findSafePos; + } else { + _tmpposition = [_position, 20, 120, 5, 0, 2000, 0] call BIS_fnc_findSafePos; + }; + if ((count _tmpposition) == 2) then { + _tmpposition set [2, 0]; + if (surfaceIsWater _tmpposition) then { + _tmpposition = ATLtoASL _tmpposition; + }; + _position = _tmpposition; + } + else { + _road = [getpos _player,100] call BIS_fnc_nearestRoad; + if (!isnull _road) then { + _position = getpos _road; + }; }; - _position = _tmpposition; }; + // select available slot + _vehslot = EPOCH_VehicleSlots select 0; + // Remove from available slots + EPOCH_VehicleSlots = EPOCH_VehicleSlots - [_vehslot]; + missionNamespace setVariable ['EPOCH_VehicleSlotCount', count EPOCH_VehicleSlots, true]; + _vehicleBought = true; + + // Group access + _lockOwner = getPlayerUID _player; + _playerGroup = _player getVariable["GROUP", ""]; + if (_playerGroup != "") then { + _lockOwner = _playerGroup; + }; + + _vehObj = [_item,_position,random 360,true,_vehslot,_lockOwner,"NONE",false,false] call EPOCH_spawn_vehicle; + _final_location = getPosATL _vehObj; + + _group = group _player; + _wp = _group addWaypoint [_final_location, 0]; + deleteWaypoint [_group, 0]; + + _returnOut pushBack _item; + + _itemQtys set[_qtyIndex, (_currQty - _itemQty)]; + _tradeOut = _tradeOut - _itemWorth; + _current_crypto = _current_crypto - _itemWorth; + _tradeQtyTotal = _tradeQtyTotal + _itemQty; + } + else { + _errorMsg = "Failed to purchase vehicle."; + [_errorMsg, 5] remoteExec ['Epoch_message',_player]; }; - // select available slot - _vehslot = EPOCH_VehicleSlots select 0; - // Remove from available slots - EPOCH_VehicleSlots = EPOCH_VehicleSlots - [_vehslot]; - missionNamespace setVariable ['EPOCH_VehicleSlotCount', count EPOCH_VehicleSlots, true]; - _vehicleBought = true; + }; + } else { - // Group access - _lockOwner = getPlayerUID _player; - _playerGroup = _player getVariable["GROUP", ""]; - if (_playerGroup != "") then { - _lockOwner = _playerGroup; + if (_item isKindOf "Bag_Base") then { + _wH = objNull; + _nearByHolder = nearestObjects [position _player,["groundWeaponHolder"],3]; + if (_nearByHolder isEqualTo []) then { + _wHPos = _player modelToWorld [0,1,0]; + if (surfaceIsWater _wHPos) then { + _wHPos = ASLToATL _wHPos; + }; + _wH = createVehicle ["groundWeaponHolder",_wHPos, [], 0, "CAN_COLLIDE"]; + } else { + _wH = _nearByHolder select 0; }; - - _vehObj = [_item,_position,random 360,true,_vehslot,_lockOwner,"NONE",false,false] call EPOCH_spawn_vehicle; - _final_location = getPosATL _vehObj; - - _group = group _player; - _wp = _group addWaypoint [_final_location, 0]; - deleteWaypoint [_group, 0]; - - _returnOut pushBack _item; - - _itemQtys set[_qtyIndex, (_currQty - _itemQty)]; - _tradeTotal = _tradeTotal - _itemWorth; - _current_crypto = _current_crypto - _itemWorth; - _tradeQtyTotal = _tradeQtyTotal + _itemQty; - } else { - // diag_log "DEBUG: no slots found to spawn vehicle"; - // [_errorMsg, 5] call Epoch_message; - _errorMsg = "Failed to purchase vehicle."; - [_errorMsg, 5] remoteExec ['Epoch_message',_player]; + _wh addBackpackCargoGlobal [_item,1]; }; + _returnOut pushBack _item; + _itemQtys set[_qtyIndex, (_currQty - _itemQty)]; + _tradeOut = _tradeOut - _itemWorth; + _current_crypto = _current_crypto - _itemWorth; + _tradeQtyTotal = _tradeQtyTotal + _itemQty; }; - } else { - - if (_item isKindOf "Bag_Base") then { - _wH = objNull; - _nearByHolder = nearestObjects [position _player,["groundWeaponHolder"],3]; - if (_nearByHolder isEqualTo []) then { - _wHPos = _player modelToWorld [0,1,0]; - if (surfaceIsWater _wHPos) then { - _wHPos = ASLToATL _wHPos; - }; - _wH = createVehicle ["groundWeaponHolder",_wHPos, [], 0, "CAN_COLLIDE"]; - } else { - _wH = _nearByHolder select 0; - }; - _wh addBackpackCargoGlobal [_item,1]; - }; - - _returnOut pushBack _item; - - _itemQtys set[_qtyIndex, (_currQty - _itemQty)]; - _tradeTotal = _tradeTotal - _itemWorth; - _current_crypto = _current_crypto - _itemWorth; - _tradeQtyTotal = _tradeQtyTotal + _itemQty; }; }; }; }; - }; - } forEach _itemsOut; + } forEach _itemsOut; + }; + + + if !(_itemsIn isEqualTo []) then { + if (_itemsIn isEqualTo _returnIn) then { + _message = _message + "All Items sold"; + } + else { + _message = _message + "Not all Items sold"; + }; + }; + if !(_itemsOut isEqualTo []) then { + if (_itemsOut isEqualTo _returnOut) then { + if !(_message isequalto "") then { + _message = _message + " | "; + }; + _message = _message + "All Items purchased"; + } + else { + if !(_message isequalto "") then { + _message = _message + " / "; + }; + _message = _message + "Not all Items purchased"; + }; + }; - if (_itemsIn isEqualTo _returnIn || _itemsOut isEqualTo _returnOut) then { - // save changes to array + _tradeTotal = _tradeIn + _tradeOut; + + + if !(_returnIn isequalto [] && _returnOut isEqualTo []) then { _trader setVariable["AI_ITEMS", [_itemClasses, _itemQtys], true]; - // Force Save _objHiveKey = format["%1:%2", (call EPOCH_fn_InstanceID), _slot]; ["AI_ITEMS", _objHiveKey, EPOCH_expiresAIdata, [_itemClasses, _itemQtys]] call EPOCH_fnc_server_hiveSETEX; - // push crypto changes to player - _playerCryptoLimit = EPOCH_customVarLimits select _cIndex; - _playerCryptoLimit params ["_playerCryptoLimitMax","_playerCryptoLimitMin"]; - _current_crypto = ((_current_cryptoRaw + _tradeTotal) min _playerCryptoLimitMax) max _playerCryptoLimitMin; - // send to player - _current_crypto remoteExec ['EPOCH_effectCrypto',_player]; - _vars set[_cIndex, _current_crypto]; - _player setVariable["VARS", _vars]; + if !(_tradeTotal isequalto 0) then { + _playerCryptoLimit = EPOCH_customVarLimits select _cIndex; + _playerCryptoLimit params ["_playerCryptoLimitMax","_playerCryptoLimitMin"]; + _current_crypto = ((_current_cryptoRaw + _tradeTotal) min _playerCryptoLimitMax) max _playerCryptoLimitMin; + _current_crypto remoteExec ['EPOCH_effectCrypto',_player]; + _vars set[_cIndex, _current_crypto]; + _player setVariable["VARS", _vars]; + }; }; // Send completed trade back to player - [["tradeComplete", [_returnIn, _returnOut]], _player] call EPOCH_sendRemoteExecClient; + [_returnIn, _returnOut,_message] remoteexec ["EPOCH_NpcTrade_return",_player]; };