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; }