Reworked NPC Trade Script

- no longer use PubVar for Trade
- "Purchase Failed" not abort the complete trade
- Added an option, that Players with to much Bank-debit can not sell,
but can purchase. The Money for Purchasing goes directly to the Bank
then.
This commit is contained in:
He-Man 2017-02-10 21:28:00 +01:00
parent 1816c9755f
commit 69a3b8b5d7
5 changed files with 293 additions and 213 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;
};
};
};

View File

@ -60,6 +60,7 @@ class CfgClientFunctions
};
class traders
{
class NpcTrade_return {};
class startInteract {};
class startInteractNPC {};
class npcTraderAdd {};

View File

@ -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];
};