/********************************************************************** * Title: skill.scriptlib * Description: skill defines and skill-related function library **********************************************************************/ /***** INCLUDES ********************************************************/ include library.prose; include library.utils; include library.list; include library.jedi; include library.combat; include library.pclib; include library.space_flags; include library.buff; /***** CONSTANTS *******************************************************/ //general defines const string DELIM_RANGE = ".."; //SKILL TRAINER SCRIPTVARS const string SCRIPTVAR_SKILLS = "trainer.skills"; const string SCRIPTVAR_JEDI_SKILLS = "trainer.jedi_skills"; //SKILL TEMPLATE DEFINES const string DATATABLE_SKILL_TEMPLATE = "datatables/skill_template/skill_template.iff"; const string DATATABLE_RACIAL_STATS = "datatables/skill/racial_stats.iff"; const string DATATABLE_EXPERTISE = "datatables/expertise/expertise.iff"; const string DATATABLE_TEMPLATE = "TEMPLATE"; const string DATATABLE_BASE_SKILLS = "BASE_SKILLS"; const string DATATABLE_INHERITS_LIST = "INHERITS_TEMPLATES"; const string DATATABLE_RECURSE = "RECURSE"; //SKILL TEMPLATE NAME DEFINES const string PLAYER_BASE = "player_base"; const string PLAYER_BETA = "player_beta"; //SKILLMOD CHECK DEFINES const string DICT_CODE = "code"; const string DICT_DELTA = "delta"; const string DICT_DELTA_SCALE = "deltaScale"; const string DICT_DELTA_PERCENT = "deltaPercent"; const int CODE_FAIL = 0; const int CODE_PASS = 1; const int CODE_ERROR = -1; const int CODE_INSUFFICIENT_SKILL = -2; //Skill Tiers const int PHASE_ONE = 1; const int PHASE_TWO = 2; const int PHASE_THREE = 3; const int PHASE_FOUR = 4; // Stat things const int HEALTH_POINTS_PER_STAMINA = 2; const int HEALTH_POINTS_PER_CONSTITUTION = 8; const int ACTION_POINTS_PER_STAMINA = 8; const int ACTION_POINTS_PER_CONSTITUTION = 2; const int NUM_STATS = 6; // Difficulty calculation consts const string[] WEAPON_TYPES = {"unarmed", "polearm", "sword1h", "sword2h", "rifle", "carbine", "pistol"}; const string[] MOD_TYPES = {"accuracy", "speed", "damage"}; //SKILL GRANT DEFINES const string DICT_SKILLNAME = "skillName"; const string HANDLER_SKILL_GRANTED = "skillGranted"; // Jedi strings const string JEDI_SKILL_REQUIREMENTS_DATATABLE = "datatables/jedi/jedi_skill_requirements.iff"; //SOUND FILES const string DEFAULT_SKILL_GRANT_SOUND = "sound/music_acq_bountyhunter.snd"; //STRING IDS const string_id PROSE_NSF_SKILL_PTS = new string_id("base_player","prose_nsf_skill_pts"); const string_id PROSE_BAD_SPECIES = new string_id("base_player","prose_bad_species"); const string TBL_SKILL = "datatables/skill/skills.iff"; const string SKILL_N = "skl_n"; const string CONVOFILE = "skill_teacher"; const string_id PROSE_SKILL_LEARNED = new string_id(CONVOFILE, "prose_skill_learned"); const string_id PROSE_TRAIN_FAILED = new string_id(CONVOFILE, "prose_train_failed"); const string_id SID_EXPERTISE_WRONG_PROFESSION = new string_id("spam", "expertise_wrong_profession"); /***** FUNCTIONS **************************************************/ /***** GENERAL SKILL FUNCTIONS ************************************/ /***************************************************************** * @brief attempts to purchas a skill based on prereqs * * @param obj_id player * @param string skillName * * @return sboolean *****************************************************************/ boolean grant(obj_id target, string skillName) { if ( grantSkillToPlayer(target, skillName) ) { dictionary d = new dictionary(); d.put(DICT_SKILLNAME, skillName); messageTo(target, HANDLER_SKILL_GRANTED, d, 0, true); return true; } boolean litmus = grantSkill(target, skillName); return litmus; } /***************************************************************** * @brief attempts to purchas a skill based on prereqs * * @param obj_id player * @param string skillName * * @return sboolean *****************************************************************/ boolean grantSkillToPlayer(obj_id player, string skillName) { if ( !isIdValid(player) || ( !isPlayer(player) ) || skillName == null || skillName.equals("") ) { return false; } string_id sid_skillName = new string_id("skl_n",skillName); string dtSpeciesReq = dataTableGetString("datatables/skill/skills.iff",skillName,"SPECIES_REQUIRED"); if ( dtSpeciesReq != null && !dtSpeciesReq.equals("") ) { dictionary species = getSkillPrerequisiteSpecies(skillName); int speciesId = getSpecies(player); string speciesName = utils.getPlayerSpeciesName(speciesId); //debugSpeakMsg(player,"my species = " + speciesId + " -> " + speciesName); boolean allowSpecies = species.getBoolean(speciesName); //debugSpeakMsg(player,"allow species = " + allowSpecies); if ( !allowSpecies ) { string_id sid_species = new string_id("species",speciesName); prose_package ppBadSpecies = prose.getPackage(PROSE_BAD_SPECIES, sid_species, sid_skillName); sendSystemMessageProse(player, ppBadSpecies); return false; } } if ( grantSkill(player, skillName) ) { //if ( !isJedi(player) ) // checkForJediAbility(player, skillName, 15); string soundFile = DEFAULT_SKILL_GRANT_SOUND; if ( skillName.startsWith("combat_") ) { soundFile = DEFAULT_SKILL_GRANT_SOUND; } else if ( utils.isProfession(player, utils.TRADER) ) { soundFile = "sound/music_acq_academic.snd"; } else if ( skillName.startsWith("outdoors_") ) { if ( skillName.startsWith("outdoors_miner_") || skillName.startsWith("outdoors_farmer_") ) { soundFile = "sound/music_acq_academic.snd"; } else { soundFile = DEFAULT_SKILL_GRANT_SOUND; } } else if ( skillName.startsWith("science_") ) { soundFile = "sound/music_acq_healer.snd"; } else if ( skillName.startsWith("social_") ) { soundFile = "sound/music_acq_thespian.snd"; } else if ( skillName.startsWith("pilot_neutral") ) { if ( space_flags.isSpaceTrack( player, space_flags.PRIVATEER_TATOOINE ) ) soundFile = soundFile = "sound/music_themequest_acc_criminal.snd"; else soundFile = soundFile = "sound/music_themequest_acc_general.snd"; } else if ( skillName.startsWith("pilot_rebel") ) { soundFile = "sound/music_themequest_acc_rebel.snd"; } else if ( skillName.startsWith("pilot_imperial") ) { soundFile = "sound/music_themequest_acc_imperial.snd"; } //debugSpeakMsg(player, "skill grant soundfile = " + soundFile); if ( soundFile.equals("") ) { playMusic(player, DEFAULT_SKILL_GRANT_SOUND); } else { playMusic(player, soundFile); } return true; } return false; } /***************************************************************** * @brief attempts to purchas a skill based on prereqs * * @param obj_id player * @param string skillName * * @return sboolean *****************************************************************/ boolean purchaseSkill(obj_id player, string skillName) { if ( !isIdValid(player) || ( !isPlayer(player) ) || skillName == null || skillName.equals("") ) { return false; } boolean hasSkills = hasRequiredSkillsForSkillPurchase(player, skillName); boolean hasXp = hasRequiredXpForSkillPurchase(player, skillName); boolean alreadyHasSkill = hasSkill(player, skillName); if (hasSkills && hasXp && !alreadyHasSkill) { //Here we call the noisy one for pilot skills, so the player gets a system message: boolean skillGrantSuccessful = false; if ( skillName.startsWith("pilot_") ) skillGrantSuccessful = noisyGrantSkill( player, skillName );//grant and send the system message, too else skillGrantSuccessful = grantSkillToPlayer(player, skillName);//other skills send messages via the trainers, for some fucking reason if ( skillGrantSuccessful ) { if ( deductXpCostForSkillPurchase(player, skillName) ) { // Trigger Holocron event-Skill Purchased dictionary holocronParams = new dictionary(); holocronParams.put("eventName","TrainedSkillBox"); messageTo(player, "handleHolocronEvent", holocronParams, 0, false); return true; } else { revokeSkill(player, skillName); //debugSpeakMsg(player, "unable to deduct xp for skill purchase!"); CustomerServiceLog("Skill","skill.purchaseSkill(): ("+player+") "+getName(player)+" was unable to pay xp costs and had skill '"+skillName+"' revoked during purchase"); return false; } } //debugSpeakMsg(player, "unable to grant the skill!"); return false; } return false; } /***************************************************************** * @brief determines if the player has proper skills for skill purchase * * @param obj_id player * @param string skillName * * @return boolean *****************************************************************/ boolean hasRequiredSkillsForSkillPurchase(obj_id player, string skillName) { if ( !isIdValid(player) || ( !isPlayer(player) ) || skillName == null || skillName.equals("") ) { return false; } string[] pSkills = getSkillListingForPlayer(player); string[] skillReqs = getSkillPrerequisiteSkills(skillName); if ( skillReqs == null ) { return true; } if ( pSkills == null ) { //since skillReqs has elements at this point return false; } //both arrays have elements! return utils.isSubset(pSkills, skillReqs); } /***************************************************************** * @brief determines if the player has sufficient xp for skill purchase * * @param obj_id player * @param string skillName * * @return boolean *****************************************************************/ boolean hasRequiredXpForSkillPurchase(obj_id player, string skillName) { if ( !isIdValid(player) || ( !isPlayer(player) ) || skillName == null || skillName.equals("") ) { return false; } dictionary xpReqs = getSkillPrerequisiteExperience(skillName); if ( (xpReqs == null) || (xpReqs.isEmpty()) ) { return true; } boolean qualifies = true; java.util.Enumeration e = xpReqs.keys(); while ( e.hasMoreElements() ) { string xpType = (string)(e.nextElement()); int xpCost = xpReqs.getInt(xpType); if ( getExperiencePoints(player, xpType) < xpCost ) { qualifies = false; } } return qualifies; } /***************************************************************** * @brief deducts appropriate xp for skill purchase * * @param obj_id player * @param string skillName * * @return sboolean *****************************************************************/ boolean deductXpCostForSkillPurchase(obj_id player, string skillName) { if ( !isIdValid(player) || ( !isPlayer(player) ) || skillName == null || skillName.equals("") ) { return false; } dictionary xpReqs = getSkillPrerequisiteExperience(skillName); if ( (xpReqs == null) || (xpReqs.isEmpty()) ) { return true; } boolean qualifies = true; java.util.Enumeration e = xpReqs.keys(); while ( e.hasMoreElements() ) { string xpType = (string)(e.nextElement()); int xpCost = xpReqs.getInt(xpType); if ( xpCost != 0 ) { if ( getExperiencePoints(player, xpType) < xpCost ) { qualifies = false; } else { //debugSpeakMsg(player, "deducting xp: " + xpType + " - " + xpCost); qualifies &= (grantExperiencePoints(player, xpType, -xpCost) != XP_ERROR); } } } return qualifies; } /***************************************************************** * @brief collects and returns an array of all unique skills * required by the specified skill * * @param string skillName * * @return string[]; null on error *****************************************************************/ string[] getAllRequiredSkills(string skillName) { if ( skillName == null || skillName.equals("") ) { return null; } string[] reqs = getSkillPrerequisiteSkills(skillName); if ( (reqs == null) || (reqs.length == 0) ) { return null; } resizeable string[] ret = reqs; for ( int i = 0; i < reqs.length; i++ ) { string[] tmp = getAllRequiredSkills(reqs[i]); if ( (tmp == null) || (tmp.length == 0) ) { } else { for ( int n = 0; n < tmp.length; n++ ) { int pos = utils.getElementPositionInArray(ret, tmp[n]); if ( pos == -1 ) { ret = utils.addElement(ret, tmp[n]); //LOG("getAllRequiredSkills", "adding req: " + tmp[n]); } } } /*else { //LOG("getAllRequiredSkills", " - no reqs for " + reqs[i]); }*/ } return ret; } /***** SKILL ASSIGNMENT FUNCTIONS *********************************/ /***************************************************************** * @brief function to assign a creature a skill template * * @param obj_id target * @param string template * * @return boolean *****************************************************************/ boolean assignSkillTemplate(obj_id target, string template) { if ( !isIdValid(target) || (!isMob(target)) || template == null || template.equals("") ) { return false; } if ( dataTableOpen(DATATABLE_SKILL_TEMPLATE) ) { string[] templates = dataTableGetStringColumn(DATATABLE_SKILL_TEMPLATE, DATATABLE_TEMPLATE); if ( (templates == null) || (templates.length == 0) ) { return false; } for ( int i = 0; i < templates.length; i++ ) { if ( toLower(templates[i]).equals(toLower(template)) ) { string skill_list = dataTableGetString(DATATABLE_SKILL_TEMPLATE, i, DATATABLE_BASE_SKILLS); if ( skill_list.startsWith("\"") ) { skill_list = skill_list.substring(1); } if ( skill_list.endsWith("\"") ) { skill_list = skill_list.substring(0, skill_list.length() - 1); } //debugSpeakMsg(target, "skill_list = " + skill_list); if ( !skill_list.equals("") ) { int recurseIntVal = dataTableGetInt(DATATABLE_SKILL_TEMPLATE, i, DATATABLE_RECURSE); boolean recurse = false; switch (recurseIntVal) { case 0: break; case 1: recurse = true; break; default: break; } //debugSpeakMsg(target, "recurseIntVal = " + recurseIntVal); //debugSpeakMsg(target, "recurse = " + recurse); boolean litmus = true; java.util.StringTokenizer skills = new java.util.StringTokenizer(toLower(skill_list),","); while ( skills.hasMoreTokens() ) { string skillName = skills.nextToken(); //debugSpeakMsg(target, "receiving skill: " + skillName); litmus &= grantSkill(target, skillName); if ( recurse ) { //debugSpeakMsg(target, "recursing skill = " + skillName); string[] reqs = getAllRequiredSkills(skillName); if ( (reqs == null) || (reqs.length == 0) ) { //debugSpeakMsg(target, "no required skills for skill: " + skillName); } else { for ( int n = 0; n < reqs.length; n++ ) { //debugSpeakMsg(target, "recursive required skill apply: " + reqs[n]); litmus &= grantSkill(target, reqs[n]); } } } } string inherits_list = dataTableGetString(DATATABLE_SKILL_TEMPLATE, i, DATATABLE_INHERITS_LIST); if ( (inherits_list != null) && (!inherits_list.equals("")) ) { java.util.StringTokenizer inheritTokens = new java.util.StringTokenizer(toLower(inherits_list),","); while ( inheritTokens.hasMoreTokens() ) { string inheritTemplate = inheritTokens.nextToken(); if ( !inheritTemplate.equals("") ) { litmus &= assignSkillTemplate(target, inheritTemplate); } } } return litmus; } } } } return false; } /***************************************************************** * @brief revokes all skills on the target creature * * @param obj_id target * * @return boolean *****************************************************************/ boolean revokeAllSkills(obj_id target) { if ( !isIdValid(target) || (!isMob(target)) ) { return false; } string skills[] = getSkillListingForPlayer(target); if ( (skills == null) || (skills.length == 0) ) { return false; } for ( int i = 0; i < skills.length; i++ ) { revokeSkill(target, skills[i]); CustomerServiceLog("Skill","skill.revokeAllSkills(): ("+target+") "+getName(target)+" is having all skills revoked!"); } return true; } void revokeAllProfessionSkills(obj_id player) { string[] skillList = getSkillListingForPlayer(player); int attempts = skillList.length; if ((skillList != null) && (skillList.length != 0)) { while(skillList.length > 0 && attempts > 0) { for (int i = 0; i < skillList.length; i++) { string skillName = skillList[i]; if (!skillName.startsWith ("species_")&& //don't revoke species skills !skillName.startsWith("social_language_")&& !skillName.startsWith("social_politician_") && !skillName.startsWith("utility_")&& !skillName.startsWith("common_")&& !skillName.startsWith("demo_")&& !skillName.startsWith("force_title_")&& !skillName.startsWith("force_sensitive_")&& !skillName.startsWith("combat_melee_basic")&& !skillName.startsWith("pilot_")&& !skillName.startsWith("internal_expertise_") && !skillName.startsWith("combat_ranged_weapon_basic")&& !skillName.equals("expertise")) { revokeSkillSilent(player, skillName); } } // The new skill list is the remaining skills. skillList = getSkillListingForPlayer(player); // Prevent data from creating an infinite loop. --attempts; } } } /***** SKILL TEACHER FUNCTIONS ***********************************/ /***************************************************************** * @brief returns skills that the target qualifies to learn * based on skill reqs only * * @param obj_id target * @param obj_id teacher * * @return string[]; null on error *****************************************************************/ string[] getTeachableSkills(obj_id target, obj_id teacher) { return null; /* if ( !isIdValid(target) || (!isMob(target)) || !isIdValid(teacher) ) { return null; } //NEWBIE HALL HACK: if ( hasObjVar( target, "newbie.hasSkill" ) && !hasObjVar( target, "newbie.trained")) { string skillName = getStringObjVar( target, "newbie.hasSkill" ); string[] retSkills = new string[1]; retSkills[0] = skillName; return retSkills; } string[] targetSkills = getSkillListingForPlayer(target); if ( (targetSkills == null) || (targetSkills.length == 0) ) { //debugSpeakMsg(teacher, getName(target) + "'s skill list is null!"); return null; } string[] delta = null; if ( isPlayer(teacher) ) { //debugSpeakMsg(teacher, "getTeachableSkills: calling deltaPlayerTeacherSkills..." ); delta = deltaPlayerTeacherSkills(target, teacher); } else { //debugSpeakMsg(teacher, "getTeachableSkills: calling deltaTeacherSkills..." ); delta = deltaTeacherSkills(target, teacher); } if ( (delta == null) || (delta.length == 0) ) { //debugSpeakMsg(teacher, "getTeachableSkills: delta = null or length = 0"); return null; } //debugSpeakMsg(teacher, "delta.length = " + delta.length); resizeable string[] teachableSkills = new string[0]; for ( int i = 0; i < delta.length; i++ ) { string[] reqs = getSkillPrerequisiteSkills(delta[i]); if ( utils.isSubset(targetSkills, reqs) || reqs == null || reqs.length == 0 ) { if ( delta[i].startsWith("species_") ) { } else if ( delta[i].equals("social_language_lekku") ) { } else if ( delta[i].equals("demo_combat") ) { } else if ( delta[i].startsWith("force_rank") ) { } else if ( delta[i].startsWith("expertise") ) { } else if ( delta[i].startsWith("force_title") ) { } else if ( delta[i].startsWith( "pilot_" ) ) { //you can't teach pilot skills. } else if ( delta[i].startsWith( "social_language_hutt" ) ) { //you can't teach the Huttese language. It is earned via quest. } //else if ( delta[i].startsWith("force_sensitive") ) //{ // //} // Don't allow a Jedi to start the light path if they are already on the dark path else if ( delta[i].equals("jedi_light_side_journeyman_novice") && hasSkill(target, "jedi_dark_side_journeyman_novice") ) { } // Don't allow a Jedi to start the dark path if they are already on the light path else if ( delta[i].equals("jedi_dark_side_journeyman_novice") && hasSkill(target, "jedi_light_side_journeyman_novice") ) { } else { teachableSkills = utils.addElement(teachableSkills, delta[i]); } } } if ( (teachableSkills == null) || (teachableSkills.length == 0) ) { return null; } return teachableSkills; */ } /***************************************************************** * @brief returns skills that the target qualifies to learn * based on skill reqs AND xp reqs * * @param obj_id target * @param obj_id teacher * * @return string[]; null on error *****************************************************************/ string[] getQualifiedTeachableSkills(obj_id target, obj_id teacher) { //debugSpeakMsg( teacher, "Unf" ); if ( !isIdValid(target) || (!isMob(target)) || (isIdNull(teacher)) || (!isMob(teacher)) ) { return null; } string[] teachableSkills = getTeachableSkills(target, teacher); if ( teachableSkills == null ) { //debugSpeakMsg(teacher, "No teachable skills"); return null; } resizeable string[] qualifiedSkills = new string[0]; for ( int i = 0; i < teachableSkills.length; i++ ) { //debugSpeakMsg(teacher, "Checking skill: " + teachableSkills[i]); boolean qualifies = true; dictionary d = getSkillPrerequisiteExperience(teachableSkills[i]); if ( d == null || d.isEmpty()) { //debugSpeakMsg(teacher, "No requisite experience for skill = " + teachableSkills[i]); } else { java.util.Enumeration keys = d.keys(); int n = 0; while (keys.hasMoreElements()) { Object o = keys.nextElement(); if ( o instanceof string ) { string xpType = (string)o; int xpCost = d.getInt(xpType); int playerXP = getExperiencePoints(target, xpType); //debugSpeakMsg(teacher, "[" + n + "] " + xpType + ": playerXP (" + playerXP + ") -> xpCost (" + xpCost + ")"); if ( playerXP >= xpCost ) { //qualifies here! } else { qualifies = false; } } else { //debugSpeakMsg(teacher, "error parsing dictionary key data!"); return null; } } } // Check to make certain that the skill is not a species only skill. dictionary species = getSkillPrerequisiteSpecies(teachableSkills[i]); if (species != null && !d.isEmpty() ) { java.util.Enumeration species_keys = species.keys(); while (species_keys.hasMoreElements()) { Object o = species_keys.nextElement(); if ( o instanceof string ) { string key = (string)o; if (species.getBoolean(key)) qualifies = false; } } } // If the trainer is the force sensitive trainer, make sure that the player has unlocked the skill. //LOG("quest", "skill ->" + teachableSkills[i]); string trainer_type = getStringObjVar(teacher, "trainer"); if (trainer_type != null && trainer_type.equals("trainer_fs")) { if (qualifies) { //LOG("quest", "... qualifies"); if (fs_quests.isVillageEligible(target)) { //LOG("quest", "...... village eligible"); string branch = fs_quests.getBranchFromSkill(teachableSkills[i]); if (!fs_quests.hasUnlockedBranch(target, branch)) qualifies = false; //else // LOG("quest", "............ has unlocked"); } else qualifies = false; } } //NEWBIE HALL HACK: if ( hasObjVar( target, "newbie.hasSkill" ) && !hasObjVar( target,"newbie.trained" )) { string skillName = getStringObjVar( target, "newbie.hasSkill" ); //debugSpeakMsg( target, "i have " + skillName ); //debugSpeakMsg( teacher, "i teach " + teachableSkills[i] ); if ( skillName.equals( teachableSkills[i] )) qualifies = true; } if ( qualifies ) { qualifiedSkills = utils.addElement(qualifiedSkills, teachableSkills[i]); } } if ( (qualifiedSkills != null) && (qualifiedSkills.length > 0) ) { return qualifiedSkills; } return null; } /***************************************************************** * @brief returns the skills that the teacher has but the target * does not * * @param obj_id target * @param obj_id teacher * * @return string[]; null on error *****************************************************************/ string[] deltaTeacherSkills(obj_id target, obj_id teacher) { if ( !isIdValid(target) || !isIdValid(teacher) ) { obj_id self = getSelf(); //debugSpeakMsg(self, "deltaTeacherSkills: passed invalid param..."); return null; } if ( isPlayer(teacher) ) return deltaPlayerTeacherSkills(target, teacher); string[] targetSkills = getSkillListingForPlayer(target); string[] teacherSkills = getTeacherSkills(teacher, target); if ( targetSkills == null || teacherSkills == null ) return null; resizeable string[] delta = new string[0]; for ( int i =0; i < teacherSkills.length; i++ ) { if ( utils.getElementPositionInArray(targetSkills, teacherSkills[i]) == -1 ) { delta = utils.addElement(delta, teacherSkills[i]); } } if ( (delta != null) && (delta.length != 0) ) { return delta; } //debugSpeakMsg(teacher, "deltaTeacherSkills: skill delta error... returning null"); return null; } string[] getTeacherSkills(obj_id teacher, obj_id target) { if ( !isIdValid(teacher) || !isIdValid(target) ) return null; if ( !jedi.isJediTrainerForPlayer(target, teacher) ) { return utils.getStringBatchScriptVar(teacher, SCRIPTVAR_SKILLS); } else { return utils.getStringBatchScriptVar(teacher, SCRIPTVAR_JEDI_SKILLS); } } /***************************************************************** * @brief returns the skills that the teacher has but the target * does not * * @param obj_id target * @param obj_id teacher * * @return string[]; null on error *****************************************************************/ string[] deltaPlayerTeacherSkills(obj_id target, obj_id teacher) { if ( !isIdValid(target) || !isIdValid(teacher) ) return null; if ( !isPlayer(teacher) ) return deltaTeacherSkills(target, teacher); string[] targetSkills = getSkillListingForPlayer(target); string[] teacherSkills = getSkillListingForPlayer(teacher); if ( (targetSkills == null) || (teacherSkills == null) ) { return null; } resizeable string[] delta = new string[0]; for ( int i =0; i < teacherSkills.length; i++ ) { if ( utils.getElementPositionInArray(targetSkills, teacherSkills[i]) == -1 ) { delta = utils.addElement(delta, teacherSkills[i]); } } if ( (delta != null) && (delta.length != 0) ) { return delta; } //debugSpeakMsg(teacher, "deltaPlayerTeacherSkills: skill delta error... returning null"); return null; } /***** SKILL CHECK FUNCTIONS ************************************/ /***************************************************************** * @brief wrapper for doing skill checks * * @param obj_id target * @param string skillmod * @param string scale * * @return int; -1 on error *****************************************************************/ int check(obj_id target, string skillmod, string scale) { if ( !isIdValid(target) || (skillmod.equals("")) ) { //bad args return -1; } dictionary params = skillModCheck(target, skillmod, scale); int retCode = params.getInt(DICT_CODE); if ( retCode < 0 ) { //there was an error in the skillModCheck - dump out of function return -1; } int deltaPercent = params.getInt(DICT_DELTA_PERCENT); if ( (retCode == CODE_PASS) && (deltaPercent >= 0) ) //success { //award XP } return deltaPercent; } /***************************************************************** * @brief performs a skillModCheck for the specified target & skillmod * on the specified scale and returns the delta, deltaScale, * and deltaPercent * * @param obj_id target * @param string skillmod * @param string scale * * @return dictionary *****************************************************************/ dictionary skillModCheck(obj_id target, string skillmod, string scale) { dictionary ret = new dictionary(); if ( !isIdValid(target) || (skillmod.equals("")) ) { ret.put(DICT_CODE, CODE_ERROR); return ret; } int modmin = 1; int modmax = 100; int scaleIdx = scale.indexOf(DELIM_RANGE); if ( scale.equals("") ) { //do nothing } else if ( scaleIdx > 0 ) { java.util.StringTokenizer rng = new java.util.StringTokenizer(scale, DELIM_RANGE); string smin = ""; string smax = ""; if ( rng.countTokens() == 2 ) { smin = rng.nextToken(); smax = rng.nextToken(); } else { ret.put(DICT_CODE, CODE_ERROR); return ret; } modmin = utils.stringToInt(smin); modmax = utils.stringToInt(smax); } else { //try to parse scale as max modmax = utils.stringToInt(scale); } if ( (modmin < 0) || (modmax <= 0) || (modmin >= modmax) ) { //malformed scale data ret.put(DICT_CODE, CODE_ERROR); return ret; } int modval = getSkillStatMod(target, skillmod); if ( modval < modmin ) { //insufficient skill! ret.put(DICT_CODE, CODE_INSUFFICIENT_SKILL); return ret; } int deltaScale = modmax - modmin; int roll = rand(modmin, modmax); int delta = modval - roll; int deltaPercent = (int)((delta/deltaScale)*100); if ( delta >= 0 ) { //success ret.put(DICT_CODE, CODE_PASS); } else { //fail ret.put(DICT_CODE, CODE_FAIL); } ret.put(DICT_DELTA, delta); ret.put(DICT_DELTA_SCALE, deltaScale); ret.put(DICT_DELTA_PERCENT, deltaPercent); return ret; } int getGroupLevel(obj_id objPlayer) { obj_id objGroup = getGroupObject(objPlayer); if (isIdValid(objGroup)) return getGroupObjectLevel(objGroup); return getLevel(objPlayer); } /** * Checks if a skill a player just got causes their Jedi slot to open. */ void checkForJediAbility(obj_id objPlayer, string strSkill, int intDelay) { // see if the Jedi system is enabled if ( !getEnableNewJediTracking() ) { LOG("jedi", "System disabled"); return; } // Jedi should never call this function. CS is seeing them show up in the logs if (isJedi(objPlayer)) return; // if we have the jedi.timestamp objvar, then we are definitely supposed to be a Jedi // if (hasObjVar(objPlayer, "jedi.timeStamp")) { // make sure we don't have the skill requirements objvar removeObjVar(objPlayer, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS); return; } resizeable string[] strSkillsNeeded = getResizeableStringArrayObjVar(objPlayer, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS); if(strSkillsNeeded==null) { if(!hasJediSlot(objPlayer)) { if(!hasObjVar(objPlayer, "jedi.timeStamp")) { if (!hasObjVar(objPlayer, "jedi.postponeGrant")) { // you're fucked CustomerServiceLog("jedi", "CATASTROPHIC JEDI FAILURE, DATA IS MUNGED ON "+objPlayer); } } } LOG("jedi", "no array"); // already jedi qualified, so go away return; } if (strSkillsNeeded.length == 0) { // Not a Jedi, but was qualified already LOG("jedi", "Array is 0?"); return; } int intElement = utils.getElementPositionInArray(strSkillsNeeded, strSkill); LOG("jedi", "checking for "+strSkill+" intIndex is "+intElement); if (intElement != -1) strSkillsNeeded = utils.removeElementAt(strSkillsNeeded, intElement); updateJediSkillRequirements(objPlayer, strSkillsNeeded); } /** * Updates the list of skills we need to become a Jedi, and starts the Jedi process if we have met all our requirements. */ void updateJediSkillRequirements(obj_id player, resizeable string[] skillsNeeded) { if ( !isIdValid(player) || isJedi(player) || skillsNeeded == null ) return; if ( skillsNeeded.length > 0 ) { setObjVar(player, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS, skillsNeeded); if (skillsNeeded.length <= 4) CustomerServiceLog("jedi", "CLOSE_PLAYER: " + getFirstName(player) + " (" + player + ") needs " + skillsNeeded.length + " more skills to activate their FS slot."); else if (skillsNeeded.length <= 6) CustomerServiceLog("jedi", getFirstName(player) + " (" + player + ") needs " + skillsNeeded.length + " more skills to activate their FS slot."); } else { removeObjVar(player, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS); setObjVar(player, "jedi.timeStamp", 0); messageTo(player, "makeJedi", null, 0.1f, false); } } /** * Sets the skills that a player will need to open up their Jedi slot. */ void setJediSkills(obj_id objPlayer) { if ( !isJedi(objPlayer) && !hasJediSlot(objPlayer) && !hasObjVar(objPlayer, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS)) { // one shot! int numRequired = 0; string[] strSkills = dataTableGetStringColumn(JEDI_SKILL_REQUIREMENTS_DATATABLE, "strSkillList"); numRequired = dataTableGetInt(JEDI_SKILL_REQUIREMENTS_DATATABLE, 0, "intNumRequired"); debugServerConsoleMsg(null, "intNumRequired is " + numRequired); if (numRequired > strSkills.length) { debugServerConsoleMsg(null, "NO JEDI ENABLED, SKILLS REQUIRED GREATER THAN SKILLS IN LIST!"); return; } int numAvailable = strSkills.length; string[] strSkillsNeeded = new string[numRequired]; for (int i = 0; i < numRequired; ++i) { int randomNumber = rand(0, numAvailable - 1); strSkillsNeeded[i] = strSkills[randomNumber]; // move remaining available skills down the list for ( int j = randomNumber; j < numAvailable - 1; ++j ) { strSkills[j] = strSkills[j+1]; } strSkills[--numAvailable] = null; } if(strSkillsNeeded.length > 0) { setObjVar(objPlayer, pclib.OBJVAR_JEDI_SKILL_REQUIREMENTS, strSkillsNeeded); } else { CustomerServiceLog("jedi", "Failed to set jedi required skills for %TU", objPlayer); } } } void fixTerrainNegotiationMods(obj_id player) { if (hasObjVar(player, "_notskill.mods.slope_move")) { int mod = getIntObjVar(player, "_notskill.mods.slope_move"); applySkillStatisticModifier(player, "slope_move", -mod); } } boolean noisyGrantSkill( obj_id player, string skillName ) { if ( grantSkillToPlayer( player, skillName ) ) { prose_package pp = prose.getPackage(PROSE_SKILL_LEARNED, new string_id(SKILL_N, skillName)); sendSystemMessageProse(player, pp); return true; } else { prose_package pp = prose.getPackage(PROSE_TRAIN_FAILED, new string_id(SKILL_N, skillName)); sendSystemMessageProse(player, pp); return false; } } void doPlayerLeveling(obj_id objPlayer, int intOldLevel, int intNewLevel) { int levelDelta = intNewLevel - intOldLevel; // Only play special effects if you leveled up for reals, identified by gaining exactly 1 level. // On loading you get leveled up from -1 to your actual level. if(levelDelta == 1) { showFlyText(objPlayer, new string_id("cbt_spam", "level_up"), 2.5f, colors.CORNFLOWERBLUE); playClientEffectObj(objPlayer, "clienteffect/level_granted.cef", objPlayer, null); } newbieTutorialHighlightUIElement (objPlayer, "/GroundHUD.MFDStatus.vsp.role.targetLevel", 5.0f); // now we do cool shit // what level setPlayerStatsForLevel(objPlayer, intNewLevel); return; } void setPlayerStatsForLevel(obj_id objPlayer, int intLevel) { //LOG("base_player - OnInitialize", "setPlayerStatsForLevel: LEVEL "+intLevel); if(intLevel < 1) { LOG("skill.scriptlib", "getPlayerStatForLevel BAD level"); return; } string strProfession = getProfessionName(getSkillTemplate(objPlayer)); player_levels.level_data stats = player_levels.getPlayerLevelData(strProfession, intLevel); if(stats == null) { LOG("skill.scriptlib", "setPlayerStatsForLevel stats == null"); return; } //LOG("base_player - OnInitialize", "setPlayerStatsForLevel: PROFESION "+strProfession); if((strProfession!=null)&&(strProfession!="")) { int intStat = getPlayerStatForLevel(objPlayer, intLevel, "luck"); if(intStat>=0) { int oldStat = getSkillStatMod(objPlayer, "luck"); applySkillStatisticModifier( objPlayer, "luck", -oldStat); applySkillStatisticModifier( objPlayer, "luck", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "precision"); if(intStat>=0) { // ranged combat state int oldStat = getSkillStatMod(objPlayer, "precision"); applySkillStatisticModifier( objPlayer, "precision", -oldStat); applySkillStatisticModifier( objPlayer, "precision", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "strength"); if(intStat>=0) { // melee combat stat int oldStat = getSkillStatMod(objPlayer, "strength"); applySkillStatisticModifier( objPlayer, "strength", -oldStat); applySkillStatisticModifier( objPlayer, "strength", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "constitution"); if(intStat>=0) { // modifier, change applicable pools please int oldStat = getSkillStatMod(objPlayer, "constitution"); applySkillStatisticModifier( objPlayer, "constitution", -oldStat); applySkillStatisticModifier( objPlayer, "constitution", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "stamina"); if(intStat>=0) { // modifier, change applicable pools please int oldStat = getSkillStatMod(objPlayer, "stamina"); applySkillStatisticModifier( objPlayer, "stamina", -oldStat); applySkillStatisticModifier( objPlayer, "stamina", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "agility"); if(intStat>=0) { int oldStat = getSkillStatMod(objPlayer, "agility"); applySkillStatisticModifier( objPlayer, "agility", -oldStat); applySkillStatisticModifier( objPlayer, "agility", intStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "health"); if(intStat>=0) { setMaxAttrib(objPlayer, HEALTH, intStat); } float fltStat = stats.health_regen; if(fltStat>=0) { setRegenRate(objPlayer, HEALTH, fltStat); } intStat = getPlayerStatForLevel(objPlayer, intLevel, "action"); if(intStat>=0) { setMaxAttrib(objPlayer, ACTION, intStat); } fltStat = stats.action_regen; if(fltStat>=0) { setRegenRate(objPlayer, ACTION, fltStat); } recalcPlayerPools(objPlayer, true); } return; } int getPlayerStatForLevel(obj_id player, int intLevel, string statString) { int intStat = 0; if(intLevel < 1 || intLevel > 90) { LOG("skill.scriptlib", "getPlayerStatForLevel BAD level"); return 0; } int playerRace = getSpecies(player); string strProfession = getProfessionName(getSkillTemplate(player)); player_levels.level_data stats = player_levels.getPlayerLevelData(strProfession, intLevel); if(stats == null) { LOG("skill.scriptlib", "setPlayerStatsForLevel stats == null"); return 0; } // Set new races int value so they work with the datatable. Ithorian = 33(8), sullustan = 49(9). if(playerRace > 7) { if(playerRace == 33) playerRace = 8; else if(playerRace == 49) playerRace = 9; else { sendSystemMessageTestingOnly(player, "Unknown race, defaulting to human"); LOG("npe", "library.skill - getPlayerStatForLevel invalid race, using human defaults"); playerRace = 0; } } int racialStatColumnReference = (playerRace * NUM_STATS); //sendSystemMessageTestingOnly(player, "playerRace = " + playerRace); if(statString.equals("luck")) { intStat = stats.luck; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 1); return intStat; } if(statString.equals("precision")) { intStat = stats.precision; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 2); return intStat; } if(statString.equals("strength")) { intStat = stats.strength; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 3); return intStat; } if(statString.equals("constitution")) { intStat = stats.constitution; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 4); return intStat; } if(statString.equals("stamina")) { intStat = stats.stamina; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 5); return intStat; } if(statString.equals("agility")) { intStat = stats.agility; intStat += dataTableGetInt(DATATABLE_RACIAL_STATS, intLevel-1, racialStatColumnReference + 6); return intStat; } if(statString.equals("health")) { intStat = stats.health; return intStat; } if(statString.equals("action")) { intStat = stats.action; return intStat; } return intStat; } // Sends a system message for each stat that increased. void sendlevelUpStatChangeSystemMessages(obj_id player, int oldCombatLevel, int newCombatLevel) { if(!isPlayer(player)) { return; } if(newCombatLevel < 1 || newCombatLevel > 90) { return; } // onCombatLevelChanged fires EVERYTIME YOU LOAD so only do this if you gained 1 level. // Clearly we need an onCombatLevelChangedForReals trigger. int levelDelta = newCombatLevel - oldCombatLevel; if(levelDelta != 1) { return; } string[] statStrings = { "luck", "precision", "strength", "constitution", "stamina", "agility", }; for(int i = 0; i < statStrings.length; i++) { int currentStat = getPlayerStatForLevel(player, newCombatLevel, statStrings[i]); int lastStat = getPlayerStatForLevel(player, oldCombatLevel, statStrings[i]); int statDelta = currentStat - lastStat; if(statDelta > 0) { prose_package ppStatGainSpam = new prose_package(); ppStatGainSpam.stringId = new string_id("spam","level_up_stat_gain_"+i); ppStatGainSpam.digitInteger = statDelta; sendSystemMessageProse(player, ppStatGainSpam); } } // Determine and display actual change in health and action including CON and STA mods. int currentCon = getPlayerStatForLevel(player, newCombatLevel, "constitution"); int lastCon = getPlayerStatForLevel(player, oldCombatLevel, "constitution"); int currentSta = getPlayerStatForLevel(player, newCombatLevel, "stamina"); int lastSta = getPlayerStatForLevel(player, oldCombatLevel, "stamina"); int currentHealth = getPlayerStatForLevel(player, newCombatLevel, "health"); int lastHealth = getPlayerStatForLevel(player, oldCombatLevel, "health"); int currentAction = getPlayerStatForLevel(player, newCombatLevel, "action"); int lastAction = getPlayerStatForLevel(player, oldCombatLevel, "action"); int forRealsHealthCurrent = currentHealth + (currentCon * HEALTH_POINTS_PER_CONSTITUTION) + (currentSta * HEALTH_POINTS_PER_STAMINA); int forRealsHealthLast = lastHealth + (lastCon * HEALTH_POINTS_PER_CONSTITUTION) + (lastSta * HEALTH_POINTS_PER_STAMINA); int forRealsActionCurrent = currentAction + (currentCon * ACTION_POINTS_PER_CONSTITUTION) + (currentSta * ACTION_POINTS_PER_STAMINA); int forRealsActionLast = lastAction + (lastCon * ACTION_POINTS_PER_CONSTITUTION) + (lastSta * ACTION_POINTS_PER_STAMINA); int healthDelta = forRealsHealthCurrent - forRealsHealthLast; int actionDelta = forRealsActionCurrent - forRealsActionLast; if(healthDelta > 0) { prose_package ppStatGainSpam = new prose_package(); ppStatGainSpam.stringId = new string_id("spam","level_up_stat_gain_6"); ppStatGainSpam.digitInteger = healthDelta; sendSystemMessageProse(player, ppStatGainSpam); } if(actionDelta > 0) { prose_package ppStatGainSpam = new prose_package(); ppStatGainSpam.stringId = new string_id("spam","level_up_stat_gain_7"); ppStatGainSpam.digitInteger = actionDelta; sendSystemMessageProse(player, ppStatGainSpam); } return; } string getProfessionName(string strTemplate) { player_levels.skill_template_data professionData = player_levels.getSkillTemplateData(strTemplate); if(professionData == null) { return null; } return professionData.strClassName; } void recalcPlayerPools(obj_id objPlayer, boolean boolHealEverything) { // FIX WOUNDS // get health from datatable //get action from datatable // get skill mod for con // get skill mod for stam // calculate additions // set max health // set max action // CON is 80/20 health/action // Stamina is 80/20 action/health if(!isPlayer(objPlayer)) { return; } int intLevel = getLevel(objPlayer); if(intLevel < 0) { intLevel = 1; } else { if(intLevel > 90) { intLevel = 90; } } string strProfession = getProfessionName(getSkillTemplate(objPlayer)); if(strProfession != null && strProfession.length() > 0) { player_levels.level_data stats = player_levels.getPlayerLevelData(strProfession, intLevel); if(stats == null) { LOG("skill.scriptlib", "recalcPlayerPools stats == null"); return; } int intBaseHealth = stats.health; int intBaseAction = stats.action; //sendSystemMessageTestingOnly(objPlayer, "profession is "+strProfession); int intConstitution = getSkillStatisticModifier(objPlayer, "constitution"); int intStamina = getSkillStatisticModifier(objPlayer, "stamina"); intConstitution += getEnhancedSkillStatisticModifierUncapped(objPlayer, "constitution_modified"); intStamina += getEnhancedSkillStatisticModifierUncapped(objPlayer, "stamina_modified"); //sendSystemMessageTestingOnly(objPlayer, "Con is "+intConstitution); //sendSystemMessageTestingOnly(objPlayer, "Stamina is "+intStamina); //sendSystemMessageTestingOnly(objPlayer, "intBaseHealth1 is "+intBaseHealth); //sendSystemMessageTestingOnly(objPlayer, "intBaseAction1 is "+intBaseAction); intBaseHealth = intBaseHealth + (HEALTH_POINTS_PER_CONSTITUTION * intConstitution); intBaseHealth = intBaseHealth + (HEALTH_POINTS_PER_STAMINA * intStamina); intBaseAction = intBaseAction + (ACTION_POINTS_PER_STAMINA * intStamina); intBaseAction = intBaseAction + (ACTION_POINTS_PER_CONSTITUTION * intConstitution); //sendSystemMessageTestingOnly(objPlayer, "intBaseHealth2 is "+intBaseHealth); //sendSystemMessageTestingOnly(objPlayer, "intBaseAction2 is "+intBaseAction); setMaxAttrib(objPlayer, ACTION, intBaseAction); setMaxAttrib(objPlayer, HEALTH, intBaseHealth); int[] myBuffs = buff.getAllBuffs(objPlayer); for(int i = 0; i < myBuffs.length; i++) { string thisBuffEffect = buff.getEffectParam(myBuffs[i], 1); float thisBuffValue = buff.getEffectValue(myBuffs[i], 1); if(thisBuffEffect != null && thisBuffValue < 0 && thisBuffEffect.equals("healthPercent")) { boolHealEverything = false; break; } } if(boolHealEverything) { setAttrib(objPlayer, ACTION, getMaxAttrib(objPlayer, ACTION)); setAttrib(objPlayer, HEALTH, getMaxAttrib(objPlayer, HEALTH)); } } return; } void grantAllPoliticianSkills( obj_id player ) { string[] skillNames = { "social_politician_novice", "social_politician_fiscal_01", "social_politician_fiscal_02", "social_politician_fiscal_03", "social_politician_fiscal_04", "social_politician_martial_01", "social_politician_martial_02", "social_politician_martial_03", "social_politician_martial_04", "social_politician_civic_01", "social_politician_civic_02", "social_politician_civic_03", "social_politician_civic_04", "social_politician_urban_01", "social_politician_urban_02", "social_politician_urban_03", "social_politician_urban_04", "social_politician_master" }; //set this to suppress skillUp particle effects setObjVar( player, "clickRespec.granting", true ); for ( int i = 0; i < skillNames.length; i++ ) { if (!hasSkill(player, skillNames[i]) ) grantSkill( player, skillNames[i] ); } removeObjVar( player, "clickRespec.granting"); } /********************************************************* * Checks for the existance of a profession skill based on the * wheel of fun. Existance of a phase skill will deterine tier * * obj_id player * * return string *********************************************************/ int getProfessionPhase(obj_id player) { string[] phaseFourSkills = { "class_forcesensitive_phase4_novice", "class_bountyhunter_phase4_novice", "class_smuggler_phase4_novice", "class_commando_phase4_novice", "class_officer_phase4_novice", "class_spy_phase4_novice", "class_medic_phase4_novice", "class_entertainer_phase4_novice", "class_domestics_phase4_novice", "class_structures_phase4_novice", "class_munitions_phase4_novice", "class_engineering_phase4_novice" }; string[] phaseThreeSkills = { "class_forcesensitive_phase3_novice", "class_bountyhunter_phase3_novice", "class_smuggler_phase3_novice", "class_commando_phase3_novice", "class_officer_phase3_novice", "class_spy_phase3_novice", "class_medic_phase3_novice", "class_entertainer_phase3_novice", "class_domestics_phase3_novice", "class_structures_phase3_novice", "class_munitions_phase3_novice", "class_engineering_phase3_novice" }; string[] phaseTwoSkills = { "class_forcesensitive_phase2_novice", "class_bountyhunter_phase2_novice", "class_smuggler_phase2_novice", "class_commando_phase2_novice", "class_officer_phase2_novice", "class_spy_phase2_novice", "class_medic_phase2_novice", "class_entertainer_phase2_novice", "class_domestics_phase2_novice", "class_structures_phase2_novice", "class_munitions_phase2_novice", "class_engineering_phase2_novice" }; for (int i=0;i