Files
dsrc/sku.0/sys.server/compiled/game/script/library/leaderboard.java
T

1697 lines
94 KiB
Java

package script.library;
import script.DiscordWebhook;
import script.obj_id;
import java.awt.*;
import java.time.*;
import java.time.temporal.*;
import java.util.*;
import java.util.stream.*;
/**
* The Leaderboard Library is used to manipulate the GCW and Fishing (or any additional)
* Leaderboard displays in the game, as well as how their respective data is tabulated
* and stored.
*
* System Nomenclature:
*
* >>> Boards are either for "players" or "entities" (which are guilds or cities)
* >>> A board "type" is what system it's for. There are 2 right now: Fishing & GCW
* which are referenced by constants LEADERBOARD_TYPE_FISHING & LEADERBOARD_TYPE_GCW
* >>> You can "rank" on a board or "win" a board. To rank means to simply be displayed when
* browsing the list. To win means you are in an upper threshold of those ranked & get prizes.
* >>> Boards that are faction-affiliated always reference faction by their base constants
* FACTION_HASH_REBEL or FACTION_HASH_IMPERIAL, the crc32 hash of the faction name (int).
* >>>>>> Default Rank Qualification: Top 25 players, Top 10 Cities, Top 10 Guilds
* >>>>>> Default Win Qualification: Top 10 players, Top 5 Cities, Top 5 Guilds
* >>> The first place winner of the board is the "champion" (or "reigning champion")
* >>> A "period" is the time from which the leaderboard starts to end (by default,
* each period is 1 week long and ends Thursdays at 19:00 GMT, same as GCW reset)
* >>> There are 3 periods of record: "Current," "Previous," and "2 Weeks Ago"
* >>>>>> The "Current" period is the active period in continuous update cycles (by
* default, the period update heartbeat is every 15 minutes). When a period
* update heartbeat happens, this is called the "Period Update." When a period
* ends and data is moved/purged, this is called the "Period Reset."
* >>>>>> The "Previous" period is the most recently ended period from which the
* "reigning champions" are determined.
* >>>>>> The "2 Weeks Ago" period is largely ornamental the falloff of which is,
* however, used to remove the "War Planner" title, which all winners of the
* Previous and 2 Weeks Ago periods get (or the leader of guilds/cities).
* >>>>>> In general, periods are referenced by constants LEADERBOARD_PERIOD_*
* >>> The "War Planner" title is a perk for past winners as explained above but when
* it is granted, it also grants access to the Rebel/Imperial War Room Chat Channels.
* >>> The "Archive" is a history for only Guilds and Cities which displays the past
* 26 weeks (6 months) of their score data, along with some other
*
* System Functionality Notes:
*
* >>> Each leaderboard has a "Master Object" in Mos Eisley which stores the data and
* is used to handle sequential/timed activities (the period updates and resets).
* **NOTE: If the game server has no authority on the master object (e.g. if the zone
* goes to sleep or isn't active because there aren't players there) any messages
* and timed sequence activities will pause until the master object can be reached.
* There is eventual plans to make the Mos Eisley zone always spin on to resolve this
* as it also impacts other controllers/master objects, but that has not been done yet.
* >>> Note that leaderboard rankings are used to award additional Travel Points under the
* Waypoint Registry system.
* >>> See the master objects for other notes at systems.leaderboard.*_master_object
* >>> Admin Commands:
* >>>>>> /admin gcwLeaderboard opens a SUI window with GM/Debug Commands
* >>>>>> /admin dumpLeaderboardInfo provides a dump of leaderboard info for the player
* >>> A Web API is implemented so you can send leaderboard data via POST or via JSON to an
* PHP script or a Discord Web Hook, respectively.
*
* Future Implementations (ideas on how this can be expanded outside of cloning the SOE version):
*
* >>> It would be fairly easy to implement tracking for Guilds/Cities to display an "MVP"
* meaning the player in the guild/city that has scored the most for the guild/city
* >>> Not only is it annoying to transpose cluster-wide scores to localized scores (see those
* methods for what this refers to), it's actually more interesting to see a % score under
* the context of the entire galaxy versus compared to just the other ranked entities, so
* either adding that to the display, or ranking by it instead, would be nice.
* >>> By SOE's design, the weighting of these calculations lean somewhat heavily on factional
* presence and PvP which doesn't seem ideal.
* >>> Space is factored into the current boards by nature of the fact that factional presence
* and GCW points are earned in space, but separate space leaderboards might be enjoyed
* more by the pilot community.
*
* Most methods have JavaDoc notes and are well-commented to explain reasoning/logic where
* applicable. But if you have any questions, or to report any bugs, please feel free to
* message Aconite in the SWG Source Discord.
*
* SWG Source Addition - 2021
* Authors: Aconite
*/
@SuppressWarnings({"rawtypes", "unused"})
public class leaderboard extends script.base_script {
public leaderboard()
{
}
public static final int LEADERBOARD_TYPE_FISHING = 1;
public static final int LEADERBOARD_TYPE_GCW = 2;
public static final int LEADERBOARD_PERIOD_CURRENT = 1;
public static final int LEADERBOARD_PERIOD_PREVIOUS = 2;
public static final int LEADERBOARD_PERIOD_2WEEKSAGO = 3;
public static final int PERIOD_LENGTH = 604800;
public static final int[] WIN_COUNTS_WITH_BADGES = {1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100};
public static final String FISHING = "_fishing";
public static final String GCW = "_gcw";
public static final String GCW_LEADERBOARD_TITLE = "GCW Leaderboard";
public static final String[] GCW_LEADERBOARD_COL_NAMES = {"Rank", "Name", "Rank Value"};
public static final String[] GCW_LEADERBOARD_COL_TYPES = {"text", "text", "text"};
public static final String[] GCW_LEADERBOARD_ARCHIVE_COL_NAMES = {"Period Ending", "Faction", "Rank Value", "Winner (Y/N)", "Champion (Y/N)"};
public static final String[] GCW_LEADERBOARD_ARCHIVE_COL_TYPES = {"text", "text", "text", "text", "text"};
public static final String OBJVAR_LEADERBOARD_PERIOD = "leaderboard.current_period";
public static final String OBJVAR_LEADERBOARD_PERIOD_START = "leaderboard.current_period_start";
public static final String OBJVAR_LEADERBOARD_PERIOD_END = "leaderboard.current_period_end";
public static final String OBJVAR_LEADERBOARD_PARTICIPANT_GROUP = "leaderboard.current_participants_";
public static final String OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS = "leaderboard.current_participants_guild_";
public static final String OBJVAR_LEADERBOARD_CITY_PARTICIPANTS = "leaderboard.current_participants_city_";
public static final String OBJVAR_LEADERBOARD_PLAYER_SCORE_IMPERIAL = "leaderboard.score.player_imperial";
public static final String OBJVAR_LEADERBOARD_PLAYER_SCORE_REBEL = "leaderboard.score.player_rebel";
public static final String OBJVAR_LEADERBOARD_GUILD_SCORE_IMPERIAL = "leaderboard.score.guild_imperial";
public static final String OBJVAR_LEADERBOARD_GUILD_SCORE_REBEL = "leaderboard.score.guild_rebel";
public static final String OBJVAR_LEADERBOARD_CITY_SCORE_IMPERIAL = "leaderboard.score.city_imperial";
public static final String OBJVAR_LEADERBOARD_CITY_SCORE_REBEL = "leaderboard.score.city_rebel";
public static final String OBJVAR_LEADERBOARD_PLAYER_PERIOD = "leaderboard.current_period_to_player";
public static final String OBJVAR_LEADERBOARD_PLAYER_WIN_COUNT = "leaderboard.winner_count.player_"; // append faction
public static final String OBJVAR_LEADERBOARD_GUILD_WIN_COUNT = "leaderboard.winner_count.guild_"; // append faction
public static final String OBJVAR_LEADERBOARD_CITY_WIN_COUNT = "leaderboard.winner_count.city_"; // append faction
public static final String OBJVAR_CURRENT_PERIOD_LAST_UPDATED = "leaderboard.data.current.last_updated";
public static final String OBJVAR_LAST_NOTIFIED_RANK_GCW_GUILD = "leaderboard.notify.gcw_guild_rank";
public static final String OBJVAR_LAST_NOTIFIED_RANK_GCW_CITY = "leaderboard.notify.gcw_city_rank";
public static final String OBJVAR_LAST_NOTIFIED_RANK_GCW_PLAYER = "leaderboard.notify.gcw_player_rank";
public static final String OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_ID = "leaderboard.gcw_data.current.players_imperial_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_SCORE = "leaderboard.gcw_data.current.players_imperial_score";
public static final String OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_ID = "leaderboard.gcw_data.current.players_rebel_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_SCORE = "leaderboard.gcw_data.current.players_rebel_score";
public static final String OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_ID = "leaderboard.gcw_data.current.guild_imperial_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_SCORE = "leaderboard.gcw_data.current.guild_imperial_score";
public static final String OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_ID = "leaderboard.gcw_data.current.guild_rebel_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_SCORE = "leaderboard.gcw_data.current.guild_rebel_score";
public static final String OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_ID = "leaderboard.gcw_data.current.city_imperial_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_SCORE = "leaderboard.gcw_data.current.city_imperial_score";
public static final String OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_ID = "leaderboard.gcw_data.current.city_rebel_ids";
public static final String OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_SCORE = "leaderboard.gcw_data.current.city_rebel_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_IMPERIAL_ID = "leaderboard.gcw_data.previous.players_imperial_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_IMPERIAL_SCORE = "leaderboard.gcw_data.previous.players_imperial_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_REBEL_ID = "leaderboard.gcw_data.previous.players_rebel_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_REBEL_SCORE = "leaderboard.gcw_data.previous.players_rebel_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_IMPERIAL_ID = "leaderboard.gcw_data.previous.guild_imperial_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_IMPERIAL_SCORE = "leaderboard.gcw_data.previous.guild_imperial_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_REBEL_ID = "leaderboard.gcw_data.previous.guild_rebel_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_REBEL_SCORE = "leaderboard.gcw_data.previous.guild_rebel_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_CITY_IMPERIAL_ID = "leaderboard.gcw_data.previous.city_imperial_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_CITY_IMPERIAL_SCORE = "leaderboard.gcw_data.previous.city_imperial_score";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_CITY_REBEL_ID = "leaderboard.gcw_data.previous.city_rebel_ids";
public static final String OBJVAR_DATA_PREVIOUS_PERIOD_CITY_REBEL_SCORE = "leaderboard.gcw_data.previous.city_rebel_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_IMPERIAL_ID = "leaderboard.gcw_data.2weeksago.players_imperial_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_IMPERIAL_SCORE = "leaderboard.gcw_data.2weeksago.players_imperial_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_REBEL_ID = "leaderboard.gcw_data.2weeksago.players_rebel_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_REBEL_SCORE = "leaderboard.gcw_data.2weeksago.players_rebel_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_IMPERIAL_ID = "leaderboard.gcw_data.2weeksago.guild_imperial_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_IMPERIAL_SCORE = "leaderboard.gcw_data.2weeksago.guild_imperial_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_REBEL_ID = "leaderboard.gcw_data.2weeksago.guild_rebel_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_REBEL_SCORE = "leaderboard.gcw_data.2weeksago.guild_rebel_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_IMPERIAL_ID = "leaderboard.gcw_data.2weeksago.city_imperial_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_IMPERIAL_SCORE = "leaderboard.gcw_data.2weeksago.city_imperial_score";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_REBEL_ID = "leaderboard.gcw_data.2weeksago.city_rebel_ids";
public static final String OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_REBEL_SCORE = "leaderboard.gcw_data.2weeksago.city_rebel_score";
public static final String OBJVAR_DATA_GCW_ENTITY_HISTORY = "leaderboard.gcw_data.entity_history_"; // append guild/cityId
public static final String OBJVAR_PLAYER_TRACKING_PLAYER_ALREADY = "leaderboard.already_tracking_player";
public static final String OBJVAR_PLAYER_TRACKING_GUILD_ALREADY = "leaderboard.already_tracking_guild";
public static final String OBJVAR_PLAYER_TRACKING_CITY_ALREADY = "leaderboard.already_tracking_city";
public static final String OBJVAR_PLAYER_IMPERIAL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.player_imperial";
public static final String OBJVAR_PLAYER_REBEL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.player_rebel";
public static final String OBJVAR_GUILD_IMPERIAL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.guild_imperial";
public static final String OBJVAR_GUILD_REBEL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.guild_rebel";
public static final String OBJVAR_CITY_IMPERIAL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.city_imperial";
public static final String OBJVAR_CITY_REBEL_CURRENT_TEMP_SCORE = "leaderboard.temp_score.city_rebel";
public static final String OBJVAR_LEADERBOARD_LAST_DATA_PULL_TIME = "leaderboard.last_data_pull_time";
public static final String SCRIPTVAR_LEADERBOARD_LOCAL_SCORE_CACHED = "leaderboard.cached_data.local_score";
public static final String SCRIPTVAR_LEADERBOARD_SCORE_DATA_CACHED = "leaderboard.cached_data.score_data";
private static final String ANNOUNCEMENT_WEBHOOK_URL = getConfigSetting("GameServer", "leaderboardDiscordWebhook");
private static final boolean VERBOSE_LOGGING = utils.checkConfigFlag("GameServer", "leaderboardVerboseLogging");
/**
* @return obj_id of the master leaderboard object, if it exists
*/
public static obj_id getMasterLeaderboardObject() {
obj_id id = getObjIdObjVar(getPlanetByName("tatooine"), "leaderboard.master_object");
return isIdValid(id) ? id : obj_id.NULL_ID;
}
/**
* @return the current period number for the leaderboards (+1 for each week they run).
*/
public static int getCurrentLeaderboardPeriod() {
return getIntObjVar(getMasterLeaderboardObject(), OBJVAR_LEADERBOARD_PERIOD);
}
/**
* @return the Unix Timestamp of when the current leaderboard period started
*/
public static int getCurrentLeaderboardPeriodStartTime() {
return getIntObjVar(getMasterLeaderboardObject(), OBJVAR_LEADERBOARD_PERIOD_START);
}
/**
* @return the stored Unix Timestamp of when the current leaderboard period will end
*/
public static int getCurrentLeaderboardPeriodEndTime() {
return getIntObjVar(getMasterLeaderboardObject(), OBJVAR_LEADERBOARD_PERIOD_END);
}
/**
* @return Unix Timestamp of when the next leaderboard period will end
* Works by evaluating the time until the nearest upcoming Thursday at 19:00 GMT
* and then converts it to Unix Timestamp
*/
public static int getNextLeaderboardPeriodEndTime() {
ZoneId zone = ZoneId.of("GMT");
LocalDateTime now = LocalDateTime.now(zone).with(TemporalAdjusters.next(DayOfWeek.THURSDAY)).withHour(19).withMinute(0).withSecond(0);
return (int)now.atZone(zone).toEpochSecond();
}
/**
* @return the Unix Timestamp of when the current period data was last updated
*/
public static int getCurrentLeaderboardPeriodLastUpdated() {
return getIntObjVar(getMasterLeaderboardObject(), OBJVAR_CURRENT_PERIOD_LAST_UPDATED);
}
/**
* @return true if we are in the middle of the period buffer,
* which is 15 minutes before the end of a period
*/
public static boolean isWithinPeriodBuffer() {
return getCalendarTime() >= (getCurrentLeaderboardPeriodEndTime() - 900);
}
/**
* @return true if the server started less than 15 minutes ago
* In which case we don't want to be tracking/pushing data
* Note: This helps catch a few issues with load sync and authority
*/
public static boolean isWithinStartupBuffer() {
//todo remove before push
return false;
//return getCalendarTime() <= (getIntObjVar(getPlanetByName("tatooine"), "server_startup_time") + 900);
}
/**
* Adds the provided player (and their guild/city) to the participant tracking list for
* the given leaderboard type so that the master object will know to include their data
* when tabulating scores and winners.
* @param player obj_id of player to add
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
*/
public static void addLeaderboardParticipantToCurrentPeriod(obj_id player, int type) throws InterruptedException {
if(isWithinPeriodBuffer() || isWithinStartupBuffer()) {
return;
}
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
final obj_id[] fishing_ids = objvar_mangle.getMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+FISHING);
if(fishing_ids == null) {
objvar_mangle.setMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+FISHING, new obj_id[] {player});
} else {
if(!Arrays.asList(fishing_ids).contains(player)) {
obj_id[] newIds = Arrays.copyOf(fishing_ids, fishing_ids.length + 1);
newIds[newIds.length - 1] = player;
objvar_mangle.setMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP + FISHING, newIds);
}
}
break;
case LEADERBOARD_TYPE_GCW:
if(!hasObjVar(player, OBJVAR_PLAYER_TRACKING_PLAYER_ALREADY)) {
final obj_id[] gcw_ids = objvar_mangle.getMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP + GCW);
if (gcw_ids == null) {
// actually confirm that it doesn't exist manually because of sync delay
if(!hasObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW+"_mangled.count")) {
objvar_mangle.setMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP + GCW, new obj_id[]{player});
}
} else {
if (!Arrays.asList(gcw_ids).contains(player)) {
obj_id[] newIds = Arrays.copyOf(gcw_ids, gcw_ids.length + 1);
newIds[newIds.length - 1] = player;
objvar_mangle.setMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP + GCW, newIds);
}
}
setObjVar(player, OBJVAR_PLAYER_TRACKING_PLAYER_ALREADY, true);
debugMsg("Adding new participant to GCW Leaderboard Participant Group: "+getPlayerName(player)+" ("+player+")");
}
final int guildId = getGuildId(player);
final int cityId = getCitizenOfCityId(player);
if(guildId > 0 && gcw.canContributeToGcwLeaderboardForGuild(player)) {
if (!hasObjVar(player, OBJVAR_PLAYER_TRACKING_GUILD_ALREADY)) {
Vector guildIds = getResizeableIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS + GCW);
if (guildIds == null) {
setObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS + GCW, new int[]{guildId});
} else {
if (!guildIds.contains(guildId)) {
utils.addElement(guildIds, guildId);
setObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS + GCW, guildIds);
}
}
setObjVar(player, OBJVAR_PLAYER_TRACKING_GUILD_ALREADY, true);
debugMsg("Adding new guild to GCW Leaderboard Participant Group: " + guildGetAbbrev(guildId) + " (" + guildId + ")");
}
}
if(cityId > 0 && gcw.canContributeToGcwLeaderboardForCity(player)) {
if (!hasObjVar(player, OBJVAR_PLAYER_TRACKING_CITY_ALREADY)) {
Vector cityIds = getResizeableIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_CITY_PARTICIPANTS + GCW);
if (cityIds == null) {
setObjVar(masterObject, OBJVAR_LEADERBOARD_CITY_PARTICIPANTS + GCW, new int[]{cityId});
} else {
if (!cityIds.contains(cityId)) {
utils.addElement(cityIds, cityId);
setObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS + GCW, cityIds);
}
}
setObjVar(player, OBJVAR_PLAYER_TRACKING_CITY_ALREADY, true);
debugMsg("Adding new city to GCW Leaderboard Participant Group: " + cityGetName(cityId) + " (" + cityId + ")");
}
}
break;
}
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @return a Set of all players that have participated in the current leaderboard period
*/
public static Set<obj_id> getPlayerParticipantsForCurrentPeriod(int type) throws InterruptedException {
final obj_id masterObject = getMasterLeaderboardObject();
obj_id[] temp;
Set<obj_id> data = new HashSet<>();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
temp = objvar_mangle.getMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+FISHING);
if(temp != null) {
data.addAll(Arrays.asList(temp));
return data;
}
case LEADERBOARD_TYPE_GCW:
temp = objvar_mangle.getMangledObjIdArrayObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW);
if(temp != null) {
data.addAll(Arrays.asList(temp));
return data;
}
}
return null;
}
/**
* @param isCity true if a city, false if a guild (to avoid 2 methods)
* @return Returns a list of all entities (guild/city) that have participated in the current
* leaderboard period for the provided leaderboard type.
*/
public static Set<Integer> getEntityParticipantsForCurrentPeriod(int type, boolean isCity) {
final obj_id masterObject = getMasterLeaderboardObject();
int[] temp;
Set<Integer> data = new HashSet<>();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
if(isCity) {
temp = getIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_CITY_PARTICIPANTS+FISHING);
} else {
temp = getIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS+FISHING);
}
if(temp != null) {
Arrays.stream(temp).forEach(data::add);
}
return data;
case LEADERBOARD_TYPE_GCW:
if(isCity) {
temp = getIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_CITY_PARTICIPANTS+GCW);
} else {
temp = getIntArrayObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS+GCW);
}
if(temp != null) {
Arrays.stream(temp).forEach(data::add);
}
return data;
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return obj_id[] players ranked for the given period given the type and faction
* Players are in rank order from 1st place to 25th place
*/
public static obj_id[] getRankedPlayersForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch(period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getObjIdArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_REBEL_ID);
}
break;
}
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return int[] guilds ranked for the given period given the type and faction
* Entities are in rank order from 1st place to 10th place
*/
public static int[] getRankedGuildsForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch(period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_REBEL_ID);
}
break;
}
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return int[] cities ranked for the given period given the type and faction
* Entities are in rank order from 1st place to 10th place
*/
public static int[] getRankedCitiesForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch (period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_CITY_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_CITY_REBEL_ID);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_IMPERIAL_ID);
} else if (faction == FACTION_HASH_REBEL) {
return getIntArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_REBEL_ID);
}
break;
}
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return float[] of scores of ranked players for the current period given the type and faction
* Scores are in rank order from 1st to 25th to match the value pair of the getRankedPlayersForPeriod() method
*/
public static float[] getRankedPlayerScoresForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch(period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_PLAYERS_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_PLAYERS_REBEL_SCORE);
}
break;
}
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return float[] of scores of ranked guilds for the current period given the type and faction
* Scores are in rank order from 1st to 10th to match the value pair of the getRankedGuildsForPeriod() method
*/
public static float[] getRankedGuildScoresForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch(period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_GUILD_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_GUILD_REBEL_SCORE);
}
break;
}
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @param faction = FACTION_HASH_IMPERIAL or FACTION_HASH_REBEL
* @param period = LEADERBOARD_PERIOD_CURRENT, LEADERBOARD_PERIOD_PREVIOUS, or LEADERBOARD_PERIOD_2WEEKSAGO
* @return float[] of scores of ranked cities for the current period given the type and faction
* Scores are in rank order from 1st to 10th to match the value pair of the getRankedCitiesForPeriod() method
*/
public static float[] getRankedCityScoresForPeriod(int type, int faction, int period) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case LEADERBOARD_TYPE_FISHING:
break;
case LEADERBOARD_TYPE_GCW:
switch (period) {
case LEADERBOARD_PERIOD_CURRENT:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_PREVIOUS:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_CITY_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_PREVIOUS_PERIOD_CITY_REBEL_SCORE);
}
break;
case LEADERBOARD_PERIOD_2WEEKSAGO:
if (faction == FACTION_HASH_IMPERIAL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_IMPERIAL_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
return getFloatArrayObjVar(masterObject, OBJVAR_DATA_2WEEKSAGO_PERIOD_CITY_REBEL_SCORE);
}
break;
}
}
return null;
}
/**
* @return a HashMap of the current Player Winners and their Scores for the given faction
*/
public static HashMap<obj_id, Float> getGcwLeaderboardCurrentWinnersPlayerData(int faction) {
HashMap<obj_id, Float> data = new HashMap<>();
final obj_id[] players = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS);
final float[] scores = getRankedPlayerScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS);
if (scores != null && players != null) {
for (int i = 0; i < 11; i++) {
data.put(players[i], scores[i]);
}
return data;
}
return null;
}
/**
* @param isCity true if asking for city data, false if asking for guild Data
* @return a HashMap of the current Entity Winners and their Scores for the given faction and entity type
*/
public static HashMap<Integer, Float> getGcwLeaderboardCurrentWinnersEntityData(int faction, boolean isCity) {
HashMap<Integer, Float> data = new HashMap<>();
final int[] entities = isCity ? getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS) :
getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS);
final float[] scores = isCity ? getRankedCityScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS) :
getRankedGuildScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, LEADERBOARD_PERIOD_PREVIOUS);
if (scores != null && entities != null) {
for (int i = 0; i < 6; i++) {
data.put(entities[i], scores[i]);
}
return data;
}
return null;
}
/**
* @param type = LEADERBOARD_TYPE_FISHING or LEADERBOARD_TYPE_GCW
* @return true if the given player is in the current participant list for the given type
*/
public static boolean isPlayerInCurrentPeriodParticipantList(obj_id player, int type) throws InterruptedException {
switch (type) {
case LEADERBOARD_TYPE_FISHING:
obj_id[] ids = objvar_mangle.getMangledObjIdArrayObjVar(getMasterLeaderboardObject(), OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+FISHING);
if(ids != null) {
return Arrays.asList(ids).contains(player);
} else {
return false;
}
case LEADERBOARD_TYPE_GCW:
obj_id[] idsx = objvar_mangle.getMangledObjIdArrayObjVar(getMasterLeaderboardObject(), OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW);
if(idsx != null) {
return Arrays.asList(idsx).contains(player);
} else {
return false;
}
}
return false;
}
/**
* @param player obj_id of player to check
* @return true if the given player is in the current ranked list
*/
public static boolean isPlayerInCurrentRankedList(obj_id player) {
final obj_id[] ranked = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, pvpGetAlignedFaction(player), LEADERBOARD_PERIOD_CURRENT);
if(ranked != null) {
return Arrays.stream(ranked).anyMatch(x-> x == player);
} else {
return false;
}
}
/**
* @param entityId guild/city ID
* @param isCity true if city, false if guild
* @return true if the given entity is in the current ranked list
*/
public static boolean isEntityInCurrentRankedList(int entityId, boolean isCity) {
int[] ranked;
if(isCity) {
ranked = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, cityGetFaction(entityId), LEADERBOARD_PERIOD_CURRENT);
} else {
ranked = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, guildGetCurrentFaction(entityId), LEADERBOARD_PERIOD_CURRENT);
}
if(ranked != null) {
return Arrays.stream(ranked).anyMatch(x-> x == entityId);
} else {
return false;
}
}
/**
* @return true if the player is currently a player/guild/city Reigning Champion
*/
public static boolean isPlayerGcwLeaderboardReigningChampion(obj_id player) {
final long impSlot = getCollectionSlotValue(player, "gcw_leaderboard_imperial_current_winner");
final long rebSlot = getCollectionSlotValue(player, "gcw_leaderboard_rebel_current_winner");
return (impSlot > 0 || rebSlot > 0);
}
/**
* Handler for players who win the GCW Leaderboard Players Category
* Winners = Top 10 Players
*/
public static void handleGcwLeaderboardPlayerWinners(int faction, obj_id[] players, float[] scores) throws InterruptedException {
if(players == null || scores == null || players.length < 1 || scores.length < 1) {
return;
}
final String factionFormatted = factions.getFactionNameByHashCode(faction);
final String badgeString = toLower(factionFormatted);
// Craft Mail Message
String message = "Congratulations.\n\n" +
getPlayerFullName(players[0])+" has won the GCW "+factionFormatted+" leaderboard for the weekly period ending " +
getCalendarTimeStringLocal(getCurrentLeaderboardPeriodEndTime())+".\n\n";
StringBuilder winners = new StringBuilder();
for (int i = 0; i < players.length; i++) {
if(isIdValid(players[i])) { // so we don't add null entries popped from leaderboard object array copy
winners.append(i + 1).append(". ").append(getPlayerFullName(players[i])).append(", ").append(scores[i]).append("%\n");
}
}
String finalMessage = message + winners;
// Handle First Place
if(!badge.hasBadge(players[0], "bdg_gcw_leaderboard_" + badgeString + "_winner_rank1")) {
badge.grantBadge(players[0], "bdg_gcw_leaderboard_" + badgeString + "_winner_rank1");
}
for (obj_id player : players) {
// Grant Badges for Record X times won
if (!hasObjVar(player, OBJVAR_LEADERBOARD_PLAYER_WIN_COUNT + badgeString)) {
// first time win
setObjVar(player, OBJVAR_LEADERBOARD_PLAYER_WIN_COUNT + badgeString, 1);
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_winner_1x");
} else {
// grant a badge if they've reached a win count that has a badge
final int winCount = getIntObjVar(player, OBJVAR_LEADERBOARD_PLAYER_WIN_COUNT + badgeString) +1;
if(Arrays.stream(WIN_COUNTS_WITH_BADGES).anyMatch(i-> i == winCount)) {
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_winner_"+winCount+"x");
}
setObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString, winCount);
}
// Handle Winner Flags & Mail Message
// All player winners get War Planner, Reigning Champion, & Email
modifyCollectionSlotValue(player, badgeString+"_gcw_war_planner", 1);
modifyCollectionSlotValue(player, "gcw_leaderboard_"+ badgeString +"_current_winner", 1);
chatSendPersistentMessage("gcw leaderboard", player, "Winner - Player - "+factionFormatted, finalMessage, null);
LOG("gcw_leaderboard_winners", getPlayerName(player)+" ("+player+") was added to "+factionFormatted+" player winners list for period "+getCurrentLeaderboardPeriod());
}
announceGcwLeaderboardWinnersToDiscord(1, faction, players, null, scores);
}
/**
* Handler for guilds who win the GCW Leaderboard
* Winners = Top 5 Guilds
*/
public static void handleGcwLeaderboardGuildWinners(int faction, int[] guildIds, float[] scores) throws InterruptedException {
if(guildIds == null || scores == null || guildIds.length < 1 || scores.length < 1) {
return;
}
final String factionFormatted = factions.getFactionNameByHashCode(faction);
final String badgeString = toLower(factionFormatted);
// Craft Mail Message
String message = "Congratulations.\n\n" +
guildGetName(guildIds[0])+" ("+guildGetAbbrev(guildIds[0])+") has won the GCW "+factionFormatted+" leaderboard for the weekly period ending " +
getCalendarTimeStringLocal(getCurrentLeaderboardPeriodEndTime())+".\n\n";
StringBuilder winners = new StringBuilder();
for (int i = 0; i < guildIds.length; i++) {
if(isIdValid(guildGetLeader(guildIds[i]))) { // so we don't add null entries popped from leaderboard object array copy
winners.append(i + 1).append(". ").append(guildGetName(guildIds[i])).append(" (").append(guildGetAbbrev(guildIds[i])).append("), ").append(scores[i]).append("%\n");
}
}
String finalMessage = message + winners;
// Handle First Place
obj_id[] firstPlaceIds = guildGetMemberIds(guildIds[0]);
for (obj_id fp : firstPlaceIds) {
if (!badge.hasBadge(fp, "bdg_gcw_leaderboard_" + badgeString + "_guild_winner_rank1")) {
badge.grantBadge(fp, "bdg_gcw_leaderboard_" + badgeString + "_guild_winner_rank1");
LOG("gcw_leaderboard_winners", getPlayerName(fp)+"("+fp+") was granted badge bdg_gcw_leaderboard_"+badgeString+"_guild_winner_rank1");
}
}
for (int i : guildIds) {
obj_id[] players = guildGetMemberIds(i);
for (obj_id player : players) {
// Grant Badges for Record X times won
if (!hasObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString)) {
// first time win
setObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString, 1);
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_guild_winner_1x");
} else {
// grant a badge if they've reached a win count that has a badge
final int winCount = getIntObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString) +1;
if(Arrays.stream(WIN_COUNTS_WITH_BADGES).anyMatch(x-> x == winCount)) {
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_guild_winner_"+winCount+"x");
}
setObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString, winCount);
}
// Handle Winner Flags & Mail Message
// All members of the guild get Reigning Champion & email
modifyCollectionSlotValue(player, "gcw_leaderboard_"+ badgeString +"_current_winner", 1);
chatSendPersistentMessage("gcw leaderboard", player, "Winner - Guild - "+factionFormatted, finalMessage, null);
}
// Only guild leader gets War Planner Slot
modifyCollectionSlotValue(guildGetLeader(i), badgeString+"_gcw_war_planner", 1);
LOG("gcw_leaderboard_winners", guildGetAbbrev(i)+" ("+i+") was added to "+factionFormatted+" guild winners list for period "+getCurrentLeaderboardPeriod());
}
announceGcwLeaderboardWinnersToDiscord(2, faction, null, guildIds, scores);
}
/**
* Handler for cities who win the GCW Leaderboard
* Winners = Top 5 Cities
*/
public static void handleGcwLeaderboardCityWinners(int faction, int[] cityIds, float[] scores) throws InterruptedException {
if(cityIds == null || scores == null || cityIds.length < 1 || scores.length < 1) {
return;
}
final String factionFormatted = factions.getFactionNameByHashCode(faction);
final String badgeString = toLower(factionFormatted);
// Craft Mail Message
String message = "Congratulations.\n\n" +
cityGetName(cityIds[0])+" has won the GCW "+factionFormatted+" leaderboard for the weekly period ending " +
getCalendarTimeStringLocal(getCurrentLeaderboardPeriodEndTime())+".\n\n";
StringBuilder winners = new StringBuilder();
for (int i = 0; i < cityIds.length; i++) {
if(isIdValid(cityGetLeader(cityIds[i]))) { // so we don't add null entries popped from leaderboard object array copy
winners.append(i + 1).append(". ").append(cityGetName(cityIds[i])).append(", ").append(scores[i]).append("%\n");
}
}
String finalMessage = message + winners;
// Handle First Place
obj_id[] firstPlaceIds = cityGetCitizenIds(cityIds[0]);
for (obj_id fp : firstPlaceIds) {
if (!badge.hasBadge(fp, "bdg_gcw_leaderboard_" + badgeString + "_city_winner_rank1")) {
badge.grantBadge(fp, "bdg_gcw_leaderboard_" + badgeString + "_city_winner_rank1");
LOG("gcw_leaderboard_winners", getPlayerName(fp)+"("+fp+") was granted badge bdg_gcw_leaderboard_"+badgeString+"_city_winner_rank1");
}
}
for (int i : cityIds) {
obj_id[] players = cityGetCitizenIds(i);
for (obj_id player : players) {
// Grant Badges for Record X times won
if (!hasObjVar(player, OBJVAR_LEADERBOARD_CITY_WIN_COUNT + badgeString)) {
// first time win
setObjVar(player, OBJVAR_LEADERBOARD_CITY_WIN_COUNT + badgeString, 1);
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_city_winner_1x");
LOG("gcw_leaderboard_winners", getPlayerName(player)+"("+player+") was granted badge bdg_gcw_leaderboard_"+badgeString+"_city_winner_1x");
} else {
// grant a badge if they've reached a win count that has a badge
final int winCount = getIntObjVar(player, OBJVAR_LEADERBOARD_CITY_WIN_COUNT + badgeString) +1;
if(Arrays.stream(WIN_COUNTS_WITH_BADGES).anyMatch(x-> x == winCount)) {
badge.grantBadge(player, "bdg_gcw_leaderboard_"+badgeString+"_city_winner_"+winCount+"x");
LOG("gcw_leaderboard_winners", getPlayerName(player)+"("+player+") was granted badge bdg_gcw_leaderboard_"+badgeString+"_city_winner_"+winCount+"x");
}
setObjVar(player, OBJVAR_LEADERBOARD_GUILD_WIN_COUNT + badgeString, winCount);
}
// Handle Winner Flags & Mail Message
// All members of the city get Reigning Champion & email
modifyCollectionSlotValue(player, "gcw_leaderboard_"+ badgeString +"_current_winner", 1);
chatSendPersistentMessage("gcw leaderboard", player, "Winner - City - "+factionFormatted, finalMessage, null);
}
// Only city mayor gets War Planner Slot
modifyCollectionSlotValue(guildGetLeader(i), badgeString+"_gcw_war_planner", 1);
LOG("gcw_leaderboard_winners", cityGetName(i)+" ("+i+") was added to "+factionFormatted+" city winners list for period "+getCurrentLeaderboardPeriod());
}
announceGcwLeaderboardWinnersToDiscord(3, faction, null, cityIds, scores);
}
/**
* Sets the score data for the current period after it has been rank_pair'ed (players)
*/
public static void setCurrentPeriodScoreData(int type, obj_id[] entity, float[] scores) {
final obj_id masterObject = getMasterLeaderboardObject();
switch (type) {
case 1: // Player Imperials
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_IMPERIAL_SCORE, scores);
break;
case 2: // Player Rebels
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_PLAYERS_REBEL_SCORE, scores);
break;
}
debugMsg("setCurrentPeriodScoreData() called for type "+type+" and entities "+Arrays.toString(entity)+" and scores "+Arrays.toString(scores));
}
/**
* Sets the score data for the current period after it has been rank_pair'ed (guild/city)
*/
public static void setCurrentPeriodScoreData(int type, int[] entity, float[] scores) {
final obj_id masterObject = getMasterLeaderboardObject();
switch(type) {
case 3: // Guild Imperials
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_IMPERIAL_SCORE, scores);
break;
case 4: // Guild Rebels
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_GUILD_REBEL_SCORE, scores);
break;
case 5: // City Imperials
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_IMPERIAL_SCORE, scores);
break;
case 6: // City Rebels
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_ID, entity);
setObjVar(masterObject, OBJVAR_DATA_CURRENT_PERIOD_CITY_REBEL_SCORE, scores);
break;
}
debugMsg("setCurrentPeriodScoreData() called for type "+type+" and entities "+Arrays.toString(entity)+" and scores "+Arrays.toString(scores));
}
/**
* This is the method called when a player selects the "GCW Leaderboards..." radial menu options
* to actually see a specific leaderboard on one of the objects that allow a player to request
* them (War Terminal, War IntelPad, War Planning Table)
*
* This does not include the City/Guild Extended History Data as that is handled in the separate
* method renderGcwLeaderboardEntityHistory()
*
* This method and the subsequent methods that access and/or store data within it identify the request
* from a "render type" which is just an integer based on the index of the radial menu option chosen:
* 1 - Player - Rebel - Current Week
* 2 - Player - Imperial - Current Week
* 3 - Guild - Rebel - Current Week
* 4 - Guild - Imperial - Current Week
* 5 - City - Rebel - Current Week
* 6 - City - Imperial - Current Week
* 7 - Player - Rebel - Previous Week
* 8 - Player - Imperial - Previous Week
* 9 - Guild - Rebel - Previous Week
* 10 - Guild - Imperial - Previous Week
* 11 - City - Rebel - Previous Week
* 12 - City - Imperial - Previous Week
* 13 - Player - Rebel - 2 Weeks Ago
* 14 - Player - Imperial - 2 Weeks Ago
* 15 - Guild - Rebel - 2 Weeks Ago
* 16 - Guild - Imperial - 2 Weeks Ago
* 17 - City - Rebel - 2 Weeks Ago
* 18 - City - Imperial - 2 Weeks Ago
*/
public static void renderGcwLeaderboardDataToPlayer(obj_id player, obj_id requestingObject, int renderType) throws InterruptedException {
// Determine if a period update hasn't occurred since our last request for data, in which case use the cached data
// that has been stored on the requesting object instead of pulling it all again to reduce the total load
int lastUpdatedTime = getCurrentLeaderboardPeriodLastUpdated();
boolean usedCachedData = false;
if(hasObjVar(requestingObject, OBJVAR_LEADERBOARD_LAST_DATA_PULL_TIME)) {
final int pullTime = getIntObjVar(requestingObject, OBJVAR_LEADERBOARD_LAST_DATA_PULL_TIME);
if(pullTime == lastUpdatedTime ||
// always used cached data for the previous/2 weeks ago boards unless a reset has happened since we logged in
(renderType > 6 && getCurrentLeaderboardPeriodStartTime() < getPlayerLastLoginTime(player))) {
usedCachedData = true;
lastUpdatedTime = pullTime;
}
}
setObjVar(requestingObject, OBJVAR_LEADERBOARD_LAST_DATA_PULL_TIME, getCurrentLeaderboardPeriodLastUpdated());
final String topMessage = "Last Updated: "+getCalendarTimeStringLocal(lastUpdatedTime)+"\n\n";
final int factionHash = renderType % 2 == 0 ? FACTION_HASH_IMPERIAL : FACTION_HASH_REBEL; // even renderType vs odd to determine faction
final int weekType = renderType < 7 ? LEADERBOARD_PERIOD_CURRENT : (renderType < 13) ? LEADERBOARD_PERIOD_PREVIOUS : LEADERBOARD_PERIOD_2WEEKSAGO;
final String weekName = renderType < 7 ? "(Current Week)" : (renderType < 13) ? "(Previous Week)" : "(2 Weeks Ago)";
final String factionName = factions.getFactionNameByHashCode(factionHash);
final int guildId = getGuildId(player);
final int cityId = getCitizenOfCityId(player);
// Handle logic relative to what type of entity we are fetching data for Player/Guild/City
String entityName = getPlayerFullName(player);
String entityType = "Player";
switch (renderType) {
case 3:
case 4:
case 9:
case 10:
case 15:
case 16:
entityType = "Guild";
if(guildId > 0) {
entityName = guildGetName(guildId) + " ("+guildGetAbbrev(guildId)+")";
} else {
entityName = "None";
}
break;
case 5:
case 6:
case 11:
case 12:
case 17:
case 18:
entityType = "City";
if(cityId > 0) {
entityName = cityGetName(cityId);
} else {
entityName = "None";
}
break;
}
float score = -1.0f;
String[][] data;
String finalMessage;
// Get Local Score (this person/entity's score relative to the ranked players)
// unless requesting data for guild/city and you aren't in a guild/city, respectively
if(entityType.contains("Player") || (entityType.contains("Guild") && guildId > 0) || (entityType.contains("City") && cityId > 0)) {
if(usedCachedData && hasCachedLocalScore(requestingObject, renderType)) { // if we should use cached data & we have it
score = getCachedLocalScore(requestingObject, renderType);
} else { // actually pull the data and cache it
if(entityType.contains("Player")) {
score = getPlayerRelativeScoreToTransposedLeaderboardScores(player, factionHash, weekType);
} else if (entityType.contains("Guild")) {
score = getEntityRelativeScoreToTransposedLeaderboardScores(guildId, factionHash, weekType, false);
} else {
score = getEntityRelativeScoreToTransposedLeaderboardScores(cityId, factionHash, weekType, true);
}
cacheLocalScore(requestingObject, renderType, score);
}
}
// Get Score Data (actual ranked entities on the board)
if(usedCachedData && hasCachedScoreData(requestingObject, renderType)) { // if we should use cached data & we have it
data = getCachedScoreData(requestingObject, renderType);
} else { // actually pull the data and cache it
data = getFormattedGcwLeaderboardData(renderType, false);
if(data != null) {
cacheScoreData(requestingObject, renderType, data);
}
}
if(data == null || data.length < 1) {
sendSystemMessageTestingOnly(player, "There is no score data available for the requested GCW Leaderboard. Please try again later.");
return;
}
String prompt = "GCW "+factionName +" "+ entityType+" Leaderboard "+weekName;
if(score >= 0) {
String scoreInfo = ". Rank Value for "+entityName+" = "+score+"%"; // append local score if we have one
finalMessage = topMessage + prompt + scoreInfo;
} else {
finalMessage = topMessage + prompt;
}
sui.table(player, player, sui.OK_ONLY, GCW_LEADERBOARD_TITLE, "noHandler", finalMessage, GCW_LEADERBOARD_COL_NAMES, GCW_LEADERBOARD_COL_TYPES, data, true, true);
}
/**
* Handles pulling ranked entities and their respective scores and compiles it
* in a format suitable for rendering in a SUI Table
* @return String[][] for sui table with ranked entities and their scores
*/
public static String[][] getFormattedGcwLeaderboardData(int renderType, boolean debug) {
final int factionHash = renderType % 2 == 0 ? FACTION_HASH_IMPERIAL : FACTION_HASH_REBEL;
final int weekType = renderType < 7 ? LEADERBOARD_PERIOD_CURRENT : (renderType < 13) ? LEADERBOARD_PERIOD_PREVIOUS : LEADERBOARD_PERIOD_2WEEKSAGO;
obj_id[] players;
int[] entities;
float[] scores;
switch (renderType) {
// Players
case 1:
case 2:
case 7:
case 8:
case 13:
case 14:
players = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
scores = getRankedPlayerScoresForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
if (scores != null && players != null) {
final float[] transposedScores = transposeClusterScoreDataToLocalLeaderboardScoreData(scores);
String[][] data = new String[players.length][GCW_LEADERBOARD_COL_NAMES.length];
for (int i = 0; i < players.length; i++) {
data[i][0] = String.valueOf(i+1);
data[i][1] = debug ? getPlayerFullName(players[i])+" ("+players[i]+")" : getPlayerFullName(players[i]);
data[i][2] = debug ? "Cluster: "+scores[i]+" / Transposed: "+transposedScores[i]+" %" : transposedScores[i] + "%";
}
return data;
}
break;
// Guilds
case 3:
case 4:
case 9:
case 10:
case 15:
case 16:
entities = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
scores = getRankedGuildScoresForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
if (scores != null && entities != null) {
final float[] transposedScores = transposeClusterScoreDataToLocalLeaderboardScoreData(scores);
String[][] data = new String[entities.length][GCW_LEADERBOARD_COL_NAMES.length];
for (int i = 0; i < entities.length; i++) {
data[i][0] = String.valueOf(i+1);
data[i][1] = debug ? guildGetName(entities[i])+" ("+entities[i]+")" : guildGetName(entities[i]);
data[i][2] = debug ? "Cluster: "+scores[i]+"% | Transposed: "+transposedScores[i]+"%" : transposedScores[i] + "%";
}
return data;
}
break;
// Cities
case 5:
case 6:
case 11:
case 12:
case 17:
case 18:
entities = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
scores = getRankedCityScoresForPeriod(LEADERBOARD_TYPE_GCW, factionHash, weekType);
if (scores != null && entities != null) {
final float[] transposedScores = transposeClusterScoreDataToLocalLeaderboardScoreData(scores);
String[][] data = new String[entities.length][GCW_LEADERBOARD_COL_NAMES.length];
for (int i = 0; i < entities.length; i++) {
data[i][0] = String.valueOf(i+1);
data[i][1] = debug ? cityGetName(entities[i])+" ("+entities[i]+")" : cityGetName(entities[i]);
data[i][2] = debug ? "Cluster: "+scores[i]+"% | Transposed: "+transposedScores[i]+"%" : transposedScores[i] + "%";
}
return data;
}
break;
}
return null;
}
/**
* Because the leaderboard does not display the % score of the ranked player/entity
* relative to the entire cluster but rather relative to the other ranked entities,
* i.e. the top score cluster-wide might only be 1.58%, but relative to the other
* top-ranked entities, it might be 25% of their collective score, so we have to
* transpose this data to display the scores relative to the other ranked scores
* as opposed to displaying cluster-wide data.
* @return float[] of transposed Cluster to Local scores
*/
public static float[] transposeClusterScoreDataToLocalLeaderboardScoreData(float[] scoreData) {
final double total = IntStream.range(0, scoreData.length).mapToDouble(i -> scoreData[i]).sum();
float[] transposedData = new float[scoreData.length];
for (int i = 0; i < scoreData.length; i++) {
transposedData[i] = (float)(scoreData[i] / total) * 100;
}
debugMsg("transposeClusterScoreDataToLocalLeaderboardScoreData() took in scores param of "+Arrays.toString(scoreData));
debugMsg("transposeClusterScoreDataToLocalLeaderboardScoreData() returned score sum "+total+" and transposed data: "+Arrays.toString(transposedData));
return transposedData;
}
/**
* Returns the % score of a player relative to the transposed local leaderboard
* scores which shows how far the player is, in relation to the other ranked
* players, from reaching a ranked position on the leaderboard.
* @return float score relative to the transposed scores
*/
public static float getPlayerRelativeScoreToTransposedLeaderboardScores(obj_id player, int faction, int period) {
final float[] scores = getRankedPlayerScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, period);
final obj_id[] players = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, faction, period);
if(players != null && scores != null) { // don't calculate a relative score for someone already ranked because that wouldn't make sense
if (Arrays.stream(players).anyMatch(x-> x== player)) {
return scores[Arrays.asList(players).indexOf(player)];
}
}
if(scores != null) {
double total = IntStream.range(0, scores.length).mapToDouble(i -> scores[i]).sum();
float score = 0.0f;
if(faction == FACTION_HASH_IMPERIAL) {
score = getFloatObjVar(player, OBJVAR_PLAYER_IMPERIAL_CURRENT_TEMP_SCORE);
} else if (faction == FACTION_HASH_REBEL) {
score = getFloatObjVar(player, OBJVAR_PLAYER_REBEL_CURRENT_TEMP_SCORE);
}
if (score > 0) {
float toReturn = (float)(score / (score + total)) * 100;
debugMsg("getPlayerRelativeScoreToTransposedLeaderboardScores() searched for scores from faction "+faction+" and period "+period+" for score data: "+Arrays.toString(scores));
debugMsg("getPlayerRelativeScoreToTransposedLeaderboardScores() returned transposed local score: "+toReturn);
return toReturn;
}
}
return 0.0f;
}
/**
* See: getPlayerRelativeScoreToTransposedLeaderboardScores but for Guilds/Cities
*/
public static float getEntityRelativeScoreToTransposedLeaderboardScores(int entity, int faction, int period, boolean isCity) {
final float[] scores = isCity ? getRankedCityScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, period) :
getRankedGuildScoresForPeriod(LEADERBOARD_TYPE_GCW, faction, period);
final int[] entities = isCity? getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, faction, period) :
getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, faction, period);
if(entities != null && scores != null) { // don't calculate a relative score for someone already ranked because that wouldn't make sense
if (Arrays.stream(entities).anyMatch(x-> x== entity)) {
return scores[Arrays.binarySearch(entities, entity)];
}
}
if(scores != null) {
final double total = IntStream.range(0, scores.length).mapToDouble(i -> scores[i]).sum();
final obj_id masterObject = isCity ? getMasterCityObject() : getMasterGuildObject();
String var;
float score = 0.0f;
if(faction == FACTION_HASH_IMPERIAL) {
var = isCity ? OBJVAR_CITY_IMPERIAL_CURRENT_TEMP_SCORE : OBJVAR_GUILD_IMPERIAL_CURRENT_TEMP_SCORE;
score = getFloatObjVar(masterObject, var+"_"+entity);
} else if (faction == FACTION_HASH_REBEL) {
var = isCity ? OBJVAR_CITY_REBEL_CURRENT_TEMP_SCORE : OBJVAR_GUILD_REBEL_CURRENT_TEMP_SCORE;
score = getFloatObjVar(masterObject, var+"_"+entity);
}
if (score > 0) {
float toReturn = (float)(score / (score + total)) * 100;
debugMsg("getEntityRelativeScoreToTransposedLeaderboardScores() searched for scores from faction "+faction+" and period "+period+" for score data: "+Arrays.toString(scores));
debugMsg("getEntityRelativeScoreToTransposedLeaderboardScores() returned transposed local score: "+toReturn);
return toReturn;
}
}
return 0;
}
/**
* Used to render the Guild/City GCW Leaderboard Score Archive SUI Tables
* See packGcwEntityHistoryData() for information on how this works
*/
public static void renderGcwLeaderboardEntityHistory(obj_id player, obj_id requestingObject, boolean isCity) throws InterruptedException {
final obj_id masterObject = isCity ? getMasterCityObject() : getMasterGuildObject();
final int entityId = isCity ? getCitizenOfCityId(player) : getGuildId(player);
final int renderType = isCity ? 20 : 19;
final String type = isCity ? "City" : "Guild";
final String ObjVar = OBJVAR_DATA_GCW_ENTITY_HISTORY + "_" + entityId;
if(entityId < 1) {
return;
}
final String[] packedData = getStringArrayObjVar(masterObject, ObjVar);
if(packedData == null || packedData.length == 0) {
sendSystemMessageTestingOnly(player, "There is no GCW Leaderboard History available for your "+type+" at this time.");
return;
}
boolean useCachedData = true;
// always used cached data for accessing archives because they're old unless a reset has happened since we've logged in
if(getCurrentLeaderboardPeriodStartTime() > getPlayerLastLoginTime(player)) {
useCachedData = false;
}
String[][] data;
if(useCachedData && hasCachedScoreData(requestingObject, renderType)) {
data = getCachedScoreData(requestingObject, renderType);
} else {
data = new String[packedData.length][GCW_LEADERBOARD_ARCHIVE_COL_NAMES.length];
String[] temp;
for(int i = 0; i < packedData.length; i++) {
temp = packedData[i].split(",");
data[i][0] = getCalendarTimeStringLocal(Integer.parseInt(temp[0]));
data[i][1] = factions.getFactionNameByHashCode(Integer.parseInt(temp[1]));
data[i][2] = temp[2]+"%";
data[i][3] = Integer.parseInt(temp[3]) == 0 ? "No" : "Yes";
data[i][4] = Integer.parseInt(temp[4]) == 0 ? "No" : "Yes";
}
cacheScoreData(requestingObject, renderType, data);
}
final String name = isCity ? cityGetName(entityId) : guildGetName(entityId) + " ("+guildGetAbbrev(entityId)+")";
final String message = "GCW Leaderboard History for "+type+": "+name;
sui.table(player, player, sui.OK_ONLY, GCW_LEADERBOARD_TITLE, "noHandler", message, GCW_LEADERBOARD_ARCHIVE_COL_NAMES, GCW_LEADERBOARD_ARCHIVE_COL_TYPES, data, true, true);
}
/**
* Used to pack the historical data of a guild/city. The data is stored as a String Array ObjVar
* on the Master Guild/City objects for each guild/city. Guild/City disband methods will remove
* this data automatically if a guild or city is disbanded/destroyed.
*
* Each object in the array is structured like this:
* Period End Unix Timestamp , Faction Hash , Percentile Score , Winner (0/1) , Champion (0/1)
* example: 1617508148,-615855020,4.49242,1,0
* Which would read in the SUI something like: "April 3 2020 | Imperial | 0.49242% | Yes | No"
*
* Up to 6 months (26 periods) of historical data is stored. If you wanted to expand this, you
* could but it would probably be better to store data older than that on an external source,
* such as a website so you don't have to mangle the ObjVars.
*
* @param isCity true if it's a city, false if it's a guild (just to save having to make 2 methods)
*/
public static void packGcwEntityHistoryData(int entityId, String[] data, boolean isCity) {
final obj_id masterObject = isCity ? getMasterCityObject() : getMasterGuildObject();
final String ObjVar = OBJVAR_DATA_GCW_ENTITY_HISTORY + "_" + entityId;
String[] packedData = new String[1];
StringBuilder buildData = new StringBuilder();
for (int i = 0; i < data.length; i++) {
buildData.append(data[i]);
if(i != data.length -1) {
buildData.append(",");
}
}
packedData[0] = buildData.toString();
if(!hasObjVar(masterObject, ObjVar)) {
setObjVar(masterObject, ObjVar, packedData);
debugMsg("packGcwEntityHistoryData added new packed history for entity "+entityId+ " as: "+ Arrays.toString(packedData));
} else {
String[] temp = getStringArrayObjVar(masterObject, ObjVar);
if(temp != null) {
Vector<String> vData = new Vector<>(Arrays.asList(temp));
if(vData.size() > 25) {
vData.remove(0);
}
vData.add(packedData[0]);
String[] newData = new String[vData.size()];
for(int i = 0; i < newData.length; i++) {
newData[i] = vData.get(i);
}
setObjVar(masterObject, ObjVar, newData);
debugMsg("packGcwEntityHistoryData added new packed history for entity "+entityId+ " as: "+Arrays.toString(newData));
}
}
}
/**
* Caches on the requesting object a tabulated local score
*/
public static void cacheLocalScore(obj_id requestingObject, int renderType, float score) throws InterruptedException {
debugMsg("cacheLocalScore cached score "+score+" on object "+requestingObject+" for renderType "+renderType);
utils.setScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_LOCAL_SCORE_CACHED+"_"+renderType, score);
}
/**
* @return true if a local score has been cached for the respective renderType
*/
public static boolean hasCachedLocalScore(obj_id requestingObject, int renderType) throws InterruptedException {
return utils.hasScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_LOCAL_SCORE_CACHED+"_"+renderType);
}
/**
* @return float cached local score
*/
public static float getCachedLocalScore(obj_id requestingObject, int renderType) throws InterruptedException {
return utils.getFloatScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_LOCAL_SCORE_CACHED+"_"+renderType);
}
/**
* Caches on the requesting object a complete ranked score data for the respective renderType
*/
public static void cacheScoreData(obj_id requestingObject, int renderType, String[][] scoreData) throws InterruptedException {
debugMsg("cacheScoreData cached score data on object "+requestingObject+" for renderType "+renderType+" with data: "+Arrays.toString(scoreData));
utils.setScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_SCORE_DATA_CACHED+"_"+renderType, scoreData);
}
/**
* @return true if score data has been cached for the respective renderType
*/
public static boolean hasCachedScoreData(obj_id requestingObject, int renderType) throws InterruptedException {
return utils.hasScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_SCORE_DATA_CACHED+"_"+renderType);
}
/**
* @return String[][] cached local score data
*/
public static String[][] getCachedScoreData(obj_id requestingObject, int renderType) throws InterruptedException {
return utils.getStringArrayArrayScriptVar(requestingObject, SCRIPTVAR_LEADERBOARD_SCORE_DATA_CACHED+"_"+renderType);
}
/**
* Adds the temporary tracking score onto the player
*/
public static void addPlayerGcwLeaderboardTempTrackingScore(obj_id player, int faction, float score) {
if(faction == FACTION_HASH_IMPERIAL) {
setObjVar(player, leaderboard.OBJVAR_PLAYER_IMPERIAL_CURRENT_TEMP_SCORE, score);
} else if (faction == FACTION_HASH_REBEL) {
setObjVar(player, leaderboard.OBJVAR_PLAYER_REBEL_CURRENT_TEMP_SCORE, score);
}
}
/**
* Adds the temporary tracking score for a guild/player to their
* respective master objects
*/
public static void addEntityGcwLeaderboardTempTrackingScore(int entity, int faction, float score, boolean isCity) {
final obj_id masterObject = isCity ? getMasterCityObject() : getMasterGuildObject();
if(faction == FACTION_HASH_IMPERIAL) {
if(isCity) {
setObjVar(masterObject, leaderboard.OBJVAR_CITY_IMPERIAL_CURRENT_TEMP_SCORE+"_"+entity, score);
} else {
setObjVar(masterObject, leaderboard.OBJVAR_GUILD_IMPERIAL_CURRENT_TEMP_SCORE+"_"+entity, score);
}
} else if (faction == FACTION_HASH_REBEL) {
if(isCity) {
setObjVar(masterObject, leaderboard.OBJVAR_CITY_REBEL_CURRENT_TEMP_SCORE+"_"+entity, score);
} else {
setObjVar(masterObject, leaderboard.OBJVAR_GUILD_REBEL_CURRENT_TEMP_SCORE+"_"+entity, score);
}
}
}
/**
* Handles announcing Leaderboard Rank Changes to Discord, if a Web Hook URL has been set in config
*/
public static void announceGcwLeaderboardRankChangeToDiscord(String message) {
if(ANNOUNCEMENT_WEBHOOK_URL == null || ANNOUNCEMENT_WEBHOOK_URL.isEmpty()) {
return;
}
DiscordWebhook hook = new DiscordWebhook(ANNOUNCEMENT_WEBHOOK_URL);
hook.setContent(message);
hook.execute();
}
/**
* Handles announcing Leaderboard Winners to Discord, if a Web Hook URL has been set in config
*/
public static void announceGcwLeaderboardWinnersToDiscord(int winnerType, int faction, obj_id[] players, int[] entities, float[] scores) {
if(ANNOUNCEMENT_WEBHOOK_URL == null || ANNOUNCEMENT_WEBHOOK_URL.isEmpty()) {
return;
}
DiscordWebhook hook = new DiscordWebhook(ANNOUNCEMENT_WEBHOOK_URL);
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject();
embed.setColor(faction == FACTION_HASH_IMPERIAL ? Color.BLUE : Color.RED);
switch (winnerType) {
case 1: // Players
if(players != null && scores != null) {
embed.setTitle("GCW Leaderboard Winners - Players - "+factions.getFactionNameByHashCode(faction));
for(int i = 0; i < players.length; i++) {
if(isIdValid(players[i])) {
embed.addField(getPlayerFullName(players[i]), "Rank " + i+1 + " ("+scores[i]+"%)", false);
}
}
}
break;
case 2: // Guilds
if(entities != null && scores != null) {
embed.setTitle("GCW Leaderboard Winners - Guilds - "+factions.getFactionNameByHashCode(faction));
for(int i = 0; i < entities.length; i++) {
if(isIdValid(guildGetLeader(entities[i]))) {
embed.addField(guildGetName(entities[i]), "Rank " + i+1 + " ("+scores[i]+"%)", false);
}
}
}
break;
case 3: // Cities
if(entities != null && scores != null) {
embed.setTitle("GCW Leaderboard Winners - Cities - "+factions.getFactionNameByHashCode(faction));
for(int i = 0; i < entities.length; i++) {
if (isIdValid(cityGetLeader(entities[i]))) {
embed.addField(cityGetName(entities[i]), "Rank "+ i+1 +" ("+scores[i]+"%)", false);
}
}
}
break;
}
hook.addEmbed(embed);
hook.execute();
}
/**
* Removes tracking vars for participant lists after a period reset.
* Note: the messageTo herein can be enabled to act as a script-trigger
* equivalent for notifying players the period has reset. It is not
* currently implemented given there was no need for it, but I left it
* here as it's a great spot to implement a notification if needed.
*/
public static void clearGcwLeaderboardTrackingVars(Set<obj_id> players) {
if(players != null) {
for (obj_id player : players) {
removeObjVar(player, leaderboard.OBJVAR_PLAYER_TRACKING_PLAYER_ALREADY);
removeObjVar(player, leaderboard.OBJVAR_PLAYER_TRACKING_GUILD_ALREADY);
removeObjVar(player, leaderboard.OBJVAR_PLAYER_TRACKING_CITY_ALREADY);
//messageTo(player, "OnGcwLeaderboardPeriodResetFinished", null, 30f, false);
}
}
}
/**
* Handles removing the players who are no longer considered "Winners" (top 10)
* because they haven't won in the most recent period.
*
* Note: This should run when a period reset occurs but before any new winners
* are assigned (so you don't purge new winners after they won).
*/
public static void handlePurgeReigningChampions() {
final obj_id[] players1 = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_PREVIOUS);
final obj_id[] players2 = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_PREVIOUS);
final int[] guild1 = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_PREVIOUS);
final int[] guild2 = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_PREVIOUS);
final int[] city1 = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_PREVIOUS);
final int[] city2 = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_PREVIOUS);
if(players1 != null) {
purgeReigningChampions(players1[0]);
}
if(players2 != null) {
purgeReigningChampions(players2[0]);
}
if(guild1 != null) {
purgeReigningChampions(guildGetMemberIds(guild1[0]));
}
if(guild2 != null) {
purgeReigningChampions(guildGetMemberIds(guild2[0]));
}
if(city1 != null) {
purgeReigningChampions(cityGetCitizenIds(city1[0]));
}
if(city2 != null) {
purgeReigningChampions(cityGetCitizenIds(city2[0]));
}
}
public static void purgeReigningChampions(obj_id[] playersToRemove) {
for(obj_id player : playersToRemove) {
purgeReigningChampions(player);
}
}
public static void purgeReigningChampions(obj_id player) {
if(getCollectionSlotValue(player, "gcw_leaderboard_imperial_current_winner") > 0) {
modifyCollectionSlotValue(player, "gcw_leaderboard_imperial_current_winner", -1);
LOG("gcw_leaderboard_purge", "gcw_leaderboard_imperial_current_winner was removed from "+getPlayerName(player)+" ("+player+")");
}
if(getCollectionSlotValue(player, "gcw_leaderboard_rebel_current_winner") > 0) {
modifyCollectionSlotValue(player, "gcw_leaderboard_rebel_current_winner", -1);
LOG("gcw_leaderboard_purge", "gcw_leaderboard_rebel_current_winner was removed from "+getPlayerName(player)+" ("+player+")");
}
}
/**
* Called to remove the "War Planner" titles from players (anyone who is currently in the
* "2 weeks ago" slot at a period reset and is getting pushed out). It will get re-applied
* if they are back in a new period.
*
* Note: This should run when a period reset occurs but before any new winners
* are assigned (so you don't purge new winners after they won).
*/
public static void handlePurgeWarPlanners() {
final obj_id[] players1 = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_2WEEKSAGO);
final obj_id[] players2 = getRankedPlayersForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_2WEEKSAGO);
final int[] guild1 = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_2WEEKSAGO);
final int[] guild2 = getRankedGuildsForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_2WEEKSAGO);
final int[] city1 = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_REBEL, LEADERBOARD_PERIOD_2WEEKSAGO);
final int[] city2 = getRankedCitiesForPeriod(LEADERBOARD_TYPE_GCW, FACTION_HASH_IMPERIAL, LEADERBOARD_PERIOD_2WEEKSAGO);
if(players1 != null) {
purgeWarPlanners(players1);
}
if(players2 != null) {
purgeWarPlanners(players2);
}
if(guild1 != null) {
for(int id : guild1) {
purgeWarPlanners(guildGetLeader(id));
}
}
if(guild2 != null) {
for(int id : guild2) {
purgeWarPlanners(guildGetLeader(id));
}
}
if(city1 != null) {
for(int id : city1) {
purgeWarPlanners(cityGetLeader(id));
}
}
if(city2 != null) {
for(int id : city2) {
purgeWarPlanners(cityGetLeader(id));
}
}
}
public static void purgeWarPlanners(obj_id[] playersToRemove) {
for(obj_id player : playersToRemove) {
purgeWarPlanners(player);
}
}
public static void purgeWarPlanners(obj_id player) {
if(getCollectionSlotValue(player, "rebel_gcw_war_planner") > 0) {
modifyCollectionSlotValue(player, "rebel_gcw_war_planner", -1);
LOG("gcw_leaderboard_purge", "rebel_gcw_war_planner was removed from "+getPlayerName(player)+" ("+player+")");
}
if(getCollectionSlotValue(player, "imperial_gcw_war_planner") > 0) {
modifyCollectionSlotValue(player, "imperial_gcw_war_planner", -1);
LOG("gcw_leaderboard_purge", "imperial_gcw_war_planner was removed from "+getPlayerName(player)+" ("+player+")");
}
}
/**
* Drops all participant data from the current period
*/
public static void clearAllCurrentPeriodParticipants() {
final obj_id masterObject = getMasterLeaderboardObject();
final int mangleCt = getIntObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW+"_mangled.count");
for(int i = 0; i < mangleCt; i++) {
removeObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW+"_mangled.segment."+i);
}
removeObjVar(masterObject, OBJVAR_LEADERBOARD_PARTICIPANT_GROUP+GCW+"_mangled.count");
removeObjVar(masterObject, OBJVAR_LEADERBOARD_GUILD_PARTICIPANTS+GCW);
removeObjVar(masterObject, OBJVAR_LEADERBOARD_CITY_PARTICIPANTS+GCW);
debugMsg("Finished running leaderboard.clearAllCurrentPeriodParticipants()");
}
/**
* Called on guild disband / city destruction to purge the history data from the master objects
*/
public static void purgeEntityGcwLeaderboardHistory(int entityId, boolean isCity) {
final obj_id masterObject = isCity ? getMasterCityObject() : getMasterGuildObject();
final String ObjVar = OBJVAR_DATA_GCW_ENTITY_HISTORY + "_" + entityId;
removeObjVar(masterObject, ObjVar);
}
/**
* Shows an SUI List Box with available Debugging/Admin Data for the GCW Leaderboard
* which is then passed to handler > renderGcwLeaderboardDataCsInternalOnly
* CS Cmd: /admin gcwLeaderboard
*/
public static void promptGcwLeaderboardDataRequestCsInternalOnly(obj_id player) throws InterruptedException {
if(!isGod(player)) {
return;
}
String[] menuOptions = {
"Leaderboard Statistics",
"Request Period Update",
"Request Period Reset",
"View Board: Current Player Imperial",
"View Board: Current Player Rebel",
"View Board: Current Guild Imperial",
"View Board: Current Guild Rebel",
"View Board: Current City Imperial",
"View Board: Current City Rebel",
"View Board: Previous Player Imperial",
"View Board: Previous Player Rebel",
"View Board: Previous Guild Imperial",
"View Board: Previous Guild Rebel",
"View Board: Previous City Imperial",
"View Board: Previous City Rebel",
"View Board: 2WksAgo Player Imperial",
"View Board: 2WksAgo Player Rebel",
"View Board: 2WksAgo Guild Imperial",
"View Board: 2WksAgo Guild Rebel",
"View Board: 2WksAgo City Imperial",
"View Board: 2WksAgo City Rebel",
};
sui.listbox(player, player, "GM Resources for GCW Leaderboard. Please select an option below.", sui.OK_CANCEL, "GCW Leaderboard Debug", menuOptions, "handleCsLeaderboardDataMenuSelection", true, false);
}
/**
* Handler for the response to promptGcwLeaderboardDataRequestCsInternalOnly
* "View Board" items are identical to renderGcwLeaderboardDataToPlayer but includes debugging/CS info
* ***NOTE*** if you add new switch options in front of the view leaderboard options, make sure you
* adjust the subtraction inside getFormattedGcwLeaderboardData so it calls the correct board
*/
public static void handleGcwLeaderboardDataRequestCsInternalOnly(obj_id player, int menuOptionIndex) throws InterruptedException {
switch(menuOptionIndex) {
case 0:
sendSystemMessageTestingOnly(player, "Nothing is implemented for this option yet because Aconite couldn't decide what all would be useful here. If you have ideas, feel free to message him.");
break;
case 1:
messageTo(getMasterLeaderboardObject(), "handleCurrentPeriodUpdateHeartbeat", null, 0f, false);
sendSystemMessageTestingOnly(player, "Requesting a forced leaderboard period update...");
LOG("leaderboard_gm", "GM "+getPlayerName(player)+" ("+player+") used /admin gcwLeaderboard to call handleCurrentPeriodUpdateHeartbeat");
break;
case 2:
messageTo(getMasterLeaderboardObject(), "handleLeaderboardPeriodReset", null, 0f, false);
sendSystemMessageTestingOnly(player, "Requesting a forced leaderboard period reset...");
LOG("leaderboard_gm", "GM "+getPlayerName(player)+" ("+player+") used /admin gcwLeaderboard to call handleLeaderboardPeriodReset");
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
String[][] data = getFormattedGcwLeaderboardData(menuOptionIndex-3, true);
sui.table(player, player, sui.OK_ONLY, GCW_LEADERBOARD_TITLE, "noHandler", "Debug GM Data", GCW_LEADERBOARD_COL_NAMES, GCW_LEADERBOARD_COL_TYPES, data, true, true);
break;
}
}
/**
* Conditionally prints a debug message to console if debug mode is on
*/
public static void debugMsg(String message) {
if(VERBOSE_LOGGING) {
LOG("leaderboard_debug", message);
}
}
} // end library.leaderboard