mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-31 00:15:54 -04:00
2785 lines
76 KiB
Plaintext
2785 lines
76 KiB
Plaintext
/**********************************************************************
|
|
* Copyright (c)2000-2002 Sony Online Entertainment Inc.
|
|
* All Rights Reserved
|
|
*
|
|
* Title: xp.scriptlib
|
|
* Description: experience type defines and xp-related function library
|
|
* @author $Author:$
|
|
* @version $Revision:$
|
|
**********************************************************************/
|
|
|
|
/***** INCLUDES ********************************************************/
|
|
|
|
include java.lang.Math;
|
|
include java.util.*;
|
|
include library.beast_lib;
|
|
include library.buff;
|
|
include library.collection;
|
|
include library.combat;
|
|
include library.gcw;
|
|
include library.group;
|
|
include library.performance;
|
|
include library.pet_lib;
|
|
include library.space_flags;
|
|
include library.utils;
|
|
|
|
/***** CONSTANTS *******************************************************/
|
|
|
|
const float COMBAT_GENERAL_EXCHANGE_RATE = 0.10f; // 10% of normal xp
|
|
const float JEDI_GENERAL_EXCHANGE_RATE = 0.18f; // 18% of normal xp
|
|
const float SOCIAL_ENTERTAINER_EXCHANGE_RATE = 1.0f; // 100% of normal xp
|
|
const float CRAFTING_MERCHANT_EXCHANGE_RATE = 0.25f; // 25% of normal xp
|
|
|
|
const float SQUADLEADER_XP_RATIO = 1.0f;
|
|
const float SOCIAL_COMBAT_XP_MOD = 1.0f;
|
|
|
|
const string VAR_SQUADLEADER_BASE = "squadleader";
|
|
const string VAR_SQUADLEADER_XP = VAR_SQUADLEADER_BASE + ".xp";
|
|
const string VAR_SQUADLEADER_AT_CAP = VAR_SQUADLEADER_BASE + ".atCap";
|
|
const string VAR_SQUADLEADER_GROUP_SIZE = VAR_SQUADLEADER_BASE + ".groupSize";
|
|
|
|
const string XP = "xp";
|
|
|
|
const string WEAPON_TYPE = "weaponType";
|
|
|
|
const string GRANT_XP_RESULT_VALUE = "delta";
|
|
const string GRANT_XP_RETURN_DATA = "data";
|
|
|
|
//*********** XP DEFINES ******************************
|
|
const float FACTIONAL_WINNING_XP_BONUS = 1.15f;
|
|
|
|
//PROFESSION XP TYPES
|
|
const string SCOUT = "scout";
|
|
const string TRAPPING = "trapping";
|
|
const string BOUNTYHUNTER = "bountyhunter";
|
|
const string SMUGGLER = "smuggler";
|
|
const string SQUADLEADER = "squadleader";
|
|
|
|
const string SLICING = "slicing";
|
|
|
|
const string IMAGEDESIGNER = "imagedesigner";
|
|
const string RANGER = "ranger";
|
|
|
|
const string FARMER = "farmer";
|
|
const string INDUSTRIALIST = "industralist";
|
|
const string BIO_ENGINEER = "bio_engineer";
|
|
const string BIO_ENGINEER_DNA_HARVESTING = "bio_engineer_dna_harvesting";
|
|
|
|
//RESOURCE
|
|
const string HARVEST_ORGANIC = "resource_harvesting_organic";
|
|
const string HARVEST_INORGANIC = "resource_harvesting_inorganic";
|
|
|
|
//CRAFTING
|
|
const string CRAFTING_GENERAL = "crafting_general";
|
|
const string CRAFTING_FOOD_GENERAL = "crafting_food_general";
|
|
const string CRAFTING_MEDICINE_GENERAL = "crafting_medical_general";
|
|
const string CRAFTING_CLOTHING_ARMOR = "crafting_clothing_armor";
|
|
const string CRAFTING_CLOTHING_GENERAL = "crafting_clothing_general";
|
|
const string CRAFTING_WEAPONS_GENERAL = "crafting_weapons_general";
|
|
const string CRAFTING_WEAPONS_MELEE = "crafting_weapons_melee";
|
|
const string CRAFTING_WEAPONS_RANGED = "crafting_weapons_ranged";
|
|
const string CRAFTING_WEAPONS_MUNITION = "crafting_weapons_munition";
|
|
const string CRAFTING_STRUCTURE_GENERAL = "crafting_structure_general";
|
|
const string CRAFTING_DROID_GENERAL = "crafting_droid_general";
|
|
const string CRAFTING_SHIPWRIGHT = "shipwright";
|
|
|
|
const string MERCHANT = "merchant";
|
|
|
|
const string CRAFTING_BIO_ENGINEER_CREATURE = "crafting_bio_engineer_creature";
|
|
const string CRAFTING_BIO_ENGINEER_TISSUE = "crafting_bio_engineer_tissue";
|
|
|
|
// JEDI
|
|
const string COMBAT_JEDI_ONEHANDLIGHTSABER = "combat_meleespecialize_onehandlightsaber";
|
|
const string COMBAT_JEDI_TWOHANDLIGHTSABER = "combat_meleespecialize_twohandlightsaber";
|
|
const string COMBAT_JEDI_POLEARMLIGHTSABER = "combat_meleespecialize_polearmlightsaber";
|
|
const string COMBAT_JEDI_FORCE_POWER = "jedi_force";
|
|
const string JEDI_GENERAL = "jedi_general";
|
|
|
|
//COMBAT
|
|
const string COMBAT_GENERAL = "combat_general";
|
|
|
|
const string COMBAT_MELEESPECIALIZE_UNARMED = "combat_meleespecialize_unarmed";
|
|
const string COMBAT_MELEESPECIALIZE_ONEHAND = "combat_meleespecialize_onehand";
|
|
const string COMBAT_MELEESPECIALIZE_TWOHAND = "combat_meleespecialize_twohand";
|
|
const string COMBAT_MELEESPECIALIZE_POLEARM = "combat_meleespecialize_polearm";
|
|
|
|
const string COMBAT_RANGEDSPECIALIZE_RIFLE = "combat_rangedspecialize_rifle";
|
|
const string COMBAT_RANGEDSPECIALIZE_PISTOL = "combat_rangedspecialize_pistol";
|
|
const string COMBAT_RANGEDSPECIALIZE_CARBINE = "combat_rangedspecialize_carbine";
|
|
const string COMBAT_RANGEDSPECIALIZE_HEAVY = "combat_rangedspecialize_heavy";
|
|
|
|
const string COMBAT_THROWN = "combat_thrown";
|
|
const string COMBAT_GRENADE = "combat_grenade";
|
|
|
|
const string CREATUREHANDLER = "creaturehandler";
|
|
const string COMBATMEDIC = "combatmedic";
|
|
const string MEDICAL = "medical";
|
|
|
|
const string SPACE_COMBAT_GENERAL = "space_combat_general";
|
|
const string SPACE_PRESTIGE_IMPERIAL = "prestige_imperial";
|
|
const string SPACE_PRESTIGE_REBEL = "prestige_rebel";
|
|
const string SPACE_PRESTIGE_PILOT = "prestige_pilot";
|
|
|
|
//ENTERTAINMENT
|
|
const string MUSIC = "music";
|
|
const string DANCE = "dance";
|
|
const string JUGGLING = "juggling";
|
|
const string ENTERTAINER = "entertainer";
|
|
|
|
//QUEST REWARDS
|
|
const string QUEST_SOCIAL = "quest_social";
|
|
const string QUEST_COMBAT = "quest_combat";
|
|
const string QUEST_CRAFTING = "quest_crafting";
|
|
const string QUEST_GENERAL = "quest_general";
|
|
|
|
//APPRENTICESHIP
|
|
const string APPRENTICESHIP = "apprenticeship";
|
|
|
|
//OTHER
|
|
const string PERMISSIONS_ONLY = "permissions_only";
|
|
const string RAW_COMBAT = "raw_combat";
|
|
const string PARTIAL_COMBAT = "partial_combat";
|
|
const string PET_DAMAGE = "pet_damage";
|
|
|
|
//POLITICIAN
|
|
const string POLITICAL = "political";
|
|
|
|
//UNKNOWN!
|
|
const string UNKNOWN = "unknown";
|
|
|
|
//*********** XP STRING ID DEFINES *********************
|
|
//STRING TABLE CONSTS
|
|
const string STF_XP_N = "exp_n";
|
|
|
|
const string_id SID_SCOUT = new string_id(STF_XP_N, SCOUT);
|
|
|
|
const string_id SID_HARVEST_ORGANIC = new string_id(STF_XP_N, HARVEST_ORGANIC);
|
|
const string_id SID_HARVEST_INORGANIC = new string_id(STF_XP_N, HARVEST_INORGANIC);
|
|
|
|
//*********** PROSE PACKAGE STRING ID DEFINES *********************
|
|
const string_id PROSE_GRANT_XP = new string_id("base_player", "prose_grant_xp");
|
|
const string_id PROSE_GRANT_GROUP_XP = new string_id("base_player", "prose_grant_group_xp");
|
|
const string_id PROSE_GRANT_BUFF_XP = new string_id("base_player", "prose_grant_buff_xp");
|
|
const string_id PROSE_GRANT_GROUP_BUFF_XP = new string_id("base_player", "prose_grant_group_buff_xp");
|
|
const string_id PROSE_REVOKE_XP = new string_id("base_player", "prose_revoke_xp");
|
|
const string_id PROSE_GRANT_XP_BONUS = new string_id("base_player", "prose_grant_xp_bonus");
|
|
|
|
const string_id PROSE_GRANT_XP1 = new string_id("base_player", "prose_grant_xp1");
|
|
const string_id PROSE_REVOKE_XP1 = new string_id("base_player", "prose_revoke_xp1");
|
|
|
|
//*********** CREDIT FOR KILLS DEFINES *********************
|
|
//BASE VARS
|
|
const string VAR_CREDIT_FOR_KILLS = "creditForKills";
|
|
const string VAR_NPC_DAMAGE = VAR_CREDIT_FOR_KILLS + ".npcDamage";
|
|
const string VAR_ATTACKER_LIST = VAR_CREDIT_FOR_KILLS + ".attackerList"; //to avoid name collisions
|
|
const string VAR_DAMAGE_TALLY = VAR_CREDIT_FOR_KILLS + ".damageTally"; //total damage done to target
|
|
const string VAR_DAMAGE_COUNT = VAR_CREDIT_FOR_KILLS + ".damageCount"; //number of hits tracked
|
|
const string VAR_COMBAT_TIMESTAMP = VAR_CREDIT_FOR_KILLS + ".timestamp"; //time combat started
|
|
|
|
// VARIABLE ADJUSTERS
|
|
const int MAX_PLAYERS = 30; //the limit to how many players receive XP
|
|
const int MAX_DISTANCE = 190; //this is the distance between the target and the player
|
|
const double PRIM_KILL_PERCENT = .2; //at least within this percent difference to top damage inflictor
|
|
const double PERCENT_ADJUSTER = .0; //adjusts a bonus percentage for all players in attacker list prior to XP delegation
|
|
const double PLAYER_RATIO_ADJUST = .1; //adjusts a bonus percentage for the player ratio of player damage to top player damage
|
|
const float COMBAT_XP_EXCHANGE = .10f;
|
|
|
|
const float MAX_NPC_DAMAGE_PERCENT = 0.65f;
|
|
const float PERCENT_DAMAGE_BAR_BASE = 0.00f;
|
|
|
|
const float GROUP_XP_BONUS = 0.35f;
|
|
const float GROUP_XP_DIVIDER = 0.6f;
|
|
const int MAX_GROUP_BONUS_COUNT = 8;
|
|
|
|
//Trader and Entertainer xp modifiers
|
|
//Added 2-28-07 as these mods were pulled out of c and placed into script
|
|
const float TRADER_XP_MOD = 7.6f;
|
|
const float ENTERTAINER_XP_MOD = 3.4f;
|
|
|
|
//CREDIT RESULTS
|
|
const string VAR_COMBAT_RESULTS = "combatResults";
|
|
const string VAR_TOP_DAMAGE = VAR_COMBAT_RESULTS + ".top_damage";
|
|
const string VAR_TOP_GROUP = VAR_COMBAT_RESULTS + ".top_group";
|
|
|
|
const string VAR_TOP_DAMAGERS = VAR_COMBAT_RESULTS + ".top_damagers";
|
|
const string VAR_LANDED_DEATHBLOW = VAR_COMBAT_RESULTS + ".landed_deathblow";
|
|
|
|
//HANDLER DEFINES
|
|
const string HANDLER_XP_DELEGATED = "xpDelegated";
|
|
|
|
//*********** SPECIES XP MOD DEFINES *********************
|
|
|
|
const string TBL_SKILL = "datatables/skill/skills.iff";
|
|
const string TBL_SPECIES_XP = "datatables/xp/species.iff";
|
|
const string TBL_PLAYER_LEVEL_XP = "datatables/player/player_level.iff";
|
|
|
|
//************collection****************************
|
|
|
|
const string CREATURES_TABLE = "datatables/mob/creatures.iff";
|
|
|
|
//*********** BUFF DEFINES *********************
|
|
|
|
const string_id SID_INSPIRE_BONUS = new string_id("performance", "perform_inspire_xp_bonus");
|
|
|
|
const string_id SID_FLYTEXT_XP = new string_id("base_player", "prose_flytext_xp");
|
|
const string_id SID_FLYTEXT_XP_GROUP = new string_id("base_player", "prose_flytext_xp_group");
|
|
|
|
//************ FREE TRIAL *************************************
|
|
const int TRIAL_LEVEL_CAP = 25;
|
|
const string_id SID_FREE_TRIAL_LEVEL_CAP = new string_id("base_player", "free_trial_level_cap");
|
|
|
|
/***** FUNCTIONS **************************************************/
|
|
|
|
int grant(obj_id target, string xp_type, int amt)
|
|
{
|
|
return grant(target, xp_type, amt, true, null, null, null);
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, string callback)
|
|
{
|
|
return grant(target, xp_type, amt, true, callback, new dictionary(), getSelf());
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, string callback, dictionary callbackData)
|
|
{
|
|
return grant(target, xp_type, amt, true, callback, callbackData, getSelf());
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, string callback, dictionary callbackData, obj_id callbackId)
|
|
{
|
|
return grant(target, xp_type, amt, true, callback, callbackData, callbackId);
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, boolean verbose)
|
|
{
|
|
return grant(target, xp_type, amt, verbose, null, null, null);
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, boolean verbose, string callback, dictionary callbackData)
|
|
{
|
|
return grant(target, xp_type, amt, verbose, callback, callbackData, getSelf());
|
|
}
|
|
|
|
int grant(obj_id target, string xp_type, int amt, boolean verbose, string callback, dictionary callbackData, obj_id callbackId)
|
|
{
|
|
//LOG("xp", "grant(" + target + ", " + xp_type + ", " + amt + ", " + verbose + ", " + callback + ", " + callbackData + ", " + callbackId + ")");
|
|
|
|
if(!isIdValid(target) || (xp_type == null) || (xp_type.equals("")))
|
|
return 0;
|
|
|
|
if(xp_type.indexOf(" ") > -1)
|
|
return 0;
|
|
|
|
amt = applySpeciesXpModifier(target, xp_type, amt);
|
|
|
|
// Group modifier is applied elsewhere specifically for combat kills only.
|
|
//amt = applyGroupXpModifier(target, amt);
|
|
|
|
amt = applyInspirationBuffXpModifier(target, xp_type, amt);
|
|
|
|
if(amt == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(grantUnmodifiedExperience(target, xp_type, amt, verbose, callback, callbackData, callbackId))
|
|
{
|
|
return amt;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt)
|
|
{
|
|
return grantUnmodifiedExperience(target, xp_type, amt, true, null, null, null);
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt, boolean verbose)
|
|
{
|
|
return grantUnmodifiedExperience(target, xp_type, amt, verbose, null, null, null);
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt, string callback)
|
|
{
|
|
return grantUnmodifiedExperience(target, xp_type, amt, true, callback, new dictionary(), getSelf());
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt, string callback, dictionary callbackData)
|
|
{
|
|
return grantUnmodifiedExperience(target, xp_type, amt, true, callback, callbackData, getSelf());
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt, string callback, dictionary callbackData, obj_id callbackId)
|
|
{
|
|
return grantUnmodifiedExperience(target, xp_type, amt, true, callback, callbackData, callbackId);
|
|
}
|
|
|
|
boolean grantUnmodifiedExperience(obj_id target, string xp_type, int amt, boolean verbose, string callback, dictionary callbackData, obj_id callbackId)
|
|
{
|
|
//LOG("xp", "grantUnmodifiedExperience(" + target + ", " + xp_type + ", " + amt + ", " + verbose + ", " + callback + ", " + callbackData + ", " + callbackId + ")");
|
|
|
|
if(!isIdValid(target) || (xp_type == null) || (xp_type.equals("")))
|
|
return false;
|
|
|
|
if(xp_type.indexOf(" ") > -1)
|
|
return false;
|
|
|
|
// target may be non-authoritative, and we need to grant the xp on the authoritative object,
|
|
// so we send a message to the target to have it grant the xp on itself
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("xp_type", xp_type);
|
|
params.put("amt", amt);
|
|
|
|
if(callback != null && callback.length() > 0)
|
|
{
|
|
params.put("fromId", callbackId);
|
|
params.put("fromCallback", callback);
|
|
if(callbackData == null)
|
|
callbackData = new dictionary();
|
|
params.put("fromCallbackData", callbackData);
|
|
}
|
|
|
|
messageTo(target, "grantUnmodifiedExperienceOnSelf", params, 0.1f, !target.isAuthoritative());
|
|
|
|
if(verbose)
|
|
{
|
|
displayXpMsg(target, xp_type, amt);
|
|
}
|
|
|
|
int currentXp = getExperiencePoints(target, xp_type);
|
|
int xpCap = getExperienceCap(target, xp_type);
|
|
|
|
return currentXp < xpCap;
|
|
}
|
|
|
|
/**
|
|
* Called ONLY by base_player.grantUnmodifiedExperienceOnSelf() on authoritative target.
|
|
*/
|
|
boolean _grantUnmodifiedExperience(obj_id target, string xp_type, int amt, string callback, dictionary callbackData, obj_id callbackId)
|
|
{
|
|
//LOG("xp", "_grantUnmodifiedExperience(" + target + ", " + xp_type + ", " + amt + ", " + callback + ", " + callbackData + ", " + callbackId + ")");
|
|
|
|
int delta = 0;
|
|
int prior = getExperiencePoints(target, xp_type);
|
|
|
|
boolean result = false;
|
|
|
|
if(grantExperiencePoints(target, xp_type, amt) != XP_ERROR)
|
|
{
|
|
result = true;
|
|
|
|
int current = getExperiencePoints(target, xp_type);
|
|
|
|
delta = current - prior;
|
|
|
|
if(skill_template.isQualifiedForWorkingSkill(target))
|
|
{
|
|
skill_template.earnWorkingSkill(target);
|
|
}
|
|
|
|
metrics.doXpRateMetrics(target, xp_type, amt);
|
|
}
|
|
|
|
if(isIdValid(callbackId))
|
|
{
|
|
dictionary returnParams = new dictionary();
|
|
|
|
returnParams.put(GRANT_XP_RESULT_VALUE, delta);
|
|
if(callbackData != null)
|
|
{
|
|
returnParams.put(GRANT_XP_RETURN_DATA, callbackData);
|
|
}
|
|
messageTo(callbackId, callback, returnParams, 0.1f, !callbackId.isAuthoritative());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean grantCraftingXpChance(obj_id item, obj_id user, int chance)
|
|
{
|
|
// only remove these objvars, this is cleanup for objects created before the partial usage xp was removed
|
|
if(!isIdValid(item))
|
|
return false;
|
|
|
|
removeObjVar(item, "crafting.creator.xp");
|
|
removeObjVar(item, "crafting.creator.pxp");
|
|
|
|
return true;
|
|
}
|
|
|
|
int applySpeciesXpModifier(obj_id target, string xp_type, int amt)
|
|
{
|
|
//LOG("xp", "applySpeciesXpModifier(" + target + ", " + xp_type + ", " + amt + ")");
|
|
|
|
xp_type = toLower(xp_type);
|
|
int mod_xp = (int) (amt * getSpeciesXpModifier(target, xp_type));
|
|
|
|
if(utils.hasScriptVar(target, "buff.dish_ormachek.value"))
|
|
{
|
|
float xp_mod = utils.getFloatScriptVar(target, "buff.dish_ormachek.value");
|
|
float f_mod = 1f + (xp_mod / 100f);
|
|
|
|
int dur = 20;
|
|
|
|
if(utils.hasScriptVar(target, "buff.dish_ormachek.duration"))
|
|
dur = (int) utils.getFloatScriptVar(target, "buff.dish_ormachek.duration");
|
|
|
|
dur--;
|
|
if(dur <= 0)
|
|
buff.removeBuff(target, "dish_ormachek");
|
|
else
|
|
utils.setScriptVar(target, "buff.dish_ormachek.duration", dur);
|
|
|
|
mod_xp = (int) (mod_xp * f_mod);
|
|
}
|
|
|
|
return mod_xp;
|
|
}
|
|
|
|
int applyGroupXpModifier(obj_id target, int amt)
|
|
{
|
|
//LOG("xp", "applyGroupXpModifier(" + target + ", " + amt + ")");
|
|
|
|
float mod = 1f;
|
|
int modAmt = amt;
|
|
|
|
if(amt > 0)
|
|
{
|
|
obj_id gid = getGroupObject(target);
|
|
int activeGroupSize = getActiveGroupSize(gid); //this handles invalid gid's properly
|
|
//we only want to give the group modifiers to groups of 3 or more
|
|
if(activeGroupSize <= 2)
|
|
{
|
|
return modAmt;
|
|
}
|
|
|
|
//now apply a group bonus for group size, retarded
|
|
mod = getGroupXpModifier(target, activeGroupSize);
|
|
modAmt = Math.round(amt * mod);
|
|
|
|
//modify amt by group size:
|
|
modAmt = (int) (modAmt / (1 + ((activeGroupSize - 1) * GROUP_XP_DIVIDER)));
|
|
|
|
}
|
|
|
|
// Remember group xp modifier for later use in flytext / system messages.
|
|
utils.setScriptVar(target, "combat.xp.groupBonus", mod);
|
|
|
|
return modAmt;
|
|
}
|
|
|
|
int applyInspirationBuffXpModifier(obj_id target, string xp_type, int amt)
|
|
{
|
|
//LOG("xp", "applyInspirationBuffXpModifier(" + target + ", " + xp_type + ", " + amt + ")");
|
|
|
|
if(amt > 0)
|
|
{
|
|
float mod = getInspirationBuffXpModifier(target, xp_type);
|
|
float modAmt = (float) amt * mod;
|
|
|
|
return Math.round(modAmt);
|
|
}
|
|
else
|
|
{
|
|
return amt;
|
|
}
|
|
}
|
|
|
|
float getGroupXpModifier(obj_id target, int activeGroupSize)
|
|
{
|
|
//LOG("xp", "getGroupXpModifier(" + target + ")");
|
|
|
|
float bonusMod = 1f;
|
|
|
|
float groupXPBonus = GROUP_XP_BONUS;
|
|
|
|
if(activeGroupSize > 1)
|
|
{
|
|
bonusMod += groupXPBonus * activeGroupSize;
|
|
}
|
|
|
|
return bonusMod;
|
|
}
|
|
|
|
float getSpeciesXpModifier(obj_id target, string xp_type)
|
|
{
|
|
//LOG("xp", "getSpeciesXpModifier(" + target + ", " + xp_type + ")");
|
|
|
|
if(!isIdValid(target) || xp_type == null || xp_type.equals(""))
|
|
{
|
|
return 1f;
|
|
}
|
|
|
|
int species = getSpecies(target);
|
|
|
|
string[] xpMods = dataTableGetStringColumn(TBL_SPECIES_XP, species);
|
|
|
|
if(xpMods != null && xpMods.length > 0)
|
|
{
|
|
for(int i = 0; i < xpMods.length; i++)
|
|
{
|
|
if(xpMods[i].startsWith(xp_type))
|
|
{
|
|
string[]s = split(xpMods[i], ':');
|
|
|
|
if((s != null) && (s.length == 2))
|
|
{
|
|
int val = utils.stringToInt(s[1]);
|
|
|
|
if(val != -1)
|
|
{
|
|
return (100f + val) / 100f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1f;
|
|
}
|
|
|
|
float getInspirationBuffXpModifier(obj_id target, string xp_type)
|
|
{
|
|
//old n busted
|
|
//LOG("xp", "getInspirationBuffXpModifier(" + target + ", " + xp_type + ")");
|
|
/*
|
|
if(utils.hasScriptVar(target, "buff.vet_exp_buff_item_buff.value"))
|
|
{
|
|
mod += (utils.getFloatScriptVar(target, "buff.vet_exp_buff_item_buff.value") / 100f);
|
|
}
|
|
|
|
if(utils.hasScriptVar(target, "buff.general_inspiration.value"))
|
|
{
|
|
mod += (utils.getFloatScriptVar(target, "buff.general_inspiration.value") / 100f);
|
|
}
|
|
*/
|
|
|
|
float mod = 1f;
|
|
|
|
// Entertainer build-a-buff xp bonus
|
|
if(utils.hasScriptVar(target, "buff.xpBonus.types"))
|
|
{
|
|
string[] xpList = utils.getStringArrayScriptVar(target, "buff.xpBonus.types");
|
|
|
|
if(xpList != null && xpList.length > 0)
|
|
{
|
|
for(int i = 0; i < xpList.length; i++)
|
|
{
|
|
if(xpList[i].equals(xp_type))
|
|
{
|
|
mod += utils.getFloatScriptVar(target, "buff.xpBonus.value");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// All other xp bonus buffs - yes, this will stack with the build-a-buff one.
|
|
if(utils.hasScriptVar(target, "buff.xpBonusGeneral.types"))
|
|
{
|
|
string[] xpList = utils.getStringArrayScriptVar(target, "buff.xpBonusGeneral.types");
|
|
|
|
if(xpList != null && xpList.length > 0)
|
|
{
|
|
for(int i = 0; i < xpList.length; i++)
|
|
{
|
|
if(xpList[i].equals(xp_type))
|
|
{
|
|
mod += utils.getFloatScriptVar(target, "buff.xpBonusGeneral.value");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
void grantSquadLeaderXp(obj_id player, int amt)
|
|
{
|
|
if(!isIdValid(player) || !player.isLoaded() || amt < 1 || !hasSkill(player, "class_officer_phase1_novice"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
dictionary resultData = new dictionary();
|
|
|
|
resultData.put("player", player);
|
|
resultData.put("amt", amt);
|
|
grant(player, SQUADLEADER, amt, false, "grantSquadLeaderXpResult", resultData, player);
|
|
|
|
return;
|
|
}
|
|
|
|
void grantSquadLeaderXpResult(obj_id player, int granted, int amt)
|
|
{
|
|
if(granted > 0)
|
|
{
|
|
int slxp = utils.getIntScriptVar(player, VAR_SQUADLEADER_XP);
|
|
|
|
slxp += granted;
|
|
utils.setScriptVar(player, VAR_SQUADLEADER_XP, slxp);
|
|
|
|
if(granted < amt)
|
|
{
|
|
utils.setScriptVar(player, VAR_SQUADLEADER_AT_CAP, true);
|
|
}
|
|
|
|
notifySquadLeaderXp(player);
|
|
}
|
|
}
|
|
|
|
void notifySquadLeaderXp(obj_id player)
|
|
{
|
|
if(!isIdValid(player) || !player.isLoaded())
|
|
{
|
|
return;
|
|
}
|
|
|
|
string_id sid_xp = new string_id(STF_XP_N, SQUADLEADER);
|
|
|
|
int slxp = utils.getIntScriptVar(player, VAR_SQUADLEADER_XP);
|
|
|
|
if(slxp > 0)
|
|
{
|
|
prose_package pp = getXpProsePackage(SQUADLEADER, slxp);
|
|
|
|
if(pp != null)
|
|
{
|
|
sendSystemMessageProse(player, pp);
|
|
}
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, VAR_SQUADLEADER_AT_CAP))
|
|
{
|
|
prose_package ppAtCap = prose.getPackage(new string_id("base_player", "prose_hit_xp_cap"), sid_xp);
|
|
|
|
sendSystemMessageProse(player, ppAtCap);
|
|
}
|
|
|
|
utils.removeScriptVarTree(player, VAR_SQUADLEADER_BASE);
|
|
}
|
|
|
|
/***** COMBAT XP TRACKING FUNCTIONS ******************************/
|
|
int getRawCombatXP(obj_id player, obj_id npc)
|
|
{
|
|
//LOG("xp", "getRawCombatXP(" + player + ", " + npc + ")");
|
|
|
|
if(isMob(npc) && (isPlayer(npc) || isIdValid(getMaster(npc))))
|
|
return 0;
|
|
|
|
float baseCombatXP = getLevelBasedXP(player, npc);
|
|
|
|
return Math.round(baseCombatXP);
|
|
}
|
|
|
|
int getActiveGroupSize(obj_id gid)
|
|
{
|
|
if(!group.isGroupObject(gid))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
obj_id[] members = getGroupMemberIds(gid);
|
|
|
|
if(members == null || members.length == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int count = 0;
|
|
|
|
for(int i = 0; i < members.length; i++)
|
|
{
|
|
if(members[i].isLoaded() && isPlayer(members[i]))
|
|
{
|
|
count++;
|
|
}
|
|
|
|
if(count >= MAX_GROUP_BONUS_COUNT)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
int getLevelBasedXP(obj_id player, obj_id npc)
|
|
{
|
|
int level = getLevel(player);
|
|
int levelDiff = combat.getAiLevelDiff(npc, player);
|
|
|
|
if(levelDiff > 0)
|
|
{
|
|
level += levelDiff;
|
|
}
|
|
|
|
int xp = getLevelBasedXP(level);
|
|
|
|
if(!isMob(npc))
|
|
{
|
|
xp = getIntObjVar(npc, "combat.intCombatXP");
|
|
}
|
|
|
|
float bonus = 0f;
|
|
|
|
if(aiIsKiller(npc) || aiIsAggressive(npc) || aiIsAssist(npc))
|
|
{
|
|
bonus += 0.05f;
|
|
}
|
|
|
|
xp += (int) (xp * bonus);
|
|
|
|
if(levelDiff < 0)
|
|
{
|
|
// every 6 levels over 20, you get another level point
|
|
float maxLevelDiff = 10f;
|
|
|
|
if(level > 20)
|
|
{
|
|
maxLevelDiff += (int) ((level - 20) / 6);
|
|
}
|
|
|
|
xp += (int) (xp * (levelDiff / maxLevelDiff));
|
|
|
|
if(xp < 1)
|
|
{
|
|
xp = 1;
|
|
}
|
|
}
|
|
|
|
return xp;
|
|
}
|
|
|
|
int getLevelBasedXP(int level)
|
|
{
|
|
if(level < 1)
|
|
{
|
|
level = 1;
|
|
}
|
|
|
|
return dataTableGetInt("datatables/mob/stat_balance.iff", level - 1, "XP");
|
|
}
|
|
|
|
/***** WEAPON XP TYPE FUNCTIONS ******************************/
|
|
/********************************************************************************************
|
|
* @brief returns a string name for xp type
|
|
*
|
|
* @param int weapon_type
|
|
*
|
|
*********************************************************************************************/
|
|
|
|
string getWeaponXpType(int weapon_type)
|
|
{
|
|
string xp_type = "unknown_xp";
|
|
|
|
switch (weapon_type)
|
|
{
|
|
case WEAPON_TYPE_RIFLE:
|
|
xp_type = COMBAT_RANGEDSPECIALIZE_RIFLE;
|
|
break;
|
|
case WEAPON_TYPE_LIGHT_RIFLE:
|
|
xp_type = COMBAT_RANGEDSPECIALIZE_CARBINE;
|
|
break;
|
|
case WEAPON_TYPE_PISTOL:
|
|
xp_type = COMBAT_RANGEDSPECIALIZE_PISTOL;
|
|
break;
|
|
case WEAPON_TYPE_HEAVY:
|
|
case WEAPON_TYPE_GROUND_TARGETTING:
|
|
case WEAPON_TYPE_DIRECTIONAL:
|
|
xp_type = COMBAT_RANGEDSPECIALIZE_RIFLE;
|
|
break;
|
|
case WEAPON_TYPE_1HAND_MELEE:
|
|
xp_type = COMBAT_MELEESPECIALIZE_ONEHAND;
|
|
break;
|
|
case WEAPON_TYPE_2HAND_MELEE:
|
|
xp_type = COMBAT_MELEESPECIALIZE_TWOHAND;
|
|
break;
|
|
case WEAPON_TYPE_UNARMED:
|
|
xp_type = COMBAT_MELEESPECIALIZE_UNARMED;
|
|
break;
|
|
case WEAPON_TYPE_POLEARM:
|
|
xp_type = COMBAT_MELEESPECIALIZE_POLEARM;
|
|
break;
|
|
case WEAPON_TYPE_THROWN:
|
|
xp_type = COMBAT_GRENADE;
|
|
break;
|
|
case WEAPON_TYPE_WT_1HAND_LIGHTSABER:
|
|
xp_type = COMBAT_MELEESPECIALIZE_ONEHAND;
|
|
break;
|
|
case WEAPON_TYPE_WT_2HAND_LIGHTSABER:
|
|
xp_type = COMBAT_MELEESPECIALIZE_TWOHAND;
|
|
break;
|
|
case WEAPON_TYPE_WT_POLEARM_LIGHTSABER:
|
|
xp_type = COMBAT_MELEESPECIALIZE_POLEARM;
|
|
break;
|
|
case combat.WEAPON_TYPE_FORCE_POWER:
|
|
xp_type = COMBAT_MELEESPECIALIZE_UNARMED;
|
|
break;
|
|
default:
|
|
xp_type = UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return xp_type;
|
|
}
|
|
|
|
string getWeaponStringFromXPType(string strXPType)
|
|
{
|
|
if(strXPType == COMBAT_RANGEDSPECIALIZE_RIFLE)
|
|
{
|
|
return "rifle";
|
|
}
|
|
else if(strXPType == COMBAT_RANGEDSPECIALIZE_CARBINE)
|
|
{
|
|
return "carbine";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_RANGEDSPECIALIZE_PISTOL)
|
|
{
|
|
return "pistol";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_RANGEDSPECIALIZE_HEAVY)
|
|
{
|
|
return "heavy";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_MELEESPECIALIZE_ONEHAND)
|
|
{
|
|
return "onehandmelee";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_MELEESPECIALIZE_TWOHAND)
|
|
{
|
|
return "twohandmelee";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_MELEESPECIALIZE_UNARMED)
|
|
{
|
|
return "unarmed";
|
|
}
|
|
else if(strXPType == COMBAT_MELEESPECIALIZE_POLEARM)
|
|
{
|
|
return "polearm";
|
|
}
|
|
else if(strXPType == COMBAT_JEDI_ONEHANDLIGHTSABER)
|
|
{
|
|
return "onehandmelee";
|
|
}
|
|
else if(strXPType == COMBAT_JEDI_TWOHANDLIGHTSABER)
|
|
{
|
|
return "twohandmelee";
|
|
}
|
|
|
|
else if(strXPType == COMBAT_JEDI_POLEARMLIGHTSABER)
|
|
{
|
|
return "polearm";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/***** CREDIT FOR KILLS FUNCTIONS ******************************/
|
|
/********************************************************************************************
|
|
* @brief setup function for the script (for testing mostly)
|
|
*
|
|
* @param obj_id self
|
|
*
|
|
*********************************************************************************************/
|
|
boolean setupCreditForKills()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/********************************************************************************************
|
|
* @brief removes objvarlists, etc...
|
|
*
|
|
* @param int weapon_type
|
|
*
|
|
*********************************************************************************************/
|
|
boolean cleanupCreditForKills()
|
|
{
|
|
obj_id self = getSelf();
|
|
|
|
utils.removeScriptVarTree(self, VAR_CREDIT_FOR_KILLS);
|
|
return true;
|
|
}
|
|
|
|
/********************************************************************************************
|
|
* @brief updates the attacker list with the correct amount of data for the current attacker
|
|
*
|
|
* @param obj_id target
|
|
* @param obj_id attacker
|
|
* @param obj_id wpn
|
|
* @param int dam
|
|
*
|
|
*********************************************************************************************/
|
|
void updateCombatXpList(obj_id target, obj_id attacker, obj_id wpn, int dam)
|
|
{
|
|
if(!isIdValid(target) || !isIdValid(attacker) || !isIdValid(wpn))
|
|
return;
|
|
|
|
int weapon_type = getWeaponType(wpn); //what kind of weapon is it?
|
|
|
|
string strTemplateName = getTemplateName(wpn);
|
|
|
|
if(strTemplateName == null || strTemplateName.equals(""))
|
|
{
|
|
LOG("DESIGNER_FATAL", "xp.updateCombatXpList: unable to getTemplateName(wpn) -> " + wpn + ":" + getName(wpn));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if(hasObjVar(wpn, WEAPON_TYPE))
|
|
weapon_type = getIntObjVar(wpn, WEAPON_TYPE);
|
|
}
|
|
|
|
string xp_type = getWeaponXpType(weapon_type); //translates into xp type
|
|
|
|
if(xp_type == null || xp_type.equals(""))
|
|
{
|
|
LOG("DESIGNER_FATAL", "xp.updateCombatXpList: unable to getWeaponXpType(wpn) -> " + wpn + ":" + getName(wpn));
|
|
return;
|
|
}
|
|
|
|
updateCombatXpList(target, attacker, xp_type, dam);
|
|
}
|
|
|
|
void updateCombatXpList(obj_id target, obj_id attacker, string xp_type, int dam)
|
|
{
|
|
if(!isIdValid(target) || !isIdValid(attacker) || xp_type == null || xp_type.equals(""))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!utils.hasScriptVar(target, VAR_COMBAT_TIMESTAMP)) //track time combat started
|
|
{
|
|
utils.setScriptVar(target, VAR_COMBAT_TIMESTAMP, getGameTime());
|
|
}
|
|
|
|
int tally = utils.getIntScriptVar(target, VAR_DAMAGE_TALLY); //how much damage has been done?
|
|
int newTally = dam + tally;
|
|
|
|
utils.setScriptVar(target, VAR_DAMAGE_TALLY, newTally);
|
|
|
|
int hitCount = utils.getIntScriptVar(target, VAR_DAMAGE_COUNT); //how many times has it been hit?
|
|
|
|
hitCount++;
|
|
utils.setScriptVar(target, VAR_DAMAGE_COUNT, hitCount);
|
|
|
|
if(!isPlayer(attacker) && !pet_lib.isPet(attacker) && !beast_lib.isBeast(attacker) && ai_lib.getDifficultyClass(target) != ai_lib.DIFFICULTY_BOSS)
|
|
{
|
|
int npcDamage = utils.getIntScriptVar(target, VAR_NPC_DAMAGE);
|
|
|
|
npcDamage += dam;
|
|
utils.setScriptVar(target, VAR_NPC_DAMAGE, npcDamage);
|
|
return;
|
|
}
|
|
|
|
if(dam > 0)
|
|
{
|
|
resizeable obj_id[] attackerList = utils.getResizeableObjIdBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers");
|
|
|
|
if(attackerList != null && attackerList.length > 0)
|
|
{
|
|
if(utils.getElementPositionInArray(attackerList, attacker) == -1)
|
|
{
|
|
attackerList = utils.addElement(attackerList, attacker);
|
|
utils.setBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers", attackerList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
attackerList = utils.addElement(attackerList, attacker);
|
|
utils.setBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers", attackerList);
|
|
}
|
|
|
|
string basePath = VAR_ATTACKER_LIST + "." + attacker;
|
|
string damPath = basePath + ".damage";
|
|
|
|
int totalDamage = dam + utils.getIntScriptVar(target, damPath); //add any previous damage done by player
|
|
|
|
utils.setScriptVar(target, damPath, totalDamage); //setting the player's net Id as a String = key and damage = value
|
|
|
|
string xpListBasePath = basePath + ".xp";
|
|
string xpListPath = xpListBasePath + ".types";
|
|
resizeable string[] xpTypes = utils.getResizeableStringBatchScriptVar(target, xpListPath);
|
|
|
|
if(xpTypes != null && xpTypes.length > 0)
|
|
{
|
|
if(utils.getElementPositionInArray(xpTypes, xp_type) == -1)
|
|
{
|
|
xpTypes = utils.addElement(xpTypes, xp_type);
|
|
utils.setBatchScriptVar(target, xpListPath, xpTypes);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xpTypes = utils.addElement(xpTypes, xp_type);
|
|
utils.setBatchScriptVar(target, xpListPath, xpTypes);
|
|
}
|
|
|
|
string xpTypePath = xpListBasePath + "." + xp_type;
|
|
int xpTally = dam + utils.getIntScriptVar(target, xpTypePath);
|
|
|
|
utils.setScriptVar(target, xpTypePath, xpTally);
|
|
|
|
}
|
|
}
|
|
|
|
void removeXpListCombatant(obj_id target, obj_id combatant)
|
|
{
|
|
if(!isIdValid(target) || !isIdValid(combatant))
|
|
{
|
|
return;
|
|
}
|
|
|
|
resizeable obj_id[] attackerList = utils.getResizeableObjIdBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers");
|
|
|
|
if(attackerList == null || attackerList.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int idx = utils.getElementPositionInArray(attackerList, combatant);
|
|
|
|
if(idx == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
attackerList = utils.removeElementAt(attackerList, idx);
|
|
|
|
if(attackerList == null || attackerList.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
utils.setBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers", attackerList);
|
|
utils.removeScriptVarTree(target, VAR_ATTACKER_LIST + "." + combatant);
|
|
}
|
|
|
|
/********************************************************************************************
|
|
* @brief attempts to assess combat results for object target
|
|
*
|
|
* @param obj_id target
|
|
* @param obj_id attacker
|
|
* @param obj_id wpn
|
|
* @param int dam
|
|
*
|
|
*********************************************************************************************/
|
|
void assessCombatResults(obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!utils.hasScriptVar(target, VAR_ATTACKER_LIST + ".attackers"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(utils.hasScriptVar(target, "storytellerid") || utils.hasObjVar(target, "storytellerid"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int npcDamage = utils.getIntScriptVar(target, VAR_NPC_DAMAGE);
|
|
int tally = utils.getIntScriptVar(target, VAR_DAMAGE_TALLY);//how much damage has been done?
|
|
float npcDamagePercent = npcDamage / (float)tally;
|
|
|
|
if(npcDamagePercent > MAX_NPC_DAMAGE_PERCENT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id[] attackerList = utils.getObjIdBatchScriptVar(target, VAR_ATTACKER_LIST + ".attackers");
|
|
|
|
if(attackerList == null || attackerList.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
attackerList = utils.validateObjIdArray(attackerList);
|
|
|
|
obj_var[] damArray = new obj_var[attackerList.length];
|
|
|
|
for(int i = 0; i < attackerList.length; i++)
|
|
{
|
|
damArray[i] = new obj_var(attackerList[i].toString(), utils.getIntScriptVar(target, VAR_ATTACKER_LIST + "." + attackerList[i] + ".damage"));
|
|
}
|
|
|
|
if(damArray == null || damArray.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_var[] killers = list.quickSort(0, attackerList.length - 1, damArray);
|
|
|
|
if((killers == null) || (killers.length == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get which individual(s) did the top amount of damage
|
|
obj_id[] topDamagers = getTopIndividualAttacker(killers);
|
|
|
|
if(topDamagers != null && topDamagers.length > 0)
|
|
{
|
|
setObjVar(target, VAR_TOP_DAMAGERS, topDamagers);
|
|
}
|
|
|
|
//sendSystemMessageTestingOnly(obj_id.getObjId(10235263), "Top Damager="+topDamagers);
|
|
|
|
//COMPILE GROUP DAMAGE
|
|
dictionary groupDamage = new dictionary();
|
|
int topDamage = Integer.MIN_VALUE;
|
|
int gDam = 0;
|
|
|
|
for(int i = 0; i < killers.length; i++)
|
|
{
|
|
obj_var attackerDamageVar = killers[i];
|
|
obj_id attacker = utils.stringToObjId(attackerDamageVar.getName());
|
|
|
|
if(isIdValid(attacker))
|
|
{
|
|
int attackerDamage = attackerDamageVar.getIntData();
|
|
|
|
gDam = updateGroupDamageDictionary(groupDamage, attacker, attackerDamage, target);
|
|
|
|
if(gDam > topDamage)
|
|
{
|
|
topDamage = gDam;
|
|
}
|
|
}
|
|
}
|
|
|
|
//FIND PRIMARY KILLER(S)
|
|
resizeable obj_id[] primaryKillers = new obj_id[0];
|
|
|
|
java.util.Enumeration keys = groupDamage.keys();
|
|
|
|
while(keys.hasMoreElements())
|
|
{
|
|
string key = (string)(keys.nextElement());
|
|
int val = groupDamage.getInt(key);
|
|
|
|
if(val >= topDamage)
|
|
{
|
|
obj_id tmpId = utils.stringToObjId(key);
|
|
|
|
if(isIdValid(tmpId))
|
|
{
|
|
primaryKillers = utils.addElement(primaryKillers, tmpId);
|
|
|
|
if(!group.isGroupObject(tmpId))
|
|
{
|
|
incrementKillMeter(tmpId, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if((primaryKillers == null) || (primaryKillers.length == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//SET RESULTS VARS
|
|
int winnerIdx = rand(0, primaryKillers.length - 1);
|
|
obj_id winner = primaryKillers[winnerIdx];
|
|
|
|
if(!group.isGroupObject(winner) && !isPlayer(winner) && isMob(winner))
|
|
{
|
|
obj_id winnerMaster = getMaster(winner);
|
|
|
|
if(!isIdNull(winnerMaster) && isIdValid(winnerMaster))
|
|
{
|
|
winner = winnerMaster;
|
|
}
|
|
}
|
|
|
|
if(group.isGroupObject(winner))
|
|
{
|
|
for(int i = 0; i < killers.length; i++)
|
|
{
|
|
obj_var attackerDamageVar = killers[i];
|
|
obj_id attacker = utils.stringToObjId(attackerDamageVar.getName());
|
|
|
|
if(isIdValid(attacker))
|
|
{
|
|
if(getGroupObject(attacker) == winner)
|
|
{
|
|
incrementKillMeter(attacker, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
setObjVar(target, VAR_TOP_DAMAGE, topDamage);
|
|
setObjVar(target, VAR_TOP_GROUP, winner);
|
|
|
|
grantQuestKillCredit(winner, target);
|
|
|
|
//DISTRIBUTE APPROPRIATE COMBAT XP
|
|
if(!hasObjVar(target, "combat.zeroXP"))
|
|
{
|
|
obj_id[] killerList = primaryKillers;
|
|
|
|
killers = grantCombatXp(target, killers);
|
|
|
|
obj_id[] playerList = getPlayersFromKillerList(killerList);
|
|
pet_lib.addToHarvestDroidArray(target, playerList);
|
|
|
|
if(killers == null || killers.length == 0)
|
|
{
|
|
setObjVar(target, VAR_TOP_DAMAGE, 0);
|
|
setObjVar(target, VAR_TOP_GROUP, obj_id.NULL_ID);
|
|
|
|
obj_id[] placeHolder = new obj_id[1];
|
|
placeHolder[0] = obj_id.NULL_ID;
|
|
setObjVar(target, scenario.VAR_PRIMARY_KILLERS, placeHolder); // OLD HACK LEFT SO OLD SYSTEMS DONT CHOKE -- Suppress Linting - We know that this array will
|
|
return; // always be at least one element long
|
|
}
|
|
}
|
|
|
|
setObjVar(target, scenario.VAR_PRIMARY_KILLERS, primaryKillers); //OLD HACK LEFT SO OLD SYSTEMS DONT CHOKE
|
|
}
|
|
|
|
obj_id[] getPlayersFromKillerList(obj_id[] killerList)
|
|
{
|
|
resizeable obj_id[] players = new obj_id[0];
|
|
|
|
for(int i = 0; i < killerList.length; i++)
|
|
{
|
|
if(group.isGroupObject(killerList[i]))
|
|
{
|
|
obj_id[]groupMembers = utils.getGroupMemberIds(killerList[i]);
|
|
|
|
for(int k = 0; k < groupMembers.length; k++)
|
|
{
|
|
if(isIdValid(groupMembers[k]) && exists(groupMembers[k]))
|
|
{
|
|
if(pet_lib.isPet(groupMembers[k]))
|
|
utils.addElement(players, getMaster(groupMembers[k]));
|
|
else
|
|
utils.addElement(players, groupMembers[k]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pet_lib.isPet(killerList[i]))
|
|
utils.addElement(players, getMaster(killerList[i]));
|
|
else
|
|
utils.addElement(players, killerList[i]);
|
|
}
|
|
}
|
|
|
|
killerList = players;
|
|
|
|
return killerList;
|
|
}
|
|
|
|
void grantQuestKillCredit(obj_id winner, obj_id target)
|
|
{
|
|
obj_id[] killList;
|
|
|
|
if(group.isGroupObject(winner))
|
|
{
|
|
killList = getGroupMemberIds(winner);
|
|
}
|
|
else
|
|
{
|
|
killList = new obj_id[1];
|
|
killList[0] = winner;
|
|
}
|
|
|
|
if(killList.length < 1)
|
|
return; //there are no valid killers
|
|
|
|
dictionary params = new dictionary();
|
|
string creatureName = getCreatureName(target);
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
return;
|
|
|
|
params.put("creatureName", creatureName);
|
|
params.put("location", getLocation(target)); //send the location to do dist. checks on the receiving end
|
|
params.put("socialGroup", ai_lib.getSocialGroup(target));
|
|
//collection usage - col_faction and difficultyClass
|
|
params.put("col_faction", dataTableGetString(CREATURES_TABLE, creatureName, "col_faction"));
|
|
params.put("difficultyClass", dataTableGetInt(CREATURES_TABLE, creatureName, "difficultyClass"));
|
|
//someday we can remove these. Old fs_quests need them still. Sux:
|
|
params.put("target", target);
|
|
|
|
if(hasObjVar(target, "quest_spawner.spawned_by"))
|
|
params.put("spawnedBy", getObjIdObjVar(target, "quest_spawner.spawned_by"));
|
|
|
|
for(int i = 0; i < killList.length; i++)
|
|
{
|
|
if(!hasObjVar(target, "soloCollection"))
|
|
{
|
|
//make sure we are valid
|
|
if(!exists(killList[i]) || !isIdValid(killList[i]))
|
|
continue;
|
|
|
|
//get the distance
|
|
float distance = getDistance(target, killList[i]);
|
|
|
|
//group members need to be within 128m of target for credit
|
|
if(distance < 128.0f && distance >= 0.0f)
|
|
{
|
|
//send to everything, including pets. They will forward the message to their master if they are not grouped
|
|
// If they are grouped, then they are grouped with their master, so he's on this kill list too.
|
|
messageTo(killList[i], "receiveCreditForKill", params, 0.0f, false);
|
|
}
|
|
}
|
|
//Kill target is an npc that should only give credit to one player, the collection owner.
|
|
else
|
|
{
|
|
obj_id collectionOwner = getObjIdObjVar(target, "soloCollection");
|
|
//make sure we are valid
|
|
if(!exists(collectionOwner) || !isIdValid(collectionOwner))
|
|
continue;
|
|
|
|
//get the distance
|
|
float distance = getDistance(target, collectionOwner);
|
|
|
|
//group members need to be within 128m of target for credit
|
|
if(distance < 128.0f && distance >= 0.0f)
|
|
{
|
|
//send to everything, including pets. They will forward the message to their master if they are not grouped
|
|
// If they are grouped, then they are grouped with their master, so he's on this kill list too.
|
|
messageTo(collectionOwner, "receiveCreditForKill", params, 0.0f, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int updateGroupDamageDictionary(dictionary groupDamage, obj_id attacker, int attackerDamage, obj_id target)
|
|
{
|
|
if(!isIdValid(attacker) || !isIdValid(target) || attackerDamage < 1)
|
|
return attackerDamage;
|
|
|
|
//LOG("xp","updateGroupDamageDictionary: attacker = " + attacker + " -> " + attackerDamage);
|
|
|
|
if(beast_lib.isBeast(attacker))
|
|
{
|
|
obj_id master = getMaster(attacker);
|
|
|
|
if(isIdValid(master) && exists(master))
|
|
{
|
|
attacker = master;
|
|
}
|
|
}
|
|
|
|
obj_id attackerGroupId = getGroupObject(attacker);
|
|
|
|
if(!isIdValid(attackerGroupId))
|
|
{
|
|
int currentPersonalDamage = groupDamage.getInt(attacker.toString());
|
|
int totalPersonalDamage = currentPersonalDamage + attackerDamage;
|
|
|
|
groupDamage.put(attacker.toString(), totalPersonalDamage);
|
|
|
|
//debugSpeakMsg(attacker, "returning personal damage:" + totalPersonalDamage);
|
|
return totalPersonalDamage;
|
|
}
|
|
else
|
|
{
|
|
int currentGroupDamage = groupDamage.getInt(attackerGroupId.toString());
|
|
int totalGroupDamage = currentGroupDamage + attackerDamage;
|
|
|
|
groupDamage.put(attackerGroupId.toString(), totalGroupDamage);
|
|
|
|
//debugSpeakMsg(attacker, "returning group damage:" + totalGroupDamage);
|
|
return totalGroupDamage;
|
|
}
|
|
}
|
|
|
|
/********************************************************************************************
|
|
* @brief attempts to grant combat xp for a killed/disabled object
|
|
*
|
|
* @param obj_id target
|
|
* @param obj_id killers
|
|
*
|
|
*********************************************************************************************/
|
|
obj_var[] grantCombatXp(obj_id target, obj_var[] killers)
|
|
{
|
|
//LOG("xp", "grantCombatXP(" + target + ", " + killers + ")");
|
|
|
|
if(!isIdValid(target) || killers == null || killers.length == 0)
|
|
return null;
|
|
|
|
resizeable obj_var[] ret = new obj_var[0];
|
|
|
|
double damageTally = (double)getIntObjVar(target, VAR_TOP_DAMAGE);
|
|
|
|
if(damageTally < 0)
|
|
{
|
|
debugServerConsoleMsg(target, "xp::grantCombatXp: damageTally(" + damageTally + ") < 0!! WTF happened in combat?");
|
|
return null;
|
|
}
|
|
|
|
obj_id killCredit = getObjIdObjVar(target, VAR_TOP_GROUP);
|
|
|
|
obj_id[] killList;
|
|
|
|
if(group.isGroupObject(killCredit))
|
|
{
|
|
killList = getGroupMemberIds(killCredit);
|
|
}
|
|
else
|
|
{
|
|
killList = new obj_id[1];
|
|
killList[0] = killCredit;
|
|
}
|
|
|
|
|
|
metrics.doKillMetrics(killCredit, target);
|
|
|
|
string faction = factions.getFaction(target);
|
|
int levelAmount = getLevel(target) * 10;
|
|
|
|
obj_id[] allKillers = new obj_id[killers.length];
|
|
|
|
for(int i = 0; i < killers.length; i++)
|
|
{
|
|
allKillers[i] = utils.stringToObjId(killers[i].getName());
|
|
}
|
|
|
|
for(int i = 0; i < killers.length; i++)
|
|
{
|
|
obj_var killerVar = killers[i];
|
|
|
|
obj_id killer = allKillers[i];
|
|
|
|
obj_id master = obj_id.NULL_ID;
|
|
|
|
if(beast_lib.isBeast(killer) && !ai_lib.aiIsDead(killer))
|
|
{
|
|
master = getMaster(killer);
|
|
|
|
if(!master.isLoaded() || master.isBeingDestroyed())
|
|
{
|
|
master = null;
|
|
}
|
|
}
|
|
|
|
// Killer must be in kill list or pet's master must be in kill list to get xp
|
|
//if killer is beast, then beasts master needs to be in killList AND needs to be in top group
|
|
if(!utils.isObjIdInArray(killList, killer))
|
|
{
|
|
if(isIdValid(master))
|
|
{
|
|
if(!utils.isObjIdInArray(killList, master))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
obj_id gcw_gain_object = null;
|
|
|
|
if (isIdValid(master))
|
|
{
|
|
if (!utils.isObjIdInArray(allKillers, master))
|
|
gcw_gain_object = master;
|
|
|
|
}
|
|
else
|
|
gcw_gain_object = killer;
|
|
|
|
if(isIdValid(gcw_gain_object) && !hasObjVar(target, gcw.GCW_POINT_OVERRIDE))
|
|
{
|
|
gcw.grantModifiedGcwPoints(target, gcw_gain_object, false, gcw.GCW_POINT_TYPE_GROUND_PVE, getName(target));
|
|
}
|
|
int dam = killerVar.getIntData();
|
|
|
|
checkAndUpdateHuntingMissions(killer, target);
|
|
|
|
if(isIdValid(killer) && killer.isLoaded() && !killer.isBeingDestroyed() && killer != target)
|
|
{
|
|
int xpTotal = getRawCombatXP(killer, target);
|
|
|
|
// Applied here instead of grant() as this modifier only applies to grants of combat xp earned from kills.
|
|
xpTotal = applyGroupXpModifier(killer, xpTotal);
|
|
|
|
if(isPlayer(killer) && beast_lib.isBeastMaster(killer))
|
|
{
|
|
obj_id beast = beast_lib.getBeastOnPlayer(killer);
|
|
|
|
//this is for when a beast is out, and the killer is a player who can get xp
|
|
//we want the beast to get xp too
|
|
if(beast_lib.isValidBeast(beast) && getDistance(killer, beast) < MAX_DISTANCE && xpTotal > 1)
|
|
{
|
|
if(utils.isObjIdInArray(killList, killer) || utils.isObjIdInArray(killList, beast))
|
|
{
|
|
beast_lib.grantBeastExperience(beast);
|
|
|
|
//checking if beast has a favorite or dislike for killing stuff.
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("targetId", target);
|
|
obj_id beastBCD = beast_lib.getBeastBCD(beast);
|
|
|
|
messageTo(beastBCD, "beastKilledSomething", params, 1, false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(beast_lib.isBeast(killer) && beast_lib.isValidPlayer(master) && beast_lib.canBeastGetLevelBasedXP(killer, target))
|
|
{
|
|
// If the master is in the killList, we don't want to give out exp to the beast twice.
|
|
if(utils.isObjIdInArray(allKillers, master))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
beast_lib.grantBeastExperience(killer);
|
|
|
|
//checking if beast has a favorite or dislike for killing stuff.
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("targetId", target);
|
|
obj_id beastBCD = beast_lib.getBeastBCD(killer);
|
|
|
|
messageTo(beastBCD, "beastKilledSomething", params, 1, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!isPlayer(killer))
|
|
{
|
|
if((beast_lib.isBeast(killer) || pet_lib.isPet(killer)) && !ai_lib.aiIsDead(killer) && isIdValid(master))
|
|
{
|
|
// Get the experience from the master's level standpoint, instead of the pet's level.
|
|
xpTotal = getRawCombatXP(master, target);
|
|
|
|
ret = utils.addElement(ret, killerVar);
|
|
|
|
// Give master xp if the pet get's kill credit and the master does not
|
|
if(!utils.isObjIdInArray(allKillers, master) && utils.isObjIdInArray(allKillers, killer))
|
|
{
|
|
grantCombatStyleXp(master, COMBAT_GENERAL, xpTotal);
|
|
|
|
displayXpFlyText(master, master, xpTotal);
|
|
displayXpMsg(master, null, xpTotal);
|
|
|
|
double percentDamage = ((double) dam / damageTally) + PERCENT_ADJUSTER;
|
|
factions.grantCombatFaction(master, target, percentDamage);
|
|
}
|
|
}
|
|
}
|
|
else if(utils.isObjIdInArray(killList, killer))
|
|
{
|
|
//the percentage of player damage / total damage inflicted on target and add a variable % adjustment
|
|
double percentDamage = ((double) dam / damageTally) + PERCENT_ADJUSTER;
|
|
|
|
//check the player's disatance from the target's corpse
|
|
if(getDistance(killer, target) < MAX_DISTANCE)
|
|
{
|
|
grantCombatXpPerAttackType(killer, target, xpTotal);
|
|
|
|
// Also, grant faction points for the kill.
|
|
factions.grantCombatFaction(killer, target, percentDamage);
|
|
|
|
if(faction != null)
|
|
factions.adjustSocialStanding(killer, faction, -levelAmount);
|
|
|
|
// Everyone can harvest, no more xp
|
|
//// Store harvest XP for this kill
|
|
//corpse.setCreatureHarvestXp(target, killer);
|
|
|
|
ret = utils.addElement(ret, killerVar);
|
|
}
|
|
}
|
|
|
|
// Remove scriptvar now that all flytext / system messages using it have been displayed.
|
|
utils.removeScriptVar(killer, "combat.xp.groupBonus");
|
|
}
|
|
}
|
|
|
|
if(ret == null || ret.length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/********************************************************************************************
|
|
* @brief breaks down total xp into xp per attack type
|
|
*
|
|
* @param obj_id killer
|
|
* @param obj_id target
|
|
* @param int total
|
|
*
|
|
*********************************************************************************************/
|
|
void grantCombatXpPerAttackType(obj_id player, obj_id target, int totalXp)
|
|
{
|
|
//LOG("xp", "grantCombatXpPerAttackType(" + player + ", " + target + ", " + totalXp + ")");
|
|
|
|
if(!isIdValid(player) || (!isPlayer(player)) || !isIdValid(target) || totalXp < 1)
|
|
return;
|
|
|
|
string basePath = VAR_ATTACKER_LIST + "." + player;
|
|
string xpListBasePath = basePath + ".xp";
|
|
string xpListPath = xpListBasePath + ".types";
|
|
|
|
if(!utils.hasScriptVar(target, xpListPath))
|
|
return;
|
|
|
|
string[]xpTypes = utils.getStringBatchScriptVar(target, xpListPath);
|
|
|
|
if(xpTypes == null || xpTypes.length == 0)
|
|
return;
|
|
|
|
if(hasScript(player, "theme_park.new_player.new_player") || hasObjVar(player, "handlePlayerCombatKill"))
|
|
{
|
|
dictionary webster = new dictionary();
|
|
|
|
webster.put("xpTypes", xpTypes);
|
|
webster.put("target", target);
|
|
|
|
messageTo(player, "handlePlayerCombatKill", webster, 1, false);
|
|
}
|
|
|
|
dictionary d = new dictionary();
|
|
|
|
string damPath = basePath + ".damage";
|
|
int tally = utils.getIntScriptVar(target, damPath);
|
|
|
|
// Remove permissions only damage from the damage tally
|
|
tally -= utils.getIntScriptVar(target, xpListBasePath + "." + PERMISSIONS_ONLY);
|
|
|
|
if(tally < 1)
|
|
return;
|
|
|
|
int raw = 0;
|
|
int bonusCombatXp = 0;
|
|
int jediXp = 0;
|
|
int generalXp = 0;
|
|
int totalXpGranted = 0;
|
|
|
|
for(int i = 0; i < xpTypes.length; i++)
|
|
{
|
|
int val = utils.getIntScriptVar(target, xpListBasePath + "." + xpTypes[i]);
|
|
float xpPercent = ((float) (val) / (float) (tally));
|
|
|
|
//sendSystemMessageTestingOnly (player, "XP:"+xpTypes[i]+"-Amt:"+val+"-Pct:"+xpPercent);
|
|
|
|
int intNewTotal = totalXp;
|
|
|
|
if(xpTypes[i].equals(RAW_COMBAT))
|
|
{
|
|
intNewTotal = (int) (intNewTotal * xpPercent);
|
|
bonusCombatXp += intNewTotal;
|
|
}
|
|
else if(xpTypes[i].equals(PARTIAL_COMBAT))
|
|
{
|
|
intNewTotal = (int) (intNewTotal * xpPercent);
|
|
intNewTotal = (int) (intNewTotal * COMBAT_GENERAL_EXCHANGE_RATE);
|
|
bonusCombatXp += intNewTotal;
|
|
}
|
|
else if(xpTypes[i].equals(COMBAT_GENERAL))
|
|
{
|
|
int amt = (int) (intNewTotal * xpPercent);
|
|
|
|
if(amt < 1)
|
|
amt = 1;
|
|
|
|
generalXp += amt;
|
|
}
|
|
else if(isCombatXpType(xpTypes[i]))
|
|
{
|
|
int amt = (int) (intNewTotal * xpPercent);
|
|
|
|
if(amt < 1)
|
|
amt = 1;
|
|
|
|
// track lightsaber xp for jedi
|
|
if(xpTypes[i].equals(COMBAT_JEDI_ONEHANDLIGHTSABER) ||
|
|
xpTypes[i].equals(COMBAT_JEDI_TWOHANDLIGHTSABER) ||
|
|
xpTypes[i].equals(COMBAT_JEDI_POLEARMLIGHTSABER) ||
|
|
xpTypes[i].equals(COMBAT_JEDI_FORCE_POWER) ||
|
|
xpTypes[i].equals(JEDI_GENERAL))
|
|
{
|
|
if(isJedi(player))
|
|
jediXp += amt;
|
|
}
|
|
else
|
|
{
|
|
raw += amt;
|
|
|
|
if(!xpTypes[i].equals(COMBAT_THROWN))
|
|
totalXpGranted += grantCombatStyleXp(player, xpTypes[i], amt);
|
|
}
|
|
}
|
|
else if(!xpTypes[i].equals(UNKNOWN) && !xpTypes[i].equals(PERMISSIONS_ONLY) && !xpTypes[i].equals(PET_DAMAGE)) // All other non-combat xp-types
|
|
{
|
|
int amt = (int) (intNewTotal * xpPercent);
|
|
|
|
if(amt < 1)
|
|
amt = 1;
|
|
|
|
totalXpGranted += grantCombatStyleXp(player, xpTypes[i], amt);
|
|
}
|
|
}
|
|
|
|
// Jedi earn JEDI_GENERAL
|
|
// Non-Jedi earn COMBAT_GENERAL
|
|
if(isJedi(player))
|
|
{
|
|
jediXp *= JEDI_GENERAL_EXCHANGE_RATE;
|
|
totalXpGranted += grantCombatStyleXp(player, JEDI_GENERAL, jediXp);
|
|
LOG("jedi", "Granting " + jediXp + " to " + player + " type is " + JEDI_GENERAL);
|
|
}
|
|
else
|
|
{
|
|
generalXp += (int) (raw * COMBAT_GENERAL_EXCHANGE_RATE);
|
|
|
|
if(generalXp < 0)
|
|
generalXp = 0;
|
|
|
|
if(bonusCombatXp > 0)
|
|
generalXp += bonusCombatXp;
|
|
|
|
if(generalXp > 0)
|
|
{
|
|
totalXpGranted += grantCombatStyleXp(player, COMBAT_GENERAL, generalXp);
|
|
}
|
|
}
|
|
|
|
if(!isPlayer(target) && factions.getFactionFlag(player) != factions.getFactionFlag(target) &&
|
|
(factions.getFactionFlag(target) == factions.FACTION_FLAG_REBEL ||
|
|
factions.getFactionFlag(target) == factions.FACTION_FLAG_IMPERIAL))
|
|
{
|
|
gcw.gcwInvasionCreditForKill(player);
|
|
}
|
|
|
|
displayXpFlyText(player, player, totalXpGranted);
|
|
displayXpMsg(player, null, totalXpGranted);
|
|
}
|
|
|
|
int grantSocialStyleXp(obj_id player, string xpType, int amount)
|
|
{
|
|
amount = Math.round(amount * ENTERTAINER_XP_MOD);
|
|
//LOG("xp", "grantSocialStyleXp(" + player + ", " + xpType + ", " + amount + ")");
|
|
|
|
string templateXp = skill_template.getTemplateSkillXpType(player, false);
|
|
|
|
if(templateXp != null)
|
|
{
|
|
if(isSocialXpType(templateXp))
|
|
{
|
|
amount = grant(player, templateXp, amount, false);
|
|
}
|
|
else
|
|
{
|
|
amount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amount = 0;
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
int grantCraftingQuestXp(obj_id player, int amount)
|
|
{
|
|
|
|
string templateXp = skill_template.getTemplateSkillXpType(player, false);
|
|
|
|
if(templateXp != null)
|
|
{
|
|
if(isCraftingXpType(templateXp))
|
|
{
|
|
amount = grant(player, templateXp, amount, false);
|
|
}
|
|
else
|
|
{
|
|
// Tried to grant non-social XP
|
|
//debugSpeakMsg(player, "You have a Crafting Skill selected as your working skill.");
|
|
amount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amount = 0;
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
|
|
int grantCraftingStyleXp(obj_id player, string xpType, int amount)
|
|
{
|
|
|
|
amount = Math.round(amount * TRADER_XP_MOD);
|
|
//LOG("xp", "grantCraftingStyleXp(" + player + ", " + xpType + ", " + amount + ")");
|
|
|
|
int merchantXP = (int) (amount * CRAFTING_MERCHANT_EXCHANGE_RATE);
|
|
float xpRatio = skill_template.NON_TEMPLATE_XP_RATIO;
|
|
|
|
if(xpType.equals(QUEST_CRAFTING))
|
|
{
|
|
merchantXP = 0;
|
|
xpRatio = skill_template.QUEST_XP_RATIO;
|
|
}
|
|
|
|
string templateXp = skill_template.getTemplateSkillXpType(player, false);
|
|
|
|
if(templateXp != null)
|
|
{
|
|
if(isCraftingXpType(templateXp))
|
|
{
|
|
if(!xpType.equals(templateXp))
|
|
{
|
|
amount = (int) (amount * xpRatio);
|
|
}
|
|
|
|
if(!templateXp.equals(MERCHANT))
|
|
{
|
|
merchantXP = (int) (merchantXP * xpRatio);
|
|
}
|
|
|
|
amount += merchantXP;
|
|
amount = grant(player, templateXp, amount, false);
|
|
}
|
|
else
|
|
{
|
|
// Tried to grant non-social XP
|
|
//debugSpeakMsg(player, "You have a Crafting Skill selected as your working skill.");
|
|
amount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amount = 0;
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
int grantCombatStyleXp(obj_id player, string xpType, int amount)
|
|
{
|
|
//LOG("xp", "grantCombatStyleXp(" + player + ", " + xpType + ", " + amount + ")");
|
|
|
|
float xpRatio = skill_template.NON_TEMPLATE_XP_RATIO;
|
|
|
|
if(xpType.equals(QUEST_COMBAT))
|
|
{
|
|
xpRatio = skill_template.QUEST_XP_RATIO;
|
|
}
|
|
|
|
string templateXp = skill_template.getTemplateSkillXpType(player, false);
|
|
|
|
if(templateXp != null)
|
|
{
|
|
if(isCombatXpType(templateXp))
|
|
{
|
|
if(!xpType.equals(templateXp))
|
|
{
|
|
amount = (int) (amount * xpRatio);
|
|
}
|
|
|
|
amount = grant(player, templateXp, amount, false);
|
|
}
|
|
else
|
|
{
|
|
// Tried to grant non-social XP
|
|
//debugSpeakMsg(player, "You have a Combat Skill selected as your working skill.");
|
|
amount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
amount = 0;
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
void displayXpMsg(obj_id player, string xpType, int amt)
|
|
{
|
|
//LOG("xp", "displayXpMsg(" + player + ", " + xpType + ", " + amt + ")");
|
|
if(xpType == null)
|
|
xpType = skill_template.getTemplateSkillXpType(player, true);
|
|
|
|
if(xpType == null || xpType.equals(""))
|
|
return;
|
|
|
|
if(xpType.equals(skill_template.NO_TEMPLATE_STARTING))
|
|
return;
|
|
|
|
int currentXp = getExperiencePoints(player, xpType);
|
|
int xpCap = getExperienceCap(player, xpType);
|
|
|
|
if(currentXp < xpCap)
|
|
{
|
|
float grpMod = 1f;
|
|
|
|
if(utils.hasScriptVar(player, "combat.xp.groupBonus"))
|
|
{
|
|
grpMod = utils.getFloatScriptVar(player, "combat.xp.groupBonus");
|
|
}
|
|
float inspMod = getInspirationBuffXpModifier(player, xpType);
|
|
|
|
prose_package pp = xp.getXpProsePackage(xpType, amt, grpMod, inspMod);
|
|
|
|
if(pp != null)
|
|
{
|
|
|
|
if(isSpaceScene())
|
|
{
|
|
sendQuestSystemMessage(player, pp);
|
|
}
|
|
}
|
|
|
|
if(currentXp + amt >= xpCap)
|
|
{
|
|
prose_package ppAtCap = prose.getPackage(new string_id("base_player", "prose_hit_xp_cap"), new string_id(STF_XP_N, xpType));
|
|
|
|
if(isSpaceScene())
|
|
{
|
|
sendQuestSystemMessage(player, ppAtCap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void displayXpFlyText(obj_id player, obj_id target, int amount)
|
|
{
|
|
if(isFreeTrialAccount(player))
|
|
{
|
|
int playerLevel = getLevel(player);
|
|
|
|
if(playerLevel >= TRIAL_LEVEL_CAP)
|
|
{
|
|
debugSpeakMsg(player, "I am a greater than or equal to level " + xp.TRIAL_LEVEL_CAP);
|
|
prose_package pp = new prose_package();
|
|
prose.setDI(pp, xp.TRIAL_LEVEL_CAP);
|
|
prose.setStringId(pp, SID_FREE_TRIAL_LEVEL_CAP);
|
|
sendSystemMessageProse(player, pp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(amount == 0)
|
|
return;
|
|
|
|
if(npe.hasReachedMaxTutorialLevel(player))
|
|
return;
|
|
|
|
prose_package pp = null;
|
|
|
|
pp = prose.getPackage(SID_FLYTEXT_XP, amount);
|
|
|
|
float groupModPct = 1.0f;
|
|
|
|
if(utils.hasScriptVar(player, "combat.xp.groupBonus"))
|
|
{
|
|
float groupBonus = utils.getFloatScriptVar(player, "combat.xp.groupBonus");
|
|
|
|
groupModPct = getGroupXpModifierPercentageOfMax(groupBonus);
|
|
}
|
|
|
|
float flyScale = getCombatXpFlyScale(groupModPct);
|
|
color flyColor = getCombatXpFlyColor(groupModPct);
|
|
|
|
showFlyTextPrivate(target, player, pp, flyScale, flyColor);
|
|
}
|
|
|
|
float getGroupXpModifierPercentageOfMax(float groupMod)
|
|
{
|
|
float groupModPct = 0;
|
|
|
|
const float maxGroupMod = GROUP_XP_BONUS * MAX_GROUP_BONUS_COUNT;
|
|
|
|
if(maxGroupMod > 0)
|
|
{
|
|
groupModPct = (groupMod - 1) / maxGroupMod;
|
|
}
|
|
if(groupModPct > 1)
|
|
{
|
|
groupModPct = 1;
|
|
}
|
|
|
|
return groupModPct;
|
|
}
|
|
|
|
float getCombatXpFlyScale(float groupModPct)
|
|
{
|
|
const float minScale = 1.5f;
|
|
const float maxScale = 2.5f;
|
|
const float scaleRange = maxScale - minScale;
|
|
|
|
float scale = minScale + (scaleRange * groupModPct);
|
|
|
|
return scale;
|
|
}
|
|
|
|
color getCombatXpFlyColor(float groupModPct)
|
|
{
|
|
const color minColor = new color(140, 15, 210, 255);
|
|
const color maxColor = new color(180, 60, 240, 255);
|
|
const int redRange = maxColor.getR() - minColor.getR();
|
|
const int greenRange = maxColor.getG() - minColor.getG();
|
|
const int blueRange = maxColor.getB() - minColor.getB();
|
|
|
|
int red = minColor.getR() + (int) (redRange * groupModPct);
|
|
int green = minColor.getG() + (int) (greenRange * groupModPct);
|
|
int blue = minColor.getB() + (int) (blueRange * groupModPct);
|
|
|
|
if(red > maxColor.getR())
|
|
{
|
|
red = maxColor.getR();
|
|
}
|
|
if(green > maxColor.getG())
|
|
{
|
|
green = maxColor.getG();
|
|
}
|
|
if(blue > maxColor.getB())
|
|
{
|
|
blue = maxColor.getB();
|
|
}
|
|
|
|
return new color(red, green, blue, 255);
|
|
}
|
|
|
|
boolean isCombatXpType(string xpType)
|
|
{
|
|
if(xpType == COMBAT_GENERAL)
|
|
return true;
|
|
if(xpType == COMBAT_RANGEDSPECIALIZE_RIFLE)
|
|
return true;
|
|
if(xpType == COMBAT_RANGEDSPECIALIZE_CARBINE)
|
|
return true;
|
|
if(xpType == COMBAT_RANGEDSPECIALIZE_PISTOL)
|
|
return true;
|
|
if(xpType == COMBAT_RANGEDSPECIALIZE_HEAVY)
|
|
return true;
|
|
if(xpType == COMBAT_MELEESPECIALIZE_ONEHAND)
|
|
return true;
|
|
if(xpType == COMBAT_MELEESPECIALIZE_TWOHAND)
|
|
return true;
|
|
if(xpType == COMBAT_MELEESPECIALIZE_UNARMED)
|
|
return true;
|
|
if(xpType == COMBAT_MELEESPECIALIZE_POLEARM)
|
|
return true;
|
|
if(xpType == COMBAT_THROWN)
|
|
return true;
|
|
if(xpType == COMBAT_GRENADE)
|
|
return true;
|
|
if(xpType == JEDI_GENERAL)
|
|
return true;
|
|
if(xpType == COMBAT_JEDI_ONEHANDLIGHTSABER)
|
|
return true;
|
|
if(xpType == COMBAT_JEDI_TWOHANDLIGHTSABER)
|
|
return true;
|
|
if(xpType == COMBAT_JEDI_POLEARMLIGHTSABER)
|
|
return true;
|
|
if(xpType == COMBAT_JEDI_FORCE_POWER)
|
|
return true;
|
|
if(xpType == MEDICAL)
|
|
return true;
|
|
if(xpType == COMBATMEDIC)
|
|
return true;
|
|
if(xpType == CREATUREHANDLER)
|
|
return true;
|
|
if(xpType == QUEST_COMBAT)
|
|
return true;
|
|
if(xpType == QUEST_GENERAL)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
boolean isSocialXpType(string xpType)
|
|
{
|
|
if(xpType == MUSIC)
|
|
return true;
|
|
if(xpType == DANCE)
|
|
return true;
|
|
if(xpType == JUGGLING)
|
|
return true;
|
|
if(xpType == ENTERTAINER)
|
|
return true;
|
|
if(xpType == IMAGEDESIGNER)
|
|
return true;
|
|
if(xpType == QUEST_SOCIAL)
|
|
return true;
|
|
if(xpType == QUEST_COMBAT)
|
|
return true;
|
|
if(xpType == QUEST_GENERAL)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
boolean isCraftingXpType(string xpType)
|
|
{
|
|
if(xpType == HARVEST_INORGANIC)
|
|
return true;
|
|
if(xpType == CRAFTING_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_FOOD_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_MEDICINE_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_CLOTHING_ARMOR)
|
|
return true;
|
|
if(xpType == CRAFTING_CLOTHING_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_WEAPONS_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_WEAPONS_MELEE)
|
|
return true;
|
|
if(xpType == CRAFTING_WEAPONS_RANGED)
|
|
return true;
|
|
if(xpType == CRAFTING_WEAPONS_MUNITION)
|
|
return true;
|
|
if(xpType == CRAFTING_STRUCTURE_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_DROID_GENERAL)
|
|
return true;
|
|
if(xpType == CRAFTING_SHIPWRIGHT)
|
|
return true;
|
|
if(xpType == MERCHANT)
|
|
return true;
|
|
if(xpType == QUEST_CRAFTING)
|
|
return true;
|
|
if(xpType == QUEST_GENERAL)
|
|
return true;
|
|
if(xpType == "crafting")
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
string[] getXpTypes(obj_id self)
|
|
{
|
|
resizeable string[] xpTypes = new string[0];
|
|
|
|
if(dataTableOpen(TBL_SKILL))
|
|
{
|
|
string[]colXpType = dataTableGetStringColumnNoDefaults(TBL_SKILL, "XP_TYPE");
|
|
|
|
if((colXpType == null) || (colXpType.length == 0))
|
|
{
|
|
//debugSpeakMsg(self, "getXpTypes: unable to retrieve the XP_TYPE column");
|
|
return null;
|
|
}
|
|
|
|
for(int i = 0; i < colXpType.length; i++)
|
|
{
|
|
string type = colXpType[i];
|
|
|
|
if(type != null && !type.equals("") && !type.equals("unobtainable") && !type.equals("jedi"))
|
|
{
|
|
//string sidType = "@exp_n:" + type;
|
|
if(utils.getElementPositionInArray(xpTypes, type) == -1)
|
|
{
|
|
xpTypes = utils.addElement(xpTypes, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return xpTypes;
|
|
}
|
|
|
|
void checkAndUpdateHuntingMissions(obj_id objPlayer, obj_id objCreature)
|
|
{
|
|
if(!isMob(objCreature))
|
|
{
|
|
|
|
return;
|
|
}
|
|
|
|
LOG("missions", "checking for " + objPlayer);
|
|
obj_id[]objMissions = getMissionObjects(objPlayer);
|
|
|
|
if(objMissions == null)
|
|
{
|
|
LOG("missions", "null missions");
|
|
return;
|
|
}
|
|
|
|
for(int intI = 0; intI < objMissions.length; intI++)
|
|
{
|
|
string strMissionType = getMissionType(objMissions[intI]);
|
|
|
|
LOG("missions", "type is " + strMissionType);
|
|
|
|
if(strMissionType == "hunting")
|
|
{
|
|
string strCreatureToKill = getStringObjVar(objMissions[intI], "strCreatureToKill");
|
|
const string strCreatureType = getCreatureName(objCreature);
|
|
|
|
LOG("missions", "creature to kill is " + strCreatureToKill);
|
|
LOG("missions", "creature type is " + strCreatureType);
|
|
|
|
if(strCreatureType == strCreatureToKill)
|
|
{
|
|
|
|
int intQuantityToKill = getIntObjVar(objMissions[intI], "intQuantityToKill");
|
|
|
|
intQuantityToKill = intQuantityToKill - 1;
|
|
|
|
if(intQuantityToKill <= 0)
|
|
{
|
|
messageTo(objMissions[intI], "huntingSuccess", null, 0, true);
|
|
}
|
|
else
|
|
{
|
|
LOG("missions", "Quanitty Ledft is " + intQuantityToKill);
|
|
setObjVar(objMissions[intI], "intQuantityToKill", intQuantityToKill);
|
|
string_id strName = new string_id("mob/creature_names", strCreatureType);
|
|
prose_package ppKillsLeft = prose.getPackage(new string_id("mission/mission_generic", "hunting_kills_remaining"), strName, intQuantityToKill);
|
|
|
|
sendSystemMessageProse(objPlayer, ppKillsLeft);
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Until list.quickSort() gets fixed to sort in the proper order (high to low instead of low to high),
|
|
// this determines whether the first or last entry is greater. It then determines if anyone else
|
|
// equalled doing the greatest amount of damage.
|
|
obj_id[] getTopIndividualAttacker(obj_var[] killers)
|
|
{
|
|
resizeable obj_id[] tempArray = new obj_id[0];
|
|
|
|
if(killers == null || killers.length <= 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_var alpha = killers[0];
|
|
obj_id alphaObjId = utils.stringToObjId(alpha.getName());
|
|
|
|
if(killers.length == 1)
|
|
{
|
|
tempArray = utils.addElement(tempArray, alphaObjId);
|
|
}
|
|
else
|
|
{
|
|
int alphaValue = alpha.getIntData();
|
|
|
|
obj_var omega = killers[killers.length - 1];
|
|
obj_id omegaObjId = utils.stringToObjId(omega.getName());
|
|
int omegaValue = omega.getIntData();
|
|
|
|
if(alphaValue > omegaValue)
|
|
{
|
|
tempArray = utils.addElement(tempArray, alphaObjId);
|
|
|
|
for(int a = 1; a < killers.length; a++)
|
|
{
|
|
obj_var next = killers[a];
|
|
int nextValue = next.getIntData();
|
|
|
|
if(nextValue == alphaValue)
|
|
{
|
|
tempArray = utils.addElement(tempArray, utils.stringToObjId(next.getName()));
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(alphaValue < omegaValue)
|
|
{
|
|
tempArray = utils.addElement(tempArray, omegaObjId);
|
|
|
|
for(int om = (killers.length - 2); om >= 0; om--)
|
|
{
|
|
obj_var next = killers[om];
|
|
int nextValue = next.getIntData();
|
|
|
|
if(nextValue == omegaValue)
|
|
{
|
|
tempArray = utils.addElement(tempArray, utils.stringToObjId(next.getName()));
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obj_id[] topAttacker = utils.toStaticObjIdArray(tempArray);
|
|
|
|
return topAttacker;
|
|
}
|
|
|
|
boolean displayForceSensitiveXP(obj_id player, string type)
|
|
{
|
|
if(!isIdValid(player))
|
|
{
|
|
LOG("force_sensitive", "xp.convertForceSensitiveXP -- player is invalid.");
|
|
return false;
|
|
}
|
|
|
|
if(type == null)
|
|
{
|
|
LOG("force_sensitive", "xp.convertForceSensitiveXP -- type is null.");
|
|
return false;
|
|
}
|
|
|
|
int index = dataTableSearchColumnForString(type, "fs_type", "datatables/quest/force_sensitive/xp_conversion.iff");
|
|
|
|
if(index < 0)
|
|
{
|
|
LOG("force_sensitive", "xp.convertForceSensitiveXP -- can't find type " + type + " in xp datatable.");
|
|
return false;
|
|
}
|
|
|
|
resizeable string[] valid_xp_types = new string[0];
|
|
|
|
for(int i = index; i < dataTableGetNumRows("datatables/quest/force_sensitive/xp_conversion.iff"); i++)
|
|
{
|
|
dictionary row = dataTableGetRow("datatables/quest/force_sensitive/xp_conversion.iff", i);
|
|
string fs_type = row.getString("fs_type");
|
|
|
|
if(fs_type == null || !fs_type.equals(type))
|
|
break;
|
|
|
|
string xp_type = row.getString("xp_type");
|
|
|
|
if(xp_type != null && xp_type.length() > 0)
|
|
{
|
|
//LOG("quest", "xp_type ->" + xp_type + " xp ->" + getExperiencePoints(player, xp_type));
|
|
if(getExperiencePoints(player, xp_type) > 0)
|
|
valid_xp_types.add(xp_type);
|
|
}
|
|
}
|
|
|
|
if(valid_xp_types.length > 0)
|
|
{
|
|
string[]dsrc = new string[valid_xp_types.length];
|
|
utils.setScriptVar(player, "force_sensitive.xp_convert", valid_xp_types);
|
|
utils.setScriptVar(player, "force_sensitive.xp_convert_type", type);
|
|
|
|
for(int i = 0; i < valid_xp_types.length; i++)
|
|
dsrc[i] = "@exp_n:" + valid_xp_types[i];
|
|
|
|
if(!hasScript(player, "quest.force_sensitive.fs_xp_convert"))
|
|
attachScript(player, "quest.force_sensitive.fs_xp_convert");
|
|
|
|
string type_name = localize(new string_id("exp_n", "fs_" + type));
|
|
|
|
//LOG("quest", "string_id ->" + new string_id("exp_n", "fs_" + type) + " string ->" + type_name);
|
|
int pid = sui.listbox(player, player, "Select the experience you wish to convert to " + type_name + ".", sui.OK_CANCEL, "Experience Conversion", dsrc, "msgFSXPConvertSelected");
|
|
|
|
utils.setScriptVar(player, "force_sensitive.sui_pid", pid);
|
|
}
|
|
else
|
|
sendSystemMessage(player, new string_id("quest/force_sensitive/utils", "convert_not_enough_for_conversion"));
|
|
|
|
return true;
|
|
}
|
|
|
|
prose_package getXpProsePackage(String xpType, int xpAmt)
|
|
{
|
|
return getXpProsePackage(xpType, xpAmt, 1f, 1f);
|
|
}
|
|
|
|
prose_package getXpProsePackage(String xpType, int xpAmt, float grpMod, float inspMod)
|
|
{
|
|
prose_package pp = new prose_package();
|
|
|
|
string_id sid_xp = new string_id(STF_XP_N, xpType);
|
|
|
|
prose.setTO(pp, sid_xp);
|
|
|
|
// 1.10 -> 10%
|
|
float grpBonus = (grpMod * 100) - 100;
|
|
float inspBonus = (inspMod * 100) - 100;
|
|
|
|
// %TT and %TU will be used to display bonus percentages instead of %DF
|
|
// because there are 2 values and we want to display them as integers.
|
|
String grpBonusString = (new Integer(Math.round(grpBonus))).toString();
|
|
String inspBonusString = (new Integer(Math.round(inspBonus))).toString();
|
|
|
|
prose.setDI(pp, Math.abs(xpAmt));
|
|
|
|
if(xpAmt >= 0)
|
|
{
|
|
if(inspBonus > 0)
|
|
{
|
|
if(grpBonus > 0)
|
|
{
|
|
prose.setStringId(pp, PROSE_GRANT_GROUP_BUFF_XP);
|
|
prose.setTT(pp, grpBonusString);
|
|
prose.setTU(pp, inspBonusString);
|
|
}
|
|
else
|
|
{
|
|
prose.setStringId(pp, PROSE_GRANT_BUFF_XP);
|
|
prose.setTT(pp, inspBonusString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(grpBonus > 0)
|
|
{
|
|
prose.setStringId(pp, PROSE_GRANT_GROUP_XP);
|
|
prose.setTT(pp, grpBonusString);
|
|
}
|
|
else
|
|
{
|
|
if(xpAmt == 1)
|
|
{
|
|
prose.setStringId(pp, PROSE_GRANT_XP1);
|
|
}
|
|
else
|
|
{
|
|
prose.setStringId(pp, PROSE_GRANT_XP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(xpAmt == 1)
|
|
{
|
|
prose.setStringId(pp, PROSE_REVOKE_XP1);
|
|
}
|
|
else
|
|
{
|
|
prose.setStringId(pp, PROSE_REVOKE_XP);
|
|
}
|
|
}
|
|
|
|
return pp;
|
|
}
|
|
|
|
int grantXpByTemplate(obj_id player, int amount)
|
|
{
|
|
//this function used for granting xp to people in the NPE sequenced events. checks their template and awards either
|
|
//combat, crafting, or entertainer xp
|
|
|
|
string template = getSkillTemplate(player);
|
|
|
|
string xpType = xp.QUEST_COMBAT;
|
|
|
|
if(!isIdValid(player) || template == null)
|
|
return 0;
|
|
|
|
if(template.startsWith("trader"))
|
|
{
|
|
grantCraftingStyleXp(player, xp.QUEST_CRAFTING, amount);
|
|
xpType = xp.QUEST_CRAFTING;
|
|
}
|
|
else if(template.startsWith("entertainer"))
|
|
{
|
|
grantSocialStyleXp(player, xp.QUEST_SOCIAL, amount);
|
|
xpType = xp.QUEST_SOCIAL;
|
|
}
|
|
else
|
|
{
|
|
grantCombatStyleXp(player, xp.QUEST_COMBAT, amount);
|
|
xpType = xp.QUEST_COMBAT;
|
|
}
|
|
|
|
xp.displayXpFlyText(player, player, amount); //xp flies off your own head!
|
|
xp.displayXpMsg(player, xpType, amount);
|
|
|
|
return amount;
|
|
}
|
|
|
|
int grantUnmodifiedXpByTemplate(obj_id player, int amount)
|
|
{
|
|
//this function used for granting xp to people in the NPE sequenced events. checks their template and awards either
|
|
//combat, crafting, or entertainer xp
|
|
|
|
string template = getSkillTemplate(player);
|
|
|
|
string xpType = xp.QUEST_COMBAT;
|
|
|
|
if(!isIdValid(player) || template == null)
|
|
return 0;
|
|
|
|
if(template.startsWith("trader"))
|
|
{
|
|
xpType = xp.QUEST_CRAFTING;
|
|
}
|
|
else if(template.startsWith("entertainer"))
|
|
{
|
|
xpType = xp.QUEST_SOCIAL;
|
|
}
|
|
else
|
|
{
|
|
xpType = xp.QUEST_COMBAT;
|
|
}
|
|
|
|
string templateXp = skill_template.getTemplateSkillXpType(player, false);
|
|
|
|
if(templateXp != null && amount > 0)
|
|
{
|
|
amount = grant(player, templateXp, amount, false);
|
|
}
|
|
|
|
xp.displayXpFlyText(player, player, amount); //xp flies off your own head!
|
|
xp.displayXpMsg(player, xpType, amount);
|
|
|
|
return amount;
|
|
}
|
|
|
|
int grantUnmodifiedXPPercentageOfLevel(obj_id player, float percentage)
|
|
{
|
|
int playerLevel = getLevel(player);
|
|
if ( playerLevel < 90 )
|
|
{
|
|
int xpForCurrentLevel = dataTableGetInt(xp.TBL_PLAYER_LEVEL_XP, playerLevel - 1, "xp_required");
|
|
int xpForNextLevel = dataTableGetInt(xp.TBL_PLAYER_LEVEL_XP, playerLevel, "xp_required");
|
|
|
|
float xpGrantedFloat = (xpForNextLevel - xpForCurrentLevel)*percentage/100;
|
|
int xpGranted = (int)xpGrantedFloat;
|
|
|
|
if ( grantUnmodifiedXpByTemplate(player, xpGranted) > 0 )
|
|
{
|
|
return xpGranted;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************/
|
|
// moved from credit_for_kills
|
|
void applyHealingCredit(obj_id self)
|
|
{
|
|
deltadictionary scriptVars = self.getScriptVars();
|
|
Vector healers = scriptVars.getResizeableObjIdArray("healers");
|
|
if(healers != null)
|
|
{
|
|
Iterator i = healers.iterator();
|
|
dictionary params = new dictionary();
|
|
params.put("target", self);
|
|
while(i.hasNext())
|
|
{
|
|
obj_id healer = (obj_id)i.next();
|
|
if(isIdValid(healer))
|
|
{
|
|
messageTo(healer, "receiveCreditForCombatHealing", params, 0.0f, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void grantMissionXp(obj_id player, int missionLevel)
|
|
{
|
|
if(isIdValid(player) && missionLevel > 0)
|
|
{
|
|
int xpToGrantForRealsies = getMissionXpAmount(player, missionLevel);
|
|
|
|
if(xpToGrantForRealsies > 0)
|
|
{
|
|
|
|
grantCombatStyleXp(player, COMBAT_GENERAL, xpToGrantForRealsies);
|
|
displayXpFlyText(player, player, xpToGrantForRealsies); //xp flies off your own head!
|
|
|
|
//we subtract one here because the increment has not had time to write out and we all know about setting and getting an objvar in the same serer frame
|
|
int huySaidToUseTTForTheSecondNumber = ((missions.DAILY_MISSION_XP_REWARD - missions.getPlayerDailyCount(player)) -1);
|
|
|
|
prose_package pp = new prose_package();
|
|
prose.setStringId(pp, missions.DAILY_REWARD_XP);
|
|
prose.setDI(pp, xpToGrantForRealsies);
|
|
prose.setTT(pp, ""+huySaidToUseTTForTheSecondNumber);
|
|
sendSystemMessageProse(player, pp);
|
|
|
|
//LOG("NewMission", "grantMissionXp: " + xpToGrantForRealsies );
|
|
//LOG("NewMission", "grantMissionXpDailiesLeft: " + huySaidToUseTTForTheSecondNumber );
|
|
}
|
|
}
|
|
}
|
|
|
|
//lets just make sure people never go over x amount per mission
|
|
int getMissionXpAmount(obj_id player, int missionLevel)
|
|
{
|
|
int xpToGrant = 0;
|
|
int playerLevel = getLevel(player);
|
|
int xpForCurrentLevel = dataTableGetInt(TBL_PLAYER_LEVEL_XP, playerLevel - 1, "xp_required");
|
|
int xpForNextLevel = dataTableGetInt(TBL_PLAYER_LEVEL_XP, playerLevel, "xp_required");
|
|
int levelDivisor = 0;
|
|
//deltas should always be a positive number
|
|
int levelDelta = Math.abs(missionLevel - playerLevel);
|
|
|
|
LOG("NewMission", "getMissionXpAmountLevelDelta: " + levelDelta);
|
|
//player is 90
|
|
if(xpForNextLevel <= 0)
|
|
return xpToGrant;
|
|
|
|
int xpForLevel = xpForNextLevel - xpForCurrentLevel;
|
|
|
|
//scale xp based on level if you go more than 15 levels out of bounds
|
|
if(levelDelta > 15)
|
|
levelDivisor += levelDelta;
|
|
|
|
if(xpForLevel > 0)
|
|
{
|
|
//low mission divisor is more xp per mission, high mission deivisor is less xp per mission
|
|
int missionXpDivisor = missions.DAILY_MISSION_XP_LOW;
|
|
|
|
//start changing the divisor based on level
|
|
if(playerLevel >=70)
|
|
missionXpDivisor = missions.DAILY_MISSION_XP_MEDIUM;
|
|
|
|
if(playerLevel >=80)
|
|
missionXpDivisor = missions.DAILY_MISSION_XP_HIGH;
|
|
|
|
missionXpDivisor += levelDivisor;
|
|
|
|
//prevent divide by 0's
|
|
if(missionXpDivisor > 0)
|
|
{
|
|
xpToGrant = xpForLevel / missionXpDivisor;
|
|
|
|
//sanity check xp amount to make sure we aren't giving out too much xp
|
|
int sanityXpAmount = xpForLevel / missions.DAILY_MISSION_XP_SANITY;
|
|
if(xpToGrant > sanityXpAmount)
|
|
xpToGrant = sanityXpAmount;
|
|
}
|
|
}
|
|
return xpToGrant;
|
|
}
|
|
|
|
int grantCollectionXP(obj_id player, string collectionName)
|
|
{
|
|
float xpToGrant = 0;
|
|
//Get XP amounts for current and next Level
|
|
int playerLevel = getLevel(player);
|
|
if(playerLevel == 90)
|
|
return 0; //no reason to continue as the player cannot receive any xp at this point.
|
|
|
|
int xpForCurrentLevel = dataTableGetInt(TBL_PLAYER_LEVEL_XP, playerLevel - 1, "xp_required");
|
|
int xpForNextLevel = dataTableGetInt(TBL_PLAYER_LEVEL_XP, playerLevel, "xp_required");
|
|
//This gets the actual XP needed for the player's "currentLevel"
|
|
float xpForLevel = xpForNextLevel - xpForCurrentLevel;
|
|
//LOG("collectionXp", "you need: " + xpForLevel + " for your current level.");
|
|
//Get Collections XP Grant Modifier
|
|
float xpModifier = dataTableGetFloat(collection.COLLECTION_REWARD_TABLE, collectionName, "xpModifier");
|
|
|
|
/*This handles the reduction of XP rewards for repeatable collections. Repeat Collections
|
|
that need diminishing returns must update the invisible slot in the tracking collection
|
|
****************Tracking Collection Name: repeat_collection_xp_modifier******************/
|
|
long repeatSlotValue = getCollectionSlotValue(player, collectionName + "_tracker") - 1;
|
|
if(repeatSlotValue > 0)
|
|
{
|
|
float repeatXpModifier = repeatCollectionXpModifier(player, repeatSlotValue);
|
|
xpToGrant = xpModifier * xpForLevel * repeatXpModifier;
|
|
return (int)xpToGrant;
|
|
}
|
|
|
|
//Calculate the amount of XP player will receive for completing the collection
|
|
xpToGrant = xpModifier * xpForLevel;
|
|
//LOG("collectionXp", "You have been granted :" + xpToGrant);
|
|
return (int)xpToGrant;
|
|
}
|
|
|
|
int grantCollectionSpaceXP(obj_id player, string collectionName)
|
|
{
|
|
if(!isValidId(player))
|
|
return 0;
|
|
|
|
//validate the player has pilot skill
|
|
if(!space_skill.hasSpaceSkills(player))
|
|
return 0;
|
|
//validate the player has a profession prefix - mainly to help with debugging
|
|
string profPrefix = space_flags.getProfessionPrefix(player);
|
|
if(profPrefix == null || profPrefix.equals(""))
|
|
return 0;
|
|
|
|
float xpToGrant = 0.0f;
|
|
int xpForCurrentLevel = -1;
|
|
int xpForNextLevel = -1;
|
|
|
|
if(space_flags.hasCompletedTierFour(player))
|
|
{
|
|
string tierFourSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER4_INDEXSTART];
|
|
string masterSkill = profPrefix+"_master";
|
|
xpForCurrentLevel = dataTableGetInt(TBL_SKILL, tierFourSkill, "XP_COST");
|
|
xpForNextLevel = dataTableGetInt(TBL_SKILL, masterSkill, "XP_COST");
|
|
}
|
|
else if(space_flags.hasCompletedTierThree(player))
|
|
{
|
|
string tierThreeSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER3_INDEXSTART];
|
|
string tierFourSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER4_INDEXSTART];
|
|
xpForCurrentLevel = dataTableGetInt(TBL_SKILL, tierThreeSkill, "XP_COST");
|
|
xpForNextLevel = dataTableGetInt(TBL_SKILL, tierFourSkill, "XP_COST");
|
|
}
|
|
else if(space_flags.hasCompletedTierTwo(player))
|
|
{
|
|
string tierTwoSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER2_INDEXSTART];
|
|
string tierThreeSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER3_INDEXSTART];
|
|
xpForCurrentLevel = dataTableGetInt(TBL_SKILL, tierTwoSkill, "XP_COST");
|
|
xpForNextLevel = dataTableGetInt(TBL_SKILL, tierThreeSkill, "XP_COST");
|
|
|
|
}
|
|
else if(space_flags.hasCompletedTierOne(player))
|
|
{
|
|
string tierOneSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER1_INDEXSTART];
|
|
string tierTwoSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER2_INDEXSTART];
|
|
xpForCurrentLevel = dataTableGetInt(TBL_SKILL, tierOneSkill, "XP_COST");
|
|
xpForNextLevel = dataTableGetInt(TBL_SKILL, tierTwoSkill, "XP_COST");
|
|
}
|
|
else
|
|
{
|
|
string tierOneSkill = profPrefix+space_flags.SKILL_NAMES[space_flags.TIER1_INDEXSTART];
|
|
xpForCurrentLevel = 0;
|
|
xpForNextLevel = dataTableGetInt(TBL_SKILL, tierOneSkill, "XP_COST");
|
|
}
|
|
|
|
if(xpForCurrentLevel == -1 || xpForNextLevel == -1)
|
|
return 0;
|
|
|
|
float xpForLevel = xpForNextLevel - xpForCurrentLevel;
|
|
float xpModifier = dataTableGetFloat(collection.COLLECTION_REWARD_TABLE, collectionName, "xpModifier");
|
|
|
|
/*This handles the reduction of XP rewards for repeatable collections. Repeat Collections
|
|
that need diminishing returns must update the invisible slot in the tracking collection
|
|
****************Tracking Collection Name: repeat_collection_xp_modifier******************/
|
|
long repeatSlotValue = getCollectionSlotValue(player, collectionName + "_tracker") - 1;
|
|
if(repeatSlotValue > 0)
|
|
{
|
|
float repeatXpModifier = repeatCollectionXpModifier(player, repeatSlotValue);
|
|
xpToGrant = xpModifier * xpForLevel * repeatXpModifier;
|
|
return (int)xpToGrant;
|
|
}
|
|
|
|
xpToGrant = xpModifier * xpForLevel;
|
|
return (int)xpToGrant;
|
|
}
|
|
|
|
float repeatCollectionXpModifier(obj_id player, long repeatSlotValue)
|
|
{
|
|
float repeatMultiplier = 0.0f;
|
|
|
|
//debugging - let's spell it all out.
|
|
float adjustedRepeatSlotValue = (float)repeatSlotValue / 10.0f;
|
|
repeatMultiplier = 1.0f - adjustedRepeatSlotValue;
|
|
if(repeatMultiplier < 0.1f)
|
|
{
|
|
//lowest we give is 1/10th the amount.
|
|
repeatMultiplier = 0.1f;
|
|
}
|
|
return repeatMultiplier;
|
|
}
|