Refactor phone and task systems around repositories
- Rename phone class to repository and update client event handlers - Split task helpers/modules/prototypes and add mission generator flow - Add reward parsing, mission manager startup, and attack logging
This commit is contained in:
parent
1b000af2e6
commit
0cd6509337
@ -1,3 +1,3 @@
|
||||
PREP(handleUIEvents);
|
||||
PREP(initClass);
|
||||
PREP(initRepository);
|
||||
PREP(openUI);
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
[QGVAR(initPhone), []] call CFUNC(localEvent);
|
||||
}] call CFUNC(waitUntilAndExecute);
|
||||
|
||||
if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
if (isNil QGVAR(PhoneRepository)) then { [] call FUNC(initRepository); };
|
||||
|
||||
[QGVAR(initPhone), {
|
||||
GVAR(PhoneClass) call ["init", []];
|
||||
GVAR(PhoneRepository) call ["init", []];
|
||||
|
||||
["forge_server_phone_requestInitPhone", [getPlayerUID player, createHashMap]] call CFUNC(serverEvent);
|
||||
["forge_server_phone_requestRefreshContacts", [getPlayerUID player, player]] call CFUNC(serverEvent);
|
||||
@ -18,7 +18,7 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
[QGVAR(responseSyncPhone), {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
GVAR(PhoneClass) call ["sync", [_data]];
|
||||
GVAR(PhoneRepository) call ["sync", [_data]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
// Contact Management Response Events
|
||||
@ -26,10 +26,10 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Contact Added", "Contact added successfully", 3000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Contact Added", "Contact added successfully", 3000]];
|
||||
[QGVAR(refreshUI), []] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Contact Error", "Failed to add contact", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Contact Error", "Failed to add contact", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -37,10 +37,10 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]], ["_phoneNumber", "", [""]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Contact Added", format ["Contact with phone %1 added successfully", _phoneNumber], 3000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Contact Added", format ["Contact with phone %1 added successfully", _phoneNumber], 3000]];
|
||||
[QGVAR(refreshUI), []] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["warning", "Contact Not Found", format ["Player with phone %1 not found", _phoneNumber], 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["warning", "Contact Not Found", format ["Player with phone %1 not found", _phoneNumber], 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -48,10 +48,10 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]], ["_email", "", [""]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Contact Added", format ["Contact with email %1 added successfully", _email], 3000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Contact Added", format ["Contact with email %1 added successfully", _email], 3000]];
|
||||
[QGVAR(refreshUI), []] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["warning", "Contact Not Found", format ["Player with email %1 not found", _email], 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["warning", "Contact Not Found", format ["Player with email %1 not found", _email], 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -59,10 +59,10 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]], ["_contactUid", "", [""]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Contact Removed", "Contact removed successfully", 3000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Contact Removed", "Contact removed successfully", 3000]];
|
||||
[QGVAR(refreshUI), []] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Contact Error", "Failed to remove contact", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Contact Error", "Failed to remove contact", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -103,7 +103,7 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
};
|
||||
} forEach _contacts;
|
||||
|
||||
[QEGVAR(notifications,recieveNotification), ["info", "New Message", format ["From %1", _senderName], 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["info", "New Message", format ["From %1", _senderName], 4000]];
|
||||
|
||||
diag_log format ["[FORGE:Client:Phone] Message received from %1: %2", _fromUid, _message];
|
||||
|
||||
@ -114,9 +114,9 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Message Sent", "Message sent successfully", 2000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Message Sent", "Message sent successfully", 2000]];
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Message Failed", "Failed to send message", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Message Failed", "Failed to send message", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -157,7 +157,7 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
diag_log format ["[FORGE:Client:Phone] Message %1 deleted", _messageId];
|
||||
[QGVAR(updateMessageDeleted), [_messageId]] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Message Delete Failed", "Failed to delete message", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Message Delete Failed", "Failed to delete message", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -184,7 +184,7 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
};
|
||||
} forEach _contacts;
|
||||
|
||||
[QEGVAR(notifications,recieveNotification), ["info", "New Email", format ["From %1: %2", _senderName, _subject], 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["info", "New Email", format ["From %1: %2", _senderName, _subject], 4000]];
|
||||
|
||||
diag_log format ["[FORGE:Client:Phone] Email received from %1: %2", _fromUid, _subject];
|
||||
|
||||
@ -195,9 +195,9 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
params [["_success", false, [false]]];
|
||||
|
||||
if (_success) then {
|
||||
[QEGVAR(notifications,recieveNotification), ["success", "Email Sent", "Email sent successfully", 2000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["success", "Email Sent", "Email sent successfully", 2000]];
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Email Failed", "Failed to send email", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Email Failed", "Failed to send email", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -233,7 +233,7 @@ if (isNil QGVAR(PhoneClass)) then { [] call FUNC(initClass); };
|
||||
diag_log format ["[FORGE:Client:Phone] Email %1 deleted", _emailId];
|
||||
[QGVAR(updateEmailDeleted), [_emailId]] call CFUNC(localEvent);
|
||||
} else {
|
||||
[QEGVAR(notifications,recieveNotification), ["danger", "Email Delete Failed", "Failed to delete email", 4000]] call CFUNC(localEvent);
|
||||
EGVAR(notifications,NotificationService) call ["create", ["danger", "Email Delete Failed", "Failed to delete email", 4000]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
|
||||
@ -162,18 +162,18 @@ switch (_event) do {
|
||||
};
|
||||
};
|
||||
case "phone::get::notes": {
|
||||
private _notes = GVAR(PhoneClass) call ["getAllNotes", []];
|
||||
private _notes = GVAR(PhoneRepository) call ["getAllNotes", []];
|
||||
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["loadNotes(%1)", (toJSON _notes)]];
|
||||
};
|
||||
case "phone::save::note": {
|
||||
private _success = GVAR(PhoneClass) call ["addNote", [_data]];
|
||||
private _success = GVAR(PhoneRepository) call ["addNote", [_data]];
|
||||
_success
|
||||
};
|
||||
case "phone::delete::note": {
|
||||
private _noteId = _data get "id";
|
||||
|
||||
private _success = GVAR(PhoneClass) call ["deleteNote", [_noteId]];
|
||||
private _success = GVAR(PhoneRepository) call ["deleteNote", [_noteId]];
|
||||
_success
|
||||
};
|
||||
case "phone::get::events": {
|
||||
|
||||
@ -4,29 +4,22 @@
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Initialize unified phone class
|
||||
* Initialize phone repository
|
||||
*
|
||||
* Arguments:
|
||||
* N/A
|
||||
*
|
||||
* Return Value:
|
||||
* N/A
|
||||
* Phone repository object
|
||||
*
|
||||
* Examples:
|
||||
* [] call forge_client_phone_fnc_initClass
|
||||
* [] call forge_client_phone_fnc_initRepository
|
||||
*
|
||||
* Public: Yes
|
||||
*/
|
||||
|
||||
// TODO: Perform comprehensive review and edit of phone class implementation
|
||||
// Then integrate this class to replace current phone handling logic
|
||||
// Key areas to address:
|
||||
// - Verify all phone data structures and methods
|
||||
// - Ensure proper data persistence
|
||||
// - Implement robust error handling
|
||||
// - Replace direct UI manipulation with class-based approach
|
||||
GVAR(PhoneClass) = createHashMapObject [[
|
||||
["#type", "IPhoneClass"],
|
||||
GVAR(PhoneRepository) = createHashMapObject [[
|
||||
["#type", "IPhoneRepository"],
|
||||
["#create", {
|
||||
_self set ["uid", getPlayerUID player];
|
||||
_self set ["notes", createHashMap];
|
||||
@ -35,7 +28,6 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
_self set ["isLoaded", false];
|
||||
_self set ["lastSave", time];
|
||||
|
||||
// Initialize default settings
|
||||
private _settings = createHashMap;
|
||||
_settings set ["theme", "light"];
|
||||
_settings set ["notifications", true];
|
||||
@ -44,8 +36,6 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
_self set ["settings", _settings];
|
||||
}],
|
||||
["init", {
|
||||
// Contacts/messages/emails are server-owned. Keep only local utility-app
|
||||
// state in profileNamespace until those apps are migrated.
|
||||
private _savedNotes = profileNamespace getVariable ["FORGE_Phone_Notes", createHashMap];
|
||||
private _savedEvents = profileNamespace getVariable ["FORGE_Phone_Events", []];
|
||||
private _savedSettings = profileNamespace getVariable ["FORGE_Phone_Settings", createHashMap];
|
||||
@ -53,7 +43,6 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
_self set ["notes", _savedNotes];
|
||||
_self set ["events", _savedEvents];
|
||||
|
||||
// Merge saved settings with defaults
|
||||
private _defaultSettings = _self get "settings";
|
||||
{
|
||||
_defaultSettings set [_x, _y];
|
||||
@ -62,32 +51,28 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
_self set ["settings", _defaultSettings];
|
||||
_self set ["isLoaded", true];
|
||||
|
||||
systemChat format ["Phone loaded for %1", (name player)];
|
||||
diag_log "[FORGE:Client:Phone] Phone Class Initialized!";
|
||||
systemChat format ["Phone loaded for %1", name player];
|
||||
diag_log "[FORGE:Client:Phone] Phone Repository Initialized!";
|
||||
}],
|
||||
["_padString", {
|
||||
params [["_number", 0, [0]], ["_length", 0, [0]]];
|
||||
|
||||
private _str = str _number;
|
||||
|
||||
while { (_str select [(_length - 1), 1]) == "" } do { _str = "0" + _str };
|
||||
_str
|
||||
}],
|
||||
["save", {
|
||||
params [["_sync", false, [false]]];
|
||||
|
||||
// Save local-only phone app state to profile.
|
||||
profileNamespace setVariable ["FORGE_Phone_Notes", _self get "notes"];
|
||||
profileNamespace setVariable ["FORGE_Phone_Events", _self get "events"];
|
||||
profileNamespace setVariable ["FORGE_Phone_Settings", _self get "settings"];
|
||||
|
||||
if (_sync) then { saveProfileNamespace; };
|
||||
|
||||
_self set ["lastSave", time];
|
||||
}],
|
||||
["sync", {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
if (_data isEqualTo createHashMap) exitWith { diag_log "[FORGE:Client:Phone] Empty data received for sync, skipping."; };
|
||||
}],
|
||||
["get", {
|
||||
@ -98,12 +83,10 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}],
|
||||
["addNote", {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
if (_data isEqualTo createHashMap) exitWith { false };
|
||||
|
||||
private _noteId = _data get "id";
|
||||
private _notes = _self get "notes";
|
||||
|
||||
_notes set [_noteId, _data];
|
||||
_self call ["save", [true]];
|
||||
|
||||
@ -128,11 +111,10 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}],
|
||||
["deleteNote", {
|
||||
params [["_noteId", "", [""]]];
|
||||
|
||||
if (_noteId == "") exitWith { false };
|
||||
|
||||
private _notes = _self get "notes";
|
||||
if (!(_noteId in _notes)) exitWith { false };
|
||||
if !(_noteId in _notes) exitWith { false };
|
||||
|
||||
_notes deleteAt _noteId;
|
||||
_self set ["notes", _notes];
|
||||
@ -159,7 +141,6 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}],
|
||||
["setSetting", {
|
||||
params [["_key", "", [""]], ["_value", nil]];
|
||||
|
||||
if (_key == "") exitWith { false };
|
||||
|
||||
private _settings = _self get "settings";
|
||||
@ -180,23 +161,18 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}],
|
||||
["addEvent", {
|
||||
params [["_eventData", createHashMap, [createHashMap]]];
|
||||
|
||||
if (_eventData isEqualTo createHashMap) exitWith { false };
|
||||
|
||||
private _eventId = _eventData get "id";
|
||||
if (isNil "_eventId" || _eventId == "") exitWith { false };
|
||||
|
||||
private _events = _self get "events";
|
||||
|
||||
// Check if event already exists
|
||||
private _existingIndex = _events findIf {(_x get "id") isEqualTo _eventId};
|
||||
private _existingIndex = _events findIf { (_x get "id") isEqualTo _eventId };
|
||||
|
||||
if (_existingIndex >= 0) then {
|
||||
// Update existing event
|
||||
_events set [_existingIndex, _eventData];
|
||||
diag_log format ["[FORGE:Client:Phone] Updated event [ID: %1]", _eventId];
|
||||
} else {
|
||||
// Add new event
|
||||
_events pushBack _eventData;
|
||||
diag_log format ["[FORGE:Client:Phone] Added event [ID: %1]", _eventId];
|
||||
};
|
||||
@ -212,8 +188,7 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
if (isNil "_eventId" || _eventId == "") exitWith { false };
|
||||
|
||||
private _events = _self get "events";
|
||||
private _existingIndex = _events findIf {(_x get "id") isEqualTo _eventId};
|
||||
|
||||
private _existingIndex = _events findIf { (_x get "id") isEqualTo _eventId };
|
||||
if (_existingIndex < 0) exitWith { false };
|
||||
|
||||
_events set [_existingIndex, _eventData];
|
||||
@ -225,12 +200,10 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}],
|
||||
["deleteEvent", {
|
||||
params [["_eventId", "", [""]]];
|
||||
|
||||
if (_eventId == "") exitWith { false };
|
||||
|
||||
private _events = _self get "events";
|
||||
private _existingIndex = _events findIf {(_x get "id") isEqualTo _eventId};
|
||||
|
||||
private _existingIndex = _events findIf { (_x get "id") isEqualTo _eventId };
|
||||
if (_existingIndex < 0) exitWith { false };
|
||||
|
||||
_events deleteAt _existingIndex;
|
||||
@ -244,32 +217,23 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
params [["_eventId", "", [""]], ["_default", nil]];
|
||||
|
||||
private _events = _self get "events";
|
||||
private _event = _events select {(_x get "id") isEqualTo _eventId};
|
||||
|
||||
if (_event isNotEqualTo []) then {
|
||||
_event select 0
|
||||
} else {
|
||||
_default
|
||||
};
|
||||
private _event = _events select { (_x get "id") isEqualTo _eventId };
|
||||
if (_event isNotEqualTo []) then { _event select 0 } else { _default };
|
||||
}],
|
||||
["getAllEvents", {
|
||||
private _events = _self get "events";
|
||||
_events
|
||||
_self get "events";
|
||||
}],
|
||||
["getEventsByDate", {
|
||||
params [["_date", "", [""]]];
|
||||
|
||||
private _events = _self get "events";
|
||||
private _dateEvents = _events select {
|
||||
_events select {
|
||||
private _eventStartTime = _x get "startTime";
|
||||
if (isNil "_eventStartTime") then { false } else {
|
||||
// Extract date from ISO string (YYYY-MM-DD)
|
||||
private _eventDate = (_eventStartTime splitString "T") select 0;
|
||||
_eventDate isEqualTo _date
|
||||
};
|
||||
};
|
||||
|
||||
_dateEvents
|
||||
}
|
||||
}],
|
||||
["clearAllEvents", {
|
||||
_self set ["events", []];
|
||||
@ -289,5 +253,4 @@ GVAR(PhoneClass) = createHashMapObject [[
|
||||
}]
|
||||
]];
|
||||
|
||||
SETVAR(player,FORGE_PhoneClass,GVAR(PhoneClass));
|
||||
GVAR(PhoneClass)
|
||||
GVAR(PhoneRepository)
|
||||
@ -42,11 +42,13 @@
|
||||
#ifdef DISABLE_COMPILE_CACHE
|
||||
#define LINKFUNC(x) {call FUNC(x)}
|
||||
#define PREP(fncName) FUNC(fncName) = compile preprocessFileLineNumbers QPATHTOF(functions\DOUBLES(fnc,fncName).sqf)
|
||||
#define PREP_SUBDIR(dir,fncName) FUNC(fncName) = compile preprocessFileLineNumbers QPATHTOF(functions\dir\DOUBLES(fnc,fncName).sqf)
|
||||
#define PREP_RECOMPILE_START if (isNil "forge_server_fnc_recompile") then {forge_server_recompiles = []; forge_server_fnc_recompile = {{call _x} forEach forge_server_recompiles;}}; private _recomp = {
|
||||
#define PREP_RECOMPILE_END }; call _recomp; forge_server_recompiles pushBack _recomp;
|
||||
#else
|
||||
#define LINKFUNC(x) FUNC(x)
|
||||
#define PREP(fncName) [QPATHTOF(functions\DOUBLES(fnc,fncName).sqf), QFUNC(fncName)] call CBA_fnc_compileFunction
|
||||
#define PREP_SUBDIR(dir,fncName) [QPATHTOF(functions\dir\DOUBLES(fnc,fncName).sqf), QFUNC(fncName)] call CBA_fnc_compileFunction
|
||||
#define PREP_RECOMPILE_START ; /* disabled */
|
||||
#define PREP_RECOMPILE_END ; /* disabled */
|
||||
#endif
|
||||
|
||||
@ -161,6 +161,7 @@ class CfgMissions {
|
||||
class Attack {
|
||||
minUnits = 4;
|
||||
maxUnits = 8;
|
||||
patrolRadius = 200;
|
||||
class Rewards {
|
||||
money[] = {25000, 60000};
|
||||
reputation[] = {6, 14};
|
||||
|
||||
@ -55,6 +55,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Attack)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Attack_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -332,6 +333,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Defend)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Defend_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -436,6 +438,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Defuse)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Defuse_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -547,6 +550,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Destroy)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Destroy_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -665,6 +669,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Hostage)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Hostage_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -811,6 +816,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_Delivery)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_Delivery_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
@ -964,6 +970,7 @@ class CfgVehicles {
|
||||
typeName = "NUMBER";
|
||||
defaultValue = 0;
|
||||
};
|
||||
REWARD_ARRAY_ATTRIBUTES(FORGE_Module_HVT)
|
||||
class RatingFail: Edit {
|
||||
property = "FORGE_Module_HVT_RatingFail";
|
||||
displayName = "Rating Loss";
|
||||
|
||||
@ -45,7 +45,7 @@ system intentionally starts clean after each server or mission restart.
|
||||
A review-only prototype for object-based task instances lives under
|
||||
`prototypes/`. It is not wired into runtime.
|
||||
|
||||
- `prototypes/taskObjectPrototypes.sqf`
|
||||
- `forge_server_task_fnc_initPrototypes`
|
||||
- `prototypes/README.md`
|
||||
|
||||
The prototype sketches `TaskInstanceBaseClass`, `HostageTaskBaseClass`, and
|
||||
|
||||
@ -1,23 +1,11 @@
|
||||
PREP(attack);
|
||||
PREP(attackModule);
|
||||
PREP(cargoModule);
|
||||
PREP(defend);
|
||||
PREP(defendModule);
|
||||
PREP(defuse);
|
||||
PREP(defuseModule);
|
||||
PREP(delivery);
|
||||
PREP(deliveryModule);
|
||||
PREP(destroy);
|
||||
PREP(destroyModule);
|
||||
PREP(explosivesModule);
|
||||
PREP(handler);
|
||||
PREP(handleTaskRewards);
|
||||
PREP(heartBeat);
|
||||
PREP(hostage);
|
||||
PREP(hostageModule);
|
||||
PREP(hostagesModule);
|
||||
PREP(hvt);
|
||||
PREP(hvtModule);
|
||||
PREP(makeCargo);
|
||||
PREP(makeHostage);
|
||||
PREP(makeHVT);
|
||||
@ -27,7 +15,32 @@ PREP(makeShooter);
|
||||
PREP(makeTarget);
|
||||
PREP(missionManager);
|
||||
PREP(initTaskStore);
|
||||
PREP(protectedModule);
|
||||
PREP(shootersModule);
|
||||
PREP(spawnEnemyWave);
|
||||
PREP(startTask);
|
||||
|
||||
PREP_SUBDIR(generators,attackMissionGenerator);
|
||||
|
||||
PREP_SUBDIR(helpers,handleTaskRewards);
|
||||
PREP_SUBDIR(helpers,heartBeat);
|
||||
PREP_SUBDIR(helpers,parseRewards);
|
||||
PREP_SUBDIR(helpers,spawnEnemyWave);
|
||||
PREP_SUBDIR(helpers,startTask);
|
||||
|
||||
PREP_SUBDIR(modules,attackModule);
|
||||
PREP_SUBDIR(modules,cargoModule);
|
||||
PREP_SUBDIR(modules,defendModule);
|
||||
PREP_SUBDIR(modules,defuseModule);
|
||||
PREP_SUBDIR(modules,deliveryModule);
|
||||
PREP_SUBDIR(modules,destroyModule);
|
||||
PREP_SUBDIR(modules,explosivesModule);
|
||||
PREP_SUBDIR(modules,hostageModule);
|
||||
PREP_SUBDIR(modules,hostagesModule);
|
||||
PREP_SUBDIR(modules,hvtModule);
|
||||
PREP_SUBDIR(modules,protectedModule);
|
||||
PREP_SUBDIR(modules,shootersModule);
|
||||
|
||||
PREP_SUBDIR(prototypes,initPrototypes);
|
||||
PREP_SUBDIR(prototypes,TaskInstanceBaseClass);
|
||||
PREP_SUBDIR(prototypes,EntityControllerBaseClass);
|
||||
PREP_SUBDIR(prototypes,AttackTaskBaseClass);
|
||||
PREP_SUBDIR(prototypes,HostageTaskBaseClass);
|
||||
PREP_SUBDIR(prototypes,HostageEntityController);
|
||||
PREP_SUBDIR(prototypes,DefuseTaskBaseClass);
|
||||
|
||||
@ -26,3 +26,4 @@
|
||||
|
||||
GVAR(TaskStore) call ["incrementDefuseCount", [_taskID]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
[] call FUNC(missionManager);
|
||||
|
||||
@ -60,10 +60,23 @@ waitUntil {
|
||||
_targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _catalogEntry = GVAR(TaskStore) call ["getTaskCatalogEntry", [_taskID]];
|
||||
["INFO", format [
|
||||
"Attack task %1 initial state before acceptance wait. Accepted=%2, RequesterUid='%3', Source='%4', TimeLimit=%5s",
|
||||
_taskID,
|
||||
_catalogEntry getOrDefault ["accepted", false],
|
||||
_catalogEntry getOrDefault ["requesterUid", ""],
|
||||
_catalogEntry getOrDefault ["source", ""],
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
["INFO", format ["Attack task %1 waiting for acceptance before starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||
};
|
||||
|
||||
["INFO", format ["Attack task %1 accepted. Starting %2s time limit.", _taskID, _timeLimit]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
|
||||
@ -77,7 +90,16 @@ waitUntil {
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _timeExpired = (floor time - _startTime >= _timeLimit);
|
||||
|
||||
if (_targetsKilled < _limitSuccess && _timeExpired) then { _result = 1; };
|
||||
if (_targetsKilled < _limitSuccess && _timeExpired) then {
|
||||
["WARNING", format [
|
||||
"Attack task %1 failed by timeout. TargetsKilled=%2, Required=%3, TimeLimit=%4s",
|
||||
_taskID,
|
||||
_targetsKilled,
|
||||
_limitSuccess,
|
||||
_timeLimit
|
||||
]] call EFUNC(common,log);
|
||||
_result = 1;
|
||||
};
|
||||
|
||||
(_result == 1) or (_targetsKilled >= _limitSuccess)
|
||||
} else {
|
||||
@ -99,6 +121,13 @@ if (_result == 1) then {
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
} else {
|
||||
["INFO", format [
|
||||
"Attack task %1 succeeded. TargetsRequired=%2, TargetsKilled=%3",
|
||||
_taskID,
|
||||
_limitSuccess,
|
||||
{ !alive _x } count _targets
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
private _rewards = createHashMap;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Manages attack-only dynamic mission generation.
|
||||
* Manages dynamic mission generators.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
@ -16,335 +16,120 @@
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
if !(isServer) exitWith { false };
|
||||
if !(isNil QGVAR(MissionManagerPFH)) exitWith { false };
|
||||
if (isNil QGVAR(AttackMissionGeneratorBaseClass)) then { call FUNC(attackMissionGenerator); };
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
|
||||
["#type", "MissionManagerBaseClass"],
|
||||
["#create", compileFinal {
|
||||
private _missionConfig = missionConfigFile >> "CfgMissions";
|
||||
_self set ["missionConfig", _missionConfig];
|
||||
_self set ["locationsConfig", (_missionConfig >> "Locations")];
|
||||
_self set ["aiGroupsConfig", (_missionConfig >> "AIGroups")];
|
||||
_self set ["attackConfig", (_missionConfig >> "MissionTypes" >> "Attack")];
|
||||
_self set ["maxConcurrentMissions", getNumber (_missionConfig >> "maxConcurrentMissions")];
|
||||
_self set ["missionInterval", getNumber (_missionConfig >> "missionInterval")];
|
||||
_self set ["recentLocationRegistry", createHashMap];
|
||||
_self set ["recentLocationRegistry", []];
|
||||
_self set ["activeMissionRegistry", createHashMap];
|
||||
_self set ["generators", [createHashMapObject [GVAR(AttackMissionGeneratorBaseClass)]]];
|
||||
}],
|
||||
["getMissionInterval", compileFinal {
|
||||
private _interval = _self getOrDefault ["missionInterval", 300];
|
||||
if (_interval <= 0) then { _interval = 300; };
|
||||
_interval
|
||||
}],
|
||||
["getMaxConcurrentMissions", compileFinal {
|
||||
private _maxConcurrent = _self getOrDefault ["maxConcurrentMissions", 1];
|
||||
if (_maxConcurrent <= 0) then { _maxConcurrent = 1; };
|
||||
private _attackLocationCount = _self call ["getAttackLocationCount", []];
|
||||
if (_attackLocationCount > 0) then {
|
||||
_maxConcurrent = _maxConcurrent min _attackLocationCount;
|
||||
};
|
||||
_maxConcurrent
|
||||
}],
|
||||
["getAttackLocationCount", compileFinal {
|
||||
private _locationsConfig = _self getOrDefault ["locationsConfig", configNull];
|
||||
if (isNull _locationsConfig) exitWith { 0 };
|
||||
|
||||
private _count = 0;
|
||||
{
|
||||
if ("attack" in getArray (_x >> "suitable")) then {
|
||||
_count = _count + 1;
|
||||
};
|
||||
} forEach ("true" configClasses _locationsConfig);
|
||||
|
||||
_count
|
||||
}],
|
||||
["getLocationReuseCooldown", compileFinal {
|
||||
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
|
||||
private _cooldown = getNumber (_missionConfig >> "locationReuseCooldown");
|
||||
if (_cooldown <= 0) then { _cooldown = 900; };
|
||||
_cooldown
|
||||
["getGenerators", compileFinal {
|
||||
_self getOrDefault ["generators", []]
|
||||
}],
|
||||
["getActiveMissionIds", compileFinal {
|
||||
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
keys _activeMissionRegistry
|
||||
}],
|
||||
["getActiveLocationKeys", compileFinal {
|
||||
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
private _locationKeys = [];
|
||||
["getActiveGeneratedMissionIds", compileFinal {
|
||||
private _activeCatalog = GVAR(TaskStore) call ["getActiveTaskCatalog", []];
|
||||
if !(_activeCatalog isEqualType []) exitWith { [] };
|
||||
|
||||
private _taskIds = [];
|
||||
{
|
||||
private _locationKey = _y getOrDefault ["locationKey", ""];
|
||||
if (_locationKey isNotEqualTo "") then {
|
||||
_locationKeys pushBackUnique _locationKey;
|
||||
if !(_x isEqualType createHashMap) then { continue; };
|
||||
if ((_x getOrDefault ["source", ""]) isNotEqualTo "mission_manager") then { continue; };
|
||||
|
||||
private _taskID = _x getOrDefault ["taskId", _x getOrDefault ["taskID", ""]];
|
||||
if (_taskID isNotEqualTo "") then {
|
||||
_taskIds pushBackUnique _taskID;
|
||||
};
|
||||
} forEach _activeMissionRegistry;
|
||||
_locationKeys
|
||||
} forEach _activeCatalog;
|
||||
|
||||
_taskIds
|
||||
}],
|
||||
["buildAttackSpawnPosition", compileFinal {
|
||||
params [["_locationConfig", configNull, [configNull]]];
|
||||
|
||||
if (isNull _locationConfig) exitWith { [0, 0, 0] };
|
||||
|
||||
private _center = getArray (_locationConfig >> "position");
|
||||
private _radius = getNumber (_locationConfig >> "radius");
|
||||
if (_radius <= 0) exitWith { _center };
|
||||
|
||||
private _spawnPosition = +_center;
|
||||
private _attempts = 0;
|
||||
while { _attempts < 8 } do {
|
||||
private _angle = random 360;
|
||||
private _distance = (_radius * 0.2) + random (_radius * 0.65);
|
||||
private _candidate = [
|
||||
(_center # 0) + ((sin _angle) * _distance),
|
||||
(_center # 1) + ((cos _angle) * _distance),
|
||||
_center param [2, 0]
|
||||
];
|
||||
|
||||
if !(surfaceIsWater _candidate) exitWith {
|
||||
_spawnPosition = _candidate;
|
||||
};
|
||||
|
||||
_attempts = _attempts + 1;
|
||||
};
|
||||
|
||||
_spawnPosition
|
||||
["getMaxConcurrentMissions", compileFinal {
|
||||
private _maxConcurrent = 1;
|
||||
{
|
||||
_maxConcurrent = _maxConcurrent max (_x call ["getMaxConcurrentMissions", []]);
|
||||
} forEach (_self call ["getGenerators", []]);
|
||||
_maxConcurrent
|
||||
}],
|
||||
["selectAttackLocation", compileFinal {
|
||||
private _locationsConfig = _self getOrDefault ["locationsConfig", configNull];
|
||||
private _locations = [];
|
||||
private _recentLocationRegistry = _self getOrDefault ["recentLocationRegistry", createHashMap];
|
||||
private _activeLocationKeys = _self call ["getActiveLocationKeys", []];
|
||||
private _reuseCooldown = _self call ["getLocationReuseCooldown", []];
|
||||
private _now = serverTime;
|
||||
["getMissionInterval", compileFinal {
|
||||
private _interval = 300;
|
||||
private _generators = _self call ["getGenerators", []];
|
||||
if (_generators isEqualTo []) exitWith { _interval };
|
||||
|
||||
_interval = (_generators select 0) call ["getMissionInterval", []];
|
||||
{
|
||||
private _locationKey = configName _x;
|
||||
private _lastUsed = _recentLocationRegistry getOrDefault [_locationKey, -1];
|
||||
private _isCoolingDown = (_lastUsed >= 0) && { (_now - _lastUsed) < _reuseCooldown };
|
||||
|
||||
if (
|
||||
"attack" in getArray (_x >> "suitable")
|
||||
&& { !(_locationKey in _activeLocationKeys) }
|
||||
&& { !_isCoolingDown }
|
||||
) then {
|
||||
_locations pushBack _x;
|
||||
};
|
||||
} forEach ("true" configClasses _locationsConfig);
|
||||
|
||||
if (_locations isEqualTo []) then {
|
||||
{
|
||||
if ("attack" in getArray (_x >> "suitable") && { !(configName _x in _activeLocationKeys) }) then {
|
||||
_locations pushBack _x;
|
||||
};
|
||||
} forEach ("true" configClasses _locationsConfig);
|
||||
};
|
||||
|
||||
if (_locations isEqualTo []) exitWith { createHashMap };
|
||||
|
||||
private _location = selectRandom _locations;
|
||||
createHashMapFromArray [
|
||||
["config", _location],
|
||||
["key", configName _location],
|
||||
["position", _self call ["buildAttackSpawnPosition", [_location]]]
|
||||
]
|
||||
_interval = _interval min (_x call ["getMissionInterval", []]);
|
||||
} forEach _generators;
|
||||
_interval
|
||||
}],
|
||||
["spawnAttackGroup", compileFinal {
|
||||
params [["_position", [0, 0, 0], [[]]]];
|
||||
|
||||
private _aiGroupsConfig = _self getOrDefault ["aiGroupsConfig", configNull];
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _groups = [];
|
||||
["cleanupCompletedMissions", compileFinal {
|
||||
{
|
||||
if ("attack" in getArray (_x >> "suitable")) then {
|
||||
_groups pushBack _x;
|
||||
};
|
||||
} forEach ("true" configClasses _aiGroupsConfig);
|
||||
|
||||
if (_groups isEqualTo []) exitWith { grpNull };
|
||||
|
||||
private _groupConfig = selectRandom _groups;
|
||||
private _side = getText (_groupConfig >> "side");
|
||||
private _group = createGroup (call compile _side);
|
||||
private _minUnits = getNumber (_attackConfig >> "minUnits");
|
||||
private _maxUnits = getNumber (_attackConfig >> "maxUnits");
|
||||
if (_minUnits <= 0) then { _minUnits = 4; };
|
||||
if (_maxUnits < _minUnits) then { _maxUnits = _minUnits; };
|
||||
private _targetUnitCount = floor random [ _minUnits, ceil ((_minUnits + _maxUnits) / 2), _maxUnits + 1 ];
|
||||
|
||||
private _unitPool = [];
|
||||
{
|
||||
if ((getText (_x >> "side")) isNotEqualTo _side) then { continue; };
|
||||
private _taskID = _x;
|
||||
private _status = GVAR(TaskStore) call ["getTaskStatus", [_taskID]];
|
||||
private _hasCatalogEntry = GVAR(TaskStore) call ["hasTaskCatalogEntry", [_taskID]];
|
||||
if !(_status in ["succeeded", "failed"] || { _status isEqualTo "" && { !_hasCatalogEntry } }) then { continue; };
|
||||
|
||||
{
|
||||
_unitPool pushBack createHashMapFromArray [
|
||||
["vehicle", getText (_x >> "vehicle")],
|
||||
["rank", getText (_x >> "rank")],
|
||||
["position", getArray (_x >> "position")]
|
||||
];
|
||||
} forEach ("true" configClasses (_x >> "Units"));
|
||||
} forEach _groups;
|
||||
if (_x call ["completeMission", [_self, _taskID]]) exitWith {};
|
||||
} forEach (_self call ["getGenerators", []]);
|
||||
|
||||
if (_unitPool isEqualTo []) exitWith {
|
||||
deleteGroup _group;
|
||||
grpNull
|
||||
};
|
||||
GVAR(TaskStore) call ["clearTaskStatus", [_taskID]];
|
||||
} forEach (_self call ["getActiveMissionIds", []]);
|
||||
|
||||
private _leaderPool = _unitPool select {
|
||||
toUpperANSI (_x getOrDefault ["rank", "PRIVATE"]) in ["SERGEANT", "LIEUTENANT", "CAPTAIN", "MAJOR", "COLONEL"]
|
||||
};
|
||||
if (_leaderPool isEqualTo []) then { _leaderPool = +_unitPool; };
|
||||
|
||||
private _spawnDefs = [selectRandom _leaderPool];
|
||||
for "_i" from 1 to (_targetUnitCount - 1) do {
|
||||
_spawnDefs pushBack (selectRandom _unitPool);
|
||||
};
|
||||
|
||||
{
|
||||
private _unitClass = _x getOrDefault ["vehicle", ""];
|
||||
if (_unitClass isEqualTo "") then { continue; };
|
||||
|
||||
private _unitOffset = +(_x getOrDefault ["position", [0, 0, 0]]);
|
||||
if (count _unitOffset < 3) then { _unitOffset resize 3; };
|
||||
_unitOffset set [0, (_unitOffset # 0) + (random 6 - 3)];
|
||||
_unitOffset set [1, (_unitOffset # 1) + (random 6 - 3)];
|
||||
|
||||
private _unit = _group createUnit [_unitClass, _position vectorAdd _unitOffset, [], 0, "NONE"];
|
||||
_unit setRank (_x getOrDefault ["rank", "PRIVATE"]);
|
||||
} forEach _spawnDefs;
|
||||
|
||||
_group
|
||||
}],
|
||||
["rollRewards", compileFinal {
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _equipmentRewards = [];
|
||||
private _supplyRewards = [];
|
||||
private _weaponRewards = [];
|
||||
private _vehicleRewards = [];
|
||||
private _specialRewards = [];
|
||||
|
||||
{
|
||||
private _category = _x;
|
||||
{
|
||||
_x params ["_item", "_chance"];
|
||||
if (random 1 < _chance) then {
|
||||
switch (_category) do {
|
||||
case "equipment": { _equipmentRewards pushBack _item; };
|
||||
case "supplies": { _supplyRewards pushBack _item; };
|
||||
case "weapons": { _weaponRewards pushBack _item; };
|
||||
case "vehicles": { _vehicleRewards pushBack _item; };
|
||||
case "special": { _specialRewards pushBack _item; };
|
||||
};
|
||||
};
|
||||
} forEach (getArray (_attackConfig >> "Rewards" >> _category));
|
||||
} forEach ["equipment", "supplies", "weapons", "vehicles", "special"];
|
||||
|
||||
createHashMapFromArray [
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
}],
|
||||
["startAttackMission", compileFinal {
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _locationData = _self call ["selectAttackLocation"];
|
||||
if (_locationData isEqualTo createHashMap) exitWith { "" };
|
||||
|
||||
private _locationKey = _locationData getOrDefault ["key", ""];
|
||||
private _position = _locationData getOrDefault ["position", [0, 0, 0]];
|
||||
private _group = _self call ["spawnAttackGroup", [_position]];
|
||||
if (isNull _group) exitWith { "" };
|
||||
|
||||
private _units = units _group;
|
||||
if (_units isEqualTo []) exitWith {
|
||||
deleteGroup _group;
|
||||
""
|
||||
};
|
||||
|
||||
private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)];
|
||||
private _rewardRange = getArray (_attackConfig >> "Rewards" >> "money");
|
||||
private _reputationRange = getArray (_attackConfig >> "Rewards" >> "reputation");
|
||||
private _penaltyRange = getArray (_attackConfig >> "penalty");
|
||||
private _timeRange = getArray (_attackConfig >> "timeLimit");
|
||||
private _rewards = _self call ["rollRewards"];
|
||||
|
||||
private _success = [
|
||||
"attack",
|
||||
_taskID,
|
||||
_position,
|
||||
format ["Attack: %1", _locationKey],
|
||||
format ["Eliminate hostile forces operating near %1.", _locationKey],
|
||||
createHashMapFromArray [["targets", _units]],
|
||||
createHashMapFromArray [
|
||||
["limitFail", 0],
|
||||
["limitSuccess", count _units],
|
||||
["funds", _rewardRange call BFUNC(randomNum)],
|
||||
["ratingFail", _penaltyRange call BFUNC(randomNum)],
|
||||
["ratingSuccess", _reputationRange call BFUNC(randomNum)],
|
||||
["endSuccess", false],
|
||||
["endFail", false],
|
||||
["timeLimit", _timeRange call BFUNC(randomNum)],
|
||||
["equipment", _rewards get "equipment"],
|
||||
["supplies", _rewards get "supplies"],
|
||||
["weapons", _rewards get "weapons"],
|
||||
["vehicles", _rewards get "vehicles"],
|
||||
["special", _rewards get "special"]
|
||||
],
|
||||
0,
|
||||
"",
|
||||
"mission_manager"
|
||||
] call FUNC(startTask);
|
||||
|
||||
if !(_success) exitWith { "" };
|
||||
|
||||
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
_activeMissionRegistry set [_taskID, createHashMapFromArray [
|
||||
["locationKey", _locationKey],
|
||||
["startedAt", serverTime]
|
||||
]];
|
||||
_self set ["activeMissionRegistry", _activeMissionRegistry];
|
||||
|
||||
_taskID
|
||||
true
|
||||
}],
|
||||
["completeMission", compileFinal {
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
if (_taskID isEqualTo "") exitWith { false };
|
||||
|
||||
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
private _missionRecord = _activeMissionRegistry getOrDefault [_taskID, createHashMap];
|
||||
private _locationKey = _missionRecord getOrDefault ["locationKey", ""];
|
||||
{
|
||||
if (_x call ["completeMission", [_self, _taskID]]) exitWith { true };
|
||||
} forEach (_self call ["getGenerators", []]);
|
||||
|
||||
_activeMissionRegistry deleteAt _taskID;
|
||||
_self set ["activeMissionRegistry", _activeMissionRegistry];
|
||||
|
||||
if (_locationKey isNotEqualTo "") then {
|
||||
private _recentLocationRegistry = _self getOrDefault ["recentLocationRegistry", createHashMap];
|
||||
_recentLocationRegistry set [_locationKey, serverTime];
|
||||
_self set ["recentLocationRegistry", _recentLocationRegistry];
|
||||
false
|
||||
}],
|
||||
["startAvailableMissions", compileFinal {
|
||||
private _activeGeneratedMissionIds = _self call ["getActiveGeneratedMissionIds", []];
|
||||
private _maxConcurrentMissions = _self call ["getMaxConcurrentMissions", []];
|
||||
if (count _activeGeneratedMissionIds >= _maxConcurrentMissions) exitWith {
|
||||
["INFO", format [
|
||||
"Mission manager skipped generation because cap was reached. ActiveGenerated=%1, Cap=%2, TaskIDs=%3",
|
||||
count _activeGeneratedMissionIds,
|
||||
_maxConcurrentMissions,
|
||||
_activeGeneratedMissionIds
|
||||
]] call EFUNC(common,log);
|
||||
""
|
||||
};
|
||||
|
||||
true
|
||||
private _startedTaskID = "";
|
||||
{
|
||||
private _taskID = _x call ["startMission", [_self]];
|
||||
if (_taskID isNotEqualTo "") exitWith {
|
||||
_startedTaskID = _taskID;
|
||||
};
|
||||
} forEach (_self call ["getGenerators", []]);
|
||||
|
||||
_startedTaskID
|
||||
}]
|
||||
];
|
||||
|
||||
GVAR(MissionManager) = createHashMapObject [GVAR(MissionManagerBaseClass)];
|
||||
GVAR(MissionManagerPFH) = [{
|
||||
GVAR(MissionManager) call ["cleanupCompletedMissions", []];
|
||||
|
||||
[{
|
||||
{
|
||||
private _status = GVAR(TaskStore) call ["getTaskStatus", [_x]];
|
||||
private _hasCatalogEntry = GVAR(TaskStore) call ["hasTaskCatalogEntry", [_x]];
|
||||
if (_status in ["succeeded", "failed"] || { _status isEqualTo "" && { !_hasCatalogEntry } }) then {
|
||||
GVAR(MissionManager) call ["completeMission", [_x]];
|
||||
GVAR(TaskStore) call ["clearTaskStatus", [_x]];
|
||||
};
|
||||
} forEach (GVAR(MissionManager) call ["getActiveMissionIds", []]);
|
||||
private _taskID = GVAR(MissionManager) call ["startAvailableMissions", []];
|
||||
if (_taskID isEqualTo "") exitWith {};
|
||||
|
||||
if (count (GVAR(MissionManager) call ["getActiveMissionIds", []]) >= (GVAR(MissionManager) call ["getMaxConcurrentMissions", []])) exitWith {};
|
||||
|
||||
private _taskID = GVAR(MissionManager) call ["startAttackMission", []];
|
||||
if (_taskID isEqualTo "") exitWith {
|
||||
["WARNING", "Mission manager failed to start an attack mission."] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
["INFO", format ["Mission manager started attack mission %1.", _taskID]] call EFUNC(common,log);
|
||||
["INFO", format ["Mission manager started mission %1.", _taskID]] call EFUNC(common,log);
|
||||
}, GVAR(MissionManager) call ["getMissionInterval", []], []] call CFUNC(addPerFrameHandler);
|
||||
|
||||
true
|
||||
|
||||
@ -0,0 +1,405 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: IDSolutions
|
||||
* Attack mission generator used by the dynamic mission manager.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call forge_server_task_fnc_attackMissionGenerator
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(AttackMissionGeneratorBaseClass) = compileFinal createHashMapFromArray [
|
||||
["#type", "AttackMissionGeneratorBaseClass"],
|
||||
["#create", compileFinal {
|
||||
private _missionConfig = missionConfigFile >> "CfgMissions";
|
||||
_self set ["missionConfig", _missionConfig];
|
||||
_self set ["aiGroupsConfig", (_missionConfig >> "AIGroups")];
|
||||
_self set ["attackConfig", (_missionConfig >> "MissionTypes" >> "Attack")];
|
||||
_self set ["generatorType", "attack"];
|
||||
}],
|
||||
["getGeneratorType", compileFinal {
|
||||
_self getOrDefault ["generatorType", "attack"]
|
||||
}],
|
||||
["getMissionInterval", compileFinal {
|
||||
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
|
||||
private _interval = getNumber (_missionConfig >> "missionInterval");
|
||||
if (_interval <= 0) then { _interval = 300; };
|
||||
_interval
|
||||
}],
|
||||
["getMaxConcurrentMissions", compileFinal {
|
||||
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
|
||||
private _maxConcurrent = getNumber (_missionConfig >> "maxConcurrentMissions");
|
||||
if (_maxConcurrent <= 0) then { _maxConcurrent = 1; };
|
||||
_maxConcurrent
|
||||
}],
|
||||
["getLocationReuseCooldown", compileFinal {
|
||||
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
|
||||
private _cooldown = getNumber (_missionConfig >> "locationReuseCooldown");
|
||||
if (_cooldown <= 0) then { _cooldown = 900; };
|
||||
_cooldown
|
||||
}],
|
||||
["pruneRecentLocations", compileFinal {
|
||||
params [["_manager", createHashMapObject [createHashMapFromArray []], [createHashMap]]];
|
||||
|
||||
private _recentLocationRegistry = _manager getOrDefault ["recentLocationRegistry", []];
|
||||
private _reuseCooldown = _self call ["getLocationReuseCooldown", []];
|
||||
private _now = serverTime;
|
||||
|
||||
_recentLocationRegistry = _recentLocationRegistry select {
|
||||
private _usedAt = _x param [1, -1, [0]];
|
||||
(_usedAt >= 0) && { (_now - _usedAt) < _reuseCooldown }
|
||||
};
|
||||
|
||||
_manager set ["recentLocationRegistry", _recentLocationRegistry];
|
||||
_recentLocationRegistry
|
||||
}],
|
||||
["getActiveMissionPositions", compileFinal {
|
||||
params [["_manager", createHashMapObject [createHashMapFromArray []], [createHashMap]]];
|
||||
|
||||
private _activeMissionRegistry = _manager getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
private _positions = [];
|
||||
{
|
||||
if ((_y getOrDefault ["generatorType", ""]) isNotEqualTo "attack") then { continue; };
|
||||
|
||||
private _position = _y getOrDefault ["position", []];
|
||||
if (_position isEqualType [] && { count _position >= 2 }) then {
|
||||
_positions pushBack _position;
|
||||
};
|
||||
} forEach _activeMissionRegistry;
|
||||
_positions
|
||||
}],
|
||||
["selectLocation", compileFinal {
|
||||
params [["_manager", createHashMapObject [createHashMapFromArray []], [createHashMap]]];
|
||||
|
||||
private _worldSize = worldSize;
|
||||
private _center = [_worldSize / 2, _worldSize / 2, 0];
|
||||
private _safeDist = 800;
|
||||
private _playerPos = _center;
|
||||
private _minEdgeDist = _safeDist + 200;
|
||||
|
||||
private _recentLocationRegistry = _self call ["pruneRecentLocations", [_manager]];
|
||||
private _activeMissionPositions = _self call ["getActiveMissionPositions", [_manager]];
|
||||
|
||||
private _blkListMarkers = allMapMarkers select { markerShape _x in ["RECTANGLE", "ELLIPSE"] };
|
||||
_blkListMarkers = _blkListMarkers select {
|
||||
(
|
||||
(toLowerANSI _x find "blklist") == 0
|
||||
|| { (toLowerANSI (markerText _x) find "blklist") == 0 }
|
||||
)
|
||||
&& { getMarkerPos _x distance2D [0, 0] > 0 }
|
||||
};
|
||||
|
||||
private _taskPos = [];
|
||||
private _attempt = 0;
|
||||
private _maxAttempts = 50;
|
||||
|
||||
while { _attempt < _maxAttempts && { _taskPos isEqualTo [] } } do {
|
||||
_attempt = _attempt + 1;
|
||||
private _candidate = [_center, _worldSize / 2 - _minEdgeDist, _worldSize / 2 - _minEdgeDist, 3, 0, 0.3, 0] call BIS_fnc_findSafePos;
|
||||
|
||||
if (_candidate isEqualTo [0, 0, 0]) then { continue; };
|
||||
if (_candidate distance2D _playerPos < _safeDist) then { continue; };
|
||||
|
||||
private _isTooClose = false;
|
||||
{
|
||||
private _prevPos = _x param [0, [], [[]]];
|
||||
if (_prevPos isEqualType [] && { count _prevPos >= 2 } && { _candidate distance2D _prevPos < 500 }) exitWith {
|
||||
_isTooClose = true;
|
||||
};
|
||||
} forEach _recentLocationRegistry;
|
||||
|
||||
if (_isTooClose) then { continue; };
|
||||
|
||||
{
|
||||
if (_candidate distance2D _x < 500) exitWith {
|
||||
_isTooClose = true;
|
||||
};
|
||||
} forEach _activeMissionPositions;
|
||||
|
||||
if (_isTooClose) then { continue; };
|
||||
|
||||
private _inBlkList = false;
|
||||
{
|
||||
if (_candidate inArea _x) exitWith {
|
||||
_inBlkList = true;
|
||||
};
|
||||
} forEach _blkListMarkers;
|
||||
|
||||
if (!_inBlkList) then {
|
||||
_taskPos = _candidate;
|
||||
};
|
||||
};
|
||||
|
||||
if (_taskPos isEqualTo []) exitWith {
|
||||
["WARNING", "Attack mission generator: selectLocation failed to find a valid dynamic position."] call EFUNC(common,log);
|
||||
createHashMap
|
||||
};
|
||||
|
||||
createHashMapFromArray [
|
||||
["position", _taskPos],
|
||||
["grid", mapGridPosition _taskPos]
|
||||
]
|
||||
}],
|
||||
["spawnAttackGroup", compileFinal {
|
||||
params [["_position", [0, 0, 0], [[]]]];
|
||||
|
||||
private _aiGroupsConfig = _self getOrDefault ["aiGroupsConfig", configNull];
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _groups = [];
|
||||
{
|
||||
if ("attack" in getArray (_x >> "suitable")) then {
|
||||
_groups pushBack _x;
|
||||
};
|
||||
} forEach ("true" configClasses _aiGroupsConfig);
|
||||
|
||||
if (_groups isEqualTo []) exitWith {
|
||||
["WARNING", "Attack mission generator: no AI group configs are suitable for attack missions."] call EFUNC(common,log);
|
||||
grpNull
|
||||
};
|
||||
|
||||
private _groupConfig = selectRandom _groups;
|
||||
private _side = getText (_groupConfig >> "side");
|
||||
private _group = createGroup (call compile _side);
|
||||
private _minUnits = getNumber (_attackConfig >> "minUnits");
|
||||
private _maxUnits = getNumber (_attackConfig >> "maxUnits");
|
||||
private _patrolRadius = getNumber (_attackConfig >> "patrolRadius");
|
||||
|
||||
if (_minUnits <= 0) then { _minUnits = 4; };
|
||||
if (_maxUnits < _minUnits) then { _maxUnits = _minUnits; };
|
||||
if (_patrolRadius <= 0) then { _patrolRadius = 200; };
|
||||
|
||||
private _targetUnitCount = floor random [_minUnits, ceil ((_minUnits + _maxUnits) / 2), _maxUnits + 1];
|
||||
private _unitPool = [];
|
||||
{
|
||||
if ((getText (_x >> "side")) isNotEqualTo _side) then { continue; };
|
||||
|
||||
{
|
||||
_unitPool pushBack createHashMapFromArray [
|
||||
["vehicle", getText (_x >> "vehicle")],
|
||||
["rank", getText (_x >> "rank")],
|
||||
["position", getArray (_x >> "position")]
|
||||
];
|
||||
} forEach ("true" configClasses (_x >> "Units"));
|
||||
} forEach _groups;
|
||||
|
||||
if (_unitPool isEqualTo []) exitWith {
|
||||
["WARNING", format ["Attack mission generator: selected AI group side '%1' produced an empty unit pool.", _side]] call EFUNC(common,log);
|
||||
deleteGroup _group;
|
||||
grpNull
|
||||
};
|
||||
|
||||
private _leaderPool = _unitPool select {
|
||||
toUpperANSI (_x getOrDefault ["rank", "PRIVATE"]) in ["SERGEANT", "LIEUTENANT", "CAPTAIN", "MAJOR", "COLONEL"]
|
||||
};
|
||||
if (_leaderPool isEqualTo []) then { _leaderPool = +_unitPool; };
|
||||
|
||||
private _spawnDefs = [selectRandom _leaderPool];
|
||||
for "_i" from 1 to (_targetUnitCount - 1) do {
|
||||
_spawnDefs pushBack (selectRandom _unitPool);
|
||||
};
|
||||
|
||||
{
|
||||
private _unitClass = _x getOrDefault ["vehicle", ""];
|
||||
if (_unitClass isEqualTo "") then { continue; };
|
||||
|
||||
private _unitOffset = +(_x getOrDefault ["position", [0, 0, 0]]);
|
||||
if (count _unitOffset < 3) then { _unitOffset resize 3; };
|
||||
_unitOffset set [0, (_unitOffset # 0) + (random 6 - 3)];
|
||||
_unitOffset set [1, (_unitOffset # 1) + (random 6 - 3)];
|
||||
|
||||
private _unit = _group createUnit [_unitClass, _position vectorAdd _unitOffset, [], 0, "NONE"];
|
||||
_unit setRank (_x getOrDefault ["rank", "PRIVATE"]);
|
||||
} forEach _spawnDefs;
|
||||
|
||||
[_group, _position, _patrolRadius] call BFUNC(taskPatrol);
|
||||
|
||||
["INFO", format [
|
||||
"Attack mission generator: spawned attack group. Side=%1, Units=%2, PatrolRadius=%3, Position=%4",
|
||||
_side,
|
||||
count (units _group),
|
||||
_patrolRadius,
|
||||
_position
|
||||
]] call EFUNC(common,log);
|
||||
_group
|
||||
}],
|
||||
["rollRewards", compileFinal {
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _equipmentRewards = [];
|
||||
private _supplyRewards = [];
|
||||
private _weaponRewards = [];
|
||||
private _vehicleRewards = [];
|
||||
private _specialRewards = [];
|
||||
|
||||
{
|
||||
private _category = _x;
|
||||
{
|
||||
_x params ["_item", "_chance"];
|
||||
if (random 1 < _chance) then {
|
||||
switch (_category) do {
|
||||
case "equipment": { _equipmentRewards pushBack _item; };
|
||||
case "supplies": { _supplyRewards pushBack _item; };
|
||||
case "weapons": { _weaponRewards pushBack _item; };
|
||||
case "vehicles": { _vehicleRewards pushBack _item; };
|
||||
case "special": { _specialRewards pushBack _item; };
|
||||
};
|
||||
};
|
||||
} forEach (getArray (_attackConfig >> "Rewards" >> _category));
|
||||
} forEach ["equipment", "supplies", "weapons", "vehicles", "special"];
|
||||
|
||||
createHashMapFromArray [
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
}],
|
||||
["startMission", compileFinal {
|
||||
params [["_manager", createHashMapObject [createHashMapFromArray []], [createHashMap]]];
|
||||
|
||||
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
|
||||
private _locationData = _self call ["selectLocation", [_manager]];
|
||||
if (_locationData isEqualTo createHashMap) exitWith { "" };
|
||||
|
||||
private _position = _locationData getOrDefault ["position", [0, 0, 0]];
|
||||
private _grid = _locationData getOrDefault ["grid", mapGridPosition _position];
|
||||
|
||||
["INFO", format [
|
||||
"Attack mission generator: selected location. Grid=%1, Position=%2",
|
||||
_grid,
|
||||
_position
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
private _group = _self call ["spawnAttackGroup", [_position]];
|
||||
if (isNull _group) exitWith {
|
||||
["WARNING", format [
|
||||
"Attack mission generator: spawnAttackGroup failed for Grid=%1, Position=%2",
|
||||
_grid,
|
||||
_position
|
||||
]] call EFUNC(common,log);
|
||||
""
|
||||
};
|
||||
|
||||
private _units = units _group;
|
||||
if (_units isEqualTo []) exitWith {
|
||||
["WARNING", format [
|
||||
"Attack mission generator: spawned group has no units. Grid=%1, Group=%2",
|
||||
_grid,
|
||||
_group
|
||||
]] call EFUNC(common,log);
|
||||
deleteGroup _group;
|
||||
""
|
||||
};
|
||||
|
||||
private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)];
|
||||
private _rewardRange = getArray (_attackConfig >> "Rewards" >> "money");
|
||||
private _reputationRange = getArray (_attackConfig >> "Rewards" >> "reputation");
|
||||
private _penaltyRange = getArray (_attackConfig >> "penalty");
|
||||
private _timeRange = getArray (_attackConfig >> "timeLimit");
|
||||
private _rewards = _self call ["rollRewards"];
|
||||
private _fundsReward = _rewardRange call BFUNC(randomNum);
|
||||
private _reputationReward = _reputationRange call BFUNC(randomNum);
|
||||
private _reputationPenalty = _penaltyRange call BFUNC(randomNum);
|
||||
private _timeLimit = _timeRange call BFUNC(randomNum);
|
||||
|
||||
["INFO", format [
|
||||
"Attack mission generator: creating task. TaskID=%1, Grid=%2, Units=%3",
|
||||
_taskID,
|
||||
_grid,
|
||||
count _units
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
private _success = [
|
||||
"attack",
|
||||
_taskID,
|
||||
_position,
|
||||
format ["Attack: Grid %1", _grid],
|
||||
format ["Eliminate hostile forces operating near grid %1.", _grid],
|
||||
createHashMapFromArray [["targets", _units]],
|
||||
createHashMapFromArray [
|
||||
["limitFail", 0],
|
||||
["limitSuccess", count _units],
|
||||
["funds", _fundsReward],
|
||||
["ratingFail", _reputationPenalty],
|
||||
["ratingSuccess", _reputationReward],
|
||||
["endSuccess", false],
|
||||
["endFail", false],
|
||||
["timeLimit", _timeLimit],
|
||||
["equipment", _rewards get "equipment"],
|
||||
["supplies", _rewards get "supplies"],
|
||||
["weapons", _rewards get "weapons"],
|
||||
["vehicles", _rewards get "vehicles"],
|
||||
["special", _rewards get "special"]
|
||||
],
|
||||
0,
|
||||
"",
|
||||
"mission_manager"
|
||||
] call FUNC(startTask);
|
||||
|
||||
if !(_success) exitWith {
|
||||
["WARNING", format [
|
||||
"Attack mission generator: startTask failed. TaskID=%1, Grid=%2, Units=%3",
|
||||
_taskID,
|
||||
_grid,
|
||||
count _units
|
||||
]] call EFUNC(common,log);
|
||||
""
|
||||
};
|
||||
|
||||
["INFO", format [
|
||||
"Attack mission generator: task registered. TaskID=%1, Source=mission_manager, TimeLimit=%2s, LimitSuccess=%3",
|
||||
_taskID,
|
||||
_timeLimit,
|
||||
count _units
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
private _activeMissionRegistry = _manager getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
_activeMissionRegistry set [_taskID, createHashMapFromArray [
|
||||
["generatorType", _self call ["getGeneratorType", []]],
|
||||
["position", _position],
|
||||
["startedAt", serverTime]
|
||||
]];
|
||||
_manager set ["activeMissionRegistry", _activeMissionRegistry];
|
||||
|
||||
["INFO", format [
|
||||
"Attack mission generator: mission started successfully. TaskID=%1, Grid=%2",
|
||||
_taskID,
|
||||
_grid
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
_taskID
|
||||
}],
|
||||
["completeMission", compileFinal {
|
||||
params [
|
||||
["_manager", createHashMapObject [createHashMapFromArray []], [createHashMap]],
|
||||
["_taskID", "", [""]]
|
||||
];
|
||||
|
||||
if (_taskID isEqualTo "") exitWith { false };
|
||||
|
||||
private _activeMissionRegistry = _manager getOrDefault ["activeMissionRegistry", createHashMap];
|
||||
private _missionRecord = _activeMissionRegistry getOrDefault [_taskID, createHashMap];
|
||||
if ((_missionRecord getOrDefault ["generatorType", ""]) isNotEqualTo (_self call ["getGeneratorType", []])) exitWith { false };
|
||||
|
||||
private _position = _missionRecord getOrDefault ["position", []];
|
||||
_activeMissionRegistry deleteAt _taskID;
|
||||
_manager set ["activeMissionRegistry", _activeMissionRegistry];
|
||||
|
||||
if (_position isEqualType [] && { count _position >= 2 }) then {
|
||||
private _recentLocationRegistry = _manager getOrDefault ["recentLocationRegistry", []];
|
||||
_recentLocationRegistry pushBack [_position, serverTime];
|
||||
_manager set ["recentLocationRegistry", _recentLocationRegistry];
|
||||
};
|
||||
|
||||
true
|
||||
}]
|
||||
];
|
||||
@ -0,0 +1,40 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Author: OpenAI
|
||||
* Parses an Eden module reward-array string into a SQF array.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Raw value <STRING>
|
||||
* 1: Task label <STRING>
|
||||
* 2: Reward key <STRING>
|
||||
*
|
||||
* Return Value:
|
||||
* Parsed reward array <ARRAY>
|
||||
*
|
||||
* Example:
|
||||
* [_logic getVariable ["EquipmentRewards", "[]"], "attack_01", "equipment"] call forge_server_task_fnc_parseRewards;
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params [
|
||||
["_rawValue", "[]", [""]],
|
||||
["_taskLabel", "", [""]],
|
||||
["_rewardKey", "", [""]]
|
||||
];
|
||||
|
||||
private _trimmed = trim _rawValue;
|
||||
if (_trimmed isEqualTo "") exitWith { [] };
|
||||
|
||||
private _parsed = parseSimpleArray _trimmed;
|
||||
if (_parsed isEqualType []) exitWith { _parsed };
|
||||
|
||||
["WARNING", format [
|
||||
"Task module '%1' reward input '%2' is invalid: %3. Expected SQF array syntax like [""ItemGPS"",""FirstAidKit""].",
|
||||
_taskLabel,
|
||||
_rewardKey,
|
||||
_rawValue
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
[]
|
||||
@ -36,6 +36,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"attack",
|
||||
_taskID,
|
||||
@ -53,7 +59,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
|
||||
["endSuccess", _logic getVariable ["EndSuccess", false]],
|
||||
["endFail", _logic getVariable ["EndFail", false]],
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]]
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -42,6 +42,12 @@ if (_defenseZone isEqualTo "" || { markerShape _defenseZone isEqualTo "" }) exit
|
||||
_logic getVariable ["MinBlufor", 1]
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"defend",
|
||||
_taskID,
|
||||
@ -59,7 +65,12 @@ if (_defenseZone isEqualTo "" || { markerShape _defenseZone isEqualTo "" }) exit
|
||||
["defendTime", _logic getVariable ["DefendTime", 600]],
|
||||
["waveCount", _logic getVariable ["WaveCount", 3]],
|
||||
["waveCooldown", _logic getVariable ["WaveCooldown", 300]],
|
||||
["minBlufor", _logic getVariable ["MinBlufor", 1]]
|
||||
["minBlufor", _logic getVariable ["MinBlufor", 1]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -53,6 +53,12 @@ private _taskPos = if (_iedEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"defuse",
|
||||
_taskID,
|
||||
@ -71,7 +77,12 @@ private _taskPos = if (_iedEntities isNotEqualTo []) then {
|
||||
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
|
||||
["endSuccess", _logic getVariable ["EndSuccess", false]],
|
||||
["endFail", _logic getVariable ["EndFail", false]],
|
||||
["iedTimer", _logic getVariable ["TimeLimit", 300]]
|
||||
["iedTimer", _logic getVariable ["TimeLimit", 300]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -46,6 +46,12 @@ private _taskPos = if (_cargoEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"delivery",
|
||||
_taskID,
|
||||
@ -64,7 +70,12 @@ private _taskPos = if (_cargoEntities isNotEqualTo []) then {
|
||||
["endSuccess", _logic getVariable ["EndSuccess", false]],
|
||||
["endFail", _logic getVariable ["EndFail", false]],
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]],
|
||||
["deliveryZone", _logic getVariable ["DeliveryZone", ""]]
|
||||
["deliveryZone", _logic getVariable ["DeliveryZone", ""]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -36,6 +36,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"destroy",
|
||||
_taskID,
|
||||
@ -53,7 +59,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
|
||||
["endSuccess", _logic getVariable ["EndSuccess", false]],
|
||||
["endFail", _logic getVariable ["EndFail", false]],
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]]
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -61,6 +61,12 @@ private _taskPos = if (_hostageEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"hostage",
|
||||
_taskID,
|
||||
@ -83,7 +89,12 @@ private _taskPos = if (_hostageEntities isNotEqualTo []) then {
|
||||
["extractionZone", _logic getVariable ["ExtZone", ""]],
|
||||
["cbrn", _logic getVariable ["CBRN", false]],
|
||||
["execution", _logic getVariable ["Execution", false]],
|
||||
["cbrnZone", _logic getVariable ["CBRNZone", ""]]
|
||||
["cbrnZone", _logic getVariable ["CBRNZone", ""]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -42,6 +42,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
getPosATL _logic
|
||||
};
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "supplies"] call FUNC(parseRewards);
|
||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||
|
||||
[
|
||||
"hvt",
|
||||
_taskID,
|
||||
@ -61,7 +67,12 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then {
|
||||
["endFail", _logic getVariable ["EndFail", false]],
|
||||
["timeLimit", _logic getVariable ["TimeLimit", 0]],
|
||||
["extractionZone", _logic getVariable ["ExtZone", ""]],
|
||||
["captureHvt", _logic getVariable ["CaptureHVT", true]]
|
||||
["captureHvt", _logic getVariable ["CaptureHVT", true]],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
["vehicles", _vehicleRewards],
|
||||
["special", _specialRewards]
|
||||
]
|
||||
] call FUNC(startTask);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# Task Object Prototypes
|
||||
|
||||
This folder contains review-only `createHashMapObject` prototypes for task
|
||||
instances. They are not part of runtime initialization.
|
||||
instances. Their source now lives under `functions/prototypes/`, and they are
|
||||
loaded for review with `[] call forge_server_task_fnc_initPrototypes;`.
|
||||
|
||||
Current prototypes:
|
||||
- `TaskInstanceBaseClass`
|
||||
@ -11,14 +12,15 @@ Current prototypes:
|
||||
- `HostageEntityController`
|
||||
- `DefuseTaskBaseClass`
|
||||
|
||||
Source:
|
||||
Review entry points:
|
||||
- [taskObjectPrototypes.sqf](./taskObjectPrototypes.sqf)
|
||||
- [TaskInstanceBaseClass.sqf](./TaskInstanceBaseClass.sqf)
|
||||
- [EntityControllerBaseClass.sqf](./EntityControllerBaseClass.sqf)
|
||||
- [AttackTaskBaseClass.sqf](./AttackTaskBaseClass.sqf)
|
||||
- [HostageTaskBaseClass.sqf](./HostageTaskBaseClass.sqf)
|
||||
- [HostageEntityController.sqf](./HostageEntityController.sqf)
|
||||
- [DefuseTaskBaseClass.sqf](./DefuseTaskBaseClass.sqf)
|
||||
- [fnc_initPrototypes.sqf](../functions/prototypes/fnc_initPrototypes.sqf)
|
||||
- [fnc_TaskInstanceBaseClass.sqf](../functions/prototypes/fnc_TaskInstanceBaseClass.sqf)
|
||||
- [fnc_EntityControllerBaseClass.sqf](../functions/prototypes/fnc_EntityControllerBaseClass.sqf)
|
||||
- [fnc_AttackTaskBaseClass.sqf](../functions/prototypes/fnc_AttackTaskBaseClass.sqf)
|
||||
- [fnc_HostageTaskBaseClass.sqf](../functions/prototypes/fnc_HostageTaskBaseClass.sqf)
|
||||
- [fnc_HostageEntityController.sqf](../functions/prototypes/fnc_HostageEntityController.sqf)
|
||||
- [fnc_DefuseTaskBaseClass.sqf](../functions/prototypes/fnc_DefuseTaskBaseClass.sqf)
|
||||
|
||||
Purpose:
|
||||
- show what per-task instance objects could look like
|
||||
@ -60,15 +60,12 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
_self set ["requiredKills", _requiredKills];
|
||||
_self set ["maxTargetLosses", _maxTargetLosses];
|
||||
_self set ["timeLimit", _taskParams getOrDefault ["timeLimit", 0]];
|
||||
_self set ["useTaskStore", _taskParams getOrDefault ["useTaskStore", false]];
|
||||
|
||||
// Review-only registry entry to demonstrate where #delete is useful.
|
||||
missionNamespace setVariable [_taskID, _self];
|
||||
_self call ["registerInstance", []];
|
||||
}],
|
||||
["#delete", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isNotEqualTo "") then {
|
||||
missionNamespace setVariable [_taskID, nil];
|
||||
};
|
||||
_self call ["unregisterInstance", []];
|
||||
}],
|
||||
["countKilledTargets", compileFinal {
|
||||
private _targets = _self getOrDefault ["targets", []];
|
||||
@ -105,14 +102,22 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
private _funds = _rewardData getOrDefault ["funds", 0];
|
||||
private _endFail = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endFail", false];
|
||||
private _endSuccess = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endSuccess", false];
|
||||
private _useTaskStore = _self getOrDefault ["useTaskStore", false];
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
count _targets > 0
|
||||
if (_useTaskStore) then {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
count _targets > 0
|
||||
};
|
||||
} else {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
count _targets > 0
|
||||
};
|
||||
};
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
if (_timeLimit isNotEqualTo 0 && { _useTaskStore }) then {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["isTaskAccepted", [_taskID]]
|
||||
@ -122,7 +127,9 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
_self call ["markActive", []];
|
||||
|
||||
while { (_self call ["getStatus", []]) isEqualTo "active" } do {
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
if (_useTaskStore) then {
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]];
|
||||
};
|
||||
|
||||
private _snapshot = _self call ["tick", []];
|
||||
|
||||
@ -140,32 +147,37 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
if ((_self call ["getStatus", []]) isEqualTo "failed") then {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
if (_useTaskStore) then {
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExec ["BIS_fnc_endMission", playerSide]; };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
[_taskID, _rewardData] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
if (_useTaskStore) then {
|
||||
[_taskID, _rewardData] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_funds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_funds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExec ["BIS_fnc_endMission", playerSide]; };
|
||||
};
|
||||
|
||||
_self call ["cleanup", []];
|
||||
true
|
||||
}]
|
||||
];
|
||||
@ -62,15 +62,13 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
||||
_self set ["requiredDefusals", _requiredDefusals];
|
||||
_self set ["maxProtectedLosses", _maxProtectedLosses];
|
||||
_self set ["iedTimer", _taskParams getOrDefault ["iedTimer", 300]];
|
||||
_self set ["useTaskStore", _taskParams getOrDefault ["useTaskStore", false]];
|
||||
_self set ["localDefuseCount", _taskParams getOrDefault ["localDefuseCount", 0]];
|
||||
|
||||
// Review-only registry entry to demonstrate where #delete is useful.
|
||||
missionNamespace setVariable [_taskID, _self];
|
||||
_self call ["registerInstance", []];
|
||||
}],
|
||||
["#delete", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isNotEqualTo "") then {
|
||||
missionNamespace setVariable [_taskID, nil];
|
||||
};
|
||||
_self call ["unregisterInstance", []];
|
||||
}],
|
||||
["countProtectedDestroyed", compileFinal {
|
||||
private _protected = _self getOrDefault ["protected", []];
|
||||
@ -79,9 +77,17 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
||||
["getDefuseCount", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isEqualTo "") exitWith { 0 };
|
||||
if !(_self getOrDefault ["useTaskStore", false]) exitWith {
|
||||
_self getOrDefault ["localDefuseCount", 0]
|
||||
};
|
||||
|
||||
GVAR(TaskStore) call ["getDefuseCount", [_taskID]]
|
||||
}],
|
||||
["incrementLocalDefuseCount", compileFinal {
|
||||
private _next = (_self getOrDefault ["localDefuseCount", 0]) + 1;
|
||||
_self set ["localDefuseCount", _next];
|
||||
_next
|
||||
}],
|
||||
["tick", compileFinal {
|
||||
private _defusedCount = _self call ["getDefuseCount", []];
|
||||
private _protectedDestroyed = _self call ["countProtectedDestroyed", []];
|
||||
@ -114,6 +120,7 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
||||
sleep 1;
|
||||
};
|
||||
|
||||
_self call ["cleanup", []];
|
||||
true
|
||||
}]
|
||||
];
|
||||
@ -63,6 +63,30 @@ GVAR(EntityControllerBaseClass) = createHashMapFromArray [
|
||||
["getEntity", compileFinal {
|
||||
_self getOrDefault ["entity", objNull]
|
||||
}],
|
||||
["getRegistryKey", compileFinal {
|
||||
private _entity = _self getOrDefault ["entity", objNull];
|
||||
if (isNull _entity) exitWith { "" };
|
||||
|
||||
format ["hostage_controller_%1", netId _entity]
|
||||
}],
|
||||
["registerInstance", compileFinal {
|
||||
private _registryKey = _self call ["getRegistryKey", []];
|
||||
if (_registryKey isEqualTo "") exitWith { false };
|
||||
|
||||
private _registry = missionNamespace getVariable [QGVAR(PrototypeControllerInstances), createHashMap];
|
||||
_registry set [_registryKey, _self];
|
||||
missionNamespace setVariable [_registryKey, _self];
|
||||
true
|
||||
}],
|
||||
["unregisterInstance", compileFinal {
|
||||
private _registryKey = _self call ["getRegistryKey", []];
|
||||
if (_registryKey isEqualTo "") exitWith { false };
|
||||
|
||||
private _registry = missionNamespace getVariable [QGVAR(PrototypeControllerInstances), createHashMap];
|
||||
_registry deleteAt _registryKey;
|
||||
missionNamespace setVariable [_registryKey, nil];
|
||||
true
|
||||
}],
|
||||
["markActive", compileFinal {
|
||||
_self set ["status", "active"];
|
||||
_self set ["startedAt", serverTime];
|
||||
@ -79,7 +103,7 @@ GVAR(EntityControllerBaseClass) = createHashMapFromArray [
|
||||
true
|
||||
}],
|
||||
["cleanup", compileFinal {
|
||||
false
|
||||
_self call ["unregisterInstance", []]
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
false
|
||||
@ -47,18 +47,10 @@ GVAR(HostageEntityController) = createHashMapFromArray [
|
||||
_self set ["loopAnimation", _controllerParams getOrDefault ["loopAnimation", "acts_executionvictim_loop"]];
|
||||
_self set ["rescueAnimation", _controllerParams getOrDefault ["rescueAnimation", "acts_executionvictim_unbow"]];
|
||||
|
||||
private _netID = if (isNull _entity) then { "" } else { netId _entity };
|
||||
if (_netID isNotEqualTo "") then {
|
||||
private _controllerKey = format ["hostage_controller_%1", _netID];
|
||||
missionNamespace setVariable [_controllerKey, _self];
|
||||
};
|
||||
_self call ["registerInstance", []];
|
||||
}],
|
||||
["#delete", compileFinal {
|
||||
private _entity = _self getOrDefault ["entity", objNull];
|
||||
if (!isNull _entity) then {
|
||||
private _controllerKey = format ["hostage_controller_%1", netId _entity];
|
||||
missionNamespace setVariable [_controllerKey, nil];
|
||||
};
|
||||
_self call ["unregisterInstance", []];
|
||||
}],
|
||||
["applyInitialState", compileFinal {
|
||||
private _entity = _self getOrDefault ["entity", objNull];
|
||||
@ -95,6 +87,7 @@ GVAR(HostageEntityController) = createHashMapFromArray [
|
||||
private _entity = _self getOrDefault ["entity", objNull];
|
||||
if (isNull _entity) exitWith {
|
||||
_self call ["markAborted", []];
|
||||
_self call ["cleanup", []];
|
||||
false
|
||||
};
|
||||
|
||||
@ -102,6 +95,7 @@ GVAR(HostageEntityController) = createHashMapFromArray [
|
||||
|
||||
if !(_self call ["applyInitialState", []]) exitWith {
|
||||
_self call ["markAborted", []];
|
||||
_self call ["cleanup", []];
|
||||
false
|
||||
};
|
||||
|
||||
@ -117,11 +111,13 @@ GVAR(HostageEntityController) = createHashMapFromArray [
|
||||
|
||||
if (isNull _entity || { !alive _entity }) exitWith {
|
||||
_self call ["markAborted", []];
|
||||
_self call ["cleanup", []];
|
||||
false
|
||||
};
|
||||
|
||||
_self call ["transitionToRescued", [_rescuer]];
|
||||
_self call ["markFinished", []];
|
||||
_self call ["cleanup", []];
|
||||
true
|
||||
}]
|
||||
];
|
||||
@ -67,20 +67,17 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
_self set ["execution", _taskParams getOrDefault ["execution", false]];
|
||||
_self set ["cbrn", _taskParams getOrDefault ["cbrn", false]];
|
||||
_self set ["cbrnZone", _taskParams getOrDefault ["cbrnZone", ""]];
|
||||
_self set ["useTaskStore", _taskParams getOrDefault ["useTaskStore", false]];
|
||||
_self set ["requiredRescues", _requiredRescues];
|
||||
_self set ["maxHostageLosses", _maxHostageLosses];
|
||||
_self set ["hostageControllers", []];
|
||||
|
||||
_self call ["createHostageControllers", []];
|
||||
|
||||
// Review-only registry entry to demonstrate where #delete is useful.
|
||||
missionNamespace setVariable [_taskID, _self];
|
||||
_self call ["registerInstance", []];
|
||||
}],
|
||||
["#delete", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isNotEqualTo "") then {
|
||||
missionNamespace setVariable [_taskID, nil];
|
||||
};
|
||||
_self call ["unregisterInstance", []];
|
||||
}],
|
||||
["createHostageControllers", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
@ -119,7 +116,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
}],
|
||||
["refreshEntitiesFromStore", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isEqualTo "") exitWith { false };
|
||||
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { false };
|
||||
|
||||
private _hostages = GVAR(TaskStore) call ["getTaskEntities", ["hostages", _taskID]];
|
||||
private _shooters = GVAR(TaskStore) call ["getTaskEntities", ["shooters", _taskID]];
|
||||
@ -130,7 +127,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
}],
|
||||
["trackParticipants", compileFinal {
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
if (_taskID isEqualTo "") exitWith { false };
|
||||
if (_taskID isEqualTo "" || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { false };
|
||||
|
||||
private _hostages = _self getOrDefault ["hostages", []];
|
||||
private _shooters = _self getOrDefault ["shooters", []];
|
||||
@ -140,17 +137,29 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
true
|
||||
}],
|
||||
["waitForRequiredEntities", compileFinal {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_self call ["refreshEntitiesFromStore", []];
|
||||
count (_self getOrDefault ["hostages", []]) > 0
|
||||
};
|
||||
if (_self getOrDefault ["useTaskStore", false]) then {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_self call ["refreshEntitiesFromStore", []];
|
||||
count (_self getOrDefault ["hostages", []]) > 0
|
||||
};
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_self call ["refreshEntitiesFromStore", []];
|
||||
_self call ["trackParticipants", []];
|
||||
count (_self getOrDefault ["shooters", []]) > 0
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
_self call ["refreshEntitiesFromStore", []];
|
||||
_self call ["trackParticipants", []];
|
||||
count (_self getOrDefault ["shooters", []]) > 0
|
||||
};
|
||||
} else {
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
count (_self getOrDefault ["hostages", []]) > 0
|
||||
};
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
count (_self getOrDefault ["shooters", []]) > 0
|
||||
};
|
||||
};
|
||||
|
||||
private _hostages = _self getOrDefault ["hostages", []];
|
||||
@ -169,7 +178,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
private _timeLimit = _self getOrDefault ["timeLimit", 0];
|
||||
private _taskID = _self getOrDefault ["taskID", ""];
|
||||
|
||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" }) exitWith { true };
|
||||
if (_timeLimit <= 0 || { _taskID isEqualTo "" } || { !(_self getOrDefault ["useTaskStore", false]) }) exitWith { true };
|
||||
|
||||
waitUntil {
|
||||
sleep 1;
|
||||
@ -238,6 +247,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
private _cbrn = _self getOrDefault ["cbrn", false];
|
||||
private _hostage = _self getOrDefault ["execution", false];
|
||||
private _cbrnZone = _self getOrDefault ["cbrnZone", ""];
|
||||
private _useTaskStore = _self getOrDefault ["useTaskStore", false];
|
||||
|
||||
if (_cbrn && { _cbrnZone isNotEqualTo "" }) then {
|
||||
"SmokeShellYellow" createVehicle getMarkerPos _cbrnZone;
|
||||
@ -272,13 +282,15 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
{ deleteVehicle _x } forEach _hostages;
|
||||
{ deleteVehicle _x } forEach _shooters;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
if (_useTaskStore) then {
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
|
||||
sleep 1;
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
true
|
||||
@ -291,18 +303,21 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
private _ratingSuccess = _rewardData getOrDefault ["ratingSuccess", 0];
|
||||
private _funds = _rewardData getOrDefault ["funds", 0];
|
||||
private _endSuccess = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endSuccess", false];
|
||||
private _useTaskStore = _self getOrDefault ["useTaskStore", false];
|
||||
|
||||
{ deleteVehicle _x } forEach _hostages;
|
||||
{ deleteVehicle _x } forEach _shooters;
|
||||
|
||||
[_taskID, _rewardData] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
if (_useTaskStore) then {
|
||||
[_taskID, _rewardData] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
|
||||
sleep 1;
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_funds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_funds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
true
|
||||
@ -334,6 +349,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
_self call ["handleSuccessOutcome", []];
|
||||
};
|
||||
|
||||
_self call ["cleanup", []];
|
||||
true
|
||||
}]
|
||||
];
|
||||
@ -86,6 +86,27 @@ GVAR(TaskInstanceBaseClass) = createHashMapFromArray [
|
||||
["getRewardData", compileFinal {
|
||||
_self getOrDefault ["rewardData", createHashMap]
|
||||
}],
|
||||
["getRegistryKey", compileFinal {
|
||||
_self getOrDefault ["taskID", ""]
|
||||
}],
|
||||
["registerInstance", compileFinal {
|
||||
private _registryKey = _self call ["getRegistryKey", []];
|
||||
if (_registryKey isEqualTo "") exitWith { false };
|
||||
|
||||
private _registry = missionNamespace getVariable [QGVAR(PrototypeTaskInstances), createHashMap];
|
||||
_registry set [_registryKey, _self];
|
||||
missionNamespace setVariable [_registryKey, _self];
|
||||
true
|
||||
}],
|
||||
["unregisterInstance", compileFinal {
|
||||
private _registryKey = _self call ["getRegistryKey", []];
|
||||
if (_registryKey isEqualTo "") exitWith { false };
|
||||
|
||||
private _registry = missionNamespace getVariable [QGVAR(PrototypeTaskInstances), createHashMap];
|
||||
_registry deleteAt _registryKey;
|
||||
missionNamespace setVariable [_registryKey, nil];
|
||||
true
|
||||
}],
|
||||
["markActive", compileFinal {
|
||||
_self set ["status", "active"];
|
||||
_self set ["startedAt", serverTime];
|
||||
@ -109,7 +130,7 @@ GVAR(TaskInstanceBaseClass) = createHashMapFromArray [
|
||||
true
|
||||
}],
|
||||
["cleanup", compileFinal {
|
||||
false
|
||||
_self call ["unregisterInstance", []]
|
||||
}],
|
||||
["tick", compileFinal {
|
||||
createHashMap
|
||||
@ -0,0 +1,42 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Review-only prototype initializer for object-based task instances.
|
||||
*
|
||||
* Usage in debug/testing:
|
||||
* private _prototypes = [] call FUNC(initPrototypes);
|
||||
*
|
||||
* private _task = createHashMapObject [
|
||||
* _prototypes get "HostageTaskBaseClass",
|
||||
* [
|
||||
* "task_hostage_review",
|
||||
* createHashMapFromArray [
|
||||
* ["hostages", [hostage1, hostage2]],
|
||||
* ["shooters", [shooter1, shooter2]]
|
||||
* ],
|
||||
* createHashMapFromArray [
|
||||
* ["extractionZone", "hostage_extract"],
|
||||
* ["limitSuccess", 2],
|
||||
* ["limitFail", 1],
|
||||
* ["execution", true],
|
||||
* ["timeLimit", 900]
|
||||
* ]
|
||||
* ]
|
||||
* ];
|
||||
*/
|
||||
|
||||
[] call FUNC(TaskInstanceBaseClass);
|
||||
[] call FUNC(EntityControllerBaseClass);
|
||||
[] call FUNC(AttackTaskBaseClass);
|
||||
[] call FUNC(HostageTaskBaseClass);
|
||||
[] call FUNC(HostageEntityController);
|
||||
[] call FUNC(DefuseTaskBaseClass);
|
||||
|
||||
createHashMapFromArray [
|
||||
["TaskInstanceBaseClass", GVAR(TaskInstanceBaseClass)],
|
||||
["EntityControllerBaseClass", GVAR(EntityControllerBaseClass)],
|
||||
["AttackTaskBaseClass", GVAR(AttackTaskBaseClass)],
|
||||
["HostageTaskBaseClass", GVAR(HostageTaskBaseClass)],
|
||||
["HostageEntityController", GVAR(HostageEntityController)],
|
||||
["DefuseTaskBaseClass", GVAR(DefuseTaskBaseClass)]
|
||||
]
|
||||
1
arma/server/addons/task/functions/script_component.hpp
Normal file
1
arma/server/addons/task/functions/script_component.hpp
Normal file
@ -0,0 +1 @@
|
||||
#include "..\script_component.hpp"
|
||||
@ -1,47 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* Review-only prototype loader for object-based task instances.
|
||||
*
|
||||
* This file is intentionally not referenced by XEH_PREP or runtime init.
|
||||
* It exists so the current procedural task flows can be compared against
|
||||
* a createHashMapObject-based design before any live refactor is attempted.
|
||||
*
|
||||
* Usage in debug/testing:
|
||||
* private _prototypes = call compile preprocessFileLineNumbers
|
||||
* "\forge\forge_server\addons\task\prototypes\taskObjectPrototypes.sqf";
|
||||
*
|
||||
* private _task = createHashMapObject [
|
||||
* _prototypes get "HostageTaskBaseClass",
|
||||
* [
|
||||
* "task_hostage_review",
|
||||
* createHashMapFromArray [
|
||||
* ["hostages", [hostage1, hostage2]],
|
||||
* ["shooters", [shooter1, shooter2]]
|
||||
* ],
|
||||
* createHashMapFromArray [
|
||||
* ["extractionZone", "hostage_extract"],
|
||||
* ["limitSuccess", 2],
|
||||
* ["limitFail", 1],
|
||||
* ["execution", true],
|
||||
* ["timeLimit", 900]
|
||||
* ]
|
||||
* ]
|
||||
* ];
|
||||
*/
|
||||
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf";
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\EntityControllerBaseClass.sqf";
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\AttackTaskBaseClass.sqf";
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\HostageTaskBaseClass.sqf";
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\HostageEntityController.sqf";
|
||||
call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\DefuseTaskBaseClass.sqf";
|
||||
|
||||
createHashMapFromArray [
|
||||
["TaskInstanceBaseClass", GVAR(TaskInstanceBaseClass)],
|
||||
["EntityControllerBaseClass", GVAR(EntityControllerBaseClass)],
|
||||
["AttackTaskBaseClass", GVAR(AttackTaskBaseClass)],
|
||||
["HostageTaskBaseClass", GVAR(HostageTaskBaseClass)],
|
||||
["HostageEntityController", GVAR(HostageEntityController)],
|
||||
["DefuseTaskBaseClass", GVAR(DefuseTaskBaseClass)]
|
||||
]
|
||||
@ -7,3 +7,35 @@
|
||||
// #define ENABLE_PERFORMANCE_COUNTERS
|
||||
|
||||
#include "\forge\forge_server\addons\main\script_macros.hpp"
|
||||
|
||||
#define REWARD_ARRAY_ATTRIBUTES(PREFIX) \
|
||||
class EquipmentRewards: Edit { \
|
||||
property = QUOTE(DOUBLES(PREFIX,EquipmentRewards)); \
|
||||
displayName = "Equipment Rewards"; \
|
||||
tooltip = "SQF array string for equipment rewards, e.g. [""ItemGPS"",""ItemCompass""]"; \
|
||||
typeName = "STRING"; \
|
||||
}; \
|
||||
class SupplyRewards: Edit { \
|
||||
property = QUOTE(DOUBLES(PREFIX,SupplyRewards)); \
|
||||
displayName = "Supply Rewards"; \
|
||||
tooltip = "SQF array string for supply rewards, e.g. [""FirstAidKit"",""Medikit""]"; \
|
||||
typeName = "STRING"; \
|
||||
}; \
|
||||
class WeaponRewards: Edit { \
|
||||
property = QUOTE(DOUBLES(PREFIX,WeaponRewards)); \
|
||||
displayName = "Weapon Rewards"; \
|
||||
tooltip = "SQF array string for weapon rewards, e.g. [""arifle_MX_F""]"; \
|
||||
typeName = "STRING"; \
|
||||
}; \
|
||||
class VehicleRewards: Edit { \
|
||||
property = QUOTE(DOUBLES(PREFIX,VehicleRewards)); \
|
||||
displayName = "Vehicle Rewards"; \
|
||||
tooltip = "SQF array string for vehicle rewards, e.g. [""B_MRAP_01_F""]"; \
|
||||
typeName = "STRING"; \
|
||||
}; \
|
||||
class SpecialRewards: Edit { \
|
||||
property = QUOTE(DOUBLES(PREFIX,SpecialRewards)); \
|
||||
displayName = "Special Rewards"; \
|
||||
tooltip = "SQF array string for special rewards, e.g. [""B_UAV_01_F""]"; \
|
||||
typeName = "STRING"; \
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user