mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-31 00:15:54 -04:00
3584 lines
96 KiB
Plaintext
3584 lines
96 KiB
Plaintext
/***** INCLUDES ********************************************************/
|
|
|
|
include ai.ai_combat;
|
|
include library.buff;
|
|
include library.colors_hex;
|
|
include library.factions;
|
|
include library.faction_perk;
|
|
include library.loot;
|
|
include library.pet_lib;
|
|
include library.prose;
|
|
include library.regions;
|
|
include library.space_flags;
|
|
include library.trial;
|
|
include library.utils;
|
|
|
|
/***** CONSTANTS *******************************************************/
|
|
|
|
const int GCW_UPDATE_PULSE = 300;
|
|
const float DECAY_PER_UPDATE = .02f;
|
|
|
|
const string SCRIPTVAR_SCAN_INTEREST = "scan.interest";
|
|
|
|
//DATATABLES
|
|
const string REGION_SPAWN_WEIGHT_PATH = "datatables/spawning/imperial_presence/presence_weight/";
|
|
const string TABLE_PLAYER_BASE_CAP = "datatables/faction_perk/hq/hq_planetary_placement_cap.iff";
|
|
|
|
//CRACKDOWN INTERESTED
|
|
const int INTEREST_FACTION = 0;
|
|
const int INTEREST_SOMETHING = 1;
|
|
const int INTEREST_SPICE = 2;
|
|
const int INTEREST_SLICED_ITEMS = 3;
|
|
const int INTEREST_FACTION_ITEMS = 4;
|
|
const int INTEREST_CONTRABAND = 28; //INTEREST_SPICE, INTEREST_SLICED, INTEREST_FACTION_ITEMS
|
|
|
|
//LAMBDA SPAWN ACTION CODES
|
|
const string TEMPLATE_LAMBDA_SHUTTLE = "object/creature/npc/theme_park/lambda_shuttle.iff";
|
|
const int AC_NONE = 0;
|
|
const int AC_ATTACK = 1;
|
|
const int AC_SCAN = 2;
|
|
|
|
//GCW PLAYER BASE PLANETARY CONSTANTS
|
|
const string GCW_BASE_MANAGER = "gcw.base_manager.master_object";
|
|
const string GCW_BASE_COUNT_REBEL = "gcw.player_base_count.rebel";
|
|
const string GCW_BASE_COUNT_IMPERIAL = "gcw.player_base_count.imperial";
|
|
const string VAR_BASE_HACK_DICTIONARY = "gcw.player_base_hack.dictionary";
|
|
|
|
const string GCW_SEQUENCER_KEREN = "-292452502778781373";
|
|
const string GCW_SEQUENCER_DEARIC = "-348184546868074204";
|
|
const string GCW_SEQUENCER_BESTINE = "-233342756915162113";
|
|
|
|
//Pub 30 Static GCW Base control values
|
|
const int NO_CONTROL = 0;
|
|
const int IMPERIAL_CONTROL = 1;
|
|
const int REBEL_CONTROL = 2;
|
|
|
|
//Faction Const
|
|
const int FACTION_NEUTRAL = 0;
|
|
const int FACTION_REBEL = 1;
|
|
const int FACTION_IMPERIAL = 2;
|
|
|
|
//GCW Ranking
|
|
const string LIST_CREDIT_FOR_KILLS = "gcw_tracking.credit_for_kills";
|
|
const string LIST_DAILY_KILL_VALUES = "gcw_tracking.daily_kill_credit";
|
|
|
|
//Npc kill credits
|
|
//Direct value per kill if players is on kill credit list
|
|
const int NORMAL_GCW_VALUE = 5;
|
|
const int ELITE_GCW_VALUE = 10;
|
|
const int BOSS_GCW_VALUE = 25;
|
|
|
|
//Modified by tier
|
|
const double SPACE_GCW_VALUE = 4.0;
|
|
|
|
//Modified by planetary control value of passed template
|
|
const int GCW_PLAYER_BASE_DESTRUCTION = 100;
|
|
const string GCW_PLAYER_BASE_POINT_VALUE = "datatables/faction_perk/hq/hq_point_values.iff";
|
|
|
|
//Rank * Rank* mod = Daily per player point cap
|
|
const int GCW_PLAYER_PVP_MODIFIER = 100;
|
|
|
|
const int BATTLEFIELD_VICTORY_POINT_VALUE = 500;
|
|
const int BATTLEFIELD_DEFEAT_POINT_VALUE = 100;
|
|
const int BATTLEFIELD_TIE_POINT_VALUE = 200;
|
|
|
|
const int BATTLEFIELD_TERMINAL_CAPTURE_MULTIPLIER = 100;
|
|
|
|
//GCW Region Modifier.
|
|
//Constant is the base bonus value modified by gameplay variables
|
|
//Max death is the cap on number of times an individual player can count for scoring
|
|
const int ACTIVITY_CONSTANT = 100;
|
|
const int MAX_DEATH_BY_PLAYER = 15;
|
|
|
|
//Modified by player level to determine minimum target level to count for kill credit
|
|
const double MIN_PVP_LEVEL_RATIO_LIMIT = 0.7;
|
|
const double MIN_NPC_LEVEL_RATIO_LIMIT = 0.86;
|
|
|
|
//GCW Messaging
|
|
const string_id SID_GENERIC_POINT_GRANT = new string_id("gcw", "gcw_rank_generic_point_grant");
|
|
const string_id SID_PVP_KILL_POINT_GRANT = new string_id("gcw", "gcw_rank_pvp_kill_point_grant");
|
|
|
|
//GCW PvP Regions
|
|
const string REGION_CONTROLLER = "gcw_pvp_region_controller";
|
|
const string PVP_REGION_ACTIVITY_PERFORMED = "gcw_pvp_region.attack_performed";
|
|
|
|
//GCW CS Log Types - Do not change these as their order is required for the pointTypes array.
|
|
const int GCW_POINT_TYPE_GROUND_PVE = 0;
|
|
const int GCW_POINT_TYPE_GROUND_PVP = 1;
|
|
const int GCW_POINT_TYPE_GROUND_PVP_REGION = 2;
|
|
const int GCW_POINT_TYPE_BASE_BUSTING = 3;
|
|
const int GCW_POINT_TYPE_SPACE_PVE = 4;
|
|
const int GCW_POINT_TYPE_SPACE_PVP = 5;
|
|
const int GCW_POINT_TYPE_GROUND_QUEST = 6;
|
|
const int GCW_POINT_TYPE_TRADING = 7;
|
|
const int GCW_POINT_TYPE_ENTERTAINING = 8;
|
|
const int GCW_POINT_TYPE_MAX = 9;
|
|
|
|
// GCW category point tracking occurs in these scenes.
|
|
//
|
|
// *****WARNING WARNING WARNING WARNING WARNING*****
|
|
// this list must be kept in sync in
|
|
// PvpNamespace::loadGcwScoreCategoryTable()
|
|
// SwgCuiSpaceZoneMap::SwgCuiSpaceZoneMap()
|
|
// SwgCuiHyperspaceMap::SwgCuiHyperspaceMap()
|
|
// and gcw.scriptlib (validScenes and defaultRegions)
|
|
// *****WARNING WARNING WARNING WARNING WARNING*****
|
|
//
|
|
const string validScenes[] =
|
|
{
|
|
"tatooine",
|
|
"corellia",
|
|
"dantooine",
|
|
"dathomir",
|
|
"endor",
|
|
"lok",
|
|
"naboo",
|
|
"rori",
|
|
"talus",
|
|
"yavin4",
|
|
|
|
"space_tatooine",
|
|
"space_corellia",
|
|
"space_dantooine",
|
|
"space_dathomir",
|
|
"space_endor",
|
|
"space_lok",
|
|
"space_naboo",
|
|
"space_yavin4"
|
|
};
|
|
|
|
// defaultRegions are the regions that get GCW points, when a region does not overlap the player's location.
|
|
// The last region in the gcw_score_category.tab file is the default region.
|
|
//
|
|
// *****WARNING WARNING WARNING WARNING WARNING*****
|
|
// this list must be kept in sync in
|
|
// PvpNamespace::loadGcwScoreCategoryTable()
|
|
// SwgCuiSpaceZoneMap::SwgCuiSpaceZoneMap()
|
|
// SwgCuiHyperspaceMap::SwgCuiHyperspaceMap()
|
|
// and gcw.scriptlib (validScenes and defaultRegions)
|
|
// *****WARNING WARNING WARNING WARNING WARNING*****
|
|
//
|
|
const string defaultRegions[] =
|
|
{
|
|
// Ground default regions
|
|
"gcw_region_tatooine_13",
|
|
"gcw_region_corellia_13",
|
|
"gcw_region_dantooine_16",
|
|
"gcw_region_dathomir_13",
|
|
"gcw_region_endor_15",
|
|
"gcw_region_lok_13",
|
|
"gcw_region_naboo_13",
|
|
"gcw_region_rori_13",
|
|
"gcw_region_talus_16",
|
|
"gcw_region_yavin4_17",
|
|
|
|
// Space default regions
|
|
"gcw_region_tatooine_12",
|
|
"gcw_region_corellia_14",
|
|
"gcw_region_dantooine_17",
|
|
"gcw_region_dathomir_12",
|
|
"gcw_region_endor_16",
|
|
"gcw_region_lok_14",
|
|
"gcw_region_naboo_14",
|
|
"gcw_region_yavin4_18"
|
|
|
|
};
|
|
|
|
const string pointTypes[] =
|
|
{
|
|
"pve",
|
|
"pvp",
|
|
"pvp_battlefield",
|
|
"pvp",
|
|
"space_pve",
|
|
"space_pvp",
|
|
"pve",
|
|
"trading",
|
|
"entertaining"
|
|
};
|
|
|
|
//Factional Collection
|
|
const float COLLECTION_DAMAGE_RATIO_MIN = .41f;
|
|
|
|
const string PVP_PUSHBACK_REGION = "pvp_pushback";
|
|
const string PVP_BATTLEFIELD_REGION = "pvp_battlefield";
|
|
|
|
//2009 GCW Entertainer Quests
|
|
const int GCW_ENTERTAINMENT_TIME = 30;
|
|
const string GCW_SCRIPTVAR_PARENT = "gcw_entertainment";
|
|
const string GCW_ENTERTAINMENT_FLAG = "gcw_entertain_npc"; //this is the objvar we look for on the gcw npc.
|
|
const string GCW_NPC_SCRIPTVAR_FLAG = "entertainment_npc"; //this is the scriptvar for the NPC spawned by the patrol point
|
|
const string GCW_PATROL_OBJ = "patrol_point_object"; //this is the scriptvar for the NPC spawned by the patrol point
|
|
const string GCW_NPC_CLEANUP_FLAG = "entertainment_npc_cleanup"; //this is to clean up the npc after entertainment is completed.
|
|
const string GCW_NPC_BEING_ENTERTAINED = "entertainment_npc"; //this is to avoid multiple spawns at the same offensive patrol location.
|
|
const string ENTERTAIN_GCW_TROOPS_PID = GCW_SCRIPTVAR_PARENT + ".gcw_entertainment_pid";
|
|
const string GCW_STOPPED_ENTERTAINING = GCW_SCRIPTVAR_PARENT + ".gcw_player_stopped_entertaining";
|
|
const string GCW_ENTERTAINER_PATROL_QUEST = "gcw_entertain_patrol";
|
|
const string GCW_REPAIR_PATROL_QUEST = "gcw_repair_patrol";
|
|
const string GCW_REPAIR_TURRET_QUEST = "gcw_repair_turret";
|
|
const string GCW_REPAIR_BARRICADE_QUEST = "gcw_repair_barricade";
|
|
const string GCW_SPY_PATROL_DESTROY_QUEST = "gcw_spy_destroy_patrol";
|
|
const string GCW_SPY_PATROL_SCOUT_QUEST = "gcw_spy_scout_patrol";
|
|
const string GCW_REPAIR_VEHICLE_PATROL_QUEST = "gcw_repair_vehicle";
|
|
const string GCW_REPAIR_DAMAGED_VEHICLE_QUEST = "gcw_repair_vehicle";
|
|
const string GCW_MEDIC_HEAL_QUEST = "gcw_medic_heal";
|
|
const string GCW_DEFEND_BARRICADE_REBEL = "gcw_defend_rebel_barricade";
|
|
const string GCW_DEFEND_BARRICADE_IMPERIAL = "gcw_defend_imperial_barricade";
|
|
const string GCW_DEFEND_TURRET_REBEL = "gcw_defend_rebel_turret";
|
|
const string GCW_DEFEND_TURRET_IMPERIAL = "gcw_defend_imperial_turret";
|
|
const string GCW_DEFEND_TOWER_REBEL = "gcw_defend_rebel_tower";
|
|
const string GCW_DEFEND_TOWER_IMPERIAL = "gcw_defend_imperial_tower";
|
|
const string GCW_DESTROY_BARRICADE = "gcw_destroy_barricade";
|
|
const string GCW_DESTROY_TURRET = "gcw_destroy_turret";
|
|
const string GCW_ENTERTAIN_RALLY = "gcw_entertain_rallying";
|
|
const string GCW_ENTERTAIN_FATIGUE = "gcw_entertain_fatigue";
|
|
const string GCW_SMUGGLER_SLICING = "gcw_smuggler_slicing";
|
|
const string GCW_ELIMINATE_REBELS = "gcw_eliminate_rebels";
|
|
const string GCW_ELIMINATE_IMPERIALS = "gcw_eliminate_imperials";
|
|
|
|
const string BUFF_SPY_EXPLOSIVES = "gcw_spy_destroy_patrol_explosive_stack";
|
|
const string BUFF_GENERAL_RESIST_STACK_IMP = "gcw_general_resistance_stack_imperial";
|
|
const string BUFF_GENERAL_RESIST_STACK_REB = "gcw_general_resistance_stack_rebel";
|
|
const string BUFF_PLAYER_FATIGUE = "gcw_fatigue";
|
|
|
|
const string SPY_DESTROY_PID = "spyDestroyPid";
|
|
const string SPY_SCOUT_PID = "spyScoutPid";
|
|
const string TRADER_REPAIR_PID = "traderRepairPid";
|
|
const string OBJECT_TO_REPAIR = "objectToRepair";
|
|
const string GCW_REPAIR_QUEST = "gcwRepairQuest";
|
|
const string GCW_REPAIR_RESOURCE_COUNT = "gcwRepairResourceCount";
|
|
const string GCW_OBJECT_REPAIR_COUNT = "gcwObjectRepairCount";
|
|
|
|
const string_id SID_COUNTDOWN_LOCOMOTION = new string_id("gcw", "locomotion_stopped_countdown");
|
|
const string_id SID_INTERRUPTED_INCAPACITATED = new string_id("gcw", "incap_stopped_countdown");
|
|
const string_id SID_INTERRUPTED_DAMAGED = new string_id("gcw", "combat_stopped_countdown");
|
|
const string_id SID_YOU_NEED_TO_BE_COMBATANT = new string_id("gcw", "you_need_to_be_combatant_to_entertain");
|
|
const string_id SID_YOU_NEED_ENTERTAINMENT_QUEST = new string_id("gcw", "you_need_entertainment_quest_to_entertain");
|
|
const string_id SID_ENTERTAIN_TROOPS_TIMER = new string_id("gcw", "entertain_troops_timer");
|
|
const string_id SID_DOESNT_NEED_REPAIR = new string_id("gcw", "gcw_object_doesnt_need_repair");
|
|
const string_id SID_DOESNT_NEED_HEALING = new string_id("gcw", "gcw_object_doesnt_need_healing");
|
|
const string_id SID_YOU_NEED_RESOURCES_REPAIR = new string_id("gcw", "you_need_resources_to_repair");
|
|
const string_id SID_YOU_NEED_TOOL_REPAIR = new string_id("gcw", "gcw_asset_repair_resource_needed");
|
|
const string_id SID_RESOURCES_NEEDED = new string_id("gcw", "pylon_resources_needed");
|
|
const string_id SID_SCOUT_PP_TIMER = new string_id("gcw", "reporting_patrol_point_position");
|
|
const string_id SID_REPAIR_PP_TIMER = new string_id("gcw", "reparing_gcw_object");
|
|
const string_id SID_HEAL_PP_TIMER = new string_id("gcw", "healing_gcw_object");
|
|
const string_id SID_DESTROY_PP_TIMER = new string_id("gcw", "placing_patrol_point_explosives");
|
|
|
|
const string GCW_RESOURCE_TYPE_BARRICADE = "ore";
|
|
const string GCW_RESOURCE_TYPE_TURRET = "metal";
|
|
const string GCW_RESOURCE_TYPE_PATROL = "metal";
|
|
const string GCW_RESOURCE_TYPE_VEHICLE = "metal";
|
|
const string GCW_RESOURCE_TYPE_DAMAGED_VEHICLE = "metal";
|
|
|
|
const string GCW_SPAWN_ID_OBJVAR = "spawn_id";
|
|
const string GCW_DEFENSE_PATROL_OBJVAR = "defense_quest_asset";
|
|
const string GCW_OFFENSE_PATROL_OBJVAR = "offense_quest_asset";
|
|
const string GCW_TOOL_TEMPLATE_OBJVAR = "tool_template";
|
|
const string GCW_DEF_TOOL_TEMPLATE_OBJVAR = "defense_tool_template";
|
|
const string GCW_OFF_TOOL_TEMPLATE_OBJVAR = "offense_tool_template";
|
|
|
|
const string GCW_CONSTRUCTION_TASK = "resourcesUsed";
|
|
const string GCW_CONSTRUCTION_SIGNAL = "resourcesUsed";
|
|
const string GCW_IMP_DESTROY_BARRICADE_TASK = "destroyBarricadeImperial";
|
|
const string GCW_IMP_DESTROY_BARRICADE_SIGNAL = "destroyBarricadeImperial";
|
|
const string GCW_REB_DESTROY_BARRICADE_TASK = "destroyBarricadeRebel";
|
|
const string GCW_REB_DESTROY_BARRICADE_SIGNAL = "destroyBarricadeRebel";
|
|
const string GCW_IMP_DESTROY_TURRET_TASK = "destroyTurretImperial";
|
|
const string GCW_IMP_DESTROY_TURRET_SIGNAL = "destroyTurretImperial";
|
|
const string GCW_REB_DESTROY_TURRET_TASK = "destroyTurretRebel";
|
|
const string GCW_REB_DESTROY_TURRET_SIGNAL = "destroyTurretRebel";
|
|
const string GCW_REB_ENTERTAIN_PATROL_TASK = "entertainedPostRebel";
|
|
const string GCW_REB_ENTERTAIN_PATROL_SIGNAL = "entertainedPostRebel";
|
|
const string GCW_IMP_ENTERTAIN_PATROL_TASK = "entertainedPostImperial";
|
|
const string GCW_IMP_ENTERTAIN_PATROL_SIGNAL = "entertainedPostImperial";
|
|
const string GCW_REB_DESTROY_PATROL_SIGNAL = "destroyedPatrolPointRebel";
|
|
const string GCW_IMP_DESTROY_PATROL_SIGNAL = "destroyedPatrolPointImperial";
|
|
const string GCW_REB_FIND_PATROL_SIGNAL = "findPatrolPointRebel";
|
|
const string GCW_IMP_FIND_PATROL_SIGNAL = "findPatrolPointImperial";
|
|
const string GCW_PARTICIPATION_FLAG = "gcw_participation_flag";
|
|
|
|
const string GCW_POINT_OVERRIDE = "gcw_point_override";
|
|
const string GCW_REBEL_TOKEN = "item_gcw_rebel_token";
|
|
const string GCW_IMPERIAL_TOKEN = "item_gcw_imperial_token";
|
|
const string GCW_NPC_ENTERTAINED_BUFF = "gcw_entertained_patrol_npc";
|
|
|
|
const int REPAIR_COUNT_MAX = 100;
|
|
const int QUEST_COUNT_MAX = 100;
|
|
const int MIN_REPAIR_AMOUNT = 10000;
|
|
const int GCW_REPAIR_AMOUNT = 50000;
|
|
const int GCW_RESOURCE_COUNT_BARRICADE = 50000;
|
|
const int GCW_RESOURCE_COUNT_TURRET = 50000;
|
|
const int GCW_RESOURCE_COUNT_PATROL = 50000;
|
|
const int GCW_RESOURCE_COUNT_VEHICLE = 50000;
|
|
const int GCW_RESOURCE_COUNT_DAMAGED_VEHICLE = 50000;
|
|
|
|
const int GCW_CITY_PHASE_UNKNOWN = 0;
|
|
const int GCW_CITY_PHASE_CONSTRUCTION = 1;
|
|
const int GCW_CITY_PHASE_COMBAT = 2;
|
|
|
|
const int GCW_CONSTRUCTION_MAXIMUM = 100;
|
|
const int GCW_FATIGUE_DIVISOR = 10;
|
|
const int GCW_FATIGUE_TIMER_MAX = 100;
|
|
|
|
const int GCW_PULSE_TIMER = 10800;
|
|
const int GCW_CONSTRUCTION_END_TIMER = 1800;
|
|
const int GCW_COMBAT_END_TIMER = 1800;
|
|
const int GCW_GENERAL_GCW_BONUS_MULTIPLIER = 5;
|
|
const int GCW_GENERAL_GCW_BASE_AMOUNT = 1000;
|
|
const int GCW_GENERAL_TOKEN_BONUS_DIVISOR = 1000;
|
|
const int GCW_POINTS_WINNER_PARTICIPANTS = 1000;
|
|
const int GCW_POINTS_LOSER_PARTICIPANTS = 100;
|
|
const int GCW_TOKENS_WINNER_PARTICIPANTS = 40;
|
|
const int GCW_TOKENS_LOSER_PARTICIPANTS = 10;
|
|
|
|
const int GCW_POINTS_CONSTRUCTION_PHASE = 100;
|
|
const int GCW_TOKENS_CONSTRUCTION_PHASE = 20;
|
|
|
|
|
|
const string DEARIC_CITY_TABLE = "datatables/gcw/gcw_city_dearic.iff";
|
|
const string KEREN_CITY_TABLE = "datatables/gcw/gcw_city_keren.iff";
|
|
const string BESTINE_CITY_TABLE = "datatables/gcw/gcw_city_bestine.iff";
|
|
|
|
const string CITY_DEARIC = "dearic";
|
|
const string CITY_BESTINE = "bestine";
|
|
const string CITY_KEREN = "keren";
|
|
|
|
const string GCW_TUTORIAL_FLAG = "gcw_tutorial_flag.has_received_tutorial";
|
|
|
|
const string COLOR_REBELS = "\\" + colors_hex.COLOR_REBELS;
|
|
const string COLOR_IMPERIALS = "\\" + colors_hex.COLOR_IMPERIALS;
|
|
/***** BASE FUNCTIONS **************************************************/
|
|
|
|
void assignScanInterests(obj_id npc)
|
|
{
|
|
if(!isIdValid(npc) || isPlayer(npc) || pet_lib.isPet(npc))
|
|
return;
|
|
|
|
int interests = 0;
|
|
|
|
location here = getLocation(npc);
|
|
|
|
if(here == null)
|
|
return;
|
|
|
|
int spawnWeight = Integer.MIN_VALUE;
|
|
|
|
string tbl = REGION_SPAWN_WEIGHT_PATH + here.area + ".iff";
|
|
|
|
string[] regionNames = dataTableGetStringColumn(tbl, "REGION_NAME");
|
|
|
|
if(regionNames != null && regionNames.length > 0)
|
|
{
|
|
region[] r = getRegionsWithSpawnableAtPoint(here, regions.SPAWN_TRUE);
|
|
if(r != null && r.length > 0)
|
|
{
|
|
for(int i = 0; i < r.length; i++)
|
|
{
|
|
string rName = r[i].getName();
|
|
int idx = utils.getElementPositionInArray(regionNames, rName);
|
|
|
|
if(idx > -1)
|
|
{
|
|
spawnWeight = dataTableGetInt(tbl, idx, "WEIGHT");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(spawnWeight == Integer.MIN_VALUE)
|
|
{
|
|
spawnWeight = rand(-10, 10);
|
|
//debugSpeakMsg(npc, "DEBUG: using spawn weight = " + spawnWeight);
|
|
}
|
|
|
|
if(rand(0, 10) <= rand(1, 10) - spawnWeight)
|
|
interests = utils.setBit(interests, INTEREST_FACTION);
|
|
|
|
if(rand(1, 100) <= 10)
|
|
{
|
|
interests |= INTEREST_CONTRABAND;
|
|
}
|
|
else
|
|
{
|
|
if(rand(0, 10) <= rand(1, 10) + spawnWeight)
|
|
interests = utils.setBit(interests, INTEREST_SPICE);
|
|
|
|
if(rand(-5, 5) <= rand(-3, 3) + spawnWeight)
|
|
interests = utils.setBit(interests, INTEREST_SLICED_ITEMS);
|
|
|
|
if(rand(0, 10) <= rand(1, 10) - spawnWeight)
|
|
interests = utils.setBit(interests, INTEREST_FACTION_ITEMS);
|
|
}
|
|
|
|
if(interests == 0)
|
|
interests = utils.setBit(interests, rand(INTEREST_FACTION, INTEREST_FACTION_ITEMS));
|
|
|
|
utils.setScriptVar(npc, SCRIPTVAR_SCAN_INTEREST, interests);
|
|
}
|
|
|
|
float scan(obj_id scanner, obj_id target)
|
|
{
|
|
if(!isIdValid(scanner) || !isIdValid(target))
|
|
return -1f;
|
|
|
|
int interests = utils.getIntScriptVar(scanner, SCRIPTVAR_SCAN_INTEREST);
|
|
|
|
if(interests == 0)
|
|
return -1f;
|
|
|
|
return getContrabandRating(target, interests);
|
|
}
|
|
|
|
/***********************************************************/
|
|
boolean isContrabandItem(obj_id item)
|
|
{
|
|
if(!isIdValid(item))
|
|
return false;
|
|
|
|
return (getContrabandRating(item, INTEREST_CONTRABAND) > 0f);
|
|
}
|
|
|
|
float getContrabandRating(obj_id target, int interests)
|
|
{
|
|
if(!isIdValid(target) || interests == 0)
|
|
return 0f;
|
|
|
|
int got = getGameObjectType(target);
|
|
|
|
if(isGameObjectTypeOf(got, GOT_creature) || isPlayer(target))
|
|
{
|
|
obj_id self = getSelf();
|
|
|
|
if(isIdValid(self))
|
|
{
|
|
int total = 0;
|
|
|
|
obj_id[] items = getInventoryAndEquipment(target);
|
|
|
|
if(items != null && items.length > 0)
|
|
{
|
|
int min_check = 1;
|
|
float dist = getDistance(self, target);
|
|
|
|
if(dist < 64f)
|
|
{
|
|
min_check -= Math.round(dist / 15f);
|
|
}
|
|
|
|
if(min_check < 1)
|
|
min_check = 1;
|
|
|
|
int i = 0;
|
|
|
|
while(i < items.length)
|
|
{
|
|
total += getContrabandRating(items[i], interests);
|
|
i += rand(1, min_check);
|
|
}
|
|
}
|
|
|
|
int myFac = pvpGetAlignedFaction(self);
|
|
int tFac = pvpGetAlignedFaction(target);
|
|
|
|
if(tFac == myFac)
|
|
{
|
|
total -= pvpGetCurrentGcwRank(target);
|
|
if(total < 0)
|
|
total = 0;
|
|
}
|
|
else
|
|
{
|
|
if(utils.checkBit(interests, INTEREST_FACTION))
|
|
{
|
|
if(pvpAreFactionsOpposed(myFac, tFac))
|
|
{
|
|
if(isPlayer(target))
|
|
{
|
|
//faction_perk.decloakCovertFactionMember(self, target);
|
|
total += pvpGetCurrentGcwRank(target);
|
|
|
|
//startCombat(self, target);
|
|
}
|
|
else
|
|
total += 5;
|
|
}
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(isSpice(target) && utils.checkBit(interests, INTEREST_SPICE)) //spice
|
|
{
|
|
int cnt = getCount(target);
|
|
|
|
if(cnt > 0)
|
|
return (float) cnt;
|
|
|
|
return 6f;
|
|
}
|
|
|
|
if(isSlicedEquipment(target) && utils.checkBit(interests, INTEREST_SLICED_ITEMS)) //sliced item
|
|
{
|
|
if(utils.isEquipped(target))
|
|
return 4f;
|
|
|
|
return 2f;
|
|
}
|
|
|
|
if(isNonImperialFactionItem(target) && utils.checkBit(interests, INTEREST_FACTION_ITEMS))
|
|
{
|
|
if(utils.isEquipped(target))
|
|
return 6f;
|
|
|
|
return 3f;
|
|
}
|
|
}
|
|
|
|
return 0f;
|
|
}
|
|
|
|
float getContrabandRating(obj_id target)
|
|
{
|
|
obj_id self = getSelf();
|
|
int interests = utils.getIntScriptVar(self, SCRIPTVAR_SCAN_INTEREST);
|
|
|
|
return getContrabandRating(target, interests);
|
|
}
|
|
|
|
boolean isSpice(obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
return false;
|
|
|
|
return (getGameObjectType(target) == GOT_misc_food && hasScript(target, "item.comestible.spice"));
|
|
}
|
|
|
|
boolean isSlicedEquipment(obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
return false;
|
|
|
|
int got = getGameObjectType(target);
|
|
|
|
return (hasObjVar(target, "slicing.hack") && (isGameObjectTypeOf(got, GOT_weapon) || isGameObjectTypeOf(got, GOT_armor)));
|
|
}
|
|
|
|
boolean isNonImperialFactionItem(obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
return false;
|
|
|
|
if(hasScript(target, faction_perk.SCRIPT_FACTION_ITEM))
|
|
{
|
|
if(hasObjVar(target, faction_perk.VAR_FACTION))
|
|
{
|
|
string iFac = getStringObjVar(target, faction_perk.VAR_FACTION);
|
|
|
|
if(!iFac.equals("imperial"))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
void harass(obj_id npc, obj_id target)
|
|
{
|
|
if(!isIdValid(npc))
|
|
return;
|
|
|
|
if(hasScript(npc, "ai.imperial_presence.harass"))
|
|
return;
|
|
|
|
attachScript(npc, "ai.imperial_presence.harass");
|
|
if(isIdValid(target) && target.isLoaded())
|
|
{
|
|
dictionary d = new dictionary();
|
|
|
|
d.put("harassTarget", target);
|
|
|
|
messageTo(npc, "handleNewHarassTarget", d, 1f, false);
|
|
}
|
|
else
|
|
{
|
|
messageTo(npc, "handleCheckpointMode", null, 1f, false);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************/
|
|
boolean spawnViaLambda(location loc, string tbl, string column, dictionary params)
|
|
{
|
|
if(loc == null || tbl == null || tbl.equals("") || column == null || column.equals(""))
|
|
return false;
|
|
|
|
string[] spawnNames = dataTableGetStringColumn(tbl, column);
|
|
return createLambdaDropship(loc, spawnNames, params);
|
|
}
|
|
|
|
boolean spawnViaLambda(location loc, string tbl, int col, dictionary params)
|
|
{
|
|
if(loc == null || tbl == null || tbl.equals("") || col < 0)
|
|
return false;
|
|
|
|
string[] spawnNames = dataTableGetStringColumn(tbl, col);
|
|
return createLambdaDropship(loc, spawnNames, params);
|
|
}
|
|
|
|
boolean createLambdaDropship(location loc, string[] spawnNames, dictionary params)
|
|
{
|
|
if(loc == null || spawnNames == null || spawnNames.length == 0)
|
|
return false;
|
|
|
|
if(!loc.area.equals(getCurrentSceneName()))
|
|
return false;
|
|
|
|
obj_id lambda = create.object("object/creature/npc/theme_park/lambda_shuttle.iff", loc);
|
|
|
|
if(!isIdValid(lambda))
|
|
return false;
|
|
|
|
setYaw(lambda, rand(-180f, 180f));
|
|
|
|
attachScript(lambda, "systems.spawning.dropship.lambda");
|
|
|
|
utils.setScriptVar(lambda, "spawnNames", spawnNames);
|
|
|
|
if(params != null && !params.isEmpty())
|
|
utils.setScriptVar(lambda, "spawnParameters", params);
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean spawnViaLambdaPerGeo(location loc, dictionary params)
|
|
{
|
|
if(loc == null)
|
|
return false;
|
|
|
|
string[] spawnNames = getGeoSpawnNames(loc);
|
|
return createLambdaDropship(loc, spawnNames, params);
|
|
}
|
|
|
|
string[] getGeoSpawnNames(location loc, boolean allowHeavy)
|
|
{
|
|
//LOG("*TEST*", "Loc equals null");
|
|
if(loc == null)
|
|
return null;
|
|
|
|
//LOG("*TEST2*", "cannot find region");
|
|
region[] r = getRegionsAtPoint(loc);
|
|
if(r == null || r.length == 0)
|
|
return null;
|
|
|
|
string tbl = "datatables/imperial_presence/geo/general.iff";
|
|
|
|
if(allowHeavy && rand(0, 10) < 2)
|
|
tbl = "datatables/imperial_presence/geo/general_heavy.iff";
|
|
|
|
for(int i = 0; i < r.length; i++)
|
|
{
|
|
int geoType = r[i].getGeographicalType();
|
|
|
|
if(geoType == regions.GEO_DESERT || geoType == regions.GEO_OASIS || geoType == regions.GEO_WASTELAND)
|
|
{
|
|
tbl = "datatables/imperial_presence/geo/desert.iff";
|
|
}
|
|
else if(geoType == regions.GEO_FOREST || geoType == regions.GEO_JUNGLE || geoType == regions.GEO_WASTELAND)
|
|
{
|
|
tbl = "datatables/imperial_presence/geo/forest.iff";
|
|
}
|
|
else if(geoType == regions.GEO_SWAMP)
|
|
{
|
|
tbl = "datatables/imperial_presence/geo/swamp.iff";
|
|
}
|
|
}
|
|
|
|
int numCol = dataTableGetNumColumns(tbl);
|
|
|
|
//LOG("*TEST3", "NumCol is zero");
|
|
if(numCol <= 0)
|
|
return null;
|
|
|
|
return dataTableGetStringColumn(tbl, rand(0, numCol - 1));
|
|
}
|
|
|
|
string[] getGeoSpawnNames(location loc)
|
|
{
|
|
return getGeoSpawnNames(loc, false);
|
|
}
|
|
|
|
obj_id getGCWMasterObject(obj_id objObject)
|
|
{
|
|
return getGCWMasterObject(getLocation(objObject));
|
|
}
|
|
|
|
obj_id getGCWMasterObject(location locTest)
|
|
{
|
|
string strPlanet = locTest.area;
|
|
dictionary dctObjectInfo = dataTableGetRow("datatables/gcw/gcw_master_objects.iff", strPlanet);
|
|
|
|
if(dctObjectInfo == null)
|
|
{
|
|
LOG("gcw", "No object for " + strPlanet);
|
|
return null;
|
|
}
|
|
|
|
string strObjId = dctObjectInfo.getString("strObjId");
|
|
Long lngId;
|
|
|
|
try
|
|
{
|
|
lngId = new Long(strObjId); // convert the string to a long so we can make an obj_id
|
|
} // try with error handling
|
|
catch(NumberFormatException err)
|
|
{
|
|
LOG("gcw", "Bad object id format for " + strPlanet);
|
|
return null;
|
|
}
|
|
|
|
obj_id objMaster = obj_id.getObjId(lngId.longValue());
|
|
|
|
return objMaster;
|
|
}
|
|
|
|
void changeGCWScore(location locTest, int intValue, string strFaction)
|
|
{
|
|
dictionary dctParams = new dictionary();
|
|
|
|
dctParams.put("intScoreChange", intValue);
|
|
dctParams.put("strFaction", strFaction);
|
|
obj_id objParent = getGCWMasterObject(locTest);
|
|
|
|
messageTo(objParent, "updateGCWScore", dctParams, 0, true);
|
|
}
|
|
|
|
void incrementGCWScore(obj_id objObject)
|
|
{
|
|
location locTest = getLocation(objObject);
|
|
int intScoreChange = faction_perk.grabFactionBasePointValue(objObject);
|
|
string strFaction = getStringObjVar(objObject, "faction");
|
|
|
|
changeGCWScore(locTest, intScoreChange, strFaction);
|
|
}
|
|
|
|
void decrementGCWScore(obj_id objObject)
|
|
{
|
|
location locTest = getLocation(objObject);
|
|
int intScoreChange = faction_perk.grabFactionBasePointValue(objObject);
|
|
|
|
intScoreChange = intScoreChange * -1;
|
|
string strFaction = getStringObjVar(objObject, "faction");
|
|
|
|
changeGCWScore(locTest, intScoreChange, strFaction);
|
|
return;
|
|
|
|
}
|
|
|
|
boolean canPlaceFactionBaseByPlanet(obj_id player, obj_id deed, string planet)
|
|
{
|
|
if(isGcwRestrictedSceneName(planet))
|
|
return false;
|
|
|
|
if(!canPlaceFactionBaseByScore(player, deed, planet))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean canPlaceFactionBaseByScore(obj_id player, obj_id deed, string planet)
|
|
{
|
|
return (getCurrentPlanetaryFactionBaseCountPlanet(planet) < getCurrentPlanetaryFactionBaseMaxPlanet(planet));
|
|
}
|
|
|
|
int getCurrentPlanetaryFactionBaseCountPlanet(string planet)
|
|
{
|
|
obj_id planetId = getPlanetByName(planet);
|
|
|
|
if(!isIdValid(planetId))
|
|
return Integer.MAX_VALUE;
|
|
|
|
return getRebelBaseCount(planetId) + getImperialBaseCount(planetId);
|
|
}
|
|
|
|
int getCurrentPlanetaryFactionBaseMaxPlanet(string planet)
|
|
{
|
|
obj_id planetId = getPlanetByName(planet);
|
|
|
|
if(!isIdValid(planetId))
|
|
return -1;
|
|
|
|
if(!dataTableOpen(TABLE_PLAYER_BASE_CAP))
|
|
return -1;
|
|
|
|
int row = dataTableSearchColumnForString(planet, "planet", TABLE_PLAYER_BASE_CAP);
|
|
|
|
return (row == -1 ? -1 : dataTableGetInt(TABLE_PLAYER_BASE_CAP, planet, "base_max"));
|
|
}
|
|
|
|
int getImperialBaseCount(obj_id planet)
|
|
{
|
|
return (utils.hasScriptVar(planet, GCW_BASE_COUNT_IMPERIAL) ? utils.getIntScriptVar(planet, GCW_BASE_COUNT_IMPERIAL) : 0);
|
|
}
|
|
|
|
int getRebelBaseCount(obj_id planet)
|
|
{
|
|
return (utils.hasScriptVar(planet, GCW_BASE_COUNT_REBEL) ? utils.getIntScriptVar(planet, GCW_BASE_COUNT_REBEL) : 0);
|
|
}
|
|
|
|
void modifyPlanetaryBaseCount(obj_id base, int faction, int delta)
|
|
{
|
|
obj_id planetId = getPlanetByName(getLocation(base).area);
|
|
obj_id planetRegister = utils.getObjIdScriptVar(planetId, GCW_BASE_MANAGER);
|
|
|
|
if(!isIdValid(planetRegister))
|
|
return;
|
|
|
|
dictionary dict = new dictionary();
|
|
|
|
dict.put("faction", faction);
|
|
dict.put("delta", delta);
|
|
messageTo(planetRegister, "alterBaseCount", dict, 0, false);
|
|
}
|
|
|
|
boolean isGcwRestrictedSceneName(string scene)
|
|
{
|
|
string[] restrictedScene =
|
|
{
|
|
"kashyyyk_main",
|
|
"kashyyyk_north_dungeons",
|
|
"kashyyyk_pob_dungeons",
|
|
"kashyyyk_south_dungeons",
|
|
"kashyyyk_rryatt_trail",
|
|
"kashyyyk_hunting",
|
|
"space_light1",
|
|
"space_heavy1",
|
|
"dathomir",
|
|
"yavin4",
|
|
"endor",
|
|
"mustafar"
|
|
};
|
|
|
|
for(int i = 0; i < restrictedScene.length; i++)
|
|
{
|
|
if(restrictedScene[i].equals(scene))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
dictionary getGCWDictionary(obj_id self)
|
|
{
|
|
int intImperialControlScore = getIntObjVar(self, "Imperial.controlScore");
|
|
int intRebelControlScore = getIntObjVar(self, "Rebel.controlScore");
|
|
dictionary dctParams = new dictionary();
|
|
|
|
LOG("gcw", "Putting Imperial " + intImperialControlScore);
|
|
LOG("gcw", "Putting Rebel " + intRebelControlScore);
|
|
dctParams.put("intImperialControlScore", intImperialControlScore);
|
|
dctParams.put("intRebelControlScore", intRebelControlScore);
|
|
return dctParams;
|
|
}
|
|
|
|
float getImperialRatio(obj_id objNPC)
|
|
{
|
|
float fltImperialControlScore = (float) getIntObjVar(objNPC, "Imperial.controlScore");
|
|
float fltRebelControlScore = (float) getIntObjVar(objNPC, "Rebel.controlScore");
|
|
|
|
if((fltImperialControlScore == 0) && (fltRebelControlScore == 0))
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
if(fltImperialControlScore == 0)
|
|
{
|
|
return .0f;
|
|
}
|
|
|
|
float fltScore = fltImperialControlScore / fltRebelControlScore;
|
|
|
|
// this is added to the min and capped
|
|
return fltScore;
|
|
}
|
|
|
|
float getRebelRatio(obj_id objNPC)
|
|
{
|
|
float fltImperialControlScore = (float) getIntObjVar(objNPC, "Imperial.controlScore");
|
|
float fltRebelControlScore = (float) getIntObjVar(objNPC, "Rebel.controlScore");
|
|
|
|
if((fltImperialControlScore == 0) && (fltRebelControlScore == 0))
|
|
{
|
|
return 1.0f;
|
|
}
|
|
|
|
if(fltRebelControlScore == 0)
|
|
{
|
|
return .0f;
|
|
}
|
|
|
|
LOG("gcw", "Rebel score is " + fltRebelControlScore);
|
|
LOG("gcw", "Imperial score is " + fltImperialControlScore);
|
|
|
|
float fltScore = fltRebelControlScore / fltImperialControlScore;
|
|
|
|
// this is added to the min and capped
|
|
return fltScore;
|
|
|
|
}
|
|
|
|
void incrementGCWStanding(obj_id killer, obj_id target)
|
|
{
|
|
obj_id self = killer;
|
|
const int MINIMUM_TIME_BETWEEN_KILLS = 900;
|
|
|
|
if(!factions.isDeclared(killer))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int intScore = 1;
|
|
|
|
if(isPlayer(target))
|
|
{
|
|
intScore = 25;
|
|
}
|
|
|
|
int intTime = getGameTime();
|
|
int intLastKill = utils.getIntScriptVar(target, "intLastGCWDeath");
|
|
|
|
if((intTime - intLastKill < MINIMUM_TIME_BETWEEN_KILLS))
|
|
{
|
|
//return; // No killy due to timers!
|
|
}
|
|
|
|
obj_id objStructure = getTopMostContainer(killer);
|
|
|
|
if(!isPlayer(objStructure))
|
|
{
|
|
if(!permissionsIsPublic(objStructure))
|
|
{
|
|
return; // HA HA
|
|
}
|
|
}
|
|
|
|
//sendSystemMessageTestingOnly(killer, "checking timers 2");
|
|
utils.setScriptVar(target, "intLastGCWDeath", intTime);
|
|
|
|
dictionary dctParams = new dictionary();
|
|
|
|
dctParams.put("intScore", intScore);
|
|
messageTo(killer, "updateGCWStanding", dctParams, 0, false);
|
|
}
|
|
|
|
void checkAndUpdateGCWStanding(obj_id self, int intAddedPoints)
|
|
{
|
|
string area = getLocation(self).area;
|
|
|
|
if(area.equals("dungeon1") || area.equals("adventure1") || area.equals("adventure2"))
|
|
return;
|
|
|
|
//sendSystemMessageTestingOnly(self, "Updating scores");
|
|
const int SCORE_THRESHOLD = 25;
|
|
const int POINT_EXCHANGE_RATE = 25;
|
|
int intCurrentScore = getIntObjVar(self, "gcw.intKillScore");
|
|
|
|
intCurrentScore = intCurrentScore + intAddedPoints;
|
|
|
|
if(intCurrentScore >= SCORE_THRESHOLD)
|
|
{
|
|
int intPlanetPoints = intCurrentScore / POINT_EXCHANGE_RATE;
|
|
|
|
//sendSystemMessageTestingOnly(self, "intCurrentScore = "+intCurrentScore);
|
|
//sendSystemMessageTestingOnly(self, "intPlanetPoints = "+intPlanetPoints);
|
|
intCurrentScore = intCurrentScore - (intPlanetPoints * POINT_EXCHANGE_RATE);
|
|
//sendSystemMessageTestingOnly(self, "intCurrentScore2 = "+intCurrentScore);
|
|
|
|
prose_package ppTest = new prose_package();
|
|
string_id strSpam = new string_id("faction_perk", "earned_gcw_points");
|
|
|
|
ppTest = prose.setStringId(ppTest, strSpam);
|
|
ppTest = prose.setDI(ppTest, intPlanetPoints);
|
|
sendSystemMessageProse(self, ppTest);
|
|
string strFaction = "";
|
|
|
|
if(factions.isRebel(self))
|
|
{
|
|
playClientEffectObj(self, "clienteffect/holoemote_rebel.cef", self, "head");
|
|
play2dNonLoopingSound(self, "sound/music_themequest_victory_rebel.snd");
|
|
strFaction = "Rebel";
|
|
|
|
}
|
|
else
|
|
{
|
|
playClientEffectObj(self, "clienteffect/holoemote_imperial.cef", self, "head");
|
|
play2dNonLoopingSound(self, "sound/music_themequest_victory_imperial.snd");
|
|
strFaction = "Imperial";
|
|
}
|
|
|
|
changeGCWScore(getLocation(self), intPlanetPoints, strFaction);
|
|
}
|
|
|
|
setObjVar(self, "gcw.intKillScore", intCurrentScore);
|
|
}
|
|
|
|
obj_id getPub30StaticBaseControllerId(obj_id subject)
|
|
{
|
|
return getFirstObjectWithScript(getLocation(trial.getTop(subject)), 500.0f, "systems.gcw.static_base.master");
|
|
}
|
|
|
|
int getPub30StaticBaseControllingFaction(obj_id subject)
|
|
{
|
|
obj_id controller = getPub30StaticBaseControllerId(subject);
|
|
|
|
return getIntObjVar(controller, "gcw.static_base.base_status");
|
|
}
|
|
|
|
int getPub30StaticBaseTimeSinceLastCapture(obj_id subject)
|
|
{
|
|
obj_id controller = getPub30StaticBaseControllerId(subject);
|
|
|
|
if(!hasObjVar(controller, "gcw.static_base.last_capture"))
|
|
return -1;
|
|
else
|
|
return getIntObjVar(controller, "gcw.static_base.last_capture");
|
|
}
|
|
|
|
void setPub30StaticBaseTimeSinceLastCapture(obj_id subject, int time)
|
|
{
|
|
obj_id controller = getPub30StaticBaseControllerId(subject);
|
|
|
|
setObjVar(controller, "gcw.static_base.last_capture", time);
|
|
}
|
|
|
|
const int PHASE_5 = 259200; //3 days
|
|
const int PHASE_4 = 86400; //1 day
|
|
const int PHASE_3 = 28800; //8 hours
|
|
const int PHASE_2 = 14400; //4 hours
|
|
|
|
int getPub30StaticBaseCapturePhase(obj_id subject)
|
|
{
|
|
int lastCapture = getPub30StaticBaseTimeSinceLastCapture(subject);
|
|
|
|
if(lastCapture == -1)
|
|
return 0;
|
|
|
|
int difference = getGameTime() - lastCapture;
|
|
|
|
//3 Days
|
|
if(difference >= 259200)
|
|
return 5;
|
|
//1 Day
|
|
if(difference >= 86400)
|
|
return 4;
|
|
//8 Hours
|
|
if(difference >= 28800)
|
|
return 3;
|
|
//4 Hours
|
|
if(difference >= 14400)
|
|
return 2;
|
|
//Immediate
|
|
return 1;
|
|
}
|
|
|
|
int getPub30TimeToNextPhaseInt(obj_id subject)
|
|
{
|
|
int lastCapture = getPub30StaticBaseTimeSinceLastCapture(subject);
|
|
int inPhase = getPub30StaticBaseCapturePhase(subject);
|
|
|
|
switch(inPhase)
|
|
{
|
|
case 1:
|
|
return (PHASE_2 + lastCapture) - getGameTime();
|
|
case 2:
|
|
return (PHASE_3 + lastCapture) - getGameTime();
|
|
case 3:
|
|
return (PHASE_4 + lastCapture) - getGameTime();
|
|
case 4:
|
|
return (PHASE_5 + lastCapture) - getGameTime();
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
string getPub30TimeToNextPhaseString(obj_id subject)
|
|
{
|
|
int timeToNext = getPub30TimeToNextPhaseInt(subject);
|
|
|
|
return utils.formatTimeVerbose(timeToNext);
|
|
}
|
|
|
|
int advancePub30StaticBaseCapturePhase(obj_id subject)
|
|
{
|
|
int currentPhase = getPub30StaticBaseCapturePhase(subject);
|
|
|
|
if(currentPhase == 5)
|
|
return 5;
|
|
|
|
int newTime = 0;
|
|
|
|
switch(currentPhase)
|
|
{
|
|
case 1:
|
|
newTime = getGameTime() - PHASE_2;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 2;
|
|
case 2:
|
|
newTime = getGameTime() - PHASE_3;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 3;
|
|
case 3:
|
|
newTime = getGameTime() - PHASE_4;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 4;
|
|
case 4:
|
|
newTime = getGameTime() - PHASE_5;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 5;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int regressPub30StaticBaseCapturePhase(obj_id subject)
|
|
{
|
|
int currentPhase = getPub30StaticBaseCapturePhase(subject);
|
|
|
|
if(currentPhase == 1)
|
|
return 1;
|
|
|
|
int newTime = 0;
|
|
|
|
switch(currentPhase)
|
|
{
|
|
case 2:
|
|
newTime = getGameTime();
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 1;
|
|
case 3:
|
|
newTime = getGameTime() - PHASE_2;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 2;
|
|
case 4:
|
|
newTime = getGameTime() - PHASE_3;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 3;
|
|
case 5:
|
|
newTime = getGameTime() - PHASE_4;
|
|
setPub30StaticBaseTimeSinceLastCapture(subject, newTime);
|
|
return 4;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
//Functions for the new GCW ranking system
|
|
|
|
void clearCreditForKills(obj_id player)
|
|
{
|
|
utils.removeBatchScriptVar(player, LIST_CREDIT_FOR_KILLS);
|
|
}
|
|
|
|
int findAttackerInArray(resizeable string[] attackList, obj_id attacker)
|
|
{
|
|
if(attackList == null || attackList.length == 0)
|
|
return -1;
|
|
|
|
for(int i = 0; i < attackList.length; i++)
|
|
{
|
|
string[] splitEntry = split(attackList[i], '-');
|
|
obj_id idAtPoint = utils.stringToObjId(splitEntry[0]);
|
|
|
|
if(idAtPoint == attacker)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int getDamageFromAttacker(resizeable string[] attackList, obj_id attacker)
|
|
{
|
|
if(attackList == null || attackList.length == 0)
|
|
return 0;
|
|
|
|
for(int i = 0; i < attackList.length; i++)
|
|
{
|
|
string[] splitEntry = split(attackList[i], '-');
|
|
obj_id idAtPoint = utils.stringToObjId(splitEntry[0]);
|
|
|
|
if(idAtPoint == attacker)
|
|
return utils.stringToInt(splitEntry[1]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
string packAttackerDamage(obj_id attacker, int damage)
|
|
{
|
|
return ("" + attacker + "-" + damage);
|
|
}
|
|
|
|
boolean releaseGcwPointCredit(obj_id player)
|
|
{
|
|
obj_id[] gcwEnemiesList = new obj_id[0];
|
|
|
|
if(verifyPvpRegionStatus(player))
|
|
{
|
|
gcwEnemiesList = getPlayerCreaturesInRange(getLocation(player), 80.0f);
|
|
}
|
|
|
|
boolean isImperialPlayer = factions.isImperial(player);
|
|
boolean isRebelPlayer = factions.isRebel(player);
|
|
|
|
obj_id landedDeathBlow = getObjIdObjVar(player, pclib.VAR_DEATHBLOW_KILLER);
|
|
|
|
if(beast_lib.isBeast(landedDeathBlow) || pet_lib.isPet(landedDeathBlow))
|
|
{
|
|
landedDeathBlow = getMaster(landedDeathBlow);;
|
|
}
|
|
|
|
if(gcwEnemiesList != null && gcwEnemiesList.length > 0)
|
|
{
|
|
removeDayOldEntries(player);
|
|
|
|
// Distribute GCW points
|
|
for(int i = 0; i < gcwEnemiesList.length; i++)
|
|
{
|
|
if(!isIdValid(gcwEnemiesList[i]) || !exists(gcwEnemiesList[i]) || !verifyPvpRegionStatus(gcwEnemiesList[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if((float)getLevel(player) / (float)getLevel(gcwEnemiesList[i]) >= MIN_PVP_LEVEL_RATIO_LIMIT &&
|
|
((isImperialPlayer && factions.isRebel(gcwEnemiesList[i])) ||
|
|
(factions.isImperial(gcwEnemiesList[i]) && isRebelPlayer)))
|
|
{
|
|
int points = distributeIndividualContribution(player, gcwEnemiesList[i], 0, GCW_POINT_TYPE_GROUND_PVP);
|
|
pvpModifyCurrentPvpKills(gcwEnemiesList[i], 1);
|
|
incrementKillMeter(gcwEnemiesList[i], 1);
|
|
}
|
|
|
|
}
|
|
|
|
resizeable string[] attackerList = utils.getResizeableStringBatchScriptVar(player, gcw.LIST_CREDIT_FOR_KILLS);
|
|
|
|
if(attackerList == null || attackerList.length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Distribute battlefield assists
|
|
for(int j = 0; j < attackerList.length; j++)
|
|
{
|
|
string[] parseKiller = split(attackerList[j], '-');
|
|
obj_id killer = utils.stringToObjId(parseKiller[0]);
|
|
|
|
if(beast_lib.isBeast(killer) || pet_lib.isPet(killer))
|
|
{
|
|
killer = getMaster(killer);
|
|
}
|
|
|
|
double vLev = (double)getLevel(player);
|
|
double kLev = (double)getLevel(killer);
|
|
boolean isOfLevel = (vLev / kLev) >= MIN_PVP_LEVEL_RATIO_LIMIT;
|
|
|
|
if(isOfLevel && killer != landedDeathBlow)
|
|
{
|
|
pvp.bfCreditForAssist(killer);
|
|
gcwInvasionCreditForAssist(killer);
|
|
}
|
|
}
|
|
|
|
utils.removeBatchScriptVar(player, gcw.LIST_CREDIT_FOR_KILLS);
|
|
|
|
return true;
|
|
}
|
|
|
|
resizeable string[] attackerList = utils.getResizeableStringBatchScriptVar(player, gcw.LIST_CREDIT_FOR_KILLS);
|
|
|
|
if(attackerList == null || attackerList.length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int totalDamage = 0;
|
|
|
|
for(int q = 0; q < attackerList.length; q++)
|
|
{
|
|
|
|
string[] damageSplit = split(attackerList[q], '-');
|
|
totalDamage += utils.stringToInt(damageSplit[1]);
|
|
}
|
|
|
|
removeDayOldEntries(player);
|
|
|
|
for(int j = 0; j < attackerList.length; j++)
|
|
{
|
|
string[] parseKiller = split(attackerList[j], '-');
|
|
obj_id killer = utils.stringToObjId(parseKiller[0]);
|
|
double vLev = (double)getLevel(player);
|
|
double kLev = (double)getLevel(killer);
|
|
boolean isOfLevel = (vLev/kLev) >= MIN_PVP_LEVEL_RATIO_LIMIT;
|
|
|
|
if(isOfLevel)
|
|
{
|
|
int points = distributeIndividualContribution(player, attackerList[j], totalDamage, GCW_POINT_TYPE_GROUND_PVP);
|
|
|
|
if(isIdValid(killer) && exists(killer))
|
|
{
|
|
pvpModifyCurrentPvpKills(killer, 1);
|
|
incrementKillMeter(killer, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
utils.removeBatchScriptVar(player, gcw.LIST_CREDIT_FOR_KILLS);
|
|
|
|
return true;
|
|
}
|
|
|
|
void notifyPvpRegionWatcherOfDeath(obj_id player)
|
|
{
|
|
obj_id pvpRegionController = gcw.getPvpRegionControllerIdByPlayer(player);
|
|
dictionary dict = new dictionary();
|
|
dict.put("player", player);
|
|
|
|
if (isIdValid(pvpRegionController))
|
|
messageTo(pvpRegionController, "diedInPvpRegion", dict, 1, false);
|
|
|
|
}
|
|
boolean isAlreadyInArray(string[] orderedList, string attackerEntry)
|
|
{
|
|
for(int i = 0; i < orderedList.length; i++)
|
|
{
|
|
if(orderedList[i].equals(attackerEntry))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int distributeIndividualContribution(obj_id victim, string playerString, int totalDamage, int point_type)
|
|
{
|
|
string[] parse = split(playerString, '-');
|
|
obj_id killer = utils.stringToObjId(parse[0]);
|
|
|
|
if(!isIdValid(killer) || !exists(killer))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return distributeIndividualContribution(victim, killer, totalDamage, point_type);
|
|
}
|
|
|
|
int distributeIndividualContribution(obj_id victim, obj_id enemy, int totalDamage, int point_type)
|
|
{
|
|
if(!isIdValid(victim) || !exists(victim) || !isIdValid(enemy) || !exists(enemy))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int targetRank = pvpGetCurrentGcwRank(enemy);
|
|
int selfRank = pvpGetCurrentGcwRank(victim);
|
|
int finalReward = (int) ((targetRank + selfRank) * (GCW_PLAYER_PVP_MODIFIER) / 10);
|
|
|
|
if(finalReward < 0)
|
|
return 0;
|
|
|
|
boolean inRegion = verifyPvpRegionStatus(enemy);
|
|
|
|
if(inRegion)
|
|
{
|
|
finalReward += (int) (finalReward * 0.5f); // 50% bonus for being in a pvp region
|
|
}
|
|
|
|
//Give them a chance to get a faction collectible
|
|
loot.rollRandomFactionalCollectible(victim, enemy, selfRank);
|
|
|
|
// Give them a chance to get a chronicles pvp relic
|
|
obj_id relic = loot.chroniclesPvpLootDrop(enemy);
|
|
|
|
finalReward = addToDailyGcwPointAllotment(victim, enemy, finalReward);
|
|
string information = getName(victim);
|
|
|
|
grantModifiedGcwPoints(victim, enemy, finalReward, true, point_type, information);
|
|
|
|
dictionary webster = new dictionary();
|
|
webster.put("victim", victim);
|
|
webster.put("victimRank", selfRank);
|
|
messageTo(enemy, "recivedGcwCreditForKill", webster, 0.0f, false);
|
|
|
|
return finalReward;
|
|
}
|
|
|
|
int addToDailyGcwPointAllotment(obj_id victim, obj_id killer, int gcwPoint)
|
|
{
|
|
resizeable string[] dailyKills = new string[0];
|
|
|
|
if(utils.hasResizeableStringBatchObjVar(victim, LIST_DAILY_KILL_VALUES))
|
|
{
|
|
dailyKills = utils.getResizeableStringBatchObjVar(victim, LIST_DAILY_KILL_VALUES);
|
|
utils.removeBatchObjVar(victim, LIST_DAILY_KILL_VALUES);
|
|
}
|
|
|
|
int targetRank = pvpGetCurrentGcwRank(killer);
|
|
int selfRank = pvpGetCurrentGcwRank(victim);
|
|
int maxInterval = (int) (targetRank + selfRank) * GCW_PLAYER_PVP_MODIFIER;
|
|
int positionInArray = findAttackerInArray(dailyKills, killer);
|
|
|
|
if(positionInArray == -1)
|
|
{
|
|
if(gcwPoint > maxInterval)
|
|
gcwPoint = maxInterval;
|
|
|
|
string newEntry = packKillerDailyPoints(killer, 1, getGameTime());
|
|
|
|
utils.addElement(dailyKills, newEntry);
|
|
}
|
|
else
|
|
{
|
|
int accruedPoints = getAccruedPoints(dailyKills[positionInArray]);
|
|
int timeAtFirstAward = getTimeOfFirstAward(dailyKills[positionInArray]);
|
|
gcwPoint = gcwPoint > maxInterval ? maxInterval : gcwPoint;
|
|
|
|
if(getGameTime() > timeAtFirstAward + 86400)
|
|
{
|
|
timeAtFirstAward = getGameTime();
|
|
accruedPoints = 1;
|
|
}
|
|
else
|
|
{
|
|
accruedPoints++;
|
|
|
|
if(accruedPoints > 4) // 50% deduction?
|
|
{
|
|
gcwPoint = (int)(gcwPoint * 0.5f); // gcwPoint * 50%
|
|
}
|
|
else
|
|
{
|
|
gcwPoint = (int)(gcwPoint * (float)((10.0f - ((float)accruedPoints - 1.0f)) / 10.0f)); // gcwPoint * (accruedPoints%)
|
|
}
|
|
}
|
|
|
|
string newEntry = packKillerDailyPoints(killer, accruedPoints, timeAtFirstAward);
|
|
|
|
dailyKills[positionInArray] = newEntry;
|
|
}
|
|
|
|
utils.setResizeableBatchObjVar(victim, LIST_DAILY_KILL_VALUES, dailyKills);
|
|
return gcwPoint;
|
|
}
|
|
|
|
void removeDayOldEntries(obj_id player)
|
|
{
|
|
if(!utils.hasResizeableStringBatchObjVar(player, LIST_DAILY_KILL_VALUES))
|
|
return;
|
|
|
|
resizeable string[] dailyList = utils.getResizeableStringBatchObjVar(player, LIST_DAILY_KILL_VALUES);
|
|
|
|
utils.removeBatchObjVar(player, LIST_DAILY_KILL_VALUES);
|
|
int dayPast = getGameTime() - 86400;
|
|
|
|
for(int i = 0; i < dailyList.length; i++)
|
|
{
|
|
string[] parse = split(dailyList[i], '-');
|
|
int timeHack = utils.stringToInt(parse[2]);
|
|
|
|
if(dayPast > timeHack)
|
|
utils.removeElementAt(dailyList, i);
|
|
}
|
|
|
|
utils.setResizeableBatchObjVar(player, LIST_DAILY_KILL_VALUES, dailyList);
|
|
}
|
|
|
|
string packKillerDailyPoints(obj_id player, int gcwPoint, int time)
|
|
{
|
|
return ("" + player + "-" + gcwPoint + "-" + time);
|
|
}
|
|
|
|
int getTimeOfFirstAward(string killRecord)
|
|
{
|
|
string[] parse = split(killRecord, '-');
|
|
|
|
return utils.stringToInt(parse[2]);
|
|
}
|
|
|
|
int getAccruedPoints(resizeable string[] attackList, obj_id attacker)
|
|
{
|
|
if(attackList == null || attackList.length == 0)
|
|
return 0;
|
|
|
|
for(int i = 0; i < attackList.length; i++)
|
|
{
|
|
string[] splitEntry = split(attackList[i], '-');
|
|
obj_id idAtPoint = utils.stringToObjId(splitEntry[0]);
|
|
|
|
if(idAtPoint == attacker)
|
|
return utils.stringToInt(splitEntry[1]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int getAccruedPoints(string killRecord)
|
|
{
|
|
string[] parse = split(killRecord, '-');
|
|
|
|
return utils.stringToInt(parse[1]);
|
|
}
|
|
|
|
void grantModifiedGcwPoints(obj_id victim, obj_id[] attackers, boolean pvpKill, int point_type, string information)
|
|
{
|
|
if(!isIdValid(victim))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(attackers == null || attackers.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < attackers.length; i++)
|
|
{
|
|
if(isIdValid(attackers[i]) && exists(attackers[i]) && getDistance(victim, attackers[i]) < 120.0f)
|
|
{
|
|
grantModifiedGcwPoints(victim, attackers[i], pvpKill, point_type, information);
|
|
}
|
|
}
|
|
}
|
|
|
|
void grantModifiedGcwPoints(obj_id attacker, int pointValue, int point_type, string information)
|
|
{
|
|
pointValue = getModifiedGcwPointValue(attacker, pointValue);
|
|
_grantGcwPoints(null, attacker, pointValue, false, point_type, information);
|
|
}
|
|
|
|
//Only called from NPC's as a victim controls point distribution
|
|
void grantModifiedGcwPoints(obj_id victim, obj_id attacker, boolean pvpKill, int point_type, string information)
|
|
{
|
|
int pointValue = getNpcKillCredit(victim, attacker);
|
|
|
|
grantModifiedGcwPoints(victim, attacker, pointValue, pvpKill, point_type, information);
|
|
}
|
|
|
|
void grantModifiedGcwPoints(obj_id victim, obj_id attacker, int pointValue, boolean pvpKill, int point_type, string information)
|
|
{
|
|
pointValue = getModifiedGcwPointValue(attacker, pointValue);
|
|
_grantGcwPoints(victim, attacker, pointValue, pvpKill, point_type, information);
|
|
}
|
|
|
|
void grantUnmodifiedGcwPoints(obj_id attacker, int pointValue)
|
|
{
|
|
_grantGcwPoints(null, attacker, pointValue, false, -1, "");
|
|
}
|
|
|
|
void _grantGcwPoints(obj_id victim, obj_id attacker, int pointValue, boolean pvpKill, int pointType, string information)
|
|
{
|
|
if(!isIdValid(attacker) || !exists(attacker) || pointValue < 1 || pet_lib.isPet(victim))
|
|
return;
|
|
|
|
|
|
float multiplier = utils.stringToFloat(getConfigSetting("GameServer", "gcwPointBonus"));
|
|
|
|
if(multiplier > 1)
|
|
{
|
|
pointValue *= multiplier; // Precision lost.
|
|
}
|
|
|
|
pvpModifyCurrentGcwPoints(attacker, pointValue);
|
|
|
|
prose_package pp = new prose_package();
|
|
|
|
pp.target.set(getName(victim));
|
|
pp.digitInteger = pointValue;
|
|
|
|
if(pvpKill)
|
|
{
|
|
pp.stringId = SID_PVP_KILL_POINT_GRANT;
|
|
sendSystemMessageProse(attacker, pp);
|
|
}
|
|
else
|
|
{
|
|
pp.stringId = SID_GENERIC_POINT_GRANT;
|
|
sendSystemMessageProse(attacker, pp);
|
|
}
|
|
|
|
doGcwPointCsLogging(attacker, pointValue, pointType, information);
|
|
gcwInvasionCreditForGCW(attacker, pointValue);
|
|
grantGcwPointsToRegion(attacker, pointValue, pointType);
|
|
}
|
|
|
|
void doGcwPointCsLogging(obj_id player, int pointValue, int pointType, string information)
|
|
{
|
|
switch(pointType)
|
|
{
|
|
case GCW_POINT_TYPE_GROUND_PVE:
|
|
CustomerServiceLog("GCW_points_ground_pve", "%TU has acquired " + pointValue + " points for defeating " + information + " in pve action", player);
|
|
break;
|
|
case GCW_POINT_TYPE_GROUND_PVP:
|
|
CustomerServiceLog("GCW_points_ground_pvp", "%TU has acquired " + pointValue + " points for defeating " + information + " in combat", player);
|
|
break;
|
|
case GCW_POINT_TYPE_GROUND_PVP_REGION:
|
|
CustomerServiceLog("GCW_points_ground_pvp_region", "%TU has acquired " + pointValue + " points in the " + information + " battlefield region.", player);
|
|
break;
|
|
case GCW_POINT_TYPE_BASE_BUSTING:
|
|
CustomerServiceLog("GCW_points_player_base_busting", "%TU has acquired " + pointValue + " points for destroying " + information, player);
|
|
break;
|
|
case GCW_POINT_TYPE_SPACE_PVE:
|
|
CustomerServiceLog("GCW_points_space_pve", "%TU has acquired " + pointValue + " points for destroying " + information, player);
|
|
break;
|
|
case GCW_POINT_TYPE_SPACE_PVP:
|
|
CustomerServiceLog("GCW_points_space_pvp", "%TU has acquired " + pointValue + " points for defeating " + information + " in space combat", player);
|
|
break;
|
|
case GCW_POINT_TYPE_GROUND_QUEST:
|
|
CustomerServiceLog("GCW_points_ground_quest", "%TU has acquired " + pointValue + " points for completing the " + information + " groundquest", player);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
string getGcwCategory(int pointType)
|
|
{
|
|
if(pointType < 0 || pointType >= GCW_POINT_TYPE_MAX)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string pointCategory = pointTypes[pointType];
|
|
string scene = getCurrentSceneName();
|
|
|
|
int sceneIndex = utils.getElementPositionInArray(validScenes, scene);
|
|
|
|
// GCW does not mean anything in specific scenes, such as dungeons, mustafar, and deep space
|
|
if(sceneIndex < 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return scene + "_" + pointCategory;
|
|
}
|
|
|
|
string getGcwRegion(obj_id target)
|
|
{
|
|
if(!isIdValid(target) || !exists(target))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
region[] regionList = getRegionsAtPoint(getLocation(target));
|
|
string regionName = "";
|
|
|
|
if(regionList != null && regionList.length > 0)
|
|
{
|
|
for(int i = 0, j = regionList.length; i < j; i++)
|
|
{
|
|
region currentRegion = regionList[i];
|
|
|
|
if(currentRegion == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string currentRegionName = currentRegion.getName();
|
|
|
|
if(currentRegionName != null && currentRegionName.length() > 0 && currentRegionName.startsWith("gcw_region"))
|
|
{
|
|
regionName = currentRegionName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If you are not in a region, then the planet-wide region name is used.
|
|
if(regionName.length() <= 0)
|
|
{
|
|
string scene = getCurrentSceneName();
|
|
|
|
int sceneIndex = utils.getElementPositionInArray(validScenes, scene);
|
|
|
|
if(sceneIndex >= 0 && sceneIndex < defaultRegions.length)
|
|
{
|
|
regionName = defaultRegions[sceneIndex];
|
|
}
|
|
}
|
|
|
|
return regionName;
|
|
}
|
|
|
|
void grantGcwPointsToRegion(obj_id player, int pointValue, int pointType)
|
|
{
|
|
if(!isIdValid(player) || pointValue < 0 || pointType < 0 || pointType >= GCW_POINT_TYPE_MAX)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string category = getGcwCategory(pointType);
|
|
|
|
string regionName = getGcwRegion(player);
|
|
|
|
if(category == null || category.length() <= 0 || regionName == null || regionName.length() <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(factions.isImperial(player))
|
|
{
|
|
// General category that the points should be added to.
|
|
adjustGcwImperialScore("grantGcwPointsToCategory", player, category, pointValue);
|
|
|
|
// Region that the points were gained in
|
|
adjustGcwImperialScore("grantGcwPointsToRegion", player, regionName, pointValue);
|
|
|
|
LOG("gcw_region_points", "Imperial gain. regionName: " + regionName + " category: " + category + " pointValue: " + pointValue);
|
|
}
|
|
else if(factions.isRebel(player))
|
|
{
|
|
// General category that the points should be added to.
|
|
adjustGcwRebelScore("grantGcwPointsToCategory", player, category, pointValue);
|
|
|
|
// Region that the points were gained in
|
|
adjustGcwRebelScore("grantGcwPointsToRegion", player, regionName, pointValue);
|
|
|
|
LOG("gcw_region_points", "Rebel gain. regionName: " + regionName + " category: " + category + " pointValue: " + pointValue);
|
|
}
|
|
}
|
|
|
|
int getImperialPercentileByRegion(obj_id target)
|
|
{
|
|
if(!isIdValid(target) || !exists(target))
|
|
{
|
|
return 50; // Invalid target means the percentage is equal (ie: a tie between factions)
|
|
}
|
|
|
|
string category = getGcwRegion(target);
|
|
|
|
if(category == null || category.length() <= 0)
|
|
{
|
|
return 50;
|
|
}
|
|
|
|
return getGcwImperialScorePercentile(category);
|
|
}
|
|
|
|
int getRebelPercentileByRegion(obj_id target)
|
|
{
|
|
return 100 - getImperialPercentileByRegion(target);
|
|
}
|
|
|
|
string getRegionFactionOwner(obj_id target)
|
|
{
|
|
string category = getGcwRegion(target);
|
|
|
|
if(category == null || category.length() <= 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int imperial = getImperialPercentileByRegion(target);
|
|
|
|
if(imperial > 50)
|
|
{
|
|
return "imperial";
|
|
}
|
|
|
|
if(imperial < 50)
|
|
{
|
|
return "rebel";
|
|
}
|
|
|
|
// Exception is 50% (equal ownership)
|
|
|
|
return null; // Nobody owns this region.
|
|
}
|
|
|
|
boolean verifyPvpRegionStatus(obj_id player)
|
|
{
|
|
obj_id region_controller = getPvpRegionControllerIdByPlayer(player);
|
|
|
|
if(!isIdValid(region_controller))
|
|
return false;
|
|
|
|
utils.setScriptVar(player, PVP_REGION_ACTIVITY_PERFORMED, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
int getNpcKillCredit(obj_id npc, obj_id player)
|
|
{
|
|
if(isPlayer(npc) || pet_lib.isPet(npc))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Faction check, must be opposed.
|
|
string faction = factions.getFaction(npc);
|
|
|
|
if(faction == null || faction.equals(""))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(!faction.equals("Rebel") && !faction.equals("Imperial"))
|
|
return 0;
|
|
|
|
if(faction.equals("Rebel") && !factions.isImperial(player))
|
|
return 0;
|
|
|
|
if(faction.equals("Imperial") && !factions.isRebel(player))
|
|
return 0;
|
|
|
|
double npcLev = (double) getLevel(npc);
|
|
double playLev = (double) getLevel(player);
|
|
|
|
if((npcLev / playLev) < MIN_NPC_LEVEL_RATIO_LIMIT)
|
|
return 0;
|
|
|
|
int difficultyClass = getIntObjVar(npc, "difficultyClass");
|
|
|
|
switch(difficultyClass)
|
|
{
|
|
case 0:
|
|
return NORMAL_GCW_VALUE;
|
|
case 1:
|
|
return ELITE_GCW_VALUE;
|
|
case 2:
|
|
return BOSS_GCW_VALUE;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int getModifiedGcwPointValue(obj_id player, int passedValue)
|
|
{
|
|
float mod = 1.0f;
|
|
|
|
// Entertainer build-a-buff xp bonus
|
|
if(utils.hasScriptVar(player, "buff.xpBonus.value"))
|
|
mod += utils.getFloatScriptVar(player, "buff.xpBonus.value");
|
|
|
|
// All other xp bonus buffs - yes, this will stack with the build-a-buff one.
|
|
if(utils.hasScriptVar(player, "buff.xpBonusGeneral.value"))
|
|
mod += utils.getFloatScriptVar(player, "buff.xpBonusGeneral.value");
|
|
|
|
// GCW bonus buffs
|
|
if(utils.hasScriptVar(player, "buff.gcwBonusGeneral.value"))
|
|
mod += utils.getFloatScriptVar(player, "buff.gcwBonusGeneral.value");
|
|
|
|
//To do: figure out how the damn planetary control system works.
|
|
|
|
if(isSpaceBattlefieldZone())
|
|
mod += .1f;
|
|
|
|
return (int) (mod * passedValue);
|
|
}
|
|
|
|
void registerPvpRegionControllerWithPlanet(obj_id controlObject, string regionName)
|
|
{
|
|
obj_id planetId = getPlanetByName(getLocation(controlObject).area);
|
|
|
|
utils.setScriptVar(planetId, REGION_CONTROLLER + "." + regionName, controlObject);
|
|
}
|
|
|
|
obj_id getPvpRegionControllerIdByName(obj_id player, string regionName)
|
|
{
|
|
obj_id planetId = getPlanetByName(getLocation(player).area);
|
|
string packedRegion = REGION_CONTROLLER + "." + regionName;
|
|
|
|
return utils.hasScriptVar(planetId, packedRegion) ? utils.getObjIdScriptVar(planetId, packedRegion) : null;
|
|
}
|
|
|
|
obj_id getPvpRegionControllerIdByPlayer(obj_id player)
|
|
{
|
|
region[] regionList = getRegionsAtPoint(getLocation(player));
|
|
obj_id planetId = getPlanetByName(getLocation(player).area);
|
|
obj_id regionController = null;
|
|
|
|
if(regionList == null || regionList.length == 0)
|
|
return null;
|
|
|
|
for(int i = 0; i < regionList.length; i++)
|
|
{
|
|
string regionName = regionList[i].getName();
|
|
string packedRegion = REGION_CONTROLLER + "." + regionName;
|
|
|
|
if(utils.hasScriptVar(planetId, packedRegion))
|
|
regionController = utils.getObjIdScriptVar(planetId, packedRegion);
|
|
}
|
|
|
|
return regionController;
|
|
}
|
|
|
|
void notifyPvpRegionControllerOfPlayerEnter(obj_id controllerId, obj_id player)
|
|
{
|
|
dictionary dict = new dictionary();
|
|
|
|
dict.put("player", player);
|
|
messageTo(controllerId, "newPlayerNotify", dict, 0, false);
|
|
|
|
doLogging("GCW_points_ground_pvp", "notifyPvpRegionControllerOfPlayerEnter" + getName(player) + "/" + player + " has entered the region. Notifying controller object " + controllerId);
|
|
}
|
|
|
|
void makeBattlefieldRegion(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makeBattlefieldRegion controller not valid");
|
|
return;
|
|
}
|
|
|
|
// We need to build a unique region name that is keyed for the control object on the planet with a scriptvar.
|
|
// This is so we can communicate with the control object across server boundaries.
|
|
string regionName = getBattlefieldRegionName(controller);
|
|
|
|
if(regionName == null || regionName.length() < 1)
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makeBattlefieldRegion regionName invalid");
|
|
return;
|
|
}
|
|
|
|
location whereIam = getLocation(controller);
|
|
|
|
region battlefieldRegion = getRegion(whereIam.area, regionName);
|
|
|
|
// Pushback region already created?
|
|
if(battlefieldRegion != null)
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makeBattlefieldRegion registerPvpRegionControllerWithPlanet battlefieldRegion exists");
|
|
registerPvpRegionControllerWithPlanet(controller, regionName);
|
|
return;
|
|
}
|
|
|
|
doLogging("GCW_points_ground_pvp", "makeBattlefieldRegion registerPvpRegionControllerWithPlanet failed");
|
|
}
|
|
|
|
// The pushback region is much like battlefield regions, except it surrounds the battlefield region. It needs to deny access to players
|
|
// not on the teams currently playing in the battlefield. It does this by SCRIPT_OVERRIDE in the base_player OnEnterRegion trigger. If the player
|
|
// attempts to get in repeatedly, the player should be warped away from the region. This lowers the battlefield's lag be reducing active players near
|
|
// the battlefield's server boundary.
|
|
void makePushbackRegion(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makePushbackRegion controller not valid");
|
|
return;
|
|
}
|
|
|
|
string regionName = getPushbackRegionName(controller);
|
|
|
|
if(regionName == null || regionName.length() < 1)
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makePushbackRegion regionName invalid");
|
|
return;
|
|
}
|
|
|
|
location whereIam = getLocation(controller);
|
|
|
|
region pushbackRegion = getRegion(whereIam.area, regionName);
|
|
|
|
// Pushback region already created?
|
|
if(pushbackRegion != null)
|
|
{
|
|
doLogging("GCW_points_ground_pvp", "makePushbackRegion registerPvpRegionControllerWithPlanet pushbackRegion exists");
|
|
registerPvpRegionControllerWithPlanet(controller, regionName);
|
|
return;
|
|
}
|
|
|
|
doLogging("GCW_points_ground_pvp", "makePushbackRegion registerPvpRegionControllerWithPlanet failed");
|
|
}
|
|
|
|
obj_id getPushbackControllerByPlayer(obj_id player)
|
|
{
|
|
return getPvpRegionControllerIdByPlayer(player);
|
|
}
|
|
|
|
//Space kills grant full credit.
|
|
void grantSpacePvpKillCredit(obj_id defender, obj_id[] attackers)
|
|
{
|
|
resizeable obj_id[] validAttackers = new obj_id[0];
|
|
|
|
for(int i = 0; i < attackers.length; i++)
|
|
{
|
|
if(factions.isImperial(defender))
|
|
{
|
|
if(factions.isRebel(attackers[i]) && validateSpaceTier(defender, attackers[i]))
|
|
utils.addElement(validAttackers, attackers[i]);
|
|
else
|
|
LOG("doLogging", "Faction or tier error");
|
|
}
|
|
if(factions.isRebel(defender))
|
|
{
|
|
if(factions.isImperial(attackers[i]) && validateSpaceTier(defender, attackers[i]))
|
|
utils.addElement(validAttackers, attackers[i]);
|
|
else
|
|
LOG("doLogging", "Faction or tier error");
|
|
}
|
|
}
|
|
|
|
if(validAttackers == null || validAttackers.length < 1)
|
|
return;
|
|
|
|
for(int k = 0; k < validAttackers.length; k++)
|
|
{
|
|
string packedAttacker = "" + validAttackers[k] + "-" + 1;
|
|
int reward = distributeIndividualContribution(defender, packedAttacker, validAttackers.length, GCW_POINT_TYPE_SPACE_PVP);
|
|
|
|
pvpModifyCurrentPvpKills(validAttackers[k], 1);
|
|
}
|
|
}
|
|
|
|
boolean validateSpaceTier(obj_id defender, obj_id attacker)
|
|
{
|
|
int defLevel = space_flags.getPilotTier(defender);
|
|
int atkLevel = space_flags.getPilotTier(attacker);
|
|
|
|
switch(defLevel)
|
|
{
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
if(atkLevel > 2)
|
|
return false;
|
|
case 2:
|
|
if(atkLevel > 3)
|
|
return false;
|
|
case 3:
|
|
break;
|
|
case 4:
|
|
break;
|
|
case 5:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int getSpaceKillCredit(int player_level, string targetTier)
|
|
{
|
|
double pointGrant = 0.0;
|
|
double pointIncriment = SPACE_GCW_VALUE;
|
|
|
|
switch(player_level)
|
|
{
|
|
case 1:
|
|
if(targetTier.equals("tier1"))
|
|
pointGrant = pointIncriment;
|
|
if(targetTier.equals("tier2") || targetTier.equals("tier3") || targetTier.equals("tier4") || targetTier.equals("tier5"))
|
|
pointGrant = pointIncriment * 1.5;
|
|
break;
|
|
case 2:
|
|
if(targetTier.equals("tier1"))
|
|
pointGrant = (pointIncriment * 2) / 2;
|
|
|
|
if(targetTier.equals("tier2"))
|
|
pointGrant = pointIncriment * 2;
|
|
|
|
if(targetTier.equals("tier3") || targetTier.equals("tier4") || targetTier.equals("tier5"))
|
|
pointGrant = pointIncriment * 2.5;
|
|
break;
|
|
case 3:
|
|
if(targetTier.equals("tier2"))
|
|
pointGrant = (pointIncriment * 3) / 2;
|
|
|
|
if(targetTier.equals("tier3"))
|
|
pointGrant = pointIncriment * 3;
|
|
|
|
if(targetTier.equals("tier4") || targetTier.equals("tier5"))
|
|
pointGrant = pointIncriment * 3.5;
|
|
break;
|
|
case 4:
|
|
if(targetTier.equals("tier3"))
|
|
pointGrant = (pointIncriment * 4) / 2;
|
|
|
|
if(targetTier.equals("tier4"))
|
|
pointGrant = pointIncriment * 4;
|
|
|
|
if(targetTier.equals("tier5"))
|
|
pointGrant = pointIncriment * 4.5;
|
|
break;
|
|
case 5:
|
|
if(targetTier.equals("tier4"))
|
|
pointGrant = (pointIncriment * 5) / 2;
|
|
|
|
if(targetTier.equals("tier5"))
|
|
pointGrant = pointIncriment * 5;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (int) pointGrant;
|
|
}
|
|
|
|
void grantBaseDestructionPoints(obj_id base)
|
|
{
|
|
obj_id[] interiorPlayers = trial.getPlayersInDungeon(base);
|
|
obj_id[] exteriorPlayers = getPlayerCreaturesInRange(getLocation(base), 120.0f);
|
|
|
|
string baseFac = factions.getFaction(base);
|
|
|
|
if(baseFac == null || (!baseFac.equals("Rebel") && !baseFac.equals("Imperial")))
|
|
{
|
|
CustomerServiceLog("GCW_points_player_base_busting", "Base(" + getName(base) + "/" + base + "/" + baseFac + ") did not have a Rebel or Imperial faction");
|
|
return;
|
|
}
|
|
|
|
resizeable obj_id[] filteredList = new obj_id[0];
|
|
|
|
if(interiorPlayers != null && interiorPlayers.length > 0)
|
|
{
|
|
for(int i = 0; i < interiorPlayers.length; i++)
|
|
{
|
|
if(isIdValid(interiorPlayers[i]) && exists(interiorPlayers[i]))
|
|
{
|
|
string playerFac = factions.getFaction(interiorPlayers[i]);
|
|
|
|
if(playerFac == null)
|
|
continue;
|
|
|
|
if(baseFac.equals("Rebel") && playerFac.equals("Imperial"))
|
|
utils.addElement(filteredList, interiorPlayers[i]);
|
|
|
|
if(baseFac.equals("Imperial") && playerFac.equals("Rebel"))
|
|
utils.addElement(filteredList, interiorPlayers[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(exteriorPlayers != null && exteriorPlayers.length > 0)
|
|
{
|
|
for(int k = 0; k < exteriorPlayers.length; k++)
|
|
{
|
|
if(isIdValid(exteriorPlayers[k]) && exists(exteriorPlayers[k]))
|
|
{
|
|
string playerFac = factions.getFaction(exteriorPlayers[k]);
|
|
|
|
if(playerFac == null)
|
|
continue;
|
|
|
|
if(baseFac.equals("Rebel") && playerFac.equals("Imperial"))
|
|
{
|
|
if(filteredList.length > 0)
|
|
{
|
|
if(utils.getElementPositionInArray(filteredList, exteriorPlayers[k]) == -1)
|
|
utils.addElement(filteredList, exteriorPlayers[k]);
|
|
|
|
}
|
|
else
|
|
utils.addElement(filteredList, exteriorPlayers[k]);
|
|
}
|
|
|
|
if(baseFac.equals("Imperial") && playerFac.equals("Rebel"))
|
|
{
|
|
if(filteredList.length > 0)
|
|
{
|
|
if(utils.getElementPositionInArray(filteredList, exteriorPlayers[k]) == -1)
|
|
utils.addElement(filteredList, exteriorPlayers[k]);
|
|
}
|
|
else
|
|
utils.addElement(filteredList, exteriorPlayers[k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string table = GCW_PLAYER_BASE_POINT_VALUE;
|
|
|
|
if(!dataTableOpen(table))
|
|
{
|
|
CustomerServiceLog("GCW_points_player_base_busting", "Could not open the " + GCW_PLAYER_BASE_POINT_VALUE + " data table");
|
|
return;
|
|
}
|
|
|
|
string template = getTemplateName(base);
|
|
int value = dataTableGetInt(table, template, "point_value");
|
|
|
|
if(value < -1)
|
|
{
|
|
CustomerServiceLog("GCW_points_player_base_busting", "template " + template + " could not be found in the dataTable " + GCW_PLAYER_BASE_POINT_VALUE);
|
|
return;
|
|
}
|
|
value *= GCW_PLAYER_BASE_DESTRUCTION;
|
|
|
|
if(filteredList == null || filteredList.length == 0)
|
|
{
|
|
CustomerServiceLog("GCW_points_player_base_busting", "There are no valid players in the filtered list");
|
|
return;
|
|
}
|
|
for(int j = 0; j < filteredList.length; j++)
|
|
{
|
|
grantModifiedGcwPoints(filteredList[j], value, gcw.GCW_POINT_TYPE_BASE_BUSTING, template);
|
|
}
|
|
}
|
|
|
|
int getGcwGroundQuestAward(obj_id player, int quest_tier)
|
|
{
|
|
if(!factions.isImperial(player) && !factions.isRebel(player))
|
|
return 0;
|
|
|
|
float tierValue = getGroundQuestTierBonus(quest_tier);
|
|
|
|
int rank = pvpGetCurrentGcwRank(player);
|
|
string gcwRankTable = "datatables/gcw/gcw_rank.iff";
|
|
|
|
int rankCapBase = dataTableGetInt(gcwRankTable, 0, "RatingEarningCap");
|
|
int rankCap = dataTableGetInt(gcwRankTable, rank - 1, "RatingEarningCap");
|
|
|
|
float groundQuestGcwValue = tierValue * 5000; //All ranks are worth 5000 rating.
|
|
float capDeltaBonus = rankCapBase / rankCap;
|
|
|
|
groundQuestGcwValue *= capDeltaBonus;
|
|
|
|
return (int) groundQuestGcwValue;
|
|
}
|
|
|
|
float getGroundQuestTierBonus(int quest_tier)
|
|
{
|
|
float tierValue = 0.0f;
|
|
|
|
switch(quest_tier)
|
|
{
|
|
case 1:
|
|
tierValue = 0.03f;
|
|
break;
|
|
case 2:
|
|
tierValue = 0.039f;
|
|
break;
|
|
case 3:
|
|
tierValue = 0.047f;
|
|
break;
|
|
case 4:
|
|
tierValue = 0.054f;
|
|
break;
|
|
case 5:
|
|
tierValue = 0.06f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return tierValue;
|
|
}
|
|
|
|
void doLogging(string section, string message)
|
|
{
|
|
CustomerServiceLog(section, message);
|
|
}
|
|
|
|
region getPvPRegion(obj_id controller)
|
|
{
|
|
region[] regions = getRegionsAtPoint(getLocation(controller));
|
|
|
|
if(regions == null || regions.length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
region pvpRegion = null;
|
|
|
|
for(int i = 0; i < regions.length; i++)
|
|
{
|
|
if(isNotifyRegion(regions[i]))
|
|
{
|
|
pvpRegion = regions[i];
|
|
}
|
|
}
|
|
|
|
return pvpRegion;
|
|
}
|
|
|
|
void getRegionToRegister(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
region pvpRegion = getPvPRegion(controller);
|
|
|
|
if(pvpRegion == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string regionName = pvpRegion.getName();
|
|
|
|
if(regionName != null)
|
|
{
|
|
utils.setScriptVar(controller, "pvp_region", regionName);
|
|
|
|
registerPvpRegionControllerWithPlanet(controller, regionName);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Validates player, battlefield, team player is on, faction
|
|
boolean isPlayerValidOnBattlefield(obj_id player, obj_id controller)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// More than one battlefield region may exist on the server and they cannot have the same name.
|
|
// So, the regions are keyed by their controller's location.
|
|
string getBattlefieldRegionName(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
location loc = getLocation(controller);
|
|
|
|
if(loc == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return PVP_BATTLEFIELD_REGION + "_" + loc.x + "_" + loc.z;
|
|
}
|
|
|
|
// More than one pushback region may exist on the server and they cannot have the same name.
|
|
// So, the regions are keyed by their controller's location.
|
|
string getPushbackRegionName(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
location loc = getLocation(controller);
|
|
|
|
if(loc == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return PVP_PUSHBACK_REGION + "_" + loc.x + "_" + loc.z;
|
|
}
|
|
|
|
/** entertainGcwNonPlayingCharacter
|
|
* Checks if a player is valid to entertain the GCW NPC. If valid,
|
|
* allows the NPC to be entertained
|
|
* @param player the id of the entertainer
|
|
* @param npc the id of the GCW NPC about to be entertained
|
|
* @return true if ...
|
|
*/
|
|
boolean canEntertainGcwNonPlayingCharacter(obj_id player, obj_id npc)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
if(!isValidId(npc) || !exists(npc))
|
|
return false;
|
|
|
|
if(!factions.isSameFactionorFactionHelper(player, npc))
|
|
return false;
|
|
|
|
string entertainerFaction = factions.getFaction(player);
|
|
if(!factions.isImperialHelper(player) && !factions.isRebelHelper(player)
|
|
&& (entertainerFaction == null || entertainerFaction.length() <= 0))
|
|
{
|
|
//player is non-combatant or neutal
|
|
sendSystemMessage(player, SID_YOU_NEED_TO_BE_COMBATANT);
|
|
doAnimationAction (npc, "shake_head_no");
|
|
return false;
|
|
}
|
|
|
|
if(!groundquests.isQuestActive(player, GCW_ENTERTAINER_PATROL_QUEST))
|
|
{
|
|
sendSystemMessage(player, SID_YOU_NEED_ENTERTAINMENT_QUEST);
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(npc, GCW_NPC_ENTERTAINED_BUFF))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean setEntertainGcwNonPlayerCharacter(obj_id player, obj_id npc)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
if(!isValidId(npc) || !exists(npc))
|
|
return false;
|
|
if(!canEntertainGcwNonPlayingCharacter(player, npc))
|
|
return false;
|
|
|
|
int flags = 0;
|
|
|
|
flags |= sui.CD_EVENT_INCAPACITATE;
|
|
flags |= sui.CD_EVENT_DAMAGED;
|
|
flags |= sui.CD_EVENT_COMBAT;
|
|
flags |= sui.CD_EVENT_LOCOMOTION;
|
|
flags |= sui.CD_EVENT_STEALTHED;
|
|
|
|
stealth.testInvisNonCombatAction(player, npc);
|
|
|
|
utils.setScriptVar(player, GCW_SCRIPTVAR_PARENT + ".playerEntertainmentStart", getGameTime());
|
|
utils.setScriptVar(player, GCW_SCRIPTVAR_PARENT + ".gcwNpc", npc);
|
|
utils.setScriptVar(npc, GCW_SCRIPTVAR_PARENT + ".isGcwEntertained", 1);
|
|
LOG("gcw_entertainer","gcwNpc: "+npc);
|
|
|
|
int pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_ENTERTAIN_TROOPS_TIMER, 0, GCW_ENTERTAINMENT_TIME, "handleEntertainingGcwTroops", 0, flags);
|
|
sui.setPid(player, pid, ENTERTAIN_GCW_TROOPS_PID);
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean canGcwObjectBeRepaired(obj_id object)
|
|
{
|
|
LOG("gcw_patrol_point","canGcwObjectBeRepaired");
|
|
|
|
if(!isValidId(object) || !exists(object))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(object, "repairCount") && getIntObjVar(object, "repairCount") >= REPAIR_COUNT_MAX)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int currentHp = getHitpoints(object);
|
|
if(currentHp <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int maxHp = getMaxHitpoints(object);
|
|
if(maxHp <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(currentHp == maxHp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
//int threshold = maxHp - currentHp >= GCW_REPAIR_AMOUNT ? GCW_REPAIR_AMOUNT : 0;
|
|
|
|
int threshold = maxHp - currentHp;
|
|
|
|
if(threshold <= MIN_REPAIR_AMOUNT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","canGcwObjectBeRepaired passed all validation");
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean useGcwObjectForQuest(obj_id player, obj_id gcwObject, string questName)
|
|
{
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest init");
|
|
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
if(!isValidId(gcwObject) || !exists(gcwObject))
|
|
return false;
|
|
|
|
int flags = 0;
|
|
|
|
flags |= sui.CD_EVENT_INCAPACITATE;
|
|
flags |= sui.CD_EVENT_LOCOMOTION;
|
|
flags |= sui.CD_EVENT_STEALTHED;
|
|
|
|
stealth.testInvisNonCombatAction(player, gcwObject);
|
|
|
|
int timeToRepair = 10 + getFatigueTimerMod(player);
|
|
|
|
if(timeToRepair > GCW_FATIGUE_TIMER_MAX)
|
|
{
|
|
timeToRepair = GCW_FATIGUE_TIMER_MAX;
|
|
}
|
|
|
|
int pid = -1;
|
|
|
|
if(questName.equals(GCW_SPY_PATROL_DESTROY_QUEST))
|
|
{
|
|
timeToRepair = 5;
|
|
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleOpposingFactionDestroyQuest");
|
|
|
|
utils.setScriptVar(player, "spyPatrolPoint", gcwObject);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_DESTROY_PP_TIMER, 0, timeToRepair, "handleOpposingFactionDestroyQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.SPY_DESTROY_PID);
|
|
}
|
|
else if(questName.equals(GCW_SPY_PATROL_SCOUT_QUEST))
|
|
{
|
|
timeToRepair = 5;
|
|
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleOpposingFactionScoutQuest");
|
|
utils.setScriptVar(player, "spyPatrolPoint", gcwObject);
|
|
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_SCOUT_PP_TIMER, 0, timeToRepair, "handleOpposingFactionScoutQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.SPY_SCOUT_PID);
|
|
}
|
|
else if(questName.equals(GCW_REPAIR_PATROL_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, gcw.OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_QUEST, gcw.GCW_REPAIR_PATROL_QUEST);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_RESOURCE_COUNT, gcw.GCW_RESOURCE_COUNT_PATROL);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_REPAIR_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
else if(questName.equals(GCW_REPAIR_TURRET_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, gcw.OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_QUEST, gcw.GCW_REPAIR_TURRET_QUEST);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_RESOURCE_COUNT, gcw.GCW_RESOURCE_COUNT_TURRET);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_REPAIR_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
else if(questName.equals(GCW_REPAIR_BARRICADE_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, gcw.OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_QUEST, gcw.GCW_REPAIR_BARRICADE_QUEST);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_RESOURCE_COUNT, gcw.GCW_RESOURCE_COUNT_BARRICADE);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_REPAIR_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
else if(questName.equals(GCW_REPAIR_VEHICLE_PATROL_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, gcw.OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_QUEST, gcw.GCW_REPAIR_VEHICLE_PATROL_QUEST);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_RESOURCE_COUNT, gcw.GCW_RESOURCE_COUNT_VEHICLE);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_REPAIR_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
else if(questName.equals(GCW_REPAIR_DAMAGED_VEHICLE_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, gcw.OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_QUEST, gcw.GCW_REPAIR_DAMAGED_VEHICLE_QUEST);
|
|
utils.setScriptVar(player, gcw.GCW_REPAIR_RESOURCE_COUNT, gcw.GCW_RESOURCE_COUNT_DAMAGED_VEHICLE);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_REPAIR_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
else if(questName.equals(GCW_MEDIC_HEAL_QUEST))
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
timeToRepair = 3;
|
|
}
|
|
|
|
LOG("gcw_patrol_point","useGcwObjectForQuest sending to messageHandler handleTraderRepairQuest");
|
|
utils.setScriptVar(player, OBJECT_TO_REPAIR, gcwObject);
|
|
utils.setScriptVar(player, GCW_REPAIR_QUEST, GCW_MEDIC_HEAL_QUEST);
|
|
utils.setScriptVar(player, GCW_REPAIR_RESOURCE_COUNT, GCW_RESOURCE_COUNT_DAMAGED_VEHICLE);
|
|
pid = sui.smartCountdownTimerSUI(player, player, "quest_countdown_timer", SID_HEAL_PP_TIMER, 0, timeToRepair, "handleTraderRepairQuest", 0, flags);
|
|
sui.setPid(player, pid, gcw.TRADER_REPAIR_PID);
|
|
}
|
|
|
|
if(pid < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean repairGcwObject(obj_id object, obj_id player, int resourceCount)
|
|
{
|
|
if(!isValidId(object) || !exists(object))
|
|
{
|
|
return false;
|
|
}
|
|
if(!isValidId(player) || !exists(player))
|
|
{
|
|
return false;
|
|
}
|
|
if(resourceCount <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(object, GCW_OBJECT_REPAIR_COUNT) && getIntObjVar(object, GCW_OBJECT_REPAIR_COUNT) >= REPAIR_COUNT_MAX)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Barricades, patrol points, turrets...
|
|
if(!isMob(object))
|
|
{
|
|
int currentHp = getHitpoints(object);
|
|
|
|
if(currentHp <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int maxHp = getMaxHitpoints(object);
|
|
|
|
if(maxHp <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//int threshold = maxHp - currentHp > resourceCount ? resourceCount : 0;
|
|
int threshold = maxHp - currentHp;
|
|
|
|
if(threshold <= MIN_REPAIR_AMOUNT)
|
|
{
|
|
sendSystemMessage(player, SID_DOESNT_NEED_REPAIR);
|
|
return false;
|
|
}
|
|
|
|
double subtract = resourceCount * .2f;
|
|
|
|
if((int)subtract <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
setMaxHitpoints(object, maxHp - (int)subtract);
|
|
setHitpoints(object, currentHp + (int)(resourceCount * .8f));
|
|
}
|
|
|
|
int repairCount = getIntObjVar(object, GCW_OBJECT_REPAIR_COUNT);
|
|
|
|
setObjVar(object, GCW_OBJECT_REPAIR_COUNT, repairCount + 1);
|
|
|
|
messageTo(object, "repairComplete", null, 1.0f, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
void playQuestIconParticle(obj_id self)
|
|
{
|
|
dictionary params = new dictionary();
|
|
|
|
location loc = getLocation(self);
|
|
|
|
params.put("particleLoc", loc);
|
|
|
|
int playIconTime = getGameTime();
|
|
|
|
params.put("iconMessageTime", playIconTime);
|
|
|
|
utils.setScriptVar(self, "iconMessageTime", playIconTime);
|
|
|
|
messageTo(self, "playQuestIcon", params, 1.0f, false);
|
|
}
|
|
|
|
void playQuestIconHandler(obj_id self, dictionary params)
|
|
{
|
|
int playIconTime = utils.getIntScriptVar(self, "iconMessageTime");
|
|
|
|
int messageTime = params.getInt("iconMessageTime");
|
|
|
|
// Time fingerprint
|
|
if(playIconTime != messageTime)
|
|
{
|
|
return;
|
|
}
|
|
|
|
location loc = params.getLocation("particleLoc");
|
|
|
|
float offset = params.getFloat("offset");
|
|
|
|
if(loc != null)
|
|
{
|
|
string particleName = utils.getStringScriptVar(self, "gcwQuestIcon");
|
|
|
|
if(particleName != null && particleName.length() > 0)
|
|
{
|
|
playClientEffectLoc(self, "appearance/" + particleName, loc, offset);
|
|
}
|
|
else
|
|
{
|
|
playClientEffectLoc(self, "appearance/pt_icon_quest_red.prt", loc, offset);
|
|
}
|
|
|
|
playIconTime = getGameTime();
|
|
|
|
params.put("iconMessageTime", playIconTime);
|
|
|
|
utils.setScriptVar(self, "iconMessageTime", playIconTime);
|
|
|
|
messageTo(self, "playQuestIcon", params, 6.0f, false);
|
|
}
|
|
}
|
|
|
|
boolean signalAllParticipantsForDamage(obj_id victimObject, string questName, string signalHasName, string signalSendName)
|
|
{
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage init");
|
|
if(!isValidId(victimObject) || !exists(victimObject))
|
|
{
|
|
return false;
|
|
}
|
|
if(questName == null || questName.length() <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
if(signalHasName == null || signalHasName.length() <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
if(signalSendName == null || signalSendName.length() <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id[] attackerList = utils.getObjIdBatchScriptVar(victimObject, "creditForKills.attackerList.attackers");
|
|
if(attackerList == null || attackerList.length <= 0)
|
|
{
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage attackerList NULL");
|
|
|
|
return false;
|
|
}
|
|
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage attackerList.length: "+attackerList.length);
|
|
|
|
for(int i = 0; i < attackerList.length; ++i)
|
|
{
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage attackerList[i]: "+attackerList[i]);
|
|
|
|
if(!isValidId(attackerList[i]))
|
|
continue;
|
|
|
|
trial.addNonInstanceFactionParticipant(attackerList[i], victimObject);
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage attackerList[i]: "+attackerList[i]+" is a valid OID");
|
|
|
|
if(!groundquests.isQuestActive(attackerList[i], questName))
|
|
continue;
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage attackerList[i]: "+attackerList[i]+" has quest active!");
|
|
|
|
if(!groundquests.isTaskActive(attackerList[i], questName, signalHasName))
|
|
continue;
|
|
|
|
LOG("signalAllParticipantsForDamage","signalAllParticipantsForDamage signal being sent to: "+attackerList[i]);
|
|
groundquests.sendSignal(attackerList[i], signalSendName);
|
|
|
|
gcw.gcwInvasionCreditForDestroy(attackerList[i]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean hasConstructionOrRepairTool(obj_id player, obj_id object)
|
|
{
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 1");
|
|
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 2");
|
|
|
|
if(!isValidId(object) || !exists(object))
|
|
return false;
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 3");
|
|
|
|
obj_id playerInventory = utils.getInventoryContainer(player);
|
|
if(!isValidId(playerInventory) || !exists(playerInventory))
|
|
return false;
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 4");
|
|
|
|
if(!hasObjVar(object, GCW_SPAWN_ID_OBJVAR))
|
|
return false;
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 5");
|
|
|
|
string spawnId = getStringObjVar(object, GCW_SPAWN_ID_OBJVAR);
|
|
if(spawnId == null || spawnId.length() <= 0)
|
|
return false;
|
|
|
|
string desiredTemplate = "";
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 6 spawnId: " + spawnId);
|
|
|
|
if(!hasObjVar(object, GCW_TOOL_TEMPLATE_OBJVAR))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
desiredTemplate = getStringObjVar(object, GCW_TOOL_TEMPLATE_OBJVAR);
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 7 desiredTemplate: " + desiredTemplate);
|
|
|
|
if(desiredTemplate == null || desiredTemplate.length() <= 0)
|
|
return false;
|
|
|
|
LOG("gcw_patrol_point","hasConstructionOrRepairTool 8");
|
|
|
|
return utils.playerHasItemByTemplateInInventoryOrEquipped(player, desiredTemplate);
|
|
}
|
|
|
|
boolean useConstructionOrRepairTool(obj_id player, obj_id object)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
|
|
if(!isValidId(object) || !exists(object))
|
|
return false;
|
|
|
|
if(!hasObjVar(object, GCW_SPAWN_ID_OBJVAR))
|
|
return false;
|
|
|
|
string spawnId = getStringObjVar(object, GCW_SPAWN_ID_OBJVAR);
|
|
if(spawnId == null || spawnId.length() <= 0)
|
|
return false;
|
|
|
|
string desiredTemplate = "";
|
|
|
|
if(spawnId.equals(GCW_DEFENSE_PATROL_OBJVAR) || spawnId.equals(GCW_OFFENSE_PATROL_OBJVAR))
|
|
{
|
|
if(!hasObjVar(object, GCW_DEF_TOOL_TEMPLATE_OBJVAR) || !hasObjVar(object, GCW_OFF_TOOL_TEMPLATE_OBJVAR))
|
|
return false;
|
|
|
|
if(spawnId.equals(GCW_DEFENSE_PATROL_OBJVAR))
|
|
desiredTemplate = getStringObjVar(object, GCW_DEF_TOOL_TEMPLATE_OBJVAR);
|
|
|
|
if(spawnId.equals(GCW_OFFENSE_PATROL_OBJVAR))
|
|
desiredTemplate = getStringObjVar(object, GCW_OFF_TOOL_TEMPLATE_OBJVAR);
|
|
}
|
|
else
|
|
{
|
|
if(!hasObjVar(object, GCW_TOOL_TEMPLATE_OBJVAR))
|
|
return false;
|
|
|
|
desiredTemplate = getStringObjVar(object, GCW_TOOL_TEMPLATE_OBJVAR);
|
|
}
|
|
|
|
obj_id toolObject = utils.getItemByTemplateInInventoryOrEquipped(player, desiredTemplate);
|
|
if(!isValidId(toolObject) || !exists(toolObject))
|
|
return false;
|
|
|
|
gcwInvasionCreditForCrafting(player);
|
|
|
|
return decrementCount(toolObject);
|
|
}
|
|
|
|
int getGcwCityInvasionPhase(obj_id childObject)
|
|
{
|
|
if(!isValidId(childObject) || !exists(childObject))
|
|
return GCW_CITY_PHASE_UNKNOWN;
|
|
|
|
obj_id parent = trial.getParent(childObject);
|
|
if(!isValidId(parent))
|
|
return GCW_CITY_PHASE_UNKNOWN;
|
|
|
|
if(utils.hasScriptVar(parent, "gcw.constructionEnded"))
|
|
return GCW_CITY_PHASE_COMBAT;
|
|
|
|
if(utils.hasScriptVar(parent, "gcw.invasionRunning"))
|
|
return GCW_CITY_PHASE_CONSTRUCTION;
|
|
|
|
return GCW_CITY_PHASE_UNKNOWN;
|
|
}
|
|
|
|
boolean playerSystemMessageResourceNeeded(obj_id player, obj_id object, boolean construction)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
return false;
|
|
if(!isValidId(object) || !exists(object))
|
|
return false;
|
|
|
|
if(!hasObjVar(object, gcw.GCW_TOOL_TEMPLATE_OBJVAR)
|
|
&& !hasObjVar(object, gcw.GCW_DEF_TOOL_TEMPLATE_OBJVAR)
|
|
&& !hasObjVar(object, gcw.GCW_OFF_TOOL_TEMPLATE_OBJVAR))
|
|
return false;
|
|
|
|
string toolTemplate = "";
|
|
|
|
if(hasObjVar(object, GCW_DEF_TOOL_TEMPLATE_OBJVAR) && hasObjVar(object, GCW_OFF_TOOL_TEMPLATE_OBJVAR))
|
|
{
|
|
|
|
if(hasObjVar(object, GCW_SPAWN_ID_OBJVAR) && getStringObjVar(object, GCW_SPAWN_ID_OBJVAR).equals(GCW_DEFENSE_PATROL_OBJVAR))
|
|
{
|
|
toolTemplate = getStringObjVar(object, gcw.GCW_DEF_TOOL_TEMPLATE_OBJVAR);
|
|
}
|
|
else if(hasObjVar(object, GCW_SPAWN_ID_OBJVAR) && getStringObjVar(object, GCW_SPAWN_ID_OBJVAR).equals(GCW_OFFENSE_PATROL_OBJVAR))
|
|
{
|
|
toolTemplate = getStringObjVar(object, gcw.GCW_OFF_TOOL_TEMPLATE_OBJVAR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
toolTemplate = getStringObjVar(object, gcw.GCW_TOOL_TEMPLATE_OBJVAR);
|
|
}
|
|
|
|
if(toolTemplate == null || toolTemplate.length() <= 0)
|
|
return false;
|
|
|
|
string_id objName = getNameFromTemplate(toolTemplate);
|
|
if(objName == null)
|
|
return false;
|
|
|
|
prose_package pp = new prose_package();
|
|
string_id msg = new string_id();
|
|
if(construction)
|
|
{
|
|
msg = SID_RESOURCES_NEEDED;
|
|
}
|
|
else
|
|
{
|
|
msg = SID_YOU_NEED_TOOL_REPAIR;
|
|
}
|
|
//"You do not have a %TU to construct this."
|
|
pp = prose.setStringId(pp, msg);
|
|
pp = prose.setTO(pp, objName);
|
|
sendSystemMessageProse(player, pp);
|
|
return true;
|
|
}
|
|
|
|
int getFatigueTimerMod(obj_id player)
|
|
{
|
|
int fatigueStack = (int)buff.getBuffStackCount(player, BUFF_PLAYER_FATIGUE);
|
|
|
|
int fatigueMod = 0;
|
|
|
|
if(fatigueStack > 0)
|
|
{
|
|
fatigueMod = fatigueStack / GCW_FATIGUE_DIVISOR;
|
|
}
|
|
|
|
return fatigueMod;
|
|
}
|
|
|
|
obj_id getInvasionSequencerNearby(obj_id target)
|
|
{
|
|
if(!isIdValid(target) || !exists(target))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_id keren = utils.stringToObjId(GCW_SEQUENCER_KEREN);
|
|
obj_id bestine = utils.stringToObjId(GCW_SEQUENCER_BESTINE);
|
|
obj_id dearic = utils.stringToObjId(GCW_SEQUENCER_DEARIC);
|
|
|
|
if(isIdValid(keren) && exists(keren))
|
|
{
|
|
return keren;
|
|
}
|
|
|
|
if(isIdValid(bestine) && exists(bestine))
|
|
{
|
|
return bestine;
|
|
}
|
|
|
|
if(isIdValid(dearic) && exists(dearic))
|
|
{
|
|
return dearic;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
string getCityFromTable(obj_id self)
|
|
{
|
|
string cityName = "";
|
|
|
|
string datatable = getStringObjVar(self, "instance.data_table");
|
|
|
|
if(datatable == null || datatable.length() < 1)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(datatable.equals(DEARIC_CITY_TABLE))
|
|
{
|
|
return CITY_DEARIC;
|
|
}
|
|
else if(datatable.equals(KEREN_CITY_TABLE))
|
|
{
|
|
return CITY_KEREN;
|
|
}
|
|
else if(datatable.equals(BESTINE_CITY_TABLE))
|
|
{
|
|
return CITY_BESTINE;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
boolean awardGcwInvasionParticipants(resizeable obj_id[] participantList, int factionFlag, int gcwTokenAmt, int gcwPointAmt, dictionary finalAnnouncementParams)
|
|
{
|
|
if(participantList == null || participantList.length <= 0)
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardInvasionParticipants: participantList array invalid. Returning False.");
|
|
return false;
|
|
}
|
|
|
|
if(factionFlag != factions.FACTION_FLAG_REBEL && factionFlag != factions.FACTION_FLAG_IMPERIAL)
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardInvasionParticipants: Faction Flag invalid. Returning False.");
|
|
return false;
|
|
}
|
|
|
|
if(gcwTokenAmt <= 0 && gcwPointAmt <= 0)
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardInvasionParticipants: participantList awards were equal to or less than 0. Returning False.");
|
|
return false;
|
|
}
|
|
|
|
string tokenStaticName = "";
|
|
obj_id[] lootList = new obj_id[1];
|
|
|
|
if(factionFlag == factions.FACTION_FLAG_IMPERIAL)
|
|
{
|
|
tokenStaticName = GCW_IMPERIAL_TOKEN;
|
|
}
|
|
else if(factionFlag == factions.FACTION_FLAG_REBEL)
|
|
{
|
|
tokenStaticName = GCW_REBEL_TOKEN;
|
|
}
|
|
|
|
// Bonus tokens configuration
|
|
float multiplier = utils.stringToFloat(getConfigSetting("GameServer", "gcwTokenBonus"));
|
|
|
|
if(multiplier > 1)
|
|
{
|
|
gcwTokenAmt *= multiplier;
|
|
}
|
|
|
|
|
|
for(int i = 0; i < participantList.length; i++)
|
|
{
|
|
if(!isValidId(participantList[i]) || !exists(participantList[i]))
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardGcwInvasionParticipants: Player: "+participantList[i]
|
|
+" is NOT receiving "+gcwTokenAmt+ " " +tokenStaticName+ " tokens or "+gcwPointAmt+" "
|
|
+factionFlag+ " GCW points (GCW points only awarded to pure faction players, not factionalHelpers) because this player OID is invalid or doesn't exist. Probably due to the player exiting the battle field, traveling or moving to another server process (crossing server boundary).");
|
|
|
|
continue;
|
|
}
|
|
if(!isPlayerConnected(participantList[i]))
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardGcwInvasionParticipants: Player: "+participantList[i]
|
|
+" is NOT receiving "+gcwTokenAmt+ " " +tokenStaticName+ " tokens or "+gcwPointAmt+" "
|
|
+factionFlag+ " GCW points (GCW points only awarded to pure faction players, not factionalHelpers) because this player is NOT CURRENTLY CONNECTED.");
|
|
|
|
continue;
|
|
}
|
|
|
|
grantUnmodifiedGcwPoints(participantList[i], gcwPointAmt);
|
|
|
|
if(tokenStaticName == null || tokenStaticName.length() <= 0)
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.awardGcwInvasionParticipants: Player: "+participantList[i]
|
|
+" is NOT receiving "+gcwTokenAmt+ " " +tokenStaticName+ " tokens or "+gcwPointAmt+" "
|
|
+factionFlag+ " GCW points (GCW points only awarded to pure faction players, not factionalHelpers) because the token string name is INVALID. This is probably due to an edge case where the player changed their faction status to be neutral.");
|
|
|
|
continue;
|
|
}
|
|
|
|
obj_id playerInv = getObjectInSlot(participantList[i], utils.SLOT_INVENTORY);
|
|
if(isValidId(playerInv))
|
|
{
|
|
obj_id lootCreated = static_item.createNewItemFunction(tokenStaticName, playerInv, gcwTokenAmt);
|
|
lootList[0] = lootCreated;
|
|
showLootBox(participantList[i], lootList);
|
|
groundquests.sendPlacedMoreThanOneInInventorySystemMessage(participantList[i], lootCreated, gcwTokenAmt);
|
|
|
|
if(finalAnnouncementParams != null)
|
|
{
|
|
messageTo(participantList[i], "playIconicGCWWrapUpMessage", finalAnnouncementParams, 13, false);
|
|
}
|
|
CustomerServiceLog("gcw_city_invasion", "GCW CITY SYSTEM HAS REWARDED PLAYER: "+participantList[i]+" with "+gcwTokenAmt+" "+tokenStaticName+" static item tokens for PARTICIPATING IN A GCW CITY CONFLICT.");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean invasionIsValidAndEngaged()
|
|
{
|
|
obj_id self = getSelf();
|
|
|
|
if(!isIdValid(self) || !exists(self))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id sequencer = getInvasionSequencerNearby(self);
|
|
|
|
if(!isIdValid(sequencer) || !exists(sequencer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!utils.hasScriptVar(sequencer, "gcw.invasionRunning"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void gcwSetCredits(obj_id who, int playerGCW, int playerPvpKills, int playerKills, int playerAssists, int playerCraftedItems, int playerDestroyedItems)
|
|
{
|
|
if(!isIdValid(who) || !exists(who) || !isPlayer(who))
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcw_score.gcw_data currentData = gcw_score.getPlayerGcwData(who);
|
|
|
|
string playerName = getName(who);
|
|
string playerFaction = factions.getFaction(who);
|
|
|
|
string factionColor = "";
|
|
|
|
if(factions.getFactionFlag(who) == factions.FACTION_FLAG_REBEL)
|
|
{
|
|
factionColor = COLOR_REBELS;
|
|
}
|
|
else if(factions.getFactionFlag(who) == factions.FACTION_FLAG_IMPERIAL)
|
|
{
|
|
factionColor = COLOR_IMPERIALS;
|
|
}
|
|
|
|
// Neutral?
|
|
if(playerFaction == null || playerFaction.length() <= 0)
|
|
{
|
|
if(factions.isRebelHelper(who))
|
|
{
|
|
factionColor = COLOR_REBELS;
|
|
playerFaction = "Rebel Helper";
|
|
}
|
|
else if(factions.isImperialHelper(who))
|
|
{
|
|
factionColor = COLOR_IMPERIALS;
|
|
playerFaction = "Imperial Helper";
|
|
}
|
|
else // This player is not helping anyone!
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
playerFaction = factionColor + playerFaction;
|
|
|
|
string playerProfession = "@ui_roadmap:" + skill.getProfessionName(getSkillTemplate(who));
|
|
|
|
int playerLevel = getLevel(who);
|
|
|
|
if(currentData != null)
|
|
{
|
|
playerGCW = currentData.playerGCW + playerGCW;
|
|
playerPvpKills = currentData.playerPvpKills + playerPvpKills;
|
|
playerKills = currentData.playerKills + playerKills;
|
|
playerAssists = currentData.playerAssists + playerAssists;
|
|
playerCraftedItems = currentData.playerCraftedItems + playerCraftedItems;
|
|
playerDestroyedItems = currentData.playerDestroyedItems + playerDestroyedItems;
|
|
}
|
|
|
|
gcw_score.setPlayerGcwData(who, playerName, playerFaction, playerProfession, playerLevel, playerGCW, playerPvpKills, playerKills, playerAssists, playerCraftedItems, playerDestroyedItems);
|
|
}
|
|
|
|
void gcwInvasionCreditForGCW(obj_id who, int gcw)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcwSetCredits(who, gcw, 0, 0, 0, 0, 0);
|
|
}
|
|
|
|
void gcwInvasionCreditForPVPKill(obj_id who)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcwSetCredits(who, 0, 1, 0, 0, 0, 0);
|
|
}
|
|
|
|
void gcwInvasionCreditForKill(obj_id who)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
gcwSetCredits(who, 0, 0, 1, 0, 0, 0);
|
|
}
|
|
|
|
void gcwInvasionCreditForAssist(obj_id who)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcwSetCredits(who, 0, 0, 0, 1, 0, 0);
|
|
}
|
|
|
|
void gcwInvasionCreditForCrafting(obj_id who)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcwSetCredits(who, 0, 0, 0, 0, 1, 0);
|
|
}
|
|
|
|
void gcwInvasionCreditForDestroy(obj_id who)
|
|
{
|
|
if(!invasionIsValidAndEngaged())
|
|
{
|
|
return;
|
|
}
|
|
|
|
gcwSetCredits(who, 0, 0, 0, 0, 0, 1);
|
|
}
|
|
|
|
string getFormattedInvasionTime(obj_id self, int phase)
|
|
{
|
|
obj_id planet = getPlanetByName("tatooine");
|
|
|
|
if(!isIdValid(planet) || !exists(planet))
|
|
{
|
|
return "";
|
|
}
|
|
|
|
int currentTime = getCalendarTime();
|
|
|
|
string cityName = getCityFromTable(self);
|
|
|
|
if(cityName == null || cityName.length() <= 0)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
int lastPulse = utils.getIntScriptVar(planet, "gcw.calendar_time." + cityName);
|
|
|
|
int nextPulseMultiplier = utils.getIntScriptVar(planet, "gcw.calendar_time_next." + cityName);
|
|
|
|
if(phase == GCW_CITY_PHASE_CONSTRUCTION)
|
|
{
|
|
int nextPulse = (lastPulse + GCW_CONSTRUCTION_END_TIMER);
|
|
|
|
return utils.padTimeHM(nextPulse - currentTime);
|
|
}
|
|
else if(phase == GCW_CITY_PHASE_COMBAT)
|
|
{
|
|
int nextPulse = (lastPulse + GCW_CONSTRUCTION_END_TIMER + GCW_COMBAT_END_TIMER);
|
|
|
|
return utils.padTimeHM(nextPulse - currentTime);
|
|
}
|
|
else if(phase == GCW_CITY_PHASE_UNKNOWN)
|
|
{
|
|
int nextPulse = gcwGetNextInvasionTime(cityName);
|
|
|
|
return utils.padTimeHM(nextPulse);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
int gcwGetTimeToInvasion()
|
|
{
|
|
string timeConfig = getConfigSetting("GameServer", "gcwInvasionCycleTime");
|
|
|
|
if(timeConfig == null || timeConfig.length() <= 0)
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
int time = utils.stringToInt(timeConfig);
|
|
|
|
if(time > 6)
|
|
{
|
|
time = 6;
|
|
}
|
|
|
|
if(time < 2)
|
|
{
|
|
time = 2;
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
int gcwGetInvasionMaximumRunning()
|
|
{
|
|
string maxConfig = getConfigSetting("GameServer", "gcwInvasionCityMaximumRunning");
|
|
int max = 3;
|
|
|
|
if(maxConfig == null || maxConfig.length() <= 0)
|
|
{
|
|
return max;
|
|
}
|
|
|
|
return utils.stringToInt(maxConfig);
|
|
}
|
|
|
|
boolean gcwIsInvasionCityOn(string city)
|
|
{
|
|
if(city == null || city.length() <= 0)
|
|
return false;
|
|
|
|
string cityConfig = getConfigSetting("GameServer", "gcwcity" + city);
|
|
|
|
if(cityConfig == null || cityConfig.length() <= 0)
|
|
{
|
|
CustomerServiceLog("gcw_city_invasion", "gcw.gcwIsInvasionCityOn: GCW City: "+city+" is not configured to run a city invasion. Function returning False.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int gcwGetCityInterval(string cityName, int cycle)
|
|
{
|
|
if(cycle < 0 || cycle > 2)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int maximumRunning = gcwGetInvasionMaximumRunning();
|
|
|
|
int[] bestineSchedule = null;
|
|
int[] dearicSchedule = null;
|
|
int[] kerenSchedule = null;
|
|
|
|
switch(maximumRunning)
|
|
{
|
|
case 0:
|
|
return -1;
|
|
case 1:
|
|
bestineSchedule = new int[] { 1, 0, 0 };
|
|
dearicSchedule = new int[] { 0, 1, 0 };
|
|
kerenSchedule = new int[] { 0, 0, 1 };
|
|
break;
|
|
case 2:
|
|
bestineSchedule = new int[] { 1, 0, 1 };
|
|
dearicSchedule = new int[] { 1, 1, 0 };
|
|
kerenSchedule = new int[] { 0, 1, 1 };
|
|
break;
|
|
case 3:
|
|
return 1;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if(cityName.equals("bestine"))
|
|
{
|
|
return bestineSchedule[cycle];
|
|
}
|
|
else if(cityName.equals("dearic"))
|
|
{
|
|
return dearicSchedule[cycle];
|
|
}
|
|
else if(cityName.equals("keren"))
|
|
{
|
|
return kerenSchedule[cycle];
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int gcwCalculateInvasionCycle()
|
|
{
|
|
int invasionInterval = gcwGetTimeToInvasion();
|
|
|
|
// Divide by zero?
|
|
if(invasionInterval <= 0)
|
|
{
|
|
invasionInterval = 3; // Default to an invasion every three hours; 8 per day per city, if all cities are on
|
|
}
|
|
|
|
// When did this city last have its invasion?
|
|
int[] convertedCalendarTime = player_structure.convertSecondsTime(getCalendarTime());
|
|
|
|
int hour = convertedCalendarTime[1];
|
|
|
|
return ((hour / invasionInterval) % invasionInterval);
|
|
}
|
|
|
|
// GMT
|
|
// Returns seconds until the next invasion
|
|
int gcwGetNextInvasionTime(string cityName)
|
|
{
|
|
LOG("gcwlog", "gcwGetNextInvasionTime cityName: " + cityName);
|
|
|
|
if(cityName == null || cityName.length() <= 0 || !gcwIsInvasionCityOn(cityName))
|
|
{
|
|
LOG("gcwlog", "cityName: " + cityName + " !gcwIsInvasionCityOn(cityName): " + !gcwIsInvasionCityOn(cityName));
|
|
|
|
return -1;
|
|
}
|
|
|
|
int invasionInterval = gcwGetTimeToInvasion();
|
|
|
|
// Divide by zero?
|
|
if(invasionInterval <= 0)
|
|
{
|
|
invasionInterval = 3; // Default to an invasion every three hours; 8 per day per city, if all cities are on
|
|
}
|
|
|
|
int calendarTime = getCalendarTime();
|
|
|
|
// When did this city last have its invasion?
|
|
int[] convertedCalendarTime = player_structure.convertSecondsTime(calendarTime);
|
|
|
|
int hour = convertedCalendarTime[1];
|
|
|
|
int cycle = gcwCalculateInvasionCycle();
|
|
|
|
/*
|
|
2 interval
|
|
0 - 1 cycle 0, hour 2 - 3 cycle 1, hour 4 - 5 cycle 2, hour 6 - 7 cycle 0...
|
|
|
|
3 interval 7
|
|
0 - 2 cycle 0, hour 3 - 5 cycle 1, hour 6 - 8 cycle 2, hour 9 - 11 cycle 0...
|
|
*/
|
|
|
|
int nextCycle = cycle + 1;
|
|
|
|
// There are only 3 cycles; 0 to 2
|
|
if(nextCycle > 2 || 24 - hour <= invasionInterval)
|
|
{
|
|
nextCycle = 0;
|
|
}
|
|
|
|
int nextInterval = gcwGetCityInterval(cityName, nextCycle);
|
|
|
|
LOG("gcwlog", "XXXXXXXX cityName: " + cityName + " nextInterval: " + nextInterval + " cycle: " + cycle + " hour: " + hour + " nextCycle: " + nextCycle);
|
|
|
|
// Bad interval? Return false
|
|
if(nextInterval < 0)
|
|
{
|
|
LOG("gcwlog", "cityName: " + cityName + " nextInterval: " + nextInterval);
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Next interval is skipped? Return the time to the next interval plus another interval cycle.
|
|
if(nextInterval == 0)
|
|
{
|
|
int nextHour = hour + (invasionInterval - (hour % invasionInterval)) + invasionInterval;
|
|
|
|
if(nextHour > 23)
|
|
{
|
|
nextHour = nextHour - 24;
|
|
}
|
|
|
|
LOG("gcwlog", "cityName: " + cityName + " nextInterval: " + nextInterval + " nextHour: " + nextHour);
|
|
|
|
return secondsUntilNextDailyTime(nextHour, 0, 0);
|
|
}
|
|
|
|
// Next interval is not skipped? Return the leftover time until the invasion begins.
|
|
if(nextInterval == 1)
|
|
{
|
|
int nextHour = hour + (invasionInterval - (hour % invasionInterval));
|
|
|
|
if(nextHour > 23)
|
|
{
|
|
nextHour = nextHour - 24;
|
|
}
|
|
|
|
LOG("gcwlog", "cityName: " + cityName + " nextInterval: " + nextInterval + " nextHour: " + nextHour);
|
|
|
|
return secondsUntilNextDailyTime(nextHour, 0, 0);
|
|
}
|
|
|
|
LOG("gcwlog", "cityName: " + cityName + " nextInterval: " + nextInterval);
|
|
|
|
return -1;
|
|
}
|
|
|
|
boolean gcwTutorialCheck(obj_id player)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(player, GCW_TUTORIAL_FLAG))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//The player hasn't received tutorial. Display UI.
|
|
gcwCityHelpText(player);
|
|
setObjVar(player, GCW_TUTORIAL_FLAG, 1);
|
|
return true;
|
|
}
|
|
|
|
boolean gcwCityHelpText(obj_id player)
|
|
{
|
|
if(!isValidId(player) || !exists(player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string gcwHelpData = localize(new string_id("gcw", "gcw_tutorial_text"));
|
|
string uiTitle = "GCW City Conflict Tutorial";
|
|
int page = createSUIPage("/Script.messageBox", player, player);
|
|
|
|
setSUIProperty(page, "Prompt.lblPrompt", "LocalText", gcwHelpData);
|
|
setSUIProperty(page, "bg.caption.lblTitle", "Text", uiTitle);
|
|
setSUIProperty(page, "Prompt.lblPrompt", "Editable", "false");
|
|
setSUIProperty(page, "Prompt.lblPrompt", "GetsInput", "false");
|
|
setSUIProperty(page, "btnCancel", "Visible", "false");
|
|
setSUIProperty(page, "btnRevert", "Visible", "false");
|
|
setSUIProperty(page, "btnOk", sui.PROP_TEXT, "Get Nearest GCW City Waypoints");
|
|
subscribeToSUIEvent(page, sui_event_type.SET_onClosedOk, "%button0%", "handleGcwCityHelpUi");
|
|
|
|
showSUIPage(page);
|
|
flushSUIPage(page);
|
|
|
|
return true;
|
|
}
|