Files
dsrc/sku.0/sys.server/compiled/game/script/library/pvp.scriptlib
T

2054 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);
}