Files
dsrc/sku.0/sys.server/compiled/game/script/ai/beast.script
T

1564 lines
34 KiB
Plaintext

include library.ai_lib;
include library.callable;
include library.chat;
include ai.ai_combat;
include library.pet_lib;
include library.beast_lib;
include library.colors;
include library.combat_consts;
include library.healing;
include library.utils;
include library.poi;
include library.xp;
include library.hue;
include library.player_stomach;
include library.consumable;
include library.food;
include library.space_transition;
include library.sui;
include library.stealth;
include library.prose;
include library.buff;
const string MENU_FILE = "pet/pet_menu";
const int MAX_UNACKNOWLEDGED_MESSAGE_COUNT = 3;
const int PING_INTERVAL = 120;
const int PING_INTERVAL_RETRY = 30;
const float MAX_DISTANCE_FROM_MASTER = 128.0f; // Meters
const boolean BEAST_DEBUG = true;
const string_id PCOLOR = new string_id("sui", "set_primary_color");
void blog(string text)
{
if(BEAST_DEBUG)
{
LOG("beast.script", text);
}
}
trigger OnAttach()
{
setObjVar(self, "beast.pingMessageNumber", 0);
setObjVar(self, "beast.lastAcknowledgeNumber", 0);
messageTo(self, "handleSetupBeast", null, 1, false);
messageTo(self, "beastPing", null, 5, false);
return SCRIPT_CONTINUE;
}
trigger OnAddedToWorld()
{
messageTo(self, "handleSetupBeast", null, 1, false);
return SCRIPT_CONTINUE;
}
trigger OnGetAttributes(obj_id player, string[] names, string[] attribs)
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
int idx = utils.getValidAttributeIndex(names);
obj_id master = getMaster(self);
obj_id beastBCD = beast_lib.getBeastBCD(self);
if(!beast_lib.isValidPlayer(master))
{
return SCRIPT_CONTINUE;
}
names[idx] = "owner";
attribs[idx] = getName(master);
idx++;
if(exists(beastBCD) && hasObjVar(beastBCD, beast_lib.OBJVAR_BEAST_ENGINEER))
{
string creatorName = getStringObjVar(beastBCD, beast_lib.OBJVAR_BEAST_ENGINEER);
names[idx] = "bm_creator";
attribs[idx] = creatorName;
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
names[idx] = "health";
attribs[idx] = " " + (int)getMaxAttrib(self, HEALTH);
idx++;
int level = getLevel(self);
if(level < 90)
{
dictionary beastStatsNextLevel = utils.dataTableGetRow(beast_lib.BEASTS_STATS, level - 1);
int experienceLastLevel = 0;
int experienceNextLevel = 0;
// You cannot get a previous level of a beast, if it is level 1 (no such thing as 0 level)
if(level > 1)
{
dictionary beastStatsLastLevel = utils.dataTableGetRow(beast_lib.BEASTS_STATS, level - 2);
if(beastStatsLastLevel == null || beastStatsLastLevel.size() <= 0)
{
return SCRIPT_CONTINUE;
}
experienceLastLevel = beastStatsLastLevel.getInt("XP");
}
if(beastStatsNextLevel == null || beastStatsNextLevel.size() <= 0)
{
blog("initializeBeastStats() missing entry in the " + beast_lib.BEASTS_STATS + " table for level: " + level + ".");
return SCRIPT_CONTINUE;
}
experienceNextLevel = beastStatsNextLevel.getInt("XP");
int beastExperience = beast_lib.getBeastExperience(self);
// A beast has progressed towards its next level by an amount, which is equal to its current experience minus the
// experience that was required for the previous level.
int experienceProgress = beastExperience - experienceLastLevel;
// This level requires an amount of experience equal to the next level's experience requirement minus the last level's
// experience requirement.
int experienceNeeded = experienceNextLevel - experienceLastLevel;
int experiencePercentage = 0;
// No division by zero.
if(experienceProgress > 0)
{
// The percentage needed to level is equal to the total amount of experience needed for the level divided by the progress
// the beast has made.
experiencePercentage = (int)(((float)experienceProgress / (float)experienceNeeded) * 100);
}
names[idx] = "level_progress";
attribs[idx] = "" + experiencePercentage + "%";
idx++;
}
if(beast_lib.getBeastCanLevel(self))
{
names[idx] = "exp_on";
attribs[idx] = "true";
idx++;
}
else
{
names[idx] = "exp_off";
attribs[idx] = "true";
idx++;
}
names[idx] = beast_lib.BEAST_MOOD_TITLE;
string currentHappiness = utils.packStringId (beast_lib.convertHappinessString (beastBCD));
if(currentHappiness != null)
{
attribs[idx] = "" + currentHappiness;
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
names[idx] = beast_lib.BEAST_LOYALTY_TITLE;
string currentLoyalty = utils.packStringId (beast_lib.convertLoyaltyString (beastBCD));
if(currentLoyalty != null)
{
attribs[idx] = "" + currentLoyalty;
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
int loyaltyPercentage = beast_lib.getBeastLoyaltyToNextLevelPercentage(self);
if(loyaltyPercentage >= 0)
{
names[idx] = beast_lib.BEAST_LOYALTY_PERCENTAGE_TITLE;
attribs[idx] = "" + loyaltyPercentage + "%";
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
/*
if(isGod(player))
{
names[idx] = "Loyalty";
attribs[idx] = "" + beast_lib.getBeastLoyalty(self);
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
*/
}
string[] abilityList = beast_lib.getBeastmasterPetBarData(player, self);
int slotsAvalable = getSkillStatisticModifier(player, "expertise_bm_add_pet_bar");
if (abilityList[3] != "" && slotsAvalable >= 0 && abilityList[3] != "empty" && abilityList[3] != "disabled")
{
names[idx] = "bm_ability_1";
attribs[idx] = ""+ utils.packStringId (new string_id("cmd_n", abilityList[3]));
idx++;
}
if (abilityList[4] != "" && slotsAvalable >= 1 && abilityList[4] != "empty" && abilityList[4] != "disabled")
{
names[idx] = "bm_ability_2";
attribs[idx] = ""+ utils.packStringId (new string_id("cmd_n", abilityList[4]));
idx++;
}
if (abilityList[5] != "" && slotsAvalable >= 2 && abilityList[5] != "empty" && abilityList[5] != "disabled")
{
names[idx] = "bm_ability_3";
attribs[idx] = ""+ utils.packStringId (new string_id("cmd_n", abilityList[5]));
idx++;
}
if (abilityList[6] != "" && slotsAvalable >= 3 && abilityList[6] != "empty" && abilityList[6] != "disabled")
{
names[idx] = "bm_ability_4";
attribs[idx] = ""+ utils.packStringId (new string_id("cmd_n", abilityList[6]));
idx++;
}
names[idx] = "armorhpmax";
attribs[idx] = "" + (utils.getIntScriptVar(self, "beast.display.armor") + getEnhancedSkillStatisticModifier(self, "private_armor_bonus"));
idx++;
obj_id beastWeapon = getCurrentWeapon(self);
if(isIdValid(beastWeapon))
{
int minDamage = getWeaponMinDamage(beastWeapon);
int maxDamage = getWeaponMaxDamage(beastWeapon);
int expertiseDamageBonus = getEnhancedSkillStatisticModifierUncapped(self, "expertise_damage_all");
minDamage = (int)(minDamage * (1.0f + ((float)expertiseDamageBonus / 100.0f)));
maxDamage = (int)(maxDamage * (1.0f + ((float)expertiseDamageBonus / 100.0f)));
float weaponSpeed = getWeaponAttackSpeed(beastWeapon);
names[idx] = "damage";
attribs[idx] = "" + minDamage + " - " + maxDamage;
idx++;
names[idx] = "attackspeed";
attribs[idx] = "" + weaponSpeed;
idx++;
float beastDPS = utils.roundFloatByDecimal((minDamage + maxDamage) / weaponSpeed / 2);
names[idx] = "basedps";
attribs[idx] = ""+beastDPS;
idx++;
}
else
{
obj_id defaultWeapon = getDefaultWeapon(self);
if(isIdValid(defaultWeapon))
{
int minDamage = getWeaponMinDamage(defaultWeapon);
int maxDamage = getWeaponMaxDamage(defaultWeapon);
float weaponSpeed = getWeaponAttackSpeed(defaultWeapon);
int expertiseDamageBonus = getEnhancedSkillStatisticModifierUncapped(self, "expertise_damage_all");
minDamage = (int)(minDamage * (1.0f + ((float)expertiseDamageBonus / 100.0f)));
maxDamage = (int)(maxDamage * (1.0f + ((float)expertiseDamageBonus / 100.0f)));
names[idx] = "damage";
attribs[idx] = "" + minDamage + " - " + maxDamage;
idx++;
names[idx] = "attackspeed";
attribs[idx] = "" + weaponSpeed;
idx++;
float beastDPS = utils.roundFloatByDecimal((minDamage + maxDamage) / weaponSpeed / 2);
names[idx] = "basedps";
attribs[idx] = ""+beastDPS;
idx++;
}
}
if(beast_lib.DISPLAY_NAMES.length == beast_lib.ARRAY_BEAST_INCUBATION_STATS.length &&
beast_lib.DISPLAY_NAMES.length == beast_lib.DISPLAY_CONVERSION_RATES.length)
{
for(int i = 0; i < beast_lib.DISPLAY_NAMES.length; ++i)
{
string name = beast_lib.DISPLAY_NAMES[i];
if(name.indexOf("_skill") < 0)
{
if(!name.equals("block_value_bonus"))
{
names[idx] = beast_lib.DISPLAY_NAMES[i];
attribs[idx] = "" + utils.roundFloatByDecimal((utils.getFloatScriptVar(self, beast_lib.ARRAY_BEAST_INCUBATION_STATS[i]) * beast_lib.DISPLAY_CONVERSION_RATES[i])) + "%";
idx++;
}
else
{
names[idx] = beast_lib.DISPLAY_NAMES[i];
attribs[idx] = "" + utils.roundFloatByDecimal(utils.getFloatScriptVar(self, beast_lib.ARRAY_BEAST_INCUBATION_STATS[i]));
idx++;
}
continue;
}
else
{
names[idx] = beast_lib.DISPLAY_NAMES[i];
attribs[idx] = "" + utils.roundFloatByDecimal(utils.getFloatScriptVar(self, beast_lib.ARRAY_BEAST_INCUBATION_STATS[i]));
idx++;
continue;
}
}
}
float glanceReduct = beast_lib.getBeastModGlancingReduction(self);
if(glanceReduct > 0)
{
names[idx] = "bm_glance_reduct";
attribs[idx] = "" + glanceReduct + "%";
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
float damageReduct = beast_lib.getBeastModDamageReduction(self);
if(damageReduct > 0)
{
names[idx] = "bm_damage_reduct";
attribs[idx] = "" + damageReduct + "%";
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
float punishReduct = beast_lib.getBeastModPunishingReduction(self);
if(punishReduct > 0)
{
names[idx] = "bm_punish_reduct";
attribs[idx] = "" + punishReduct + "%";
idx++;
if (idx >= names.length)
return SCRIPT_CONTINUE;
}
return SCRIPT_CONTINUE;
}
trigger OnDetach()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(isIdValid(master))
{
if(isIdValid(beast_lib.getBeastOnPlayer(master)))
{
blog("Beast not cleaned up on " + master + " and something attempted to detach its beast script.");
return SCRIPT_OVERRIDE;
}
}
return SCRIPT_CONTINUE;
}
trigger OnDestroy()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(isIdValid(master))
{
if(isIdValid(beast_lib.getBeastOnPlayer(master)))
{
blog("Beast not cleaned up on " + master + " and something attempted to destroy it.");
return SCRIPT_OVERRIDE;
}
}
return SCRIPT_CONTINUE;
}
messageHandler handleSetupBeast()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(!isIdValid(master) || !exists(master))
{
blog("handleSetupBeast() master is not valid. Destroying beast.");
destroyObject(self);
return SCRIPT_CONTINUE;
}
if(isPlayer(master))
{
// Make sure the pet objvar is set.
setObjVar(self, "ai.pet", true);
}
beast_lib.beastFollowTarget(self, master);
if(!ai_lib.isInCombat(self) && stealth.canBeastStealth(self))
{
stealth.makeBeastInvisible(master, "appearance/pt_smoke_puff.prt");
}
return SCRIPT_CONTINUE;
}
messageHandler beastPing()
{
int lastMessage = getIntObjVar(self, "beast.pingMessageNumber");
int nextMessage = lastMessage + 1;
setObjVar(self, "beast.pingMessageNumber", nextMessage);
int acknowledgedMessage = getIntObjVar(self, "beast.lastAcknowledgeNumber");
int unacknowledgedGap = lastMessage - acknowledgedMessage;
obj_id master = getMaster(self);
if(!isIdValid(master) || !exists(master))
{
setMaster(self, null);
blog("beastPing() master is not valid. Destroying beast.");
if(destroyObject(self))
{
return SCRIPT_CONTINUE;
}
else
{
blog("beastPing() failed to destroy the beast with no master online");
}
}
obj_id bcd = beast_lib.getBeastBCD(self);
if(!beast_lib.isValidBCD(bcd))
{
setMaster(self, null);
blog("beastPing() bcd is not valid. Destroying beast.");
if(destroyObject(self))
{
return SCRIPT_CONTINUE;
}
else
{
blog("beastPing() failed to destroy the beast with no BCD online");
}
}
if(unacknowledgedGap > MAX_UNACKNOWLEDGED_MESSAGE_COUNT)
{
beast_lib.storeBeast(bcd);
return SCRIPT_CONTINUE;
}
if(getDistance(self, master) > MAX_DISTANCE_FROM_MASTER)
{
blog("self: " + self + " too far from master: " + master + "(" + getDistance(self, master) + " and is being stored.");
beast_lib.storeBeast(bcd);
return SCRIPT_CONTINUE;
}
int nextCallbackInterval = (unacknowledgedGap <= 0) ? PING_INTERVAL : PING_INTERVAL_RETRY;
dictionary messageData = new dictionary();
messageData.put("beastId", self);
messageData.put("messageId", nextMessage);
messageTo(bcd, "beastPing", messageData, 1, false);
messageTo(self, "beastPing", null, nextCallbackInterval, false);
return SCRIPT_CONTINUE;
}
messageHandler bcdPingResponse()
{
int ack = params.getInt("messageId");
int previousAck = getIntObjVar(self, "beast.pingMessageNumber");
if(ack > previousAck)
{
setObjVar(self, "beast.lastAcknowledgeNumber", ack);
}
return SCRIPT_CONTINUE;
}
messageHandler resumeDefaultCalmBehavior()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(!isIdValid(master))
{
blog("resumeDefaultCalmBehavior() master is invalid and is being destroyed.");
destroyObject(self);
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self))
{
return SCRIPT_CONTINUE;
}
if(isIdValid(master))
{
setHomeLocation(self, getLocation(master));
}
return SCRIPT_CONTINUE;
}
messageHandler checkMovementSpeed()
{
obj_id target = params.getObjId("target");
beast_lib.validateFollowTarget(self, target);
return SCRIPT_CONTINUE;
}
trigger OnFollowWaiting(obj_id target)
{
beast_lib.validateFollowTarget(self, target);
return SCRIPT_CONTINUE;
}
trigger OnFollowMoving(obj_id target)
{
beast_lib.validateFollowTarget(self, target);
return SCRIPT_CONTINUE;
}
trigger OnBehaviorChange(int newBehavior, int oldBehavior, int[] changeFlags)
{
if(ai_lib.isAiDead(self))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self))
{
return SCRIPT_CONTINUE;
}
LOG("beast", "OnBehaviorChange newBehavior: " + newBehavior + " oldBehavior: " + oldBehavior);
//don't put code in this trigger. Put it in doCalmerBehavior or doAgitated behavior
// this is hard enough to read already:
if(newBehavior <= oldBehavior)
{
if(doCalmerBehavior(self, newBehavior, oldBehavior) == SCRIPT_CONTINUE)
{
return SCRIPT_CONTINUE;
}
else
{
return SCRIPT_OVERRIDE;
}
}
else
{
if(doAgitatedBehavior(self, newBehavior, oldBehavior) == SCRIPT_CONTINUE)
{
return SCRIPT_CONTINUE;
}
else
{
return SCRIPT_OVERRIDE;
}
}
}
int doCalmerBehavior(obj_id npc, int newBehavior, int oldBehavior)
{
//You are now calming down.
// return SCRIPT_OVERRIDE to stomp on default behaviors:
switch (newBehavior)
{
case BEHAVIOR_CALM:
//going from More Agitated to Calm
// Your code goes here.
break;
case BEHAVIOR_ALERT:
//going from More Agitated to Alert
// Your code goes here.
break;
case BEHAVIOR_THREATEN:
//going from More Agitated to Threaten
// Your code goes here.
break;
case BEHAVIOR_FLEE:
//going from More Agitated to Flee
// Your code goes here.
break;
case BEHAVIOR_PANIC:
//going from More Agitated to Panic
// Your code goes here.
break;
case BEHAVIOR_ATTACK:
//Going from More Agitated to Attack
// Your code goes here.
break;
case BEHAVIOR_FRENZY:
//This is impossible.
break;
default :
// This is an error
break;
}
return SCRIPT_OVERRIDE;
}
int doAgitatedBehavior(obj_id npc, int newBehavior, int oldBehavior)
{
//You are becoming more Fearful or more Angry
// return SCRIPT_OVERRIDE to stomp on default behaviors:
switch (newBehavior)
{
case BEHAVIOR_CALM:
//this is impossible
break;
case BEHAVIOR_ALERT:
//going from Calmer to Alert
// Your code goes here.
break;
case BEHAVIOR_THREATEN:
//going from Calmer to Threaten
// Your code goes here.
break;
case BEHAVIOR_FLEE:
//going from Calmer to Flee
// Your code goes here.
break;
case BEHAVIOR_PANIC:
//Going from Calmer to Panic
// Your code goes here.
break;
case BEHAVIOR_ATTACK:
//Going from Calmer to Attack
// Your code goes here.
break;
case BEHAVIOR_FRENZY:
//going from Calmer to Frenzy
break;
default :
// This is an error
break;
}
return SCRIPT_OVERRIDE;
}
messageHandler lairThreatened()
{
return SCRIPT_OVERRIDE;
}
//--------------- DEATH -----------------------//
trigger OnIncapacitated(obj_id attacker)
{
beast_lib.killBeast(self, attacker);
return SCRIPT_CONTINUE;
}
messageHandler handleBeastIncappedDecay()
{
int incapTimer = utils.getIntScriptVar(self, "incapTimer");
int messageTimer = params.getInt("incapTimer");
if(incapTimer != messageTimer)
{
return SCRIPT_CONTINUE;
}
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(!isIdValid(master))
{
return SCRIPT_CONTINUE;
}
if(isIncapacitated(self))
{
blog("handleBeastIncappedDecay() destroying self from incapacitation.");
destroyObject(self);
}
return SCRIPT_CONTINUE;
}
trigger OnRecapacitated()
{
beast_lib.beastFollowTarget(self, getMaster(self));
return SCRIPT_CONTINUE;
}
trigger OnObjectMenuRequest(obj_id player, menu_info mi)
{
if(!isIdValid(player))
{
return SCRIPT_CONTINUE;
}
if(isDead(self) || ai_lib.aiIsDead(player))
{
return SCRIPT_CONTINUE;
}
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
if(player != getMaster(self))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self))
{
return SCRIPT_CONTINUE;
}
int opt_menu = 0;
if(beast_lib.isBeastMaster(player))
{
if(!beast_lib.getBeastCanLevel(self))
{
mi.addRootMenu(menu_info_types.SERVER_MENU1, new string_id(MENU_FILE, "exp_on"));
}
else
{
mi.addRootMenu(menu_info_types.SERVER_MENU1, new string_id(MENU_FILE, "exp_off"));
}
}
obj_id bcd = beast_lib.getBeastBCD(self);
if(!beast_lib.hasBeastName(self) || (hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_IDENTIFIER) && !hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_RENAMED)))
{
mi.addRootMenu(menu_info_types.SERVER_MENU2, new string_id("beast", "name_beast"));
}
if(!ai_lib.isInCombat(player))
{
mi.addRootMenu(menu_info_types.PET_STORE, new string_id(MENU_FILE, "menu_store"));
}
if(ai_lib.isAiDead(self))
{
return SCRIPT_CONTINUE;
}
if(hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_IDENTIFIER) && !hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_REHUED))
{
mi.addRootMenu(menu_info_types.SERVER_MENU3, PCOLOR);
}
return SCRIPT_CONTINUE;
}
trigger OnObjectMenuSelect(obj_id player, int item)
{
if(!isIdValid(player))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.aiIsDead(player))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self))
{
return SCRIPT_CONTINUE;
}
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
if(player != getMaster(self))
{
return SCRIPT_CONTINUE;
}
obj_id bcd = beast_lib.getBeastBCD(self);
if(!isIdValid(bcd) || !exists(bcd))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isAiDead(self))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(player) || ai_lib.isAiDead(player))
{
return SCRIPT_CONTINUE;
}
if(item == menu_info_types.SERVER_MENU1)
{
boolean canLevel = !beast_lib.getBeastCanLevel(self);
beast_lib.setBeastCanLevel(self, canLevel);
beast_lib.setBCDBeastCanLevel(bcd, canLevel);
sendDirtyObjectMenuNotification(self);
return SCRIPT_CONTINUE;
}
if(item == menu_info_types.SERVER_MENU2)
{
if(!beast_lib.hasBeastName(self) || (hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_IDENTIFIER) && !hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_RENAMED)))
{
sui.inputbox(self, player, "@beast:name_d", sui.OK_CANCEL, "@beast:name_t", sui.INPUT_NORMAL, null, "handleSetBeastName", null);
}
return SCRIPT_CONTINUE;
}
if(item == menu_info_types.PET_STORE)
{
if(callable.hasCallable(player, callable.CALLABLE_TYPE_COMBAT_PET))
{
obj_id currentBeast = callable.getCallable(player, callable.CALLABLE_TYPE_COMBAT_PET);
if(ai_lib.isInCombat(currentBeast))
{
sendSystemMessage(player, new string_id("beast", "beast_cant_store"));
return SCRIPT_CONTINUE;
}
}
beast_lib.storeBeast(bcd);
return SCRIPT_CONTINUE;
}
if(item == menu_info_types.PET_COMMAND)
{
pet_lib.openLearnCommandSui(self, player);
}
if(item == menu_info_types.SERVER_MENU3)
{
if(hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_IDENTIFIER) && !hasObjVar(bcd, beast_lib.OBJVAR_OLD_PET_REHUED))
{
sui.colorize(self, player, self, hue.INDEX_1, "handlePrimaryColorize");
}
}
return SCRIPT_CONTINUE;
}
messageHandler handlePrimaryColorize()
{
obj_id beast = self;
if (!isValidId(beast))
{
return SCRIPT_CONTINUE;
}
obj_id bcd = beast_lib.getBeastBCD(beast);
if(!isValidId(bcd))
{
return SCRIPT_CONTINUE;
}
int idx = sui.getColorPickerIndex(params);
int bp = sui.getIntButtonPressed(params);
if ( bp == sui.BP_CANCEL )
{
return SCRIPT_CONTINUE;
}
if ( idx > -1 )
{
setObjVar(bcd, beast_lib.OBJVAR_BEAST_HUE, idx);
hue.setColor(beast, "/private/index_color_1", idx);
setObjVar(bcd, beast_lib.OBJVAR_OLD_PET_REHUED, 1);
}
return SCRIPT_CONTINUE;
}
messageHandler handleSetBeastName()
{
obj_id player = sui.getPlayerId(params);
string beastName = sui.getInputBoxText(params);
int btn = sui.getIntButtonPressed(params);
if(btn == sui.BP_CANCEL)
{
return SCRIPT_CONTINUE;
}
// No zero length or obscene names.
if(beastName == "" || isNameReserved(beastName))
{
// Ask them about the name.
sendSystemMessage(player, new string_id("player_structure", "obscene"));
sui.inputbox(self, player, "@beast:name_d", sui.OK_CANCEL, "@beast:name_t", sui.INPUT_NORMAL, null, "handleSetBeastName", null);
return SCRIPT_CONTINUE;
}
beastName = beastName.trim();
if(beastName.length() < 3)
{
sendSystemMessage(player, new string_id("beast", "name_too_short"));
sui.inputbox(self, player, "@beast:name_d", sui.OK_CANCEL, "@beast:name_t", sui.INPUT_NORMAL, null, "handleSetBeastName", null);
return SCRIPT_CONTINUE;
}
if(beastName.length() > 40)
{
sendSystemMessage(player, new string_id("beast", "name_too_long"));
sui.inputbox(self, player, "@beast:name_d", sui.OK_CANCEL, "@beast:name_t", sui.INPUT_NORMAL, null, "handleSetBeastName", null);
return SCRIPT_CONTINUE;
}
sendDirtyObjectMenuNotification(self);
beast_lib.setBCDBeastName(beast_lib.getBeastBCD(self), beastName);
beast_lib.setBeastName(self, beastName);
if(hasObjVar(beast_lib.getBeastBCD(self), beast_lib.OBJVAR_OLD_PET_IDENTIFIER))
{
setObjVar(beast_lib.getBeastBCD(self), beast_lib.OBJVAR_OLD_PET_RENAMED, 1);
}
return SCRIPT_CONTINUE;
}
messageHandler handleGroupInvite()
{
return SCRIPT_CONTINUE;
}
messageHandler postCombatPathHome()
{
LOGC(aiLoggingEnabled(self), "debug_ai", "pet::postCombatPathHome() self(" + self + ":" + getName(self) + ") master(" + getMaster(self) + ":" + getName(getMaster(self)) + ")");
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self))
{
return SCRIPT_CONTINUE;
}
if(utils.hasScriptVar(self, "ai.pet.staying"))
{
return SCRIPT_CONTINUE;
}
obj_id guardTarget = utils.getObjIdScriptVar(self, "ai.pet.guarding");
if(isIdValid(guardTarget))
{
pet_lib.petFollow(self, guardTarget);
}
else if(hasObjVar(self, "ai.inFormation"))
{
ai_lib.resumeFormationFollowing(self);
}
else
{
obj_id master = getMaster(self);
if(isIdValid(master) && master.isLoaded())
{
pet_lib.petFollow(self, master);
}
else
{
stop(self);
}
}
return SCRIPT_OVERRIDE;
}
messageHandler handleAbandonment()
{
int ignoredForDays = getIntObjVar(self, "ai.pet.ignoredForDays");
ignoredForDays++;
if(ignoredForDays > 3)
{
pet_lib.releasePet(self);
return SCRIPT_CONTINUE;
}
else
{
setObjVar(self, "ai.pet.ignoredForDays", ignoredForDays);
}
messageTo(self, "handleAbandonment", null, 86400, false);
return SCRIPT_CONTINUE;
}
messageHandler resumePetTrick()
{
obj_id master = params.getObjId("master");
int trickNum = params.getInt("trickNum");
pet_lib.doStupidPetTrick(self, master, trickNum);
return SCRIPT_CONTINUE;
}
trigger OnGiveItem(obj_id item, obj_id player)
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(!isIdValid(master))
{
return SCRIPT_CONTINUE;
}
// Check to see if this is pet food.
int pet_food = getIntObjVar(item, "race_restriction");
if(pet_lib.isCreaturePet(self) && (pet_food == -2) && (player == master))
{
food.petEatFood(self, master, item, player);
return SCRIPT_CONTINUE;
}
obj_id petControlDevice = callable.getCallableCD(self);
obj_id petInv = utils.getInventoryContainer(petControlDevice);
if(!isIdValid(petInv))
{
return SCRIPT_CONTINUE;
}
if(player != master)
{
return SCRIPT_CONTINUE;
}
if(isMob(item))
{
return SCRIPT_CONTINUE;
}
else if(!ai_lib.isNpc(self) || !ai_lib.isAndroid(self))
{
return SCRIPT_CONTINUE;
}
putIn(item, petInv);
return SCRIPT_CONTINUE;
}
messageHandler handleMoveToMaster()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(isIdValid(master))
{
location targetLocation = new location(getLocation(master));
targetLocation.x += rand(-4.0f, +4.0f);
targetLocation.z += rand(-4.0f, +4.0f);
targetLocation.y = getHeightAtLocation(targetLocation.x, targetLocation.z);
setLocation(self, getLocation(master));
pathTo(self, targetLocation);
}
return SCRIPT_CONTINUE;
}
/*******************************************/
messageHandler handlePackRequest()
{
blog("handlePackRequest() destroying beast.");
if(!destroyObject(self))
{
debugServerConsoleMsg(null, "+++ beast.messageHandler handlePackRequest +++ WARNINGWARNING - FAILED TO DESTROY SELF");
}
return SCRIPT_CONTINUE;
}
/*******************************************/
messageHandler handleAddToStomach()
{
int stomach = params.getInt("stomach");
int vol = params.getInt("vol");
player_stomach.addToStomach(self, stomach, vol);
return SCRIPT_CONTINUE;
}
messageHandler handleSetColors()
{
if(params == null || params.isEmpty())
{
return SCRIPT_CONTINUE;
}
setColors(self, params);
return SCRIPT_CONTINUE;
}
boolean setColors(obj_id self, dictionary params)
{
if(params == null || params.isEmpty())
{
return false;
}
boolean litmus = true;
java.util.Enumeration keys = params.keys();
while (keys.hasMoreElements())
{
string var = (string)keys.nextElement();
int idx = params.getInt(var);
litmus &= hue.setColor(self, var, idx);
}
return litmus;
}
trigger OnEnteredCombat()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
obj_id master = getMaster(self);
if(isIdValid(master) && master.isLoaded())
{
aiSetHomeLocation(self, getLocation(master));
}
else
{
aiSetHomeLocation(self, getLocation(self));
}
return SCRIPT_CONTINUE;
}
trigger OnDefenderCombatAction(obj_id attacker, obj_id weapon, int combatResult)
{
//check to see if beast is passive
if(!beast_lib.getBeastDefensive(self))
{
//if so, dont do anything. This will fix AOE attacks causing a beast to attack in passive.
return SCRIPT_OVERRIDE;
}
if(utils.hasScriptVar(self, "petIgnoreAttacks"))
{
int timeStarted = utils.getIntScriptVar(self, "petIgnoreAttacks");
blog("OnDefenderCombatAction() beast hasScriptVar petIgnoreAttacks timeStarted: " + timeStarted + " getGameTime: " + getGameTime());
if(getGameTime() < (timeStarted + 20))
{
//not time to stop ignoring combat just yet:
return SCRIPT_OVERRIDE;
}
utils.removeScriptVar(self, "petIgnoreAttacks");
}
return SCRIPT_CONTINUE;
}
//onSawAttack never fires for beasts, yet we have this here because Eidson swears it worked at one point
//Travis thinks it never worked
trigger OnSawAttack(obj_id defender, obj_id[] attackers)
{
if(beast_lib.isBeast(self) && beast_lib.getBeastDefensive(self))
{
for (int i = 0; i < attackers.length; ++i)
{
const obj_id attacker = attackers[i];
if(isIdValid(attacker) && !ai_lib.isInCombat(self) && defender == getMaster(self))
{
addHate(self, attacker, 0.0f);
}
}
}
return SCRIPT_CONTINUE;
}
trigger OnExitedCombat()
{
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
utils.setScriptVar(self, "beast.combatEnded", getGameTime());
if(!utils.hasScriptVar(self, "ai.pet.staying"))
{
obj_id master = getMaster(self);
if(isIdValid(master) && master.isLoaded())
{
beast_lib.beastFollowTarget(self, master);
// If the master is in combat and the beast is defensive, then ping 1 sec later due to race conditions.
if(beast_lib.getBeastDefensive(self))
{
messageTo(self, "defensiveModePing", null, 1.0f, false);
}
if(!beast_lib.getBeastDefensive(self) && !utils.hasScriptVar(self, "petIgnoreAttacks"))
utils.setScriptVar(self, "petIgnoreAttacks", getGameTime());
}
else
{
aiSetHomeLocation(self, getLocation(self));
}
}
else
{
obj_id master = getMaster(self);
if(isIdValid(master) && master.isLoaded())
{
beast_lib.doStayCommand(self, master);
}
else // My master is missing
{
ai_lib.aiStopFollowing(self);
location myLocation = getLocation(self);
setHomeLocation(self, myLocation);
setMovementWalk(self);
ai_lib.setDefaultCalmBehavior(self, ai_lib.BEHAVIOR_STOP);
}
}
return SCRIPT_CONTINUE;
}
trigger OnSawEmote(obj_id performer, string emote)
{
obj_id master = getMaster(self);
if(performer != master)
{
return SCRIPT_CONTINUE;
}
if(!isMob(self))
{
return SCRIPT_CONTINUE;
}
if(!isPlayer(master))
{
return SCRIPT_CONTINUE;
}
if(ai_lib.isInCombat(self) || ai_lib.isInCombat(master) || ai_lib.aiIsDead(self))
{
return SCRIPT_CONTINUE;
}
if(getLookAtTarget(master) != self)
{
if(getIntendedTarget(master) != self)
{
return SCRIPT_CONTINUE;
}
}
if(emote.startsWith("pet") || emote.equals("reassure") || emote.equals("nuzzle") || emote.equals("hug"))
{
if(rand(1,2) == 1)
{
stop(self);
if(ai_lib.canSit(self))
{
if(rand(1,2) == 1)
ai_lib.aiSetPosture(self, POSTURE_SITTING);
else
ai_lib.aiSetPosture(self, POSTURE_LYING_DOWN);
}
else if(ai_lib.canLieDown(self))
{
ai_lib.aiSetPosture(self, POSTURE_LYING_DOWN);
}
}
else
{
ai_lib.doAction(self, "happy");
}
}
else if(emote.equals("bonk") || emote.equals("whap") || emote.equals("scold") || emote.equals("bad") || emote.equals("slap"))
{
ai_lib.doAction(self, "ashamed");
}
else if(emote.equals("pointat") || emote.equals("tap"))
{
ai_lib.doAction(self, ai_lib.ACTION_ALERT);
}
else if(emote.equals("beckon") || emote.equals("summon"))
{
if(rand(1,2)==1)
{
ai_lib.doAction(self, "confused");
}
else
{
beast_lib.doFollowCommand(self, master);
}
}
return SCRIPT_CONTINUE;
}
/********* see systems.combat.credit_for_kills ***********/
trigger OnDeath(obj_id killer, obj_id corpseId)
{
obj_id master = getMaster(self);
CustomerServiceLog("Beasts","*Beast DEATH: (" + self + ": " + getName(self) + ") has died. Their master was, (" + master + " sid:"+getPlayerObject(master));
return SCRIPT_CONTINUE;
}
messageHandler receiveCreditForKill()
{
obj_id master = getMaster(self);
if(isIdValid(master))
{
string creatureName = params.getString("creatureName");
params.put("col_faction", dataTableGetString("datatables/mob/creatures.iff", creatureName, "col_faction"));
params.put("col_faction", dataTableGetInt("datatables/mob/creatures.iff", creatureName, "difficultyClass"));
messageTo(master, "receiveCreditForKill", params, 0.0f, false);
}
return SCRIPT_CONTINUE;
}
messageHandler handleHealOverTimeTick()
{
float duration = params.getFloat("duration");
float tick = params.getFloat("tick");
int heal = params.getInt("heal");
int hot_id = params.getInt("id");
int isCombat = params.getInt("combat");
obj_id medic = params.getObjId("medic");
if(isDead(self))
{
if(buff.hasBuff(self, "healOverTime"))
buff.removeBuff(self, "healOverTime");
return SCRIPT_CONTINUE;
}
if(!utils.hasScriptVar(self, healing.VAR_PLAYER_HOT_ID))
{
if(buff.hasBuff(self, "healOverTime"))
buff.removeBuff(self, "healOverTime");
return SCRIPT_CONTINUE;
}
int player_id = utils.getIntScriptVar(self, healing.VAR_PLAYER_HOT_ID);
if(hot_id != player_id)
return SCRIPT_CONTINUE;
if(!isIncapacitated(self))
{
int delta = healing.healDamage(medic, self, HEALTH, heal);
if(isCombat == 1 && isIdValid(medic))
{
prose_package pp = new prose_package();
pp = prose.setStringId(pp, new string_id("healing", "heal_fly"));
pp = prose.setDI(pp, delta);
pp = prose.setTO(pp, "HEALTH");
showFlyTextPrivateProseWithFlags(self, self, pp, 2.0f, colors.SEAGREEN, FLY_TEXT_FLAG_IS_HEAL);
if(medic != self)
showFlyTextPrivateProseWithFlags(self, medic, pp, 2.0f, colors.SEAGREEN, FLY_TEXT_FLAG_IS_HEAL);
pp = prose.setStringId(pp, healing.SID_PERFORM_HEAL_DAMAGE_SUCCESS);
pp = prose.setTT(pp, medic);
pp = prose.setTO(pp, self);
pp = prose.setDI(pp, delta);
healing.sendMedicalSpam(medic, self, pp, true, true, true, COMBAT_RESULT_MEDICAL);
delta = (int)(delta*healing.HEAL_OVER_TIME_AGGRO_REDUCER);
healing._addMedicalHate(medic, self, delta);
}
}
duration -= tick;
if(duration <= 0.0f)
{
if(buff.hasBuff(self, "healOverTime"))
buff.removeBuff(self, "healOverTime");
return SCRIPT_CONTINUE;
}
dictionary d = new dictionary();
d.put("duration", duration);
d.put("tick", tick);
d.put("heal", heal);
d.put("id", hot_id);
d.put("combat", isCombat);
d.put("medic", medic);
if(tick >= 0)
messageTo(self, healing.MSG_HEAL_OVER_TIME, d, tick, false);
return SCRIPT_CONTINUE;
}
//this resolves when combat ends with beast and beast is in defensive mode and master is still in combat
messageHandler defensiveModePing()
{
obj_id master = getMaster(self);
if(isIdValid(master) && ai_lib.isInCombat(master) )
{
obj_id[] masterAttackers = getHateList(master);
for(int i = 0; i < masterAttackers.length; i++)
{
if(getHateTarget(masterAttackers[i]) == master)
{
utils.removeScriptVar(self, "petIgnoreAttacks");
startCombat(self, masterAttackers[i]);
addHate(self, masterAttackers[i], 0.0f);
utils.setScriptVar(self, "ai.combat.target", masterAttackers[i]);
break;
}
}
}
return SCRIPT_CONTINUE;
}
messageHandler doBeastFlourish()
{
obj_id beast = (self);
int beastTrickNum = rand(1, 2);
doAnimationAction(beast, "trick_" + beastTrickNum);
return SCRIPT_CONTINUE;
}