mirror of
https://bitbucket.org/seefoe/dsrc.git
synced 2026-07-31 01:15:55 -04:00
You can now rename your beast as much as you would like. Police officers will no longer harass corpses. Static NPC's conversing will now successfully converse with one another. When a player dies, the pet will go invulnerable and be immediately packed up. When respecing professions, your weapon will now be unequiped. Fixed the chat system for some NPC's. You can now only mount your vehicle from less than 5 meters. Quest items that can be clicked on cannot be used from less than 5 meters. Added a message to the QaSetup script, notifying the QA that they have received their items.
6371 lines
145 KiB
Plaintext
6371 lines
145 KiB
Plaintext
/**
|
|
* Title: library.beast_lib
|
|
* Description:
|
|
*/
|
|
|
|
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.factions;
|
|
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
|
|
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 an 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);
|
|
}
|
|
//Called when player beastmaster dies. Pet no longer dies, but is packed up instead.
|
|
void playerDeathStoreBeast(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;
|
|
}
|
|
|
|
factions.setFaction(currentBeast, "Unattackable");
|
|
stop(currentBeast);
|
|
storeBeast(bcd);
|
|
}
|
|
|
|
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;
|
|
}
|