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