mirror of
https://bitbucket.org/seefoe/dsrc.git
synced 2026-07-31 01:15:55 -04:00
You can now rename your beast as much as you would like. Police officers will no longer harass corpses. Static NPC's conversing will now successfully converse with one another. When a player dies, the pet will go invulnerable and be immediately packed up. When respecing professions, your weapon will now be unequiped. Fixed the chat system for some NPC's. You can now only mount your vehicle from less than 5 meters. Quest items that can be clicked on cannot be used from less than 5 meters. Added a message to the QaSetup script, notifying the QA that they have received their items.
1563 lines
34 KiB
Plaintext
1563 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(!ai_lib.isInCombat(player) && !ai_lib.isAiDead(self) && !ai_lib.aiIsDead(player))
|
|
{
|
|
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(!ai_lib.isInCombat(player) && !ai_lib.isAiDead(self) && !ai_lib.aiIsDead(player))
|
|
{
|
|
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 Development swears it worked at one point
|
|
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;
|
|
}
|