mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-31 00:15:54 -04:00
2055 lines
60 KiB
Plaintext
2055 lines
60 KiB
Plaintext
|
|
/***** INCLUDES ********************************************************/
|
|
|
|
include library.buff;
|
|
include library.colors;
|
|
include library.factions;
|
|
include library.force_rank;
|
|
include library.pet_lib;
|
|
include library.prose;
|
|
include library.sui;
|
|
include library.trace;
|
|
include library.utils;
|
|
|
|
include java.lang.Math;
|
|
|
|
/***** CONSTANTS *******************************************************/
|
|
|
|
const int BUFFER_TIME = 180;
|
|
|
|
const string SCRIPTVAR_PVP_BASE = "pvp";
|
|
|
|
const string SCRIPTVAR_DAMAGE_BASE = SCRIPTVAR_PVP_BASE +".damage";
|
|
const string SCRIPTVAR_PVP_DAMAGE = SCRIPTVAR_DAMAGE_BASE +".pvp";
|
|
const string SCRIPTVAR_PVE_DAMAGE = SCRIPTVAR_DAMAGE_BASE +".pve";
|
|
const string SCRIPTVAR_PVP_STAMP = SCRIPTVAR_DAMAGE_BASE +".stamp";
|
|
|
|
// Contribution tracking
|
|
const string VAR_CREDIT_FOR_PVP_KILLS = "creditForPvPKills";
|
|
const string VAR_ATTACKER_LIST = VAR_CREDIT_FOR_PVP_KILLS + ".attackerList";
|
|
const string VAR_TOTAL_DAMAGE_TALLY = VAR_CREDIT_FOR_PVP_KILLS + ".totalDamageTally"; //total damage done to target
|
|
const string VAR_LAST_PVP_DAMAGE_TIME = "creditForPvPKillsTracking.lastDamageTimeStamp"; // last time we took pvp damage
|
|
const string VAR_LAST_PVP_DAMAGE_MSG_SENT = "creditForPvPKillsTracking.clearContribsMsgPending"; // have we setup the clear contribution callback?
|
|
|
|
// PvP Ratings
|
|
const int MAX_RATING_DELTA = 40; // the max amount any player can gain or lose for a single match
|
|
const boolean ENFORCE_MINIMUM_DELTA = false; // winner must always gain 2 points, loser must always lose 2 points
|
|
const string VAR_PVP_RATING = "pvp_rating.value";
|
|
|
|
// VARIABLE ADJUSTERS APPLY TO BOTH RATING AND XP
|
|
const float MAX_RATING_AFFECT_ON_XP = 0.50f; // the PvP XP gained/lost is modified by a player's PvP rating, currently the rating can modify the XP gain/loss by 50 %
|
|
const int MAX_RATING_DISPARITY = 2000; // The maximum modification to PvP XP gain/loss is currently 50% and occurs when the rating disparity = = 2000. If disparity > 2000 then disparity = 2000.
|
|
const boolean COUNT_PET_DAMAGE = false; // should a player's pet damage be added to a player's tally?
|
|
const float COUNT_PET_DAMAGE_PERCENT = 0.25f; // if counting pet damage, at what percentage should it be scaled?
|
|
const int CONTRIB_CLEAR_INTERVAL = 180; // how often to check for clearing contributions. 3 min currently.
|
|
const int MAX_PLAYERS = 30; // the max number of player that can be evaluated for XP/Ratings when a player dies
|
|
const int MAX_DISTANCE = 80; // max # of feet a winner can be from a victim's corpse to get xp/rating from the death
|
|
const string VAR_PVP_LAST_KILLS = "pvp_tracker.lastkills"; // stores the last XP_FARMING_TRACKING_SLOTS PvP kills for a player
|
|
const string VAR_PVP_LAST_UPDATE = "pvp_tracker.lastupdate"; // The last time a player was added to the kills tracker.
|
|
const int VISIBILITY_THRESHOLD = 2000; // the threshold of visibility at which a player will begin to show up on bounty hunter terminals
|
|
const int INITIAL_RANKING_VALUE = 1200; // initial PvP rating for a new player
|
|
const int XP_FARMING_TRACKING_SLOTS = 20; // the number of players we track for PvP kills. A value of 10 indicates that once i have slain
|
|
const int PVP_TRACKING_INTERVAL = 259200; // the time in which the PvP kill track list resets if it hasn't changed.
|
|
// a player for PvP XP and/or Rating adjustments, i have to kill 9 other players before i can gain off of player #1 again
|
|
// FORCE RANK XP DATA
|
|
const string FRS_XP_DATATABLE = "datatables/pvp/force_rank_xp.iff";
|
|
const string FRS_DT_COLUMN_PREFIX = "rank";
|
|
const string FRS_DT_ROW_NONJEDI_PREFIX = "nj"; // nj_xp_gain; nj_xp_loss; -- aplies to factioned non-jedis
|
|
const string FRS_DT_ROW_BNTYHUNTER_PREFIX = "bh";
|
|
const string FRS_DT_ROW_PADAWAN_PREFIX = "pw";
|
|
const string FRS_DT_RANKED_JEDI_PREFIX = "r"; //r0_xp_gain, r0_xp_loss, r3_xp_gain, ...
|
|
const string FRS_DT_ROW_LOSS_SUFFIX = "_xp_loss";
|
|
const string FRS_DT_ROW_WIN_SUFFIX = "_xp_gain";
|
|
|
|
const string PVP_CS_LOG = "pvp";
|
|
|
|
const int PVP_STATE_NONE = 0;
|
|
const int PVP_STATE_INITIALIZE = 1;
|
|
const int PVP_STATE_NEW_BATTLE = 2;
|
|
const int PVP_STATE_BUILD_QUEUE = 3;
|
|
const int PVP_STATE_INVITE_QUEUE = 4;
|
|
const int PVP_STATE_INVITE_OVER = 5;
|
|
const int PVP_STATE_ABORT_QUEUE = 6;
|
|
const int PVP_STATE_BATTLE_SETUP = 7;
|
|
const int PVP_STATE_BATTLE_START = 8;
|
|
const int PVP_STATE_BATTLE_ENGAGED = 9;
|
|
const int PVP_STATE_BATTLE_END = 10;
|
|
const int PVP_STATE_BATTLE_CLEANUP = 11;
|
|
|
|
const string [] BATTLEFIELD_STATUS =
|
|
{
|
|
"Loading", // PVP_STATE_NONE
|
|
"Initializing", // PVP_STATE_INITIALIZE
|
|
"Starting", // PVP_STATE_NEW_BATTLE
|
|
"Reviewing Queue", // PVP_STATE_BUILD_QUEUE
|
|
"Inviting Participants", // PVP_STATE_INVITE_QUEUE
|
|
"Reviewing Invitations", // PVP_STATE_INVITE_OVER
|
|
"Resetting", // PVP_STATE_ABORT_QUEUE
|
|
"Battle Setting Up", // PVP_STATE_BATTLE_SETUP
|
|
"Battle Started", // PVP_STATE_BATTLE_START
|
|
"Battle Engaged", // PVP_STATE_BATTLE_ENGAGED
|
|
"Battle Ending", // PVP_STATE_BATTLE_END
|
|
"Battle Cleaning Up" // PVP_STATE_BATTLE_CLEANUP
|
|
};
|
|
|
|
const int BATTLEFIELD_DURATION = 900; // 900
|
|
const float BATTLEFIELD_INVITATION_WAIT_TIME = 120.0f; // 120.0f
|
|
const float BATTLEFIELD_SETUP_WAIT_TIME = 120.0f; // 120.0f
|
|
const float BATTLEFIELD_END_CLEANUP = 30.0f; // 30.0f
|
|
const float BATTLEFIELD_RUNNER_DURATION = 95.0f; // 95.0f
|
|
|
|
const int BATTLEFIELD_TYPE_NONE = 0;
|
|
const int BATTLEFIELD_TYPE_REINFORCEMENTS = 1;
|
|
const int BATTLEFIELD_TYPE_CAPTURE_THE_FLAG = 2;
|
|
|
|
const string BATTLEFIELD_ACTIVE_PLAYERS = "battlefield.active_players";
|
|
const string BATTLEFIELD_ACTIVE_REBEL_PLAYERS = "battlefield.active_rebel_players";
|
|
const string BATTLEFIELD_ACTIVE_IMPERIAL_PLAYERS = "battlefield.active_imperial_players";
|
|
|
|
const string COLOR_REBELS = "\\" + colors_hex.COLOR_REBELS;
|
|
const string COLOR_IMPERIALS = "\\" + colors_hex.COLOR_IMPERIALS;
|
|
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-START PVP ** RATING ** SECTION/-/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
|
|
//
|
|
// compiles and modifies the winners list of a fight against @victim. internal helper method.
|
|
//
|
|
obj_var[] _packageCombatWinners(obj_id victim)
|
|
{
|
|
if(!isIdValid(victim))
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib._packageCombatWinners:-> victim is invalid. not handing out points.", victim, trace.TL_WARNING);
|
|
return null;
|
|
}
|
|
|
|
if(!utils.hasScriptVar(victim, VAR_ATTACKER_LIST))
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib._packageCombatWinners:-> victim missing scriptvars. not handing out points.", victim, trace.TL_WARNING);
|
|
return null;
|
|
}
|
|
|
|
obj_id[] attackerList = utils.getObjIdBatchScriptVar(victim, VAR_ATTACKER_LIST);
|
|
if(attackerList == null || attackerList.length == 0 )
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib._packageCombatWinners:-> attackerList is bunk. Bailing.", victim, trace.TL_WARNING);
|
|
return null;
|
|
}
|
|
|
|
obj_var[] damArray = new obj_var[attackerList.length];
|
|
for(int i = 0; i < attackerList.length; i++)
|
|
{
|
|
damArray[i] = new obj_var(attackerList[i].toString(), utils.getIntScriptVar(victim, VAR_ATTACKER_LIST + "." + attackerList[i] + ".damage"));
|
|
}
|
|
|
|
if(damArray == null || damArray.length == 0)
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib._packageCombatWinners:-> damArray is bunk. Bailing..", victim, trace.TL_WARNING);
|
|
return null;
|
|
}
|
|
|
|
obj_var[] killers = list.quickSort(0, attackerList.length - 1, damArray);
|
|
if((killers == null) || (killers.length == 0))
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib._packageCombatWinners:-> killers obj_var[] is bunk. Bailing..", victim, trace.TL_WARNING);
|
|
return null;
|
|
}
|
|
|
|
// cull killers down to top MAX_PLAYERS, if necessary
|
|
string logPlayers = "";
|
|
if(killers.length > MAX_PLAYERS)
|
|
{
|
|
obj_var[] tmpArray = new obj_var[MAX_PLAYERS];
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
tmpArray[i] = killers[i];
|
|
logPlayers += "" + killers[i] + ",";
|
|
}
|
|
|
|
killers = tmpArray;
|
|
}
|
|
|
|
if (killers.length > 0 && force_rank.isForceRanked(utils.stringToObjId(killers[killers.length-1].getName())))
|
|
{
|
|
if (pvp.isPvpDeath(victim))
|
|
utils.setScriptVar(victim, "jedi.rankedDeath", 1);
|
|
}
|
|
|
|
// CS logging
|
|
trace.log(PVP_CS_LOG, "%TU died [in part] to PvP damage. " + killers.length + " players contributed to the death: [" + logPlayers + "]", victim, trace.TL_CS_LOG);
|
|
|
|
return killers;
|
|
}
|
|
|
|
//
|
|
// constructs a prose package for those times a player has gained or lost PvP rating points do to a PvP death
|
|
//
|
|
prose_package getPvPRatedProsePackage(obj_id winner, obj_id loser, boolean winnerPackage)
|
|
{
|
|
string stringId = "";
|
|
string stringFile = "pvp_rating";
|
|
string[] phrases = null;
|
|
obj_id actor = null;
|
|
obj_id target = null;
|
|
|
|
if(winnerPackage)
|
|
{
|
|
actor = winner;
|
|
target = loser;
|
|
}
|
|
else
|
|
{
|
|
actor = loser;
|
|
target = winner;
|
|
}
|
|
|
|
if(isSpecies(actor, SPECIES_TRANDOSHAN))
|
|
{ // Trandoshan win/loss packets
|
|
if(!winnerPackage)
|
|
{
|
|
phrases = new string[3];
|
|
phrases[0] = "trandoshan_killed1";
|
|
phrases[1] = "trandoshan_killed1";
|
|
phrases[2] = "trandoshan_killed1";
|
|
}
|
|
else
|
|
{
|
|
phrases = new string[3];
|
|
phrases[0] = "trandoshan_win1";
|
|
phrases[1] = "trandoshan_win1";
|
|
phrases[2] = "trandoshan_win1";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// all other races win/loss packets
|
|
if(!winnerPackage)
|
|
{
|
|
phrases = new string[3];
|
|
phrases[0] = "killed1";
|
|
phrases[1] = "killed2";
|
|
phrases[2] = "killed3";
|
|
}
|
|
else
|
|
{
|
|
phrases = new string[3];
|
|
phrases[0] = "win1";
|
|
phrases[1] = "win2";
|
|
phrases[2] = "win3";
|
|
}
|
|
}
|
|
|
|
stringId = phrases[rand(0, phrases.length -1)];
|
|
|
|
prose_package pp_msg = new prose_package();
|
|
pp_msg.stringId = new string_id(stringFile, stringId);
|
|
pp_msg.actor.set(actor);
|
|
pp_msg.target.set(utils.getRealPlayerFirstName(target));
|
|
pp_msg.digitInteger = getCurrentPvPRating(actor);
|
|
|
|
return pp_msg;
|
|
}
|
|
|
|
//
|
|
// constructs a prose package for those times where a player cannot gain or lose PvP rating points because they have
|
|
// already engaged in combat with that player recently
|
|
//
|
|
prose_package getPvPThrottleProsePackage(obj_id winner, obj_id loser, boolean winnerPackage)
|
|
{
|
|
string stringId = "";
|
|
string stringFile = "pvp_rating";
|
|
obj_id actor = null;
|
|
obj_id target = null;
|
|
|
|
if(winnerPackage)
|
|
{
|
|
actor = winner;
|
|
target = loser;
|
|
}
|
|
else
|
|
{
|
|
actor = loser;
|
|
target = winner;
|
|
}
|
|
|
|
if(isSpecies(actor, SPECIES_TRANDOSHAN))
|
|
{
|
|
if(winnerPackage)
|
|
{
|
|
stringId = "rating_throttle_trandoshan_winner";
|
|
}
|
|
else
|
|
{
|
|
stringId = "rating_throttle_trandoshan_loser";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(winnerPackage)
|
|
{
|
|
stringId = "rating_throttle_winner";
|
|
}
|
|
else
|
|
{
|
|
stringId = "rating_throttle_loser";
|
|
}
|
|
}
|
|
|
|
prose_package pp_msg = new prose_package();
|
|
pp_msg.stringId = new string_id(stringFile, stringId);
|
|
pp_msg.actor.set(actor);
|
|
pp_msg.target.set(utils.getRealPlayerFirstName(target));
|
|
pp_msg.digitInteger = getCurrentPvPRating(actor);
|
|
|
|
|
|
return pp_msg;
|
|
}
|
|
|
|
//
|
|
// construcs prose packages for those times when a player cannot gain or lose pvp rating points because the loser is at the
|
|
// floor of points (i.e doesnt have enough points to lose)
|
|
//
|
|
prose_package getPvPRatingFloorProsePackage(obj_id winner, obj_id loser, boolean winnerPackage)
|
|
{
|
|
string stringId = "";
|
|
string stringFile = "pvp_rating";
|
|
obj_id actor = null;
|
|
obj_id target = null;
|
|
|
|
if(winnerPackage)
|
|
{
|
|
actor = winner;
|
|
target = loser;
|
|
}
|
|
else
|
|
{
|
|
actor = loser;
|
|
target = winner;
|
|
}
|
|
|
|
if(isSpecies(actor, SPECIES_TRANDOSHAN))
|
|
{
|
|
if(winnerPackage)
|
|
{
|
|
stringId = "rating_floor_trandoshan_winner";
|
|
}
|
|
else
|
|
{
|
|
stringId = "rating_floor_trandoshan_loser";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(winnerPackage)
|
|
{
|
|
stringId = "rating_floor_winner";
|
|
}
|
|
else
|
|
{
|
|
stringId = "rating_floor_loser";
|
|
}
|
|
}
|
|
|
|
prose_package pp_msg = new prose_package();
|
|
pp_msg.stringId = new string_id(stringFile, stringId);
|
|
pp_msg.actor.set(actor);
|
|
pp_msg.target.set(utils.getRealPlayerFirstName(target));
|
|
pp_msg.digitInteger = getCurrentPvPRating(actor);
|
|
|
|
return pp_msg;
|
|
}
|
|
|
|
//
|
|
// adjusts the pvp ratings and Force Rank XP for @victim and all players that took part in his/her death
|
|
//
|
|
void adjustPvPPoints(obj_id victim)
|
|
{
|
|
// objvars look like this...
|
|
// creditForPvPKills.attackerList
|
|
// creditForPvPKills.attackerList.1311.damage
|
|
// creditForPvPKills.attackerList.2455.damage
|
|
// creditForPvPKills.attackerList.2343.damage
|
|
|
|
int totalDamageTally = 0;
|
|
if (utils.hasScriptVar(victim, VAR_TOTAL_DAMAGE_TALLY))
|
|
totalDamageTally = (int) utils.getIntScriptVar(victim, VAR_TOTAL_DAMAGE_TALLY);
|
|
|
|
|
|
if(totalDamageTally < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_var[] winners = _packageCombatWinners(victim);
|
|
if (winners == null)
|
|
return;
|
|
|
|
boolean victimDeathCountsForPoints = false;
|
|
boolean onlyThrottledAttackers = true;
|
|
obj_id messageWinner = null;
|
|
int victimRatingDelta = 0;
|
|
int victimXPDelta = 0;
|
|
dictionary darkJediXPRequests = new dictionary();
|
|
darkJediXPRequests.put("victim", victim);
|
|
darkJediXPRequests.put("totalDamageToVictim", totalDamageTally);
|
|
|
|
dictionary darkJediPvPAction = new dictionary(); // used to store dmg amounts and types against dark jedi who might be involved in a pvp ACTION
|
|
darkJediPvPAction.put("victim", victim);
|
|
darkJediPvPAction.put("totalDamageToVictim", totalDamageTally);
|
|
|
|
for(int i = 0; i < winners.length; i++)
|
|
{
|
|
obj_var winnerVar = winners[i];
|
|
|
|
int dam = winnerVar.getIntData();
|
|
obj_id winner = utils.stringToObjId(winnerVar.getName());
|
|
|
|
//the percentage of player damage / total damage inflicted on target
|
|
float percentContribution = ((float)dam / totalDamageTally);
|
|
|
|
trace.log("pvp_rating", "Calculating point exchange for winner " + winnerVar.getName(), victim, trace.TL_DEBUG);
|
|
|
|
if((!isIdValid(winner)) ||
|
|
(!winner.isLoaded()) ||
|
|
(winner.isBeingDestroyed()) ||
|
|
(winner == victim)
|
|
)
|
|
{
|
|
//hrmm
|
|
trace.log("pvp_rating", "pvp.scriptlib.adjustPvPRatings: killer is invalid, !isLoaded, or isBeingDestroyed. ignoring...", winner, trace.TL_WARNING);
|
|
continue;
|
|
}
|
|
|
|
else // killer is valid
|
|
{
|
|
messageWinner = winner; // this is used for the "you've died to blah blah" message
|
|
|
|
// neither XP nor ratings can be affected if the winner has killed the victim recently (victim is in winners last 10 victims list)
|
|
if(!hasKilledVictimRecently(winner, victim))
|
|
{
|
|
onlyThrottledAttackers = false;
|
|
if(getDistance(winner, victim) > MAX_DISTANCE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// In the case of duels or players going on leave after doing damage do not award them pvp rating -PMW
|
|
if ( !pvpCanAttack(victim, winner) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(canAffectForceRankXPChange(victim, winner))
|
|
{
|
|
// need to see if this was two dark jedis fighting. if so, we need to send the XP request to the enclave
|
|
// for validation. see comments in canAffectForceRankXPChange for details.
|
|
if(force_rank.getCouncilAffiliation(victim) == force_rank.DARK_COUNCIL && force_rank.getCouncilAffiliation(winner) == force_rank.DARK_COUNCIL)
|
|
{
|
|
darkJediXPRequests.put(winner, percentContribution);
|
|
darkJediPvPAction.put(winner, percentContribution);
|
|
}
|
|
else
|
|
{
|
|
// Grant Force Rank XP to the winner
|
|
int xpAdjustment = getAdjustedForceRankXPDelta(victim, winner, percentContribution, false);
|
|
if(xpAdjustment != 0)
|
|
{
|
|
force_rank.adjustForceRankXP(winner, xpAdjustment);
|
|
}
|
|
|
|
victimXPDelta += getAdjustedForceRankXPDelta(victim, winner, percentContribution, true);
|
|
trace.log("force_rank", "Winner " + utils.getRealPlayerFirstName(winner) + "(" + winner + ") was awarded " + xpAdjustment + " Council XP for their " + percentContribution*100 + "% contribution to the death of %TU", victim, trace.TL_CS_LOG | trace.TL_DEBUG);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// still need to check if they are both dark council...
|
|
if(force_rank.getCouncilAffiliation(victim) == force_rank.DARK_COUNCIL && force_rank.getCouncilAffiliation(winner) == force_rank.DARK_COUNCIL)
|
|
{
|
|
darkJediPvPAction.put(winner, percentContribution);
|
|
}
|
|
}
|
|
|
|
// Grant PvP Rating points
|
|
int winnerDelta = getAdjustedPvPRatingDelta(victim, winner, percentContribution, false);
|
|
int winnerCur = getCurrentPvPRating(winner);
|
|
|
|
trace.log(PVP_CS_LOG, "Winner " + utils.getRealPlayerFirstName(winner) + "(" + winner + ") was awarded " + winnerDelta + " PvP Rating Points for their " + percentContribution*100 + "% contribution to the death of %TU", victim, trace.TL_CS_LOG | trace.TL_DEBUG);
|
|
setCurrentPvPRating(winner, winnerCur + winnerDelta);
|
|
|
|
// track victim delta
|
|
victimRatingDelta += getAdjustedPvPRatingDelta(victim, winner, percentContribution, true);
|
|
|
|
// message the winner (victim gets message after all the winners are notified)
|
|
prose_package ppr = getPvPRatedProsePackage(winner, victim, true);
|
|
sendSystemMessageProse(winner, ppr);
|
|
|
|
// sploit inhibitor so that people can't farm each other for XP repeatedly
|
|
registerPlayerKill(winner, victim);
|
|
}
|
|
else
|
|
{
|
|
// still need to check if they are both dark council...because even if killed recently, can still kill for PVP_ACTION, well give em the XP too
|
|
if(force_rank.getCouncilAffiliation(victim) == force_rank.DARK_COUNCIL && force_rank.getCouncilAffiliation(winner) == force_rank.DARK_COUNCIL)
|
|
{
|
|
darkJediPvPAction.put(winner, percentContribution);
|
|
}
|
|
else
|
|
{
|
|
// message the winner to let them know they cant gain off of that victim yet
|
|
prose_package pp = getPvPThrottleProsePackage(winner, victim, true);
|
|
sendSystemMessageProse(winner, pp);
|
|
trace.log(PVP_CS_LOG, "PvP points not awarded to " + utils.getRealPlayerFirstName(winner) + "(" + winner + ") for participation in death of %TU - recently killed.", victim, trace.TL_CS_LOG | trace.TL_DEBUG);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if(darkJediXPRequests.size() > 2) // >2 because there are always 2 items in the dictionary from init at method beginning
|
|
{
|
|
if(hasObjVar(victim, force_rank.VAR_MY_ENCLAVE_ID))
|
|
{
|
|
obj_id myEnclave = getObjIdObjVar(victim, force_rank.VAR_MY_ENCLAVE_ID);
|
|
messageTo(myEnclave, "XPDeathValidateRequest", darkJediXPRequests, 0, false);
|
|
}
|
|
}
|
|
|
|
// need to deal with dark jedi death due to PvP action
|
|
if(hasObjVar(victim, force_rank.VAR_NOTIFY_ENCLAVE_OF_DEATH) && darkJediPvPAction.size() > 2)
|
|
{
|
|
obj_id darkEnclave = getObjIdObjVar(victim, force_rank.VAR_NOTIFY_ENCLAVE_OF_DEATH);
|
|
messageTo(darkEnclave, "msgVictimizedJediDeath", darkJediPvPAction, 0, false);
|
|
}
|
|
*/
|
|
|
|
if(victimXPDelta != 0)
|
|
{
|
|
trace.log("force_rank", "%TU lost a total of " + victimXPDelta + " Council XP due to normal pvp for this death.", victim, trace.TL_CS_LOG);
|
|
force_rank.adjustForceRankXP(victim, victimXPDelta);
|
|
}
|
|
|
|
if(victimRatingDelta != 0) // loser has died to at least one valid winner.
|
|
{
|
|
trace.log(PVP_CS_LOG, "%TU lost a total of " + victimRatingDelta + " PvP rating points to normal pvp for this death.", victim, trace.TL_CS_LOG);
|
|
// system message the victim
|
|
setCurrentPvPRating(victim, getCurrentPvPRating(victim) + victimRatingDelta);
|
|
prose_package pp = getPvPRatedProsePackage(messageWinner, victim, false);
|
|
sendSystemMessageProse(victim, pp);
|
|
}
|
|
else if(onlyThrottledAttackers && isIdValid(messageWinner))
|
|
{
|
|
prose_package pp = getPvPThrottleProsePackage(messageWinner, victim, false);
|
|
sendSystemMessageProse(victim, pp);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// checks any rules to see if these two people can affect each other's force rank XP.
|
|
// this is where we check for faction alignment, vendettas, banishments, etc
|
|
//
|
|
boolean canAffectForceRankXPChange(obj_id victim, obj_id winner)
|
|
{
|
|
// if neither person is force ranked, then we can stop right here
|
|
if((!force_rank.isForceRanked(victim)) && (!force_rank.isForceRanked(winner)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// **at this point, we have at least one person who is force ranked...**
|
|
// if they're in opposing factions and at least one of them is a jedi,
|
|
// this has to be a light vs. dark fight - someone is gaining or losing XP
|
|
boolean isOpposed = isInOpposingPvPFactions(victim, winner);
|
|
if(isOpposed)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// we know now that the two players are not opposed - check for light vs light and dark vs dark fights
|
|
int winnerCouncil = force_rank.getCouncilAffiliation(winner);
|
|
int victimCouncil = force_rank.getCouncilAffiliation(victim);
|
|
|
|
if((victimCouncil == force_rank.LIGHT_COUNCIL && winnerCouncil == force_rank.LIGHT_COUNCIL) ||
|
|
(victimCouncil == force_rank.DARK_COUNCIL && winnerCouncil == force_rank.DARK_COUNCIL))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
// we now know that...
|
|
// 1. Either victim or winner is force ranked
|
|
// 2. Winner and victim are not opposing faction members
|
|
// 3. Winner and victim are not part of the same council affiliation
|
|
|
|
// From that we can extrapolate that either victim or winner is a neutral.
|
|
// If that neutral is either a padawan or a bounty hunter, XP is going be lost/gained
|
|
int winnerFaction = pvpGetAlignedFaction(winner);
|
|
int victimFaction = pvpGetAlignedFaction(victim);
|
|
|
|
if(winnerFaction == 0 && (isPadawan(winner) || isBountyHunter(winner)))
|
|
{
|
|
// victim is the FR jedi and loses some points for being killed by either a non-factioned padawan or bounty hunter
|
|
return true;
|
|
}
|
|
|
|
if(victimFaction == 0 && (isPadawan(victim) || isBountyHunter(victim)))
|
|
{
|
|
// winner is FR jedi and gains some points for killing either a non-factioned padawan or bounty hunter
|
|
return true;
|
|
}
|
|
|
|
/*********** CODE BELOW HAS BEEN COMMENTED AND IS TO BE USED ONLY WHEN BANISHMENT/COUNCIL PURGES ARE ENABLED AGAIN********
|
|
|
|
// at this point we can assume that both people are force ranked and of the same alignment (at least one person force ranked, and both people in the same
|
|
// faction). We also know that no Padawans and no Bounty hunters are involved in the battle.
|
|
|
|
// now we know we're dealing with two ranked, dark jedi. check for being part of the same
|
|
// vendetta, if the victim is banished or the victim is being council-purged.
|
|
// since we have to be authoritative with the enclave to read the data that lets us know if
|
|
// the two players are part of any Dark Jedi special case PvP scenarios (vendetta, banishment, etc)
|
|
// we have to send a message to the enclave to validate the kill. the check for dark jedi happens
|
|
// in the method that calls this one. the message handler to finally dish out the XP after the kill
|
|
// has been validated can be found in player_force_rank::XPDeathValidateResponse
|
|
|
|
/*
|
|
if(!hasObjVar(victim, force_rank.VAR_MY_ENCLAVE_ID)
|
|
{
|
|
LOG("pvp_rating", "pvp::canAffectForceRankXPChange: -> Missing enclave objvar on victim. bailing out and denying pvp XP delta.");
|
|
return false;
|
|
}
|
|
|
|
obj_id darkEnclave = getObjVar(victim, VAR_MY_ENCLAVE_ID);
|
|
if(force_rank.isPartOfSameVendetta(victim, winner, darkEnclave))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
force_rank.isPvPActionVictim(victim, force_rank.ACTION_BANISHMENT, darkEnclave)
|
|
force_rank.isPvPActionVictim(victim, force_rank.ACTION_COUNCIL_PURGE, darkEnclave)
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Returns the Force Rank XP delta for @player based on a fight against @opponent and
|
|
// whether or not the @playerWon the fight or not. The delta is unmodified at this stage.
|
|
// Currently the XP Delta comes from a datatable (FRS_XP_DATATABLE)
|
|
//
|
|
int getRawForceRankXPDelta(obj_id player, boolean playerWon, obj_id opponent)
|
|
{
|
|
int playerRank = force_rank.getForceRank(player);
|
|
int opponentRank = force_rank.getForceRank(opponent);
|
|
|
|
if(playerRank < 0)
|
|
{
|
|
// @player is not ranked, hence needs no adjustments to the force ranking xp.
|
|
return 0;
|
|
}
|
|
|
|
string columnName = "";
|
|
string rowName = "";
|
|
string rowPrefix = "";
|
|
string rowSuffix = "";
|
|
|
|
columnName = "rank" + playerRank; // <-- this is the @player in the data table
|
|
|
|
// check if we killed a factioned individual
|
|
if(isInOpposingPvPFactions(player, opponent))
|
|
{
|
|
rowPrefix = FRS_DT_ROW_NONJEDI_PREFIX;
|
|
}
|
|
|
|
// padawn?
|
|
if(isPadawan(opponent))
|
|
{
|
|
rowPrefix = FRS_DT_ROW_PADAWAN_PREFIX;
|
|
}
|
|
|
|
// ranked jedi?
|
|
if(opponentRank > -1)
|
|
{
|
|
rowPrefix = FRS_DT_RANKED_JEDI_PREFIX + opponentRank;
|
|
}
|
|
|
|
// bounty hunter?
|
|
if(isBountyHunter(opponent))
|
|
{
|
|
rowPrefix = FRS_DT_ROW_BNTYHUNTER_PREFIX;
|
|
}
|
|
|
|
// win or lose against opponent?
|
|
if(playerWon)
|
|
{
|
|
rowSuffix = FRS_DT_ROW_WIN_SUFFIX;
|
|
}
|
|
else
|
|
{
|
|
rowSuffix = FRS_DT_ROW_LOSS_SUFFIX;
|
|
}
|
|
|
|
rowName = rowPrefix + rowSuffix; // <-- this is the opponent in the data table
|
|
|
|
int xpDelta = dataTableGetInt(FRS_XP_DATATABLE, rowName, columnName);
|
|
if(xpDelta == -1)
|
|
{
|
|
return 0; // -1 if the column can't be found, so return zero so that nothing happens.
|
|
}
|
|
|
|
if(!playerWon)
|
|
{
|
|
xpDelta *= -1;
|
|
}
|
|
|
|
trace.log("pvp_rating", "Force Rank XP Delta for player " + utils.getRealPlayerFirstName(player) + " is " + xpDelta, player, trace.TL_DEBUG);
|
|
return xpDelta;
|
|
}
|
|
|
|
//
|
|
// gets an adjusted rating delta for eaither victim or killer, based on the percentCOntribution of the killer to the victim's death
|
|
//
|
|
int getAdjustedForceRankXPDelta(obj_id victim, obj_id killer, double percentContribution, boolean gettingVictimsDelta)
|
|
{
|
|
string wonlost = "winning the battle against";
|
|
if(gettingVictimsDelta)
|
|
{
|
|
wonlost = "losing the battle to";
|
|
}
|
|
|
|
obj_id targetPlayer = null;
|
|
obj_id opponent = null;
|
|
|
|
if(gettingVictimsDelta)
|
|
{
|
|
targetPlayer = victim;
|
|
opponent = killer;
|
|
}
|
|
else
|
|
{
|
|
targetPlayer = killer;
|
|
opponent = victim;
|
|
}
|
|
|
|
int targetRat = getCurrentPvPRating(targetPlayer);
|
|
int opponentRat = getCurrentPvPRating(opponent);
|
|
int disparity = Math.abs(targetRat - opponentRat);
|
|
|
|
if(disparity > MAX_RATING_DISPARITY)
|
|
{
|
|
disparity = MAX_RATING_DISPARITY;
|
|
}
|
|
|
|
trace.log("pvp_rating", "Target " + utils.getRealPlayerFirstName(targetPlayer) + "'s PvP rating is " + targetRat + "; Opponent " + utils.getRealPlayerFirstName(opponent) + "'s rating is " + opponentRat + " for a final disparity of " + disparity, opponent, trace.TL_DEBUG);
|
|
|
|
// this is the % of XP adjustment, based on total PvP Rating disparity
|
|
float pvpRatingXPAdjustment = ((float)disparity / (float)MAX_RATING_DISPARITY) * (float)MAX_RATING_AFFECT_ON_XP;
|
|
|
|
trace.log("pvp_rating", "Rating Disparity is " + disparity + ", creating an XP mod of " + (pvpRatingXPAdjustment*100) + "% (" + pvpRatingXPAdjustment + ")", null, trace.TL_DEBUG);
|
|
|
|
//
|
|
// Do the targets adjustments
|
|
//
|
|
int targetDelta = getRawForceRankXPDelta(targetPlayer, !gettingVictimsDelta, opponent);
|
|
trace.log("pvp_rating", utils.getRealPlayerFirstName(targetPlayer) + "'s raw target player XP Delta for " + wonlost + " opponent " + utils.getRealPlayerFirstName(opponent) + " is " + targetDelta, targetPlayer, trace.TL_DEBUG);
|
|
|
|
if(targetDelta != 0)
|
|
{
|
|
// adjust the point delta by the amount of killer contribution to victim's death
|
|
targetDelta = (int)((float) targetDelta * percentContribution);
|
|
trace.log("pvp_rating", "After modifying the XP delta with the % contribution the adjusted XP delta for " + utils.getRealPlayerFirstName(targetPlayer) + " is " + targetDelta, null, trace.TL_DEBUG);
|
|
|
|
// adjust the point delta further given the PvP Rating disparity between the two players
|
|
if((targetRat < opponentRat && gettingVictimsDelta) || (targetRat > opponentRat && !gettingVictimsDelta))
|
|
{
|
|
targetDelta -= (int)((float)targetDelta * pvpRatingXPAdjustment);
|
|
}
|
|
else
|
|
{
|
|
targetDelta += (int)((float)targetDelta * pvpRatingXPAdjustment);
|
|
}
|
|
|
|
trace.log("pvp_rating", "After adjusting target " + utils.getRealPlayerFirstName(targetPlayer) + "'s XP delta against the RatingDisparity adjustment of " + pvpRatingXPAdjustment + ", the **FINAL** adjusted XP Delta is " + targetDelta, targetPlayer, trace.TL_DEBUG);
|
|
}
|
|
|
|
return targetDelta;
|
|
}
|
|
|
|
//
|
|
// gets PvP Ratings delta for either @loser or @winner, based on the @percentInvolvement of the winner
|
|
//
|
|
int getAdjustedPvPRatingDelta(obj_id loser, obj_id winner, double percentInvolvement, boolean gettingVictimsDelta)
|
|
{
|
|
if(percentInvolvement > 1.0)
|
|
{
|
|
trace.log("pvp_rating", "pvp.scriptlib.getAdjustedPvPRatingDelta:-> percentInvolvement > 1.0 (" + percentInvolvement + "). Bailing.", loser, trace.TL_WARNING | trace.TL_DEBUG);
|
|
return 0;
|
|
}
|
|
|
|
obj_id targetPlayer = null;
|
|
obj_id opponent = null;
|
|
|
|
if(gettingVictimsDelta)
|
|
{
|
|
targetPlayer = loser;
|
|
opponent = winner;
|
|
}
|
|
else
|
|
{
|
|
targetPlayer = winner;
|
|
opponent = loser;
|
|
}
|
|
|
|
// Get current ratings
|
|
int targetRating = getCurrentPvPRating(targetPlayer);
|
|
int opponentRating = getCurrentPvPRating(opponent);
|
|
|
|
// Get deltas at 100%
|
|
int targetDelta = getPvPRatingDelta(targetRating, opponentRating, (!gettingVictimsDelta));
|
|
trace.log("pvp_rating", utils.getRealPlayerFirstName(targetPlayer) + "'s raw RATING DELTA, based on a battle against " + utils.getRealPlayerFirstName(opponent) + " is " + targetDelta, targetPlayer, trace.TL_DEBUG);
|
|
|
|
// Adjust deltas based on rules
|
|
targetDelta = _massageDeltaWithRules(targetDelta, !gettingVictimsDelta);
|
|
trace.log("pvp_rating", "After adjusting " + utils.getRealPlayerFirstName(targetPlayer) + "'s raw RATING DELTA, based on Min/Max loss/gain rules, the adjusted delta is " + targetDelta, targetPlayer, trace.TL_DEBUG);
|
|
|
|
// scale deltas
|
|
targetDelta *= percentInvolvement;
|
|
trace.log("pvp_rating", "After scaling " + utils.getRealPlayerFirstName(targetPlayer) + "'s delta, based on the % involvement vs. opponent (" + utils.getRealPlayerFirstName(opponent) + "), the **FINAL** adjusted delta is " + targetDelta, targetPlayer, trace.TL_DEBUG);
|
|
|
|
return targetDelta;
|
|
}
|
|
|
|
//
|
|
// returns current pvp rating for @player
|
|
//
|
|
int getCurrentPvPRating(obj_id player)
|
|
{
|
|
if(!isIdValid(player))
|
|
{
|
|
trace.log("pvp_rating", "pvp_rating.scriptlib:-> Can't fetch PvP Rating for Invalid player ID. Bailing.", player, trace.TL_ERROR_LOG | trace.TL_DEBUG);
|
|
return -1;
|
|
}
|
|
|
|
return getIntObjVar(player, VAR_PVP_RATING);
|
|
}
|
|
|
|
//
|
|
// sets a new pvp @rating on @player and returns success or failure of the operation
|
|
//
|
|
boolean setCurrentPvPRating(obj_id player, int rating)
|
|
{
|
|
if(!isIdValid(player))
|
|
{
|
|
trace.log("pvp_rating", "pvp_rating.scriptlib:-> Can't set PvP Rating for Invalid player ID. Bailing.", player, trace.TL_ERROR_LOG | trace.TL_DEBUG);
|
|
return false;
|
|
}
|
|
|
|
setObjVar(player, VAR_PVP_RATING, rating);
|
|
|
|
// If force ranked, update the council pvp board.
|
|
//int council = force_rank.getCouncilAffiliation(player);
|
|
//if (council != -1)
|
|
// force_rank.updatePvpBoardData(player, rating, council, "msgUpdatePvpBoard");
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// private helper method that makes any necessary adjustments to a rating @delta value, based on various rules, given
|
|
// wheter or not the player @wonFight
|
|
//
|
|
int _massageDeltaWithRules(int delta, boolean wonFight)
|
|
{
|
|
// No one can ever gain or lose more than 40 points
|
|
if(Math.abs(delta) > MAX_RATING_DELTA)
|
|
{
|
|
if(wonFight)
|
|
{
|
|
delta = MAX_RATING_DELTA;
|
|
}
|
|
else
|
|
{
|
|
delta = MAX_RATING_DELTA * -1;
|
|
}
|
|
}
|
|
|
|
// Winner must never lose points,
|
|
if(delta < 0 && wonFight)
|
|
{
|
|
delta = 0;
|
|
}
|
|
// Loser must never gain points.
|
|
else if(delta > 0 && (!wonFight))
|
|
{
|
|
delta = 0;
|
|
}
|
|
|
|
// Winner must always gain at least two points,
|
|
// loser must always lose at least two points
|
|
// -=-=currently disabled=-=-
|
|
if(ENFORCE_MINIMUM_DELTA)
|
|
{
|
|
if(delta < 2 && wonFight)
|
|
{
|
|
delta = 2;
|
|
}
|
|
else if(delta > -2 && (!wonFight))
|
|
{
|
|
delta = -2;
|
|
|
|
}
|
|
}
|
|
|
|
return delta;
|
|
}
|
|
|
|
//
|
|
// calculates and returns the PvP Rating delta
|
|
// for a fight, given the @oldRating, the @opponentRating and wether or not
|
|
// the player with the @oldRating @wonFight or not
|
|
//
|
|
int getPvPRatingDelta(int oldRating, int opponentRating, boolean wonFight)
|
|
{
|
|
int score = 1;
|
|
if(!wonFight)
|
|
{
|
|
score = -1;
|
|
}
|
|
|
|
int newRating = oldRating + (score * 21) + ((opponentRating - oldRating) / 25);
|
|
int ratingDelta = newRating - oldRating;
|
|
|
|
trace.log("pvp_rating", "pvp::getPvPRatingDelta -> returned " + ratingDelta);
|
|
return ratingDelta;
|
|
}
|
|
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
///-/-/-/-/-/-/-/-/-START CREDIT FOR PVP SECTION/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
|
|
//
|
|
// Updates the script vars on @target with new total damage and ads @dam to @attacker's total
|
|
//
|
|
void updateDamageContributorList(obj_id target, obj_id attackr, int dam)
|
|
{
|
|
obj_id realAttacker = attackr;
|
|
|
|
if (!isIdValid(target) || (!isIdValid(realAttacker)))
|
|
{
|
|
trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Target or Attacker is invalid. Bailing.", target, trace.TL_WARNING | trace.TL_DEBUG);
|
|
return;
|
|
}
|
|
|
|
// are we getting attacked by a pet?
|
|
if(pet_lib.isPet(realAttacker))
|
|
{
|
|
if(!COUNT_PET_DAMAGE)
|
|
{
|
|
//trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Ignoring damage from pet.", realAttacker, trace.TL_DEBUG);
|
|
return;
|
|
}
|
|
|
|
if(!pet_lib.hasMaster(realAttacker))
|
|
{
|
|
//trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Pet has no master. Ignoring damage.", realAttacker, trace.TL_WARNING | trace.TL_DEBUG);
|
|
return;
|
|
}
|
|
|
|
realAttacker = getMaster(attackr); // pet's master has already been validated in previous call to pet_lib.hasMaster
|
|
|
|
//trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Scaling pet damage of " + dam + " down by " + COUNT_PET_DAMAGE_PERCENT, realAttacker);
|
|
// adjust damage
|
|
dam = (int)((float)dam * COUNT_PET_DAMAGE_PERCENT);
|
|
//trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Pet damage now " + dam + ".", realAttacker);
|
|
}
|
|
else if(!isPlayer(realAttacker))
|
|
{
|
|
//trace.log("pvp_rating.scriptlib", "updateDamageContributorList:-> Ignoring NPC Damage.", realAttacker);
|
|
return;
|
|
}
|
|
|
|
if(dam > 0)
|
|
{
|
|
// Update last PvP damage timestamp
|
|
updatePvPDamageTimeStamp(target);
|
|
|
|
// Update total damage done.
|
|
double tally = utils.getIntScriptVar(target, VAR_TOTAL_DAMAGE_TALLY); //how much total damage has been done?
|
|
tally += dam;
|
|
utils.setScriptVar(target, VAR_TOTAL_DAMAGE_TALLY, (int)tally);
|
|
|
|
// Setup objvars for this attacker if they don't exist
|
|
resizeable obj_id[] attackerList = utils.getResizeableObjIdBatchScriptVar(target, VAR_ATTACKER_LIST);
|
|
if(attackerList != null && attackerList.length > 0)
|
|
{
|
|
if (utils.getElementPositionInArray(attackerList, realAttacker) == -1)
|
|
{
|
|
attackerList = utils.addElement(attackerList, realAttacker);
|
|
utils.setBatchScriptVar(target, VAR_ATTACKER_LIST, attackerList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
attackerList = utils.addElement(attackerList, realAttacker);
|
|
utils.setBatchScriptVar(target, VAR_ATTACKER_LIST, attackerList);
|
|
}
|
|
|
|
// Update damage for this attacker
|
|
string basePath = VAR_ATTACKER_LIST + "." + realAttacker;
|
|
string damPath = basePath + ".damage";
|
|
|
|
int totalDamage = dam + utils.getIntScriptVar(target, damPath); //add any previous damage done by player
|
|
utils.setScriptVar(target, damPath, totalDamage); //setting the player's net Id as a String = key and damage = value
|
|
|
|
//trace.log("pvp_rating", utils.getRealPlayerFirstName(realAttacker) + " now has " + totalDamage+ " credits.", realAttacker);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
///-/-/-/-/-/-/-/-/-START HOUSEKEEPING FOR XP & RATINGS/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
|
|
//
|
|
// @returns true if @attacker can affect a rating/force ranking update as a result of killing @victim based on the
|
|
// the @attacker's VAR_PVP_LAST_KILLS list.
|
|
//
|
|
boolean hasKilledVictimRecently(obj_id attacker, obj_id victim)
|
|
{
|
|
boolean rslt = true;
|
|
|
|
// try grabbing the list of objvars
|
|
obj_id[] victimList = utils.getObjIdBatchObjVar(attacker, VAR_PVP_LAST_KILLS);
|
|
|
|
if(victimList != null)
|
|
{
|
|
// exists in victim list?
|
|
if(utils.getElementPositionInArray(victimList, victim) == -1)
|
|
{
|
|
rslt = false;
|
|
}
|
|
}
|
|
else // list is null, cause we don't have one set yet, we return true
|
|
{
|
|
rslt = false;
|
|
}
|
|
|
|
// trace.log("pvp_rating", "pvp.scriptlib::hasKilledVictimRecently(attacker:" + utils.getRealPlayerFirstName(attacker) +
|
|
// ", victim:" + utils.getRealPlayerFirstName(victim) + ") returned: " + rslt, attacker);
|
|
return rslt;
|
|
}
|
|
|
|
//
|
|
// adds the @victim to the @attacker's VAR_PVP_LAST_KILLS list. this will disable any further
|
|
// PvP related point updates based on any subsequent battles in which @attacker kills @victim.
|
|
// Note that if @victim should kill @attacker PvP related points will be updated.
|
|
// @attacker can once again affect a PvP related point update against @victim once @attacker
|
|
// adds enough people (more than XP_FARMING_TRACKING_SLOTS) to his VAR_PVP_LAST_KILLS list, thus
|
|
// bumping the @victim off. Currently registering as script vars. if it becomes a problem that
|
|
// players are logging out a bunch to clear these, then we need to consider objvars
|
|
//
|
|
void registerPlayerKill(obj_id attacker, obj_id victim)
|
|
{
|
|
// try grabbing the list of objvars
|
|
resizeable obj_id[] victimList = utils.getResizeableObjIdBatchObjVar(attacker, VAR_PVP_LAST_KILLS);
|
|
|
|
if(victimList != null && victimList.length > 0)
|
|
{
|
|
// sanity check - don't want to add the victim again for some strange reason
|
|
if(utils.getElementPositionInArray(victimList, victim) == -1)
|
|
{
|
|
//trace.log("pvp_rating", "pvp.scriptlib::registerPlayerKill-> 1. adding victim " + utils.getRealPlayerFirstName(victim) + " to " + utils.getRealPlayerFirstName(attacker) + "'s victim list.", attacker);
|
|
victimList = utils.addElement(victimList, victim);
|
|
while(victimList.length > XP_FARMING_TRACKING_SLOTS)
|
|
{
|
|
victimList = utils.removeElementAt(victimList, 0);
|
|
}
|
|
|
|
utils.setResizeableBatchObjVar(attacker, VAR_PVP_LAST_KILLS, victimList);
|
|
trace.log(PVP_CS_LOG, "Adding " + utils.getRealPlayerFirstName(victim) + "(" + victim + ") to last-50-victim-list of %TU", attacker, trace.TL_CS_LOG);
|
|
}
|
|
else
|
|
{
|
|
//trace.log("pvp_rating", "pvp.scriptlib::registerPlayerKill:-> tried adding a victim that was already in our list. shouldn't happen. ignoring request.", attacker, trace.TL_WARNING);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
victimList = utils.addElement(victimList, victim);
|
|
while(victimList.length > XP_FARMING_TRACKING_SLOTS)
|
|
{
|
|
victimList = utils.removeElementAt(victimList, 0);
|
|
}
|
|
|
|
utils.setResizeableBatchObjVar(attacker, VAR_PVP_LAST_KILLS, victimList);
|
|
}
|
|
|
|
setObjVar(attacker, VAR_PVP_LAST_UPDATE, getGameTime() + PVP_TRACKING_INTERVAL);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Whipes all of the credit vars off of the player. Normally called if no PvP damage has been sustained for PVP_DAMAGE_CLEAR_INTERVAL seconds
|
|
//
|
|
boolean cleanupCreditForPvPKills(obj_id whichPlayer)
|
|
{
|
|
utils.removeScriptVarTree(whichPlayer, "creditForPvPKills");
|
|
utils.removeScriptVarTree(whichPlayer, "creditForPvPKillsTracking");
|
|
|
|
if (hasObjVar(whichPlayer, VAR_PVP_LAST_UPDATE))
|
|
{
|
|
int last_update = getIntObjVar(whichPlayer, VAR_PVP_LAST_UPDATE);
|
|
if (last_update < getGameTime())
|
|
{
|
|
// The kill tracker list has expired.
|
|
removeObjVar(whichPlayer, VAR_PVP_LAST_KILLS);
|
|
removeObjVar(whichPlayer, VAR_PVP_LAST_UPDATE);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Sets a timestamp for the last time we took eligible PvP damage
|
|
//
|
|
void updatePvPDamageTimeStamp(obj_id player)
|
|
{
|
|
int now = getGameTime();
|
|
|
|
setupClearPvPDamageCallback(player);
|
|
utils.setScriptVar(player, VAR_LAST_PVP_DAMAGE_TIME, now);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Sends a timed message to self if one isn't already pending. msg is handled in player_pvp.
|
|
// message handler determines if the contributions scriptvars should be cleared or not
|
|
//
|
|
void setupClearPvPDamageCallback(obj_id player)
|
|
{
|
|
if(utils.hasScriptVar(player, VAR_LAST_PVP_DAMAGE_MSG_SENT))
|
|
{
|
|
return;
|
|
}
|
|
|
|
messageTo(player, "checkPvPDamageTime", null, CONTRIB_CLEAR_INTERVAL, false);
|
|
utils.setScriptVar(player, VAR_LAST_PVP_DAMAGE_MSG_SENT, true);
|
|
|
|
return;
|
|
}
|
|
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
//-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/
|
|
|
|
// convenience functions to determine various player states. encpasulated in seperate methods as
|
|
// criteria for some of these may change
|
|
|
|
boolean isBountyHunter(obj_id player)
|
|
{
|
|
return hasSkill(player, "class_bountyhunter_phase2_novice");
|
|
}
|
|
|
|
boolean isPadawan(obj_id player)
|
|
{
|
|
return hasSkill(player, "force_title_jedi_rank_02"); // padawan title box
|
|
}
|
|
|
|
boolean isInOpposingPvPFactions(obj_id player1, obj_id player2)
|
|
{
|
|
if ( isPlayer( player1 ) && pvpGetType(player1) == PVPTYPE_NEUTRAL )
|
|
return false;
|
|
else if ( isPlayer( player2 ) && pvpGetType(player2) == PVPTYPE_NEUTRAL )
|
|
return false;
|
|
else
|
|
return(pvpAreFactionsOpposed(pvpGetAlignedFaction(player1), pvpGetAlignedFaction(player2)));
|
|
}
|
|
|
|
|
|
/***** BASE FUNCTIONS **************************************************/
|
|
/*****************************************************************
|
|
* @brief grants a waypoint to the specified object to the player
|
|
*
|
|
* @param obj_id player
|
|
*
|
|
* @return boolean;
|
|
*****************************************************************/
|
|
boolean updatePlayerDamageTracking(obj_id player, obj_id attacker, obj_id wpn, int[] damage)
|
|
{
|
|
if ( !isIdValid(player) || !isPlayer(player) || !isIdValid(attacker) || !isIdValid(wpn) )
|
|
return false;
|
|
|
|
if ( damage == null || damage.length == 0 )
|
|
return false;
|
|
|
|
long tmpTotal = 0;
|
|
for ( int i = 0; i < damage.length; i++ )
|
|
tmpTotal += damage[i];
|
|
|
|
if ( tmpTotal > Integer.MAX_VALUE )
|
|
tmpTotal = Integer.MAX_VALUE;
|
|
|
|
int totalDamage = (int)tmpTotal;
|
|
if ( isPvpDamageSource(player, attacker) )
|
|
{
|
|
int currentDamage = utils.getIntScriptVar(player, SCRIPTVAR_PVP_DAMAGE);
|
|
|
|
long newTotal = currentDamage + totalDamage;
|
|
if ( newTotal > Integer.MAX_VALUE )
|
|
newTotal = Integer.MAX_VALUE;
|
|
|
|
utils.setScriptVar(player, SCRIPTVAR_PVP_DAMAGE, (int)newTotal);
|
|
utils.setScriptVar(player, SCRIPTVAR_PVP_STAMP, getGameTime());
|
|
}
|
|
else //not PVP damage
|
|
{
|
|
int currentDamage = utils.getIntScriptVar(player, SCRIPTVAR_PVE_DAMAGE);
|
|
|
|
long newTotal = currentDamage + totalDamage;
|
|
if ( newTotal > Integer.MAX_VALUE )
|
|
newTotal = Integer.MAX_VALUE;
|
|
|
|
utils.setScriptVar(player, SCRIPTVAR_PVE_DAMAGE, (int)newTotal);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
boolean isPvpDamageSource(obj_id player, obj_id attacker)
|
|
{
|
|
if ( !isIdValid(player) || !isIdValid(attacker) )
|
|
return false;
|
|
|
|
if ( pet_lib.isPet(attacker) )
|
|
{
|
|
obj_id master = getMaster(attacker);
|
|
if ( isIdValid(master) && master.isLoaded() )
|
|
attacker = master;
|
|
}
|
|
|
|
if ( isPlayer(attacker) )
|
|
return true;
|
|
|
|
if ( isPlayer( player ) && pvpGetType(player) == PVPTYPE_NEUTRAL )
|
|
return false;
|
|
|
|
return pvpAreFactionsOpposed(pvpGetAlignedFaction(player), pvpGetAlignedFaction(attacker));
|
|
}
|
|
|
|
/*****************************************************************/
|
|
void validatePlayerDamageTracking(obj_id player)
|
|
{
|
|
if ( !isIdValid(player) )
|
|
return;
|
|
|
|
int now = getGameTime();
|
|
int stamp = utils.getIntScriptVar(player, SCRIPTVAR_PVP_STAMP);
|
|
int delta = now - stamp;
|
|
|
|
if ( delta < BUFFER_TIME )
|
|
return;
|
|
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
int attrib = i*3;
|
|
if ( getAttrib(player, attrib) < getWoundedMaxAttrib(player, attrib) )
|
|
return;
|
|
}
|
|
|
|
clearPlayerDamageTracking(player);
|
|
}
|
|
|
|
/*****************************************************************/
|
|
void clearPlayerDamageTracking(obj_id player)
|
|
{
|
|
if ( !isIdValid(player) )
|
|
return;
|
|
|
|
utils.removeScriptVarTree(player, SCRIPTVAR_DAMAGE_BASE);
|
|
}
|
|
|
|
/*****************************************************************/
|
|
boolean isPvpDeath(obj_id player)
|
|
{
|
|
if ( !isIdValid(player) || !isPlayer(player) || !isDead(player) )
|
|
return false;
|
|
|
|
int now = getGameTime();
|
|
int stamp = utils.getIntScriptVar(player, SCRIPTVAR_PVP_STAMP);
|
|
int delta = now - stamp;
|
|
|
|
if ( delta >= BUFFER_TIME )
|
|
return false;
|
|
|
|
int pvpDamage = utils.getIntScriptVar(player, SCRIPTVAR_PVP_DAMAGE);
|
|
int pveDamage = utils.getIntScriptVar(player, SCRIPTVAR_PVE_DAMAGE);
|
|
|
|
//sendSystemMessageTestingOnly(player, "pvp death = "+(pvpDamage > pveDamage));
|
|
return (pvpDamage > pveDamage);
|
|
}
|
|
|
|
void incrementPlayerDeathBounty(obj_id killer, obj_id victim)
|
|
{
|
|
if(!isIdValid(killer) || !isIdValid(victim))
|
|
return;
|
|
|
|
if(!isPlayer(killer) || !isPlayer(victim))
|
|
return;
|
|
|
|
if(hasKilledVictimRecently(killer, victim))
|
|
return;
|
|
|
|
registerPlayerKill(killer, victim);
|
|
|
|
int bounty = 0;
|
|
|
|
if(hasObjVar(killer, "bounty.amount"))
|
|
bounty = getIntObjVar(killer, "bounty.amount");
|
|
|
|
bounty += 1000;
|
|
|
|
setObjVar(killer, "bounty.amount", bounty);
|
|
|
|
if(bounty >= 10000)
|
|
{
|
|
setJediBountyValue(killer, bounty);
|
|
}
|
|
|
|
// int visibilityDelta = (int)(pvpRating / 6.0f);
|
|
// changeJediVisibility(killer, visibilityDelta);
|
|
|
|
CustomerServiceLog("bounty", "A bounty of 1000 credits has been automatically put on %TU for killing %TT", killer, victim);
|
|
}
|
|
|
|
// Handles the warping of players to locations in a battlefield.
|
|
void battlefieldWarp(obj_id player, location loc)
|
|
{
|
|
// If the player we're inviting is dead, then we should revive them.
|
|
if(isIncapacitated(player) || isDead(player))
|
|
{
|
|
// Close the cloning location selection window, so they do not warp
|
|
// themselves out of the battlefield after they've been moved to the battlefield.
|
|
int closeSui = utils.getIntScriptVar(player, pclib.VAR_SUI_CLONE);
|
|
|
|
forceCloseSUIPage(closeSui);
|
|
|
|
// Battlefield players do not get cloning sickness.
|
|
utils.setScriptVar(player, "no_cloning_sickness", 1);
|
|
pclib.playerRevive(player);
|
|
}
|
|
|
|
// Set non-combatant (outside of pvp region)
|
|
pvpMakeCovert(player);
|
|
|
|
utils.warpPlayer(player, loc);
|
|
}
|
|
|
|
void bfActivePlayersAnnounce(obj_id controller, string_id announcement)
|
|
{
|
|
resizeable obj_id[] players = new obj_id[0];
|
|
|
|
if(utils.hasResizeableObjIdBatchScriptVar(controller, BATTLEFIELD_ACTIVE_PLAYERS))
|
|
{
|
|
players = utils.getResizeableObjIdBatchScriptVar(controller, BATTLEFIELD_ACTIVE_PLAYERS);
|
|
}
|
|
|
|
dictionary params = new dictionary();
|
|
|
|
if(players != null && players.length > 0)
|
|
{
|
|
for(int i = 0, j = players.length; i < j; i++)
|
|
{
|
|
if(!isIdValid(players[i]) || !exists(players[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sendSystemMessage(players[i], announcement);
|
|
}
|
|
}
|
|
}
|
|
|
|
void bfMessagePlayers(obj_id controller, string faction, string message, dictionary params)
|
|
{
|
|
resizeable string[] players = new string[0];
|
|
|
|
if(utils.hasResizeableStringBatchScriptVar(controller, faction))
|
|
{
|
|
players = utils.getResizeableStringBatchScriptVar(controller, faction);
|
|
}
|
|
|
|
if(players != null && players.length > 0)
|
|
{
|
|
// blog(controller, " bfMessagePlayers players.length: " + players.length);
|
|
|
|
for(int i = 0, j = players.length; i < j; i++)
|
|
{
|
|
if(players[i] == null || players[i].length() < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string [] playerInfo = split(players[i], '^');
|
|
|
|
if(playerInfo == null || playerInfo.length < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
obj_id player = utils.stringToObjId(playerInfo[0]);
|
|
|
|
if(!isIdValid(player))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
messageTo(player, message, params, 1.0f, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void bfMessagePlayersOnBattlefield(obj_id controller, string faction, string message, dictionary params)
|
|
{
|
|
resizeable string[] players = new string[0];
|
|
|
|
if(utils.hasResizeableStringBatchScriptVar(controller, faction))
|
|
{
|
|
players = utils.getResizeableStringBatchScriptVar(controller, faction);
|
|
}
|
|
|
|
if(players != null && players.length > 0)
|
|
{
|
|
// LOG("gcw_" ,"bfMessagePlayers players.length: " + players.length);
|
|
|
|
for(int i = 0, j = players.length; i < j; i++)
|
|
{
|
|
if(players[i] == null || players[i].length() < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string [] playerInfo = split(players[i], '^');
|
|
|
|
if(playerInfo == null || playerInfo.length < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
obj_id player = utils.stringToObjId(playerInfo[0]);
|
|
|
|
if(!isIdValid(player) || !exists(player))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
messageTo(player, message, params, 1.0f, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
resizeable dictionary[] bfActiveAppendStatistics(obj_id controller, string scriptVar, resizeable dictionary[] battlefieldTeams)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
resizeable string[] activelTeam = new string[0];
|
|
|
|
if(utils.hasResizeableStringBatchScriptVar(controller, scriptVar))
|
|
{
|
|
activelTeam = utils.getResizeableStringBatchScriptVar(controller, scriptVar);
|
|
}
|
|
|
|
if(activelTeam != null && activelTeam.length > 0)
|
|
{
|
|
int count = 0;
|
|
|
|
for(int i = 0, j = activelTeam.length; i < j; i++)
|
|
{
|
|
// Validate the string
|
|
if(activelTeam[i] == null || activelTeam[i].length() < 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string [] activeMember = split(activelTeam[i], '^');
|
|
|
|
// Malformed data for member?
|
|
if(activeMember.length < 9)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dictionary member = new dictionary();
|
|
|
|
member.put("player", utils.stringToObjId(activeMember[0]));
|
|
|
|
// We do not need to put activeMember index 1 in this dictionary (calendar time)
|
|
|
|
member.put("name", activeMember[2]);
|
|
|
|
if(scriptVar.equals(BATTLEFIELD_ACTIVE_REBEL_PLAYERS))
|
|
{
|
|
member.put("faction", factions.FACTION_FLAG_REBEL);
|
|
}
|
|
else if(scriptVar.equals(BATTLEFIELD_ACTIVE_IMPERIAL_PLAYERS))
|
|
{
|
|
member.put("faction", factions.FACTION_FLAG_IMPERIAL);
|
|
}
|
|
|
|
member.put("kills", utils.stringToInt(activeMember[3]));
|
|
member.put("deaths", utils.stringToInt(activeMember[4]));
|
|
member.put("damage", utils.stringToInt(activeMember[5]));
|
|
member.put("healing", utils.stringToInt(activeMember[6]));
|
|
member.put("captures", utils.stringToInt(activeMember[7]));
|
|
member.put("assists", utils.stringToInt(activeMember[8]));
|
|
|
|
// Add this member to the rest of em...
|
|
utils.addElement(battlefieldTeams, member);
|
|
count++;
|
|
}
|
|
|
|
// Store the size of the team in the battlefield teams vector.
|
|
if(battlefieldTeams.length > 0)
|
|
{
|
|
battlefieldTeams[0].put(scriptVar, count);
|
|
}
|
|
}
|
|
|
|
return battlefieldTeams;
|
|
}
|
|
|
|
resizeable dictionary[] bfActiveGetStatistics(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Vector battlefieldTeams = new Vector();
|
|
|
|
battlefieldTeams = bfActiveAppendStatistics(controller, BATTLEFIELD_ACTIVE_REBEL_PLAYERS, battlefieldTeams);
|
|
battlefieldTeams = bfActiveAppendStatistics(controller, BATTLEFIELD_ACTIVE_IMPERIAL_PLAYERS, battlefieldTeams);
|
|
|
|
return battlefieldTeams;
|
|
}
|
|
|
|
string[][] bfStatisticsToArray(resizeable dictionary[] battlefieldPlayers, boolean colored)
|
|
{
|
|
if(battlefieldPlayers == null || battlefieldPlayers.length < 1)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string[][] scoreData = new string[battlefieldPlayers.length][8];
|
|
|
|
for(int i = 0, j = battlefieldPlayers.length; i < j; i++)
|
|
{
|
|
int faction = battlefieldPlayers[i].getInt("faction");
|
|
|
|
string color = COLOR_IMPERIALS;
|
|
string factionName = "Imperial";
|
|
|
|
if(faction == factions.FACTION_FLAG_REBEL)
|
|
{
|
|
color = COLOR_REBELS;
|
|
factionName = "Rebel";
|
|
}
|
|
|
|
if(!colored)
|
|
{
|
|
color = "";
|
|
}
|
|
|
|
scoreData[i][1] = color + factionName;
|
|
|
|
string name = battlefieldPlayers[i].getString("name");
|
|
|
|
if(name == null || name.length() < 1)
|
|
{
|
|
name = "Unknown";
|
|
}
|
|
|
|
int kills = battlefieldPlayers[i].getInt("kills");
|
|
int deaths = battlefieldPlayers[i].getInt("deaths");
|
|
int damage = battlefieldPlayers[i].getInt("damage");
|
|
int healing = battlefieldPlayers[i].getInt("healing");
|
|
int captures = battlefieldPlayers[i].getInt("captures");
|
|
int assists = battlefieldPlayers[i].getInt("assists");
|
|
|
|
scoreData[i][0] = color + name;
|
|
scoreData[i][2] = "" + kills;
|
|
scoreData[i][3] = "" + assists;
|
|
scoreData[i][4] = "" + deaths;
|
|
scoreData[i][5] = "" + damage;
|
|
scoreData[i][6] = "" + healing;
|
|
scoreData[i][7] = "" + captures;
|
|
}
|
|
|
|
return scoreData;
|
|
}
|
|
|
|
boolean bfIsValidAndEngaged(obj_id player)
|
|
{
|
|
// Is the attacker valid?
|
|
if(!isIdValid(player) || !exists(player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!utils.hasScriptVar(player, "battlefield.active"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id controller = utils.getObjIdScriptVar(player, "battlefield.active");
|
|
|
|
// If the controller is invalid or the controller does not exist, the player is not on a battlefield.
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int bfState = utils.getIntScriptVar(controller, "battlefield.state");
|
|
|
|
// We do not record battlefield data for phases outside of a running battle.
|
|
if(bfState != pvp.PVP_STATE_BATTLE_ENGAGED)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void bfCreditForDamage(obj_id attacker, int damage)
|
|
{
|
|
if(!bfIsValidAndEngaged(attacker))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalDamage = utils.getIntScriptVar(attacker, "battlefield.damage");
|
|
|
|
if(damage < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
totalDamage += damage;
|
|
|
|
utils.setScriptVar(attacker, "battlefield.damage", totalDamage);
|
|
}
|
|
|
|
void bfCreditForHealing(obj_id healer, int healed)
|
|
{
|
|
if(!bfIsValidAndEngaged(healer))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalhealed = utils.getIntScriptVar(healer, "battlefield.healing");
|
|
|
|
if(healed < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
totalhealed += healed;
|
|
|
|
utils.setScriptVar(healer, "battlefield.healing", totalhealed);
|
|
}
|
|
|
|
void bfCreditForKill(obj_id who)
|
|
{
|
|
if(!bfIsValidAndEngaged(who))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalKills = utils.getIntScriptVar(who, "battlefield.kills");
|
|
|
|
totalKills++;
|
|
|
|
utils.setScriptVar(who, "battlefield.kills", totalKills);
|
|
}
|
|
|
|
void bfCreditForDeath(obj_id who, obj_id killer)
|
|
{
|
|
if(!bfIsValidAndEngaged(who))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if((factions.isRebel(who) && factions.isImperial(killer)) || (factions.isImperial(who) && factions.isRebel(killer)))
|
|
{
|
|
int totalDeaths = utils.getIntScriptVar(who, "battlefield.deaths");
|
|
|
|
totalDeaths++;
|
|
|
|
utils.setScriptVar(who, "battlefield.deaths", totalDeaths);
|
|
}
|
|
|
|
// Is this player running a flag?
|
|
if(buff.hasBuff(who, "battlefield_communication_run"))
|
|
{
|
|
obj_id controller = utils.getObjIdScriptVar(who, "battlefield.active");
|
|
|
|
// If the controller is invalid or the controller does not exist, the player is not on a battlefield.
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
pvp.bfClearRunner(controller);
|
|
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("gcwCredits", 100);
|
|
|
|
// Announce the runner has been killed.
|
|
int faction = factions.getFactionFlag(who);
|
|
|
|
if(faction == factions.FACTION_FLAG_REBEL)
|
|
{
|
|
pvp.bfMessagePlayersOnBattlefield(controller, pvp.BATTLEFIELD_ACTIVE_IMPERIAL_PLAYERS, "receiveBattlefieldTerminalCapture", params);
|
|
pvp.bfActivePlayersAnnounce(controller, new string_id("spam", "battlefield_runner_killed_rebel"));
|
|
}
|
|
else if(faction == factions.FACTION_FLAG_IMPERIAL)
|
|
{
|
|
pvp.bfMessagePlayersOnBattlefield(controller, pvp.BATTLEFIELD_ACTIVE_REBEL_PLAYERS, "receiveBattlefieldTerminalCapture", params);
|
|
pvp.bfActivePlayersAnnounce(controller, new string_id("spam", "battlefield_runner_killed_imperial"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void bfCreditForAssist(obj_id who)
|
|
{
|
|
if(!bfIsValidAndEngaged(who))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalAssists = utils.getIntScriptVar(who, "battlefield.assists");
|
|
|
|
totalAssists++;
|
|
|
|
utils.setScriptVar(who, "battlefield.assists", totalAssists);
|
|
}
|
|
|
|
void bfCreditForCapture(obj_id who, int captureValue)
|
|
{
|
|
if(!bfIsValidAndEngaged(who))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalCaptures = utils.getIntScriptVar(who, "battlefield.captures");
|
|
|
|
if(captureValue < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
totalCaptures += captureValue;
|
|
|
|
utils.setScriptVar(who, "battlefield.captures", totalCaptures);
|
|
}
|
|
|
|
boolean bfTerminalIsRegistered(obj_id controller, obj_id terminal)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller) || !isIdValid(terminal) || !exists(terminal))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
resizeable obj_id[] terminals = new obj_id[0];
|
|
|
|
if(utils.hasResizeableObjIdBatchScriptVar(controller, "battlefield.terminals"))
|
|
{
|
|
terminals = utils.getResizeableObjIdBatchScriptVar(controller, "battlefield.terminals");
|
|
}
|
|
|
|
if(terminals == null || terminals.length < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for(int i = 0, j = terminals.length; i < j; i++)
|
|
{
|
|
if(!isIdValid(terminals[i]) || !exists(terminals[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(terminals[i] == terminal)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void bfTerminalRegister(obj_id controller, obj_id terminal)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!exists(controller))
|
|
{
|
|
if(isIdValid(terminal) && exists(terminal))
|
|
{
|
|
messageTo(terminal, "registerTerminal", null, 1.0f, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if(!isIdValid(terminal) || !exists(terminal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
resizeable obj_id[] terminals = new obj_id[0];
|
|
|
|
if(utils.hasResizeableObjIdBatchScriptVar(controller, "battlefield.terminals"))
|
|
{
|
|
terminals = utils.getResizeableObjIdBatchScriptVar(controller, "battlefield.terminals");
|
|
}
|
|
|
|
if(terminals == null || terminals.length < 1)
|
|
{
|
|
utils.addElement(terminals, terminal);
|
|
|
|
utils.setBatchScriptVar(controller, "battlefield.terminals", terminals);
|
|
|
|
return;
|
|
}
|
|
|
|
for(int i = 0, j = terminals.length; i < j; i++)
|
|
{
|
|
if(!isIdValid(terminals[i]) || !exists(terminals[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(terminals[i] == terminal)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
utils.addElement(terminals, terminal);
|
|
|
|
utils.setBatchScriptVar(controller, "battlefield.terminals", terminals);
|
|
}
|
|
|
|
resizeable obj_id[] bfTerminalsGetRegistered(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
resizeable obj_id[] terminals = new obj_id[0];
|
|
|
|
if(utils.hasResizeableObjIdBatchScriptVar(controller, "battlefield.terminals"))
|
|
{
|
|
terminals = utils.getResizeableObjIdBatchScriptVar(controller, "battlefield.terminals");
|
|
}
|
|
|
|
return terminals;
|
|
}
|
|
|
|
boolean bfTerminalsReset(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
resizeable obj_id[] terminals = new obj_id[0];
|
|
|
|
if(utils.hasResizeableObjIdBatchScriptVar(controller, "battlefield.terminals"))
|
|
{
|
|
terminals = utils.getResizeableObjIdBatchScriptVar(controller, "battlefield.terminals");
|
|
}
|
|
|
|
if(terminals == null || terminals.length < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int terminalCount = getIntObjVar(controller, "battlefield.terminalCount");
|
|
|
|
if(terminalCount != terminals.length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for(int i = 0, j = terminals.length; i < j; i++)
|
|
{
|
|
if(!isIdValid(terminals[i]) || !exists(terminals[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
utils.removeScriptVar(terminals[i], "battlefield.captured");
|
|
|
|
messageTo(terminals[i], "receiveBattlefieldReset", null, 1.0f, false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int bfGetBattlefieldType(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return BATTLEFIELD_TYPE_NONE;
|
|
}
|
|
|
|
return utils.getIntScriptVar(controller, "battlefieldType");
|
|
}
|
|
|
|
obj_id bfGetRunner(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return utils.getObjIdScriptVar(controller, "battlefield.runner");
|
|
}
|
|
|
|
void bfSetRunner(obj_id controller, obj_id runner)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller) || !isIdValid(runner) || !exists(runner))
|
|
{
|
|
return;
|
|
}
|
|
|
|
utils.setScriptVar(controller, "battlefield.runner", runner);
|
|
}
|
|
|
|
void bfClearRunner(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id runner = utils.getObjIdScriptVar(controller, "battlefield.runner");
|
|
|
|
if(isIdValid(runner) && exists(runner))
|
|
{
|
|
buff.removeBuff(runner, "battlefield_communication_run");
|
|
}
|
|
|
|
obj_id terminal = utils.getObjIdScriptVar(controller, "battlefield.runnerTerminal");
|
|
|
|
if(!isIdValid(terminal) || !exists(terminal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
dictionary dict = new dictionary();
|
|
|
|
dict.put("terminal_location", getWorldLocation(terminal));
|
|
dict.put("terminal", terminal);
|
|
|
|
pvp.bfMessagePlayersOnBattlefield(controller, pvp.BATTLEFIELD_ACTIVE_REBEL_PLAYERS, "destroyBattlefieldWaypoint", dict);
|
|
pvp.bfMessagePlayersOnBattlefield(controller, pvp.BATTLEFIELD_ACTIVE_IMPERIAL_PLAYERS, "destroyBattlefieldWaypoint", dict);
|
|
|
|
utils.removeScriptVar(controller, "battlefield.runner");
|
|
utils.removeScriptVar(controller, "battlefield.runnerTerminal");
|
|
}
|
|
|
|
boolean bfHasRunner(obj_id controller)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id runner = utils.getObjIdScriptVar(controller, "battlefield.runner");
|
|
|
|
if(isIdValid(runner) && exists(runner))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void bfActiveWarpPlayerToStart(obj_id controller, obj_id player)
|
|
{
|
|
if(!isIdValid(controller) || !exists(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int battlefieldState = utils.getIntScriptVar(controller, "battlefield.state");
|
|
location loc = null;
|
|
|
|
if(factions.isRebel(player))
|
|
{
|
|
loc = utils.getLocationScriptVar(controller, "battlefieldRebelSpawn");
|
|
}
|
|
else
|
|
{
|
|
if(factions.isImperial(player))
|
|
{
|
|
loc = utils.getLocationScriptVar(controller, "battlefieldImperialSpawn");
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("bfState", battlefieldState);
|
|
params.put("controller", controller);
|
|
|
|
if(loc != null)
|
|
{
|
|
params.put("warpLocation", loc);
|
|
messageTo(player, "receiveBattlefieldWarpLocation", params, 1.0f, false);
|
|
}
|
|
}
|
|
|
|
void bfLog(obj_id controller, string text)
|
|
{
|
|
if(!isIdValid(controller))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string battlefieldName = "error";
|
|
|
|
if(exists(controller))
|
|
{
|
|
battlefieldName = getStringObjVar(controller, "battlefieldName");
|
|
}
|
|
|
|
if(battlefieldName == null || battlefieldName.length() < 1)
|
|
{
|
|
battlefieldName = "error";
|
|
}
|
|
|
|
CustomerServiceLog("battlefield_" + battlefieldName, text);
|
|
}
|