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

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