Files
dsrc/sku.0/sys.server/compiled/game/script/library/beast_lib.scriptlib
T

6355 lines
145 KiB
Plaintext

/**
* Copyright (c) ©2007 Sony Online Entertainment Inc.
* All Rights Reserved
*
* Title: library.beast_lib
* Description:
* @author teidson
* @version $Revision: 0$
*/
include ai.ai_combat;
include java.util.Arrays;
include java.util.Vector;
include library.ai_lib;
include library.armor;
include library.buff;
include library.callable;
include library.colors;
include library.corpse;
include library.group;
include library.hue;
include library.incubator;
include library.instance;
include library.pet_lib;
include library.prose;
include library.stealth;
include library.storyteller;
include library.utils;
include library.xp;
/***** CONSTANTS ****************************************************/
const string BEASTS_TABLE = "datatables/beast/beasts.iff";
const string BEASTS_STATS = "datatables/beast/beast_stats.iff";
const string BEASTS_SPECIALS = "datatables/beast/beast_specials.iff";
const string BEASTS_SPECIES_BONUS_TABLE = "datatables/beast/beast_class_attribute_grouping.iff";
const string MOBILE_TEMPLATE_PREFIX = "object/mobile/beast_master/";
const string INTANGIBLE_TEMPLATE_PREFIX = "object/intangible/beast/";
const string BEAST_DISPLAY_TEMPLATE = "object/tangible/furniture/decorative/beast_stuffed.iff";
const string OBJVAR_BEAST_NAME = "beast.name";
const string OBJVAR_BEAST_TYPE = "beast.type";
const string OBJVAR_BEAST_ENGINEER = "beast.creator";
const string OBJVAR_SKILL_AGGRESSION = "beast.aggression";
const string OBJVAR_SKILL_BEASTIAL_RESILIENCE = "beast.beastialResilience";
const string OBJVAR_SKILL_CUNNING = "beast.cunning";
const string OBJVAR_SKILL_HUNTERS_INSTINCT = "beast.huntersInstinct";
const string OBJVAR_SKILL_INTELLIGENCE = "beast.intelligence";
const string OBJVAR_SKILL_SURVIVAL = "beast.survival";
const string OBJVAR_INCREASE_ARMOR = "beast.armor_increase";
const string OBJVAR_INCREASE_DPS = "beast.dps_increase";
const string OBJVAR_INCREASE_HIT_CHANCE = "beast.hit_chance_increase";
const string OBJVAR_INCREASE_DODGE = "beast.dodge_increase";
const string OBJVAR_INCREASE_HEALTH = "beast.health_increase";
const string OBJVAR_INCREASE_PARRY = "beast.parry_increase";
const string OBJVAR_INCREASE_GLANCING_BLOW = "beast.glancing_blow_increase";
const string OBJVAR_INCREASE_BLOCK_CHANCE = "beast.block_chance_increase";
const string OBJVAR_INCREASE_BLOCK_VALUE = "beast.block_value_increase";
const string OBJVAR_INCREASE_CRITICAL_HIT = "beast.critical_hit_increase";
const string OBJVAR_INCREASE_EVASION = "beast.evasion_increase";
const string OBJVAR_INCREASE_EVASION_RATING = "beast.evasion_rating_increase";
const string OBJVAR_INCREASE_STRIKETHROUGH = "beast.strikethrough_increase";
const string OBJVAR_INCREASE_STRIKETHROUGH_RATING = "beast.strikethrough_rating_increase";
const string OBJVAR_BEAST_BCD_OWNER = "beast.bcdOwner";
const string OBJVAR_BEAST_APPEARANCE = "beast.appearance_template";
const string OBJVAR_BEAST_PARENT = "beast.parent_creature_template";
const string OBJVAR_BEAST_INCUBATION_BONUSES = "beast.incubation_bonuses";
const string OBJVAR_OLD_PET_IDENTIFIER = "beast.converted_pet";
const string OBJVAR_OLD_PET_RENAMED = "beast.old_pet_renamed";
const string OBJVAR_OLD_PET_REHUED = "beast.old_pet_rehued";
const string OBJVAR_BEAST_HUE = "beast.hue";
const string OBJVAR_BEAST_HUE2 = "beast.hue2";
const string OBJVAR_BEAST_HUE3 = "beast.hue3";
const string OBJVAR_BEAST_PALETTE = "beast.palette";
const string OBJVAR_BEAST_PALETTE2 = "beast.palette2";
const string OBJVAR_BEAST_PALETTE3 = "beast.palette3";
const string OBJVAR_BEAST_MOUNT_FLAG = "beast.isMount";
const string OBJVAR_INJECTOR_MARK = "beast.injectorMark";
const string[] DISPLAY_NAMES =
{
"aggression_skill",
"beastial_resilience_skill",
"cunning_skill",
"hunters_instinct_skill",
"intelligence_skill",
"survival_skill",
"armor_bonus",
"dps_bonus",
"health_bonus",
"hit_chance_bonus",
"dodge_bonus",
"parry_bonus",
"glancing_blow_bonus",
"block_chance_bonus",
"block_value_bonus",
"critical_hit_bonus",
"evasion_bonus",
"evasion_rating_bonus",
"strikethrough_bonus",
"strikethrough_rating_bonus"
};
const float[] DISPLAY_CONVERSION_RATES =
{
1.0f, //aggression
1.0f, //resilience
1.0f, //cunning
1.0f, //hunters instinct
1.0f, //intelligence
1.0f, //survival
0.1f, //armor
0.1f, //dps
0.2f, //health
0.1f, //hit chance
1.0f, //dodge
1.0f, //parry
0.1f, //glancing blow
1.0f, //block chance
1.0f, //block value
1.0f, //critical hit
1.0f, //evasion
1.0f, //evasion rating
1.0f, //strikethrough chance
1.0f //strikethrough rating
};
const float[] DISPLAY_OBJVAR_CONVERSION_RATES =
{
1.0f, //aggression
1.0f, //resilience
1.0f, //cunning
1.0f, //hunters instinct
1.0f, //intelligence
1.0f, //survival
0.1f, //armor
0.1f, //dps
0.2f, //health
0.1f, //hit chance
0.1f, //dodge
0.1f, //parry
0.1f, //glancing blow
0.1f, //block chance
1.0f, //block value
0.1f, //critical hit
0.1f, //evasion
1.0f, //evasion rating
0.1f, //strikethrough chance
1.0f //strikethrough rating
};
const string[] ARRAY_BEAST_INCUBATION_STATS =
{
OBJVAR_SKILL_AGGRESSION,
OBJVAR_SKILL_BEASTIAL_RESILIENCE,
OBJVAR_SKILL_CUNNING,
OBJVAR_SKILL_HUNTERS_INSTINCT,
OBJVAR_SKILL_INTELLIGENCE,
OBJVAR_SKILL_SURVIVAL,
OBJVAR_INCREASE_ARMOR,
OBJVAR_INCREASE_DPS,
OBJVAR_INCREASE_HEALTH,
OBJVAR_INCREASE_HIT_CHANCE,
OBJVAR_INCREASE_DODGE,
OBJVAR_INCREASE_PARRY,
OBJVAR_INCREASE_GLANCING_BLOW,
OBJVAR_INCREASE_BLOCK_CHANCE,
OBJVAR_INCREASE_BLOCK_VALUE,
OBJVAR_INCREASE_CRITICAL_HIT,
OBJVAR_INCREASE_EVASION,
OBJVAR_INCREASE_EVASION_RATING,
OBJVAR_INCREASE_STRIKETHROUGH,
OBJVAR_INCREASE_STRIKETHROUGH_RATING
};
const int MOD_HUNTERS_INSTINCT = 0;
const int MOD_AGGRESSION = 1;
const int MOD_INTELLIGENCE = 2;
const int MOD_CUNNING = 3;
const int MOD_RESILIENCE = 4;
const int MOD_SURVIVAL = 5;
const float BEAST_WEAPON_SPEED = 1.5f;
const int BASE_ATTENTION_PENALTY = -25;
const int BEASTS_STORED_MAXIMUM = 10;
const int BEAST_LEVEL_MAX_DIFFERENCE = 10;
const int STEROID_BUFF_LEVEL_CAP = 74;
const boolean BEAST_DEBUG = true;
const string_id SID_BEAST_TOO_HIGH_LEVEL = new string_id("beast", "cant_call_level");
const string_id SID_MAXIMUM_BEASTS = new string_id("beast", "cant_call_maximum");
const string_id SID_STORED = new string_id("beast", "stored");
const string_id SID_NOT_BEAST_MASTER = new string_id("beast", "not_beast_master");
const string_id SID_NO_BEAST_OUT = new string_id("beast", "no_beast");
const string_id SID_MAXIMUM_COMBAT_CONTROL_DEVICES = new string_id("beast", "maximum_combat_control_devices");
const string_id SID_BEAST_CANT_WHEN_DEAD = new string_id("beast", "beast_cant_when_dead");
const string_id SID_BEAST_LEVEL_TOO_HIGH = new string_id("beast", "beast_buff_level_too_high");
const string_id SID_BEAST_LEVEL_TOO_LOW = new string_id("beast", "beast_buff_level_too_low");
const string_id SID_NO_TRICKS_WHILE_SWIMMING = new string_id("beast", "no_tricks_while_swimming");
const string PET_TRAINED_SKILLS_LIST = "abilities.trained_skills";
const string PET_AUTO_REPEAT_LIST = "abilities.auto_repeat";
const string PLAYER_KNOWN_SKILLS_LIST = "beast_master.known_skills";
const string ATTENTION_PENALTY_DEBUFF = "bm_attention_penalty_";
//Pet happiness and loyalty
const string_id SID_BEAST_WILD = new string_id("beast", "beast_wild");
const int LOYALTY_LVL_TWO = 2001;
const string_id SID_BEAST_DISOBEDIENT = new string_id("beast", "beast_disobedient");
const int LOYALTY_LVL_THREE = 10001;
const string_id SID_BEAST_TRAINED = new string_id("beast", "beast_trained");
const int LOYALTY_LVL_FOUR = 50001;
const string_id SID_BEAST_LOYAL = new string_id("beast", "beast_loyal");
const int LOYALTY_LVL_FIVE = 250001;
const string_id SID_BEAST_BFF = new string_id("beast", "beast_bff");
const int LOYALTY_MAX = 500000;
const string BEAST_LOYALTY_TITLE = "beast_loyalty_title";
const string BEAST_LOYALTY_PERCENTAGE_TITLE = "beast_loyalty_percentage_title";
const string PET_LOYALTY_OBJVAR = "beastmood.beastLoyalty";
const string PET_LOYALTY_LEVEL_OBJVAR = "beastmood.beastLoyaltyLevel";
const float LOYALTY_DEATH_LOSS = 100;
const float LOYALTY_FOOD_LOSS = 50;
const string_id SID_LOYALTY_LEVEL_GAIN = new string_id("beast", "loyalty_gained");
const string_id SID_LOYALTY_LEVEL_LOSS = new string_id("beast", "loyalty_lost");
const string PET_HAPPINESS_SCRIPTVAR = "beastmood.beastHappiness";
const string OBJVAR_BEAST_HAPPINESS = "beastmood.beastHappiness";
const int PET_HAPPINESS_DEFAULT = 0;
const int PET_HAPPINESS_LOYALTY_BONUS = 5;
const int BEAST_HAPPINESS_MAX = 50;
const int BEAST_HAPPINESS_MIN = -25;
const string PET_LOYALTY_BONUS_SCRIPTVAR = "beastmood.beastLoyaltyBonus";
//Pet happiness dislikes and likes
const string BEAST_MOOD_TITLE = "beast_mood_title";
const string_id SID_BEAST_FURIOUS = new string_id("beast", "beast_furious");
const string_id SID_BEAST_ANGRY = new string_id("beast", "beast_angry");
const string_id SID_BEAST_MISERABLE = new string_id("beast", "beast_miserable");
const string_id SID_BEAST_UNHAPPY = new string_id("beast", "beast_unhappy");
const string_id SID_BEAST_ANNOYED = new string_id("beast", "beast_annoyed");
const string_id SID_BEAST_NORMAL = new string_id("beast", "beast_content");
const string_id SID_BEAST_JOYOUS = new string_id("beast", "beast_joyous");
const string_id SID_BEAST_HAPPY = new string_id("beast", "beast_happy");
const string_id SID_BEAST_VERY_HAPPY = new string_id("beast", "beast_very_happy");
const string_id SID_BEAST_ECSTATIC = new string_id("beast", "beast_ecstatic");
const string DATATABLE_BEAST_FAVORITES = "datatables/beast/favorites.iff";
const string DATATABLE_FOOD_COL = "beastFoodName";
const string DATATABLE_LOCATION_COL = "beastLocationName";
const string DATATABLE_ACTIVITY_COL = "beastActivityName";
const int FOOD_FAVORITE = 0;
const int FOOD_DISLIKE = 1;
const int ACTIVITY_FAVORITE = 2;
const int ACTIVITY_DISLIKE = 3;
const int LOCATION_FAVORITE = 4;
const int LOCATION_DISLIKE = 5;
const int LIKE_DISLIKE_MAX = 6;
const string PET_FAVORITES_OBJVAR = "beastmood.beastHappinessFactors";
//Pet food consts
const string_id SID_NO_BEAST = new string_id("beast", "no_beast_to_feed");
const string_id SID_NORMAL_FOOD = new string_id("beast", "fed_normal");
const string_id SID_FAVORITE_FOOD = new string_id("beast", "fed_favorite");
const string_id SID_DISLIKE_FOOD = new string_id("beast", "fed_dislike");
const string_id SID_PET_HUNGRY = new string_id("beast", "pet_hungry");
const string PET_HUNGRY_MESSAGE_SCRIPTVAR = "beastmood.ownerReceivedHungryMessage";
const int PET_WHICH_FOOD = 0;
const int PET_TIME_SINCE_FEEDING = 1;
const int PET_FOOD_MAX_ARRAYS = 2;
const int PET_NORMAL_FOOD = 5;
const int PET_FAVORITE_FOOD = 10;
const int PET_DISLIKE_FOOD = -5;
const int PET_NO_FOOD = -10;
const string PET_FED_SCRIPTVAR = "beastmood.beastLastFed";
const string OBJVAR_BEAST_FOOD = "beastmood.beastLastFed";
//Pet location consts
const string_id SID_FAVORITE_LOCATION = new string_id("beast", "location_favorite");
const string_id SID_DISLIKE_LOCATION = new string_id("beast", "location_dislike");
const int PET_NORMAL_LOCATION = 5;
const int PET_FAVORITE_LOCATION = 10;
const int PET_DISLIKE_LOCATION = -5;
const string PET_LOCATION_SCRIPTVAR = "beastmood.beastLastLocation";
//Pet activity consts
const string_id SID_FAVORITE_ACTIVITY = new string_id("beast", "activity_favorite");
const string_id SID_DISLIKE_ACTIVITY = new string_id("beast", "activity_dislike");
const string PET_ACTIVITY_MESSAGE_SCRIPTVAR = "beastmood.ownerReceivedActivityMessage";
const int PET_NORMAL_ACTIVITY = 0;
const int PET_FAVORITE_ACTIVITY = 10;
const int PET_DISLIKE_ACTIVITY = -10;
const string PET_ACTIVITY_SCRIPTVAR = "beastmood.beastLastActivity";
const string PET_KILL_MESSAGE_SCRIPTVAR = "beastmood.ownerReceivedKillMessage";
const int EXTRACTION_LIMIT = 1;
const string EXTRACTION_LIST = "enzyme.extraction_list";
const string_id[] ENZYME_EXTRACTION_ERRORS =
{
new string_id("beast", "enzyme_fail_invalid_target"),
new string_id("beast", "enzyme_fail_too_many_extractions"),
new string_id("beast", "enzyme_fail_beast_not_dead"),
new string_id("beast", "enzyme_fail_target_is_pet"),
new string_id("beast", "enzyme_fail_target_not_monster"),
new string_id("beast", "enzyme_fail_no_loot_permission"),
new string_id("beast", "enzyme_fail_already_harvested"),
new string_id("beast", "beast_master_use_only")
};
const int ENZ_ERROR_INVALID_TARGET = 0;
const int ENZ_ERROR_TOO_MANY_EXTRACTIONS = 1;
const int ENZ_ERROR_NOT_DEAD = 2;
const int ENZ_ERROR_TARGET_IS_PET = 3;
const int ENZ_ERROR_TARGET_NOT_MONSTER = 4;
const int ENZ_ERROR_NO_LOOT_PERMISSION = 5;
const int ENZ_ERROR_ALREADY_HARVEST = 6;
const int ENZ_ERROR_NOT_BEAST_MASTER = 7;
const string BM_COMMAND_ATTACK = "bm_pet_attack_1";
const string BM_COMMAND_FOLLOW = "bm_follow_1";
const string BM_COMMAND_STAY = "bm_stay_1";
const string BM_COMMAND_DISABLED = "disabled";
const string BM_DISPOSITION_DEFENSIVE = "toggleBeastDefensive";
const string BM_DISPOSITION_PASSIVE = "toggleBeastPassive";
// Pet Bar Data
const string[] PET_BAR_DEFAULT_ARRAY =
{
BM_COMMAND_DISABLED,
BM_COMMAND_FOLLOW,
BM_COMMAND_STAY,
BM_COMMAND_DISABLED,
BM_COMMAND_DISABLED,
BM_COMMAND_DISABLED,
BM_COMMAND_DISABLED,
BM_DISPOSITION_DEFENSIVE,
BM_DISPOSITION_PASSIVE
};
const string_id SID_TOGGLE_DEFENSIVE = new string_id("beast", "defensive_message");
// Skill Comparison Return Values
const int SKILL_CANNOT_COMPARE = -1;
const int SKILL_DIFFERENT_LINE = 0;
const int SKILL_SAME = 1;
const int SKILL_HIGHER = 2;
const int SKILL_LOWER = 3;
// Beast special types
const int ABILITY_INVALID = -1;
const int ABILITY_TYPE_COMBAT = 0;
const int ABILITY_TYPE_NONCOMBAT = 1;
const int ABILITY_TYPE_INNATE = 2;
// Training Return Codes
const int TRAINING_VALID = 0;
const int TRAINING_INVALID_PETTYPE = 1;
const int TRAINING_SKILL_TOO_HIGH = 2;
const int TRAINING_INSUFFICIENT_POINTS = 3;
const int TRAINING_NON_IMPROVED_SKILL = 4;
const int TRAINING_NO_AVAILABLE_SLOTS = 5;
// Beast Injetor return codes
const int INJECTOR_RETURN_BAD_DATA = 0;
const int INJECTOR_RETURN_WRONG_LEVEL = 1;
const int INJECTOR_RETURN_HAS_BUFF = 2;
const int INJECTOR_RETURN_WRONG_FAMILY = 3;
const int INJECTOR_RETURN_SUCESS = 4;
//Timestamp to make sure that happiness doesn't get adjusted more than every 2 seconds, to avoid spamming.
const string PET_HAPPINESS_BLOCK_SCRIPTVAR = "beastmood.beastHappinessBlock";
/************************************************************/
// Holo-Beast Constants
// normal purple holo-beast
const int HOLO_BEAST_TYPE2_QUALITY3 = 6;
// rare orange holo-beast
const int HOLO_BEAST_TYPE3_QUALITY3 = 10;
// Maximum hologram setting. Change if more are added
const int HOLOGRAM_MAX_SETTING = 11;
const string HOLO_BEAST_RARE_COLOR_OBJVAR = "rareHoloBeastColor";
/************************************************************/
/***** FUNCTIONS *******************************************************/
void blog(string text)
{
if(BEAST_DEBUG)
{
LOG("beast_lib", text);
}
}
boolean isBeast(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return false;
}
if(hasScript(beast, "ai.beast"))
{
return true;
}
return false;
}
//has the player purchased expertise skill box 1?
boolean isBeastMaster(obj_id player)
{
if(!isIdValid(player))
{
return false;
}
int skill = getSkillStatisticModifier(player, "expertise_bm_base_mod");
if(skill > 0)
{
return true;
}
return false;
}
boolean isValidBeast(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return false;
}
if(!callable.hasCallableCD(beast))
{
return false;
}
return true;
}
boolean isValidBCD(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd))
{
return false;
}
if(!hasObjVar(bcd, OBJVAR_BEAST_TYPE))
{
return false;
}
return true;
}
boolean isValidPlayer(obj_id player)
{
if(!isIdValid(player) || !exists(player))
{
return false;
}
return true;
}
float getBeastScaleByLevel(obj_id beast)
{
if(!isIdValid(beast))
{
return 1.0f;
}
string beastType = getBeastType(beast);
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
//set scale:
float minScale = beastDict.getFloat("minScale");
float maxScale = beastDict.getFloat("maxScale");
int level = getLevel(beast);
float scaleDifference = maxScale - minScale;
if(scaleDifference <= 0)
{
return minScale;
}
// Scale of the creature is the minimum plus the difference between minimum and maximum times the percentage of
// the creature's level from level 90. IE: a Level 45 pet is halfway through its growth, so it is halfway between
// its smallest size and its largest size.
return (float)(minScale + (scaleDifference * ((float)level / 90.0f)));
}
obj_id getBCDPlayer(obj_id bcd)
{
obj_id datapad = getContainedBy(bcd);
obj_id player = getContainedBy(datapad);
if(!isIdValid(player) || !isPlayer(player))
{
if ( hasObjVar(bcd, OBJVAR_BEAST_BCD_OWNER) )
{
obj_id owner = getObjIdObjVar(bcd, OBJVAR_BEAST_BCD_OWNER);
if ( isIdValid(owner) )
{
return owner;
}
else
{
return null;
}
}
else
{
return null;
}
}
return player;
}
// Beast Control Device has a link to the beast it has called
void setBCDBeastCalled(obj_id bcd, obj_id beast)
{
if(!isValidBCD(bcd))
{
return;
}
setBCDBeastCalledTime(bcd, getGameTime());
callable.setCDCallable(bcd, beast);
}
obj_id getBCDBeastCalled(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return null;
}
return callable.getCDCallable(bcd);
}
// Beast called by the player is set on that player
void setBeastOnPlayer(obj_id player, obj_id beast)
{
if(!isIdValid(player))
{
return;
}
callable.setCallable(player, beast, callable.CALLABLE_TYPE_COMBAT_PET);
}
obj_id getBeastOnPlayer(obj_id player)
{
if(!isIdValid(player))
{
return null;
}
obj_id beast = callable.getCallable(player, callable.CALLABLE_TYPE_COMBAT_PET);
if( !isValidBeast(beast) || !beast_lib.isBeast(beast) )
{
beast = null;
}
return beast;
}
boolean hasActiveBeast(obj_id player)
{
if ( beast_lib.isBeastMaster(player) )
{
obj_id activePet = beast_lib.getBeastOnPlayer(player);
if ( isIdValid(activePet) && exists(activePet) )
{
if ( beast_lib.isBeast(activePet) )
{
return true;
}
}
}
return false;
}
// Beast Control Device Time Stored
void setBCDBeastTimeStored(obj_id bcd, int time)
{
if(!isIdValid(bcd))
{
return;
}
setObjVar(bcd, "beast.timeStored", time);
}
int getBCDBeastTimeStored(obj_id bcd)
{
if(!isIdValid(bcd))
{
return 0;
}
int time = 0;
if(hasObjVar(bcd, "beast.timeStored"))
{
time = getIntObjVar(bcd, "beast.timeStored");
}
return time;
}
void setBCDBeastCalledTime(obj_id bcd, int time)
{
if(!isIdValid(bcd))
{
return;
}
setObjVar(bcd, "beast.timeCalled", time);
}
int getBCDBeastCalledTime(obj_id bcd)
{
if(!isIdValid(bcd))
{
return 0;
}
int time = 0;
if(hasObjVar(bcd, "beast.timeStored"))
{
time = getIntObjVar(bcd, "beast.timeCalled");
}
return time;
}
string getBeastLocalizedName(obj_id bcd)
{
string beastType = getBCDBeastType(bcd);
string beastDisplayType = stripBmFromType(beastType);
string_id beastTypeSid = new string_id("monster_name", beastDisplayType);
return localize(beastTypeSid);
}
// Beast Control Device Type
void setBCDBeastName(obj_id bcd, string beastName)
{
if(!isIdValid(bcd) || beastName == null)
{
return;
}
setName(bcd, beastName);
setObjVar(bcd, "beast.beastName", beastName);
}
boolean hasBCDBeastName(obj_id bcd)
{
if(hasObjVar(bcd, "beast.beastName"))
{
return true;
}
return false;
}
string getBCDBeastName(obj_id bcd)
{
string beastName = getBeastLocalizedName(bcd);
if(hasBCDBeastName(bcd))
{
beastName = getStringObjVar(bcd, "beast.beastName");
}
return beastName;
}
void setBeastName(obj_id beast, string beastName)
{
if(!isValidBeast(beast))
{
return;
}
setName(beast, beastName);
}
boolean hasBeastName(obj_id beast)
{
if(!isValidBeast(beast))
{
return false;
}
return hasBCDBeastName(getBeastBCD(beast));
}
string getBeastName(obj_id beast)
{
if(!isValidBeast(beast))
{
return null;
}
return getBCDBeastName(getBeastBCD(beast));
}
// Beast Control Device Type
void setBCDBeastType(obj_id bcd, string beastType)
{
if(!isIdValid(bcd))
{
return;
}
int hashBeastType = incubator.getHashType(beastType);
setObjVar(bcd, OBJVAR_BEAST_TYPE, hashBeastType);
}
string getBCDBeastType(obj_id bcd)
{
string beastType = "";
if(hasObjVar(bcd, OBJVAR_BEAST_TYPE))
{
int hashBeastType = getIntObjVar(bcd, OBJVAR_BEAST_TYPE);
beastType = incubator.convertHashTypeToString(hashBeastType);
}
return beastType;
}
// Beast Type
// Changing the type with a beast in the world means it needs to be stored.
void setBeastType(obj_id beast, string template)
{
if(!isIdValid(beast) || template == null || template.length() <= 0)
{
return;
}
int row = dataTableSearchColumnForString(template, "template", BEASTS_TABLE);
if(row < 0)
{
return;
}
dictionary dict = dataTableGetRow(BEASTS_TABLE, row);
if(dict == null)
{
return;
}
string type = dict.getString("beastType");
obj_id bcd = getBeastBCD(beast);
if(!isValidBCD(bcd))
{
return;
}
obj_id player = getBCDPlayer(bcd);
if(!isValidPlayer(player))
{
return;
}
// All the objects are valid for this change at this point.
storeBeast(bcd);
setBCDBeastType(bcd, type);
createBeastFromBCD(player, bcd);
}
string getBeastType(obj_id beast)
{
if(!isIdValid(beast))
{
return "";
}
string template = getTemplateName(beast);
string[] templateSplit = split(template, '/');
template = templateSplit[templateSplit.length - 1];
int row = dataTableSearchColumnForString(template, "template", BEASTS_TABLE);
dictionary dict = dataTableGetRow(BEASTS_TABLE, row);
if(dict == null)
{
return "";
}
string type = dict.getString("beastType");
return type;
}
// Beast's Beast Control Device object
void setBeastBCD(obj_id beast, obj_id bcd)
{
if(!isIdValid(beast))
{
return;
}
callable.setCallableCD(beast, bcd);
}
obj_id getBeastBCD(obj_id beast)
{
if(!isIdValid(beast))
{
return null;
}
obj_id bcd = callable.getCallableCD(beast);
if(!isValidBCD(bcd))
{
bcd = null;
}
return bcd;
}
// BCD Level
void setBCDBeastLevel(obj_id bcd, int level)
{
if(!isIdValid(bcd) || level < 1 || level > 90)
{
return;
}
setObjVar(bcd, "beast.level", level);
}
int getBCDBeastLevel(obj_id bcd)
{
if(!isIdValid(bcd))
{
return -1;
}
int level = 0;
if(hasObjVar(bcd, "beast.level"))
{
level = getIntObjVar(bcd, "beast.level");
}
return level;
}
// Beast Level
void setBeastLevel(obj_id beast, int level)
{
if(!isIdValid(beast) || level < 1 || level > 90)
{
return;
}
setLevel(beast, level);
}
int getBeastLevel(obj_id beast)
{
if(!isIdValid(beast))
{
return -1;
}
return getLevel(beast);
}
// BCD Experience
void setBCDBeastExperience(obj_id bcd, int exp)
{
if(!isIdValid(bcd) || exp < 0)
{
return;
}
setObjVar(bcd, "beast.experience", exp);
}
int getBCDBeastExperience(obj_id bcd)
{
if(!isIdValid(bcd))
{
return -1;
}
int exp = 0;
if(hasObjVar(bcd, "beast.experience"))
{
exp = getIntObjVar(bcd, "beast.experience");
}
return exp;
}
// Beast Experience
void setBeastExperience(obj_id beast, int exp)
{
if(!isIdValid(beast) || exp < 0)
{
return;
}
setObjVar(beast, "beast.experience", exp);
}
int getBeastExperience(obj_id beast)
{
if(!isIdValid(beast))
{
return -1;
}
int exp = 0;
if(hasObjVar(beast, "beast.experience"))
{
exp = getIntObjVar(beast, "beast.experience");
}
return exp;
}
void setBCDBeastHealth(obj_id bcd, int health)
{
if(!isIdValid(bcd) || health < 0)
{
return;
}
setObjVar(bcd, "beast.health", health);
}
int getBCDBeastHealth(obj_id bcd)
{
if(!isIdValid(bcd))
{
return -1;
}
int health = 0;
if(hasObjVar(bcd, "beast.health"))
{
health = getIntObjVar(bcd, "beast.health");
}
else
{
return -1;
}
return health;
}
void setBCDBeastCanLevel(obj_id bcd, boolean canLevel)
{
if(!isIdValid(bcd))
{
return;
}
setObjVar(bcd, "beast.canLevel", canLevel);
}
boolean getBCDBeastCanLevel(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
boolean canLevel = false;
if(hasObjVar(bcd, "beast.canLevel"))
{
canLevel = getBooleanObjVar(bcd, "beast.canLevel");
}
return canLevel;
}
void setBeastCanLevel(obj_id beast, boolean canLevel)
{
if(!isIdValid(beast))
{
return;
}
setObjVar(beast, "beast.canLevel", canLevel);
}
boolean getBeastCanLevel(obj_id beast)
{
if(!isValidBeast(beast))
{
return false;
}
boolean canLevel = false;
if(hasObjVar(beast, "beast.canLevel"))
{
canLevel = getBooleanObjVar(beast, "beast.canLevel");
}
return canLevel;
}
void setBCDBeastIsDead(obj_id bcd, boolean beastIsDead)
{
if(!isValidBCD(bcd))
{
return;
}
setObjVar(bcd, "beast.isDead", beastIsDead);
}
boolean getBCDBeastIsDead(obj_id bcd)
{
if(!isIdValid(bcd))
{
return true;
}
boolean beastIsDead = false;
if(hasObjVar(bcd, "beast.isDead"))
{
beastIsDead = getBooleanObjVar(bcd, "beast.isDead");
}
return beastIsDead;
}
void setBCDBeastDefensive(obj_id bcd, obj_id player, boolean beastIsDefensive)
{
if(!isValidBCD(bcd))
{
return;
}
obj_id beast = getBCDBeastCalled(bcd);
if(!isValidBeast(beast))
{
return;
}
prose_package pp = new prose_package();
if(beastIsDefensive)
{
utils.removeScriptVar(beast, "petIgnoreAttacks");
pp = prose.setTT(pp, "DEFENSIVE");
}
else
{
utils.setScriptVar(beast, "petIgnoreAttacks", getGameTime());
pp = prose.setTT(pp, "PASSIVE");
}
pp = prose.setStringId(pp, SID_TOGGLE_DEFENSIVE);
sendSystemMessageProse(player, pp);
setObjVar(bcd, "beast.isDefensive", beastIsDefensive);
setToggledCommands(player, bcd);
}
boolean getBCDBeastDefensive(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
if(hasObjVar(bcd, "beast.isDefensive"))
{
return getBooleanObjVar(bcd, "beast.isDefensive");
}
return false;
}
void setBeastDefensive(obj_id beast, obj_id player, boolean beastIsDefensive)
{
if(!isValidBeast(beast))
{
return;
}
setBCDBeastDefensive(getBeastBCD(beast), player, beastIsDefensive);
}
boolean getBeastDefensive(obj_id beast)
{
if(!isValidBeast(beast))
{
return false;
}
return getBCDBeastDefensive(getBeastBCD(beast));
}
obj_id[] getDatapadBCDs(obj_id player)
{
resizeable obj_id[] datapadBCDs = new obj_id[0];
if( !isIdValid(player) || !exists(player) )
{
return null;
}
obj_id datapad = utils.getPlayerDatapad(player);
if(!isIdValid(datapad))
{
return null;
}
obj_id[] dataItems = getContents(datapad);
for(int i = 0; i < dataItems.length; i++)
{
if( beast_lib.isValidBCD(dataItems[i]) )
{
datapadBCDs = utils.addElement(datapadBCDs, dataItems[i]);
}
}
return datapadBCDs;
}
int getTotalBeastControlDevices(obj_id player)
{
resizeable obj_id[] datapadBCDs = new obj_id[0];
if( !isIdValid(player) || !exists(player) )
{
return 0;
}
obj_id datapad = utils.getPlayerDatapad(player);
if(!isIdValid(datapad))
{
return 0;
}
obj_id[] dataItems = getContents(datapad);
if( dataItems == null || dataItems.length < 1 )
{
return 0;
}
int numStored = 0;
for( int i = 0; i < dataItems.length; i++ )
{
if( beast_lib.isValidBCD(dataItems[i]) )
{
numStored++;
}
}
return numStored;
}
int getTotalBeastsCalled(obj_id player)
{
if(!isIdValid(player))
{
return 0;
}
obj_id datapad = utils.getPlayerDatapad(player);
if(!isIdValid(datapad))
{
return 0;
}
obj_id[] datapadContents = getContents(datapad);
if(datapadContents == null)
{
return 0;
}
int beastTotal = 0;
// Search for the new beasts in the datapad contents.
for(int i = 0; i < datapadContents.length; i++)
{
if(hasObjVar(datapadContents[i], "beast.called") && isValidBeast(getBCDBeastCalled(datapadContents[i])))
{
beastTotal++;
}
}
return beastTotal;
}
// Beast Happiness
// ...happy...HAPPY...happy...HA HA HA! Obey my dog!
int getBCDBeastHappiness(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return 0;
}
if(!hasBCDBeastHappiness(bcd))
{
setBCDBeastHappiness(bcd, 0);
return 0;
}
return getIntObjVar(bcd, OBJVAR_BEAST_HAPPINESS);
}
void setBCDBeastHappiness(obj_id bcd, int happiness)
{
if(!isValidBCD(bcd))
{
return;
}
setObjVar(bcd, OBJVAR_BEAST_HAPPINESS, happiness);
}
boolean hasBCDBeastHappiness(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
if(hasObjVar(bcd, OBJVAR_BEAST_HAPPINESS))
{
return true;
}
return false;
}
int getBeastHappiness(obj_id beast)
{
if(!isValidBeast(beast))
{
return 0;
}
return getBCDBeastHappiness(getBeastBCD(beast));
}
void setBeastHappiness(obj_id beast, int happiness)
{
if(!isValidBeast(beast))
{
return;
}
setBCDBeastHappiness(getBeastBCD(beast), happiness);
}
// Loyalty
float getBCDBeastLoyalty(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return 0;
}
if(!hasBCDBeastLoyalty(bcd))
{
setBCDBeastLoyalty(bcd, 1.0f);
return 0;
}
return getFloatObjVar(bcd, PET_LOYALTY_OBJVAR);
}
void setBCDBeastLoyalty(obj_id bcd, float newLoyalty)
{
if(!isValidBCD(bcd))
{
return;
}
setObjVar(bcd, PET_LOYALTY_OBJVAR, newLoyalty);
}
boolean hasBCDBeastLoyalty(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
if(hasObjVar(bcd, PET_LOYALTY_OBJVAR))
{
return true;
}
return false;
}
float getBeastLoyalty(obj_id beast)
{
if(!isValidBeast(beast))
{
return 0;
}
return getBCDBeastLoyalty(getBeastBCD(beast));
}
void setBeastLoyalty(obj_id beast, float newLoyalty)
{
if(!isValidBeast(beast))
{
return;
}
// Caps
if(newLoyalty < 1.0f)
{
newLoyalty = 1.0f;
}
else if(newLoyalty > LOYALTY_MAX)
{
newLoyalty = LOYALTY_MAX;
}
setBCDBeastLoyalty(getBeastBCD(beast), newLoyalty);
}
int getBeastLoyaltyToNextLevelPercentage(obj_id beast)
{
int loyalty = (int)getBeastLoyalty(beast);
if(loyalty >= LOYALTY_LVL_FIVE)
{
return -1;
}
float percentage = 0.0f;
if(loyalty < LOYALTY_LVL_TWO)
{
percentage = (float)loyalty / (float)LOYALTY_LVL_TWO;
}
else if(loyalty < LOYALTY_LVL_THREE)
{
percentage = (float)(loyalty - LOYALTY_LVL_TWO) / (float)LOYALTY_LVL_THREE;
}
else if(loyalty < LOYALTY_LVL_FOUR)
{
percentage = (float)(loyalty - LOYALTY_LVL_THREE) / (float)LOYALTY_LVL_FOUR;
}
else if(loyalty < LOYALTY_LVL_FIVE)
{
percentage = (float)(loyalty - LOYALTY_LVL_FOUR) / (float)LOYALTY_LVL_FIVE;
}
return (int)(percentage * 100);
}
void incrementBeastLoyalty(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return;
}
float loyalty = getBCDBeastLoyalty(bcd);
if(loyalty >= LOYALTY_MAX)
{
return;
}
int happiness = getBCDBeastHappiness(bcd);
//Happiness has a effect on loyalty gain, so it can scale from -50 to +50
if(happiness < 0)
{
happiness = 0;
}
float modifiedHappiness = 10 * (1.0f + ((float)happiness / 50.0f));
int multiplier = utils.stringToInt(getConfigSetting("GameServer", "xpMultiplier"));
if(multiplier > 1)
{
modifiedHappiness *= multiplier;
}
float newLoyalty = loyalty + modifiedHappiness;
if(loyalty < LOYALTY_LVL_TWO && newLoyalty >= LOYALTY_LVL_TWO)
{
incrementBeastLoyaltyLevel(bcd);
}
if(loyalty < LOYALTY_LVL_THREE && newLoyalty >= LOYALTY_LVL_THREE)
{
incrementBeastLoyaltyLevel(bcd);
}
if(loyalty < LOYALTY_LVL_FOUR && newLoyalty >= LOYALTY_LVL_FOUR)
{
incrementBeastLoyaltyLevel (bcd);
}
if(loyalty < LOYALTY_LVL_FIVE && newLoyalty >= LOYALTY_LVL_FIVE)
{
incrementBeastLoyaltyLevel(bcd);
}
setBCDBeastLoyalty(bcd, newLoyalty);
}
void decrementBeastLoyalty(obj_id bcd, float loyaltyDecrement)
{
if(!isValidBCD(bcd))
{
return;
}
if(!hasObjVar(bcd, PET_LOYALTY_OBJVAR))
{
setObjVar(bcd, PET_LOYALTY_OBJVAR, 1.0f);
return;
}
float loyalty = getBCDBeastLoyalty(bcd);
if(loyalty <= 101)
{
return;
}
float newLoyalty = loyalty - loyaltyDecrement;
if(loyalty >= LOYALTY_LVL_TWO && newLoyalty < LOYALTY_LVL_TWO)
{
decrementBeastLoyaltyLevel(bcd);
}
if(loyalty >= LOYALTY_LVL_THREE && newLoyalty < LOYALTY_LVL_THREE)
{
decrementBeastLoyaltyLevel (bcd);
}
if(loyalty >= LOYALTY_LVL_FOUR && newLoyalty < LOYALTY_LVL_FOUR)
{
decrementBeastLoyaltyLevel(bcd);
}
if(loyalty >= LOYALTY_LVL_FIVE && newLoyalty < LOYALTY_LVL_FIVE)
{
decrementBeastLoyaltyLevel(bcd);
}
setBCDBeastLoyalty(bcd, newLoyalty);
}
// Loyalty Level
int getBCDBeastLoyaltyLevel(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return 0;
}
if(!hasBCDBeastLoyalty(bcd))
{
setBCDBeastLoyaltyLevel(bcd, 1);
return 0;
}
return getIntObjVar(bcd, PET_LOYALTY_LEVEL_OBJVAR);
}
void setBCDBeastLoyaltyLevel(obj_id bcd, int newLevel)
{
if(!isValidBCD(bcd))
{
return;
}
// Caps
if(newLevel < 1)
{
newLevel = 1;
}
else if(newLevel > 5)
{
newLevel = 5;
}
setObjVar(bcd, PET_LOYALTY_LEVEL_OBJVAR, newLevel);
}
boolean hasBCDBeastLoyaltyLevel(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
if(hasObjVar(bcd, PET_LOYALTY_LEVEL_OBJVAR))
{
return true;
}
return false;
}
int getBeastLoyaltyLevel(obj_id beast)
{
if(!isValidBeast(beast))
{
return 0;
}
return getBCDBeastLoyaltyLevel(getBeastBCD(beast));
}
void setBeastLoyaltyLevel(obj_id beast, int newLevel)
{
if(!isValidBeast(beast))
{
return;
}
setBCDBeastLoyalty(getBeastBCD(beast), newLevel);
}
void incrementBeastLoyaltyLevel(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return;
}
obj_id beast = getBCDBeastCalled(bcd);
obj_id player = getBCDPlayer(bcd);
if(!isValidBeast(beast) || !isValidPlayer(player))
{
return;
}
int level = 1;
if(hasBCDBeastLoyaltyLevel(bcd))
{
level = getBCDBeastLoyaltyLevel(bcd) + 1;
}
setBCDBeastLoyaltyLevel(bcd, level);
playClientEffectObj(beast, "clienteffect/level_granted.cef", beast, null);
sendSystemMessage(player, SID_LOYALTY_LEVEL_GAIN);
updateBeastStats(bcd, beast);
}
void decrementBeastLoyaltyLevel(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return;
}
obj_id beast = getBCDBeastCalled(bcd);
obj_id player = getBCDPlayer(bcd);
if(!isValidBeast(beast) || !isValidPlayer(player))
{
return;
}
int level = 1;
if(hasBCDBeastLoyaltyLevel(bcd))
{
level = getBCDBeastLoyaltyLevel(bcd) - 1;
}
setBCDBeastLoyaltyLevel(bcd, level);
playClientEffectObj(beast, "clienteffect/medic_traumatize.cef", beast, null);
sendSystemMessage(player, SID_LOYALTY_LEVEL_LOSS);
updateBeastStats(bcd, beast);
}
// Food fed to the pet
int [] getBCDBeastFood(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return null;
}
if(!hasBCDBeastFood(bcd))
{
// Init food
int [] newFood = new int [PET_FOOD_MAX_ARRAYS];
newFood[PET_WHICH_FOOD] = PET_NORMAL_FOOD;
newFood[PET_TIME_SINCE_FEEDING] = getGameTime();
setBCDBeastFood(bcd, newFood);
return newFood;
}
return getIntArrayObjVar(bcd, OBJVAR_BEAST_FOOD);
}
void setBCDBeastFood(obj_id bcd, int [] newFood)
{
if(!isValidBCD(bcd))
{
return;
}
// Initialize the food array
if(newFood == null || newFood.length < PET_FOOD_MAX_ARRAYS)
{
newFood = new int [PET_FOOD_MAX_ARRAYS];
newFood[PET_WHICH_FOOD] = PET_NORMAL_FOOD;
newFood[PET_TIME_SINCE_FEEDING] = getGameTime();
}
setObjVar(bcd, OBJVAR_BEAST_FOOD, newFood);
}
boolean hasBCDBeastFood(obj_id bcd)
{
if(!isValidBCD(bcd))
{
return false;
}
if(hasObjVar(bcd, OBJVAR_BEAST_FOOD))
{
return true;
}
return false;
}
int [] getBeastFood(obj_id beast)
{
if(!isValidBeast(beast))
{
return null;
}
return getBCDBeastFood(getBeastBCD(beast));
}
void setBeastFood(obj_id beast, int [] newFood)
{
if(!isValidBeast(beast))
{
return;
}
setBCDBeastFood(getBeastBCD(beast), newFood);
}
int getBCDBeastLastFedTime(obj_id bcd)
{
//Checking if pet is hungry
int storedTime = getBCDBeastTimeStored(bcd);
int calledTime = getBCDBeastCalledTime(bcd);
int currentTime = getGameTime();
int fedTimeDelta = 0;
int [] beastFood = getBCDBeastFood(bcd);
// Was the beast fed before it was called?
if(calledTime > beastFood[PET_TIME_SINCE_FEEDING])
{
// If the beast was fed before it was stored/called, then the time that has elapsed is
// its stored time minus its last fed time plus the current time minus the time it's been in the world.
fedTimeDelta = (storedTime - beastFood[PET_TIME_SINCE_FEEDING]) + (currentTime - calledTime);
}
else
{
// If the time that it was stored is greater than its fed time, but calledTime is not, then there is either a synch
// issue or calledTime was not initialized properly.
if(storedTime > beastFood[PET_TIME_SINCE_FEEDING])
{
fedTimeDelta = currentTime - storedTime;
}
else
{
// At this point, the beast has been in the world since it was last fed
fedTimeDelta = currentTime - beastFood[PET_TIME_SINCE_FEEDING];
}
}
return fedTimeDelta;
}
// Zero out happiness and sum up all modifiers in this function
void updateBeastHappiness(obj_id bcd, obj_id beast)
{
if(!isValidBCD(bcd) || !isValidBeast(beast))
{
return;
}
obj_id player = getBCDPlayer(bcd);
if(!isValidPlayer(player))
{
return;
}
//////////////
// Happiness and Loyalty setup
//////////////
// int testFood = 0;
// int testLoyalty = 0;
// int testLocation = 0;
// int testActivity = 0;
// int testExpertise = 0;
int happiness = 0;
float loyalty = getBCDBeastLoyalty(bcd); // If no loyalty is on the beast, this initializes it.
int loyaltyLevel = getBCDBeastLoyaltyLevel(bcd); // If no loyalty level is on the beast, this initializes it.
int [] beastFood = getBCDBeastFood(bcd);
// Food happiness
if(getBCDBeastLastFedTime(bcd) > 3600)
{
dictionary params = new dictionary();
params.put("beastId", beast);
//Pet is hungry
messageTo(bcd, "beastHungry", params, 1, false);
}
// testFood = beastFood[PET_WHICH_FOOD];
happiness += beastFood[PET_WHICH_FOOD];
// Loyalty happiness
if(loyaltyLevel >= 3)
{
// testLoyalty = PET_HAPPINESS_LOYALTY_BONUS;
happiness += PET_HAPPINESS_LOYALTY_BONUS;
}
// Location happiness
if(utils.hasScriptVar(bcd, PET_LOCATION_SCRIPTVAR))
{
// testLocation = utils.getIntScriptVar(bcd, PET_LOCATION_SCRIPTVAR);
happiness += utils.getIntScriptVar(bcd, PET_LOCATION_SCRIPTVAR);
}
// Activity happiness
if(utils.hasScriptVar(bcd, PET_ACTIVITY_SCRIPTVAR))
{
// testActivity = utils.getIntScriptVar(bcd, PET_ACTIVITY_SCRIPTVAR);
happiness += utils.getIntScriptVar(bcd, PET_ACTIVITY_SCRIPTVAR);
}
// Expertise happiness
// testExpertise = (int)getSkillStatisticModifier(player, "expertise_bm_pet_happiness");
happiness += (int)getSkillStatisticModifier(player, "expertise_bm_pet_happiness");
// debugSpeakMsg(beast, "updateBeastHappiness food: " + testFood + " loyalty: " + testLoyalty + " location: " + testLocation + " activity: " + testActivity + " expertise: " + testExpertise + " happiness: " + happiness + " lastFedDelta: " + getBCDBeastLastFedTime(bcd));
setBCDBeastHappiness(bcd, happiness);
// We put a buff on the beast that show the player what mood the pet is in.
applyHappinessBuffIcon(beast);
}
void setBeastLinks(obj_id player, obj_id bcd, obj_id beast)
{
if(!isIdValid(player) || !isIdValid(bcd) || !isIdValid(beast))
{
return;
}
setBCDBeastCalled(bcd, beast);
setBeastBCD(beast, bcd);
setBeastOnPlayer(player, beast);
// setMaster() and setOwner() do the same thing in the beast system, except setMaster()/getMaster() is exclusively used.
// setOwner() is for legacy systems from the ability to friend pets to other people
//setOwner(beast, player);
setMaster(beast, player);
}
void removeBeastLinks(obj_id player, obj_id bcd, obj_id beast)
{
if(!isIdValid(player) || !isIdValid(bcd) || !isIdValid(beast))
{
return;
}
setBCDBeastCalled(bcd, null);
setBeastBCD(beast, null);
setBeastOnPlayer(player, null);
setMaster(beast, null);
//setOwner(beast, null);
}
obj_id createBCDFromEgg(obj_id player, obj_id egg)
{
if(!isIdValid(player))
{
blog("BCD could not be made because the player is invalid.");
return null;
}
if(callable.hasMaxStoredCombatPets(player))
{
sendSystemMessage(player, SID_MAXIMUM_COMBAT_CONTROL_DEVICES);
return null;
}
if(!isIdValid(egg))
{
blog("BCD could not be made because its egg is invalid.");
return null;
}
int hashType = getIntObjVar(egg, OBJVAR_BEAST_TYPE);
string beastType = incubator.convertHashTypeToString(hashType);
if(beastType == "" || beastType == null)
{
blog("BCD could not be made because beastType is null.");
return null;
}
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
if(beastDict == null)
{
blog("BCD could not be created because no dictionary exists in beast/beasts.tab. for the beastType: " + beastType);
return null;
}
string intangibleTemplateName = beastDict.getString("template");
if(intangibleTemplateName == "" || intangibleTemplateName == null)
{
blog("BCD could not be made because intangibleTemplateName is null.");
return null;
}
obj_id datapad = utils.getPlayerDatapad(player);
if(!isIdValid(datapad))
{
blog("BCD could not be made because datapad is null.");
return null;
}
blog("BCD attempting to create object for template: " + INTANGIBLE_TEMPLATE_PREFIX + intangibleTemplateName + " in datapad: " + datapad);
obj_id beastControlDevice = createObject(INTANGIBLE_TEMPLATE_PREFIX + intangibleTemplateName, datapad, "");
if(!isIdValid(beastControlDevice))
{
beastControlDevice = createObject(pet_lib.PET_CTRL_DEVICE_TEMPLATE, datapad, "");
if(!isIdValid(beastControlDevice))
{
sendSystemMessage(player, pet_lib.SID_SYS_TOO_MANY_STORED_PETS);
return null;
}
}
if(!isIdValid(beastControlDevice))
{
blog("Beast control device not created for " + beastType);
return null;
}
// This must be set on the BCD prior to making the BCD's name.
setBCDBeastType(beastControlDevice, beastType);
if(hasObjVar(egg, OBJVAR_OLD_PET_IDENTIFIER))
{
string oldName = getAssignedName(egg);
if(oldName != null && !oldName.equals(""))
{
setBCDBeastName(beastControlDevice, oldName);
}
else
{
setName(beastControlDevice, getBeastLocalizedName(beastControlDevice));
}
setObjVar(beastControlDevice, OBJVAR_OLD_PET_IDENTIFIER, 1);
if(hasObjVar(egg, OBJVAR_OLD_PET_REHUED))
setObjVar(beastControlDevice, OBJVAR_OLD_PET_REHUED, 1);
}
else
{
setName(beastControlDevice, getBeastLocalizedName(beastControlDevice));
}
setBCDIncubationStats(beastControlDevice, egg);
setBCDBeastHueFromEgg(beastControlDevice, egg);
setBCDBeastLevel(beastControlDevice, 1);
setBCDBeastExperience(beastControlDevice, 0);
setBCDBeastCanLevel(beastControlDevice, true);
setBCDBeastCalled(beastControlDevice, null);
attachScript(beastControlDevice, "ai.beast_control_device");
setupHappinessLoyalty(beastControlDevice);
return beastControlDevice;
}
//function used to remove the begining bm_ from creature types
//mostly used for displaying type on BCD and Eggs.
string stripBmFromType (string beastType)
{
if(beastType.indexOf("bm_") > -1)
{
string[] splitType = split(beastType, '_');
beastType = splitType[1];
for(int i = 2; i < splitType.length; ++i)
{
beastType += "_" + splitType[i];
}
}
return beastType;
}
obj_id createBasicBeastFromObject(obj_id source)
{
location spawnLocation = getLocation(source);
return createBasicBeastFromObject(source, spawnLocation);
}
obj_id createBasicBeastFromObject(obj_id source, location spawnLocation)
{
if(!isIdValid(source))
{
blog("createBeastFromBCD: Beast Statue could not be made because the source is invalid.");
return null;
}
string beastType = getBCDBeastType(source);
if(beastType == "" || beastType == null)
{
blog("createBeastFromBCD: Beast Statue could not be made because beastType is null.");
return null;
}
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
if(beastDict == null)
{
blog("createBeastFromBCD: Beast could not be created because no dictionary exists in beast/beasts.tab.");
return null;
}
string mobileTemplateName = beastDict.getString("template");
if(mobileTemplateName == "" || mobileTemplateName == null)
{
blog("createBeastFromBCD: Beast could not be made because mobileTemplateName is null.");
return null;
}
obj_id beast = createObject(MOBILE_TEMPLATE_PREFIX + mobileTemplateName, spawnLocation);
setBeastBCD(beast, source);
//set the beasts hue
initializeBeastColor(beast);
return beast;
}
obj_id createBasicBeastFromPlayer(obj_id player, obj_id bcd)
{
if(!isIdValid(player))
{
blog("createBeastFromBCD: Beast could not be made because the player is invalid.");
return null;
}
if(!isBeastMaster(player))
{
sendSystemMessage(player, SID_NOT_BEAST_MASTER);
return null;
}
string beastType = getBCDBeastType(bcd);
if(beastType == "" || beastType == null)
{
blog("createBeastFromBCD: Beast could not be made because beastType is null.");
return null;
}
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
if(beastDict == null)
{
blog("createBeastFromBCD: Beast could not be created because no dictionary exists in beast/beasts.tab.");
return null;
}
string mobileTemplateName = beastDict.getString("template");
if(mobileTemplateName == "" || mobileTemplateName == null)
{
blog("createBeastFromBCD: Beast could not be made because mobileTemplateName is null.");
return null;
}
location spawnLocation = getLocation(player);
obj_id beast = createObject(MOBILE_TEMPLATE_PREFIX + mobileTemplateName, spawnLocation);
// Link the beast to the bcd and to the master
setBeastLinks(player, bcd, beast);
//set the beasts hue
initializeBeastColor(beast);
return beast;
}
obj_id createBeastFromBCD(obj_id player, obj_id bcd)
{
obj_id beast = createBasicBeastFromPlayer(player, bcd);
if(!isIdValid(beast))
{
string beastType = getBCDBeastType(bcd);
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
string mobileTemplateName = beastDict.getString("template");
blog("createBeastFromBCD: Beast failed to be created by createObject: " + (MOBILE_TEMPLATE_PREFIX + mobileTemplateName));
return null;
}
utils.setScriptVar(beast, "beast.creationInProgress", true);
initializeBeastScripts(beast);
initializeBeastStats(bcd, beast);
setYaw(beast, rand(0.0f, 360.0f));
updatePetAbilityList(bcd, beast);
setBeastmasterPet(player, beast);
utils.removeScriptVar(beast, "beast.creationInProgress");
if(BEAST_DEBUG)
{
string beastType = getBCDBeastType(bcd);
location spawnLocation = getLocation(player);
blog(getName(getSelf()) + " creating beastType: " + beastType + " at " + spawnLocation);
}
setAttributeAttained(beast, attrib.BEAST);
return beast;
}
void initializeBeastScripts(obj_id beast)
{
attachScript(beast, "ai.ai");
attachScript(beast, "ai.creature_combat");
attachScript(beast, "systems.combat.combat_actions");
attachScript(beast, "ai.beast");
}
void initializeBeastStats(obj_id bcd, obj_id beast)
{
if(!isValidBCD(bcd) || !isValidBeast(beast))
{
return;
}
int level = getBCDBeastLevel(bcd);
setBeastLevel(beast, level);
string beastType = getBCDBeastType(bcd);
if(beastType == null || beastType.length() <= 0)
{
blog("initializeBeastStats() bad beast type.");
return;
}
string beastName = getBCDBeastName(bcd);
beastName = beastName.trim();
// Check to see if the beast name is valid (3 characters or more)
if(beastName.length() >= 3)
{
setBeastName(beast, getBCDBeastName(bcd));
}
else // Illegal name on the beast. Remove it from the BCD so it can be renamed.
{
removeObjVar(bcd, "beast.beastName");
}
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
if(beastDict == null || beastDict.size() <= 0)
{
blog("initializeBeastStats() missing entry in the " + BEASTS_TABLE + " table for beastType: " + beastType + ".");
return;
}
dictionary beastStatsDict = utils.dataTableGetRow(BEASTS_STATS, level - 1);
if(beastStatsDict == null || beastStatsDict.size() <= 0)
{
blog("initializeBeastStats() missing entry in the " + BEASTS_STATS + " table for beastType: " + beastType + ".");
return;
}
int [] incubationBonuses = new int[ARRAY_BEAST_INCUBATION_STATS.length];
if(hasObjVar(bcd, OBJVAR_BEAST_INCUBATION_BONUSES) )
{
incubationBonuses = getIntArrayObjVar(bcd, OBJVAR_BEAST_INCUBATION_BONUSES);
}
else
{
for(int i = 0; i < ARRAY_BEAST_INCUBATION_STATS.length; ++i)
{
incubationBonuses[i] = 0;
}
}
for(int i = 0; i < ARRAY_BEAST_INCUBATION_STATS.length; ++i)
{
utils.setScriptVar(beast, ARRAY_BEAST_INCUBATION_STATS[i], (float)incubationBonuses[i]);
}
//add species bonuses
addInnateBeastBonuses(beast, beastType);
//add skill point bonuses
setBeastSkillBonuses(beast);
// Do not set scale if the beast has a buff that changes its scale.
// When the buff is removed, it will handle setting the beast to the correct scale.
int[] beastScaleUpBuffCrc = buff.getAllBuffsByEffect(beast, "beast_scale_up");
int[] beastScaleDownBuffCrc = buff.getAllBuffsByEffect(beast, "beast_scale_down");
if ( (beastScaleUpBuffCrc == null || beastScaleUpBuffCrc.length <= 0) && (beastScaleDownBuffCrc == null || beastScaleDownBuffCrc.length <= 0) )
{
setScale(beast, getBeastScaleByLevel(beast));
}
// Terrain Negotiation
addSkillModModifier(beast, "slope_move", "slope_move", 50, -1, false, false);
// Recalculate happiness
updateBeastHappiness(bcd, beast);
//////////////
// Base expertise modifiers.
//////////////
int expertiseRegen = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_regen");
int expertiseHealth = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_health");
int expertiseAttackSpeed = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_attack_speed");
int expertiseArmor = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_armor");
int expertiseDamage = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_damage");
// Incubation bonuses
float incubationArmorBonus = utils.getFloatScriptVar(beast, OBJVAR_INCREASE_ARMOR);
float incubationDamageBonus = utils.getFloatScriptVar(beast, OBJVAR_INCREASE_DPS);
float incubationHealthBonus = utils.getFloatScriptVar(beast, OBJVAR_INCREASE_HEALTH);
// Weapons and damage ranges
int intMinDamage = (int)beastStatsDict.getInt("MinDmg");
int intMaxDamage = (int)beastStatsDict.getInt("MaxDmg");
intMinDamage = getExpertiseStat(intMinDamage, expertiseDamage, .5f);
intMaxDamage = getExpertiseStat(intMaxDamage, expertiseDamage, .5f);
float floatMinDamage = (float)intMinDamage * (1.0f + incubationDamageBonus / 100.0f);
float floatMaxDamage = (float)intMaxDamage * (1.0f + incubationDamageBonus / 100.0f);
intMinDamage = (int)floatMinDamage;
intMaxDamage = (int)floatMaxDamage;
//The above only does white damage. This nerfs special damage as well.
int specialDamagePercent = getExpertiseStat(100, expertiseDamage, .5f) - 100;
if(!hasSkillModModifier (beast, "expertise_damage_line_beast_only"))
{
addSkillModModifier(beast, "expertise_damage_line_beast_only", "expertise_damage_line_beast_only", specialDamagePercent, -1, false, false);
}
float primarySpeed = getExpertiseSpeed(BEAST_WEAPON_SPEED, expertiseAttackSpeed);
// Weapon initialization
obj_id beastWeapon = getCurrentWeapon(beast);
if(isIdValid(beastWeapon))
{
setWeaponAttackSpeed(beastWeapon, primarySpeed);
setWeaponMaxDamage(beastWeapon, intMaxDamage);
setWeaponMinDamage(beastWeapon, intMinDamage);
weapons.setWeaponData(beastWeapon);
utils.setScriptVar(beastWeapon, "isCreatureWeapon", 1);
}
obj_id defaultWeapon = getDefaultWeapon(beast);
if(isIdValid(defaultWeapon))
{
setWeaponAttackSpeed(defaultWeapon, primarySpeed);
setWeaponMaxDamage(defaultWeapon, intMaxDamage);
setWeaponMinDamage(defaultWeapon, intMinDamage);
weapons.setWeaponData(defaultWeapon);
utils.setScriptVar(defaultWeapon, "isCreatureWeapon", 1);
}
// Stats
int beastHealth = (int)(getExpertiseStat(beastStatsDict.getInt("HP"), expertiseHealth, .5f) * (1.0f + (incubationHealthBonus * 0.2f) / 100.0f));
int constitutionBonus = getEnhancedSkillStatisticModifierUncapped(beast, "constitution_modified");
int staminaBonus = getEnhancedSkillStatisticModifierUncapped(beast, "stamina_modified");
beastHealth += (constitutionBonus * 8) + (staminaBonus * 2);
setMaxAttrib(beast, HEALTH, beastHealth);
beastHealth = getBCDBeastHealth(bcd);
if(beastHealth <= 0)
{
setAttrib(beast, HEALTH, getMaxAttrib(beast, HEALTH));
}
else
{
if(utils.hasScriptVar(beast, "beast.creationInProgress"))
{
setAttrib(beast, HEALTH, beastHealth);
setAttrib(beast, ACTION, 100);
}
if(getBCDBeastIsDead(bcd))
{
kill(beast);
}
}
setMaxAttrib(beast, ACTION, 100);
if(!combat.isInCombat(beast))
{
int healthRegen = getExpertiseStat(beastStatsDict.getInt("HealthRegen"), expertiseRegen, .5f);
int actionRegen = getExpertiseStat(beastStatsDict.getInt("ActionRegen"), expertiseRegen, .5f);
setRegenRate(beast, HEALTH, healthRegen);
setRegenRate(beast, ACTION, actionRegen);
}
// Movement
float runSpeed = 12;
setBaseRunSpeed(beast, runSpeed);
if(getBaseWalkSpeed(beast) > runSpeed)
{
setBaseWalkSpeed(beast, runSpeed);
}
/////////////
// Armor
/////////////
// Set base armor info
// Clear out any existing armor data
armor.removeAllArmorData(beast);
int intArmor = (int)(getExpertiseStat(beastStatsDict.getInt("Armor"), expertiseArmor, .5f) * (1.0f + incubationArmorBonus / 100.0f));
intArmor += (int)getSkillStatisticModifier(beast, "expertise_innate_protection_all");
utils.setScriptVar(beast, "beast.display.armor", intArmor);
if(intArmor >= 0)
{
// Set base armor info
string armorCategoryObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_ARMOR_CATEGORY;
setObjVar(beast, armorCategoryObjVar, AC_battle);
string armorLevelObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_ARMOR_LEVEL;
setObjVar(beast, armorLevelObjVar, AL_standard);
string genProtectionObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_GENERAL_PROTECTION;
setObjVar(beast, genProtectionObjVar, (float)intArmor);
utils.setScriptVar(beast, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION, intArmor);
}
setBeastExperience(beast, getBCDBeastExperience(bcd));
setBeastCanLevel(beast, getBCDBeastCanLevel(bcd));
// Initialize Incubation Modifiers
// Apply "Divided Attention" debuff
// Ok. Setting a custom value on a buff works but there's a display problem where the first value displayed is the only
// one shown and never changes even if the value changes. So we'll use five different buffs rather then risk waiting on a programming fix.
int attentionPenaltyReduction = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_self_debuff_reduction");
int attentionPenalty = BASE_ATTENTION_PENALTY + attentionPenaltyReduction;
int hackeyWorkAroundValue = (attentionPenalty / 5) * -1;
if(hackeyWorkAroundValue > 5)
{
hackeyWorkAroundValue = 5;
}
if(attentionPenalty < 0 || hackeyWorkAroundValue > 0)
{
buff.applyBuff(getMaster(beast), ATTENTION_PENALTY_DEBUFF + hackeyWorkAroundValue);
}
else
{
removeAttentionPenaltyDebuff(getMaster(beast));
}
}
void storeBeast(obj_id bcd)
{
if(!isIdValid(bcd))
{
return;
}
obj_id datapad = getContainedBy(bcd);
obj_id player = getContainedBy(datapad);
obj_id currentBeast = getBCDBeastCalled(bcd);
if(!isIdValid(datapad) || !isIdValid(player) || !isIdValid(currentBeast))
{
return;
}
storeBeastStats(bcd, currentBeast);
removeBeastLinks(player, bcd, currentBeast);
if(currentBeast.isLoaded() && exists(currentBeast))
{
if(!destroyObject(currentBeast))
{
return;
}
}
else
{
messageTo(currentBeast, "handlePackRequest", null, 1, false);
}
setBCDBeastTimeStored(bcd, getGameTime());
setCount(bcd, 0);
setBeastmasterPet(player, null);
if(!utils.hasScriptVar(player, "beast.no_store_message"))
{
sendSystemMessage(player, beast_lib.SID_STORED);
}
else
{
utils.removeScriptVar(player, "beast.no_store_message");
}
removeAttentionPenaltyDebuff(player);
int petBuff = buff.getBuffOnTargetFromGroup(player, "bm_player_buff");
if(petBuff !=0)
buff.removeBuff(player, petBuff);
}
void storeBeastStats(obj_id bcd, obj_id beast)
{
int health = getAttrib(beast, HEALTH);
setBCDBeastHealth(bcd, health);
boolean beastIsDead = isDead(beast);
setBCDBeastIsDead(bcd, beastIsDead);
}
void storeBeasts(obj_id player)
{
if(!isIdValid(player))
{
return;
}
obj_id beast = getBeastOnPlayer(player);
if(!isIdValid(beast))
{
return;
}
obj_id bcd = getBeastBCD(beast);
// Destroy the beast if it is not controlled by a beast control device.
if(!isIdValid(bcd))
{
destroyObject(beast);
setBeastmasterPet(player, null);
return;
}
storeBeast(bcd);
}
void killBeast(obj_id beast, obj_id killer)
{
if(!isValidBeast(beast))
{
return;
}
obj_id bcd = getBeastBCD(beast);
if(!isValidBCD(bcd))
{
destroyObject(beast);
}
utils.setScriptVar(beast, "recapTimer", getGameTime());
if(isIdValid(killer) && exists(killer) && isMob(killer))
{
if(pet_lib.isPet(killer) || beast_lib.isBeast(killer))
{
killer = getMaster(killer);
}
utils.setScriptVar(beast, "killer", killer);
}
blog("killBeast beast: " + beast + " killer " + killer);
xp.cleanupCreditForKills();
dictionary dictIncap = new dictionary();
int incapTimer = getGameTime();
utils.setScriptVar(beast, "incapTimer", incapTimer);
dictIncap.put("incapTimer", incapTimer);
messageTo(beast, "handleBeastIncappedDecay", dictIncap, 120, false);//auto-store the pet in 120
deltadictionary dict = beast.getScriptVars();
dict.put("pet.regenMultiplier", 0);
// PvP should not reduce loyalty
if(isIdValid(killer) && !isPlayer(killer) && isMob(killer))
{
obj_id master = getMaster(killer);
if(!isIdValid(master) || !isPlayer(master))
{
decrementBeastLoyalty(bcd, LOYALTY_DEATH_LOSS);
}
}
stop(beast);
kill(beast);
setBCDBeastHealth(bcd, 0);
setBCDBeastIsDead(bcd, true);
}
void incrementBeastExperience(obj_id beast)
{
if(!isValidBeast(beast))
{
return;
}
int experience = getBeastExperience(beast);
int previousExperience = experience;
obj_id bcd = getBeastBCD(beast);
if(!isValidBCD(bcd))
{
return;
}
// Happiness is capped at -50 through to 50... converted to a percentage of 0.5 to 1.5 (50% to 150% experience penalty/bonus)
float happinessAdjustment = ((float)getBCDBeastHappiness(bcd) / 100.0f) + 1.0f;
// Make sure the caps are enforced... Else, we could go off the handle with a massively high happiness adjustment.
if(happinessAdjustment > 1.5f)
{
happinessAdjustment = 1.5f;
}
else if(happinessAdjustment < 0.5f)
{
happinessAdjustment = 0.5f;
}
float percentageBonuses = happinessAdjustment;
if(buff.hasBuff(beast, "bm_beast_steroid"))
{
percentageBonuses += 0.01f * utils.getIntScriptVar(beast, "beastBuff.beastXpBonusPercent");
}
obj_id master = getMaster(beast);
if(isValidPlayer(master))
{
int experienceGain = Math.round(100 * percentageBonuses);
experienceGain += experienceGain * 0.01f * getEnhancedSkillStatisticModifierUncapped(master, "bm_xp_mod_boost");
int multiplier = utils.stringToInt(getConfigSetting("GameServer", "xpMultiplier"));
if(multiplier > 1)
{
experienceGain *= multiplier;
}
experience += experienceGain;
prose_package pp = prose.getPackage(xp.SID_FLYTEXT_XP, experience - previousExperience);
showFlyTextPrivate(beast, master, pp, 2.5f, new color(180, 60, 240, 255));
}
setBeastExperience(beast, experience);
setBCDBeastExperience(bcd, experience);
}
void incrementBeastLevel(obj_id beast)
{
if(!isValidBeast(beast))
{
return;
}
int level = getLevel(beast) + 1;
//need to remove mark I buffs
if(level == 75 && buff.hasBuff(beast, "bm_beast_steroid"))
{
buff.removeBuff(beast, "bm_beast_steroid");
obj_id master = getMaster(beast);
sendSystemMessage(master, SID_BEAST_LEVEL_TOO_HIGH);
}
if(level > 90)
{
return;
}
obj_id bcd = getBeastBCD(beast);
if(!isValidBCD(bcd))
{
return;
}
setBeastLevel(beast, level);
setBCDBeastLevel(bcd, level);
playClientEffectObj(beast, "clienteffect/level_granted.cef", beast, null);
initializeBeastStats(bcd, beast);
}
boolean canBeastLevelUp(obj_id beast)
{
if(!isValidBeast(beast))
{
return false;
}
int level = getBeastLevel(beast);
obj_id master = getMaster(beast);
// Can't level up while your master is not around.
// Can't level past your master's level
if(!isValidPlayer(master) || level >= getLevel(master))
{
return false;
}
dictionary beastStatsDict = utils.dataTableGetRow(BEASTS_STATS, level - 1);
if(beastStatsDict == null || beastStatsDict.size() <= 0)
{
blog("initializeBeastStats() missing entry in the " + BEASTS_STATS + " table for level: " + level + ".");
return false;
}
int experience = (int)beastStatsDict.getInt("XP");
if(experience > 0 && getBeastExperience(beast) >= experience)
{
return true;
}
return false;
}
void grantBeastExperience(obj_id beast)
{
if(!isValidBeast(beast) || isDead(beast))
{
return;
}
// Is my ability to gain experience turned off?
if(!getBeastCanLevel(beast))
{
return;
}
obj_id bcd = getBeastBCD(beast);
incrementBeastLoyalty(bcd);
int level = getBeastLevel(beast);
if(level < 90)
{
incrementBeastExperience(beast);
}
if(canBeastLevelUp(beast))
{
incrementBeastLevel(beast);
}
}
float scaleDistanceByLevel(obj_id beast, float distance)
{
int level = getLevel(beast);
distance += (level / 45);
return distance;
}
void validateFollowTarget(obj_id beast, obj_id target)
{
if(!isIdValid(beast) || !isIdValid(target))
{
return;
}
obj_id master = getMaster(beast);
if(!isValidPlayer(master))
{
return;
}
dictionary params = new dictionary();
if(utils.getBooleanScriptVar(beast, "ai.pet.staying"))
{
return;
}
if(target != master && (stealth.hasServerCoverState(target) || stealth.hasInvisibleBuff(target)))
{
doStayCommand(beast, master);
doConfusedEmote(beast);
return;
}
// Beast is following something other than its master and is not in combat. It should follow its master.
if(target != master && !ai_lib.isInCombat(beast))
{
doFollowCommand(beast, master);
return;
}
// Beast is in combat and in defensive mode. It should be following something other than its master.
if(ai_lib.isInCombat(beast) && getBeastDefensive(beast))
{
if(getLocomotion(beast) != LOCOMOTION_RUNNING);
{
setMovementRun(beast);
}
return;
}
// Beast is in combat and in passive mode. This should only happen if it has been told to attack.
// It should be following something other than its master.
if(ai_lib.isInCombat(beast) && !getBeastDefensive(beast) && !utils.hasScriptVar(beast, "petIgnoreAttacks"))
{
if(getLocomotion(beast) != LOCOMOTION_RUNNING);
{
setMovementRun(beast);
}
return;
}
int currentTime = getGameTime();
int lastFollowTest = utils.getIntScriptVar(beast, "beast.lastFollowTest");
if(currentTime <= lastFollowTest)
{
return;
}
utils.setScriptVar(beast, "beast.lastFollowTest", currentTime);
// Beast is following its master.
if(target == master)
{
params.put("target", master);
if(getDistance(beast, master) >= 1.5f && getDistance(beast, master) < 5.0f)
{
if(getLocomotion(beast) == LOCOMOTION_RUNNING)
{
setMovementWalk(beast);
}
}
// If the beast is close to its master, it doesn't have to run to catch up.
else if(getDistance(beast, master) < 1.5f)
{
if(getLocomotion(beast) == LOCOMOTION_RUNNING)
{
setMovementWalk(beast);
}
follow(beast, target, scaleDistanceByLevel(beast, 0.1f), scaleDistanceByLevel(beast, 0.5f));
}
else
{
location offset = new location();
offset.x = -1.5f;
offset.z = -1;
if(getLocomotion(beast) != LOCOMOTION_RUNNING && getDistance(beast, master) >= 5.0f);
{
setMovementRun(beast);
}
// Beasts have a tendency to get stuck inside of POBs. This should help correct that issue.
if(getDistance(beast, master) >= 5.0f && !canSee(beast, master))
{
setLocation(beast, getLocation(master));
}
follow(beast, target, offset);
}
messageTo(beast, "checkMovementSpeed", params, 1, false);
return;
}
return;
}
void doConfusedEmote(obj_id beast)
{
if(!isIdValid(beast))
{
return;
}
showFlyText(beast, new string_id("npc_reaction/flytext", "alert"), 3.0f, colors.COPPER);
doAnimationAction(beast, "emt_stand_confused");
}
void doStayCommand(obj_id beast, obj_id master)
{
if(!isIdValid(beast) || !isIdValid(master))
{
return;
}
setMovementWalk(beast);
if(ai_lib.isInCombat(beast))
{
utils.removeScriptVar(beast, "ai.combat.target");
utils.setScriptVar(beast, "petIgnoreAttacks", getGameTime());
stopCombat(beast);
}
utils.setScriptVar(beast, "ai.pet.staying", true);
ai_lib.aiStopFollowing(beast);
location myLocation = getLocation(beast);
setHomeLocation(beast, myLocation);
setMovementWalk(beast);
doAnimationAction(beast, "vocalize");
ai_lib.setDefaultCalmBehavior(beast, ai_lib.BEHAVIOR_STOP);
}
void doFollowCommand(obj_id beast, obj_id master)
{
if(!isIdValid(beast) || !isIdValid(master))
{
return;
}
if(ai_lib.isInCombat(beast))
{
utils.removeScriptVar(beast, "ai.combat.target");
utils.setScriptVar(beast, "petIgnoreAttacks", getGameTime());
stopCombat(beast);
}
utils.removeScriptVar(beast, "ai.pet.staying");
if(hasObjVar(beast, "ai.wounded"))
{
removeObjVar(beast, "ai.wounded");
}
int myPosture = getPosture(beast);
if(myPosture != POSTURE_UPRIGHT && myPosture != POSTURE_SITTING)
{
stop(beast);
removeObjVar(beast, "ai.combat.moveMode");
queueCommand(beast, ##"stand", beast, "", COMMAND_PRIORITY_FRONT);
}
beastFollowTarget(beast, master);
}
void beastFollowTarget(obj_id beast, obj_id target)
{
//sets a leash on how far away the pet will react to the master moving
follow(beast, target, scaleDistanceByLevel(beast, 0.1f), scaleDistanceByLevel(beast, 0.2f));
setMovementRun(beast);
}
void doAttackCommand(obj_id beast, obj_id master)
{
if(!isIdValid(master))
{
return;
}
utils.removeScriptVar(beast, "petIgnoreAttacks");
if(getState(beast, STATE_COVER) == 1)
{
stealth._makeVisible(master, new string_id("spam", "stealthoff"), null);
}
obj_id target = getIntendedTarget(master);
if(isIdValid(target) == false || target == beast)
{
doConfusedEmote(beast);
return;
}
if(!pvpCanAttack(beast, target))
{
doConfusedEmote(beast);
return;
}
if(!pvpCanAttack(master, target))
{
doConfusedEmote(beast);
return;
}
if(master.isLoaded())
{
setHomeLocation(beast, getLocation(master));
}
else if(beast.isLoaded())
{
setHomeLocation(beast, getLocation(beast));
}
utils.removeScriptVar(beast, "ai.pet.staying");
chat.setBadMood(beast);
if(!ai_lib.isInCombat(beast))
{
startCombat(beast, target);
setHate(beast, target, 0);
setHate(target, beast, 0);
utils.setScriptVar(beast, "ai.combat.target", target);
}
else
{
utils.setScriptVar(beast, "ai.combat.target", target);
float maxHate = getMaxHate(beast);
setHate(beast, target, maxHate + 5000);
}
}
void doTrickCommand(obj_id beast, int trickNumber)
{
int myPosture = getPosture(beast);
obj_id beastBCD = getBeastBCD (beast);
obj_id master = getMaster(beast);
int state = getState(beast, STATE_SWIMMING);
if(state == 1)
{
sendSystemMessage(master, SID_NO_TRICKS_WHILE_SWIMMING);
return;
}
//Checking if doing a trick is a favorite activity for the pet.
if(isIdValid (beastBCD))
{
messageTo(beastBCD, "petDoingTrick", null, 1, false);
}
if(myPosture == POSTURE_SITTING)
{
doAnimationAction(beast, "sit_trick_" + trickNumber);
}
else
{
doAnimationAction(beast, "trick_" + + trickNumber);
}
return;
}
//initial setup of dislikes and likes as well as favorite food etc.
void setupHappinessLoyalty(obj_id beastBCD)
{
if(!isIdValid(beastBCD))
{
return;
}
// Happiness
if(!hasBCDBeastHappiness(beastBCD))
{
setBCDBeastHappiness(beastBCD, 0);
obj_id beast = getBCDBeastCalled(beastBCD);
updateBeastStats(beastBCD, beast);
}
// Loyalty points
if(!hasBCDBeastLoyalty(beastBCD))
{
setBCDBeastLoyalty(beastBCD, 1.0f);
}
// Loyalty level derived from loyalty points
if(!hasBCDBeastLoyaltyLevel(beastBCD))
{
setBCDBeastLoyaltyLevel(beastBCD, 1);
}
// Favorites already initialized?
if(hasObjVar(beastBCD, PET_FAVORITES_OBJVAR))
{
return;
}
// Initialize favorites
int [] beastHappiness = new int [LIKE_DISLIKE_MAX];
//determine what the pet likes and dislikes
string[] favoriteFood = dataTableGetStringColumnNoDefaults(DATATABLE_BEAST_FAVORITES, DATATABLE_FOOD_COL);
string[] favoriteLocation = dataTableGetStringColumnNoDefaults(DATATABLE_BEAST_FAVORITES, DATATABLE_LOCATION_COL);
string[] favoriteActivity = dataTableGetStringColumnNoDefaults(DATATABLE_BEAST_FAVORITES, DATATABLE_ACTIVITY_COL);
//favorite food
int foodNumberGood = rand(1, favoriteFood.length);
beastHappiness[0] = foodNumberGood;
//least favorite food, can't be the same as favorite food
boolean goodNumberFood = false;
while(!goodNumberFood)
{
int foodNumberBad = rand(1, favoriteFood.length);
if(beastHappiness[0] != foodNumberBad)
{
beastHappiness[1] = foodNumberBad;
goodNumberFood = true;
}
}
//favorite activity
int activityNumberGood = rand(1, favoriteActivity.length);
beastHappiness[2] = activityNumberGood;
//least favorite activity
boolean goodNumberAct = false;
while(!goodNumberAct)
{
int activityNumberBad = rand(1, favoriteActivity.length);
obj_id player = getBCDPlayer(beastBCD);
if(hasObjVar(player, "qa_beast_hate"))
{
if(activityNumberGood == 1)
{
beastHappiness[2] = 2;
}
activityNumberBad = 1;
}
if(beastHappiness[2] != activityNumberBad)
{
beastHappiness[3] = activityNumberBad;
goodNumberAct = true;
}
}
//favorite location
int locationNumberGood = rand(1, favoriteLocation.length);
beastHappiness[4] = locationNumberGood;
//least favorite location, can't be the same as favorite location
boolean goodNumberLoc = false;
while(!goodNumberLoc)
{
int locationNumberBad = rand(1, favoriteLocation.length);
if(beastHappiness[4] != locationNumberBad)
{
beastHappiness[5] = locationNumberBad;
goodNumberLoc = true;
}
}
setObjVar(beastBCD, PET_FAVORITES_OBJVAR, beastHappiness);
}
boolean feedBeastUsingAutoFeeder(obj_id beast, obj_id player, string autoFeederBuffName)
{
obj_id autofeeder = utils.getObjIdScriptVar(beast, beast_lib.getBeastBuffItemVar(autoFeederBuffName));
if ( isIdValid(autofeeder) )
{
obj_id beastFood = beast_lib.checkAutoFeederForBeastFood(autofeeder);
if ( isIdValid(beastFood) )
{
// autofeeder contained beast food - so feed the beast
beast_lib.applyFoodEffect(beastFood, beast, player);
static_item.decrementStaticItem(beastFood);
sendSystemMessage(player, new string_id("tcg", "beast_auto_feeder_fed_beast"));
return true;
}
}
sendSystemMessage(player, new string_id("tcg", "beast_auto_feeder_no_food"));
buff.removeBuff(beast, autoFeederBuffName);
return false;
}
string getBeastBuffItemVar(string buffName)
{
return "beastBuffItem." + buffName;
}
obj_id checkAutoFeederForBeastFood(obj_id autofeeder)
{
obj_id[] contents = getContents(autofeeder);
if ( contents != null && contents.length > 0 )
{
string[] beastFoods = dataTableGetStringColumnNoDefaults(beast_lib.DATATABLE_BEAST_FAVORITES, beast_lib.DATATABLE_FOOD_COL);
for ( int i = 0; i < contents.length; i++ )
{
obj_id food = contents[i];
if ( isIdValid(food) )
{
string templateNoPath = utils.getTemplateFilenameNoPath(food);
if ( utils.getElementPositionInArray(beastFoods, templateNoPath) > -1 )
{
return food;
}
}
}
}
return obj_id.NULL_ID;
}
void applyFoodEffect(obj_id food, obj_id beast, obj_id player)
{
obj_id beastBCD = getBeastBCD(beast);
string foodTemplate = utils.getTemplateFilenameNoPath(food);
int currentTime = getGameTime();
int [] petFood = getBCDBeastFood(beastBCD);
if(!hasObjVar(beastBCD, PET_FAVORITES_OBJVAR))
{
LOG("beast", "Somehow this beast was created without having his favorites decided. Setting them now to avoid exceptions");
setupHappinessLoyalty(beastBCD);
}
int [] beastHappiness = getIntArrayObjVar(beastBCD, PET_FAVORITES_OBJVAR);
int oldFood = petFood[PET_WHICH_FOOD];
dictionary params = new dictionary();
params.put("beastId", beast);
int foodType = PET_NORMAL_FOOD;
string datatableFavorite = dataTableGetString(DATATABLE_BEAST_FAVORITES, (beastHappiness[0] - 1), DATATABLE_FOOD_COL);
string datatableDislike = dataTableGetString(DATATABLE_BEAST_FAVORITES, (beastHappiness[1] - 1), DATATABLE_FOOD_COL);
//Checking if pet is being fed his favorite food.
if(datatableFavorite == foodTemplate)
{
foodType = PET_FAVORITE_FOOD;
petFood[PET_WHICH_FOOD] = PET_FAVORITE_FOOD;
petFood[PET_TIME_SINCE_FEEDING] = currentTime;
sendSystemMessage(player, SID_FAVORITE_FOOD);
}
//Checking if pet is being fed his dislike food.
else if(datatableDislike == foodTemplate)
{
foodType = PET_DISLIKE_FOOD;
petFood[PET_WHICH_FOOD] = PET_DISLIKE_FOOD;
petFood[PET_TIME_SINCE_FEEDING] = currentTime;
sendSystemMessage(player, SID_DISLIKE_FOOD);
}
//Pet is being fed "normal" food.
else
{
foodType = PET_NORMAL_FOOD;
petFood[PET_WHICH_FOOD] = PET_NORMAL_FOOD;
petFood[PET_TIME_SINCE_FEEDING] = currentTime;
sendSystemMessage(player, SID_NORMAL_FOOD);
}
setBCDBeastFood(beastBCD, petFood);
updateBeastHappiness(beastBCD, beast);
updateBeastStats(beastBCD, beast);
ai_lib.doAction(beast, "eat");
//Pet will be hungry in an hour
messageTo(beastBCD, "beastHungry", params, 3600, false);
}
//Checking if pet is in his favorite location when he gets called from BCD.
void checkForFavoriteLocation(obj_id beastBCD)
{
if(!isValidBCD(beastBCD))
{
return;
}
if(!hasObjVar(beastBCD, PET_FAVORITES_OBJVAR))
{
LOG("beast", "Somehow this beast was created without having his favorites decided. Setting them now to avoid exceptions");
setupHappinessLoyalty(beastBCD);
}
obj_id player = getBCDPlayer(beastBCD);
obj_id beast = getBCDBeastCalled (beastBCD);
int [] beastHappiness = getIntArrayObjVar(beastBCD, PET_FAVORITES_OBJVAR);
string favoriteLocation = dataTableGetString(DATATABLE_BEAST_FAVORITES, (beastHappiness[4] -1), DATATABLE_LOCATION_COL);
string dislikeLocation = dataTableGetString(DATATABLE_BEAST_FAVORITES, (beastHappiness[5] -1), DATATABLE_LOCATION_COL);
string currentLocation = getCurrentSceneName();
int locationType = PET_NORMAL_LOCATION;
if(isDead(beast))
{
return;
}
//Checking if pet is at his favorite location.
if(currentLocation.indexOf(favoriteLocation) > -1 || favoriteLocation.equals(currentLocation))
{
locationType = PET_FAVORITE_LOCATION;
sendSystemMessage(player, SID_FAVORITE_LOCATION);
}
// Checking if pet's favorite location is associated with an instance
// that the player is in.
// Example: the Tusken Invasion of Mos Espa instance is an alternate for Tatooine
else if(instance.isInInstanceArea(player))
{
string alternatesColumn = favoriteLocation + "Alternates";
if(dataTableHasColumn(DATATABLE_BEAST_FAVORITES, alternatesColumn))
{
string areaName = locations.getBuildoutAreaName(player);
if(areaName != null && areaName.length() > 0 )
{
string[] alternateLocs = dataTableGetStringColumn(DATATABLE_BEAST_FAVORITES, alternatesColumn);
if(alternateLocs != null && alternateLocs.length > 0)
{
for(int i = 0; i < alternateLocs.length; i++)
{
string alternateLocation = alternateLocs[i];
if(alternateLocation.equals(areaName))
{
locationType = PET_FAVORITE_LOCATION;
sendSystemMessage(player, SID_FAVORITE_LOCATION);
}
}
}
}
}
}
//Checking if pet is at his dislike location.
else if(currentLocation.indexOf (dislikeLocation) > -1 || dislikeLocation.equals(currentLocation))
{
locationType = PET_DISLIKE_LOCATION;
sendSystemMessage(player, SID_DISLIKE_LOCATION);
}
//Pet is at a "normal" location.
else
{
locationType = PET_NORMAL_LOCATION;
}
//first time a pet is taken out of their BCD they wont have an old location set.
if(!utils.hasScriptVar(beastBCD, PET_LOCATION_SCRIPTVAR))
{
utils.setScriptVar(beastBCD, PET_LOCATION_SCRIPTVAR, locationType);
}
int currentTime = getGameTime();
//Making sure that the player doesn't spam their pets happiness up.
if(!utils.hasScriptVar(beastBCD, PET_HAPPINESS_BLOCK_SCRIPTVAR))
{
utils.setScriptVar(beastBCD, PET_HAPPINESS_BLOCK_SCRIPTVAR, currentTime);
}
int timeStamp = utils.getIntScriptVar(beastBCD, PET_HAPPINESS_BLOCK_SCRIPTVAR);
if((currentTime - timeStamp) > 5)
{
int oldLocation = utils.getIntScriptVar(beastBCD, PET_LOCATION_SCRIPTVAR);
utils.setScriptVar(beastBCD, PET_HAPPINESS_BLOCK_SCRIPTVAR, currentTime);
utils.setScriptVar(beastBCD, PET_LOCATION_SCRIPTVAR, locationType);
//debugSpeakMsg(beastBCD, "checkForFavoriteLocation function 3 set happiness to " + happiness);
updateBeastStats(beastBCD, beast);
}
updateBeastHappiness(beastBCD, beast);
}
/*
// Pet Related Methods
*/
// Single Skill max is loyalty level multiplied by pet level
//
// obj_id pet The pet to calculate skill for
//
// returns int Computed value of loyalty * level
int getSingleSkillMaxTrainingPoints(obj_id pet)
{
int loyaltyLevel = getBeastLoyaltyLevel(pet);
int level = 0;
if(isBeast(pet))
{
level = getBeastLevel(pet);
}
else
level = getBCDBeastLevel(pet);
return loyaltyLevel * level;
}
int getSingleSkillMaxTrainingPoints(obj_id bcd, int level)
{
int loyaltyLevel = getBCDBeastLoyaltyLevel(bcd);
return loyaltyLevel * level;
}
// Since there are 4 skill boxes, max points is singel skill max * 4
//
// obj_id pet Pet to check max TP for
//
// returns int single skill * 4
int getMaxTrainingPoints(obj_id pet)
{
return getSingleSkillMaxTrainingPoints(pet) * 4;
}
// Since available points shift as the pet gains level and loyalty
// Points will be dynamically calculated.
// Can be negative if the pet has lost loyalty.
//
// obj_id pet the pet to calculate
//
// returns int maximum - spent
int getAvailableTrainingPoints(obj_id pet)
{
return getAvailableTrainingPoints(pet, null);
}
int getAvailableTrainingPoints(obj_id pet, string passedAbility)
{
int spent = calculateTrainingPointsSpent(pet, passedAbility);
int maximum = getMaxTrainingPoints(pet);
return maximum - spent;
}
// We compare the points aviable for a skill vs the cost of the skill.
//
// obj_id pet The pet to compare
// string ability The ability to get a training cost for
//
// returns boolean Is avaible points >= than ability cost.
boolean hasSufficientTrainingPoints(obj_id pet, string ability)
{
int available = getAvailableTrainingPoints(pet, ability);
return (getAbilityCost(ability) <= available);
}
// Single skill max is 1/4 the total since there are 4 ability boxes.
//
// obj_id pet The pet to check against
// string ability The pet special to get cost for
//
// return boolean True if the players single skill max is greater or equal to the ability cost.
boolean isValidForSingleSkillMaxCost(obj_id pet, string ability)
{
int singleSkillMax = getSingleSkillMaxTrainingPoints(pet);
return getAbilityCost(ability) <=singleSkillMax;
}
// Pulls the abilty cost of an ability from the special attack datatable
//
// string ability The ability to check cost of
//
// returns int The training cost
int getAbilityCost(string ability)
{
return dataTableGetInt(BEASTS_SPECIALS, ability, "training_cost");
}
// Gets a string array of known skills on the pet. This will always return an array length of 4, even if empty
//
// obj_id pet The pet to get the trained skills for
//
// returns string[] An array of skills
string[] getTrainedSkills(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd))
{
return null;
}
if(isBeast(bcd))// Passed a pet instead, is ok.. this happens
{
return getTrainedSkills(getBeastBCD(bcd));
}
string[] trainedSkills = {"empty","empty","empty","empty"};
if(!hasObjVar(bcd, PET_TRAINED_SKILLS_LIST))
{
return trainedSkills;
}
else
{
trainedSkills = getStringArrayObjVar(bcd, PET_TRAINED_SKILLS_LIST);
}
trainedSkills = correctNullSkills(trainedSkills);
return trainedSkills;
}
string[] getAutoRepeatAbilityList(obj_id bcd)
{
if(isBeast(bcd))
return getAutoRepeatAbilityList(getBeastBCD(bcd));
string[] barData = getBeastmasterPetBarData(getBCDBeastCalled(bcd));
string[] abilityList = {barData[3], barData[4], barData[5], barData[6]};
string[] repeatList = {"","","",""};
if(hasObjVar(bcd, PET_AUTO_REPEAT_LIST))
repeatList = getStringArrayObjVar(bcd, PET_AUTO_REPEAT_LIST);
// Lets validate our list against known commands.
for(int i=0;i<repeatList.length;i++)
{
boolean contains = false;
for(int k=0;k<abilityList.length;k++)
{
if(abilityList[k] == repeatList[i])
contains = true;
}
if(!contains)
repeatList[i] == "";
}
return repeatList;
}
void clearAutoRepeatAbiltiyList(obj_id bcd)
{
if (isBeast(bcd))
bcd = getBeastBCD(bcd);
string[] repeatList = {"", "", "", ""};
setObjVar(bcd, PET_AUTO_REPEAT_LIST, repeatList);
}
boolean isAbilityAutoRepeat(obj_id bcd, string abilityName)
{
if(isBeast(bcd))
return isAbilityAutoRepeat(getBeastBCD(bcd), abilityName);
string[] repeatList = getAutoRepeatAbilityList(bcd);
for(int i = 0; i < repeatList.length; i++)
{
if(repeatList[i] != "" && repeatList[i].equals(abilityName))
{
return true;
}
}
return false;
}
string[] setAbilityAutoRepeat(obj_id bcd, string abilityName)
{
if(isBeast(bcd))
return setAbilityAutoRepeat(getBeastBCD(bcd), abilityName);
string[] abilityList = getTrainedSkills(bcd);
string[] repeatList = getAutoRepeatAbilityList(bcd);
int row = dataTableSearchColumnForString(abilityName, "ability_name", BEASTS_SPECIALS);
if(row < 0)
{
return null;
}
dictionary dict = dataTableGetRow(BEASTS_SPECIALS, row);
if(dict == null || dict.isEmpty())
{
return null;
}
int specialType = dict.getInt("specialType");
for(int i = 0; i < abilityList.length; i++)
{
if(abilityName == abilityList[i])
{
if(specialType != 1)
repeatList[i] = abilityName;
}
}
setObjVar(bcd, PET_AUTO_REPEAT_LIST, repeatList);
return repeatList;
}
string[] unsetAbilityAutoRepeat(obj_id bcd, string abilityName)
{
if(isBeast(bcd))
return unsetAbilityAutoRepeat(getBeastBCD(bcd), abilityName);
string[] repeatList = getAutoRepeatAbilityList(bcd);
for(int i = 0; i < repeatList.length; i++)
{
if(repeatList[i] == abilityName)
{
repeatList[i] = "";
}
}
setObjVar(bcd, PET_AUTO_REPEAT_LIST, repeatList);
return repeatList;
}
string getBestAutoRepeatAbility(obj_id beast)
{
string[] abilityList = getAutoRepeatAbilityList(beast);
string bestAction = "";
float highestCost = 0.0f;
for(int i = 0; i < abilityList.length; i++)
{
if(abilityList[i] == "")
continue;
combat_data abilityData = combat_engine.getCombatData(abilityList[i]);
if(abilityData == null)
continue;
string cooldownGroup = abilityData.cooldownGroup;
int groupCrc = getStringCrc(cooldownGroup);
float coolDownLeft = getCooldownTimeLeft(getMaster(beast), groupCrc);
if (coolDownLeft > 0.0f)
continue;
if(combat.canDrainCombatActionAttributes(beast, (int)abilityData.vigorCost) && abilityData.vigorCost > highestCost)
{
highestCost = abilityData.vigorCost;
bestAction = abilityList[i];
}
}
return bestAction;
}
// Sets an array of known skills on the Beast Control Device. The list will always be length of 4.
//
// obj_id pet The pet to train
// string[] abilityList The list of known abilityes.
//
// return void
void setTrainedPetSkills(obj_id pet, string[] abilityList)
{
obj_id bcd = getBeastBCD(pet);
abilityList = correctNullSkills(abilityList);
setObjVar(bcd, PET_TRAINED_SKILLS_LIST, abilityList);
obj_id player = getMaster(pet);
setBeastmasterToggledPetCommands(player, abilityList);
updatePetAbilityList(bcd);
}
string[] correctNullSkills(string[] abilityList)
{
for (int i=0;i<abilityList.length;i++)
{
if (abilityList[i] == "")
abilityList[i] = "empty";
}
return abilityList;
}
void clearTrainedSkills(obj_id pet)
{
obj_id bcd = getBeastBCD(pet);
string[] emptyList = {"empty","empty","empty","empty"};
string[] nullList = {"","","",""};
setObjVar(bcd, PET_TRAINED_SKILLS_LIST, emptyList);
setObjVar(bcd, PET_AUTO_REPEAT_LIST, nullList);
updatePetAbilityList(bcd);
}
// Determines if the player can even use the button on their pet toolbar.
//
// obj_id player Player attempting to use a pet toolbar skill
// obj_id pet The pet attempting to execute the command
// string command The pet ability to execute
//
// return boolean True if yes, False if no
boolean canPerformCommand(obj_id player, obj_id pet, string command)
{
int additionalAbilitySlot = getSkillStatisticModifier(player, "expertise_bm_add_pet_bar");
string[] abilityList = getTrainedSkills(pet);
for(int i=0;i<abilityList.length;i++)
{
if(abilityList == "")
continue;
if(abilityList[i].equals(command) && i <= additionalAbilitySlot)
return true;
}
return false;
}
// An available slot is an entry of "" in the array.
//
// obj_id pet The pet to get ability array for
//
// return int The number of "" found in the string array
int getAvailableTrainingSlots(obj_id pet, string abilityName)
{
string[] knownSkills = getTrainedSkills(pet);
int additionalAbilitySlot = getSkillStatisticModifier(getBCDPlayer(getBeastBCD(pet)), "expertise_bm_add_pet_bar");
int openSlots = 0;
if (!hasCommand(getMaster(pet), BM_COMMAND_ATTACK) && getBeastMasterSpecialType(abilityName) != ABILITY_TYPE_NONCOMBAT)
{
return 0;
}
for(int i=0; i < knownSkills.length; i++)
{
if(i <= additionalAbilitySlot)
{
if(knownSkills[i].equals("empty") || isSkillUpgrade(pet, abilityName))
openSlots++;
}
}
return openSlots;
}
// Determines of a particular skill is valid for the special attack family of a particular beast.
//
// obj_id pet the pet to check
// string skill The skill name to check valid attack family type for.
//
// return boolean True of the pet can learn that ability.
boolean isValidBeastSkillForCreature(obj_id pet, string skill)
{
obj_id bcd = getBeastBCD(pet);
string beastName = getBeastType(bcd);
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastName);
string specialAttackFamily = beastDict.getString("special_attack_family");
int isValidForType = dataTableGetInt(BEASTS_SPECIALS, skill, specialAttackFamily);
return isValidForType == 1;
}
// Cycles through the array of known abilities and sums their training cost
// This allows us to modify training costs on the fly without mucking with
// stored values in objvars.
//
// obj_id pet The pet to get a summed training spent for
//
// return int The total of training points spent
int calculateTrainingPointsSpent(obj_id pet)
{
return calculateTrainingPointsSpent(pet, null);
}
int calculateTrainingPointsSpent(obj_id pet, string passedAbility)
{
boolean compareAbility = passedAbility != null;
string[] attackList = getTrainedSkills(pet);
int trainedTotal = 0;
for(int i = 0; i < attackList.length; i++)
{
if(attackList == "empty")
continue;
//Give credit Skills spent, so you can afford an upgrade.
if (compareAbility && getSkillBeastmasterSkillComparison(passedAbility, attackList[i]) == SKILL_HIGHER)
{
int costOldAbility = getAbilityCost(attackList[i]);
trainedTotal -= costOldAbility;
continue;
}
int cost = getAbilityCost(attackList[i]);
if(cost != -1)
trainedTotal += cost;
}
return trainedTotal;
}
// Checks the ability list on a pet for presence of the passed ability
//
// obj_id pet The pet to check their skill list
// string ability The name of the ability
//
// return boolean true if the passed skill exists in the known ability array
boolean isSkillAlreadyTrained(obj_id pet, string ability)
{
string[] knownSkills = getTrainedSkills(pet);
for(int i = 0; i < knownSkills.length; i++)
{
if(knownSkills[i].equals(ability) || getSkillBeastmasterSkillComparison(ability, knownSkills[i]) == SKILL_LOWER)
return true;
}
return false;
}
boolean isSkillUpgrade(obj_id pet, string ability)
{
string[] knownSkills = getTrainedSkills(pet);
if (knownSkills == null || knownSkills.length == 0)
return true;
for (int i=0;i<knownSkills.length;i++)
{
if (getSkillBeastmasterSkillComparison(ability, knownSkills[i]) == SKILL_HIGHER)
return true;
}
return false;
}
// Compares the skill against a number of conditions.
// Is there enough points.
// Does the pet already know that skill.
// Can that pet type learn that skill.
// Does the pet have any remaining pet slots
//
// obj_id pet The pet to check
// string abilityName The ability name to teach
//
// return boolean true if all of the conditions are met
boolean canLearnSkill(obj_id pet, string abilityName)
{
int returnCode = getTrainingSuccessCode(pet, abilityName);
boolean canLearn = true;
switch (returnCode)
{
case TRAINING_INVALID_PETTYPE:
sendSystemMessage(getMaster(pet), new string_id("beast", "train_fail_spam_bad_pet_type"));
canLearn = false;
break;
case TRAINING_SKILL_TOO_HIGH:
sendSystemMessage(getMaster(pet), new string_id("beast", "trail_fail_spam_single_skill_max"));
canLearn = false;
break;
case TRAINING_INSUFFICIENT_POINTS:
sendSystemMessage(getMaster(pet), new string_id("beast", "train_fail_spam_insufficient_points"));
canLearn = false;
break;
case TRAINING_NON_IMPROVED_SKILL:
sendSystemMessage(getMaster(pet), new string_id("beast", "train_fail_spam_already_known"));
canLearn = false;
break;
case TRAINING_NO_AVAILABLE_SLOTS:
sendSystemMessage(getMaster(pet), new string_id("beast", "train_fail_spam_no_slots"));
canLearn = false;
break;
}
return canLearn;
}
int getTrainingSuccessCode(obj_id pet, string abilityName)
{
if (!isValidBeastSkillForCreature(pet, abilityName))
{
return TRAINING_INVALID_PETTYPE;
}
if (isSkillAlreadyTrained(pet, abilityName))
{
return TRAINING_NON_IMPROVED_SKILL;
}
if (!isValidForSingleSkillMaxCost(pet, abilityName))
{
return TRAINING_SKILL_TOO_HIGH;
}
if (!hasSufficientTrainingPoints(pet, abilityName))
{
return TRAINING_INSUFFICIENT_POINTS;
}
if (getAvailableTrainingSlots(pet, abilityName) < 1)
{
return TRAINING_NO_AVAILABLE_SLOTS;
}
return TRAINING_VALID;
}
void updatePetAbilityList(obj_id bcd)
{
obj_id pet = getBCDBeastCalled(bcd);
if(!isIdValid(pet) || !exists(pet))
{
return;
}
updatePetAbilityList(bcd, pet);
}
void updatePetAbilityList(obj_id bcd, obj_id pet)
{
string[] petAbilities = getTrainedSkills(bcd);
setObjVar(pet, PET_TRAINED_SKILLS_LIST, petAbilities);
setBeastmasterPetCommands(getBCDPlayer(bcd), getBeastmasterPetBarData(getBCDPlayer(bcd), pet));
setToggledCommands(getBCDPlayer(bcd), bcd);
}
/*
// Beastmaster methods
*/
// Quick check to see if I can even use the trainPet commandj
//
// obj_id player The player attemping to train
// obj_id pet The pet you are trying to teach
//
// return boolean If the player is a beastmaster with a valid pet
boolean canTrainPet(obj_id player, obj_id pet)
{
if(!beast_lib.isBeastMaster(player))
{
sendSystemMessage(player, new string_id("beast", "train_fail_beastmaster_only"));
return false;
}
if(!isIdValid(pet) || !exists(pet))
{
sendSystemMessage(player, new string_id("beast", "train_fail_invalid_pet"));
return false;
}
return true;
}
// Gets the hash code of an ability. Abilities are stored on the player by hash, and on the pet by name.
//
// string abilityName Name of the ability
//
// return int Hash code of the ability
int getSkillCrcByName(string abilityName)
{
return dataTableGetInt(BEASTS_SPECIALS, abilityName, "abilityCrc");
}
// Adds a new ability to the players known skills list after converting it into a hash
//
// obj_id player The player to teach
// string skill The skill to learn
// boolean notify Send the learn skill spam
//
// return string[] Returns the updated skill list, use as you will.
string[] playerLearnBeastMasterSkill(obj_id player, string newSkill)
{
return playerLearnBeastMasterSkill(player, newSkill, true);
}
string[] playerLearnBeastMasterSkill(obj_id player, string newSkill, boolean notify)
{
resizeable int[] abilityCrcList = new int[0];
if(!isLearnableBeastMasterSkill(newSkill))
{
return getKnownSkills(player);
}
if(getKnownSkillsCrc(player) != null)
{
abilityCrcList = getKnownSkillsCrc(player);
}
utils.removeBatchObjVar(player, PLAYER_KNOWN_SKILLS_LIST);
int newSkillCrc = getSkillCrcByName(newSkill);
int[] castToIntArray = abilityCrcList;
if(utils.getElementPositionInArray(castToIntArray, newSkillCrc) > -1)
{
//Already in the list, skip it.
}
else
{
//Add the skill to your list
utils.addElement(abilityCrcList, newSkillCrc);
if(notify)
{
sendCreatureAbilitySkillLearnSpam(player, newSkill);
}
}
castToIntArray = abilityCrcList;
utils.setBatchObjVar(player, PLAYER_KNOWN_SKILLS_LIST, castToIntArray);
return getKnownSkills(player);
}
boolean isLearnableBeastMasterSkill(string skillName)
{
int row = dataTableSearchColumnForString(skillName, "ability_name", BEASTS_SPECIALS);
if (row == -1)
return false;
int specialType = getBeastMasterSpecialType(skillName);
return (specialType != ABILITY_INVALID && specialType != ABILITY_TYPE_INNATE);
}
int getBeastMasterSpecialType(string skillName)
{
int row = dataTableSearchColumnForString(skillName, "ability_name", BEASTS_SPECIALS);
if (row == -1)
return ABILITY_INVALID;
return dataTableGetInt(BEASTS_SPECIALS, row, "specialType");
}
//Returns true if the player already has a specified beast skill
boolean hasBeastMasterSkill(obj_id player, string beastAbility)
{
if(isIdValid(player))
{
if(beastAbility != null && beastAbility != "")
{
resizeable int[] abilityCrcList = getKnownSkillsCrc(player);
int newSkillCrc = getSkillCrcByName(beastAbility);
int[] castToIntArray = abilityCrcList;
if(utils.getElementPositionInArray(castToIntArray, newSkillCrc) > -1)
return true;
}
}
return false;
}
//Sends a message to the player telling them they have learned a new creature ability
//
// obj_id player The player learning the skill
// string ability The name of the ability
//
// return void
void sendCreatureAbilitySkillLearnSpam(obj_id player, string newSkill)
{
sendSystemMessage(player, new string_id("beast", "ability_learn_new_skill"));
sendSystemMessage(player, new string_id("cmd_n", newSkill));
}
// Gets a list of known skills by the player in string array format
//
// obj_id player To players whos skills we want
//
// return string[] String array of skills or null if none
string[] getKnownSkills(obj_id player)
{
resizeable int[] abilityCrc = getKnownSkillsCrc(player);
if(abilityCrc == null || abilityCrc.length == 0)
{
return null;
}
string[] abilityString = new string[abilityCrc.length];
for(int i = 0; i < abilityCrc.length; i++)
{
int row = dataTableSearchColumnForInt(abilityCrc[i], "abilityCrc", BEASTS_SPECIALS);
abilityString[i] = dataTableGetString(BEASTS_SPECIALS, row, "ability_name");
}
if(abilityString == null || abilityString.length == 0)
{
return null;
}
Arrays.sort(abilityString);
return abilityString;
}
// Gets the crc list of known skills of a player
//
// obj_id player The player who's skills to return
//
// return resizeable int[] An vector array of type Int or null if empty
resizeable int[] getKnownSkillsCrc(obj_id player)
{
if(!utils.hasIntBatchObjVar(player, PLAYER_KNOWN_SKILLS_LIST))
{
return null;
}
return utils.getResizeableIntBatchObjVar(player, PLAYER_KNOWN_SKILLS_LIST);
}
// Adds a skill to a pets known ability list and refreshes their pet bar
//
// obj_id pet The pet to train
// string The ability name to learn
//
// return void
void trainPetAbility(obj_id pet, string abilityName)
{
if(!canLearnSkill(pet, abilityName))
{
return;
}
string[] trainedSkills = getTrainedSkills(pet);
for(int i = 0; i < trainedSkills.length; i++)
{
if(getSkillBeastmasterSkillComparison(abilityName, trainedSkills[i]) == SKILL_HIGHER || trainedSkills[i].equals("empty"))
{
trainedSkills[i] = abilityName;
break;
}
}
setTrainedPetSkills(pet, trainedSkills);
sendPetTrainNotification(pet, abilityName);
}
void sendPetTrainNotification(obj_id pet, string abilityName)
{
obj_id master = getMaster(pet);
prose_package pp = new prose_package();
pp.stringId = new string_id("beast", "train_success_new_ability");
pp.actor.set(new string_id("cmd_n", abilityName));
sendSystemMessageProse(master, pp);
}
// We nerf a stat by nerfPercent and it is returned to 100% via expertise.
int getExpertiseStat(int baseStat, int expertiseMod, float nerfPercent)
{
float expertisePercent = (float)expertiseMod/100;
float baseStatFloat = (float)baseStat;
float expertiseStatFloat = baseStatFloat - (baseStatFloat * (nerfPercent - (nerfPercent * expertisePercent) ) );
int expertiseStat = (int)expertiseStatFloat;
return expertiseStat;
}
// Speed is doubled. Expertise will half it.
float getExpertiseSpeed(float baseStat, int expertiseMod)
{
float expertisePercent = (float)expertiseMod/100;
float expertiseSpeed = baseStat + (baseStat * (1.0f - expertisePercent) );
return expertiseSpeed;
}
/* Ability Learning Methods
*/
boolean isPreparedToLearnCreatureAbility(obj_id player)
{
return buff.hasBuff(player, "bm_creature_knowledge");
}
// Gets the learning potential of the beastmaster.
float getCreatureKnowledgeLevel(obj_id player)
{
int ck_skill = getEnhancedSkillStatisticModifier(player, "bm_creature_knowledge");
int level = getLevel(player);
float learningPotential = (((float)ck_skill + (float)level) / 10.0f);
return learningPotential;
}
boolean makeAbilityLearnSkillCheck(obj_id player, string ability)
{
float learningPotential = getCreatureKnowledgeLevel(player);
int challengeRating = getDifficultyToLearnAbility(ability);
float beastLearnBonus = getBeastLearnBonus(player);
boolean watchRoll = isGod(player) && hasObjVar(player, "viewLearnCreatureAbilityRoll");
if(challengeRating < 1)
{
return false;
}
if(learningPotential < challengeRating)
{
if (watchRoll)
sendSystemMessageTestingOnly(player, "Ability("+ability+") challenge rating too high for user");
return false;
}
if(learningPotential > challengeRating)
{
learningPotential += ((1.0f + (learningPotential - (float)challengeRating)) * 100.0f) + (beastLearnBonus * 10.0f);
}
float roll_mod = (float)challengeRating * 1000.0f;
float skillGoal = roll_mod - learningPotential;
float learnRoll = rand(learningPotential, roll_mod) ;
boolean skillLearnResult = learnRoll > skillGoal;
if (watchRoll)
sendSystemMessageTestingOnly(player, "Abiltiy: "+ability+", Random("+learningPotential+", "+roll_mod+") > ("+skillGoal+")"+ " = "+learnRoll+"("+skillLearnResult+")"+", "+beastLearnBonus*10.0f+" chance gained from pet.");
return skillLearnResult;
}
int getDifficultyToLearnAbility(string ability)
{
return dataTableGetInt(BEASTS_SPECIALS, ability, "level_to_learn");
}
boolean checkForSkillAcquisition(obj_id attacker, obj_id defender, string actionName)
{
// Attacks on the pet can gain skill.
if(isBeast(defender))
defender = getMaster(defender);
if(!isIdValid(attacker) || !exists(attacker) || isPlayer(attacker) || isBeast(attacker) || pet_lib.isPet(attacker))
{
return false;
}
if(!isIdValid(defender) || !exists(defender) || !isPlayer(defender) || !isPreparedToLearnCreatureAbility(defender))
{
return false;
}
if(!isIdValid(getBeastOnPlayer(defender)))
{
return false;
}
if(!isLearnableCreatureAbility(defender, actionName))
{
return false;
}
if(makeAbilityLearnSkillCheck(defender, actionName))
{
playerLearnBeastMasterSkill(defender, actionName);
return true;
}
return false;
}
boolean isLearnableCreatureAbility(obj_id player, string actionName)
{
dictionary actionData = dataTableGetRow(BEASTS_SPECIALS, actionName);
if(actionData == null)
{
return false;
}
if(actionData.getInt("level_to_learn") < 0)
{
return false;
}
if(!isValidBeastSkillForCreature(getBeastOnPlayer(player), actionName))
{
return false;
}
return true;
}
// All the provoke command does is add a hate DoT to assist your pet in overcoming your threat
// This handles the acutal hate list manipulation.
//
// obj_id self The taunter
// obj_id target What I'm taunting
// int provokeLevel 1-5 of the level I'm provoking
//
// return void
void doBmProvokeCommand(obj_id self, obj_id target, int provokeLevel)
{
if(!isIdValid(target) || !exists(target) || isPlayer(target) || isBeast(target))
{
return;
}
obj_id[] enemies = getHateList(target);
// Target hates nobody
if(enemies == null || enemies.length == 0)
{
return;
}
obj_id topHateTarget = getHateTarget(target);
// Target already hates me
if(topHateTarget == self)
{
return;
}
// We get taunt every 18 levels.
// Hate range is 80 to 120% by default
// My floor is 80%
const int SKILL_LEVEL_CONST = 18;
const float HATE_RANGE = 0.4f;
const float HATE_FLOOR = 0.8f;
// Level of the target is divided by the constant to get which class of taunt we are in
// We then divide the passed provoke level by the resistance. Using a level 1 taunt on a level 90 target only gets 20% effectiveness
float provokeResistance = getLevel(target) / SKILL_LEVEL_CONST;
float provokeRating = provokeLevel / provokeResistance;
//Our threashold is 40% and floor is 80%, thus our range is 80-120%
float hateThreashold = getHate(target, topHateTarget) * HATE_RANGE;
float hateFloor = hateThreashold * HATE_FLOOR;
// This will set my own hate to the floor
float distanceToFloor = hateFloor - getMaxHate(self);
// Thought you were getting 40 points of hate? Sorry, you used a level 1 taunt on a level 90 creature
// so you only get 8 points.
float modifiedHateRange = hateThreashold * provokeRating;
// Add our range to our floor equalizer.
float finalHateAdd = distanceToFloor + modifiedHateRange;
// If your taunt is bad enough, you might have actually lost hate. This floors that.
finalHateAdd = finalHateAdd < 1 ? 1 : finalHateAdd;
addHate(target, self, finalHateAdd);
}
//Beast is performing his favorite or dislike activity
void performingActivity(obj_id beastBCD, int activity)
{
obj_id player = getBCDPlayer(beastBCD);
utils.setScriptVar(beastBCD, PET_ACTIVITY_SCRIPTVAR, activity);
obj_id beast = getBCDBeastCalled(beastBCD);
updateBeastHappiness(beastBCD, beast);
updateBeastStats(beastBCD, beast);
}
// Find if we have a beast, get its BCD and re-initialize its stats. Used when expertise changes.
void verifyAndUpdateCalledBeastStats(obj_id player)
{
obj_id beast = getBeastOnPlayer(player);
if(!isIdValid(beast) || !exists(beast) )
{
removeAttentionPenaltyDebuff(player);
return;
}
obj_id BCD = getBeastBCD(beast);
if(!isIdValid(BCD) || !exists(BCD) )
{
removeAttentionPenaltyDebuff(player);
return;
}
initializeBeastStats(BCD, beast);
return;
}
void removeAttentionPenaltyDebuff(obj_id player)
{
for(int i = 1; i < 6; i++)
{
if(buff.hasBuff(player, ATTENTION_PENALTY_DEBUFF + i) )
{
buff.removeBuff(player, ATTENTION_PENALTY_DEBUFF + i);
}
}
return;
}
int[] fillIncubationArray(obj_id egg, obj_id beastControlDevice)
{
int[] incubationBonuses = new int[ARRAY_BEAST_INCUBATION_STATS.length];
for(int i = 0; i < ARRAY_BEAST_INCUBATION_STATS.length; ++i)
{
if(hasObjVar(egg, ARRAY_BEAST_INCUBATION_STATS[i]))
{
incubationBonuses[i] = getIntObjVar(egg, ARRAY_BEAST_INCUBATION_STATS[i]);
}
else
{
incubationBonuses[i] = 0;
}
}
return incubationBonuses;
}
string_id convertHappinessString (obj_id beastBCD)
{
int currentHappiness = getBCDBeastHappiness(beastBCD);
string_id happiness = convertHappinessString (currentHappiness);
if ( happiness == null )
{
LOG("beast", "This beast control device (" + beastBCD + ") is somehow missing a happiness value. This should not be possible. Ask customer to relog");
}
return happiness;
}
string_id convertHappinessString (int currentHappiness)
{
if(currentHappiness < -20)
{
return SID_BEAST_FURIOUS;
}
if(currentHappiness < -15)
{
return SID_BEAST_ANGRY;
}
if(currentHappiness < -10)
{
return SID_BEAST_MISERABLE;
}
if(currentHappiness < -5)
{
return SID_BEAST_UNHAPPY;
}
if(currentHappiness < 0)
{
return SID_BEAST_ANNOYED;
}
if(currentHappiness >= 20)
{
return SID_BEAST_ECSTATIC;
}
if(currentHappiness >= 15)
{
return SID_BEAST_VERY_HAPPY;
}
if(currentHappiness >= 10)
{
return SID_BEAST_HAPPY;
}
if(currentHappiness >= 5)
{
return SID_BEAST_JOYOUS;
}
if(currentHappiness >= 0)
{
return SID_BEAST_NORMAL;
}
else
{
return null;
}
}
void applyHappinessBuffIcon(obj_id beast)
{
obj_id beastBCD = getBeastBCD(beast);
if(!isValidBCD(beastBCD))
{
return;
}
int currentHappiness = getBCDBeastHappiness(beastBCD);
int oldBuff = buff.getBuffOnTargetFromGroup(beast, "bm_happiness_icon");
if(oldBuff != 0)
{
buff.removeBuff(beast, oldBuff);
}
if(currentHappiness <= -20)
{
buff.applyBuff(beast, "bm_happiness_furious");
return;
}
if(currentHappiness <= -15)
{
buff.applyBuff(beast, "bm_happiness_angry");
return;
}
if(currentHappiness <= -10)
{
buff.applyBuff(beast, "bm_happiness_miserable");
return;
}
if(currentHappiness <= -5)
{
buff.applyBuff(beast, "bm_happiness_unhappy");
return;
}
if(currentHappiness < 0)
{
buff.applyBuff(beast, "bm_happiness_annoyed");
return;
}
if(currentHappiness >= 20)
{
buff.applyBuff(beast, "bm_happiness_ecstatic");
return;
}
if(currentHappiness >= 15)
{
buff.applyBuff(beast, "bm_happiness_very_happy");
return;
}
if(currentHappiness >= 10)
{
buff.applyBuff(beast, "bm_happiness_happy");
return;
}
if(currentHappiness >= 5)
{
buff.applyBuff(beast, "bm_happiness_joyous");
return;
}
if(currentHappiness >= 0)
{
buff.applyBuff(beast, "bm_happiness_content");
return;
}
}
string_id convertLoyaltyString(obj_id beastBCD)
{
int currentLoyalty = getBCDBeastLoyaltyLevel(beastBCD);
string_id loyalty = convertLoyaltyString(currentLoyalty);
if ( loyalty == null )
{
LOG("beast", "This beast control device (" + beastBCD + ") is somehow missing its Loyalty level or it's a different number than 1 to 5. This should not be possible. Check value in obj var beastmood.beastLoyaltyLevel on the Beast Control Device");
}
return loyalty;
}
string_id convertLoyaltyString(int currentLoyalty)
{
switch(currentLoyalty)
{
case 1:
return SID_BEAST_WILD;
case 2:
return SID_BEAST_DISOBEDIENT;
case 3:
return SID_BEAST_TRAINED;
case 4:
return SID_BEAST_LOYAL;
case 5:
return SID_BEAST_BFF;
default:
return null;
}
}
obj_id generateTypeThreeEnzyme(obj_id player, float enzymeValue)
{
return generateTypeThreeEnzyme(player, null, enzymeValue, enzymeValue, "none");
}
obj_id generateTypeThreeEnzyme(obj_id player, float enzymePurity, float enzymeMutagen)
{
return generateTypeThreeEnzyme(player, null, enzymePurity, enzymeMutagen, "none");
}
obj_id generateTypeThreeEnzyme(obj_id player, obj_id target, float enzymePurity, float enzymeMutagen, string trait)
{
if(!isIdValid(target))
{
obj_id newEnzyme = createObjectInInventoryAllowOverload("object/tangible/loot/beast/enzyme_3.iff", player);
setObjVar(newEnzyme, "enzyme.enzyme_purity", enzymePurity);
setObjVar(newEnzyme, "enzyme.enzyme_mutagen", enzymeMutagen);
setObjVar(newEnzyme, "enzyme.trait", trait);
return newEnzyme;
}
int skillMod = getEnhancedSkillStatisticModifierUncapped(player, "expertise_bm_genetic_engineering");
float ge_mod = 1.0f + (skillMod / 1000.0f);
float levelDifference = (float)getLevel(target) / (float)getLevel(player);
levelDifference = levelDifference > 1.0f ? 1.0f : levelDifference;
float valuePerLevel = 0.02f;
float levelAddedValue = (float)getLevel(target) * valuePerLevel;
float randomFloor = levelAddedValue / 2.0f;
float randomCeiling = levelAddedValue * 2.1f;
float eliteValueAdd = levelAddedValue * 0.7f;
float bossValueAdd = levelAddedValue * 1.3f;
int difficultyClass = getIntObjVar(target, "difficultyClass");
int seed = 0;
switch (difficultyClass)
{
case 0:
seed = 1;
break;
case 1: // Elite
seed = 3;
randomCeiling += eliteValueAdd;
break;
case 2: // Boss
seed = 5;
randomCeiling += bossValueAdd;
break;
}
float basePurity = distributedRand(randomFloor, randomCeiling, seed);
float baseMutagen = distributedRand(randomFloor, randomCeiling, seed);
enzymePurity += basePurity;
enzymeMutagen += baseMutagen;
enzymePurity *= ge_mod;
enzymeMutagen *= ge_mod;
enzymePurity *= levelDifference;
enzymeMutagen *= levelDifference;
addToExtractionList(player, target);
obj_id newEnzyme = createObjectInInventoryAllowOverload("object/tangible/loot/beast/enzyme_3.iff", player);
setObjVar(newEnzyme, "enzyme.enzyme_purity", enzymePurity);
setObjVar(newEnzyme, "enzyme.enzyme_mutagen", enzymeMutagen);
setObjVar(newEnzyme, "enzyme.trait", trait);
return newEnzyme;
}
int getEnzymeExtractionReturnCode(obj_id player, obj_id creatureCorpse)
{
if (!isBeastMaster(player))
{
return ENZ_ERROR_NOT_BEAST_MASTER;
}
if(!isIdValid(creatureCorpse))
{
return ENZ_ERROR_INVALID_TARGET;
}
if(!ai_lib.isAiDead(creatureCorpse))
{
return ENZ_ERROR_NOT_DEAD;
}
if(!ai_lib.isMonster(creatureCorpse) || storyteller.isAnyNpc(creatureCorpse))
{
return ENZ_ERROR_TARGET_NOT_MONSTER;
}
if(beast_lib.isBeast(creatureCorpse) || pet_lib.isPet(creatureCorpse))
{
return ENZ_ERROR_TARGET_IS_PET;
}
resizeable obj_id[] extractions = getExtractionList(creatureCorpse);
int numExtractions = 0;
if(extractions != null && extractions.length > 0)
{
numExtractions = extractions.length;
}
if(numExtractions > 0 && extractions.contains(player))
{
return ENZ_ERROR_ALREADY_HARVEST;
}
if(numExtractions >= EXTRACTION_LIMIT)
{
return ENZ_ERROR_TOO_MANY_EXTRACTIONS;
}
if(!corpse.hasLootPermissions(creatureCorpse, player))
{
return ENZ_ERROR_NO_LOOT_PERMISSION;
}
return -1;
}
resizeable obj_id[] getExtractionList(obj_id corpse)
{
if(hasObjVar(corpse, EXTRACTION_LIST))
{
return getResizeableObjIdArrayObjVar(corpse, EXTRACTION_LIST);
}
resizeable obj_id[] extractionList = new obj_id[0];
return extractionList;
}
void addToExtractionList(obj_id player, obj_id corpse)
{
resizeable obj_id[] extractionList = getExtractionList(corpse);
utils.addElement(extractionList, player);
setObjVar(corpse, EXTRACTION_LIST, extractionList);
}
float distributedRand(float min, float max, int level)
{
const int levelMin = 0;
const int levelMax = 10;
boolean inverted = false;
float _min = min;
float _max = max;
if (min > max)
{
inverted = true;
min = _max;
max = _min;
}
//if (level < levelMin) level = levelMin;
//if (level > levelMax) level = levelMax;
float rank = (float)(level - levelMin) / (float)(levelMax - levelMin);
float mid = min + ((max - min) * rank);
if (mid < min) { max += (mid-min); mid = min; }
if (mid > max) { min += (mid-max); mid = max; }
float minRand = rand(min, mid);
float maxRand = rand(mid, max);
float randNum = rand(minRand, maxRand);
if (inverted)
randNum = _min + (_max - randNum);
return randNum;
}
string[] getBeastmasterPetBarData(obj_id pet)
{
return getBeastmasterPetBarData(getMaster(pet), pet);
}
string[] getBeastmasterPetBarData(obj_id player, obj_id pet)
{
//we have to clone the array not set it equal to the constant
//otherwise we are just pointing to the reference and are overwriting the valuse
//in the constant
string[] barData = (string[])PET_BAR_DEFAULT_ARRAY.clone();
string[] knownSkills = getTrainedSkills(pet);
int additionalAbilitySlot = 0;
additionalAbilitySlot = getSkillStatisticModifier(player, "expertise_bm_add_pet_bar");
if(hasCommand(player, BM_COMMAND_ATTACK))
{
barData[0] = BM_COMMAND_ATTACK;
}
if (hasCommand(player, BM_COMMAND_ATTACK) || getBeastMasterSpecialType(knownSkills[0]) == ABILITY_TYPE_NONCOMBAT)
{
barData[3] = knownSkills[0];
}
if(additionalAbilitySlot == 0)
{
return barData;
}
for(int i=0;i < additionalAbilitySlot; i++)
{
//max extra ability slots is 3 but since we start at 0 the number is 2 TWO TWO 2 TWO!!
if( additionalAbilitySlot > 3 )
{
break;
}
barData[i + 4] = knownSkills[i + 1];
}
return barData;
}
boolean isChargeAttack(string actionName)
{
if(actionName.startsWith("bm_charge") || actionName.startsWith("bm_trample"))
{
return true;
}
else
{
return false;
}
}
boolean setBCDBeastHueFromEgg(obj_id bcd, obj_id egg)
{
if(!isIdValid(bcd) || !exists(bcd) || !isIdValid(egg) || !exists(egg))
{
return false;
}
if(hasObjVar(egg, OBJVAR_BEAST_HUE))
{
int beastHue = getIntObjVar(egg, OBJVAR_BEAST_HUE);
setBCDBeastHuePrimary(bcd, "/private/index_color_1", beastHue);
return true;
}
return false;
}
void setBCDBeastHuePrimary(obj_id bcd, string palette, int hueIndex)
{
// utils.tlog("beast", "1 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
// utils.tlog("beast", "2 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
hue.setColor(bcd, palette, hueIndex); // palette was "/private/index_color_1"
setObjVar(bcd, OBJVAR_BEAST_HUE, hueIndex);
setObjVar(bcd, OBJVAR_BEAST_PALETTE, palette);
}
void setBeastHuePrimary(obj_id beast, string palette, int hueIndex)
{
obj_id bcd = getBeastBCD(beast);
// utils.tlog("beast", " bcd: " + bcd + "beast: " + beast + " palette: " + palette + " hueIndex: " + hueIndex);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
// utils.tlog("beast", "bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
setBCDBeastHuePrimary(bcd, palette, hueIndex);
hue.setColor(beast, palette, hueIndex); // palette was "/private/index_color_1"
}
string getBCDPalettePrimary(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_PALETTE))
{
return "";
}
return getStringObjVar(bcd, OBJVAR_BEAST_PALETTE);
}
int getBCDHuePrimary(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_HUE))
{
return -1;
}
return getIntObjVar(bcd, OBJVAR_BEAST_HUE);
}
string getBeastPalettePrimary(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return "";
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return "";
}
return getBCDPalettePrimary(bcd);
}
int getBeastHuePrimary(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return -1;
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return -1;
}
return getBCDHuePrimary(bcd);
}
void setBCDBeastHueSecondary(obj_id bcd, string palette, int hueIndex)
{
// utils.tlog("beast", "1 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
// utils.tlog("beast", "2 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
hue.setColor(bcd, palette, hueIndex);
setObjVar(bcd, OBJVAR_BEAST_HUE2, hueIndex);
setObjVar(bcd, OBJVAR_BEAST_PALETTE2, palette);
}
void setBeastHueSecondary(obj_id beast, string palette, int hueIndex)
{
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
setBCDBeastHueSecondary(bcd, palette, hueIndex);
hue.setColor(beast, palette, hueIndex);
}
string getBCDPaletteSecondary(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_PALETTE2))
{
return "";
}
return getStringObjVar(bcd, OBJVAR_BEAST_PALETTE2);
}
int getBCDHueSecondary(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_HUE2))
{
return -1;
}
return getIntObjVar(bcd, OBJVAR_BEAST_HUE2);
}
string getBeastPaletteSecondary(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return "";
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return "";
}
return getBCDPaletteSecondary(bcd);
}
int getBeastHueSecondary(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return -1;
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return -1;
}
return getBCDHueSecondary(bcd);
}
void setBCDBeastHueThird(obj_id bcd, string palette, int hueIndex)
{
// utils.tlog("beast", "1 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
// utils.tlog("beast", "2 bcd: " + bcd + " palette: " + palette + " hueIndex: " + hueIndex);
hue.setColor(bcd, palette, hueIndex);
setObjVar(bcd, OBJVAR_BEAST_HUE3, hueIndex);
setObjVar(bcd, OBJVAR_BEAST_PALETTE3, palette);
}
void setBeastHueThird(obj_id beast, string palette, int hueIndex)
{
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd) || palette == null || palette.length() <= 0 || hueIndex < 0)
{
return;
}
setBCDBeastHueThird(bcd, palette, hueIndex);
hue.setColor(beast, palette, hueIndex);
}
string getBCDPaletteThird(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_PALETTE3))
{
return "";
}
return getStringObjVar(bcd, OBJVAR_BEAST_PALETTE3);
}
int getBCDHueThird(obj_id bcd)
{
if(!isIdValid(bcd) || !exists(bcd) || !hasObjVar(bcd, OBJVAR_BEAST_HUE3))
{
return -1;
}
return getIntObjVar(bcd, OBJVAR_BEAST_HUE3);
}
string getBeastPaletteThird(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return "";
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return "";
}
return getBCDPaletteThird(bcd);
}
int getBeastHueThird(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return -1;
}
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return -1;
}
return getBCDHueThird(bcd);
}
void clearBeastColor(obj_id beast)
{
obj_id bcd = getBeastBCD(beast);
if(!isIdValid(bcd) || !exists(bcd))
{
return;
}
removeObjVar(bcd, OBJVAR_BEAST_HUE);
removeObjVar(bcd, OBJVAR_BEAST_HUE2);
removeObjVar(bcd, OBJVAR_BEAST_HUE3);
removeObjVar(bcd, OBJVAR_BEAST_PALETTE);
removeObjVar(bcd, OBJVAR_BEAST_PALETTE2);
removeObjVar(bcd, OBJVAR_BEAST_PALETTE3);
}
void initializeBeastColor(obj_id beast)
{
obj_id bcd = getBeastBCD(beast);
// utils.tlog("beast", " bcd: " + bcd);
if(!isIdValid(bcd) || !exists(bcd))
{
return;
}
int primaryHue = getBCDHuePrimary(bcd);
int secondaryHue = getBCDHueSecondary(bcd);
int thirdHue = getBCDHueThird(bcd);
string primaryPalette = getBCDPalettePrimary(bcd);
string secondaryPalette = getBCDPaletteSecondary(bcd);
string thirdPalette = getBCDPaletteThird(bcd);
// utils.tlog("beast", "" + " primaryHue: " + primaryHue + " secondaryHue: " + secondaryHue + " thirdHue: " + thirdHue +
// " primaryPalette: " + primaryPalette + " secondaryPalette: " + secondaryPalette + " thirdPalette: " + thirdPalette);
// Old pets do not use the new hue system
if(primaryHue >= 0 && (primaryPalette == null || primaryPalette.length() <= 0))
{
setBeastHuePrimary(beast, "/private/index_color_1", primaryHue);
return;
}
// utils.tlog("beast");
setBeastHuePrimary(beast, primaryPalette, primaryHue);
setBeastHueSecondary(beast, secondaryPalette, secondaryHue);
setBeastHueThird(beast, thirdPalette, thirdHue);
}
//adds innate bonuses by species
boolean addInnateBeastBonuses(obj_id beast, string beastType)
{
int row = dataTableSearchColumnForString(beastType, "beastType", BEASTS_TABLE);
if( row < 0)
{
return false;
}
//get bonus group from beasts.tab
int intBonusGroup = dataTableGetInt(BEASTS_TABLE, row, "attribute_bonus_class");
row = dataTableSearchColumnForInt(intBonusGroup, "intNameClass", BEASTS_SPECIES_BONUS_TABLE);
string strBonusGroup = dataTableGetString(BEASTS_SPECIES_BONUS_TABLE, row, "stringNameClass");
//get the column number, so we can findout what the column number is of the amount updated
int columnNumber = dataTableFindColumnNumber(BEASTS_SPECIES_BONUS_TABLE, strBonusGroup);
//increment column is one over from the attribute list column
int incrementColumnNumber = columnNumber + 1;
//got to bonus table and get list of attributes increased
string[] attributesAffected = dataTableGetStringColumnNoDefaults(BEASTS_SPECIES_BONUS_TABLE, strBonusGroup);
//move one column over and get thier amounts updated
int[] incrementAmounts = dataTableGetIntColumnNoDefaults(BEASTS_SPECIES_BONUS_TABLE, incrementColumnNumber);
//these two arrays should be the same lengh, lets make sure
if(attributesAffected.length != incrementAmounts.length)
{
return false;
}
//loop thru list of attributes updated
for(int i = 0; i < attributesAffected.length; ++i)
{
string fullName = "beast."+attributesAffected[i];
//loop thru incubation stats
for(int j = 0; j < ARRAY_BEAST_INCUBATION_STATS.length; ++j)
{
//compare our species bonuses to incubation stats. If they are the same, we update.
if(fullName == ARRAY_BEAST_INCUBATION_STATS[j])
{
float currentBonus = 0.0f;
//check to see if we have the scriptvar
if(utils.hasScriptVar(beast, ARRAY_BEAST_INCUBATION_STATS[j]))
{
//current bonus is now equal to the scriptvar
currentBonus = utils.getFloatScriptVar(beast, ARRAY_BEAST_INCUBATION_STATS[j]);
}
//new bonus is the current bonus, from incubation, plus our new species bonus.
float newBonus = currentBonus + (float)incrementAmounts[i];
utils.setScriptVar(beast, ARRAY_BEAST_INCUBATION_STATS[j], newBonus);
break;
}
}
}
return true;
}
dictionary getBeastMasterExamineInfo(obj_id player, obj_id creature, string[] names, string[] attribs, int idx)
{
dictionary dict = new dictionary();
dict.put("names", names);
dict.put("attribs", attribs);
dict.put("idx", idx);
if(!buff.hasBuff(player, "bm_creature_knowledge") || !ai_lib.isMonster(creature) || !utils.hasScriptVar(player, "creature_knowledge.species"))
{
return dict;
}
if ( hasObjVar(creature, "bm_doNotShowExamineInfo") )
{
return dict;
}
int species = utils.getIntScriptVar(player, "creature_knowledge.species");
if (species != getSpecies(creature))
return dict;
int ck_skill = getEnhancedSkillStatisticModifierUncapped(player, "bm_creature_knowledge");
string creatureName = getCreatureName(creature);
dictionary creatureDict = dataTableGetRow("datatables/mob/creatures.iff", creatureName);
//Show meat, bone and hide
if(ck_skill > -1)
{
if(creatureDict.getString("hideType") != "none")
{
names[idx] = "creature_resource_hide";
attribs[idx] = utils.packStringId(new string_id("obj_attr_n", creatureDict.getString("hideType")));
idx++;
}
if(creatureDict.getString("meatType") != "none")
{
names[idx] = "creature_resource_meat";
attribs[idx] = utils.packStringId(new string_id("obj_attr_n", creatureDict.getString("meatType")));
idx++;
}
if(creatureDict.getString("boneType") != "none")
{
names[idx] = "creature_resource_bone";
attribs[idx] = utils.packStringId(new string_id("obj_attr_n", creatureDict.getString("boneType")));
idx++;
}
}
if(ck_skill > 0)
{
names[idx] = "aggro";
attribs[idx] = ""+creatureDict.getFloat("aggressive");
idx++;
names[idx] = "assist_range";
attribs[idx] = ""+creatureDict.getFloat("assist");
idx++;
names[idx] = "deathblow";
boolean deathBlow= (creatureDict.getInt("death_blow") > 0);
attribs[idx] = deathBlow ? "True" : "False";
idx++;
}
dictionary statDict = dataTableGetRow("datatables/mob/stat_balance.iff", creatureDict.getInt("BaseLevel"));
int difficultyClass = getIntObjVar(creature, "difficultyClass");
string prefix = "";
switch(difficultyClass)
{
case 1:
prefix = "Elite_";
break;
case 2:
prefix = "Boss_";
break;
default:
break;
}
if (ck_skill > 3)
{
float dps = statDict.getFloat(prefix+"damagePerSecond");
float attackSpeed = creatureDict.getFloat("primary_weapon_speed");
int minDamage = Math.round((dps * attackSpeed) *0.5f);
int maxDamage = Math.round((dps * attackSpeed) *1.5f);
names[idx] = "damage_min";
attribs[idx] = ""+minDamage;
idx++;
names[idx] = "damage_max";
attribs[idx] = ""+maxDamage;
idx++;
names[idx] = "attackspeed";
attribs[idx] = ""+attackSpeed;
idx++;
names[idx] = "damage_dps";
attribs[idx] = ""+dps;
idx++;
}
if (ck_skill > 5)
{
names[idx] = "armor_rating";
attribs[idx] = ""+statDict.getInt(prefix+"Armor");
idx++;
}
if(ck_skill > 7)
{
string special_list = creatureDict.getString("primary_weapon_specials");
if(special_list != null && special_list != "")
{
resizeable string[] actionList = new string[0];
for(int i = 1; i < 15; i++)
{
string ability = dataTableGetString("datatables/ai/ai_combat_profiles.iff", special_list, "action"+i);
if(ability != null && ability != "" && actionList.indexOf(ability) == -1)
{
utils.addElement(actionList, ability);
}
}
if(actionList.length > 0)
{
for(int k = 0; k < actionList.length; k++)
{
names[idx] = "special_attack";
attribs[idx] = utils.packStringId(new string_id("cmd_n", actionList[k]));
idx++;
}
}
}
}
dict.put("names", names);
dict.put("attribs", attribs);
dict.put("idx", idx);
return dict;
}
string getBestBeastMasterSkill(obj_id player, string skillName)
{
string[] knownSkills = getKnownSkills(player);
if (knownSkills == null || knownSkills.length == 0)
return skillName;
string passedSkillLine = getSkillLine(skillName);
int passedSkillRank = getSkillRank(skillName);
string bestSkill = skillName;
for (int i=0;i<knownSkills.length; i++)
{
string skillLine = getSkillLine(knownSkills[i]);
int skillRank = getSkillRank(knownSkills[i]);
if (skillLine != passedSkillLine)
continue;
if (skillRank <= passedSkillRank)
continue;
bestSkill = knownSkills[i];
}
return bestSkill;
}
string getNextSkillInLine(string skillName)
{
string[] allSkills = dataTableGetStringColumn(BEASTS_SPECIALS, "ability_name");
string passedSkillLine = getSkillLine(skillName);
int passedSkillRank = getSkillRank(skillName);
int nextRank = passedSkillRank + 1;
for (int i=0;i<allSkills.length;i++)
{
string skillLine = getSkillLine(allSkills[i]);
int skillRank = getSkillRank(allSkills[i]);
if (skillLine != passedSkillLine)
{
continue;
}
if (skillRank == nextRank)
{
return allSkills[i];
}
}
return skillName;
}
int getSkillBeastmasterSkillComparison(string querySkill, string skillToCompare)
{
string queryLine = getSkillLine(querySkill);
string compareLine = getSkillLine(skillToCompare);
if (queryLine != compareLine)
return SKILL_DIFFERENT_LINE;
int queryRank = getSkillRank(querySkill);
int compareRank = getSkillRank(skillToCompare);
if (queryRank < compareRank)
return SKILL_LOWER;
if (queryRank == compareRank)
return SKILL_SAME;
if (queryRank > compareRank)
return SKILL_HIGHER;
return SKILL_CANNOT_COMPARE;
}
string getSkillLine(string skillName)
{
return dataTableGetString(BEASTS_SPECIALS, skillName, "skill_line");
}
int getSkillRank(string skillName)
{
return dataTableGetInt(BEASTS_SPECIALS, skillName, "skill_rank");
}
void updateBeastStats(obj_id bcd, obj_id beast)
{
if(!isValidBCD(bcd) || !isValidBeast(beast))
{
return;
}
int level = getBCDBeastLevel(bcd);
string beastType = getBCDBeastType(bcd);
if(beastType == null || beastType.length() <= 0)
{
blog("updateBeastStats() bad beast type.");
return;
}
dictionary beastDict = utils.dataTableGetRow(BEASTS_TABLE, beastType);
if(beastDict == null || beastDict.size() <= 0)
{
blog("updateBeastStats() missing entry in the " + BEASTS_TABLE + " table for beastType: " + beastType + ".");
return;
}
dictionary beastStatsDict = utils.dataTableGetRow(BEASTS_STATS, level - 1);
if(beastStatsDict == null || beastStatsDict.size() <= 0)
{
blog("updateBeastStats() missing entry in the " + BEASTS_STATS + " table for beastType: " + beastType + ".");
return;
}
int expertiseDamage = getEnhancedSkillStatisticModifierUncapped(getMaster(beast), "expertise_bm_pet_damage");
float incubationDamageBonus = utils.getFloatScriptVar(beast, OBJVAR_INCREASE_DPS);
// Weapons and damage ranges
int intMinDamage = (int)beastStatsDict.getInt("MinDmg");
int intMaxDamage = (int)beastStatsDict.getInt("MaxDmg");
intMinDamage = getExpertiseStat(intMinDamage, expertiseDamage, .5f);
intMaxDamage = getExpertiseStat(intMaxDamage, expertiseDamage, .5f);
float floatMinDamage = (float)intMinDamage * (1.0f + incubationDamageBonus / 100.0f);
float floatMaxDamage = (float)intMaxDamage * (1.0f + incubationDamageBonus / 100.0f);
intMinDamage = (int)floatMinDamage;
intMaxDamage = (int)floatMaxDamage;
// Weapon initialization
obj_id beastWeapon = getCurrentWeapon(beast);
if(isIdValid(beastWeapon))
{
setWeaponMaxDamage(beastWeapon, intMaxDamage);
setWeaponMinDamage(beastWeapon, intMinDamage);
weapons.setWeaponData(beastWeapon);
}
obj_id defaultWeapon = getDefaultWeapon(beast);
if(isIdValid(defaultWeapon))
{
setWeaponMaxDamage(defaultWeapon, intMaxDamage);
setWeaponMinDamage(defaultWeapon, intMinDamage);
weapons.setWeaponData(defaultWeapon);
}
updateBeastHappiness(bcd, beast);
}
void setBCDIncubationStats(obj_id bcd, obj_id egg)
{
int[] incubationBonuses = fillIncubationArray(egg, bcd);
setObjVar(bcd, OBJVAR_BEAST_INCUBATION_BONUSES, incubationBonuses);
string parentTemplate = getStringObjVar(egg, OBJVAR_BEAST_PARENT);
setObjVar(bcd, OBJVAR_BEAST_PARENT, parentTemplate);
string creatorName = getStringObjVar(egg, OBJVAR_BEAST_ENGINEER);
setObjVar(bcd, OBJVAR_BEAST_ENGINEER, creatorName);
}
void setBeastSkillBonuses(obj_id beast)
{
setBeastCritChance(beast);
setBeastStrikethroughChance(beast);
setBeastStrikethroughValue(beast);
setBeastBlockChance(beast);
setBeastBlockValue(beast);
setBeastDodgeChance(beast);
setBeastParryChance(beast);
setBeastEvadeChance(beast);
setBeastEvadeValue(beast);
}
boolean setBeastCritChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float hi_mod = getBeastCustomMod(beast, MOD_HUNTERS_INSTINCT);
float critChance = hi_mod * 0.8f;
critChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_CRITICAL_HIT) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_CRITICAL_HIT, critChance);
return true;
}
float getBeastCritChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float crit_mod = getBeastCritChance(beast);
cun_mod = (cun_mod / 100.0f);
return crit_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_CRITICAL_HIT);
}
boolean setBeastStrikethroughChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float hi_mod = getBeastCustomMod(beast, MOD_HUNTERS_INSTINCT);
float strikeChance = hi_mod * 0.8f;
strikeChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_STRIKETHROUGH) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_STRIKETHROUGH, strikeChance);
return true;
}
float getBeastStrikethroughChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float strike_mod = getBeastStrikethroughChance(beast);
cun_mod = (cun_mod / 100.0f);
return strike_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_STRIKETHROUGH);
}
boolean setBeastStrikethroughValue(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float int_mod = getBeastCustomMod(beast, MOD_INTELLIGENCE);
float strikeValue = int_mod * 2.0f;
strikeValue += utils.getFloatScriptVar(beast, OBJVAR_INCREASE_STRIKETHROUGH_RATING);
utils.setScriptVar(beast, OBJVAR_INCREASE_STRIKETHROUGH_RATING, strikeValue);
return true;
}
float getBeastStrikethroughValue(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float strike_value = getBeastStrikethroughValue(beast);
cun_mod = (cun_mod / 100.0f);
return strike_value * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_STRIKETHROUGH_RATING);
}
float getBeastHitChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float hit_mod = getBeastHitChance(beast);
cun_mod = (cun_mod / 100.0f);
return hit_mod * cun_mod;
}
return (utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_HIT_CHANCE) / 10.0f);
}
boolean setBeastBlockChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float svl_mod = getBeastCustomMod(beast, MOD_SURVIVAL);
float blockChance = svl_mod * 0.65f;
blockChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_BLOCK_CHANCE) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_BLOCK_CHANCE, blockChance);
return true;
}
float getBeastBlockChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float block_mod = getBeastBlockChance(beast);
cun_mod = (cun_mod / 100.0f);
return block_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_BLOCK_CHANCE);
}
float getBeastGlanceChance(obj_id beast)
{
if(!isIdValid(beast) || !exists(beast))
{
return 0.0f;
}
return utils.getFloatScriptVar(beast, OBJVAR_INCREASE_GLANCING_BLOW);
}
boolean setBeastBlockValue(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float int_mod = getBeastCustomMod(beast, MOD_INTELLIGENCE);
float blockValue = int_mod * 25;
blockValue += utils.getFloatScriptVar(beast, OBJVAR_INCREASE_BLOCK_VALUE);
utils.setScriptVar(beast, OBJVAR_INCREASE_BLOCK_VALUE, blockValue);
return true;
}
int getBeastBlockValue(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0;
float cun_mod = getBeastCunningRating(beast);
float block_value = (float)getBeastBlockValue(beast);
cun_mod = (cun_mod / 100.0f);
return Math.round(block_value * cun_mod);
}
return (int)utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_BLOCK_VALUE);
}
boolean setBeastDodgeChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float svl_mod = getBeastCustomMod(beast, MOD_SURVIVAL);
float dodgeChance = svl_mod * 0.65f;
dodgeChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_DODGE) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_DODGE, dodgeChance);
return true;
}
float getBeastDodgeChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float dodge_mod = getBeastDodgeChance(beast);
cun_mod = (cun_mod / 100.0f);
return dodge_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_DODGE);
}
boolean setBeastParryChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float svl_mod = getBeastCustomMod(beast, MOD_SURVIVAL);
float parryChance = svl_mod * 0.65f;
parryChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_PARRY) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_PARRY, parryChance);
return true;
}
float getBeastParryChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float parry_mod = getBeastParryChance(beast);
cun_mod = (cun_mod / 100.0f);
return parry_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_PARRY);
}
boolean setBeastEvadeChance(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float svl_mod = getBeastCustomMod(beast, MOD_SURVIVAL);
float evadeChance = svl_mod * 0.65f;
evadeChance += (utils.getFloatScriptVar(beast, OBJVAR_INCREASE_EVASION) / 10.0f);
utils.setScriptVar(beast, OBJVAR_INCREASE_EVASION, evadeChance);
return true;
}
float getBeastEvadeChance(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float evade_mod = getBeastEvadeChance(beast);
cun_mod = (cun_mod / 100.0f);
return evade_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_EVASION);
}
boolean setBeastEvadeValue(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return false;
if(!isBeast(beast))
return false;
float int_mod = getBeastCustomMod(beast, MOD_INTELLIGENCE);
float evadeValue = int_mod * 2.0f;
evadeValue += utils.getFloatScriptVar(beast, OBJVAR_INCREASE_EVASION_RATING);
utils.setScriptVar(beast, OBJVAR_INCREASE_EVASION_RATING, evadeValue);
return true;
}
float getBeastEvadeValue(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float evade_mod = getBeastEvadeValue(beast);
cun_mod = (cun_mod / 100.0f);
return evade_mod * cun_mod;
}
return utils.getFloatScriptVar(attacker, OBJVAR_INCREASE_EVASION_RATING);
}
float getBeastLearnBonus(obj_id beast)
{
if (isPlayer(beast))
beast = getBeastOnPlayer(beast);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float int_mod = getBeastCustomMod(beast, MOD_INTELLIGENCE);
return (int_mod * 1.5f);
}
float getBeastCunningRating(obj_id beast)
{
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCustomMod(beast, MOD_CUNNING);
return (cun_mod * 3.0f);
}
float getBeastModDamageBonus(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float dam_mod = getBeastModDamageBonus(beast);
cun_mod = (cun_mod / 100.0f);
return dam_mod * cun_mod;
}
float agg_mod = getBeastCustomMod(attacker, MOD_AGGRESSION);
return (float)agg_mod * 2.0f;
}
float getBeastModGlancingReduction(obj_id attacker)
{
if (isPlayer(attacker))
{
obj_id beast = getBeastOnPlayer(attacker);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float dam_mod = getBeastModGlancingReduction(beast);
cun_mod = (cun_mod / 100.0f);
return dam_mod * cun_mod;
}
float agg_mod = getBeastCustomMod(attacker, MOD_AGGRESSION);
return agg_mod * 1.0f;
}
float getBeastModDamageReduction(obj_id defender)
{
if (isPlayer(defender))
{
obj_id beast = getBeastOnPlayer(defender);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float dam_mod = getBeastModDamageReduction(beast);
cun_mod = (cun_mod / 100.0f);
return dam_mod * cun_mod;
}
float res_mod = getBeastCustomMod(defender, MOD_RESILIENCE);
return res_mod * 1.25f;
}
float getBeastModPunishingReduction(obj_id defender)
{
if (isPlayer(defender))
{
obj_id beast = getBeastOnPlayer(defender);
if (!isIdValid(beast) || !exists(beast))
return 0.0f;
float cun_mod = getBeastCunningRating(beast);
float dam_mod = getBeastModPunishingReduction(beast);
cun_mod = (cun_mod / 100.0f);
return dam_mod * cun_mod;
}
float res_mod = getBeastCustomMod(defender, MOD_RESILIENCE);
return res_mod * 1.0f;
}
float getBeastCustomMod(obj_id beast, int type)
{
if (isPlayer(beast))
beast = getBeastOnPlayer(beast);
if (!isIdValid(beast) || !exists(beast) || !isBeast(beast))
return 0;
switch(type)
{
case MOD_HUNTERS_INSTINCT:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_HUNTERS_INSTINCT);
case MOD_AGGRESSION:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_AGGRESSION);
case MOD_INTELLIGENCE:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_INTELLIGENCE);
case MOD_CUNNING:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_CUNNING);
case MOD_RESILIENCE:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_BEASTIAL_RESILIENCE);
case MOD_SURVIVAL:
return utils.getFloatScriptVar(beast, OBJVAR_SKILL_SURVIVAL);
}
return 0;
}
boolean setToggledCommands(obj_id player, obj_id bcd)
{
if(!isIdValid(player))
{
//LOG("BEAST_ABILITIES", "setToggledCommands::!isIdValid(player)");
return false;
}
if(!isIdValid(bcd))
{
//LOG("BEAST_ABILITIES", "setToggledCommands::!isIdValid(bcd)");
return false;
}
string[] toggledAbilities = {"","","","",""};
string[] toggledAutoRepeats = getAutoRepeatAbilityList(bcd);
for(int i = 0; i < toggledAutoRepeats.length; ++i)
{
//LOG("BEAST_ABILITIES", "setToggledCommands::!toggledAutoRepeats[" + i + "] " + toggledAutoRepeats[i] );
toggledAbilities[i] = toggledAutoRepeats[i];
//LOG("BEAST_ABILITIES", "setToggledCommands::!toggledAbilities[" + i + "] " + toggledAbilities[i] );
}
if(getBCDBeastDefensive(bcd))
{
toggledAbilities[4] = "toggleBeastDefensive";
}
else
toggledAbilities[4] = "toggleBeastPassive";
setBeastmasterToggledPetCommands(player, toggledAbilities);
return true;
}
string getBeastTrait(obj_id player, obj_id target)
{
return "none";
}
boolean canBeastGetLevelBasedXP(obj_id beast, obj_id npc)
{
int beastLevel = getBeastLevel(beast);
int mobLevel = getLevel(npc);
int levelDiff = mobLevel - beastLevel;
if(levelDiff > 0)
{
beastLevel += levelDiff;
}
int exp = xp.getLevelBasedXP(beastLevel);
if(levelDiff < 0)
{
// every 6 levels over 20, you get another level point
float maxLevelDiff = 10f;
if(beastLevel > 20)
{
maxLevelDiff += (int) ((beastLevel - 20) / 6);
}
exp += (int) (exp * (levelDiff / maxLevelDiff));
if(exp < 1)
{
exp = 1;
}
}
if(exp > 1)
return true;
else
return false;
}
int useBeastInjector(obj_id player, obj_id injector, obj_id beast, string beastFamily, string[] injectorFamilies, int mark)
{
if(!exists(player) || !exists(injector) || !exists(beast))
return INJECTOR_RETURN_BAD_DATA;
if(!isIdValid(player) || !isIdValid(injector) || !isIdValid(beast))
return INJECTOR_RETURN_BAD_DATA;
if(beastFamily == null || beastFamily.equals(""))
return INJECTOR_RETURN_BAD_DATA;
if(injectorFamilies == null || injectorFamilies.length <= 0)
return INJECTOR_RETURN_BAD_DATA;
int level = getBeastLevel(beast);
if(level > STEROID_BUFF_LEVEL_CAP && mark <= 1)
{
sendSystemMessage(player, SID_BEAST_LEVEL_TOO_HIGH);
return INJECTOR_RETURN_WRONG_LEVEL;
}
if(level <= STEROID_BUFF_LEVEL_CAP && mark == 2)
{
sendSystemMessage(player, SID_BEAST_LEVEL_TOO_LOW);
return INJECTOR_RETURN_WRONG_LEVEL;
}
if(buff.hasBuff(beast, "bm_beast_steroid"))
return INJECTOR_RETURN_HAS_BUFF;
//this means multiple families can use it.
if(injectorFamilies.length > 0)
{
for(int i = 0; i < injectorFamilies.length; ++i)
{
//find out if our family
if(beastFamily.equals(injectorFamilies[i]))
{
//addXpBuff(injector, beast);
return INJECTOR_RETURN_SUCESS;
}
}
// no family handle message in injector script
return INJECTOR_RETURN_WRONG_FAMILY;
}
return INJECTOR_RETURN_BAD_DATA;
}
boolean convertBcdIntoBeastItem(obj_id player, obj_id bcd)
{
obj_id pInv = utils.getInventoryContainer(player);
obj_id beastDisplayBase = createObjectOverloaded(BEAST_DISPLAY_TEMPLATE, pInv);
if(!isIdValid(beastDisplayBase))
{
debugSpeakMsg(player, "Stuffed beast base is null.");
return false;
}
setObjVar(beastDisplayBase, "no_dna_core", 1);
string allObjVars = getPackedObjvars(bcd);
setPackedObjvars(beastDisplayBase, allObjVars);
setName(beastDisplayBase, getBCDBeastName(bcd));
attachScript(beastDisplayBase, "systems.beast.beast_display");
destroyObject(bcd);
// If I failed to destroy the bcd, then destroy the base.
if(isIdValid(bcd))
{
destroyObject(beastDisplayBase);
}
return true;
}
obj_id createHolopetCubeFromEgg(obj_id player, obj_id egg)
{
int beastHashType = getIntObjVar(egg, beast_lib.OBJVAR_BEAST_TYPE);
string beastType = incubator.convertHashTypeToString(beastHashType);
if ( beastType != null && beastType.length() > 0 )
{
obj_id holepetCube = createBeastHolopet(player, egg, beastType);
if ( isIdValid(holepetCube) )
{
if ( rand(0,99) == 10 )
{
setObjVar(holepetCube, HOLO_BEAST_RARE_COLOR_OBJVAR, HOLO_BEAST_TYPE3_QUALITY3);
}
return holepetCube;
}
}
return obj_id.NULL_ID;
}
obj_id createBeastHolopet(obj_id player, obj_id source, string beastType)
{
obj_id playerInv = utils.getInventoryContainer(player);
if ( isIdValid(playerInv) )
{
string cubeTemplate = "object/tangible/item/data_cube_holobeast.iff";
obj_id holepetCube = createObjectOverloaded(cubeTemplate, playerInv);
if ( isIdValid(holepetCube) )
{
// Set Holo-Pet type
setObjVar(holepetCube, "holopet_type", beastType);
// Set Egg Creator's Name
if ( hasObjVar(source, beast_lib.OBJVAR_BEAST_ENGINEER) )
{
string creatorName = getStringObjVar(source, beast_lib.OBJVAR_BEAST_ENGINEER);
if ( creatorName != null && creatorName.length() > 0 )
{
setObjVar(holepetCube, beast_lib.OBJVAR_BEAST_ENGINEER, creatorName);
}
}
// Set Hol-Pet Cube Name
string holoBeastName = localizedHoloBeastBaseName( beastType);
setName(holepetCube, holoBeastName + "Holo-beast Data Cube");
attachScript(holepetCube, "item.publish_gift.holopet_data_cube");
return holepetCube;
}
}
return obj_id.NULL_ID;
}
string localizedHoloBeastBaseName(string beastType)
{
if ( beastType.startsWith("bm_") )
{
beastType = beast_lib.stripBmFromType(beastType);
}
string finalName = "";
string_id beastTypeSid = new string_id("monster_name", beastType);
string lowercaseName = localize(beastTypeSid);
if ( lowercaseName != null && lowercaseName.length() > 0 )
{
string[] splitName = split(lowercaseName, ' ');
for ( int i = 0; i < splitName.length; i++ )
{
finalName += toUpper(splitName[i], 0) + " ";
}
}
return finalName;
}