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