Files
dsrc/sku.0/sys.server/compiled/game/script/library/arena.scriptlib
T
2013-09-10 23:17:15 -07:00

1604 lines
54 KiB
Plaintext

/***** INCLUDES ********************************************************/
include library.sui;
include library.utils;
include library.prose;
include library.pet_lib;
include library.factions;
include library.force_rank;
include library.trace;
include java.lang.Math;
/*
Challenge terminal data structure
{ // players who have issued challenges against a rank and the ranks they have challenged.
// also includes the timestamps at which the challenges were issued. this prevents someone issuing a challenge 3 seconds before the
// end of the challenge period causing the challenged rank to get bad scores because they didnt have enough time to accept
-> obj_id[] challengers
-> int[] ranksChallenged
-> int[] challengeTimes
}
---------------------------------------------------------------------------------
{ // all of the ranks and their negative score for not answering challenges
-> int[] allRanks
-> int[] rankChallengeScores
}
---------------------------------------------------------------------------------
{ // challenges that have been accepted and are in progress
-> obj_id[] acceptedChallengers
-> obj_id[] acceptedDefenders
}
// true if the arena is open for challenges, false if otherwise
boolean openForChallenges
// true if the player is in the middle of a challenge duel, false if not
boolean
---------------------------------------------------------------------------------
*/
const string VAR_ARENA_OPEN_FOR_CHALLENGES = "arena.isOpen";
const string VAR_CHALLENGE_DATA = "arena.challengeData";
const string VAR_ARENA_LAST_OPEN_TIME = "arena.lastChallengeStartTime";
const int CHALLENGE_PERIOD_LENGTH = 3600; // 60 minutes to answer a challenge
const int ARENA_OPEN_LENGTH = 5400; // arena open for 90 minutes for issuing challenges
const int ARENA_OPEN_EVERY = 108000; // every 30 hours
const string SCRIPT_VAR_SUI_CH_PID = "force_rank.challenge_sui";
const string SCRIPT_VAR_CH_TERMINAL = "force_rank.challengeTerminal";
const string SCRIPT_VAR_CH_VIEW_CHALLENGES = "force_rank.viewChallenges";
const string SCRIPT_VAR_CH_SELECT_ACCEPT = "force_rank.viewAcceptChallenges";
const string VAR_CH_RANKS_WHO_ANSWERED = "arena.challengeData.ranksThatAnswered"; // the ranks that answered during the current challenge schedule
const string VAR_CH_RANKS_GOT_CHALLENGED = "arena.challengeData.ranksThatGotChallenged"; // the ranks that got challenged during this period
//{ // people who have issued challenges, the ranks they are challenging and the times of their challenge
const string VAR_CHALLENGERS = "arena.challengeData.challengers";
const string VAR_CHALLENGED_RANKS = "arena.challengeData.ranksChallenged";
const string VAR_CHALLENGE_TIMES = "arena.challengeData.challengeTimes";
//}
//{
const string VAR_ALL_RANKS = "arena.allRanks";
const string VAR_RANK_CHALLENGE_SCORES = "arena.rankChallengeScores";
const int MISSED_CHALLENGE_PENALTY = -1;
const int MET_CHALLENGE_REWARD = 0; // set to zero to bring rankChallengeScore to zero when a challenge is met, set to any positive value to add that amount to the score
const int CHALLENGE_SCORE_CEILING = 0; // set to positive value if MET_CHALLENGE_REWARD is non-zero. This will indicate the max number of points that can be stored in the "challenge met" score bank
const int PENALTY_DEMOTE_THRESHOLD = -8; // must miss 25 challenges to demote entire rank.
const float PERCENT_MET_CHALLENGE = 0.50f;// the % of challenges that must be met for a challenge period was completed "satisfactory" for a given rank.
const boolean COUNT_NO_CHALLENGER_AS_PASSED = true; // true if we should consider a challenge period with no challengers as "satisfactory", and thus will apply the MET_CHALLENGE_REWARD
//}
//{ // people currently in a challenge fight and the rank they are fighting for
const string VAR_ACCEPTED_CHALLENGERS = "arena.challengeData.acceptedChallengers";
const string VAR_ACCEPTED_DEFENDERS = "arena.challengeData.acceptedDefenders";
const string VAR_ACCEPTED_CHALLENGE_RANKS = "arena.challengeData.acceptedChallengeRanks"; // the ranks for which challenger/defender are fighting
//}
//{
const string VAR_MY_ARENA_TERMINAL = "arena.terminalId";
const string VAR_MY_ARENA = "arena.arenaCellId";
//}
const string VAR_I_AM_ARENA = "arena.iAmArena"; // attached to the arena cell
const string VAR_LAST_CHALLENGE_TIME = "force_rank.lastRankChallengeTime"; // this is on the player
const int CHALLENGE_TIME_INTERVAL = 90000; // can issue challenge every 25 hours. defender also gets hit with this stamp if they lose a challenge
const string MAIL_FROM_ENCLAVE = "Hidden Enclave";
const string VAR_I_AM_DUELING = "force_rank.rankChallengeDuelInProgress"; // attached to the player
const string VAR_CHALLENGE_TERMINAL = "force_rank.challengeTerminal";
const float ARENA_X_MIN = -13.0f;
const float ARENA_X_MAX = 13.0f;
const float ARENA_Z_MIN = -86.0f;
const float ARENA_Z_MAX = -60.0f;
const float ARENA_Y = -47.42435f;
// accessor methods for times via config file
float getArenaTimeFactorFromConfig()
{
string strVal = getConfigSetting("GameServer", "darkArenaTimeFactor");
if(strVal == null || strVal == "")
{
return 1.0f;
}
float flVal = utils.stringToFloat(strVal);
if(flVal == Float.NEGATIVE_INFINITY)
{
return 1.0f;
}
return flVal;
}
int getChallengePeriodLength()
{
return (int)Math.ceil(((float)CHALLENGE_PERIOD_LENGTH) * getArenaTimeFactorFromConfig() );
}
int getArenaOpenLength()
{
return (int)Math.ceil(((float)ARENA_OPEN_LENGTH) * getArenaTimeFactorFromConfig() );
}
int getArenaOpenEvery()
{
return (int)Math.ceil(((float)ARENA_OPEN_EVERY) * getArenaTimeFactorFromConfig() );
}
int getChallengeTimeInterval()
{
return (int)Math.ceil(((float)CHALLENGE_TIME_INTERVAL) * getArenaTimeFactorFromConfig() );
}
//
// add a @rankA to the list of ranks that answered a challenge during the current voting period.
//
void addRankAnsweredChallenge(obj_id terminal, int rankA)
{
resizeable int[] ranksA = new int[0];
if(hasObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED))
{
utils.concatArrays(ranksA, utils.getIntBatchObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED));
}
utils.addElement(ranksA, rankA);
utils.setBatchObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED, ranksA.toArray());
return;
}
//
// add @rankC to the list of ranks that were challenged during this challenge period
//
void addRankGotChallenged(obj_id terminal, int rankC)
{
resizeable int[] ranksC = new int[0];
if(hasObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED))
{
utils.concatArrays(ranksC, utils.getIntBatchObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED));
}
utils.addElement(ranksC, rankC);
utils.setBatchObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED, ranksC.toArray());
return;
}
//
// returns an array of ranks that currently have active challenges listed
//
resizeable int[] getRanksWithActiveChallenges(obj_id terminal)
{
resizeable int[] ranks = new int[0];
if(!hasObjVar(terminal, VAR_CHALLENGED_RANKS) || !hasObjVar(terminal, VAR_ALL_RANKS))
{
return ranks;
}
int[] allRanks = getIntArrayObjVar(terminal, VAR_ALL_RANKS);
int[] challengedRanks = utils.getIntBatchObjVar(terminal, VAR_CHALLENGED_RANKS);
for(int i = 0; i < allRanks.length; i++)
{
if(utils.getElementPositionInArray(challengedRanks, allRanks[i]) > -1)
{
utils.addElement(ranks, allRanks[i]);
}
}
return ranks;
}
//
// gets a string array of ranks that that currently have active challanges
//
string[] getRanksWithActiveChallengesString(obj_id terminal)
{
resizeable int[] ranks = getRanksWithActiveChallenges(terminal);
string[] sranks = new string[ranks.length];
for(int i = 0; i < ranks.length; i++)
{
sranks[i] = "Rank " + ranks[i];
}
return sranks;
}
//
// adds or removes challenge score points from a rank depending on wether they answered a challenged during the current challenge period or not
//
void resolveRankChallengeResult(obj_id terminal, int rank, int timesChallenged, int timesAnswered)
{
if(!hasObjVar(terminal, VAR_RANK_CHALLENGE_SCORES) || !hasObjVar(terminal, VAR_ALL_RANKS))
{
LOG("force_rank", "arena::resolveRankChallengeResult: -> missing VAR_RANK_CHALLENGE_SCORES. Rank score not updated.");
return;
}
int[] ranks = getIntArrayObjVar(terminal, VAR_ALL_RANKS);
int[] score = getIntArrayObjVar(terminal, VAR_RANK_CHALLENGE_SCORES);
if(ranks.length != score.length)
{
trace.log("force_rank", "arena::resolveRankChallengeResult: -> ranks.length=" + ranks.length + " and score.length=" + score.length + ". Unacceptable. Not updating ranks challenge score.", null, trace.TL_ERROR_LOG | trace.TL_DEBUG);
}
int idx = utils.getElementPositionInArray(ranks, rank);
if(idx < 0)
{
trace.log("force_rank", "arena::resolveRankChallengeResult: -> Can't update rank " + rank + " challenge score as there is no record for this rank.", null, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
float rateAnswered = (float)timesAnswered / (float)timesChallenged;
if(rateAnswered >= PERCENT_MET_CHALLENGE || (COUNT_NO_CHALLENGER_AS_PASSED && timesChallenged == 0))
{
trace.log("force_rank", "Rank #" + rank + " met all requirements for this challenge period.", null, trace.TL_CS_LOG);
if(MET_CHALLENGE_REWARD == 0)
{
score[idx] = 0;
}
else
{
score[idx] += MET_CHALLENGE_REWARD;
if(score[idx] > CHALLENGE_SCORE_CEILING)
{
score[idx] = CHALLENGE_SCORE_CEILING;
}
}
}
else
{
score[idx] += MISSED_CHALLENGE_PENALTY;
trace.log("force_rank", "Rank #" + rank + " failed to meet the requirements for this challenge period. New rank challenge score is " + score[idx] + ".", null, trace.TL_CS_LOG);
}
if(score[idx] <= PENALTY_DEMOTE_THRESHOLD)
{
obj_id enclave = getTopMostContainer(terminal);
if(!hasScript(enclave, force_rank.SCRIPT_ENCLAVE_CONTROLLER))
{
trace.log("force_rank", "arena::resolveRankChallengeResult -- voting terminal " + terminal + " is not in an enclave. Not demoting rank " + rank, null, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
// Notify everyone in the ranks about the demotion
prose_package ppDemote = prose.getPackage(new string_id("pvp_rating", "ch_terminal_demote_rank_penalty"), rank);
Vector members = force_rank.getAllPlayerNamesInForceRank(enclave); // for notification
string_id subj = new string_id("pvp_rating", "ch_terminal_demote_subject");
for(int i = 0; i < members.size(); i++)
{
utils.sendMail(subj, ppDemote, (string)members.get(i), MAIL_FROM_ENCLAVE);
}
// Perform actual demotion
string[] demotees = force_rank.getPlayersInForceRank(enclave, rank);
trace.log("force_rank", "Initiating demotion for rank #" + rank + " (currently has " + demotees.length + " members): Rank challenge score too low.", null, trace.TL_CS_LOG);
for(int x = 0; x < demotees.length; x++)
{
trace.log("force_rank", "Arena initiating low-challenge-score demotion for player " + demotees[x], getPlayerIdFromFirstName(demotees[x]), trace.TL_CS_LOG);
force_rank.demoteForceRank(enclave, demotees[x], 0);
}
score[idx] = 0; // reset score to zero after demoting the rank
}
trace.log("force_rank", "Rank #" + rank + " now has a new challenge score of " + score[idx], null, trace.TL_CS_LOG);
setObjVar(terminal, VAR_ALL_RANKS, ranks);
setObjVar(terminal, VAR_RANK_CHALLENGE_SCORES, score);
return;
}
//
// returns a string of scores for every rank that can be challenged
//
string[] getChallengeScoreStrings(obj_id terminal)
{
if(!hasObjVar(terminal, VAR_ALL_RANKS))
{
trace.log("force_rank", "arena::getChallengeScoreStrings: -> Terminal doesnt have the necessary rank data setup!", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return new string[0];
}
int[] ranks = getIntArrayObjVar(terminal, VAR_ALL_RANKS);
int[] score = getIntArrayObjVar(terminal, VAR_RANK_CHALLENGE_SCORES);
string[] strings = new string[ranks.length];
for(int i = 0; i < ranks.length; i++)
{
strings[i] = "Score for rank " + ranks[i] + " --> " + score[i];
}
return strings;
}
//
// sets up some initial rank data
//
void setupRankScores(obj_id terminal)
{
if(!hasObjVar(terminal, VAR_RANK_CHALLENGE_SCORES))
{
int[] scores = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int[] ranks = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
setObjVar(terminal, VAR_RANK_CHALLENGE_SCORES, scores);
setObjVar(terminal, VAR_ALL_RANKS, ranks);
}
return;
}
//
// runs every time the arena loads up.
//
void initializeArena(obj_id terminal)
{
cleanupChallengeData(terminal);
setupRankScores(terminal);
utils.setScriptVar(terminal, "arenaTimeFactorUsed", getArenaTimeFactorFromConfig());
if(!hasObjVar(terminal, VAR_ARENA_LAST_OPEN_TIME))
{
// the arena has never been open for challenges. lets open it.
openArenaForChallenges(terminal);
}
else
{
closeArenaForChallenges(terminal);
}
obj_id enclave = getTopMostContainer(terminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "terminal_frs_voting.OnInitialize -- voting terminal " + terminal + " is not in an enclave.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
if(!hasScript(enclave, force_rank.SCRIPT_ENCLAVE_CONTROLLER))
{
trace.log("force_rank", "terminal_frs_voting.OnInitialize -- voting terminal " + terminal + " is not in an enclave.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
utils.setScriptVar(enclave, VAR_CHALLENGE_TERMINAL, terminal);
obj_id arenaCell = getContainedBy(terminal);
if(arenaCell != null)
{
utils.setScriptVar(arenaCell, VAR_MY_ARENA_TERMINAL, terminal);
utils.setScriptVar(terminal, VAR_MY_ARENA, arenaCell);
if(!hasScript(arenaCell, "systems.gcw.dark_jedi_arena"))
{
attachScript(arenaCell, "systems.gcw.dark_jedi_arena");
}
}
return;
}
//
// removes ALL challenge data. debugging use only
//
void cleanupChallengeData(obj_id terminal)
{
removeObjVar(terminal, VAR_CHALLENGE_DATA);
}
//
// executed every maintenance pulse. handles turning the arena on and off as well as
// culling out expired challenges
//
void checkArenaMaintenance(obj_id enclave)
{
if(!utils.hasScriptVar(enclave, VAR_CHALLENGE_TERMINAL))
{
trace.log("force_rank", "arena::checkArenaMaintenance: -> enclave missing challenge terminal scriptvar.", enclave, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
obj_id terminal = utils.getObjIdScriptVar(enclave, VAR_CHALLENGE_TERMINAL);
if(!isIdValid(terminal))
{
trace.log("force_rank", "arena::checkArenaMaintenance: -> terminal is not valid. maintenance not done.", enclave, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
if(!hasObjVar(terminal, VAR_ARENA_LAST_OPEN_TIME))
{
return;
}
int lastOpen = getIntObjVar(terminal, VAR_ARENA_LAST_OPEN_TIME);
int now = getGameTime();
boolean closedArenaThisCycle = false;
if(isArenaOpenForChallenges(terminal))
{ // time to close?
if(lastOpen + getArenaOpenLength() <= now)
{
closeArenaForChallenges(terminal);
closedArenaThisCycle = true;
}
}
else
{ // time to open?
if(lastOpen + getArenaOpenLength() + getArenaOpenEvery() <= now)
{
openArenaForChallenges(terminal);
}
}
// cull out challenges that have expired. if the last challenge has been removed, time to dish out points to the ranks.
resizeable obj_id[] challengers = new obj_id[0];
resizeable int[] ranks = new int[0];
resizeable int[] timeStamps = new int[0];
resizeable obj_id[] acceptedChallengers = new obj_id[0];
if(hasObjVar(terminal, VAR_ACCEPTED_CHALLENGERS))
{
utils.concatArrays(acceptedChallengers, utils.getObjIdBatchObjVar(terminal, VAR_ACCEPTED_CHALLENGERS));
}
if(hasObjVar(terminal, VAR_CHALLENGERS))
{
challengers.addAll(utils.getResizeableObjIdBatchObjVar(terminal, VAR_CHALLENGERS));
}
if(hasObjVar(terminal, VAR_CHALLENGED_RANKS))
{
ranks.addAll(utils.getResizeableIntBatchObjVar(terminal, VAR_CHALLENGED_RANKS));
}
if(hasObjVar(terminal, VAR_CHALLENGE_TIMES))
{
timeStamps.addAll(utils.getResizeableIntBatchObjVar(terminal, VAR_CHALLENGE_TIMES));
}
// ensure data has the same # of elements
int len = challengers.length;
if(ranks.length != len || timeStamps.length != len)
{
trace.log("force_rank", "arena::checkArenaMaintenance: -> Existing data on challengeTerminal is out of synch: challengers.length=" + challengers.length +
", ranks.length=" + ranks.length + ", timeStamps.length=" + timeStamps.length + ".", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
resizeable string[] players = new string[0];
int timePassed = 0;
int timeRemaining = 0;
string timeLeft = "";
int i = 0;
boolean removedAChallenger = false;
while(i < challengers.length)
{
timeRemaining = (timeStamps[i] + getChallengePeriodLength()) - now;
if(timeRemaining < 0)
{
trace.log("force_rank", "Dark arena challenge issued by %TU against rank #" + ranks[i] + " has timed out.", challengers[i], trace.TL_CS_LOG);
notifyChallengeTimedOut(terminal, challengers[i], ranks[i]);
utils.removeElementAt(challengers, i);
utils.removeElementAt(ranks, i);
utils.removeElementAt(timeStamps, i);
removedAChallenger = true;
}
else
{
i++;
}
}
trace.log("force_rank", "arena::checkArenaMaintenange-> challengers.length=" + challengers.length + ", acceptedChallengers.length=" + acceptedChallengers.length , null, trace.TL_DEBUG);
if(challengers.length > 0)
{
utils.setBatchObjVar(terminal, VAR_CHALLENGERS, challengers.toArray());
utils.setBatchObjVar(terminal, VAR_CHALLENGED_RANKS, ranks.toArray());
utils.setBatchObjVar(terminal, VAR_CHALLENGE_TIMES, timeStamps.toArray());
}
else
{ // every challenge either answered or timed out
removeObjVar(terminal, VAR_CHALLENGERS);
removeObjVar(terminal, VAR_CHALLENGED_RANKS);
removeObjVar(terminal, VAR_CHALLENGE_TIMES);
// if we closed the arena this maintenance cycle AND we have no challengers pending AND there are no current fights going on
if(
(!isArenaOpenForChallenges(terminal) && acceptedChallengers.length < 1 /* && challengers.length < 1 - implied */) &&
(closedArenaThisCycle || removedAChallenger)
)
{
//trace.log("force_rank", "**AM** dishing out challenge points");
dishOutChallengeAnswerPointsToRanks(terminal);
}
else
{
//trace.log("force_rank", "**NOT** dishout out challenge points: closedArenaThisCycle=" + closedArenaThisCycle + ", removedAChallenger="+removedAChallenger);
}
}
return;
}
//
// runs at the end of a challenge period and handles giving out challenge score points based on wether or not
// the rank answered a challenge during the current challenge period
//
void dishOutChallengeAnswerPointsToRanks(obj_id terminal)
{
if(!hasObjVar(terminal, VAR_ALL_RANKS))
{
trace.log("force_rank", "arena::dishOutChallengeAnswerPointsToRanks: -> missing VAR_ALL_RANKS. Bailing.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
removeObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED); // just in case.
removeObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED); // just in case.
return;
}
int[] challenged = utils.getIntBatchObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED);
int[] answered = utils.getIntBatchObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED);
int[] allRanks = getIntArrayObjVar(terminal, VAR_ALL_RANKS);
int rankChallenges = 0;
int rankChallengesAccepted = 0;
for(int i = 0; i < allRanks.length; i++)
{
if(challenged != null)
{
for(int x = 0; x < challenged.length; x++)
{
if(challenged[x] == allRanks[i])
{
rankChallenges++;
}
}
}
if(answered != null)
{
for(int y = 0; y < answered.length; y++)
{
if(answered[y] == allRanks[i])
{
rankChallengesAccepted++;
}
}
}
trace.log("force_rank", "Rank #" + allRanks[i] + " was challenged " + rankChallenges + " times and answered " + rankChallengesAccepted + " times.", null, trace.TL_CS_LOG);
resolveRankChallengeResult(terminal, allRanks[i], rankChallenges, rankChallengesAccepted);
rankChallenges = 0;
rankChallengesAccepted = 0;
}
removeObjVar(terminal, VAR_CH_RANKS_GOT_CHALLENGED);
removeObjVar(terminal, VAR_CH_RANKS_WHO_ANSWERED);
return;
}
//
// makes it so the arena will allow issuing of challenges
//
void openArenaForChallenges(obj_id terminal)
{
trace.log("force_rank", "Opening arena for challenges (GameTime: " + getGameTime() + ").", null, trace.TL_CS_LOG | trace.TL_DEBUG);
setObjVar(terminal, VAR_ARENA_OPEN_FOR_CHALLENGES, true);
setObjVar(terminal, VAR_ARENA_LAST_OPEN_TIME, getGameTime());
return;
}
//
// prevents the terminal from accepting challenge issues
//
void closeArenaForChallenges(obj_id terminal)
{
trace.log("force_rank", "Closing arena for challenges (GameTime: " + getGameTime() + ").", null, trace.TL_CS_LOG | trace.TL_DEBUG);
setObjVar(terminal, VAR_ARENA_OPEN_FOR_CHALLENGES, false);
return;
}
//
// checks to see if the arena can accept challenges
//
boolean isArenaOpenForChallenges(obj_id terminal)
{
if(!hasObjVar(terminal, VAR_ARENA_OPEN_FOR_CHALLENGES))
{
return false;
}
return getBooleanObjVar(terminal, VAR_ARENA_OPEN_FOR_CHALLENGES);
}
//
// returns the arena cell in which the terminal lives
//
obj_id getMyArena(obj_id terminal)
{
if(!hasObjVar(terminal, VAR_MY_ARENA))
{
return null;
}
return getObjIdObjVar(terminal, VAR_MY_ARENA);
}
//
// send messages to involved people as a result of a new challenge issued
//
void notifyChallengeIssued(obj_id terminal, obj_id challenger, int rankChallenged)
{
obj_id enclave = getTopMostContainer(terminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::notifyChallengeIssued: -> enclave is not valid. Not notifying anyone of issued challenge.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
// message the challenger
prose_package ppChallenger = prose.getPackage(new string_id("pvp_rating", "challenge_issued_challenger"), rankChallenged);
sendSystemMessageProse(challenger, ppChallenger);
// message the rank
obj_id[] rankMembers = force_rank.getAllPlayersInForceRank(enclave, rankChallenged);
prose_package ppRank = new prose_package();
ppRank.target.set(utils.getRealPlayerFirstName(challenger));
ppRank.digitInteger = rankChallenged;
ppRank.stringId = new string_id("pvp_rating", "challenge_issued_rank");
//prose.getPackage(new string_id("pvp_rating", "challenge_issued_rank"), challenger, rankChallenged);
string_id msgSubject = new string_id("pvp_rating", "challenge_issued_subject_header");
for(int i = 0; i < rankMembers.length; i++)
{
sendSystemMessageProse(rankMembers[i], ppRank);
utils.sendMail(msgSubject, ppRank, utils.getRealPlayerFirstName(rankMembers[i]), MAIL_FROM_ENCLAVE);
}
return;
}
//
// send messages to involved people as a result of an accepted challenge
//
void notifyChallengeAccepted(obj_id terminal, obj_id challenger, obj_id defender, int rankFightingFor)
{
obj_id enclave = getTopMostContainer(terminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::notifyChallengeAccepted: -> enclave is not valid. Not notifying anyone of accepted challenge.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
// message the challenger
prose_package ppAll = new prose_package();//
ppAll.actor.set(utils.getRealPlayerFirstName(defender));
ppAll.target.set(utils.getRealPlayerFirstName(challenger));
ppAll.digitInteger = rankFightingFor;
ppAll.stringId = new string_id("pvp_rating", "challenge_accepted");
//prose.getPackage(new string_id("pvp_rating", "challenge_accepted"), defender, challenger, rankFightingFor);
sendSystemMessageProse(challenger, ppAll);
// message the rank
obj_id[] rankMembers = force_rank.getAllPlayersInForceRank(enclave, rankFightingFor);
string_id msgSubject = new string_id("pvp_rating", "challenge_accepted_subject_header");
for(int i = 0; i < rankMembers.length; i++)
{
sendSystemMessageProse(rankMembers[i], ppAll);
utils.sendMail(msgSubject, ppAll, utils.getRealPlayerFirstName(rankMembers[i]), MAIL_FROM_ENCLAVE);
}
return;
}
//
// send messages to involved people as a result of a concluded challenge
//
void notifyChallengeConcluded(obj_id terminal, obj_id challenger, obj_id defender, boolean challengerWon, int rankFightingFor)
{
obj_id enclave = getTopMostContainer(terminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::notifyChallengeConcluded: -> enclave is not valid. Not notifying anyone of accepted challenge.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
// message the challenger
string whichString = "challenge_concluded_challenger_won";
if(!challengerWon)
{
whichString = "challenge_concluded_defender_win";
}
prose_package ppAll = new prose_package();
ppAll.actor.set(utils.getRealPlayerFirstName(defender));
ppAll.target.set(utils.getRealPlayerFirstName(challenger));
ppAll.digitInteger = rankFightingFor;
ppAll.stringId = new string_id("pvp_rating", whichString);
//prose.getPackage(new string_id("pvp_rating", whichString), defender, challenger, rankFightingFor);
string_id msgSubject = new string_id("pvp_rating", "challenge_concluded_subject_header");
sendSystemMessageProse(challenger, ppAll);
utils.sendMail(msgSubject, ppAll, utils.getRealPlayerFirstName(challenger), MAIL_FROM_ENCLAVE);
// message the rank
obj_id[] rankMembers = force_rank.getAllPlayersInForceRank(enclave, rankFightingFor);
for(int i = 0; i < rankMembers.length; i++)
{
sendSystemMessageProse(rankMembers[i], ppAll);
utils.sendMail(msgSubject, ppAll, utils.getRealPlayerFirstName(rankMembers[i]), MAIL_FROM_ENCLAVE);
}
return;
}
//
// send messages to involved people as a result of a timed out challenge
//
void notifyChallengeTimedOut(obj_id terminal, obj_id challenger, int rankChallenged)
{
obj_id enclave = getTopMostContainer(terminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::notifyChallengeTimedOut: -> enclave is not valid. Not notifying anyone of timed out challenge.", terminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
// message the challenger
prose_package ppAll = new prose_package();
ppAll.target.set(utils.getRealPlayerFirstName(challenger));
ppAll.digitInteger = rankChallenged;
ppAll.stringId = new string_id("pvp_rating", "challenge_concluded_timeout");
//prose.getPackage(new string_id("pvp_rating", "challenge_concluded_timeout"), challenger, rankChallenged);
sendSystemMessageProse(challenger, ppAll);
// message the rank
obj_id[] rankMembers = force_rank.getAllPlayersInForceRank(enclave, rankChallenged);
string_id msgSubject = new string_id("pvp_rating", "challenge_timout_subject_header");
for(int i = 0; i < rankMembers.length; i++)
{
sendSystemMessageProse(rankMembers[i], ppAll);
utils.sendMail(msgSubject, ppAll, utils.getRealPlayerFirstName(rankMembers[i]), MAIL_FROM_ENCLAVE);
}
return;
}
//
// returns all the obj_ids of persons who have a current challenge against @rank
//
resizeable obj_id[] getChallengerIdsForRank(obj_id challengeTerminal, int rank)
{
obj_id[] challengers = new obj_id[0];
int[] ranks = new int[0];
resizeable obj_id[] players = new obj_id[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
challengers = utils.getObjIdBatchObjVar(challengeTerminal, VAR_CHALLENGERS);
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGED_RANKS))
{
ranks = utils.getIntBatchObjVar(challengeTerminal, VAR_CHALLENGED_RANKS);
}
// ensure data has the same # of elements
if(ranks.length != challengers.length)
{
trace.log("force_rank", "arena::getChallengerIdsForRank: -> Existing data on challengeTerminal is out of synch: challengers.length=" + challengers.length +
", ranks.length=" + ranks.length + ".", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return players;
}
for(int i = 0; i < challengers.length; i ++)
{
if(ranks[i] == rank)
{
utils.addElement(players, challengers[i]);
}
}
return players;
}
//
// returns the names of all challengers that currently have an active challenge against @rank
//
string[] getChallengerNamesForRank(obj_id challengeTerminal, int rank)
{
resizeable obj_id[] players = getChallengerIdsForRank(challengeTerminal, rank);
return utils.makeNameList(players);
}
//
// gets an array of strings that contains the names of all current challengers against @rank as well as the amount of time remaining on each of those
// challenges
//
string[] getChallengerNamesWithTimeRemaining(obj_id challengeTerminal, int rank)
{
obj_id[] challengers = new obj_id[0];
int[] ranks = new int[0];
int[] timeStamps = new int[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
challengers = utils.getObjIdBatchObjVar(challengeTerminal, VAR_CHALLENGERS);
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGED_RANKS))
{
ranks = utils.getIntBatchObjVar(challengeTerminal, VAR_CHALLENGED_RANKS);
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGE_TIMES))
{
timeStamps = utils.getIntBatchObjVar(challengeTerminal, VAR_CHALLENGE_TIMES);
}
// ensure data has the same # of elements
int len = challengers.length;
if(ranks.length != len || timeStamps.length != len)
{
trace.log("force_rank", "arena::getChallengerNamesWithTimeRemaining: -> Existing data on challengeTerminal is out of synch: challengers.length=" + challengers.length +
", ranks.length=" + ranks.length + ", timeStamps.length=" + timeStamps.length + ".", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return new string[0];
}
resizeable string[] players = new string[0];
int now = getGameTime();
int timePassed = 0;
int timeRemaining = 0;
string timeLeft = "";
for(int i = 0; i < challengers.length; i ++)
{
if(ranks[i] == rank && isIdValid(challengers[i]))
{
timeRemaining = (timeStamps[i] + getChallengePeriodLength()) - now;
if(timeRemaining < 0)
{
timeLeft = " ( challenge expiring shortly )";
}
else if(timeRemaining < 60)
{
timeLeft = " ( less than one minute remaining on challenge )";
}
else
{
timeLeft = " ( " + timeRemaining / 60 + " minutes remaining on this challenge )";
}
utils.addElement(players, utils.getRealPlayerFirstName(challengers[i]) + timeLeft);
}
}
string[] arrayNames = utils.toStaticStringArray(players);
return arrayNames;
}
//
// writes challenge issued data to the terminal
//
boolean addChallengeIssueData(obj_id challengeTerminal, obj_id challenger, int rank)
{
obj_id enclave = getTopMostContainer(challengeTerminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::addChallengeIssueData: -> enclave is not valid. Not adding challenge issued data.", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return false;
}
if(hasChallengeIssuedCurrently(challenger, challengeTerminal))
{
trace.log("force_rank", "arena::addChallengeIssuData: -> " + utils.getRealPlayerFirstName(challenger) + " already has a challenge issued. Not adding another one.", challengeTerminal, trace.TL_WARNING | trace.TL_DEBUG);
return false;
}
resizeable obj_id[] challengers = new obj_id[0];
resizeable int[] ranks = new int[0];
resizeable int[] timeStamps = new int[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
utils.concatArrays(challengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGED_RANKS))
{
utils.concatArrays(ranks, utils.getIntBatchObjVar(challengeTerminal, VAR_CHALLENGED_RANKS));
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGE_TIMES))
{
utils.concatArrays(timeStamps, utils.getIntBatchObjVar(challengeTerminal, VAR_CHALLENGE_TIMES));
}
// ensure data has the same # of elements
int len = challengers.length;
if(ranks.length != len || timeStamps.length != len)
{
trace.log("force_rank", "arena::addChallengeIssueData: -> Existing data on challengeTerminal is out of synch: challengers.length=" + challengers.length +
", ranks.length=" + ranks.length + ", timeStamps.length=" + timeStamps.length + ". Not adding challenge data for " + utils.getRealPlayerFirstName(challenger),
challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return false;
}
int challengerRank = force_rank.getForceRank(enclave, getFirstName(challenger));
// check the ranks, just in case
if(challengerRank+1 != rank)
{
trace.log("force_rank", "arena::addChallengeIssueData: -> cannot allow " + getFirstName(challenger) + "(rank " + challengerRank + ") to challenge rank # " + getFirstName(challenger) + " (rank " + challengerRank + "), as they are attempting to challenge " + rank + ".",
challenger, trace.TL_WARNING | trace.TL_DEBUG);
return false;
}
utils.addElement(challengers, challenger);
utils.addElement(ranks, rank);
utils.addElement(timeStamps, getGameTime());
utils.setBatchObjVar(challengeTerminal, VAR_CHALLENGERS, challengers.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_CHALLENGED_RANKS, ranks.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_CHALLENGE_TIMES, timeStamps.toArray());
return true;
}
//
// writes challenge accepted data to the terminal and removes the corresponding challenge issued data
//
boolean addChallengeAcceptedData(obj_id challengeTerminal, obj_id challenger, obj_id defender)
{
obj_id enclave = getTopMostContainer(challengeTerminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::addChallengeAcceptedData: -> enclave is not valid. Not adding challenge accepted data.", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return false;
}
resizeable obj_id[] acceptedChallengers = new obj_id[0];
resizeable obj_id[] acceptedDefenders = new obj_id[0];
resizeable int[] acceptedChllngRanks = new int[0];
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS))
{
utils.concatArrays(acceptedChallengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS))
{
utils.concatArrays(acceptedDefenders, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS))
{
utils.concatArrays(acceptedChllngRanks, utils.getIntBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS));
}
if(acceptedChallengers.length != acceptedDefenders.length)
{
trace.log("force_rank", "acceptedChallenge data out of synch on challengeTerminal: acceptedChallengers.length=" + acceptedChallengers.length + ", acceptedDefenders.length=" + acceptedDefenders.length, null, trace.TL_DEBUG | trace.TL_CS_LOG);
return false;
}
// forgo this sanity check if the challenger doesnt exist on the game server, because we have no good way to verify their rank.
int defenderRank = force_rank.getForceRank(enclave, getFirstName(defender));
if(exists(challenger))
{
int challengerRank = force_rank.getForceRank(enclave, getRealPlayerFirstNameAuth(challenger)); // challenger could be dead
// check the ranks, just in case
if(challengerRank+1 != defenderRank)
{
trace.log("force_rank", "Cannot allow %TU (rank " + defenderRank + ") to accept challenge from " + utils.getRealPlayerFirstName(challenger) + " (rank " + challengerRank + "), due to a rank number descrepancy.", defender, trace.TL_CS_LOG | trace.TL_DEBUG);
return false;
}
}
utils.addElement(acceptedChallengers, challenger);
utils.addElement(acceptedDefenders, defender);
utils.addElement(acceptedChllngRanks, defenderRank);
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS, acceptedChallengers.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS, acceptedDefenders.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS, acceptedChllngRanks.toArray());
return true;
}
//
// attempts to issue a challenge against a rank.
//
boolean issueChallengeAgainstRank(obj_id challengeTerminal, obj_id player, int rank)
{
if(addChallengeIssueData(challengeTerminal, player, rank))
{
addRankGotChallenged(challengeTerminal, rank);
notifyChallengeIssued(challengeTerminal, player, rank);
utils.setObjVar(player, VAR_LAST_CHALLENGE_TIME, getGameTime()); // defender only gets hit with this stamp if they lose the battle
trace.log("force_rank", "Dark arena challenge issued by %TU against rank #" + rank + ".", player, trace.TL_CS_LOG | trace.TL_DEBUG);
return true;
}
return false;
}
//
// checks to see if a player currently has a challange issued.
//
boolean hasChallengeIssuedCurrently(obj_id player, obj_id challengeTerminal)
{
resizeable obj_id[] challengers = new obj_id[0];
resizeable obj_id[] acceptedChallengers = new obj_id[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
utils.concatArrays(challengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS))
{
utils.concatArrays(acceptedChallengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS));
}
return ((utils.getElementPositionInArray(challengers, player) > -1) || (utils.getElementPositionInArray(acceptedChallengers, player) > -1));
}
//
// checks various data to see if a player is eligible to issue a challenge
//
boolean canPlayerIssueChallenge(obj_id player, obj_id challengeTerminal)
{
if(utils.hasScriptVar(player, arena.VAR_I_AM_DUELING))
{
trace.log("force_rank", "arena::canPlayerIssueChallenge: -> False : Currently dueling.", player, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return false;
}
if(hasChallengeIssuedCurrently(player, challengeTerminal) )
{
return false;
}
if(!hasObjVar(player, VAR_LAST_CHALLENGE_TIME))
{
return true;
}
int lastChallengeTime = getIntObjVar(player, VAR_LAST_CHALLENGE_TIME);
int now = getGameTime();
if((now - lastChallengeTime) >= getChallengeTimeInterval())
{
return true;
}
return false;
}
//
// checks to see if the challenger is present in the arena
//
boolean isPlayerPresentInArena(obj_id arenaCell, obj_id player)
{
if(!isIdValid(player) || (isDead(player)) || (!player.isAuthoritative()))
{
return false;
}
location theloc = getLocation(player);
if(theloc.cell == arenaCell && theloc.x > ARENA_X_MIN && theloc.x < ARENA_X_MAX && theloc.z > ARENA_Z_MIN && theloc.z < ARENA_Z_MAX)
{
return true;
}
return false;
}
//
// iniitates a duel as a result of accepting a challange.
//
boolean acceptChallenge(obj_id challengeTerminal, obj_id defender, obj_id challenger)
{
obj_id enclave = getTopMostContainer(challengeTerminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::acceptChallenge: -> enclave is not valid. Not accepting challenge.", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return false;
}
if(addChallengeAcceptedData(challengeTerminal, challenger, defender))
{
int rank = force_rank.getForceRank(enclave, getFirstName(defender));
addRankAnsweredChallenge(challengeTerminal, rank);
notifyChallengeAccepted(challengeTerminal, challenger, defender, rank);
trace.log("force_rank", "Defender %TU has accepted a challenge for the honor of rank " + rank + ". Starting duel.", defender, trace.TL_CS_LOG | trace.TL_DEBUG);
beginChallengeDuel(challengeTerminal, challenger, defender);
return true;
}
else
{
trace.log("force_rank", "Error accepting challenge with defender %TU due to a data problem. Challenge not accepted.", defender, trace.TL_CS_LOG | trace.TL_DEBUG);
}
return false;
}
//
// performs actions as a result of a defender losing a challenge, namely XP exchange as well as demotion
//
void doDefenderLostPenalties(obj_id defender, obj_id challenger, obj_id enclave, boolean forfeit)
{
trace.log("force_rank", "Defender %TU lost arena duel to " + utils.getRealPlayerFirstName(challenger) + "(" + challenger + ")", defender, trace.TL_CS_LOG | trace.TL_DEBUG);
if(!forfeit)
{
force_rank.adjustForceRankXP(defender, pvp.getAdjustedForceRankXPDelta(defender, challenger, 1.0f, true));
force_rank.adjustForceRankXP(challenger, pvp.getAdjustedForceRankXPDelta(defender, challenger, 1.0f, false));
}
setObjVar(defender, VAR_LAST_CHALLENGE_TIME, getGameTime()); // this is so they can't immediately challenge back
int defenderRank = force_rank.getForceRank(enclave, getRealPlayerFirstNameAuth(defender));
force_rank.demoteForceRank(enclave, getRealPlayerFirstNameAuth(defender), defenderRank-1);
force_rank.promoteForceRank(challenger);
return;
}
//
// performs actions as a result of a challenge losing a challenge
//
void doChallengerLostPenalties(obj_id defender, obj_id challenger, obj_id enclave, boolean forfeit)
{
trace.log("force_rank", "Challenger %TU lost arena duel to " + utils.getRealPlayerFirstName(defender) + "(" + defender+ ")", challenger, trace.TL_CS_LOG | trace.TL_DEBUG);
// only XP exchanges
if(!forfeit)
{
force_rank.adjustForceRankXP(challenger, pvp.getAdjustedForceRankXPDelta(challenger, defender, 1.0f, true));
force_rank.adjustForceRankXP(defender, pvp.getAdjustedForceRankXPDelta(challenger, defender, 1.0f, false));
}
return;
}
//
// called when someone in a challenge duel dies
//
void duelistDied(obj_id player, obj_id opponent, obj_id enclave, boolean forfeit)
{
utils.removeScriptVar(player, "noBeneficialJediHelp");
utils.removeScriptVar(opponent, "noBeneficialJediHelp");
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::duelistDied: -> enclave is not valid. Not adding challenge accepted data.", null, trace.TL_DEBUG);
return;
}
if(!hasScript(enclave, force_rank.SCRIPT_ENCLAVE_CONTROLLER))
{
trace.log("force_rank", "arena::duelistDied-- " + enclave + " is not an enclave building.", null, trace.TL_DEBUG);
return;
}
obj_id challengeTerminal = null;
if(utils.hasScriptVar(enclave, VAR_CHALLENGE_TERMINAL))
{
challengeTerminal = utils.getObjIdScriptVar(enclave, VAR_CHALLENGE_TERMINAL);
}
if(!isIdValid(challengeTerminal))
{
trace.log("force_rank", "arena::duelistDied: -> unable to read challenge terminal objvar from enclave.", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
resizeable obj_id[] acceptedChallengers = new obj_id[0];
resizeable obj_id[] acceptedDefenders = new obj_id[0];
resizeable int[] acceptedChllngRanks = new int[0];
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS))
{
utils.concatArrays(acceptedChallengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS))
{
utils.concatArrays(acceptedDefenders, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS))
{
utils.concatArrays(acceptedChllngRanks, utils.getIntBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS));
}
if(acceptedChallengers.length != acceptedDefenders.length)
{
trace.log("force_rank", "arena::duelistDied: -> acceptedChallenge data out of synch on challengeTerminal: acceptedChallengers.length=" +
acceptedChallengers.length + ", acceptedDefenders.length=" + acceptedDefenders.length, challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return;
}
boolean isChallenger = false;
boolean foundMatch = false;
int idx = utils.getElementPositionInArray(acceptedChallengers, player);
if(idx >= 0 && acceptedDefenders[idx] == opponent)
{
foundMatch = true;
isChallenger = true;
}
else
{ // maybe a defender?
idx = utils.getElementPositionInArray(acceptedDefenders, player);
if(idx >= 0 && acceptedChallengers[idx] == opponent)
{
foundMatch = true;
isChallenger = false;
}
}
if(!foundMatch)
{
trace.log("force_rank", "arena::duelistDied: -> No duelist match found in data for player %TU. Shouldn't really ever happen. This is a problem.", player, trace.TL_ERROR_LOG | trace.TL_DEBUG | trace.TL_CS_LOG);
return;
}
int rankChallenged = acceptedChllngRanks[idx];
trace.log("force_rank", "arena::duelistDied: -> " + utils.getRealPlayerFirstName(player) + " was defeated " + utils.getRealPlayerFirstName(opponent), null, trace.TL_CS_LOG | trace.TL_DEBUG);
utils.removeScriptVar(player, VAR_I_AM_DUELING);
utils.removeScriptVar(opponent, VAR_I_AM_DUELING);
utils.removeElementAt(acceptedChallengers, idx);
utils.removeElementAt(acceptedDefenders, idx);
utils.removeElementAt(acceptedChllngRanks, idx);
if(acceptedChallengers.length > 0)
{
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS, acceptedChallengers.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS, acceptedDefenders.toArray());
utils.setBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS, acceptedChllngRanks.toArray());
}
else
{
removeObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS);
removeObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS);
removeObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGE_RANKS);
// if the arena is closed, it may have been waiting on this duel to conclude to dishout the points for this challenge period
// if the arena is closed and we have no more challengers on the board, then we can dishout the points
if(!isArenaOpenForChallenges(challengeTerminal))
{
resizeable obj_id[] challengers = new obj_id[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
challengers.addAll(utils.getResizeableObjIdBatchObjVar(challengeTerminal, VAR_CHALLENGERS));
if(challengers.length < 1)
{
// we have no pending challenges, the arena is closed and we just concluded the last active duel.
dishOutChallengeAnswerPointsToRanks(challengeTerminal);
}
}
}
}
if(isChallenger)
{
doChallengerLostPenalties(opponent, player, enclave, forfeit);
notifyChallengeConcluded(challengeTerminal, player, opponent, false, rankChallenged);
}
else
{
doDefenderLostPenalties(player, opponent, enclave, forfeit);
notifyChallengeConcluded(challengeTerminal, opponent, player, true, rankChallenged);
}
return;
}
//
// called when a player leaves the arena during a duel. currently this is the same as dying
//
void leftArenaDuringDuel(obj_id player)
{
if(!utils.hasScriptVar(player, arena.VAR_I_AM_DUELING))
{
trace.log("force_rank", "Arena error: player_force_rank::handlePlayerDeath: -> " + player + " is missing arena.VAR_I_AM_DUELING", null, trace.TL_CS_LOG | trace.TL_DEBUG);
return;
}
obj_id enclave = force_rank.getEnclave(player);
trace.log("force_rank", "Player %TU has somehow left the arena during a duel. Counting abandonment as death.", player, trace.TL_CS_LOG | trace.TL_DEBUG);
duelistDied(player, utils.getObjIdScriptVar(player, arena.VAR_I_AM_DUELING), enclave, true);
return;
}
//
// Checks to see if either duelist is currently in a duel. if not the method
// @returns null, otherwise it @returns the obj id of the first person found
// in a duel
//
obj_id isEitherDuelistInADuel(obj_id challengeTerminal, obj_id duelist1, obj_id duelist2)
{
resizeable obj_id[] acceptedChallengers = new obj_id[0];
resizeable obj_id[] acceptedDefenders = new obj_id[0];
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS))
{
utils.concatArrays(acceptedChallengers, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS))
{
utils.concatArrays(acceptedDefenders, utils.getObjIdBatchObjVar(challengeTerminal, VAR_ACCEPTED_DEFENDERS));
}
if(acceptedChallengers.length != acceptedDefenders.length)
{
trace.log("force_rank", "arena::isEitherDuelistInADuel: -> acceptedChallenge data out of synch on challengeTerminal: acceptedChallengers.length=" +
acceptedChallengers.length + ", acceptedDefenders.length=" + acceptedDefenders.length, challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return null;
}
if(utils.getElementPositionInArray(acceptedChallengers, duelist1) > -1 || utils.getElementPositionInArray(acceptedDefenders, duelist1) > -1)
{
return duelist1;
}
if(utils.getElementPositionInArray(acceptedChallengers, duelist2) > -1 || utils.getElementPositionInArray(acceptedDefenders, duelist2) > -1)
{
return duelist2;
}
return null;
}
//
// checks to make sure the challanger should be fighing the defender, rank wise. if so, the challenge
// data is removed
//
boolean validateAndRemoveChallenger(obj_id terminal, obj_id challenger, obj_id defender, obj_id enclave)
{
obj_id bogard = isEitherDuelistInADuel(terminal, challenger, defender);
if(bogard != null)
{
string bogardName = utils.getRealPlayerFirstName(bogard);
prose_package pp = new prose_package();
pp.stringId = new string_id("pvp_rating", "arena_duelist_already_engaged");
pp.target.set(bogardName);
sendSystemMessageProse(defender,pp);
return false;
}
resizeable obj_id[] challengers = new obj_id[0];
resizeable int[] ranksChallenged = new int[0];
resizeable int[] timeStamps = new int[0];
if(hasObjVar(terminal, VAR_CHALLENGERS))
{
utils.concatArrays(challengers, utils.getObjIdBatchObjVar(terminal, VAR_CHALLENGERS));
}
if(hasObjVar(terminal, VAR_CHALLENGED_RANKS))
{
utils.concatArrays(ranksChallenged, utils.getIntBatchObjVar(terminal, VAR_CHALLENGED_RANKS));
}
if(hasObjVar(terminal, VAR_CHALLENGE_TIMES))
{
utils.concatArrays(timeStamps, utils.getIntBatchObjVar(terminal, VAR_CHALLENGE_TIMES));
}
int idx = utils.getElementPositionInArray(challengers, challenger);
if(idx < 0)
{
sendSystemMessage(defender, new string_id("pvp_rating", "ch_terminal_challenge_no_more"));
return false;
}
int rankChallenged = ranksChallenged[idx];
if(force_rank.getForceRank(enclave, getFirstName(defender)) != rankChallenged)
{
sendSystemMessage(defender, new string_id("pvp_rating", "ch_terminal_challenge_wrong_rank"));
return false;
}
utils.removeElementAt(challengers, idx);
utils.removeElementAt(ranksChallenged, idx);
utils.removeElementAt(timeStamps, idx);
if(challengers.length > 0)
{
utils.setBatchObjVar(terminal, VAR_CHALLENGERS, challengers.toArray());
utils.setBatchObjVar(terminal, VAR_CHALLENGED_RANKS, ranksChallenged.toArray());
utils.setBatchObjVar(terminal, VAR_CHALLENGE_TIMES, timeStamps.toArray());
}
else
{
removeObjVar(terminal, VAR_CHALLENGERS);
removeObjVar(terminal, VAR_CHALLENGED_RANKS);
removeObjVar(terminal, VAR_CHALLENGE_TIMES);
}
return true;
}
//
// initiates the challange duel, does the flagging, etc
//
void beginChallengeDuel(obj_id terminal, obj_id challenger, obj_id defender)
{
obj_id arena_cell = getContainedBy(terminal);
if(!isIdValid(arena_cell) || !hasScript(arena_cell, "systems.gcw.dark_jedi_arena"))
{
trace.log("force_rank", "beginChallengeDuel: -> Couldn't make out the arena cell. Duel aborted.", null, trace.TL_CS_LOG | trace.TL_DEBUG);
return;
}
obj_id enclave = getTopMostContainer(terminal);
if (!isIdValid(enclave))
{
trace.log("force_rank", "beginChallengeDuel -- enclave is invalid. Duel aborted.", null, trace.TL_CS_LOG | trace.TL_DEBUG);
return;
}
if(!isPlayerInEnclave(challenger))
{
trace.log("force_rank", "%TU was not in the enclave when challenge was accepted. Counting as a death-loss.", null, trace.TL_CS_LOG | trace.TL_DEBUG);
duelistDied(challenger, defender, enclave, true);
sendSystemMessage(defender, new string_id("pvp_rating", "ch_terminal_challenger_forfeit"));
sendSystemMessage(challenger, new string_id("pvp_rating", "ch_terminal_challenger_forfeit_ch"));
utils.sendMail(new string_id("pvp_rating", "challenge_concluded_subject_header"), new string_id("pvp_rating", "ch_terminal_challenger_forfeit_ch"), utils.getRealPlayerFirstName(challenger), "Dark Enclave");
return;
}
if(!isPlayerPresentInArena(arena_cell, challenger))
{
teleportPlayerToRandomArenaLoc(challenger, arena_cell);
}
if(!isPlayerPresentInArena(arena_cell, defender))
{
teleportPlayerToRandomArenaLoc(defender, arena_cell);
}
setCombatTarget(challenger, defender);
setCombatTarget(defender, challenger);
utils.setScriptVar(challenger, "noBeneficialJediHelp", 1);
utils.setScriptVar(challenger, VAR_I_AM_DUELING, defender);
utils.setScriptVar(defender, VAR_I_AM_DUELING, challenger);
utils.setScriptVar(defender, "noBeneficialJediHelp", 1);
force_rank.makePlayersPermaEnemies(challenger, defender);
sendDirtyObjectMenuNotification(terminal);
return;
}
//
// teleports the player to a random location inside of the arena proper
//
void teleportPlayerToRandomArenaLoc(obj_id player, obj_id arenaCell)
{
float x = rand(ARENA_X_MIN, ARENA_X_MAX);
float z = rand(ARENA_Z_MIN, ARENA_Z_MAX);
location newloc = new location(x, ARENA_Y, z);
newloc.x = x;
newloc.y = ARENA_Y;
newloc.z = z;
newloc.cell = arenaCell;
setLocation(player, newloc);
}
//
// tests to see if @player is present in the enclave
//
boolean isPlayerInEnclave(obj_id player)
{
if(!isIdValid(player))
{
return false;
}
if(!player.isAuthoritative())
{
return false;
}
obj_id container = getTopMostContainer(player);
if(!isIdValid(container) || !hasScript(container, force_rank.SCRIPT_ENCLAVE_CONTROLLER))
{
return false;
}
return true;
}
//
// returns a string of all player names that have a challange issued against @rank
//
string[] getPlayerNamesForIssuedChallenges(obj_id challengeTerminal, int rank)
{
obj_id[] ids = getPlayerIdsForIssuedChallenges(challengeTerminal, rank);
return utils.makeNameList(ids);
}
//
// returns a list of obj_ids for players that have a challange issued against @ranks
//
obj_id[] getPlayerIdsForIssuedChallenges(obj_id challengeTerminal, int rank)
{
obj_id enclave = getTopMostContainer(challengeTerminal);
if(!isIdValid(enclave))
{
trace.log("force_rank", "arena::getPlayerIdsForIssuedChallenges: -> enclave is not valid. Not adding challenge issued data.", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return new obj_id[0];
}
resizeable obj_id[] challengers = new obj_id[0];
resizeable int[] ranks = new int[0];
if(hasObjVar(challengeTerminal, VAR_CHALLENGERS))
{
utils.concatArrays(challengers, getObjIdArrayObjVar(challengeTerminal, VAR_CHALLENGERS));
}
if(hasObjVar(challengeTerminal, VAR_CHALLENGED_RANKS))
{
utils.concatArrays(ranks, getIntArrayObjVar(challengeTerminal, VAR_CHALLENGED_RANKS));
}
// ensure data has the same # of elements
if(ranks.length != challengers.length)
{
trace.log("force_rank", "arena::getPlayerIdsForIssuedChallenges: -> Existing data on challengeTerminal is out of synch: challengers.length=" + challengers.length +
", ranks.length=" + ranks.length + ".", challengeTerminal, trace.TL_ERROR_LOG | trace.TL_DEBUG);
return new obj_id[0];
}
return new obj_id[0];
}
//
// @returns the true first name of the player if they are authoritative, otherwise returns null
//
string getRealPlayerFirstNameAuth(obj_id player)
{
if(!player.isAuthoritative())
{
return null;
}
// get the full player name
string name = getName(player);
// chop off the "corpse of" bit
int idx = name.toLowerCase().indexOf("corpse of");
if(idx >= 0)
{
name = name.substring(idx+1);
}
// return the first token of what's left
java.util.StringTokenizer tok = new java.util.StringTokenizer(name);
if(tok.hasMoreTokens ())
{
return tok.nextToken ();
}
return null;
}