mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-31 00:15:54 -04:00
7116 lines
187 KiB
Plaintext
7116 lines
187 KiB
Plaintext
include ai.ai_combat;
|
|
include java.util.Arrays;
|
|
include java.util.Vector;
|
|
include library.ai_lib;
|
|
include library.beast_lib;
|
|
include library.buff;
|
|
include library.callable;
|
|
include library.camping;
|
|
include library.chat;
|
|
include library.colors;
|
|
include library.consumable;
|
|
include library.create;
|
|
include library.group;
|
|
include library.locations;
|
|
include library.pclib;
|
|
include library.player_structure;
|
|
include library.space_dungeon;
|
|
include library.space_transition;
|
|
include library.stealth;
|
|
include library.utils;
|
|
|
|
const int CUSTOMIZATION_COUNT = 2000;
|
|
|
|
const int MAX_NUMBER_OF_FRIENDS = 5;
|
|
const int MAX_WAYPOINTS = 10;
|
|
|
|
const int COMMAND_FOLLOW = 0;
|
|
const int COMMAND_STAY = 1;
|
|
const int COMMAND_GUARD = 2;
|
|
const int COMMAND_FRIEND = 3;
|
|
const int COMMAND_ATTACK = 4;
|
|
const int COMMAND_PATROL = 5;
|
|
const int COMMAND_SET_PATROL_POINT = 6;
|
|
const int COMMAND_CLEAR_PATROL_POINTS = 7;
|
|
const int COMMAND_ASSUME_FORMATION_1 = 8;
|
|
const int COMMAND_ASSUME_FORMATION_2 = 9;
|
|
const int COMMAND_TRANSFER = 10;
|
|
const int COMMAND_RELEASE = 11;
|
|
const int COMMAND_TRICK_1 = 12;
|
|
const int COMMAND_TRICK_2 = 13;
|
|
const int COMMAND_TRICK_3 = 14;
|
|
const int COMMAND_TRICK_4 = 15;
|
|
const int COMMAND_GROUP = 16;
|
|
const int COMMAND_FOLLOW_OTHER = 17;
|
|
|
|
const int COMMAND_SPECIALATTACK_ONE = 18;
|
|
const int COMMAND_SPECIALATTACK_TWO = 19;
|
|
const int COMMAND_RANGED_ATTACK = 20;
|
|
const int COMMAND_TARGET_HARVEST = 21;
|
|
const int COMMAND_TRAP_THROW_ONE = 22;
|
|
|
|
const int NUM_COMMANDS = 23; //must be last
|
|
|
|
const int PET_TYPE_NON_AGGRO = 0;
|
|
const int PET_TYPE_AGGRO = 1;
|
|
const int PET_TYPE_NPC = 2;
|
|
const int PET_TYPE_DROID = 3;
|
|
const int PET_TYPE_FAMILIAR = 4;
|
|
const int PET_TYPE_MOUNT = 5;
|
|
|
|
const int MAX_NPC_PETS = 1;
|
|
const int MAX_DROID_PETS = 1;
|
|
const int MAX_FAMILIAR_PETS = 1;
|
|
const int MAX_MOUNT_PETS = 1;
|
|
|
|
const int MAX_UNTRAINED_PETS = 1;
|
|
const int MAX_UNTRAINED_STORED_PETS = 2;
|
|
|
|
const int MAX_NONCH_PET_LEVEL = 10;
|
|
|
|
const int MAX_REGEN_STAT = 4000;
|
|
|
|
const int MAX_PET_LEVELS = 70;
|
|
const int MAX_PET_LEVELS_ABOVE_CALLER = 5;
|
|
|
|
const int MAX_PET_MOUNT_DISTANCE = 4;
|
|
const int MAX_PET_MOUNT_OFFER_DISTANCE = 32;
|
|
|
|
const int MAX_VITALITY_LOSS = 100;
|
|
|
|
const int COMBATMODE_NONE = 0;
|
|
const int COMBATMODE_DEFAULT = 1;
|
|
const int COMBATMODE_EQUIPPED = 2;
|
|
const int COMBATMODE_SPECIAL = 3;
|
|
|
|
const int PET_ABILITY_INNATE = 1;
|
|
const int PET_ABILITY_TRIGGERED = 2;
|
|
const int PET_ABILITY_ACTION = 3;
|
|
|
|
const float XP_CURVE_STEEPNESS = 2.2f;
|
|
const float XP_LOW_END_REWARD = 100.0f;
|
|
|
|
const string BATTERY = "object/tangible/droid_battery/battery.iff";
|
|
const int OUT_OF_POWER = 100; //make it lower for droids to eat more batteries
|
|
// (run out of power sooner)
|
|
|
|
const float PET_GROWTH_CYCLE = 43200.0f;//12 hours
|
|
|
|
const string CREATURE_TABLE = "datatables/mob/creatures.iff";
|
|
const string PET_ABILITY_TABLE = "datatables/pet/pet_ability.iff";
|
|
const string WILD_ABILITY_TABLE = "datatables/pet/wild_ability.iff";
|
|
|
|
const string MENU_FILE = "pet/pet_menu";
|
|
|
|
const int CALL_DELAY = 0; // this delay cant be set to 0 or multiple creatures can be called,force it to at least a frame later to prevent exploits
|
|
const int BUTTON_MASHING_DELAY = 3;
|
|
const int POST_COMBAT_DELAY = 30;
|
|
const string COMBAT_ENDED = "player.combatEnded";
|
|
const string CALL_FINISH = "player.callFinish";
|
|
|
|
const int BASE_MAX_TRAP_LOAD = 20;
|
|
|
|
const string VAR_PALVAR_BASE = "ai.pet.palvar";
|
|
const string VAR_PALVAR_VARS = VAR_PALVAR_BASE + ".vars";
|
|
const string VAR_PALVAR_CNT = VAR_PALVAR_BASE + ".cnt";
|
|
|
|
const string[] LIGHTING_EFFECTS = {"droid_effect_foam", "droid_effect_electric_fog", "droid_effect_confetti", "droid_effect_mind_bloom", "droid_effect_jawa_dance", "droid_effect_doves"};
|
|
|
|
const string_id SID_SYS_CANT_TAME = new string_id("pet/pet_menu","sys_cant_tame");
|
|
const string_id SID_SYS_LACK_SKILL = new string_id("pet/pet_menu", "sys_lack_skill");
|
|
const string_id SID_MAX_PETS = new string_id("error_message", "too_many_pets");
|
|
const string_id SID_PET_DIED_MASTER = new string_id("error_message", "pet_died_master");
|
|
const string_id SID_PET_DIED_OTHER = new string_id("error_message", "pet_died_other");
|
|
|
|
const string_id SID_SYS_CANT_TRICK = new string_id("pet/pet_menu","sys_cant_trick");
|
|
const string_id SID_SYS_LEARN_COMMAND = new string_id("pet/pet_menu","pet_learn");
|
|
const string_id SID_SYS_CANT_LEARN = new string_id("pet/pet_menu","pet_nolearn");
|
|
const string_id SID_SYS_NOT_HANDLER = new string_id("pet/pet_menu","pet_nothandler");
|
|
const string_id SID_SYS_XFER_SUCCESS = new string_id("pet/pet_menu","pet_transfer_succeed");
|
|
const string_id SID_SYS_TOO_MANY_PETS = new string_id("pet/pet_menu","pet_too_many_pets");
|
|
const string_id SID_SYS_TOO_MANY_STORED_PETS = new string_id("pet/pet_menu","sys_too_many_stored");
|
|
const string_id SID_PRIVATE_HOUSE = new string_id("pet/pet_menu","private_house");
|
|
const string_id SID_INVALID_CRAFTED_PET = new string_id("pet/pet_menu", "invalid_crafted_pet");
|
|
|
|
const string_id SID_SYS_PATROL_ADDED = new string_id("pet/pet_menu","patrol_added");
|
|
const string_id SID_SYS_PATROL_REMOVED = new string_id("pet/pet_menu","patrol_removed");
|
|
const string_id SID_SYS_CANT_CALL = new string_id("pet/pet_menu","cant_call");
|
|
const string_id SID_SYS_CANT_CALL_YET = new string_id("pet/pet_menu","prose_cant_call_yet");
|
|
const string_id SID_SYS_CANT_CALL_LEVEL = new string_id("pet/pet_menu", "cant_call_level");
|
|
const string_id SID_SYS_CANT_GENERATE_YET = new string_id("pet/pet_menu","prose_cant_generate_yet");
|
|
const string_id SID_SYS_CALL_DELAY_FINISH_VEHICLE = new string_id("pet/pet_menu","call_delay_finish_vehicle");
|
|
const string_id SID_SYS_CALL_DELAY_FINISH_PET = new string_id("pet/pet_menu","call_delay_finish_pet");
|
|
const string_id MOUNTED_CALL_WARNING = new string_id("pet/pet_menu","mounted_call_warning");
|
|
const string_id SID_SYS_CANT_CALL_ANOTHER_RIDEABLE = new string_id("pet/pet_menu", "cannot_call_another_rideable");
|
|
const string_id SID_SYS_CANT_CALL_ANOTHER_FAMILIAR = new string_id("pet/pet_menu", "cannot_call_another_familiar");
|
|
const string_id SID_SYS_PACKED_DROID = new string_id("pet/pet_menu","droid_packed");
|
|
|
|
const string_id SID_SYS_CANT_CALL_VEHICLE = new string_id("pet/pet_menu","cant_call_vehicle");
|
|
const string_id SID_SYS_VEHICLE_RESTRICTED_SCENE = new string_id("pet/pet_menu", "vehicle_restricted_scene");
|
|
const string_id SID_SYS_MOUNT_RESTRICTED_SCENE = new string_id("pet/pet_menu", "mount_restricted_scene");
|
|
const string_id SID_SYS_CALL_VEHICLE_DELAY = new string_id("pet/pet_menu","call_vehicle_delay");
|
|
const string_id SID_SYS_CALL_PET_DELAY = new string_id("pet/pet_menu","call_pet_delay");
|
|
const string_id SID_SYS_CALL_MAX_DATAPAD = new string_id("pet/pet_menu","max_npc_in_datapad");
|
|
|
|
const string_id SID_PET_HAS_NOTRADE = new string_id("pet/pet_menu", "pet_has_notrade");
|
|
|
|
const string_id SID_SYS_CANT_MOUNT = new string_id("pet/pet_menu","cant_mount");
|
|
const string_id SID_SYS_CANT_DISMOUNT = new string_id("pet/pet_menu","cant_dismount");
|
|
|
|
const string PET_CTRL_DEVICE_TEMPLATE = "object/intangible/pet/pet_control.iff";
|
|
|
|
const string_id SID_PET_INVITED = new string_id("pet/pet_menu","pet_invited");
|
|
const string_id SID_PET_NO_GROUP_IN_SPACE = new string_id("pet/pet_menu","pet_no_group_in_space");
|
|
|
|
const string_id SID_GROWTH_CHOICE_TITLE = new string_id("pet/pet_menu", "mount_growth_title");
|
|
const string_id SID_GROWTH_CHOICE_PROMPT = new string_id("pet/pet_menu", "mount_growth_prompt");
|
|
|
|
const string_id SID_SYS_CANT_MOUNT_VEHICLE_DISABLED = new string_id("pet/pet_menu","cant_mount_veh_disabled");
|
|
const string_id SID_SYS_VEHICLE_DISABLED = new string_id("pet/pet_menu","veh_disabled");
|
|
|
|
const string TEMPLATE_PREFIX = "object/mobile/";
|
|
const string CREATURE_NAME_FILE = "mob/creature_names";
|
|
const string NPC_CUSTOMIZATION_PREFIX = "datatables/npc_customization/";
|
|
const string DRESSED_NPC_TABLE = "datatables/npc_customization/dressed_species.iff";
|
|
const string MOUNT_VALID_SCALE_RANGE = "datatables/mount/valid_scale_range.iff";
|
|
|
|
const string_id SID_SYS_CUST_FADING = new string_id("pet/pet_menu","customization_fading");
|
|
const string_id SID_SYS_CUST_GONE = new string_id("pet/pet_menu","customization_gone");
|
|
const string_id SID_SYS_VEH_CUST_FADING = new string_id("pet/pet_menu","customization_fading_veh");
|
|
const string_id SID_SYS_VEH_CUST_GONE = new string_id("pet/pet_menu","customization_gone_veh");
|
|
const string_id SID_MOUNT_STATUS_CODE_ERROR = new string_id("pet/pet_menu","mount_status_code_error");
|
|
|
|
const string DROID_COMBAT_TABLE = "datatables/combat/droid_combat_capabilities.iff";
|
|
|
|
//The MaxDroidCombatValue is basically equal to 110x the number of combat modules a normal module-based droid can be crafted with.
|
|
const int MAX_DROID_COMBAT_VALUE = 880;
|
|
const string PROBOT_DROID_NAME = "probot";
|
|
const string DZ70_DROID_NAME = "dz70_fugitive_tracker_droid";
|
|
const string_id SID_SYS_RANGED_ATTACK_TOGGLED = new string_id("pet/pet_menu","ranged_attack_toggled");
|
|
const string_id SID_SYS_RANGED_ATTACK_TOGGLED_OFF = new string_id("pet/pet_menu","ranged_attack_toggled_off");
|
|
|
|
//Constants for use by the post Combat Upgrade Combat Droid changes. - RcT - 5/2005
|
|
const int DEFAULT_DROID_LEVEL = 1;
|
|
const string TBL_MOB_STAT_BALANCE = "datatables/mob/stat_balance.iff";
|
|
const string TBL_DROID_COMBAT = "datatables/combat/droid_combat_capabilities.iff";
|
|
const string DROID_RANGED_WEAPON = "object/weapon/ranged/vehicle/droid_weapon.iff";
|
|
const int COMBAT_MODULE_PROTECT_LVL = 500;
|
|
const int DEFENSE_MODULE_PROTECT_LVL = 1000;
|
|
|
|
//For use with bomb droids
|
|
const int DETONATION_DROID_MIN_DAMAGE = 65;
|
|
const int DETONATION_DROID_MAX_DAMAGE = 97;
|
|
|
|
const string OBJVAR_PET_PATROL_POINTS = "pet.patrolPoints";
|
|
|
|
const string DROID_HARVEST_ARRAY = "droid.toHarvest";
|
|
|
|
const string[] MOUNTABILITY_STATUS_CODES =
|
|
{
|
|
"MSC_CREATURE_MOUNTABLE",
|
|
"MSC_SPECIES_UNMOUNTABLE",
|
|
"MSC_SPECIES_MOUNTABLE_SEATING_CAPACITY_UNSUPPORTED",
|
|
"MSC_SPECIES_MOUNTABLE_SCALE_OUT_OF_RANGE",
|
|
"MSC_SPECIES_MOUNTABLE_MISSING_RIDER_SLOT"
|
|
};
|
|
|
|
const int HEALTH_DAMAGE = 0;
|
|
const int ACTION_DAMAGE = 1;
|
|
const int MIND_DAMAGE = 2;
|
|
|
|
const int HEALTH_WOUND = 3;
|
|
const int ACTION_WOUND = 4;//deprecated!
|
|
const int MIND_WOUND = 5;//deprecated!
|
|
|
|
|
|
const string FAMILAR_DATA_DATATABLE = "datatables/pet/non_combat_familiar.iff";
|
|
|
|
|
|
void petFollow(obj_id pet, obj_id master)
|
|
{
|
|
const float collisionRadius = getObjectCollisionRadius(pet) * 2.0f;
|
|
ai_lib.aiFollow(pet, master, collisionRadius, collisionRadius);
|
|
setMovementRun(pet);
|
|
}
|
|
|
|
void setUpPetControlDevice(obj_id petControlDevice, obj_id pet)
|
|
{
|
|
setObjVar(petControlDevice, "pet.creatureName", ai_lib.getCreatureName(pet));
|
|
|
|
callable.setCDCallable(petControlDevice, pet);
|
|
callable.setCallableCD(pet, petControlDevice);
|
|
|
|
attachScript(petControlDevice, "ai.pet_control_device");
|
|
|
|
setControllerName(petControlDevice, pet);
|
|
|
|
int petType = getPetType(pet);
|
|
setObjVar(petControlDevice, "ai.pet.type", petType);
|
|
|
|
if(couldPetBeMadeMountable(pet) == MSC_SPECIES_UNMOUNTABLE)
|
|
{
|
|
setObjVar(petControlDevice, "ai.petAdvance.cannotBeMount", true);
|
|
}
|
|
|
|
markExpansionPets(petControlDevice, pet);
|
|
}
|
|
|
|
obj_id makeControlDevice(obj_id master, obj_id pet)
|
|
{
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
return callable.getCallableCD(pet);
|
|
}
|
|
|
|
//debugSpeakMsg(master, "setting up the pcd");
|
|
obj_id datapad = utils.getPlayerDatapad(master);
|
|
|
|
if(!isIdValid(datapad))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string creatureName = ai_lib.getCreatureName(pet);
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string controlTemplate = "object/intangible/pet/" + utils.dataTableGetString(create.CREATURE_TABLE, creatureName, "template");
|
|
|
|
if(!controlTemplate.endsWith(".iff"))
|
|
{
|
|
controlTemplate = pet_lib.PET_CTRL_DEVICE_TEMPLATE;
|
|
}
|
|
|
|
obj_id petControlDevice = createObject(controlTemplate, datapad, "");
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
{
|
|
petControlDevice = createObject(pet_lib.PET_CTRL_DEVICE_TEMPLATE, datapad, "");
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
{
|
|
sendSystemMessage(master, pet_lib.SID_SYS_TOO_MANY_STORED_PETS);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
setUpPetControlDevice(petControlDevice, pet);
|
|
|
|
if(hasObjVar(pet, "ai.petAbility.wildAbility"))
|
|
{
|
|
string wildAbility = getStringObjVar(pet, "ai.petAbility.wildAbility");
|
|
|
|
int[] abilityList = new int[1];
|
|
abilityList[0] = getStringCrc(wildAbility.toLowerCase());;
|
|
|
|
setObjVar(petControlDevice, "ai.petAbility.abilityList", abilityList);
|
|
setObjVar(petControlDevice, "ai.petAbility.wildAbility", wildAbility);
|
|
}
|
|
|
|
// Reset pet stats
|
|
if(hasObjVar(petControlDevice, "ai.petAdvance.growthStage"))
|
|
{
|
|
pet_lib.setPetStatsByGrowth(pet, getIntObjVar(petControlDevice, "ai.petAdvance.growthStage"));
|
|
}
|
|
else
|
|
{
|
|
pet_lib.setPetStatsByGrowth(pet, 10);
|
|
}
|
|
|
|
callable.setCallableCD(pet, petControlDevice);
|
|
|
|
return petControlDevice;
|
|
}
|
|
|
|
void makePet(obj_id pet, obj_id master)
|
|
{
|
|
if(!isIdValid(pet) || !isIdValid(master) || !exists(pet) || !exists(master))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!hasScript(master, "ai.pet_master"))
|
|
{
|
|
attachScript(master, "ai.pet_master");
|
|
}
|
|
|
|
dictionary params = new dictionary();
|
|
|
|
params.put("pet", pet);
|
|
params.put("master", master);
|
|
|
|
petFollow(pet, master);
|
|
|
|
int petSpecies = ai_lib.aiGetSpecies(pet);
|
|
|
|
if(petSpecies == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string masterFaction = factions.getFaction(master);
|
|
|
|
if(masterFaction != null && (masterFaction == "Rebel" || masterFaction == "Imperial"))
|
|
{
|
|
setObjVar(pet, factions.FACTION, masterFaction);
|
|
}
|
|
|
|
if(!utils.hasScriptVar(pet, "petBeingInitialized") || (callable.hasCallable(master, callable.getCallableType(pet)) &&
|
|
callable.getCallable(master, callable.getCallableType(pet)) != pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id controlDevice = obj_id.NULL_ID;
|
|
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
controlDevice = callable.getCallableCD(pet);
|
|
}
|
|
|
|
int petType = pet_lib.getPetType(pet);
|
|
|
|
setObjVar(pet, "ai.pet.type", petType);
|
|
|
|
if(!pet_lib.hasMaxPets(master, petType, pet) || (pet_lib.hasMaxPets(master, petType) && hasObjVar(controlDevice, "ai.pet.trainedMount")))
|
|
{
|
|
controlDevice = makeControlDevice(master, pet);
|
|
|
|
ai_lib.setDefaultCalmBehavior(pet, ai_lib.BEHAVIOR_STOP);
|
|
|
|
if(!isIdValid(controlDevice) || !exists(controlDevice))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//debugSpeakMsg(self, "adding " + pet + " to list ");
|
|
pet_lib.addToPetList(master, pet);
|
|
params.put("controlDevice", controlDevice);
|
|
|
|
messageTo(pet, "handleAddMaster", params, 0, false);
|
|
}
|
|
else
|
|
{
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
destroyObject(pet); //this thing was created and now it can't be made a pet, so get rid of it
|
|
}
|
|
}
|
|
|
|
if(isIdValid(controlDevice) && hasMountName(controlDevice))
|
|
{
|
|
setName(pet, getMountName(controlDevice));
|
|
}
|
|
}
|
|
|
|
boolean hasMaster(obj_id pet)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet) || !isMob(pet) || isPlayer(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!isIdValid(master) || !exists(master))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void removeFromPetList(obj_id pet)
|
|
{
|
|
if(!isIdValid(pet) || !hasMaster(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id master = getMaster(pet);
|
|
int petSpecies = ai_lib.aiGetSpecies(pet);
|
|
|
|
if(!isValidId(master) || !exists(master) || petSpecies == -1 || callable.getCallable(master, callable.getCallableType(pet)) != pet)
|
|
{
|
|
return;
|
|
}
|
|
|
|
callable.setCallable(master, null, callable.getCallableType(pet));
|
|
|
|
return;
|
|
}
|
|
|
|
void releasePet(obj_id pet)
|
|
{
|
|
dictionary params = new dictionary();
|
|
params.put("pet", pet);
|
|
params.put("species", ai_lib.aiGetSpecies(pet));
|
|
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName != null)
|
|
{
|
|
params.put("ai.creatureName", creatureName);
|
|
}
|
|
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
obj_id controller = callable.getCallableCD(pet);
|
|
|
|
if(isIdValid(controller))
|
|
{
|
|
obj_id currentPet = callable.getCDCallable(controller);
|
|
|
|
if(isIdValid(currentPet) && currentPet == pet)
|
|
{
|
|
savePetInfo(pet, controller);
|
|
setObjVar(controller, "pet.timeStored", getGameTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
messageTo(pet, "handlePackRequest", null, 0, false);
|
|
}
|
|
|
|
int getMaxPets(obj_id master, int petType)
|
|
{
|
|
if(petType == PET_TYPE_NPC)
|
|
{
|
|
return MAX_NPC_PETS;
|
|
}
|
|
else if(petType == PET_TYPE_DROID)
|
|
{
|
|
return MAX_DROID_PETS;
|
|
}
|
|
else if(petType == PET_TYPE_FAMILIAR)
|
|
{
|
|
return MAX_FAMILIAR_PETS;
|
|
}
|
|
else if(petType == PET_TYPE_MOUNT)
|
|
{
|
|
return MAX_MOUNT_PETS;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int countPetsOfType(obj_id master, int petType)
|
|
{
|
|
int petCount = 0;
|
|
|
|
if(hasMaxPets(master, petType))
|
|
{
|
|
petCount++;
|
|
}
|
|
|
|
return petCount;
|
|
}
|
|
|
|
boolean hasMaxPets(obj_id master, int petType)
|
|
{
|
|
return hasMaxPets(master, petType, obj_id.NULL_ID);
|
|
}
|
|
|
|
|
|
boolean hasMaxPets(obj_id master, int petType, obj_id pet)
|
|
{
|
|
int callableType = callable.CALLABLE_TYPE_COMBAT_PET;
|
|
|
|
if(petType == PET_TYPE_FAMILIAR)
|
|
{
|
|
callableType = callable.CALLABLE_TYPE_FAMILIAR;
|
|
}
|
|
else if(petType == PET_TYPE_MOUNT)
|
|
{
|
|
callableType = callable.CALLABLE_TYPE_RIDEABLE;
|
|
}
|
|
|
|
if(!isIdValid(pet))
|
|
{
|
|
if(callable.hasCallable(master, callableType))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(callable.hasCallable(master, callableType, pet))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void initializePetCommandList(obj_id pet, obj_id pcd)
|
|
{
|
|
dictionary petCommandList = new dictionary();
|
|
|
|
obj_var_list commandList = getObjVarList(pcd, "ai.pet.command");
|
|
|
|
if(commandList != null && commandList.getNumItems() > 0)
|
|
{
|
|
int numItems = commandList.getNumItems();
|
|
|
|
for(int i = 0; i < numItems; i++)
|
|
{
|
|
obj_var ov = commandList.getObjVar(i);
|
|
|
|
petCommandList.put(ov.getStringData(), Integer.parseInt(ov.getName()));
|
|
}
|
|
}
|
|
|
|
utils.setScriptVar(pet, "ai.pet.commandList", petCommandList);
|
|
}
|
|
|
|
int getDroidModuleCommandLevel(obj_id player, int modulePotency)
|
|
{
|
|
if(!isIdValid(player) || !isPlayer(player))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
string profession = skill.getProfessionName(getSkillTemplate(player));
|
|
boolean isTrader = false;
|
|
|
|
if(profession.equals("trader"))
|
|
{
|
|
isTrader = true;
|
|
}
|
|
|
|
int playerLevel = getLevel(player);
|
|
|
|
int commandLevel = 0;
|
|
|
|
if(modulePotency >= 100 && isTrader && playerLevel >= 60)
|
|
{
|
|
commandLevel = 3;
|
|
}
|
|
else if(modulePotency >= 50 && isTrader && playerLevel >= 30)
|
|
{
|
|
commandLevel = 2;
|
|
}
|
|
else if(modulePotency > 0) // All players of all levels get a level 1 command
|
|
{
|
|
commandLevel = 1;
|
|
}
|
|
|
|
return commandLevel;
|
|
}
|
|
|
|
void learnCommand(obj_id pet, int commandNum, string text, obj_id master)
|
|
{
|
|
if(utils.isExtendedASCII(text))
|
|
{
|
|
sendSystemMessage(master, new string_id(MENU_FILE, "invalid_command"));
|
|
return;
|
|
}
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
//debugSpeakMsg(pet, "learning commandNum " + commandNum);
|
|
string command = getStringObjVar(petControlDevice, "ai.pet.command."+commandNum);
|
|
string oldCommand = null;
|
|
|
|
// ////debugSpeakMsg(pet, "command " + command);
|
|
if(command == null || command.equals(""))
|
|
{
|
|
if(isCreaturePet(pet))
|
|
{
|
|
// The first time you teach a pet a new command, you have to pass a skill check.
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int petType = pet_lib.getPetType(pet);
|
|
int tamingSkillMod = 0;
|
|
|
|
if(petType == pet_lib.PET_TYPE_AGGRO)
|
|
{
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(master, "tame_aggro");
|
|
}
|
|
else
|
|
{
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(master, "tame_non_aggro");
|
|
}
|
|
|
|
float chance = 0;
|
|
|
|
if(tamingSkillMod > 0)
|
|
{
|
|
// Base chance.
|
|
chance = 50 + tamingSkillMod;
|
|
|
|
// Subtract creature level.
|
|
chance -= getAdultLevel(pet) * 2;
|
|
|
|
if(chance < 20)
|
|
{
|
|
chance = 20;
|
|
}
|
|
}
|
|
|
|
//cap max number of attempts before success at 3 because this sucks
|
|
int attempts = utils.getIntScriptVar(pet, "ai.pet.trainCommand" + commandNum);
|
|
int roll = rand(1,100);
|
|
//////debugSpeakMsg(pet, "learn command " + roll + "/" + chance);
|
|
|
|
if(roll > chance && attempts < 3)
|
|
{
|
|
doConfusedEmote(pet);
|
|
attempts++;
|
|
sendSystemMessage(master, SID_SYS_CANT_LEARN);
|
|
utils.setScriptVar(pet, "ai.pet.trainCommand" + commandNum, attempts);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
oldCommand = getStringObjVar(petControlDevice, "ai.pet.command."+commandNum);
|
|
}
|
|
|
|
//debugSpeakMsg(pet, "setting to " + text);
|
|
command = text;
|
|
setObjVar(petControlDevice, "ai.pet.command."+commandNum, command);
|
|
|
|
dictionary petCommandList = utils.getDictionaryScriptVar(pet, "ai.pet.commandList");
|
|
|
|
if(petCommandList == null)
|
|
{
|
|
petCommandList = new dictionary();
|
|
}
|
|
|
|
petCommandList.put(command, commandNum);
|
|
|
|
if(oldCommand != null)
|
|
{
|
|
petCommandList.remove(oldCommand);
|
|
}
|
|
|
|
utils.setScriptVar(pet, "ai.pet.commandList", petCommandList);
|
|
utils.removeScriptVar(pet, "ai.pet.learnCommand");
|
|
|
|
if(!pet_lib.isSpeakingPet(pet) || pet_lib.isDroidPet(pet))
|
|
{
|
|
if(!pet_lib.isDroidPet(pet))
|
|
{
|
|
sendSystemMessage(master, SID_SYS_LEARN_COMMAND);
|
|
pet_lib.openLearnCommandSui(pet, master);
|
|
}
|
|
|
|
color textColor = colors.SILVER;
|
|
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "threaten"), .75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
checkForName(pet, petControlDevice);
|
|
}
|
|
else
|
|
{
|
|
string myStringFile = "hireling/hireling";
|
|
|
|
if(hasObjVar(pet, "ai.diction"))
|
|
{
|
|
string myDiction = getStringObjVar(pet, "ai.diction");
|
|
|
|
if(myDiction != null && myDiction != "townperson")
|
|
{
|
|
myStringFile = "hireling/hireling" + "_" + myDiction;
|
|
}
|
|
}
|
|
|
|
chat.chat(pet, new string_id(myStringFile, "end_convo"));
|
|
}
|
|
|
|
savePetInfo(pet, petControlDevice);
|
|
}
|
|
|
|
int getCommandNum(obj_id pet, string text)
|
|
{
|
|
if(!utils.hasScriptVar(pet, "ai.pet.commandList"))
|
|
{
|
|
//debugSpeakMsg(pet, "I don't have a command list");
|
|
return -1;
|
|
}
|
|
|
|
dictionary petCommandList = utils.getDictionaryScriptVar(pet, "ai.pet.commandList");
|
|
string myName = toLower(getName(pet));
|
|
// remove parens from myname.
|
|
int suffix = myName.indexOf(")");
|
|
|
|
if(suffix > 1)
|
|
{
|
|
myName = myName.substring(1, suffix);
|
|
}
|
|
|
|
int commandNum = -1;
|
|
|
|
if(petCommandList.containsKey(text))
|
|
{
|
|
commandNum = petCommandList.getInt(text);
|
|
}
|
|
else if(petCommandList.containsKey(myName + " " + text))
|
|
{
|
|
commandNum = petCommandList.getInt(myName + " " + text);
|
|
}
|
|
|
|
//this is to stop mounts that have the attack command, pre NGE, from attacking when you tell them to.
|
|
if(callable.getCallableType(pet) == callable.CALLABLE_TYPE_RIDEABLE)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if(commandNum > -1)
|
|
{
|
|
color textColor = colors.BLUE;
|
|
showFlyTextPrivate(pet, getMaster(pet), new string_id("npc_reaction/flytext", "threaten"), 1.75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
}
|
|
|
|
return commandNum;
|
|
}
|
|
|
|
void doCommand(obj_id pet, string text, obj_id master)
|
|
{
|
|
int commandNum = getCommandNum(pet, text);
|
|
|
|
if(commandNum == -1)
|
|
{
|
|
//debugSpeakMsg(pet, "I don't know what that means");
|
|
return;
|
|
}
|
|
|
|
doCommandNum(pet, commandNum, master);
|
|
}
|
|
|
|
boolean doCommandNum(obj_id master, int commandNum)
|
|
{
|
|
obj_id pet = callable.getCallable(master, callable.CALLABLE_TYPE_COMBAT_PET);
|
|
|
|
if(!isIdValid(pet) || !exists(pet) || !pet_lib.isDroidPet(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(getMaster(pet) != master)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return doPetCommand(pet, commandNum, master);
|
|
}
|
|
|
|
// Old command function; Newer command function returns success or fail
|
|
void doCommandNum(obj_id pet, int commandNum, obj_id master)
|
|
{
|
|
doPetCommand(pet, commandNum, master);
|
|
}
|
|
|
|
boolean doPetCommand(obj_id pet, int commandNum, obj_id master)
|
|
{
|
|
if(ai_lib.isInCombat(pet))
|
|
{
|
|
if( commandNum >= COMMAND_FOLLOW && commandNum < NUM_COMMANDS &&
|
|
commandNum != COMMAND_STAY && commandNum != COMMAND_FOLLOW &&
|
|
commandNum != COMMAND_FOLLOW_OTHER && commandNum != COMMAND_ATTACK &&
|
|
commandNum != COMMAND_SPECIALATTACK_ONE && commandNum != COMMAND_SPECIALATTACK_TWO)
|
|
return false;
|
|
}
|
|
|
|
if(getMountsEnabled())
|
|
{
|
|
obj_id petsCurrentRider = getRiderId(pet);
|
|
|
|
if(isIdValid(petsCurrentRider))
|
|
{
|
|
if(commandNum != COMMAND_TRICK_1 && commandNum != COMMAND_TRICK_2 && commandNum != COMMAND_RELEASE && commandNum != COMMAND_GROUP)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(isDroidPet(pet))
|
|
{
|
|
if(isLowOnPower(pet))
|
|
{
|
|
color textColor = colors.MEDIUMPURPLE;
|
|
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "low_power"), 0.75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
|
|
if(commandNum != COMMAND_FOLLOW && commandNum != COMMAND_STAY && commandNum != COMMAND_RELEASE)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//debugSpeakMsg(pet, "executing command number " + commandNum);
|
|
|
|
switch(commandNum)
|
|
{
|
|
case COMMAND_FOLLOW :
|
|
doFollowCommand(pet, master);
|
|
break;
|
|
case COMMAND_FOLLOW_OTHER :
|
|
doFollowOtherCommand(pet, master);
|
|
break;
|
|
case COMMAND_STAY :
|
|
doStayCommand(pet, master);
|
|
break;
|
|
case COMMAND_GUARD :
|
|
doGuardCommand(pet, master);
|
|
break;
|
|
case COMMAND_FRIEND :
|
|
doFriendCommand(pet, master);
|
|
break;
|
|
case COMMAND_ATTACK :
|
|
doAttackCommand(pet, master);
|
|
break;
|
|
case COMMAND_PATROL :
|
|
doPatrol(pet, master);
|
|
break;
|
|
case COMMAND_SET_PATROL_POINT :
|
|
doSetPatrolPoint(pet, master);
|
|
break;
|
|
case COMMAND_CLEAR_PATROL_POINTS :
|
|
doClearPatrolPoints(pet, master);
|
|
break;
|
|
case COMMAND_ASSUME_FORMATION_1 :
|
|
doPetFormation(pet, master, ai_lib.FORMATION_COLUMN);
|
|
break;
|
|
case COMMAND_ASSUME_FORMATION_2 :
|
|
doPetFormation(pet, master, ai_lib.FORMATION_WEDGE);
|
|
break;
|
|
case COMMAND_RELEASE :
|
|
doPetRelease(pet);
|
|
break;
|
|
case COMMAND_TRANSFER :
|
|
doPetTransfer(pet, master);
|
|
break;
|
|
case COMMAND_TRICK_1 :
|
|
doStupidPetTrick(pet, master, COMMAND_TRICK_1);
|
|
break;
|
|
case COMMAND_TRICK_2 :
|
|
doStupidPetTrick(pet, master, COMMAND_TRICK_2);
|
|
break;
|
|
case COMMAND_TRICK_3 :
|
|
doStupidPetTrick(pet, master, COMMAND_TRICK_3);
|
|
break;
|
|
case COMMAND_TRICK_4 :
|
|
doStupidPetTrick(pet, master, COMMAND_TRICK_4);
|
|
break;
|
|
case COMMAND_GROUP :
|
|
doPetGroup(pet, master);
|
|
break;
|
|
case COMMAND_SPECIALATTACK_ONE:
|
|
doPetSpecialAttack(pet, master, 0);
|
|
break;
|
|
case COMMAND_SPECIALATTACK_TWO:
|
|
doPetSpecialAttack(pet, master, 1);
|
|
break;
|
|
case COMMAND_TARGET_HARVEST:
|
|
doTargetHarvest(pet, master);
|
|
break;
|
|
case COMMAND_TRAP_THROW_ONE:
|
|
doPetThrowTrap(pet, master, commandNum);
|
|
break;
|
|
default:
|
|
doTriggeredPetAbility(pet, master, commandNum);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void prepToLearn(obj_id pet, int commandNum)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//////debugSpeakMsg(pet, "prepToLearn()");
|
|
utils.setScriptVar(pet, "ai.pet.learnCommand", commandNum);
|
|
|
|
if(!hasMaster(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!pet_lib.isSpeakingPet(pet))
|
|
{
|
|
color textColor = colors.GOLD;
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "alert"), 1.25f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
}
|
|
else
|
|
{
|
|
string myStringFile = "hireling/hireling";
|
|
|
|
if(hasObjVar(pet, "ai.diction"))
|
|
{
|
|
string myDiction = getStringObjVar(pet, "ai.diction");
|
|
|
|
if(myDiction != null && myDiction != "townperson")
|
|
{
|
|
myStringFile = "hireling/hireling" + "_" + myDiction;
|
|
}
|
|
}
|
|
chat.chat(pet, new string_id(myStringFile, "start_convo_4"));
|
|
}
|
|
}
|
|
|
|
string createLearnCommandListHeader(string_id header)
|
|
{
|
|
prose_package pp = new prose_package();
|
|
pp.stringId = new string_id("pet/pet_ability", "learn_command_list_header");
|
|
pp.actor.set(header);
|
|
|
|
return " \0"+packOutOfBandProsePackage(null, pp);
|
|
}
|
|
|
|
string createLearnCommandListEntry(string_id entry, string command)
|
|
{
|
|
prose_package pp = new prose_package();
|
|
pp.stringId = new string_id("pet/pet_ability", "learn_command_list_entry");
|
|
pp.actor.set(entry);
|
|
pp.target.set(command);
|
|
|
|
return " \0"+packOutOfBandProsePackage(null, pp);
|
|
}
|
|
|
|
string getCommandString(obj_id pcd, int command)
|
|
{
|
|
return getCommandString(getObjVarList(pcd, "ai.pet.command"), command);
|
|
}
|
|
|
|
string getCommandString(obj_var_list commandList, int command)
|
|
{
|
|
string commandString = " ";
|
|
|
|
if(commandList != null && commandList.hasObjVar(""+command))
|
|
{
|
|
commandString = " - " + commandList.getStringObjVar(""+command);
|
|
}
|
|
|
|
return commandString;
|
|
}
|
|
|
|
boolean getLearnableCommandList(obj_id pet)
|
|
{
|
|
// Create string array of packed command string ids and matched int array of menu_info_type indexes and store
|
|
// results on the pet. Although this list is used in a SUI listbox the menu_info_type enums are still used for
|
|
// old code compatibility and simplicity.
|
|
// Return sucess or fail of storage of arrays.
|
|
|
|
obj_id master = getMaster(pet);
|
|
if(!isIdValid(master))
|
|
return false;
|
|
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(pcd))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Management skill mod.
|
|
int manageSkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
manageSkillMod = getSkillStatisticModifier(master, "private_creature_management");
|
|
|
|
// Empathy skill mod.
|
|
int empathySkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
empathySkillMod = getSkillStatisticModifier(master, "private_creature_empathy");
|
|
|
|
// Training skill mod.
|
|
int trainingSkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
trainingSkillMod = getSkillStatisticModifier(master, "private_creature_training");
|
|
|
|
resizeable string[] commandNameList = new string[0];
|
|
resizeable int[] commandIndexList = new int[0];
|
|
|
|
obj_var_list commandList = getObjVarList(pcd, "ai.pet.command");
|
|
|
|
// Add standard commands
|
|
commandIndexList = utils.addElement(commandIndexList, 0);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListHeader(new string_id("obj_attr_n","pet_command")));
|
|
{
|
|
//Everything can be released
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_RELEASE);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_release"), getCommandString(commandList, COMMAND_RELEASE)));
|
|
|
|
if(hasSkill(master, "outdoors_creaturehandler_master"))
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRANSFER);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_transfer"), getCommandString(commandList, COMMAND_TRANSFER)));
|
|
}
|
|
|
|
// Creature pets do tricks!
|
|
if(pet_lib.isCreaturePet(pet))
|
|
{
|
|
if(empathySkillMod >= 30)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRICK_2);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_trick_2"), getCommandString(commandList, COMMAND_TRICK_2)));
|
|
}
|
|
if(empathySkillMod >= 10)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRICK_1);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_trick_1"), getCommandString(commandList, COMMAND_TRICK_1)));
|
|
}
|
|
}
|
|
else if(ai_lib.aiGetSpecies(pet) == 215)
|
|
{
|
|
//Power Droid's "trick_1" is to recharge other droids:
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRICK_1);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_recharge_other"), getCommandString(commandList, COMMAND_TRICK_1)));
|
|
}
|
|
else if(ai_lib.aiGetSpecies(pet) == 212)
|
|
{
|
|
//Tip Droid's "trick_1" is to search for master's watchers and solicit tips:
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRICK_1);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_solicit_tip"), getCommandString(commandList, COMMAND_TRICK_1)));
|
|
}
|
|
|
|
if(!pet_lib.isCreaturePet(pet) && pet_lib.isRepairDroid(pet))
|
|
{
|
|
//Repair Droid's "trick_1" is to repair other droids:
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_TRICK_2);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_repair_other"), getCommandString(commandList, COMMAND_TRICK_2)));
|
|
}
|
|
|
|
// Everything can group, patrol and befriend.
|
|
if(manageSkillMod >= 10)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_GROUP);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_group"), getCommandString(commandList, COMMAND_GROUP)));
|
|
}
|
|
if(trainingSkillMod >= 30)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_CLEAR_PATROL_POINTS);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_clear_patrol_points"), getCommandString(commandList, COMMAND_CLEAR_PATROL_POINTS)));
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_GET_PATROL_POINT);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_get_patrol_point"), getCommandString(commandList, COMMAND_SET_PATROL_POINT)));
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_PATROL);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_patrol"), getCommandString(commandList, COMMAND_PATROL)));
|
|
}
|
|
if(trainingSkillMod >= 40)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_ASSUME_FORMATION_1);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_assume_formation_1"), getCommandString(commandList, COMMAND_ASSUME_FORMATION_1)));
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_ASSUME_FORMATION_2);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_assume_formation_2"), getCommandString(commandList, COMMAND_ASSUME_FORMATION_2)));
|
|
}
|
|
if(manageSkillMod >= 30)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_FRIEND);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_friend"), getCommandString(commandList, COMMAND_FRIEND)));
|
|
}
|
|
|
|
// Creatures and combat-droids can guard and attack!
|
|
if(!pet_lib.isDroidPet(pet) || pet_lib.isCombatDroid(pet))
|
|
{
|
|
if(trainingSkillMod >= 20)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_GUARD);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_guard"), getCommandString(commandList, COMMAND_GUARD)));
|
|
}
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_ATTACK);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_attack"), getCommandString(commandList, COMMAND_ATTACK)));
|
|
}
|
|
|
|
//Everything can stay and follow:
|
|
if(trainingSkillMod >= 10)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_STAY);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_stay"), getCommandString(commandList, COMMAND_STAY)));
|
|
}
|
|
if(manageSkillMod >= 20)
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.DICE_ROLL);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_follow_other"), getCommandString(commandList, COMMAND_FOLLOW_OTHER)));
|
|
}
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.PET_FOLLOW);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_follow"), getCommandString(commandList, COMMAND_FOLLOW)));
|
|
|
|
//MOUNT COMMANDS ONLY ACCESSIBLE ifYOUR ARE IN GOD MODE, FOR NOW
|
|
if(getMountsEnabled() && (!hasObjVar(pcd, "ai.pet.trainedMount")))
|
|
{
|
|
//Train pet as a mount ifpossible:
|
|
if(pet_lib.canTrainAsMount(pet, master))
|
|
{
|
|
commandIndexList = utils.addElement(commandIndexList, menu_info_types.SERVER_PET_TRAIN_MOUNT);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id(MENU_FILE,"menu_train_mount"), " "));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add pet ability commands
|
|
commandIndexList = utils.addElement(commandIndexList, 0);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListHeader(new string_id("obj_attr_n","pet_ability")));
|
|
{
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
if(row == -1)
|
|
continue;
|
|
|
|
int type = dataTableGetInt(PET_ABILITY_TABLE, row, "type");
|
|
if(type == PET_ABILITY_TRIGGERED || type == PET_ABILITY_ACTION)
|
|
{
|
|
string abilityName = dataTableGetString(PET_ABILITY_TABLE, row, "abilityName");
|
|
|
|
commandIndexList = utils.addElement(commandIndexList, abilityList[i]);
|
|
commandNameList = utils.addElement(commandNameList, createLearnCommandListEntry(new string_id("pet/pet_ability",abilityName), getCommandString(commandList, abilityList[i])));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(commandIndexList.size() > 0 && commandNameList.size() > 0 && commandIndexList.size() == commandNameList.size())
|
|
{
|
|
utils.setScriptVar(pet, "ai.learnCommand.commandIndexList", commandIndexList);
|
|
utils.setScriptVar(pet, "ai.learnCommand.commandNameList", commandNameList);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void learnPetCommand(obj_id pet, int command)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
if(!isIdValid(master))
|
|
return;
|
|
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(pcd))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(command == 0)
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "learn_command_error"));
|
|
return;
|
|
}
|
|
|
|
// Management skill mod.
|
|
int manageSkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
manageSkillMod = getSkillStatisticModifier(master, "private_creature_management");
|
|
|
|
// Empathy skill mod.
|
|
int empathySkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
empathySkillMod = getSkillStatisticModifier(master, "private_creature_empathy");
|
|
|
|
// Training skill mod.
|
|
int trainingSkillMod = 100;
|
|
if(!pet_lib.isDroidPet(pet))
|
|
trainingSkillMod = getSkillStatisticModifier(master, "private_creature_training");
|
|
|
|
// Learn basic commands
|
|
if(command > menu_info_types.UNKNOWN && command < menu_info_types.MENU_LAST)
|
|
{
|
|
if(command == menu_info_types.SERVER_PET_TRAIN_MOUNT)
|
|
{
|
|
if(getMountsEnabled() && (!hasObjVar(pcd, "ai.pet.trainedMount")))
|
|
{
|
|
if(pet_lib.canTrainAsMount(pet, master))
|
|
pet_lib.trainMount(pet, master);
|
|
}
|
|
}
|
|
else if(command == menu_info_types.PET_FOLLOW)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_FOLLOW);
|
|
}
|
|
else if(command == menu_info_types.DICE_ROLL && (manageSkillMod >= 20))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_FOLLOW_OTHER);
|
|
}
|
|
else if(command == menu_info_types.PET_STAY && (trainingSkillMod >= 10))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_STAY);
|
|
}
|
|
else if(command == menu_info_types.PET_RELEASE)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_RELEASE);
|
|
}
|
|
else if(command == menu_info_types.PET_TRANSFER && (manageSkillMod >= 40))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_TRANSFER);
|
|
}
|
|
else if(command == menu_info_types.PET_TRICK_4)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_TRICK_4);
|
|
}
|
|
else if(command == menu_info_types.PET_TRICK_3)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_TRICK_3);
|
|
}
|
|
else if(command == menu_info_types.PET_TRICK_2 && (empathySkillMod >= 30))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_TRICK_2);
|
|
}
|
|
else if(command == menu_info_types.PET_TRICK_1 && (empathySkillMod >= 10))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_TRICK_1);
|
|
}
|
|
else if(command == menu_info_types.PET_GROUP && (manageSkillMod >= 10))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_GROUP);
|
|
}
|
|
else if(command == menu_info_types.PET_CLEAR_PATROL_POINTS && (trainingSkillMod >= 30))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_CLEAR_PATROL_POINTS);
|
|
}
|
|
else if(command == menu_info_types.PET_GET_PATROL_POINT && (trainingSkillMod >= 30))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_SET_PATROL_POINT);
|
|
}
|
|
else if(command == menu_info_types.PET_PATROL && (trainingSkillMod >= 30))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_PATROL);
|
|
}
|
|
else if(command == menu_info_types.PET_ASSUME_FORMATION_1 && (trainingSkillMod >= 40))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_ASSUME_FORMATION_1);
|
|
}
|
|
else if(command == menu_info_types.PET_ASSUME_FORMATION_2 && (trainingSkillMod >= 40))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_ASSUME_FORMATION_2);
|
|
}
|
|
else if(command == menu_info_types.PET_FRIEND && (manageSkillMod >= 30))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_FRIEND);
|
|
}
|
|
else if(command == menu_info_types.PET_GUARD && (trainingSkillMod >= 20))
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_GUARD);
|
|
}
|
|
else if(command == menu_info_types.PET_ATTACK)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_ATTACK);
|
|
}
|
|
else if(command == menu_info_types.PET_SPECIAL_ATTACK_ONE)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_SPECIALATTACK_ONE);
|
|
}
|
|
else if(command == menu_info_types.PET_SPECIAL_ATTACK_TWO)
|
|
{
|
|
pet_lib.prepToLearn(pet, pet_lib.COMMAND_SPECIALATTACK_TWO);
|
|
}
|
|
}
|
|
else // Learn pet ability command
|
|
{
|
|
pet_lib.prepToLearn(pet, command);
|
|
}
|
|
}
|
|
|
|
void doTriggeredPetAbility(obj_id pet, obj_id master, int commandNum)
|
|
{
|
|
int row = dataTableSearchColumnForInt(commandNum, "abilityCrc", PET_ABILITY_TABLE);
|
|
if(row == -1)
|
|
return;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
if(abilityData == null)
|
|
return;
|
|
|
|
int[] actionCost = combat.getActionCost(pet, getWeaponData(getCurrentWeapon(pet)), abilityData);
|
|
|
|
if(combat.canDrainCombatActionAttributes(pet, actionCost))
|
|
{
|
|
int type = abilityData.getInt("type");
|
|
if(type == PET_ABILITY_TRIGGERED)
|
|
{
|
|
combat.drainCombatActionAttributes(pet, actionCost);
|
|
|
|
string selfBuff = abilityData.getString("selfBuffName");
|
|
string targetBuff = abilityData.getString("targetBuffName");
|
|
string masterBuff = abilityData.getString("masterBuffName");
|
|
int duration = abilityData.getInt("duration");
|
|
|
|
if(selfBuff != null && !selfBuff.equals(""))
|
|
{
|
|
if(duration > 0)
|
|
buff.applyBuff(pet, selfBuff, duration);
|
|
else
|
|
buff.applyBuff(pet, selfBuff);
|
|
}
|
|
|
|
if(targetBuff != null && !targetBuff.equals(""))
|
|
{
|
|
obj_id target = getTarget(pet);
|
|
|
|
if(isIdValid(target))
|
|
{
|
|
if(duration > 0)
|
|
buff.applyBuff(target, targetBuff, duration);
|
|
else
|
|
buff.applyBuff(target, targetBuff);
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
}
|
|
|
|
if(masterBuff != null && !masterBuff.equals(""))
|
|
{
|
|
if(isIdValid(master) && exists(master))
|
|
{
|
|
if(duration > 0)
|
|
buff.applyBuff(master, masterBuff, duration);
|
|
else
|
|
buff.applyBuff(master, masterBuff);
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
}
|
|
|
|
}
|
|
else if(type == PET_ABILITY_ACTION)
|
|
{
|
|
obj_id target = getTarget(master);
|
|
string actionName = abilityData.getString("actionName");
|
|
|
|
int idx = actionName.indexOf('_');
|
|
if(idx > 0)
|
|
actionName = actionName.substring(0, idx);
|
|
|
|
if(isIdValid(target) && pvpCanAttack(pet, target) && pvpCanAttack(master, target))
|
|
{
|
|
setObjVar(pet, "ai.combat.forcedAction", actionName);
|
|
if(!ai_lib.isInCombat(pet))
|
|
{
|
|
startCombat(pet, target);
|
|
}
|
|
else
|
|
{
|
|
float hate = getMaxHate(pet) + 100;
|
|
setHate(pet, target, hate);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
}
|
|
|
|
void doFollowCommand(obj_id pet, obj_id master)
|
|
{
|
|
if(ai_lib.isInCombat(pet))
|
|
{
|
|
utils.removeScriptVar(pet, "ai.combat.target");
|
|
utils.setScriptVar(pet, "petIgnoreAttacks", getGameTime());
|
|
utils.setScriptVar(pet, "pet.combatEnded", getGameTime());
|
|
stopCombat(pet);
|
|
}
|
|
|
|
ai_lib.clearPatrolPath(pet);
|
|
removeObjVar(pet, "ai.inFormation");
|
|
removeObjVar(pet, "ai.formationType");
|
|
removeObjVar(pet, "ai.formationPosition");
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
|
|
if(hasObjVar(pet, "ai.wounded"))
|
|
removeObjVar(pet, "ai.wounded");
|
|
|
|
int myPosture = getPosture(pet);
|
|
if(myPosture != POSTURE_UPRIGHT && myPosture != POSTURE_SITTING)
|
|
{
|
|
////////debugSpeakMsg(npc, "stopping to stand so I can follow");
|
|
stop(pet);
|
|
removeObjVar(pet, "ai.combat.moveMode");
|
|
queueCommand(pet, ##"stand", pet, "", COMMAND_PRIORITY_FRONT);
|
|
}
|
|
|
|
petFollow(pet, master);
|
|
}
|
|
|
|
void doFollowOtherCommand(obj_id pet, obj_id master)
|
|
{
|
|
obj_id target = getIntendedTarget(master);
|
|
if(isIdValid(target) == false || target == pet)
|
|
target = master;
|
|
|
|
if(ai_lib.isInCombat(pet))
|
|
{
|
|
utils.removeScriptVar(pet, "ai.combat.target");
|
|
utils.setScriptVar(pet, "petIgnoreAttacks", getGameTime());
|
|
utils.setScriptVar(pet, "pet.combatEnded", getGameTime());
|
|
stopCombat(pet);
|
|
}
|
|
|
|
ai_lib.clearPatrolPath(pet);
|
|
removeObjVar(pet, "ai.inFormation");
|
|
removeObjVar(pet, "ai.formationType");
|
|
removeObjVar(pet, "ai.formationPosition");
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
|
|
setMovementRun(pet);
|
|
ai_lib.aiFollow(pet, target);
|
|
}
|
|
|
|
void doStayCommand(obj_id pet, obj_id master)
|
|
{
|
|
ai_lib.clearPatrolPath(pet);
|
|
removeObjVar(pet, "ai.inFormation");
|
|
removeObjVar(pet, "ai.formationType");
|
|
removeObjVar(pet, "ai.formationPosition");
|
|
|
|
setMovementWalk(pet);
|
|
if(ai_lib.isInCombat(pet))
|
|
{
|
|
utils.removeScriptVar(pet, "ai.combat.target");
|
|
utils.setScriptVar(pet, "petIgnoreAttacks", getGameTime());
|
|
utils.setScriptVar(pet, "pet.combatEnded", getGameTime());
|
|
stopCombat(pet);
|
|
}
|
|
|
|
utils.setScriptVar(pet, "ai.pet.staying", true);
|
|
ai_lib.aiStopFollowing(pet);
|
|
location myLoc = getLocation(pet);
|
|
setHomeLocation(pet, myLoc);
|
|
|
|
setMovementWalk(pet);
|
|
if(ai_lib.isMonster(pet))
|
|
{
|
|
doAnimationAction(pet, "vocalize");
|
|
ai_lib.setDefaultCalmMood(pet, "bored");
|
|
}
|
|
else if(pet_lib.isDroidPet(pet))
|
|
{
|
|
doAnimationAction(pet, "emt_nod_head_once");
|
|
}
|
|
else
|
|
{
|
|
doAnimationAction(pet, "salute1");
|
|
}
|
|
}
|
|
|
|
void doGuardCommand(obj_id pet, obj_id master)
|
|
{
|
|
if(!isMob(master) && !isPlayer(master))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
|
|
utils.setScriptVar(pet, "ai.pet.guarding", master);
|
|
setWantSawAttackTriggers(pet, true);
|
|
}
|
|
|
|
boolean isGuarding(obj_id pet, obj_id target)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet) || !isIdValid(target) || !exists(target) || !hasMaster(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id guarding = utils.getObjIdScriptVar(pet, "ai.pet.guarding");
|
|
|
|
if(isIdValid(guarding) && exists(guarding))
|
|
{
|
|
return (guarding == target);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void doFriendCommand(obj_id pet, obj_id master)
|
|
{
|
|
obj_id target = getIntendedTarget(master);
|
|
|
|
if(isIdValid(target) == false || isPlayer(target) == false)
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
resizeable obj_id[] friendList = new obj_id[0];
|
|
|
|
if(hasObjVar(pet, "ai.pet.friendList"))
|
|
{
|
|
friendList = getResizeableObjIdArrayObjVar(pet, "ai.pet.friendList");
|
|
}
|
|
|
|
int indexNum = utils.getElementPositionInArray(friendList, target);
|
|
|
|
if(indexNum != -1)
|
|
{
|
|
utils.removeElementAt(friendList, indexNum);
|
|
return;
|
|
}
|
|
|
|
if(friendList.length > MAX_NUMBER_OF_FRIENDS)
|
|
{
|
|
utils.removeElementAt(friendList, 0);
|
|
}
|
|
|
|
friendList = utils.addElement(friendList, target);
|
|
setObjVar(pet, "ai.pet.friendList", friendList);
|
|
}
|
|
|
|
boolean isFriend(obj_id pet, obj_id target)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
if(!isIdValid(master))
|
|
return false;
|
|
|
|
if(master == target)
|
|
return true;
|
|
|
|
if(!hasObjVar(pet, "ai.pet.friendList"))
|
|
return false;
|
|
|
|
obj_id[] friendList = getObjIdArrayObjVar(pet, "ai.pet.friendList");
|
|
int indexNum = utils.getElementPositionInArray(friendList, target);
|
|
if(indexNum != -1)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void doConfusedEmote(obj_id pet)
|
|
{
|
|
if(!pet_lib.isSpeakingPet(pet))
|
|
{
|
|
showFlyText(pet, new string_id("npc_reaction/flytext", "alert"), 3.0f, colors.COPPER);
|
|
doAnimationAction(pet, "emt_stand_confused");
|
|
}
|
|
else
|
|
{
|
|
string myStringFile = "hireling/hireling";
|
|
if(hasObjVar(pet, "ai.diction"))
|
|
{
|
|
string myDiction = getStringObjVar(pet, "ai.diction");
|
|
if(myDiction != null && myDiction != "townperson")
|
|
myStringFile = "hireling/hireling" + "_" + myDiction;
|
|
}
|
|
chat.chat(pet, chat.CHAT_COMPLAIN, chat.MOOD_CONFUSED, new string_id(myStringFile, "confused"));
|
|
}
|
|
}
|
|
|
|
void doAttackCommand(obj_id pet, obj_id master)
|
|
{
|
|
if(!isIdValid(master))
|
|
return;
|
|
|
|
utils.removeScriptVar(pet, "petIgnoreAttacks");
|
|
|
|
obj_id target = getIntendedTarget(master);
|
|
|
|
if(isIdValid(target) == false || target == pet)
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(!pvpCanAttack(pet, target))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(!pvpCanAttack(master, target))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(master.isLoaded())
|
|
setHomeLocation(pet, getLocation(master));
|
|
else if(pet.isLoaded())
|
|
setHomeLocation(pet, getLocation(pet));
|
|
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
|
|
chat.setBadMood(pet);
|
|
|
|
if(!ai_lib.isInCombat(pet))
|
|
{
|
|
//ifnot in combat, then just attack:
|
|
if(!pet_lib.cancelAttackDueToFactionalRestrictions(pet, target))
|
|
{
|
|
////debugSpeakMsg(pet, "attacking " + getName(target));
|
|
startCombat(pet, target);
|
|
utils.setScriptVar(pet, "ai.combat.target", target);
|
|
}
|
|
}
|
|
else if(!pet_lib.cancelAttackDueToFactionalRestrictions(pet, target))
|
|
{
|
|
utils.setScriptVar(pet, "ai.combat.target", target);
|
|
|
|
float maxHate = getMaxHate(pet);
|
|
setHate(pet, target, maxHate + 5000);
|
|
}
|
|
}
|
|
|
|
void doPatrol(obj_id pet, obj_id master)
|
|
{
|
|
if(!hasObjVar(pet, OBJVAR_PET_PATROL_POINTS))
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
else
|
|
{
|
|
removeObjVar(pet, "ai.inFormation");
|
|
removeObjVar(pet, "ai.formationType");
|
|
removeObjVar(pet, "ai.formationPosition");
|
|
|
|
location[] patrolLoc = getLocationArrayObjVar(pet, OBJVAR_PET_PATROL_POINTS);
|
|
|
|
if(patrolLoc != null)
|
|
{
|
|
ai_lib.setPatrolPath(pet, patrolLoc);
|
|
}
|
|
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
}
|
|
}
|
|
|
|
void doSetPatrolPoint(obj_id pet, obj_id master)
|
|
{
|
|
location[] patrolLoc = getLocationArrayObjVar(pet, OBJVAR_PET_PATROL_POINTS);
|
|
|
|
if(patrolLoc == null)
|
|
{
|
|
patrolLoc = new location[1];
|
|
}
|
|
else if(patrolLoc.length < MAX_WAYPOINTS)
|
|
{
|
|
location[] temp = new location[patrolLoc.length + 1];
|
|
for(int i = 0; i < patrolLoc.length; ++i)
|
|
temp[i] = patrolLoc[i];
|
|
patrolLoc = temp;
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
patrolLoc[patrolLoc.length - 1] = getLocation(master);
|
|
setObjVar(pet, OBJVAR_PET_PATROL_POINTS, patrolLoc);
|
|
|
|
sendSystemMessage(master, SID_SYS_PATROL_ADDED);
|
|
}
|
|
|
|
void doClearPatrolPoints(obj_id pet, obj_id master)
|
|
{
|
|
if(hasObjVar(pet, OBJVAR_PET_PATROL_POINTS))
|
|
{
|
|
removeObjVar(pet, OBJVAR_PET_PATROL_POINTS);
|
|
sendSystemMessage(master, SID_SYS_PATROL_REMOVED);
|
|
|
|
ai_lib.clearPatrolPath(pet);
|
|
stop(pet);
|
|
messageTo(pet, "resumeDefaultCalmBehavior", null, 1, false);
|
|
}
|
|
else
|
|
{
|
|
doConfusedEmote(pet);
|
|
}
|
|
}
|
|
|
|
void doPetFormation(obj_id pet, obj_id master, int formationType)
|
|
{
|
|
int position = 0;
|
|
|
|
switch(callable.getCallableType(pet))
|
|
{
|
|
case callable.CALLABLE_TYPE_COMBAT_OTHER:
|
|
position = 0;
|
|
break;
|
|
case callable.CALLABLE_TYPE_COMBAT_PET:
|
|
position = 1;
|
|
break;
|
|
case callable.CALLABLE_TYPE_FAMILIAR:
|
|
position = 2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
setMovementRun(pet);
|
|
ai_lib.followInFormation(pet, master, formationType, position);
|
|
|
|
utils.removeScriptVar(pet, "ai.pet.staying");
|
|
}
|
|
|
|
void doPetRelease(obj_id pet)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
//ifmasterId isn't valid, then you can store regardless
|
|
|
|
CustomerServiceLog("Pets","*Pet Release: (" + master + " sid:"+getPlayerObject(master)+") " + getName(master) + ": has requested that their pet, " + pet + getName(pet) + " be released. Script is attempting to do so.");
|
|
|
|
if(isIdValid(master) && !wasInCombatRecently(pet, master, true))
|
|
{
|
|
ai_lib.clearPatrolPath(pet);
|
|
removeObjVar(pet, "ai.inFormation");
|
|
removeObjVar(pet, "ai.formationType");
|
|
removeObjVar(pet, "ai.formationPosition");
|
|
|
|
//say goodbye here
|
|
releasePet(pet);
|
|
}
|
|
}
|
|
|
|
void doPetTransfer(obj_id pet, obj_id master)
|
|
{
|
|
obj_id target = getIntendedTarget(master);
|
|
|
|
if(isIdValid(target) == false || !isPlayer(target))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(!hasScript(target, "ai.pet_master"))
|
|
{
|
|
attachScript(target, "ai.pet_master");
|
|
}
|
|
|
|
int petType = pet_lib.getPetType(pet);
|
|
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
{
|
|
destroyObject(pet);
|
|
return;
|
|
}
|
|
|
|
int niche = ai_lib.aiGetNiche(pet);
|
|
|
|
if(niche == NICHE_NPC || niche == NICHE_VEHICLE)
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "bad_type"));
|
|
sendSystemMessage(target, new string_id("pet/pet_menu", "bad_type"));
|
|
return;
|
|
}
|
|
|
|
if(hasMaxPets(target, petType))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "targ_too_many"));
|
|
sendSystemMessage(target, SID_SYS_TOO_MANY_PETS);
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(hasMaxStoredPetsOfType(target, petType))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "targ_too_many_stored"));
|
|
sendSystemMessage(target, SID_SYS_TOO_MANY_STORED_PETS);
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
int petLevel = getLevel(pet);
|
|
|
|
if(!pet_lib.canControlPetsOfLevel(target, petType, petLevel, creatureName))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "no_chance"));
|
|
//The correct system message is sent to target in the canControlPetsOfLevel() function:
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
// cannot transfer ifthe pet contains a noTrade item
|
|
obj_id pcd = null;
|
|
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
pcd = callable.getCallableCD(pet);
|
|
}
|
|
|
|
obj_id[] petArray = null;
|
|
|
|
if(isIdValid(pcd) && exists(pcd))
|
|
{
|
|
petArray = new obj_id[2];
|
|
petArray[0] = pet;
|
|
petArray[1] = pcd;
|
|
}
|
|
else
|
|
{
|
|
petArray = new obj_id[1];
|
|
petArray[0] = pet;
|
|
}
|
|
|
|
obj_id notradeItem = utils.findNoTradeItem(petArray, false);
|
|
|
|
if(isIdValid(notradeItem))
|
|
{
|
|
prose_package pp = new prose_package();
|
|
pp.stringId = SID_PET_HAS_NOTRADE;
|
|
pp.target.id = notradeItem;
|
|
sendSystemMessageProse(master, pp);
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
//disband, ifgrouped:
|
|
obj_id group = getGroupObject(pet);
|
|
|
|
if(isIdValid(group))
|
|
{
|
|
queueCommand(pet, ##"leaveGroup", group, "", COMMAND_PRIORITY_IMMEDIATE);
|
|
}
|
|
|
|
obj_id controller = null;
|
|
|
|
//move the old controller(this destroys the pet)
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
controller = callable.getCallableCD(pet);
|
|
obj_id newMasterPad = utils.getPlayerDatapad(target);
|
|
|
|
if(!isIdValid(controller) || !isIdValid(newMasterPad))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "targ_too_many_stored"));
|
|
sendSystemMessage(target, SID_SYS_TOO_MANY_STORED_PETS);
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(!putIn(controller, newMasterPad))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu", "targ_too_many_stored"));
|
|
sendSystemMessage(target, SID_SYS_TOO_MANY_STORED_PETS);
|
|
doConfusedEmote(pet);
|
|
CustomerServiceLog("Pets","***Pet Transfer: (" + master + " sid:"+getPlayerObject(master)+") " + getName(master) + ": Pet FAILED to transfer " + target + " sid:"+getPlayerObject(target)+") " + getName(target));
|
|
return;
|
|
}
|
|
}
|
|
|
|
sendSystemMessage(master, SID_SYS_XFER_SUCCESS);
|
|
sendSystemMessage(target, SID_SYS_XFER_SUCCESS);
|
|
CustomerServiceLog("Pets","***Pet Transferrer: Player " + getName(master) + "(" + master + ") StationId("+getPlayerObject(master)+") successfully transfered PetControlDevice "+getName(controller)+" ("+controller+") of template: "+getTemplateName(controller)+" to receiving player "+ getName(target)+"(" + target + ") StationId("+getPlayerObject(target)+")");
|
|
}
|
|
|
|
void doStupidPetTrick(obj_id pet, obj_id master, int trickNum)
|
|
{
|
|
//Power droid's "trick_1" is to recharge other pets:
|
|
if(ai_lib.aiGetSpecies(pet) == SPECIES_POWERDROID && trickNum == COMMAND_TRICK_1)
|
|
{
|
|
rechargeOtherDroid(pet, master);
|
|
return;
|
|
}
|
|
|
|
//Tip droid's "trick_1" is to solicit master's watchers for tips:
|
|
if(ai_lib.aiGetSpecies(pet) == SPECIES_POWERDROID && trickNum == COMMAND_TRICK_1)
|
|
{
|
|
solicitTipsForMaster(pet, master);
|
|
return;
|
|
}
|
|
|
|
//a repair droid's "trick_2" is to repair other pets:
|
|
if(isRepairDroid(pet) && trickNum == COMMAND_TRICK_2)
|
|
{
|
|
repairOtherDroid(pet, master);
|
|
return;
|
|
}
|
|
|
|
int myPosture = getPosture(pet);
|
|
|
|
if(myPosture != POSTURE_UPRIGHT && myPosture != POSTURE_SITTING)
|
|
{
|
|
////////debugSpeakMsg(npc, "stopping to stand so I can follow");
|
|
stop(pet);
|
|
removeObjVar(pet, "ai.combat.moveMode");
|
|
queueCommand(pet, ##"stand", pet, "", COMMAND_PRIORITY_FRONT);
|
|
//setPosture(pet, POSTURE_UPRIGHT);
|
|
dictionary params = new dictionary();
|
|
params.put("master",master);
|
|
params.put("trickNum",trickNum);
|
|
messageTo(pet, "resumePetTrick", params, 2, false);
|
|
return;
|
|
}
|
|
|
|
int action = getAttrib(master, ACTION);
|
|
int actionCost = 100;
|
|
|
|
if(action < actionCost)
|
|
{
|
|
// Not enough mind to do trick.
|
|
sendSystemMessage(master, SID_SYS_CANT_TRICK);
|
|
return;
|
|
}
|
|
|
|
drainAttributes(master, actionCost, 0);
|
|
|
|
stop(pet);
|
|
|
|
float maxHealing = .2f;
|
|
|
|
switch(trickNum)
|
|
{
|
|
case COMMAND_TRICK_1 :
|
|
if(myPosture == POSTURE_SITTING)
|
|
{
|
|
doAnimationAction(pet, "sit_trick_1");
|
|
}
|
|
else
|
|
{
|
|
doAnimationAction(pet, "trick_1");
|
|
}
|
|
break;
|
|
case COMMAND_TRICK_2 :
|
|
if(myPosture == POSTURE_SITTING)
|
|
{
|
|
doAnimationAction(pet, "sit_trick_2");
|
|
}
|
|
else
|
|
{
|
|
doAnimationAction(pet, "trick_2");
|
|
}
|
|
|
|
maxHealing = .4f;
|
|
|
|
break;
|
|
}
|
|
|
|
healShockWound(pet, getShockWound(pet));
|
|
attribute[] maxAttribs = getUnmodifiedMaxAttribs(pet);
|
|
int healAmount = (int)(maxAttribs[MIND].getValue() * maxHealing);
|
|
addAttribModifier(pet, maxAttribs[MIND].getType(), healAmount, 0, 0, MOD_POOL);
|
|
|
|
if(getBehavior(pet) == BEHAVIOR_CALM)
|
|
{
|
|
messageTo(pet, "resumeDefaultCalmBehavior", null, 8, false);
|
|
}
|
|
}
|
|
|
|
void rechargeOtherDroid(obj_id pet, obj_id master)
|
|
{
|
|
obj_id target = getIntendedTarget(master);
|
|
|
|
if(isIdValid(target) == false || !isDroidPet(target))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(pet == target) {
|
|
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(getDistance(pet, target) > 10f)
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
follow(pet, target, 1, 2);
|
|
|
|
//Feeding droids batteries will repower them, and heal mind-based wounds:
|
|
color textColor = colors.PURPLE;
|
|
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "recharged"), .75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
|
|
obj_id targetPetControlDevice = callable.getCallableCD(target);
|
|
|
|
removeObjVar(targetPetControlDevice, "ai.pet.powerLevel");
|
|
|
|
healShockWound(target, getShockWound(target));
|
|
|
|
if(getBehavior(pet) == BEHAVIOR_CALM)
|
|
{
|
|
messageTo(pet, "resumeDefaultCalmBehavior", null, 8, false);
|
|
}
|
|
|
|
//consume an additional powerUnit and begin low-power warning ifnecessary.
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
int powerLevel = getIntObjVar(petControlDevice, "ai.pet.powerLevel");
|
|
powerLevel++;
|
|
|
|
if(powerLevel == pet_lib.OUT_OF_POWER)
|
|
{
|
|
messageTo(pet, "powerWarningText", null, 30, false);
|
|
}
|
|
|
|
if(powerLevel < pet_lib.OUT_OF_POWER)
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.powerLevel", powerLevel);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.powerLevel", (pet_lib.OUT_OF_POWER + 1));
|
|
}
|
|
}
|
|
|
|
void repairOtherDroid(obj_id pet, obj_id master)
|
|
{
|
|
obj_id target = getIntendedTarget(master);
|
|
|
|
if(isIdValid(target) == false || !isDroidPet(target))
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(pet == target)
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
if(getDistance(pet, target) > 10f)
|
|
{
|
|
doConfusedEmote(pet);
|
|
return;
|
|
}
|
|
|
|
follow(pet, target, 1, 2);
|
|
|
|
//Feeding droids batteries will repower them, and heal wounds:
|
|
showFlyText(target, new string_id("npc_reaction/flytext", "repaired"), .75f, colors.PURPLE);
|
|
|
|
healShockWound(target, getShockWound(target));
|
|
|
|
if(getBehavior(pet) == BEHAVIOR_CALM)
|
|
{
|
|
messageTo(pet, "resumeDefaultCalmBehavior", null, 8, false);
|
|
}
|
|
}
|
|
|
|
void solicitTipsForMaster(obj_id pet, obj_id master)
|
|
{
|
|
//attachScript(pet, "ai.special.tip_solicitor");
|
|
}
|
|
|
|
void doPetGroup(obj_id pet, obj_id master)
|
|
{
|
|
if(group.isGrouped(pet))
|
|
{
|
|
obj_id group = getGroupObject(pet);
|
|
|
|
if(isIdValid(group))
|
|
{
|
|
queueCommand(pet, ##"leaveGroup", group, "", COMMAND_PRIORITY_DEFAULT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
obj_id groupLeader = group.getLeader(master);
|
|
|
|
if(!isIdValid(groupLeader) || !exists(groupLeader))
|
|
{
|
|
groupLeader = master;
|
|
}
|
|
|
|
|
|
//////debugSpeakMsg(pet, "inqueueing invite, and messaging myself to join - leader is " + getName(groupLeader));
|
|
queueCommand(groupLeader, ##"invite", pet, "", COMMAND_PRIORITY_DEFAULT);
|
|
messageTo(pet, "handleGroupInvite", null, 2, false);
|
|
sendSystemMessage(master, SID_PET_INVITED);
|
|
}
|
|
|
|
}
|
|
|
|
void checkForName(obj_id pet, obj_id pcd)
|
|
{
|
|
if(!isCreaturePet(pet) && !isDroidPet(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!isIdValid(master))
|
|
{
|
|
return;
|
|
}
|
|
|
|
resizeable string[] nameList = new string[0];
|
|
resizeable int[] nameCount = new int[0];
|
|
|
|
obj_var_list commandList = getObjVarList(pcd, "ai.pet.command");
|
|
|
|
if(commandList != null && commandList.getNumItems() > 0)
|
|
{
|
|
int numItems = commandList.getNumItems();
|
|
|
|
for(int i = 0; i < numItems; i++)
|
|
{
|
|
obj_var ov = commandList.getObjVar(i);
|
|
|
|
int commandNum = Integer.parseInt(ov.getName());
|
|
string commandName = ov.getStringData();
|
|
|
|
if(commandName.indexOf(" ") > -1)
|
|
{
|
|
string name = commandName.substring(0, commandName.indexOf(" "));
|
|
int indexNum = utils.getElementPositionInArray(nameList, name);
|
|
|
|
if(indexNum == -1)
|
|
{
|
|
nameList = utils.addElement(nameList, name);
|
|
nameCount = utils.addElement(nameCount, 0);
|
|
}
|
|
else
|
|
{
|
|
nameCount[ indexNum ] = nameCount[ indexNum ] + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string highName = new string();
|
|
int highCount = 0;
|
|
|
|
for(int i = 0; i < nameList.length; i++)
|
|
{
|
|
if(nameCount[i] > highCount)
|
|
{
|
|
highName = nameList[i];
|
|
highCount = nameCount[i];
|
|
}
|
|
}
|
|
|
|
if(highCount < 2 || highName == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(highName.length() < 2)
|
|
{
|
|
highName = highName.toUpperCase();
|
|
}
|
|
else
|
|
{
|
|
string firstLetter = highName.substring(0, 1);
|
|
firstLetter = firstLetter.toUpperCase();
|
|
string theName = highName.substring(1);
|
|
highName = firstLetter + theName;
|
|
}
|
|
|
|
string oldName = getName(pet);
|
|
// remove parens from myname.
|
|
int suffix = oldName.indexOf(")");
|
|
|
|
if(suffix > 2)
|
|
{
|
|
oldName = oldName.substring(1, suffix);
|
|
}
|
|
|
|
string learnedName = "("+highName+")";
|
|
|
|
if(highCount > 2 && learnedName != oldName && oldName != highName && isNameReserved(highName)==false)
|
|
{
|
|
setName(pet, learnedName);
|
|
chat.thinkTo(pet, master, "?");
|
|
}
|
|
}
|
|
|
|
void feedPet(obj_id pet, obj_id master)
|
|
{
|
|
//feeding pets heals their health-based wounds:
|
|
// Feeding droids batteries will repower them.
|
|
if(isDroidPet(pet))
|
|
{
|
|
if(!eatBattery(master))
|
|
{
|
|
color textColor = colors.YELLOW;
|
|
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "nobattery"), .75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
return;
|
|
}
|
|
|
|
color textColor = colors.MEDIUMPURPLE;
|
|
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "recharged"), 0.75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
removeObjVar(petControlDevice, "ai.pet.powerLevel");
|
|
}
|
|
else
|
|
{
|
|
// Feed them food.
|
|
if(!isWounded(pet))
|
|
{
|
|
color textColor = colors.YELLOW;
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "nothungry"), .75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
return;
|
|
}
|
|
if(!eatFood(master))
|
|
{
|
|
color textColor = colors.YELLOW;
|
|
showFlyTextPrivate(pet, master, new string_id("npc_reaction/flytext", "nofood"), .75f, textColor.getR(), textColor.getG(), textColor.getB(), true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
stop(pet);
|
|
faceTo(pet, master);
|
|
faceTo(master, pet);
|
|
|
|
doAnimationAction(master, "manipulate_medium");
|
|
|
|
if(isCreaturePet(pet))
|
|
{
|
|
doAnimationAction(pet, "eat");
|
|
}
|
|
|
|
healShockWound(pet, getShockWound(pet));
|
|
|
|
if(getBehavior(pet) == BEHAVIOR_CALM)
|
|
{
|
|
messageTo(pet, "resumeDefaultCalmBehavior", null, 8, false);
|
|
}
|
|
}
|
|
|
|
// Check to see ifthe pet is wounded.
|
|
boolean isWounded(obj_id pet)
|
|
{
|
|
if(getShockWound(pet) > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean eatFood(obj_id master)
|
|
{
|
|
obj_id inv = utils.getInventoryContainer(master);
|
|
|
|
if(!isIdValid(inv) || !exists(inv))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id[] contents = getContents(inv);
|
|
|
|
for(int i = 0; i < contents.length; i++)
|
|
{
|
|
int got = getGameObjectType(contents[i]);
|
|
|
|
if(isGameObjectTypeOf(got, GOT_misc_food))
|
|
{
|
|
consumable.decrementCharges(contents[i],master);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
boolean eatBattery(obj_id master)
|
|
{
|
|
obj_id inv = utils.getInventoryContainer(master);
|
|
|
|
if(!isIdValid(inv) || !exists(inv))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id[] contents = getContents(inv);
|
|
|
|
for(int i = 0; i < contents.length; i++)
|
|
{
|
|
if(getTemplateName(contents[i]).equals(BATTERY))
|
|
{
|
|
consumable.decrementCharges(contents[i],master);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void storePet(obj_id pet)
|
|
{
|
|
storePet(pet, null);
|
|
}
|
|
|
|
void storePet(obj_id pet, obj_id master)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(isIdValid(petControlDevice) && petControlDevice.isLoaded())
|
|
{
|
|
obj_id currentPet = callable.getCDCallable(petControlDevice);
|
|
|
|
if(isIdValid(currentPet) && currentPet == pet && currentPet.isLoaded())
|
|
{
|
|
savePetInfo(currentPet, petControlDevice);
|
|
setObjVar(petControlDevice, "pet.timeStored", getGameTime());
|
|
|
|
callable.setCDCallable(petControlDevice, null);
|
|
|
|
setCount(petControlDevice, 0);
|
|
}
|
|
}
|
|
|
|
messageTo(pet, "handlePackRequest", null, 0, false);//use a message in case the damn thing isn't on this server
|
|
}
|
|
|
|
boolean hasMaxStoredPetsOfType(obj_id player, int petType)
|
|
{
|
|
if(!isIdValid(player))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int maxStored = getMaxStoredPetsOfType(player, petType);
|
|
int numStored = getNumStoredPetsOfType(player, petType);
|
|
|
|
return (numStored >= maxStored);
|
|
}
|
|
|
|
int getNumStoredPetsOfType(obj_id player, int petType)
|
|
{
|
|
obj_id datapad = utils.getPlayerDatapad(player);
|
|
|
|
if(!isIdValid(datapad))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
obj_id[] myStuff = getContents(datapad);
|
|
|
|
if(myStuff == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int callableType = callable.CALLABLE_TYPE_UNKNOWN;
|
|
|
|
switch(petType)
|
|
{
|
|
case PET_TYPE_NON_AGGRO:
|
|
case PET_TYPE_AGGRO:
|
|
case PET_TYPE_NPC:
|
|
case PET_TYPE_DROID:
|
|
callableType = callable.CALLABLE_TYPE_COMBAT_PET;
|
|
break;
|
|
case PET_TYPE_FAMILIAR:
|
|
callableType = callable.CALLABLE_TYPE_FAMILIAR;
|
|
break;
|
|
case PET_TYPE_MOUNT:
|
|
callableType = callable.CALLABLE_TYPE_RIDEABLE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(callableType == callable.CALLABLE_TYPE_UNKNOWN)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return callable.getNumStoredCDByType(player, callableType);
|
|
}
|
|
|
|
int getMaxStoredPetsOfType(obj_id master, int petType)
|
|
{
|
|
if(!isIdValid(master) || !exists(master))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
switch(petType)
|
|
{
|
|
case PET_TYPE_NON_AGGRO:
|
|
case PET_TYPE_AGGRO:
|
|
case PET_TYPE_NPC:
|
|
case PET_TYPE_DROID:
|
|
return callable.MAX_STORED_COMBAT_PETS;
|
|
case PET_TYPE_FAMILIAR:
|
|
return callable.MAX_STORED_FAMILIARS;
|
|
case PET_TYPE_MOUNT:
|
|
return callable.getMaxAllowedStoredRideables(master);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int getMaxStoredPets(obj_id pet)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!isIdValid(master))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int petType = pet_lib.getPetType(pet);
|
|
return getMaxStoredPetsOfType(master, petType);
|
|
}
|
|
|
|
boolean isPet(obj_id npc)
|
|
{
|
|
if(isValidId(npc) && exists(npc))
|
|
{
|
|
return hasObjVar(npc, "ai.pet");
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
boolean isCreaturePet(obj_id npc)
|
|
{
|
|
return (ai_lib.isMonster(npc) && hasObjVar(npc, "ai.pet"));
|
|
}
|
|
|
|
boolean isNpcPet(obj_id npc)
|
|
{
|
|
int niche = ai_lib.aiGetNiche(npc);
|
|
|
|
if(niche == NICHE_VEHICLE)
|
|
{
|
|
niche = NICHE_NPC;
|
|
}
|
|
|
|
return (niche == NICHE_NPC && hasObjVar(npc, "ai.pet"));
|
|
}
|
|
|
|
boolean isVehiclePet(obj_id npc)
|
|
{
|
|
int niche = ai_lib.aiGetNiche(npc);
|
|
|
|
return (niche == NICHE_VEHICLE && hasObjVar(npc, "ai.pet"));
|
|
}
|
|
|
|
boolean isMyPet(obj_id npc, obj_id player)
|
|
{
|
|
if(!isIdValid(npc) || !isIdValid(player) || !hasMaster(npc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (getMaster(npc) == player);
|
|
}
|
|
|
|
boolean isDroidPet(obj_id npc)
|
|
{
|
|
int gotType = getGameObjectType(npc);
|
|
int niche = ai_lib.aiGetNiche(npc);
|
|
if(gotType == GOT_data_pet_control_device || gotType == GOT_data_droid_control_device)
|
|
{
|
|
if(hasObjVar(npc, "pet.creatureName"))
|
|
{
|
|
string creatureName = getStringObjVar(npc, "pet.creatureName");
|
|
int row = dataTableSearchColumnForString(creatureName, "creatureName", create.CREATURE_TABLE);
|
|
if(row >= 0)
|
|
{
|
|
dictionary dict = dataTableGetRow(create.CREATURE_TABLE, row);
|
|
niche = dict.getInt("niche");
|
|
}
|
|
}
|
|
}
|
|
|
|
return ((niche == NICHE_DROID || niche == NICHE_ANDROID) && hasObjVar(npc, "ai.pet"));
|
|
}
|
|
|
|
boolean isSpeakingPet(obj_id npc)
|
|
{
|
|
if ( pet_lib.getPetType(npc) == pet_lib.PET_TYPE_FAMILIAR )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isNpcPet(npc) || ai_lib.aiGetNiche(npc) == NICHE_VEHICLE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(isDroidPet(npc) && ai_lib.aiGetSpecies(npc) != -1)
|
|
{
|
|
int speaking = dataTableGetInt("datatables/ai/species.iff", ai_lib.aiGetSpecies(npc), "Speaking");
|
|
|
|
if(speaking == 1)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean hasContainer(obj_id pet)
|
|
{
|
|
if(isDroidPet(pet))
|
|
{
|
|
return (hasObjVar(pet, "ai.pet.hasContainer"));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean hasDatapad(obj_id pet)
|
|
{
|
|
if(isDroidPet(pet))
|
|
{
|
|
return (hasObjVar(pet, "ai.pet.hasDatapad"));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean isCombatDroid(obj_id npc)
|
|
{
|
|
obj_id petControlDevice = callable.getCallableCD(npc);
|
|
|
|
if(!isIdValid(npc) && exists(petControlDevice))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(ai_lib.aiGetNiche(npc) != NICHE_DROID && ai_lib.aiGetNiche(npc) != NICHE_ANDROID)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(petControlDevice, "pet.combatDroid"))
|
|
{
|
|
return true;
|
|
}
|
|
else if(hasObjVar(petControlDevice, "pet.creatureName")) // ifit doesn't have pet.combatDroid, it might still be an elderly probot or dz70, and should be grandfathered in
|
|
{
|
|
string creatureName = getStringObjVar(petControlDevice, "pet.creatureName");
|
|
|
|
if(creatureName == PROBOT_DROID_NAME || creatureName == DZ70_DROID_NAME)
|
|
{
|
|
if(hasObjVar(petControlDevice, "pet.nonCombatDroid")) // This is a probot or dz70 that was crafted since the implementation of the combatDroid system.
|
|
{
|
|
return false; //No combat ability was crafted into it, so no combat ability.
|
|
}
|
|
else
|
|
{
|
|
return true; //It looks like this probot or DZ70 was crafted prior to the combat droid system, so we'll let it retain it's inherent combat ability.
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
return false; //fall-through case. Can't figure this droid out, so no combat ability
|
|
}
|
|
|
|
boolean isCombatDroidPCD(obj_id petControlDevice)
|
|
{
|
|
if(!isIdValid(petControlDevice))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(petControlDevice, "pet.combatDroid"))
|
|
{
|
|
return true;
|
|
}
|
|
else if(hasObjVar(petControlDevice, "pet.creatureName")) // ifpcd doesn't have pet.combatDroid, it might still be for an elderly probot or dz70, and should be grandfathered in
|
|
{
|
|
string creatureName = getStringObjVar(petControlDevice, "pet.creatureName");
|
|
|
|
if(creatureName == PROBOT_DROID_NAME || creatureName == DZ70_DROID_NAME)
|
|
{
|
|
if(hasObjVar(petControlDevice, "pet.nonCombatDroid")) // This is a probot or dz70 that was crafted since the implementation of the combatDroid system.
|
|
{
|
|
return false; // No combat ability was crafted into it, so no combat ability.
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return false; //fall-through case. Can't figure this droid pcd out, so no combat ability
|
|
}
|
|
}
|
|
|
|
void openPetContainer(obj_id pet, obj_id master)
|
|
{
|
|
stop(pet);
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
obj_id petInv = utils.getInventoryContainer(petControlDevice);
|
|
|
|
if(!isIdValid(petInv) || !exists(petInv) || !isIdValid(master) || !exists(master))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setOwner(petInv, master);
|
|
|
|
utils.requestContainerOpen(master, petInv);
|
|
}
|
|
|
|
void openPetDatapad(obj_id pet, obj_id master)
|
|
{
|
|
stop(pet);
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
obj_id petDPad = utils.getDatapad(petControlDevice);
|
|
|
|
if(!isIdValid(petDPad) || !exists(petDPad) || !isIdValid(master) || !exists(master))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(isLowOnPower(pet))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu","droid_out_of_power"));
|
|
return;
|
|
}
|
|
|
|
setOwner(petDPad, master);
|
|
|
|
utils.requestContainerOpen(master, petDPad);
|
|
}
|
|
|
|
boolean isLowOnPower(obj_id pet)
|
|
{
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
int powerLevel = getIntObjVar(petControlDevice, "ai.pet.powerLevel");
|
|
|
|
if(powerLevel >= OUT_OF_POWER)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean findMaster(obj_id pet)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet) || !pet.isLoaded() || !hasMaster(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!isIdValid(master) || !exists(master) || !master.isLoaded())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void removeFromList(obj_id master, obj_id pet)
|
|
{
|
|
if(!isIdValid(master) || !exists(master) || !isIdValid(pet) || !exists(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(hasObjVar(master, "pet.petList")) // Legacy list system for pets.
|
|
{
|
|
removeObjVar(master, "pet.petList"); // Legacy list system for pets.
|
|
}
|
|
|
|
callable.setCallable(master, null, callable.getCallableType(pet));
|
|
}
|
|
|
|
void addToPetList(obj_id master, obj_id pet)
|
|
{
|
|
if(!isIdValid(master) || !exists(master) || !isIdValid(pet) || !exists(pet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(hasObjVar(master, "pet.petList")) // Legacy list system for pets.
|
|
{
|
|
removeObjVar(master, "pet.petList"); // Legacy list system for pets.
|
|
}
|
|
|
|
// If I have a pet of the type I'm trying to call now, then store the old one.
|
|
if(callable.hasCallable(master, callable.getCallableType(pet), pet))
|
|
{
|
|
callable.storeCallable(master, callable.getCallable(master, callable.getCallableType(pet)));
|
|
}
|
|
|
|
callable.setCallable(master, pet, callable.getCallableType(pet));
|
|
}
|
|
|
|
void storeAllPets(obj_id master)
|
|
{
|
|
callable.storeCallables(master);
|
|
}
|
|
|
|
//returns True ifthe player is within Range of a surgical droid owned by Himself.
|
|
// return False ifnot, or ifall surgical droids in range are low on power.
|
|
boolean isNearMedicalDroid(obj_id master, float range)
|
|
{
|
|
obj_id[] critters = getCreaturesInRange(getLocation(master), range);
|
|
if(critters == null || critters.length <= 0)
|
|
return false;
|
|
|
|
for(int i = 0; i < critters.length; ++i)
|
|
{
|
|
if(hasObjVar(critters[i], "medpower"))
|
|
{
|
|
if(isMyPet(critters[i], master) && isLowOnPower(critters[i])==false)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//returns True ifthe player is within Range of a surgical droid owned by Himself.
|
|
// return False ifnot, or ifall surgical droids in range are low on power.
|
|
float getMedicalDroidMod(obj_id master, float range)
|
|
{
|
|
float medMod = 1.0f;
|
|
|
|
obj_id[] critters = getCreaturesInRange(getLocation(master), range);
|
|
if(critters == null || critters.length <= 0)
|
|
return medMod;
|
|
|
|
for(int i = 0; i < critters.length; ++i)
|
|
{
|
|
if(hasObjVar(critters[i], "medpower"))
|
|
{
|
|
if(isMyPet(critters[i], master) && isLowOnPower(critters[i])==false)
|
|
{
|
|
medMod = getFloatObjVar(critters[i], "medpower");
|
|
return medMod;
|
|
}
|
|
}
|
|
}
|
|
return medMod;
|
|
}
|
|
|
|
|
|
void setupDefaultCommands(obj_id pet)
|
|
{
|
|
if(utils.hasScriptVar(pet, "ai.pet.commandList"))
|
|
return;//don't stomp existing command list!
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
int restrictCommands = getIntObjVar(pet, "pet.petRestriction");
|
|
dictionary petCommandList = utils.getDictionaryScriptVar(pet, "ai.pet.commandList");
|
|
|
|
if(petCommandList == null)
|
|
{
|
|
petCommandList = new dictionary();
|
|
}
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_FOLLOW, "follow");
|
|
petCommandList.put("follow", COMMAND_FOLLOW);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_STAY, "stay");
|
|
petCommandList.put("stay", COMMAND_STAY);
|
|
|
|
if(!isNewPlayerHelperDroid(pet))
|
|
{
|
|
if(restrictCommands > 0)
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_PATROL, "patrol");
|
|
petCommandList.put("patrol", COMMAND_PATROL);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_SET_PATROL_POINT, "get patrol point");
|
|
petCommandList.put("get patrol point", COMMAND_SET_PATROL_POINT);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_CLEAR_PATROL_POINTS, "clear patrol point");
|
|
petCommandList.put("clear patrol point", COMMAND_CLEAR_PATROL_POINTS);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_ASSUME_FORMATION_1, "column formation");
|
|
petCommandList.put("column formation", COMMAND_ASSUME_FORMATION_1);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_ASSUME_FORMATION_2, "wedge formation");
|
|
petCommandList.put("wedge formation", COMMAND_ASSUME_FORMATION_2);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_FOLLOW_OTHER, "chase");
|
|
petCommandList.put("chase", COMMAND_FOLLOW_OTHER);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_FRIEND, "friend");
|
|
petCommandList.put("friend", COMMAND_FRIEND);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_TRANSFER, "transfer");
|
|
petCommandList.put("transfer", COMMAND_TRANSFER);
|
|
}
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_RELEASE, "release");
|
|
petCommandList.put("release", COMMAND_RELEASE);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_GROUP, "group");
|
|
petCommandList.put("group", COMMAND_GROUP);
|
|
}
|
|
if(pet_lib.isRepairDroid(pet))
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_TRICK_2, "repair");
|
|
petCommandList.put("repair", COMMAND_TRICK_2);
|
|
}
|
|
|
|
// Creatures and combat-droids can guard and attack!
|
|
if(isCombatDroid(pet) || !isDroidPet(pet))
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_ATTACK, "attack");
|
|
petCommandList.put("attack", COMMAND_ATTACK);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_GUARD, "guard");
|
|
petCommandList.put("guard", COMMAND_GUARD);
|
|
}
|
|
|
|
utils.setScriptVar(pet, "ai.pet.commandList", petCommandList);
|
|
setObjVar(pet, "alreadyTrained", true);
|
|
}
|
|
|
|
void setupOfficerPetCommands(obj_id pet)
|
|
{
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
dictionary petCommandList = utils.getDictionaryScriptVar(pet, "ai.pet.commandList");
|
|
|
|
if(petCommandList == null)
|
|
{
|
|
petCommandList = new dictionary();
|
|
}
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_ATTACK, "attack");
|
|
petCommandList.put("attack", COMMAND_ATTACK);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_GUARD, "guard");
|
|
petCommandList.put("guard", COMMAND_GUARD);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_FOLLOW, "follow");
|
|
petCommandList.put("follow", COMMAND_FOLLOW);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_STAY, "stay");
|
|
petCommandList.put("stay", COMMAND_STAY);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_RELEASE, "release");
|
|
petCommandList.put("release", COMMAND_RELEASE);
|
|
|
|
setObjVar(petControlDevice, "ai.pet.command." + COMMAND_GROUP, "group");
|
|
petCommandList.put("group", COMMAND_GROUP);
|
|
|
|
utils.setScriptVar(pet, "ai.pet.commandList", petCommandList);
|
|
setObjVar(pet, "alreadyTrained", true);
|
|
|
|
return;
|
|
}
|
|
|
|
void markExpansionPets(obj_id petControlDevice, obj_id pet)
|
|
{
|
|
if(hasObjVar(pet,"storageUnlimited"))
|
|
setObjVar(petControlDevice, "storageUnlimited",1);
|
|
}
|
|
|
|
boolean isExpansionPet(obj_id petControlDevice)
|
|
{
|
|
if(hasObjVar(petControlDevice,"storageUnlimited"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean isMountPcd(obj_id pcd)
|
|
{
|
|
if(hasObjVar(pcd, "ai.pet.trainedMount"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean isPetType(obj_id pcd, int petType)
|
|
{
|
|
int pcdsPetType = getPcdPetType(pcd);
|
|
|
|
if(petType == pcdsPetType)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int getPcdPetType(obj_id pcd)
|
|
{
|
|
if(hasObjVar(pcd, "ai.pet.type"))
|
|
{
|
|
return getIntObjVar(pcd, "ai.pet.type");
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int getPetType(obj_id pet)
|
|
{
|
|
return getPetType(pet, obj_id.NULL_ID);
|
|
}
|
|
|
|
int getPetType(obj_id pet, obj_id pcd)
|
|
{
|
|
if( isFamiliarPetType(pet) )
|
|
{
|
|
return PET_TYPE_FAMILIAR;
|
|
}
|
|
|
|
if(!isValidId(pcd))
|
|
{
|
|
pcd = getPetControlDevice(pet);
|
|
if(isValidId(pcd) && exists(pcd))
|
|
{
|
|
if(hasObjVar(pcd, "ai.pet.trainedMount"))
|
|
return PET_TYPE_MOUNT;
|
|
}
|
|
}
|
|
|
|
if(ai_lib.isMonster(pet))
|
|
{
|
|
string creatureName = getCreatureName(pet);
|
|
if(creatureName == null)
|
|
return PET_TYPE_NPC;
|
|
|
|
if(utils.dataTableGetFloat(CREATURE_TABLE, creatureName, "aggressive") > 0.0f)
|
|
return PET_TYPE_AGGRO;
|
|
else
|
|
return PET_TYPE_NON_AGGRO;
|
|
}
|
|
else if(pet_lib.isDroidPet(pet))
|
|
{
|
|
return PET_TYPE_DROID;
|
|
}
|
|
|
|
return PET_TYPE_NPC;
|
|
}
|
|
|
|
int getPetType(string pet)
|
|
{
|
|
int niche = utils.dataTableGetInt(CREATURE_TABLE, pet, "niche");
|
|
|
|
if(niche == NICHE_DROID || niche == NICHE_ANDROID)
|
|
{
|
|
return PET_TYPE_DROID;
|
|
}
|
|
else if(niche == NICHE_MONSTER || niche == NICHE_HERBIVORE || niche == NICHE_CARNIVORE || niche == NICHE_PREDATOR)
|
|
{
|
|
if(utils.dataTableGetFloat(CREATURE_TABLE, pet, "aggressive") > 0.0f)
|
|
return PET_TYPE_AGGRO;
|
|
else
|
|
return PET_TYPE_NON_AGGRO;
|
|
}
|
|
|
|
return PET_TYPE_NPC;
|
|
}
|
|
|
|
boolean isFamiliarPetType(obj_id pet)
|
|
{
|
|
if( hasObjVar(pet, "ai.pet.type") )
|
|
{
|
|
int petType = getIntObjVar(pet, "ai.pet.type");
|
|
if ( petType == PET_TYPE_FAMILIAR )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string creatureName = getCreatureName(pet);
|
|
if( creatureName != null && creatureName.length() > 0 )
|
|
{
|
|
if ( isFamiliarPetTypeByCreatureName(creatureName) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean isFamiliarPetTypeByCreatureName(string creatureName)
|
|
{
|
|
if( creatureName != null && creatureName.length() > 0 )
|
|
{
|
|
int dataRow = dataTableSearchColumnForString(creatureName, "creatureName", FAMILAR_DATA_DATATABLE);
|
|
if ( dataRow >= 0 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
obj_id generatePetOfTypeFamiliar(string creatureName, obj_id player)
|
|
{
|
|
obj_id familiarControlDevice = obj_id.NULL_ID;
|
|
|
|
dictionary familiarData = dataTableGetRow(FAMILAR_DATA_DATATABLE, creatureName);
|
|
if ( familiarData != null && !familiarData.isEmpty() )
|
|
{
|
|
obj_id playerDatapad = utils.getPlayerDatapad(player);
|
|
if ( isIdValid(playerDatapad) )
|
|
{
|
|
string controlDeviceTemplate = familiarData.getString("controlDeviceTemplate");
|
|
familiarControlDevice = createObject(controlDeviceTemplate, playerDatapad, "");
|
|
if ( isIdValid(familiarControlDevice) )
|
|
{
|
|
setObjVar(familiarControlDevice, "pet.creatureName", creatureName);
|
|
attachScript(familiarControlDevice, "ai.pet_control_device");
|
|
setName(familiarControlDevice, getString(new string_id("mob/creature_names", creatureName)));
|
|
setObjVar(familiarControlDevice, "ai.pet.type", pet_lib.PET_TYPE_FAMILIAR);
|
|
|
|
int hasTricks = familiarData.getInt("hasTricks");
|
|
if ( hasTricks <= 0 )
|
|
{
|
|
setObjVar(familiarControlDevice, "familiar_noTricks", true);
|
|
}
|
|
|
|
return familiarControlDevice;
|
|
}
|
|
}
|
|
}
|
|
return familiarControlDevice;
|
|
}
|
|
|
|
boolean isRepairDroid(obj_id pet)
|
|
{
|
|
return (hasObjVar(pet, "ai.pet.isRepairDroid"));
|
|
}
|
|
|
|
boolean hasTamingMenuOption(obj_id pet, obj_id player)
|
|
{
|
|
if(pet_lib.hasMaster(pet))
|
|
return false;
|
|
|
|
if(utils.hasScriptVar(player,"pet.tameLoopNum"))
|
|
return false;
|
|
|
|
if(ai_lib.isInCombat(pet) || ai_lib.isAiDead(pet))
|
|
return false;
|
|
|
|
if(!hasSkill(player, "outdoors_creaturehandler_novice"))
|
|
return false;
|
|
|
|
if(!ai_lib.isMonster(pet))
|
|
{
|
|
detachScript(pet, "ai.pet_advance");
|
|
return false;//I am not a monster (shouldn't even have this script)
|
|
}
|
|
|
|
if(pet_lib.getChanceToTame(pet, player) <= 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//returns TRUE ifyou are in a valid location to call pets,
|
|
// returns false ifyou are not in a valid location to call pets
|
|
boolean isInValidUnpackLocation(obj_id master)
|
|
{
|
|
// cannot call pets in ships
|
|
if(isIdValid(space_transition.getContainingShip(master)))
|
|
{
|
|
if(space_utils.isInStation(master))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(hasObjVar(master, "fasttame"))
|
|
{
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because you have the fasttame objvar set");
|
|
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean isInRestrictedScene(obj_id master)
|
|
{
|
|
string scene = getCurrentSceneName();
|
|
string restrictionTable = "datatables/vehicle/vehicle_restrictions.iff";
|
|
string[] restrictedScenes = dataTableGetStringColumn(restrictionTable, 0);
|
|
int [] restrictions = dataTableGetIntColumn(restrictionTable, 2);
|
|
|
|
|
|
if (isIdValid(instance.getAreaInstanceController(master)) && !instance.vehicleAllowedInInstance(instance.getAreaInstanceController(master)))
|
|
{
|
|
sendSystemMessage(master, new string_id("instance", "no_vehicle"));
|
|
return true;
|
|
}
|
|
|
|
if(hasObjVar(master, space_dungeon.VAR_DUNGEON_ID) ||
|
|
(locations.isInRegion(master, "@dathomir_region_names:black_mesa") && buff.hasBuff(master, "death_troopers_no_vehicle")))
|
|
return true;
|
|
|
|
for(int i = 0; i < restrictedScenes.length; i++)
|
|
{
|
|
if(restrictedScenes[i].equals(scene))
|
|
{
|
|
if(restrictions[i] == 1)
|
|
{
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You are in a mount restricted scene.");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean canQuickUnpack(obj_id master)
|
|
{
|
|
if(isGod(master))
|
|
{
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can quickUnpack because you are in GOD MODE:");
|
|
return true;
|
|
}
|
|
|
|
if(hasObjVar(master, "fasttame"))
|
|
{
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because you have the fasttame objvar set");
|
|
|
|
return true;
|
|
}
|
|
|
|
location yourLoc = getLocation(master);
|
|
|
|
if(locations.isInMissionCity(yourLoc))
|
|
{
|
|
if(isIdValid(yourLoc.cell) && !city.isInCity(yourLoc))
|
|
return false;//ifyou are in a city and in a building then you can't call shit <-- then why no error message here?!
|
|
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because locations.isInMissionCity() return TRUE");
|
|
|
|
return true;
|
|
}
|
|
else if(isIdValid(yourLoc.cell))
|
|
{
|
|
//you're in a building, but since you aren't in town, then call-away:
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because you are in a building, not in town");
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
obj_id yourCamp = camping.getCurrentCamp(master);
|
|
|
|
if(isIdValid(yourCamp) && yourCamp.isLoaded())
|
|
{
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because camping.getCurrentCamp() return TRUE");
|
|
|
|
return true;
|
|
}
|
|
|
|
if(isNearResidence(master))
|
|
{
|
|
if(isGod(master))
|
|
sendSystemMessageTestingOnly(master, "GODMODE MSG: You can call a pet because isNearResidence() returned true");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
}//end canQuickUnpack
|
|
|
|
int getChanceToTame(obj_id pet, obj_id player)
|
|
{
|
|
// Get player's taming skill mod.
|
|
const string creatureName = getCreatureName(pet);
|
|
int petType = pet_lib.getPetType(pet);
|
|
int tamingSkillMod = 0;
|
|
|
|
if(petType == pet_lib.PET_TYPE_AGGRO)
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_aggro");
|
|
else
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_non_aggro");
|
|
|
|
tamingSkillMod += getEnhancedSkillStatisticModifier(player, "tame_bonus");
|
|
|
|
if(tamingSkillMod > 80)
|
|
tamingSkillMod = 80;
|
|
|
|
if(tamingSkillMod <= 0)
|
|
return 0;
|
|
|
|
int tameLevelSkillMod = getMaxTameLevel(player);
|
|
|
|
int level = create.calcCreatureLevel(creatureName);
|
|
|
|
return getChanceToTame(level, tamingSkillMod, tameLevelSkillMod);
|
|
}
|
|
|
|
int getChanceToTame(string petName, obj_id player)
|
|
{
|
|
// Get player's taming skill mod.
|
|
int petType = pet_lib.getPetType(petName);
|
|
int tamingSkillMod = 0;
|
|
|
|
if(petType == pet_lib.PET_TYPE_AGGRO)
|
|
{
|
|
LOG ("creature_taming", "tame_aggro = " + tamingSkillMod);
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_aggro");
|
|
}
|
|
else
|
|
{
|
|
LOG ("creature_taming", "tame_non_aggro = " + tamingSkillMod);
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_non_aggro");
|
|
}
|
|
|
|
tamingSkillMod += getEnhancedSkillStatisticModifier(player, "tame_bonus");
|
|
LOG ("creature_taming", "final tamingSkillMod = " + tamingSkillMod);
|
|
|
|
if(tamingSkillMod > 80)
|
|
tamingSkillMod = 80;
|
|
|
|
if(tamingSkillMod <= 0)
|
|
return 0;
|
|
|
|
int level = create.calcCreatureLevel(petName);
|
|
|
|
LOG ("creature_taming", "tamingSkillMod = " + tamingSkillMod);
|
|
LOG ("creature_taming", "creatureLevel = " + level);
|
|
|
|
int tameLevelSkillMod = getMaxTameLevel(player);
|
|
|
|
return getChanceToTame(level, tamingSkillMod, tameLevelSkillMod);
|
|
}
|
|
|
|
int getChanceToTame(int level, int petType, obj_id player)
|
|
{
|
|
// Get player's taming skill mod.
|
|
int tamingSkillMod = 0;
|
|
|
|
if(petType == pet_lib.PET_TYPE_AGGRO)
|
|
{
|
|
LOG ("creature_taming", "tame_aggro = " + tamingSkillMod);
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_aggro");
|
|
}
|
|
else
|
|
{
|
|
LOG ("creature_taming", "tame_non_aggro = " + tamingSkillMod);
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_non_aggro");
|
|
}
|
|
|
|
tamingSkillMod += getEnhancedSkillStatisticModifier(player, "tame_bonus");
|
|
LOG ("creature_taming", "final tamingSkillMod = " + tamingSkillMod);
|
|
|
|
if(tamingSkillMod > 80)
|
|
tamingSkillMod = 80;
|
|
|
|
if(tamingSkillMod <= 0)
|
|
return 0;
|
|
|
|
LOG ("creature_taming", "tamingSkillMod = " + tamingSkillMod);
|
|
LOG ("creature_taming", "creatureLevel = " + level);
|
|
|
|
int tameLevelSkillMod = getMaxTameLevel(player);
|
|
|
|
return getChanceToTame(level, tamingSkillMod, tameLevelSkillMod);
|
|
}
|
|
|
|
int getChanceToTame(int level, int skillMod, int tameLevelSkillMod)
|
|
{
|
|
//this caps your ability to tame based on your tame level skill mod:
|
|
if(level > tameLevelSkillMod)
|
|
return 0;
|
|
|
|
float chance = 0;
|
|
// Base chance.
|
|
chance = 40 + (33 * (skillMod/30.0f));
|
|
// Subtract creature level.
|
|
if(level > 30)
|
|
level *= 1.5;
|
|
chance -= level;
|
|
|
|
//Ensure that you have a chance to tame any pet you should be
|
|
// able to tame according to tame_level.
|
|
if(chance < 5)
|
|
chance = 5;
|
|
|
|
LOG ("creature_taming", "tameChance = " + chance);
|
|
|
|
return (int)chance;
|
|
}
|
|
|
|
void setControllerName(obj_id petControlDevice, obj_id pet)
|
|
{
|
|
string myName = getAssignedName(pet);
|
|
|
|
if(myName != null && myName != "")
|
|
{
|
|
if(myName != "null")
|
|
{
|
|
setName(petControlDevice, myName);
|
|
}
|
|
else
|
|
{
|
|
string crName = getCreatureName(pet);
|
|
debugServerConsoleMsg(petControlDevice, "WARNING: " + crName + " has an assigned name of Null - why is that?");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(myName != "null")
|
|
{
|
|
setName(petControlDevice, getString(getNameStringId(pet)));
|
|
}
|
|
else
|
|
{
|
|
string crName = getCreatureName(pet);
|
|
debugServerConsoleMsg(petControlDevice, "WARNING: " + crName + " has an assigned name of Null - why is that?");
|
|
}
|
|
}
|
|
}
|
|
|
|
void grantPetGrowthXp(obj_id pet, int xpTotal)
|
|
{
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
int growthStage = 10;
|
|
|
|
if(hasObjVar(pcd, "ai.petAdvance.growthStage"))
|
|
growthStage = getIntObjVar(pcd, "ai.petAdvance.growthStage");
|
|
|
|
int curXp = getIntObjVar(pcd, "ai.petAdvance.xpEarned");
|
|
int xpToLevel = getPetXpForNextLevel(pet);
|
|
|
|
curXp += xpTotal;
|
|
|
|
// Check to see ifpet grows
|
|
if(curXp > xpToLevel)
|
|
{
|
|
curXp -= xpToLevel;
|
|
growthStage += 1;
|
|
|
|
if(growthStage >= 10)
|
|
removeObjVar(pcd, "ai.petAdvance.growthStage");
|
|
else
|
|
setObjVar(pcd, "ai.petAdvance.growthStage", growthStage);
|
|
|
|
// Check to see ifthe pet gets a new ability slot
|
|
if(rand(0,100) < growthStage * 10)
|
|
{
|
|
earnPetTrainingAbility(pet);
|
|
}
|
|
|
|
// Grant the pet new training points
|
|
earnPetTrainingPoints(pet);
|
|
}
|
|
|
|
setObjVar(pcd, "ai.petAdvance.xpEarned", curXp);
|
|
|
|
// Check to see ifthe master learns wild pet ability
|
|
if(!hasObjVar(pcd, "pet.crafted"))
|
|
{
|
|
float chance = (growthStage * growthStage * growthStage) / 10f;
|
|
|
|
if(rand(0f, 100f) < chance)
|
|
learnPetAbility(pet);
|
|
}
|
|
}
|
|
|
|
int getPetXpForNextLevel(obj_id pet)
|
|
{
|
|
int level = getLevel(pet);
|
|
return getXpForNextLevel(level);
|
|
}
|
|
|
|
int getPcdXpForNextLevel(obj_id pcd)
|
|
{
|
|
string creatureName = getStringObjVar(pcd, "pet.creatureName");
|
|
|
|
int level = pet_lib.getAdultLevel(creatureName);
|
|
|
|
if(hasObjVar(pcd, "creature_attribs.level"))
|
|
{
|
|
level = getIntObjVar(pcd, "creature_attribs.level");
|
|
}
|
|
|
|
return getXpForNextLevel(level);
|
|
}
|
|
|
|
int getXpForNextLevel(int level)
|
|
{
|
|
float modifiedLevel = (level + 4) / 2f;
|
|
float levelSquared = modifiedLevel * modifiedLevel;
|
|
float total = levelSquared * 300;
|
|
return (int)total - 500;
|
|
}
|
|
|
|
int getMaxAbilitySlots(int level)
|
|
{
|
|
return (int)((level + 25) / 8f + 0.5f);
|
|
}
|
|
|
|
/*
|
|
ai.petAdvance.growthStage = Growth stage (1 - 10)
|
|
ai.petAdvance.xpEarned = Current XP
|
|
ai.petAbility.wildAbility = Innate wild ability
|
|
ai.petAbility.toBeEarned = Ability slots to be earned
|
|
ai.petAbility.available = Available ability slots
|
|
ai.petAbility.trainingPts = Available ability points
|
|
ai.petAbility.abilityList = Learned abilities
|
|
*/
|
|
void earnPetTrainingAbility(obj_id pet)
|
|
{
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
int toBeEarned = getIntObjVar(pcd, "ai.petAbility.toBeEarned");
|
|
int available = getIntObjVar(pcd, "ai.petAbility.available");
|
|
|
|
if(toBeEarned == 0)
|
|
return;
|
|
|
|
toBeEarned--;
|
|
available++;
|
|
|
|
setObjVar(pcd, "ai.petAbility.toBeEarned", toBeEarned);
|
|
setObjVar(pcd, "ai.petAbility.available", available);
|
|
|
|
obj_id master = getMaster(pet);
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "earn_new_ability"));
|
|
}
|
|
|
|
void earnPetTrainingPoints(obj_id pet)
|
|
{
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
// See ifpet is already fully trained
|
|
int available = getIntObjVar(pcd, "ai.petAbility.available");
|
|
int toBeEarned = getIntObjVar(pcd, "ai.petAbility.toBeEarned");
|
|
|
|
if(available == 0 && toBeEarned == 0)
|
|
return;
|
|
|
|
// Calc new training points
|
|
int level = getLevel(pet);
|
|
int newPts = (int)((level + 55) / 8f + 0.5f);
|
|
|
|
int trainingPts = getIntObjVar(pcd, "ai.petAbility.trainingPts");
|
|
trainingPts += newPts;
|
|
setObjVar(pcd, "ai.petAbility.trainingPts", trainingPts);
|
|
|
|
// Send message to player
|
|
obj_id master = getMaster(pet);
|
|
prose_package pp = new prose_package();
|
|
pp.stringId = new string_id("pet/pet_ability", "earn_training_points");
|
|
pp.digitInteger = newPts;
|
|
sendSystemMessageProse(master, pp);
|
|
}
|
|
|
|
void learnPetAbility(obj_id pet)
|
|
{
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
obj_id master = getMaster(pet);
|
|
|
|
string petAbility = getStringObjVar(pcd, "ai.petAbility.wildAbility");
|
|
|
|
if(petAbility == null || petAbility.equals(""))
|
|
return;
|
|
|
|
int abilityCrc = getStringCrc(petAbility.toLowerCase());
|
|
int[] abilityList = getIntArrayObjVar(master, "ch.petAbility.abilityList");
|
|
|
|
int row = dataTableSearchColumnForString(petAbility, "abilityName", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
{
|
|
LOG("pet_ability", "pet_lib::learnPetAbility() - ERROR: Unable to find pet ability in datatable");
|
|
return;
|
|
}
|
|
|
|
int[] newAbilityList = null;
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
if(utils.getElementPositionInArray(abilityList, abilityCrc) > -1)
|
|
return;
|
|
|
|
newAbilityList = new int[abilityList.length + 1];
|
|
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
newAbilityList[i] = abilityList[i];
|
|
|
|
newAbilityList[newAbilityList.length-1] = abilityCrc;
|
|
}
|
|
else
|
|
{
|
|
newAbilityList = new int[1];
|
|
newAbilityList[0] = abilityCrc;
|
|
}
|
|
|
|
setObjVar(master, "ch.petAbility.abilityList", newAbilityList);
|
|
removeObjVar(pcd, "ai.petAbility.wildAbility");
|
|
|
|
string msg = utils.packStringId(new string_id("pet/pet_ability", "learn_pet_ability_begin"));
|
|
msg += " \\#pcontrast2 "+utils.packStringId(new string_id("pet/pet_ability", petAbility));
|
|
msg += " \\#. "+utils.packStringId(new string_id("pet/pet_ability", "learn_pet_ability_end"));
|
|
sendSystemMessage(master, msg, null);
|
|
}
|
|
|
|
void initializeWildPetAbility(obj_id pet)
|
|
{
|
|
string creatureName = ai_lib.getCreatureName(pet);
|
|
if(creatureName == null || creatureName == "")
|
|
return;
|
|
|
|
string wildListName = dataTableGetString(create.CREATURE_TABLE, creatureName, "wildAbilityList");
|
|
|
|
if(wildListName == null || wildListName.equals("") || wildListName.equals("none"))
|
|
return;
|
|
|
|
dictionary abilityData = utils.dataTableGetRow(WILD_ABILITY_TABLE, wildListName);
|
|
|
|
if(abilityData == null)
|
|
return;
|
|
|
|
float wildAbilityChance = abilityData.getFloat("wildAbilityChance");
|
|
|
|
if(rand(0f, 100f) < wildAbilityChance)
|
|
{
|
|
string wildAbilityList = abilityData.getString("wildAbilityList");
|
|
|
|
string[] wildList = split(wildAbilityList, ',');
|
|
|
|
if(wildList != null && wildList.length > 0)
|
|
{
|
|
int idx = rand(0, wildList.length - 1);
|
|
|
|
int row = dataTableSearchColumnForString(wildList[idx], "abilityName", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
return;
|
|
|
|
setObjVar(pet, "ai.petAbility.wildAbility", wildList[idx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void validateFollowTarget(obj_id pet, obj_id target)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
if(target == master)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(stealth.hasServerCoverState(target))
|
|
{
|
|
doStayCommand(pet,master);
|
|
doConfusedEmote(pet);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void trainPetAbility(obj_id pet, string abilityName)
|
|
{
|
|
int ability = getStringCrc(abilityName.toLowerCase());
|
|
|
|
trainPetAbility(pet, ability);
|
|
}
|
|
|
|
void trainPetAbility(obj_id pet, int ability)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet))
|
|
{
|
|
sendSystemMessage(getSelf(), new string_id("pet/pet_ability", "train_ability_failed"));
|
|
return;
|
|
}
|
|
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!hasSkill(master, "outdoors_creaturehandler_novice"))
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "train_ability_ch_only"));
|
|
|
|
return;
|
|
}
|
|
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList == null)
|
|
abilityList = new int[0];
|
|
|
|
if(utils.getElementPositionInArray(abilityList, ability) > -1)
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "train_ability_already_known"));
|
|
return;
|
|
}
|
|
|
|
int row = dataTableSearchColumnForInt(ability, "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
{
|
|
LOG("pet_ability", "pet_lib::trainPetAbility() - ERROR: Unable to find pet ability in datatable");
|
|
return;
|
|
}
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
// Check for ability slots
|
|
int available = getIntObjVar(pcd, "ai.petAbility.available");
|
|
|
|
if(available < 1)
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "train_ability_no_slots"));
|
|
return;
|
|
}
|
|
|
|
// Check for training points
|
|
int trainingCost = abilityData.getInt("trainingCost");
|
|
int trainingPts = getIntObjVar(pcd, "ai.petAbility.trainingPts");
|
|
|
|
if(trainingPts < trainingCost)
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_ability", "train_ability_no_points"));
|
|
return;
|
|
}
|
|
|
|
// Check for prereq and train
|
|
string prereq = abilityData.getString("prereq");
|
|
int[] newAbilityList = null;
|
|
|
|
if(prereq == null || prereq.equals(""))
|
|
{
|
|
// No prereq add new ability to list
|
|
newAbilityList = new int[abilityList.length + 1];
|
|
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
newAbilityList[i] = abilityList[i];
|
|
|
|
newAbilityList[newAbilityList.length-1] = ability;
|
|
}
|
|
else
|
|
{
|
|
// Needs prereq, check for it
|
|
int prereqCrc = getStringCrc(prereq.toLowerCase());
|
|
int idx = -1;
|
|
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
if(abilityList[i] == prereqCrc)
|
|
{
|
|
idx = i;
|
|
}
|
|
}
|
|
|
|
if(idx == -1)
|
|
{
|
|
// Missing prereq
|
|
string msg = utils.packStringId(new string_id("pet/pet_ability", "need_prereq_begin"));
|
|
msg += " \\#pcontrast2 "+utils.packStringId(new string_id("pet/pet_ability", prereq));
|
|
msg += " \\#. "+utils.packStringId(new string_id("pet/pet_ability", "need_prereq_end"));
|
|
sendSystemMessage(master, msg, null);
|
|
return;
|
|
}
|
|
|
|
newAbilityList = abilityList;
|
|
newAbilityList[idx] = ability;
|
|
}
|
|
|
|
setObjVar(pcd, "ai.petAbility.abilityList", newAbilityList);
|
|
setObjVar(pcd, "ai.petAbility.available", available - 1);
|
|
setObjVar(pcd, "ai.petAbility.trainingPts", trainingPts - trainingCost);
|
|
|
|
string msg = utils.packStringId(new string_id("pet/pet_ability", "train_ability_begin"));
|
|
msg += " \\#pcontrast1 "+utils.packStringId(new string_id("pet/pet_ability", abilityData.getString("abilityName")));
|
|
msg += " \\#. "+utils.packStringId(new string_id("pet/pet_ability", "train_ability_end"));
|
|
sendSystemMessage(master, msg, null);
|
|
}
|
|
|
|
string getPetAbilityFromCrc(int ability)
|
|
{
|
|
int row = dataTableSearchColumnForInt(ability, "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
return "";
|
|
|
|
return dataTableGetString(PET_ABILITY_TABLE, row, "abilityName");
|
|
}
|
|
|
|
void initializeTrainedPetAbilities(obj_id pcd, obj_id pet)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList == null)
|
|
return;
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
continue;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
if(abilityData == null)
|
|
continue;
|
|
|
|
switch(abilityData.getInt("type"))
|
|
{
|
|
case PET_ABILITY_INNATE:
|
|
addInnateAbilityStatBonus(pet, abilityData);
|
|
addInnateScriptVar(pet, abilityData);
|
|
addInnateBuff(pet, abilityData);
|
|
break;
|
|
|
|
case PET_ABILITY_TRIGGERED:
|
|
addTriggeredAbility(pet, abilityData);
|
|
break;
|
|
|
|
case PET_ABILITY_ACTION:
|
|
addActionAbility(pet, abilityData);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void addInnateAbilityStatBonus(obj_id pet, dictionary abilityData)
|
|
{
|
|
int stat = abilityData.getInt("statMod");
|
|
|
|
if(stat == -1)
|
|
return;
|
|
|
|
int statValBonus = abilityData.getInt("statModVal");
|
|
float statPctBonus = abilityData.getFloat("statModPct");
|
|
|
|
int curStat = getMaxAttrib(pet, stat);
|
|
int newStat = curStat + (int)(curStat*statPctBonus) + statValBonus;
|
|
|
|
setMaxAttrib(pet, stat, newStat);
|
|
setAttrib(pet, stat, newStat);
|
|
}
|
|
|
|
void addInnateScriptVar(obj_id pet, dictionary abilityData)
|
|
{
|
|
string varName = abilityData.getString("scriptVar");
|
|
|
|
if(varName == null || varName.equals(""))
|
|
return;
|
|
|
|
float varValue = abilityData.getFloat("scriptVarVal");
|
|
deltadictionary dd = pet.getScriptVars();
|
|
|
|
if(pet.hasScriptVar(varName))
|
|
{
|
|
Object curValue = dd.getObject(varName);
|
|
|
|
if(curValue instanceof Integer)
|
|
{
|
|
int newValue = ((Integer)curValue).intValue() + (int)varValue;
|
|
dd.put(varName, varValue);
|
|
}
|
|
else if(curValue instanceof Float)
|
|
{
|
|
float newValue = ((Float)curValue).floatValue() + varValue;
|
|
dd.put(varName, varValue);
|
|
}
|
|
else
|
|
{
|
|
LOG("pet_ability", "Scriptvar name already present with unknown type, leaving current scriptvar");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dd.put(varName, varValue);
|
|
}
|
|
}
|
|
|
|
void addInnateBuff(obj_id pet, dictionary abilityData)
|
|
{
|
|
string buffName = abilityData.getString("selfBuffName");
|
|
|
|
if(buffName == null || buffName.equals(""))
|
|
return;
|
|
|
|
int duration = abilityData.getInt("duration");
|
|
|
|
if(duration > 0)
|
|
buff.applyBuff(pet, buffName, duration);
|
|
else
|
|
buff.applyBuff(pet, buffName);
|
|
}
|
|
|
|
void addTriggeredAbility(obj_id pet, dictionary abilityData)
|
|
{
|
|
string actionName = abilityData.getString("actionName");
|
|
|
|
if(actionName != null && !actionName.equals(""))
|
|
grantCommand(pet, actionName);
|
|
}
|
|
|
|
void addActionAbility(obj_id pet, dictionary abilityData)
|
|
{
|
|
string actionName = abilityData.getString("actionName");
|
|
|
|
if(actionName != null && !actionName.equals(""))
|
|
grantCommand(pet, actionName);
|
|
}
|
|
|
|
int getPetAbilityHealthBonus(obj_id pcd, int baseHealth)
|
|
{
|
|
int healthBonus = 0;
|
|
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
continue;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
if(abilityData == null)
|
|
continue;
|
|
|
|
if(abilityData.getInt("statMod") == HEALTH)
|
|
{
|
|
int statValBonus = abilityData.getInt("statModVal");
|
|
float statPctBonus = abilityData.getFloat("statModPct");
|
|
|
|
healthBonus += (int)(baseHealth*statPctBonus) + statValBonus;
|
|
baseHealth += healthBonus;
|
|
}
|
|
}
|
|
}
|
|
|
|
return healthBonus;
|
|
}
|
|
|
|
int getPetAbilityRegenBonus(obj_id pcd, int baseRegen)
|
|
{
|
|
int regenBonus = 0;
|
|
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
continue;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
if(abilityData == null)
|
|
continue;
|
|
|
|
if(abilityData.getInt("statMod") == CONSTITUTION)
|
|
{
|
|
int statValBonus = abilityData.getInt("statModVal");
|
|
float statPctBonus = abilityData.getFloat("statModPct");
|
|
|
|
regenBonus += (int)(baseRegen*statPctBonus) + statValBonus;
|
|
baseRegen += regenBonus;
|
|
}
|
|
}
|
|
}
|
|
|
|
return regenBonus;
|
|
}
|
|
|
|
int[] getPetAbilityDamageBonus(obj_id pcd, int baseMinDamage, int baseMaxDamage)
|
|
{
|
|
int[] damageBonus = { 0, 0 };
|
|
float dmgPctBonus = 0f;
|
|
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
if(row == -1)
|
|
continue;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
if(abilityData == null)
|
|
continue;
|
|
|
|
string scriptVar = abilityData.getString("scriptVar");
|
|
|
|
if(scriptVar != null && scriptVar.equals("ai.combat.damagePctBonus"))
|
|
{
|
|
dmgPctBonus += abilityData.getFloat("scriptVarVal");
|
|
}
|
|
}
|
|
}
|
|
|
|
damageBonus[0] = (int)(baseMinDamage * dmgPctBonus);
|
|
damageBonus[1] = (int)(baseMaxDamage * dmgPctBonus);
|
|
|
|
return damageBonus;
|
|
}
|
|
|
|
float getPetAbilityAttackSpdBonus(obj_id pcd, float baseSpeed)
|
|
{
|
|
float speedBonus = 0f;
|
|
float spdPctBonus = 0f;
|
|
|
|
int[] abilityList = getIntArrayObjVar(pcd, "ai.petAbility.abilityList");
|
|
|
|
if(abilityList != null && abilityList.length > 0)
|
|
{
|
|
for(int i = 0; i < abilityList.length; i++)
|
|
{
|
|
int row = dataTableSearchColumnForInt(abilityList[i], "abilityCrc", PET_ABILITY_TABLE);
|
|
|
|
if(row == -1)
|
|
continue;
|
|
|
|
dictionary abilityData = dataTableGetRow(PET_ABILITY_TABLE, row);
|
|
|
|
if(abilityData == null)
|
|
continue;
|
|
|
|
string scriptVar = abilityData.getString("scriptVar");
|
|
|
|
if(scriptVar != null && scriptVar.equals("ai.combat.attackSpdPctBonus"))
|
|
{
|
|
spdPctBonus += abilityData.getFloat("scriptVarVal");
|
|
}
|
|
}
|
|
}
|
|
|
|
speedBonus = -1 * (baseSpeed * spdPctBonus);
|
|
|
|
return speedBonus;
|
|
}
|
|
|
|
boolean isPetFrenzied(obj_id pet)
|
|
{
|
|
return (utils.hasScriptVar(pet, "buff.petFrenzy_1.value") ||
|
|
utils.hasScriptVar(pet, "buff.petFrenzy_2.value") ||
|
|
utils.hasScriptVar(pet, "buff.petFrenzy_3.value"));
|
|
}
|
|
|
|
float getPetFrenzyAmount(obj_id pet)
|
|
{
|
|
if(utils.hasScriptVar(pet, "buff.petFrenzy_3.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petFrenzy_3.value");
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petFrenzy_2.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petFrenzy_2.value");
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petFrenzy_1.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petFrenzy_1.value");
|
|
|
|
return 0f;
|
|
}
|
|
|
|
boolean isPetDefenseStance(obj_id pet)
|
|
{
|
|
return (utils.hasScriptVar(pet, "buff.petDefense_1.value") ||
|
|
utils.hasScriptVar(pet, "buff.petDefense_2.value") ||
|
|
utils.hasScriptVar(pet, "buff.petDefense_3.value"));
|
|
}
|
|
|
|
float getPetDefenseStanceAmount(obj_id pet)
|
|
{
|
|
if(utils.hasScriptVar(pet, "buff.petDefense_3.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petDefense_3.value");
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petDefense_2.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petDefense_2.value");
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petDefense_1.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petDefense_1.value");
|
|
|
|
return 0f;
|
|
}
|
|
|
|
boolean isPetDefenseBreak(obj_id pet)
|
|
{
|
|
return (utils.hasScriptVar(pet, "buff.petDefenseBreakAttack.value") ||
|
|
utils.hasScriptVar(pet, "buff.petDefenseBreakAttack_1.value"));
|
|
}
|
|
|
|
float getPetDefenseBreakAmount(obj_id pet)
|
|
{
|
|
if(utils.hasScriptVar(pet, "buff.petDefenseBreakAttack_1.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petDefenseBreakAttack_1.value");
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petDefenseBreakAttack.value"))
|
|
return utils.getFloatScriptVar(pet, "buff.petDefenseBreakAttack.value");
|
|
|
|
return 0f;
|
|
}
|
|
|
|
obj_id getShieldingPet(obj_id player)
|
|
{
|
|
float closestPet = 10f; // Pet must be closer than 10m to shield
|
|
|
|
obj_id pet = callable.getCallable(player, callable.CALLABLE_TYPE_COMBAT_PET);
|
|
|
|
if(!isIdValid(pet) || !exists(pet) || !isPetShielding(pet))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return pet;
|
|
}
|
|
|
|
boolean isPetShielding(obj_id pet)
|
|
{
|
|
return (utils.hasScriptVar(pet, "buff.petShield_1.value") ||
|
|
utils.hasScriptVar(pet, "buff.petShield_2.value") ||
|
|
utils.hasScriptVar(pet, "buff.petShield_3.value"));
|
|
}
|
|
|
|
float getPetShieldAmount(obj_id pet)
|
|
{
|
|
if(utils.hasScriptVar(pet, "buff.petShield_3.value"))
|
|
{
|
|
return utils.getFloatScriptVar(pet, "buff.petShield_3.value");
|
|
}
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petShield_2.value"))
|
|
{
|
|
return utils.getFloatScriptVar(pet, "buff.petShield_2.value");
|
|
}
|
|
|
|
if(utils.hasScriptVar(pet, "buff.petShield_1.value"))
|
|
{
|
|
return utils.getFloatScriptVar(pet, "buff.petShield_1.value");
|
|
}
|
|
|
|
return 0f;
|
|
}
|
|
|
|
// TODO: Remove: Old bio-engineer code
|
|
void setPetStatsByGrowth(obj_id pet, int growthStage)
|
|
{
|
|
|
|
}
|
|
|
|
// TODO: Remove: Old bio-engineer code
|
|
void setCraftedPetStatsByGrowth(obj_id petControlDevice, obj_id pet, int growthStage)
|
|
{
|
|
setObjVar (pet, "combat.intCombatXP", 0);
|
|
|
|
if(growthStage < 1)
|
|
{
|
|
growthStage = 1;
|
|
}
|
|
else if(growthStage > 10)
|
|
{
|
|
growthStage = 10;
|
|
}
|
|
|
|
//first, get the base stats:
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName == null)
|
|
{
|
|
return; //wtf is this?
|
|
}
|
|
|
|
dictionary creatureDict = utils.dataTableGetRow(create.CREATURE_TABLE, creatureName);
|
|
|
|
if(creatureDict == null)
|
|
{
|
|
return; //gah!
|
|
}
|
|
|
|
//difficulty:
|
|
int level = getIntObjVar(petControlDevice, "creature_attribs.level");
|
|
|
|
if(level == 0)
|
|
{
|
|
level = getLevel(pet);
|
|
}
|
|
|
|
if(level < 1)
|
|
{
|
|
level = 1;
|
|
}
|
|
|
|
obj_id player = utils.getContainingPlayer(petControlDevice);
|
|
int myHealth = getIntObjVar(petControlDevice, "creature_attribs." + create.MAXATTRIBNAMES[HEALTH]);
|
|
int healthRegen = getIntObjVar(petControlDevice, "creature_attribs." + create.MAXATTRIBNAMES[CONSTITUTION]);
|
|
|
|
int[] armorData = new int[10];
|
|
armorData[1] = getIntObjVar(petControlDevice, "creature_attribs.general_protection");
|
|
|
|
float stateResist = getFloatObjVar(petControlDevice, "creature_attribs.stateResist");
|
|
float critChance = getFloatObjVar(petControlDevice, "creature_attribs.critChance");
|
|
float critSave = getFloatObjVar(petControlDevice, "creature_attribs.critSave");
|
|
float aggroBonus = getFloatObjVar(petControlDevice, "creature_attribs.aggroBonus");
|
|
int toHit = getIntObjVar(petControlDevice, "creature_attribs.toHitChance") - 5;
|
|
int defenseValue = getIntObjVar(petControlDevice, "creature_attribs.defenseValue") - 5; // Subtract five to compensate for the values in creatures datatable.
|
|
float wpnSpeed = getFloatObjVar(petControlDevice, "creature_attribs.attackSpeed");
|
|
float spdBonus = getPetAbilityAttackSpdBonus(petControlDevice, wpnSpeed);
|
|
int minDamage = getIntObjVar(petControlDevice, "creature_attribs.minDamage");
|
|
int maxDamage = getIntObjVar(petControlDevice, "creature_attribs.maxDamage");
|
|
|
|
if(isIdValid(player) && exists(player))
|
|
{
|
|
if(level > 60 && !craftinglib.isTrader(player))
|
|
{
|
|
level = 60;
|
|
}
|
|
|
|
dictionary droidDefaultStatsDict = dataTableGetRow(TBL_MOB_STAT_BALANCE, level - 1);
|
|
|
|
myHealth = droidDefaultStatsDict.getInt("HP");
|
|
toHit = droidDefaultStatsDict.getInt("ToHit") - 5;
|
|
defenseValue = droidDefaultStatsDict.getInt("Def") - 5;
|
|
|
|
float creatureAttribs_dps = droidDefaultStatsDict.getFloat("damagePerSecond");
|
|
|
|
minDamage = (int)creatureAttribs_dps;
|
|
maxDamage = (int)creatureAttribs_dps * 3;
|
|
}
|
|
|
|
setLevel(pet, level);
|
|
utils.setScriptVar(pet, "ai.level", level);
|
|
|
|
if(hasObjVar(pet, "difficultyClass"))
|
|
{
|
|
removeObjVar(pet, "difficultyClass");
|
|
}
|
|
|
|
//HEALTH:
|
|
if(myHealth < 1)
|
|
{
|
|
myHealth = 1;
|
|
}
|
|
|
|
setMaxAttrib(pet, HEALTH, myHealth);
|
|
setAttrib(pet, HEALTH, myHealth);
|
|
|
|
//ACTION:
|
|
setMaxAttrib(pet, ACTION, 1000);
|
|
setAttrib(pet, ACTION, 1000);
|
|
|
|
//MIND:
|
|
setMaxAttrib(pet, MIND, 1000);
|
|
setAttrib(pet, MIND, 1000);
|
|
|
|
//REGEN:
|
|
fixMinRegenStats(pet);
|
|
|
|
if(!ai_lib.isDroid(pet) && !ai_lib.isAndroid(pet))
|
|
{
|
|
|
|
if(healthRegen < 1)
|
|
{
|
|
healthRegen = 1;
|
|
}
|
|
|
|
setRegenRate(pet, CONSTITUTION, 150);
|
|
}
|
|
|
|
// Armor
|
|
if(armorData != null)
|
|
{
|
|
create.initializeArmor(pet, armorData);
|
|
}
|
|
|
|
//scale:
|
|
float baseScale = 1.0f;
|
|
|
|
if(utils.hasScriptVar(pet, "ai.baseScale"))
|
|
{
|
|
baseScale = utils.getFloatScriptVar(pet, "ai.baseScale");
|
|
}
|
|
|
|
float minScale = 0;
|
|
float maxScale = 9999;
|
|
float scaleAdjust = 1;
|
|
|
|
if(hasObjVar(petControlDevice, "creature_attribs.scale"))
|
|
{
|
|
scaleAdjust = getFloatObjVar(petControlDevice, "creature_attribs.scale");
|
|
}
|
|
else
|
|
{
|
|
minScale = creatureDict.getFloat("minScale");
|
|
maxScale = creatureDict.getFloat("maxScale");
|
|
scaleAdjust = (minScale + maxScale) / 2.0f;
|
|
}
|
|
|
|
float realScale = baseScale * scaleAdjust;
|
|
float babyScale = realScale * .3f;
|
|
float scaleIncrement = (realScale - babyScale) / 10.0f;
|
|
float finalScale = babyScale + (scaleIncrement * growthStage);
|
|
|
|
if(finalScale > maxScale)
|
|
{
|
|
finalScale = maxScale;
|
|
}
|
|
|
|
if(finalScale < babyScale)
|
|
{
|
|
finalScale = babyScale;
|
|
}
|
|
|
|
setScale(pet, finalScale);
|
|
|
|
// state resist
|
|
utils.setScriptVar(pet, "ai.combat.stateResist", stateResist);
|
|
|
|
// crit chance
|
|
utils.setScriptVar(pet, "ai.combat.critChance", critChance);
|
|
|
|
// crit save
|
|
utils.setScriptVar(pet, "ai.combat.critSave", critSave);
|
|
|
|
// aggro bonus
|
|
utils.setScriptVar(pet, "ai.combat.aggroBonus", aggroBonus);
|
|
|
|
// to hit:
|
|
create.applySkillStatisticModifiers(pet , toHit, defenseValue);
|
|
|
|
//weapons: lets make sure speed never goes to 0 like droids which lack this objvar
|
|
if(wpnSpeed <= 1.0f)
|
|
{
|
|
wpnSpeed = 1.0f;
|
|
}
|
|
|
|
wpnSpeed += spdBonus;
|
|
|
|
int[] dmgBonus = getPetAbilityDamageBonus(petControlDevice, minDamage, maxDamage);
|
|
minDamage += dmgBonus[0];
|
|
maxDamage += dmgBonus[1];
|
|
|
|
if(minDamage < 5)
|
|
{
|
|
minDamage = 5;
|
|
}
|
|
|
|
if(maxDamage < 10)
|
|
{
|
|
maxDamage = 10;
|
|
}
|
|
|
|
obj_id creatureWeapon = getCurrentWeapon(pet);
|
|
setWeaponMaxDamage(creatureWeapon, maxDamage);
|
|
setWeaponMinDamage(creatureWeapon, minDamage);
|
|
setWeaponAttackSpeed(creatureWeapon, wpnSpeed);
|
|
weapons.setWeaponData(creatureWeapon);
|
|
|
|
creatureWeapon = aiGetPrimaryWeapon(pet);
|
|
|
|
if(isIdValid(creatureWeapon))
|
|
{
|
|
setWeaponMaxDamage(creatureWeapon, maxDamage);
|
|
setWeaponMinDamage(creatureWeapon, minDamage);
|
|
setWeaponAttackSpeed(creatureWeapon, wpnSpeed);
|
|
weapons.setWeaponData(creatureWeapon);
|
|
}
|
|
|
|
creatureWeapon = aiGetSecondaryWeapon(pet);
|
|
|
|
if(isIdValid(creatureWeapon))
|
|
{
|
|
setWeaponMaxDamage(creatureWeapon, maxDamage);
|
|
setWeaponMinDamage(creatureWeapon, minDamage);
|
|
setWeaponAttackSpeed(creatureWeapon, wpnSpeed);
|
|
weapons.setWeaponData(creatureWeapon);
|
|
}
|
|
|
|
// Ranged Weapon
|
|
string rangedWeaponName = getStringObjVar(petControlDevice, "creature_attribs." + bio_engineer.ATTRIB_DICT_RANGED_WEAPON);
|
|
|
|
if(rangedWeaponName != null && rangedWeaponName != "")
|
|
{
|
|
obj_id inventory = getObjectInSlot(pet, "inventory");
|
|
|
|
if(isIdValid(inventory))
|
|
{
|
|
obj_id rangedWeapon = createObject(rangedWeaponName, inventory, "");
|
|
|
|
if(isIdValid(rangedWeapon))
|
|
{
|
|
setWeaponAttackSpeed(rangedWeapon, wpnSpeed);
|
|
setWeaponMaxDamage(rangedWeapon, maxDamage);
|
|
setWeaponMinDamage(rangedWeapon, minDamage);
|
|
weapons.setWeaponData(creatureWeapon);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special Attacks
|
|
int[] specialAttacks = new int[2];
|
|
specialAttacks[0] = getIntObjVar(petControlDevice, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_1);
|
|
specialAttacks[1] = getIntObjVar(petControlDevice, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_2);
|
|
utils.setScriptVar(pet, "ai.combat.creatureSpecialAttacks", specialAttacks);
|
|
|
|
// Set customization info unless it's a helper droid
|
|
if(!isNewPlayerHelperDroid(pet))
|
|
{
|
|
int colorIdx = hue.getVarColorIndex(petControlDevice, hue.INDEX_1);
|
|
|
|
if(hasObjVar(petControlDevice, "creature_attribs.hue"))
|
|
{
|
|
colorIdx = getIntObjVar(petControlDevice, "creature_attribs.hue");
|
|
}
|
|
|
|
hue.setColor(pet, hue.INDEX_1, colorIdx);
|
|
}
|
|
}
|
|
|
|
void fixMinRegenStats(obj_id pet)
|
|
{
|
|
//fix the pools to bypass Dan's hack:
|
|
int healthAttrib = 0;
|
|
|
|
if(!ai_lib.isDroid(pet) && !ai_lib.isAndroid(pet))
|
|
healthAttrib = calcRegenStat(getMaxAttrib(pet, HEALTH));
|
|
|
|
setRegenRate(pet, CONSTITUTION, 150);
|
|
|
|
setRegenRate(pet, STAMINA, 150);
|
|
|
|
setRegenRate(pet, WILLPOWER, 150);
|
|
}
|
|
|
|
int calcRegenStat(int poolStat)
|
|
{
|
|
int regenStat = poolStat/10;
|
|
|
|
if(regenStat > MAX_REGEN_STAT)
|
|
regenStat = MAX_REGEN_STAT;
|
|
else if(regenStat < 1)
|
|
regenStat = 1;
|
|
|
|
return regenStat;
|
|
}
|
|
|
|
boolean isNearResidence(obj_id master)
|
|
{
|
|
obj_id yourHome = player_structure.getResidence(master);
|
|
|
|
if(!isIdValid(yourHome))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!yourHome.isLoaded())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (getDistance(master, yourHome) < 60.0f);
|
|
}
|
|
|
|
boolean wasInCombatRecently(obj_id currentPet, obj_id master, boolean systemMessage)
|
|
{
|
|
if(!utils.hasScriptVar(currentPet, "pet.combatEnded"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int combatEnded = utils.getIntScriptVar(currentPet, "pet.combatEnded");
|
|
int canStoreAgain = combatEnded + 60;//60-second delay to store post-combat
|
|
int currentTime = getGameTime();
|
|
|
|
if(currentTime >= canStoreAgain)
|
|
{
|
|
return false;//has not been in combat for at least 2 minuts
|
|
}
|
|
|
|
if(systemMessage)
|
|
{
|
|
int timeLeft = canStoreAgain - currentTime;
|
|
|
|
if(timeLeft > 1)
|
|
{
|
|
prose_package pp = prose.getPackage(new string_id("pet/pet_menu","prose_cant_store_yet") , timeLeft);
|
|
sendSystemMessageProse(master, pp);
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(master, new string_id("pet/pet_menu","cant_store_1sec"));
|
|
}
|
|
}
|
|
|
|
return true;//was in combat recently
|
|
}
|
|
|
|
//Adding a second check for the player now that camp restrictions have been removed.
|
|
boolean wasInCombat(obj_id player)
|
|
{
|
|
if(!utils.hasScriptVar(player, COMBAT_ENDED))
|
|
return false;
|
|
|
|
int combatEnded = utils.getIntScriptVar(player, COMBAT_ENDED);
|
|
int callPetDelay = combatEnded + POST_COMBAT_DELAY;
|
|
int currentTime = getGameTime();
|
|
|
|
if(currentTime >= callPetDelay)
|
|
return false;
|
|
|
|
int timeLeft = callPetDelay - currentTime;
|
|
return true;
|
|
|
|
}//end wasInCombat
|
|
|
|
int modifyCallTime(obj_id player, string combatEnded, int callDelay, int combatDelay)
|
|
{
|
|
int combatEnd = utils.getIntScriptVar(player, combatEnded);
|
|
int combatEndDifference = getGameTime() - combatEnd;
|
|
int combatDelayDifference = combatDelay - combatEndDifference;
|
|
|
|
if(combatDelayDifference < callDelay)
|
|
return callDelay;
|
|
|
|
if(combatDelayDifference > combatDelay)
|
|
return combatDelay;
|
|
|
|
return combatDelayDifference;
|
|
|
|
}//end modifyCallTime
|
|
|
|
int getTimeLeft(obj_id player, string toCheckFor, string modifiedTime)
|
|
{
|
|
|
|
int currentTime = getGameTime();
|
|
int timeCalled = utils.getIntScriptVar(player, toCheckFor);
|
|
|
|
if(timeCalled < 1)
|
|
return -1;
|
|
|
|
int timePenalty = utils.getIntScriptVar(player, modifiedTime);
|
|
int timeDone = timeCalled + timePenalty;
|
|
int timeLeft = timeDone - currentTime;
|
|
|
|
return timeLeft;
|
|
}
|
|
|
|
int getCurrentPetLevels(obj_id master)
|
|
{
|
|
if(!isIdValid(master))
|
|
return 0;
|
|
|
|
if(!isPlayer(master))
|
|
return 0;
|
|
|
|
if(!callable.hasCallable(master, callable.CALLABLE_TYPE_COMBAT_PET))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
obj_id pet = callable.getCallable(master, callable.CALLABLE_TYPE_COMBAT_PET);
|
|
|
|
return getLevel(pet);
|
|
}
|
|
|
|
boolean canControlPetsOfLevel(obj_id player, int petType, int petLevel, string creatureName)
|
|
{
|
|
if(!hasSkill(player, "outdoors_creaturehandler_novice"))
|
|
return true;//the level and number of CH pets is determined differently for non-CH's
|
|
|
|
if(petType != PET_TYPE_NON_AGGRO && petType != PET_TYPE_AGGRO)
|
|
return true;//whether non-creature pets can be called or not is determined differently
|
|
|
|
if(creatureName==null)
|
|
return false;//dunno what the hell this is, so you can't have it
|
|
|
|
int tameLevelSkillMod = getMaxTameLevel(player);
|
|
|
|
int tamingSkillMod = 0;
|
|
|
|
if(petType == pet_lib.PET_TYPE_AGGRO)
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_aggro");
|
|
else
|
|
tamingSkillMod = getEnhancedSkillStatisticModifier(player, "tame_non_aggro");
|
|
|
|
tamingSkillMod += getEnhancedSkillStatisticModifier(player, "tame_bonus");
|
|
|
|
int chanceToTame = getChanceToTame(petLevel, tamingSkillMod, tameLevelSkillMod);
|
|
|
|
if(chanceToTame < 1)
|
|
{
|
|
sendSystemMessage(player, new string_id("pet/pet_menu", "sys_lack_skill"));
|
|
return false;//You have no chance
|
|
}
|
|
|
|
int numOutNow = getCurrentPetLevels(player);
|
|
|
|
if((numOutNow + petLevel) > tameLevelSkillMod)
|
|
{
|
|
|
|
if(numOutNow == 0)
|
|
{
|
|
sendSystemMessage(player, new string_id("pet/pet_menu", "too_hard"));
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(player, new string_id("pet/pet_menu", "control_exceeded"));
|
|
}
|
|
|
|
return false;//this would put you over the max
|
|
}
|
|
return true;//ok to call this
|
|
}
|
|
|
|
int getMaxTameLevel(obj_id player)
|
|
{
|
|
int baseTameLevel = getSkillStatMod(player, "tame_level");
|
|
int bonusTameLevel = getEnhancedSkillStatisticModifier(player, "tame_level_bonus");
|
|
int playerLevel = getLevel(player);
|
|
|
|
if(baseTameLevel + bonusTameLevel > playerLevel)
|
|
{
|
|
// Basic skill mods from CH tree can surpass player level
|
|
if(baseTameLevel > playerLevel)
|
|
return baseTameLevel;
|
|
|
|
// Cap tame level with bonus at the players level
|
|
bonusTameLevel = playerLevel - baseTameLevel;
|
|
if(bonusTameLevel < 0) bonusTameLevel = 0;
|
|
}
|
|
|
|
return (baseTameLevel + bonusTameLevel);
|
|
}
|
|
|
|
int getModifiedAttribDamage(obj_id target, int attrib)
|
|
{
|
|
int max = getWoundedMaxAttrib(target, attrib);
|
|
int current = getAttrib(target, attrib);
|
|
if(max == ATTRIB_ERROR || current == ATTRIB_ERROR)
|
|
return ATTRIB_ERROR;
|
|
return max - current;
|
|
}
|
|
|
|
void storeDamage(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(!isIdValid(pet) || !isIdValid(petControlDevice))
|
|
return;
|
|
|
|
int[] mainAttribs = new int[6];
|
|
|
|
mainAttribs[HEALTH_DAMAGE] = getModifiedAttribDamage(pet, HEALTH);
|
|
mainAttribs[ACTION_DAMAGE] = getModifiedAttribDamage(pet, ACTION);
|
|
mainAttribs[MIND_DAMAGE] = getModifiedAttribDamage(pet, MIND);
|
|
|
|
mainAttribs[HEALTH_WOUND] = getAttribWound(pet, HEALTH);
|
|
|
|
if(petControlDevice.isLoaded())
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.currentStats", mainAttribs);
|
|
setObjVar(petControlDevice, "pet.timeStored", getGameTime());
|
|
}
|
|
else
|
|
{
|
|
dictionary parms = new dictionary();
|
|
parms.put("attribs", mainAttribs);
|
|
parms.put("time", getGameTime());
|
|
messageTo(petControlDevice, "handleStatUpdate", parms, 0, true);
|
|
}
|
|
}
|
|
|
|
void restoreDamage(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(!isIdValid(pet) || !isIdValid(petControlDevice))
|
|
return;
|
|
|
|
if(!hasObjVar(petControlDevice, "ai.pet.currentStats"))
|
|
return;
|
|
|
|
int[] mainAttribs = getIntArrayObjVar(petControlDevice, "ai.pet.currentStats");
|
|
|
|
int timeStored = getIntObjVar(petControlDevice, "pet.timeStored");
|
|
|
|
if(timeStored > 0)
|
|
{
|
|
timeStored = getGameTime() - timeStored;//time stored is current time minus when this was packed up:
|
|
int amountHealed = (timeStored/5)*2;//now amount healed is time stored
|
|
|
|
mainAttribs[HEALTH_DAMAGE] -= amountHealed;//lower the amount of health dmg by the amt healed
|
|
mainAttribs[ACTION_DAMAGE] -= amountHealed;//lower the amount of action dmg by the amt healed
|
|
mainAttribs[MIND_DAMAGE] -= amountHealed;//lower the amount of mind dmg by the amt healed
|
|
removeObjVar(petControlDevice, "pet.timeStored");
|
|
}
|
|
|
|
int currentHealth = getMaxAttrib(pet,HEALTH);
|
|
|
|
if(mainAttribs[HEALTH_DAMAGE] > 0)
|
|
{
|
|
currentHealth = currentHealth - mainAttribs[HEALTH_DAMAGE];
|
|
if(currentHealth < 1)
|
|
currentHealth = 1;
|
|
|
|
setAttrib(pet, HEALTH, currentHealth);
|
|
}
|
|
|
|
int currentAction = getMaxAttrib(pet,ACTION);
|
|
|
|
if(mainAttribs[ACTION_DAMAGE] > 0)
|
|
{
|
|
currentAction = currentAction - mainAttribs[ACTION_DAMAGE];
|
|
if(currentAction < 1)
|
|
currentAction = 1;
|
|
|
|
setAttrib(pet, ACTION, currentAction);
|
|
}
|
|
|
|
int currentMind = getMaxAttrib(pet,MIND);
|
|
|
|
if(mainAttribs[MIND_DAMAGE] > 0)
|
|
{
|
|
currentMind = currentMind - mainAttribs[MIND_DAMAGE];
|
|
if(currentMind < 1)
|
|
currentMind = 1;
|
|
|
|
setAttrib(pet, MIND, currentMind);
|
|
}
|
|
}
|
|
|
|
//don't actually kill the pet: Just increment the suckiness of the pet
|
|
void killPet(obj_id pet)
|
|
{
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(isIdValid(petControlDevice))
|
|
{
|
|
dictionary params = new dictionary();
|
|
params.put("killer", utils.getObjIdScriptVar(pet, "killer"));
|
|
|
|
messageTo(petControlDevice, "handlePetDeathblow", params, 0, false);
|
|
}
|
|
else // I'm not really a pet.
|
|
{
|
|
pclib.coupDeGrace(pet, pet);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void reduceStatsForVitality(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
int maxVitality = 100 - getIntObjVar(petControlDevice, "pet.maxVitality");
|
|
int vitality = maxVitality - getIntObjVar(petControlDevice, "pet.vitality");
|
|
|
|
if(vitality == 100)
|
|
return;//he's fine
|
|
|
|
//clamp vitality stat-reduction:
|
|
if(vitality > 75)
|
|
return;//he's fine
|
|
else if(vitality > 50)
|
|
vitality = 75;
|
|
else if(vitality > 25)
|
|
vitality = 50;
|
|
else
|
|
vitality = 25;
|
|
|
|
int currentHealth = getMaxAttrib(pet,HEALTH);
|
|
setMaxAttrib(pet, HEALTH, getReducedStatValue(currentHealth, vitality));
|
|
|
|
int currentAction = getMaxAttrib(pet,ACTION);
|
|
setMaxAttrib(pet, ACTION, getReducedStatValue(currentAction, vitality));
|
|
|
|
int currentMind = getMaxAttrib(pet,MIND);
|
|
setMaxAttrib(pet, MIND, getReducedStatValue(currentMind, vitality));
|
|
}
|
|
|
|
int getReducedStatValue(int oldValue, int vitality)
|
|
{
|
|
if(vitality == 100)
|
|
return oldValue;//he's fine
|
|
|
|
//clamp vitality reduction:
|
|
if(vitality > 75)
|
|
return oldValue;//he's fine
|
|
else if(vitality > 50)
|
|
vitality = 75;
|
|
else if(vitality > 25)
|
|
vitality = 50;
|
|
else
|
|
vitality = 25;
|
|
|
|
float reduction = vitality / 100f; //1% reduction per vitality loss
|
|
int newValue = (int)(oldValue * reduction);//betweem 1% and 99% reduction
|
|
return newValue;
|
|
}
|
|
|
|
boolean performVitalityHeal(obj_id player, obj_id pet, obj_id petControlDevice, obj_id pet_med)
|
|
{
|
|
if(!isIdValid(player) || !isIdValid(petControlDevice) || !isIdValid(pet_med))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int vitality = getIntObjVar(petControlDevice, "pet.vitality");
|
|
int maxVitality = getIntObjVar(petControlDevice, "pet.maxVitality");
|
|
int strength = getIntObjVar(pet_med, "consumable.strength");
|
|
|
|
// Calculate the loss to maxVitality
|
|
int tmpStrength = (int) strength / 25;
|
|
if(tmpStrength < 1) tmpStrength = 1;
|
|
int penalty = (int) vitality / 5;
|
|
penalty = (int) ((penalty / tmpStrength) + .99f);
|
|
if(penalty < 1) penalty = 1;
|
|
|
|
// Set new vitality values
|
|
vitality = 0;
|
|
maxVitality += penalty;
|
|
|
|
//never exceed the max reduction and never kill the pet
|
|
if(maxVitality > MAX_VITALITY_LOSS)
|
|
maxVitality = MAX_VITALITY_LOSS;
|
|
|
|
// Save new vitality values
|
|
setObjVar(petControlDevice, "pet.vitality", vitality);
|
|
setObjVar(petControlDevice, "pet.maxVitality", maxVitality);
|
|
|
|
// Save the current info and reinitialize the pet's stats
|
|
if(isIdValid(pet))
|
|
{
|
|
savePetInfo(pet, petControlDevice);
|
|
|
|
int growthStage = 10;
|
|
|
|
if(hasObjVar(petControlDevice, "ai.petAdvance.growthStage"))
|
|
growthStage = getIntObjVar(petControlDevice, "ai.petAdvance.growthStage");
|
|
|
|
if(hasObjVar(petControlDevice, "pet.crafted"))
|
|
{
|
|
setCraftedPetStatsByGrowth(petControlDevice, pet, growthStage);
|
|
}
|
|
else
|
|
{
|
|
setPetStatsByGrowth(pet, growthStage);
|
|
}
|
|
|
|
initializeTrainedPetAbilities(petControlDevice, pet);
|
|
|
|
reduceStatsForVitality(pet, petControlDevice);
|
|
restoreDamage(pet, petControlDevice);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void savePetInfo(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(!isIdValid(pet) || !isIdValid(petControlDevice))
|
|
return;
|
|
|
|
if(!pet.isLoaded() || !petControlDevice.isLoaded())
|
|
return;
|
|
|
|
obj_id master = getMaster(pet);
|
|
if(isIdValid(master) && master.isLoaded())
|
|
{
|
|
setHomeLocation(pet, getLocation(master));
|
|
}
|
|
|
|
setName(petControlDevice, getAssignedName(pet));
|
|
setName(petControlDevice, getNameStringId(pet));
|
|
|
|
storeDamage(pet, petControlDevice);
|
|
setObjVar(petControlDevice, "pet.timeStored", getGameTime());
|
|
|
|
if(!isNewPlayerHelperDroid(pet))
|
|
{
|
|
ranged_int_custom_var[] ri = hue.getPalcolorVars(pet);
|
|
if(ri != null && ri.length > 0)
|
|
{
|
|
boolean wasUpdated = false;
|
|
for(int i = 0; i < ri.length; i++)
|
|
{
|
|
int val = ri[i].getValue();
|
|
if(val > -1)
|
|
{
|
|
string varpath = VAR_PALVAR_VARS + "." + ri[i].getVarName();
|
|
int cur = getIntObjVar(petControlDevice, varpath);
|
|
if(cur != val)
|
|
{
|
|
setObjVar(petControlDevice, varpath, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(utils.hasScriptVar(pet, "customizationUpdated"))
|
|
setObjVar(petControlDevice, VAR_PALVAR_CNT, CUSTOMIZATION_COUNT);
|
|
}
|
|
|
|
int varValue = getRangedIntCustomVarValue(pet, "/private/index_texture_1");
|
|
setObjVar(petControlDevice, VAR_PALVAR_VARS +"./private/index_texture_1", varValue);
|
|
}
|
|
}
|
|
|
|
void processSpeech(obj_id pet, obj_id master, string text)
|
|
{
|
|
if(!isIdValid(pet))
|
|
return;
|
|
|
|
if(!isIdValid(master))
|
|
return;
|
|
|
|
if(ai_lib.isAiDead(pet))
|
|
return;
|
|
|
|
if(text.equals(""))
|
|
return;
|
|
|
|
if(master != getMaster(pet))
|
|
{
|
|
if(!pet_lib.isFriend(pet, master))
|
|
return;
|
|
}
|
|
|
|
if(utils.hasScriptVar(pet, "stored"))
|
|
{
|
|
destroyObject(pet);
|
|
return;
|
|
}
|
|
|
|
if(callable.hasCallableCD(pet))
|
|
{
|
|
obj_id controller = callable.getCallableCD(pet);
|
|
|
|
if(isIdValid(controller) && controller.isLoaded())
|
|
{
|
|
obj_id currentPet = callable.getCDCallable(controller);
|
|
|
|
if(isIdValid(currentPet) && currentPet != pet)
|
|
{
|
|
destroyObject(pet);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
location masterLoc = getLocation(master);
|
|
location petLoc = getLocation(pet);
|
|
|
|
if(masterLoc == null || petLoc == null) // ifthis doesn't fix that stupid null pointer exception,
|
|
return; // then I don't know what will.
|
|
|
|
// obj_id masterCell = masterLoc.cell;
|
|
// obj_id petCell = petLoc.cell;
|
|
if(((masterLoc.cell != null) && isIdValid(masterLoc.cell)) && ((petLoc.cell == null) || (!isIdValid(petLoc.cell))))
|
|
{
|
|
//pet is outside and master is inside
|
|
return;
|
|
}
|
|
|
|
if(text == null || text.equals(""))
|
|
return;//don't process null strings
|
|
|
|
if(text.length() > 30)
|
|
return;//pets can't hear anything over 30 characters long
|
|
|
|
//debugSpeakMsg(pet, "I hear " + text);
|
|
|
|
if(utils.hasScriptVar(pet, "ai.pet.learnCommand"))
|
|
{
|
|
//debugSpeakMsg(pet, "Trying to learn");
|
|
int commandNum = utils.getIntScriptVar(pet, "ai.pet.learnCommand");
|
|
pet_lib.learnCommand(pet, commandNum, text, master);
|
|
}
|
|
else if(isSameFaction(pet, master))
|
|
{
|
|
//debugSpeakMsg(pet, "doing command: " + text);
|
|
pet_lib.doCommand(pet, text, master);
|
|
}
|
|
else
|
|
{
|
|
releasePet(pet);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int getAdultLevel(obj_id pet)
|
|
{
|
|
string creatureName = getCreatureName(pet);
|
|
if(creatureName != null)
|
|
{
|
|
int level = create.calcCreatureLevel(creatureName);
|
|
return level;
|
|
}
|
|
return 1;//returning -1 here causes bad things to happen to various systems, so everything is at least level 1
|
|
}
|
|
|
|
int getAdultLevel(string creatureName)
|
|
{
|
|
if(creatureName != null)
|
|
{
|
|
int level = create.calcCreatureLevel(creatureName);
|
|
return level;
|
|
}
|
|
return 1;//returning -1 here causes bad things to happen to various systems, so everything is at least level 1
|
|
}
|
|
|
|
boolean canTrainAsMount(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet))
|
|
{
|
|
debugServerConsoleMsg(player, "+++ PET_LIB . canTrainAsMount +++ isIdValid failed for(pet)");
|
|
return false;
|
|
}
|
|
if(!isIdValid(player))
|
|
{
|
|
debugServerConsoleMsg(player, "+++ PET_LIB . canTrainAsMount +++ isIdValid failed for(player)");
|
|
return false;
|
|
}
|
|
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
{
|
|
debugServerConsoleMsg(player, "+++ PET_LIB . canTrainAsMount +++ isIdValid failed for(petControlDevice)");
|
|
return false;
|
|
}
|
|
|
|
//check ifthe pet isn't already a mount
|
|
if(hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check that the pet can be a mount
|
|
// couldPetBeMadeMountable() == MSC_CREATURE_MOUNTABLE
|
|
if(!(couldPetBeMadeMountable(pet)== MSC_CREATURE_MOUNTABLE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check that the player is skilled enoughcreature is mountable, code-wise
|
|
//if(!('player has mad training skills')
|
|
// return false;
|
|
if(!hasSkill(player, "outdoors_creaturehandler_support_04"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
boolean trainMount(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet))
|
|
return false;
|
|
if(!isIdValid(player))
|
|
return false;
|
|
|
|
//check that the pet can be a mount
|
|
if(!canTrainAsMount(pet, player))
|
|
return false;
|
|
|
|
if(!makePetAMount(pet, player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean makePetAMount(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet))
|
|
return false;
|
|
if(!isIdValid(player))
|
|
return false;
|
|
|
|
//check that the pet can be a mount
|
|
// couldPetBeMadeMountable() == MRC_CREATURE_MOUNTABLE
|
|
if(!(couldPetBeMadeMountable(pet)==0))
|
|
return false;
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
return false;
|
|
|
|
//check ifthe pet is already a mount
|
|
if(hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
return false;
|
|
|
|
if(!makePetMountable(pet))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
setObjVar(petControlDevice, "ai.pet.trainedMount", 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//This function made to be used EACH time a pet that has been trained as a mount is called from it's PetControlDevice
|
|
boolean mountablePetInit(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet) || !isIdValid(player))
|
|
return false;
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
return false;
|
|
|
|
//check ifthe pet is already a mount
|
|
if(!hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
return false;
|
|
|
|
//check that the pet can be a mount
|
|
// couldPetBeMadeMountable() == MRC_CREATURE_MOUNTABLE
|
|
int mountableFlag = couldPetBeMadeMountable(pet);
|
|
if(mountableFlag !=0)
|
|
{
|
|
if(mountableFlag == 3)
|
|
{
|
|
|
|
string appearance = getAppearance(pet);
|
|
int row = dataTableSearchColumnForString(appearance, "appearance_name", MOUNT_VALID_SCALE_RANGE);
|
|
dictionary dict = dataTableGetRow(MOUNT_VALID_SCALE_RANGE, row);
|
|
|
|
float validMinRange = dict.getFloat("valid_scale_min");
|
|
float validMaxRange = dict.getFloat("valid_scale_max");
|
|
float currentScale = getScale(pet);
|
|
if(currentScale > validMaxRange)
|
|
{
|
|
setScale(pet, validMaxRange);
|
|
}
|
|
else if(currentScale < validMinRange)
|
|
{
|
|
setScale(pet, validMinRange);
|
|
}
|
|
|
|
mountableFlag = couldPetBeMadeMountable(pet);
|
|
|
|
if(mountableFlag !=0)
|
|
{
|
|
if(isGod(player))
|
|
{
|
|
prose_package pp = new prose_package();
|
|
prose.setTO(pp, MOUNTABILITY_STATUS_CODES[mountableFlag]);
|
|
prose.setStringId(pp, SID_MOUNT_STATUS_CODE_ERROR);
|
|
sendSystemMessageProse(player, pp);
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if(!makePetMountable(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
if(isGod(player))
|
|
{
|
|
prose_package pp = new prose_package();
|
|
prose.setTO(pp, MOUNTABILITY_STATUS_CODES[mountableFlag]);
|
|
prose.setStringId(pp, SID_MOUNT_STATUS_CODE_ERROR);
|
|
sendSystemMessageProse(player, pp);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if(!makePetMountable(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean isMount(obj_id pet)
|
|
{
|
|
if(!isIdValid(pet) || !exists(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice) || !hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean canMount(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet))
|
|
{
|
|
debugServerConsoleMsg(player, "+++ PET_LIB.canMount +++ isIdValid failed for(pet)");
|
|
return false;
|
|
}
|
|
|
|
if(!isIdValid(player))
|
|
{
|
|
debugServerConsoleMsg(player, "+++ PET_LIB.canMount +++ isIdValid failed for(player)");
|
|
return false;
|
|
}
|
|
|
|
//check that the player is mounted on something
|
|
obj_id playerCurrentMount = getMountId(player);
|
|
|
|
if(isIdValid(playerCurrentMount))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isDead(pet) || ai_lib.isIncapacitated(pet))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check that the pet is close enough to mount
|
|
int mountDistance = (int)(getDistance(pet, player));
|
|
|
|
int maxDistance = (getOwner(pet) == player) ? MAX_PET_MOUNT_DISTANCE : MAX_PET_MOUNT_OFFER_DISTANCE;
|
|
|
|
if(mountDistance > maxDistance)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check that the creature is trained as a mount
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "mountBlock"))
|
|
{
|
|
int blockTime = utils.getIntScriptVar(player, "mountBlock");
|
|
|
|
if(getPlayerPlayedTime(player)<blockTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(getMovementPercent(player) == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
boolean isMountedOnCreatureQueried(obj_id pet, obj_id player)
|
|
{
|
|
//check that both master and pet are valid objects
|
|
if(!isIdValid(pet) || !exists(pet) || !isIdValid(player) || !exists(player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check that the creature is trained as a mount
|
|
obj_id petControlDevice = callable.getCallableCD(pet);
|
|
|
|
if(!isIdValid(petControlDevice) || !hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id playerCurrentMount = getMountId (player);
|
|
|
|
//check that the player is mounted on something
|
|
if(!isIdValid(playerCurrentMount) || playerCurrentMount != pet)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean isSameFaction(obj_id pet, obj_id master)
|
|
{
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string petFaction = utils.dataTableGetString(CREATURE_TABLE, creatureName, "pvpFaction");
|
|
|
|
if(petFaction == null || petFaction == "")
|
|
{
|
|
////debugSpeakMsg(master, "ok to call!");
|
|
return true;//ok to call
|
|
}
|
|
|
|
if(petFaction != "Imperial" && petFaction != "Rebel")
|
|
{
|
|
////debugSpeakMsg(master, "ok to call!");
|
|
return true;//ok to call
|
|
}
|
|
|
|
string masterFaction = factions.getFaction(master);
|
|
|
|
if(masterFaction == null || masterFaction != petFaction)
|
|
{
|
|
|
|
return false;//so you can't have it.
|
|
}
|
|
|
|
if(pvpGetType(master) == PVPTYPE_NEUTRAL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
////debugSpeakMsg(master, "ok to call!");
|
|
return true;//ok to call!
|
|
}
|
|
|
|
boolean cancelAttackDueToFactionalRestrictions(obj_id pet, obj_id target)
|
|
{
|
|
obj_id master = getMaster(pet);
|
|
|
|
if(!isIdValid(master) || !exists(master) || !master.isLoaded())
|
|
{
|
|
return true;//cancel this attack because I have no master
|
|
}
|
|
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
{
|
|
return true;//cancel attack because I don't know what I am.
|
|
}
|
|
|
|
if(!pvpCanAttack(pet, target))
|
|
{
|
|
return true;//cancel attack iftarget is not pvpAttackable
|
|
}
|
|
|
|
string petFaction = utils.dataTableGetString(CREATURE_TABLE, creatureName, "pvpFaction");
|
|
|
|
if(petFaction == null || petFaction == "")
|
|
{
|
|
return false; //ok to attack because I am not a factional pet
|
|
}
|
|
|
|
if(petFaction != "Imperial" && petFaction != "Rebel")
|
|
{
|
|
return false; //ok to attack because I am not an Imperial or Rebel
|
|
}
|
|
|
|
int intAlignedFaction = pvpGetAlignedFaction(target);
|
|
|
|
if(intAlignedFaction == 0)
|
|
{
|
|
return true; // no attacking real neutrals;
|
|
}
|
|
|
|
if(!pvpCanAttack(master, target))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;//no idea, so go ahead with this
|
|
}
|
|
|
|
|
|
|
|
|
|
// WANT YOUR PET TO GROW? Pet growth arrest for Mountability SUI Lockdown
|
|
// ********************************************************************************************************************************************
|
|
// ********************************************************************************************************************************************
|
|
|
|
int showGrowthChoiceSui (obj_id owner, obj_id target, string targetName)
|
|
{
|
|
if(!isIdValid(owner) || !isIdValid(target) || (targetName == null) || (targetName.equals("")))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
obj_id petControlDevice = null;
|
|
|
|
if(utils.hasScriptVar(target, "mountgrowthcheck.pcd"))
|
|
{
|
|
petControlDevice = utils.getObjIdScriptVar(target, "mountgrowthcheck.pcd");
|
|
}
|
|
|
|
LOG("pet_lib.showGrowthChoiceSui", "calling mount-related show growth choice sui...");
|
|
string title = utils.packStringId(SID_GROWTH_CHOICE_TITLE);
|
|
string prompt = utils.packStringId(SID_GROWTH_CHOICE_PROMPT);
|
|
int pid = sui.msgbox(petControlDevice, target, prompt, sui.YES_NO, title, "handleGrowthChoice");
|
|
|
|
return pid;
|
|
}
|
|
|
|
|
|
// doMountOutOfScaleCheck
|
|
// returns 13 ifthere was a data error
|
|
// returns 5 ifthe creature can NEVER be a mount
|
|
// returns 3 ifthe creature is OUT of scale to be a mount, TOO BIG. Do the growth menu
|
|
// returns 2 ifthe creature is OUT of scale to be a mount, TOO BIG. Do the growth menu
|
|
// returns 1 ifthe creature is IN scale to be a mount
|
|
int doMountOutOfScaleCheck (obj_id petControlDevice, string creatureName)
|
|
{
|
|
if(creatureName == null)
|
|
{
|
|
LOG("create", "create.createCreature called with NULL creatureName!");
|
|
return 13;
|
|
}
|
|
|
|
dictionary creatureDict = utils.dataTableGetRow(CREATURE_TABLE, creatureName);
|
|
if(creatureDict == null)
|
|
{
|
|
debugServerConsoleMsg(null, "WARNING: Unable to spawn invalid creatureType: " + creatureName);
|
|
return 13;
|
|
}
|
|
|
|
string templateName = creatureDict.getString("template");
|
|
if(templateName == "" || templateName == null)
|
|
{
|
|
debugServerConsoleMsg(null, "WARNING: Unable to spawn invalid creatureType: " + creatureName + " - bad templateName?");
|
|
return 13;
|
|
}
|
|
|
|
if(!templateName.endsWith(".iff"))
|
|
{
|
|
|
|
if(dataTableHasColumn(DRESSED_NPC_TABLE, templateName))
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults(DRESSED_NPC_TABLE, templateName);
|
|
if(templateList.length < 1 || templateList == null)
|
|
{
|
|
LOG("create", templateName + " column in dressed_species is empty!");
|
|
return 13;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1) ];
|
|
}
|
|
else
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults(NPC_CUSTOMIZATION_PREFIX + templateName + ".iff", rand(0,1));
|
|
if(templateList == null)
|
|
{
|
|
debugServerConsoleMsg(null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName);
|
|
LOG("create", templateName + " does not exist!");
|
|
return 13;
|
|
}
|
|
if(templateList.length == 0)
|
|
{
|
|
debugServerConsoleMsg(null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName);
|
|
LOG("create", templateName + " File was Empty!");
|
|
return 13;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1) ] + ".iff";
|
|
}
|
|
}
|
|
|
|
int growthStage = 10;
|
|
if(hasObjVar(petControlDevice, "ai.petAdvance.growthStage"))
|
|
{
|
|
growthStage = getIntObjVar(petControlDevice, "ai.petAdvance.growthStage");
|
|
}
|
|
|
|
float baseScale = getDefaultScaleFromObjectTemplate ("object/mobile/"+ templateName);
|
|
float minScale = creatureDict.getFloat("minScale");
|
|
float maxScale = creatureDict.getFloat("maxScale");
|
|
float scaleAdjust = (minScale+maxScale)/2.0f;
|
|
float realScale = baseScale * scaleAdjust;
|
|
float babyScale = realScale * .3f;
|
|
float scaleIncrement = (realScale-babyScale)/10.0f;
|
|
float finalScale = babyScale + (scaleIncrement * growthStage);
|
|
|
|
if(finalScale > maxScale)
|
|
{
|
|
finalScale = maxScale;
|
|
}
|
|
|
|
if(couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) != MSC_SPECIES_UNMOUNTABLE)
|
|
{
|
|
if(couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) == MSC_CREATURE_MOUNTABLE)
|
|
{
|
|
setObjVar(petControlDevice,"ai.petAdvance.rdyToMnt",1);
|
|
return 1;
|
|
}
|
|
else if((couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) == MSC_SPECIES_MOUNTABLE_SCALE_OUT_OF_RANGE))
|
|
{
|
|
while ((couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) == MSC_SPECIES_MOUNTABLE_SCALE_OUT_OF_RANGE) && (growthStage > 0))
|
|
{
|
|
growthStage = growthStage - 1;
|
|
finalScale = babyScale + (scaleIncrement * growthStage);
|
|
}
|
|
if((couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) == MSC_CREATURE_MOUNTABLE))
|
|
{
|
|
setObjVar(petControlDevice,"ai.petAdvance.lrgMnt",growthStage);
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** Was unable to find a smaller version that is ready to be a mount as of yet.");
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** Since this pet IS mountable, it simply hasn't grown up enough yet.");
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** We'll have to run this check again next time it's called.");
|
|
return 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** I should NEVER see this message. It means that we've snuck through a hole in the possible results. ");
|
|
}
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** the template returned as MOUNTABLE-ScaleOutOfRange at the current finalScale, and has the ai.petAdvance.rdyToMnt objvar");
|
|
}
|
|
else if(couldObjectTemplateBeMadeMountable("object/mobile/"+ templateName, finalScale) == MSC_SPECIES_UNMOUNTABLE)
|
|
{
|
|
debugServerConsoleMsg(null, "Pet_Lib script: doMountOutOfScaleCheck: ** the template came in as UNMOUNTABLE, and not just OUT OF SCALE. It was all for naught! This is the Fall-Through case.");
|
|
return 5;
|
|
}
|
|
|
|
return 13;
|
|
}
|
|
|
|
dictionary getMountMovementInfo(obj_id objMount) // returns datatable info for mount skeleton movement ratess
|
|
{
|
|
string strSkeleton = ai_lib.getSkeleton(objMount);
|
|
|
|
if(strSkeleton != null)
|
|
{
|
|
dictionary dctMountInfo = dataTableGetRow("datatables/mount/mount_movement_speeds.iff", strSkeleton);
|
|
|
|
if(dctMountInfo != null)
|
|
{
|
|
return dctMountInfo;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void setMountedMovementRate(obj_id objPlayer, obj_id objTarget)
|
|
{
|
|
dictionary dctMountInfo = getMountMovementInfo(objTarget);
|
|
|
|
if(dctMountInfo != null)
|
|
{
|
|
float fltBaseRunSpeed = dctMountInfo.getFloat("fltBaseRunSpeed");
|
|
//LOG("combat", "setting mounted movement speed to "+fltBaseRunSpeed);
|
|
setBaseRunSpeed(objTarget, fltBaseRunSpeed); // slow mob down for time being.
|
|
}
|
|
}
|
|
|
|
void setUnmountedMovementRate(obj_id self, obj_id objTarget)
|
|
{
|
|
dictionary dctMountInfo = getMountMovementInfo(objTarget);
|
|
|
|
if(dctMountInfo != null)
|
|
{
|
|
float fltBurstRunSpeed = dctMountInfo.getFloat("fltBurstRunSpeed");
|
|
//LOG("combat", "setting non mounted movement speed to "+fltBurstRunSpeed);
|
|
setBaseRunSpeed(objTarget, fltBurstRunSpeed); // set mob back to "pet" movement rate
|
|
}
|
|
}
|
|
|
|
void setMountBurstRunMovementRate(obj_id objPlayer, obj_id objTarget)
|
|
{
|
|
dictionary dctMountInfo = getMountMovementInfo(objTarget);
|
|
obj_id objControlDevice = callable.getCallableCD(objTarget);
|
|
|
|
if(dctMountInfo != null)
|
|
{
|
|
|
|
float fltBurstRunSpeed = dctMountInfo.getFloat("fltBurstRunSpeed"); // normal run speed
|
|
float fltBaseRunSpeed = dctMountInfo.getFloat("fltBaseRunSpeed"); // normal mount movement rate
|
|
float fltBurstRunDuration = dctMountInfo.getFloat("fltBurstRunDuration"); // duration for run
|
|
float fltBurstRunRecycleTime = dctMountInfo.getFloat("fltBurstRunRecycleTime"); // downtime for run
|
|
|
|
LOG("pet", "setting run speed to "+fltBurstRunSpeed);
|
|
LOG("pet", "fltBurstRunDuration is "+fltBurstRunDuration);
|
|
LOG("pet", "fltBurstRunRecycleTiem is "+fltBurstRunRecycleTime);
|
|
|
|
setBaseRunSpeed(objTarget, fltBurstRunSpeed); // RUn like the wind!
|
|
utils.setScriptVar(objControlDevice, "mount.intGalloping", 1);
|
|
utils.setScriptVar(objControlDevice, "mount.intTired", 1);
|
|
|
|
messageTo(objControlDevice, "removeGallop", null, fltBurstRunDuration, false);
|
|
messageTo(objControlDevice, "removeGallopTired", null, fltBurstRunRecycleTime, false);
|
|
|
|
string_id strSpam = new string_id("combat_effects", "gallop_start");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
}
|
|
}
|
|
void setMountCombatMoveSpeed(obj_id objRider, obj_id objMount)
|
|
{
|
|
LOG("pet", "setting run speed of "+objMount+" to 6.0f");
|
|
|
|
setBaseRunSpeed(objMount, 6.0f); // RUn like the wind!
|
|
string_id strSpam = new string_id("combat_effects", "mount_slow_for_combat");
|
|
sendSystemMessage(objRider, strSpam);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean isGalloping(obj_id objCreature)
|
|
{
|
|
if(!isIdValid(objCreature) || !exists(objCreature))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!callable.hasCallableCD(objCreature))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id objControlDevice = callable.getCallableCD(objCreature);
|
|
|
|
if(!isIdValid(objControlDevice))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(objControlDevice, "mount.intGalloping"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean isTameable(obj_id objCreature)
|
|
{
|
|
const string strCreatureName = getCreatureName(objCreature);
|
|
|
|
if(strCreatureName != null)
|
|
{
|
|
dictionary dctCreatureInfo = dataTableGetRow("datatables/mob/creatures.iff", strCreatureName);
|
|
|
|
if(dctCreatureInfo != null)
|
|
{
|
|
float fltCanTame = dctCreatureInfo.getFloat("canTame");
|
|
|
|
if(fltCanTame > 0.0f)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void doPetSpecialAttack(obj_id pet, obj_id master, int attackNum)
|
|
{
|
|
debugServerConsoleMsg(null, "PET_LIB:doPetSpecialAttack *** entered doPetSpecialAttack ");
|
|
//debugServerConsoleMsg(null, "PET_LIB:doPetSpecialAttack *** ATTACKNUM came in as "+attackNum);
|
|
|
|
switch(attackNum)
|
|
{
|
|
case 2:
|
|
|
|
obj_id currentWeapon = getCurrentWeapon(pet);
|
|
obj_id defaultWeapon = getDefaultWeapon(pet);
|
|
int wpnType = getWeaponType(defaultWeapon);
|
|
int crrntWpnType = getWeaponType (currentWeapon);
|
|
string defaultWeaponTemplateName = getTemplateName(defaultWeapon);
|
|
string currentWeaponTemplateName = getTemplateName(currentWeapon);
|
|
debugServerConsoleMsg(null, "PET_LIB:doPetSpecialAttack *** defaultWeapon type comes in as :"+wpnType+" and being of template: "+defaultWeaponTemplateName);
|
|
debugServerConsoleMsg(null, "PET_LIB:doPetSpecialAttack *** currentWeapon type comes in as :"+crrntWpnType+" and being of template: "+currentWeaponTemplateName);
|
|
|
|
break;
|
|
case 1:
|
|
case 0:
|
|
|
|
if(!ai_lib.isInCombat(pet))
|
|
{
|
|
doAttackCommand(pet, master);
|
|
}
|
|
if(ai_lib.isInCombat(pet))
|
|
{
|
|
const obj_id hateTarget = getHateTarget(pet);
|
|
}
|
|
//do special attack number 1 or 2
|
|
break;
|
|
}
|
|
}
|
|
|
|
boolean hasRangedAttack(obj_id pet)
|
|
{
|
|
// New pet combat droid ranged weapon check
|
|
if(isCombatDroid(pet))
|
|
{
|
|
const string droidType = getCreatureName(pet);
|
|
int droidRangedCapable = utils.dataTableGetInt(DROID_COMBAT_TABLE, droidType, "hasRangedAttack");
|
|
|
|
if(droidRangedCapable != 1)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(!ai_lib.isMonster(pet) && !isCombatDroid(pet)) //NEW STUFF HERE. DROIDS WILL FAIL AI_LIB.ISMONSTER
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string creatureName = getCreatureName(pet);
|
|
|
|
if(creatureName != null)
|
|
{
|
|
string weaponOne = dataTableGetString(CREATURE_TABLE, creatureName, "primary_weapon");
|
|
string weaponTwo = dataTableGetString(CREATURE_TABLE, creatureName, "secondary_weapon");
|
|
boolean rangedWeapon = false;
|
|
|
|
if(weaponOne != null && weaponOne != "" && weaponOne.indexOf("_spit_") != -1)
|
|
{
|
|
rangedWeapon = true;
|
|
}
|
|
else if(weaponTwo != null && weaponTwo != "" && weaponTwo.indexOf("_spit_") != -1)
|
|
{
|
|
rangedWeapon = true;
|
|
}
|
|
else
|
|
{
|
|
obj_id pcd = callable.getCallableCD(pet);
|
|
|
|
if(isIdValid(pcd) && hasObjVar(pcd, "pet.crafted"))
|
|
{
|
|
string rangedWeaponName = getStringObjVar(pcd, "creature_attribs." + bio_engineer.ATTRIB_DICT_RANGED_WEAPON);
|
|
|
|
if(rangedWeaponName == null || rangedWeaponName == "")
|
|
{
|
|
rangedWeapon = false;
|
|
}
|
|
else
|
|
{
|
|
rangedWeapon = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rangedWeapon = false;
|
|
}
|
|
}
|
|
|
|
return rangedWeapon;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void packAllCurrentPets(obj_id player, string_id optionalMsg)
|
|
{
|
|
callable.storeCallables(player);
|
|
|
|
if(optionalMsg != null)
|
|
{
|
|
sendSystemMessage(player, optionalMsg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to dismount a rider from a mount/vehicle.
|
|
*
|
|
* This function will attempt to make this happen right here
|
|
* in this function. It will not defer activity to another
|
|
* function.
|
|
*
|
|
* @return true ifthe dismount succeeded, false ifit failed.
|
|
*/
|
|
boolean doDismountNow(obj_id riderId, boolean notify)
|
|
{
|
|
if(!isIdValid(riderId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!getMountsEnabled())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
boolean dismountOccurred = false;
|
|
|
|
obj_id playerCurrentMount = getMountId (riderId);
|
|
|
|
if(isIdValid(playerCurrentMount) && exists(playerCurrentMount))
|
|
{
|
|
queueClear(riderId);
|
|
|
|
dismountCreature(riderId);
|
|
dismountOccurred = true;
|
|
|
|
if(hasScript(playerCurrentMount, "ai.mount_combat"))
|
|
{
|
|
detachScript(playerCurrentMount, "ai.mount_combat");
|
|
}
|
|
|
|
//remove buffs from player
|
|
int vehicleBuff = buff.getBuffOnTargetFromGroup(riderId, "vehicle");
|
|
|
|
if(vehicleBuff !=0)
|
|
{
|
|
buff.removeBuff(riderId, vehicleBuff);
|
|
}
|
|
|
|
//remvoe buffs from vehicle
|
|
int vehicleHasBuff = buff.getBuffOnTargetFromGroup(playerCurrentMount, "vehicle");
|
|
|
|
if(vehicleHasBuff !=0)
|
|
{
|
|
buff.removeBuff(playerCurrentMount, vehicleHasBuff);
|
|
}
|
|
|
|
//We need to make sure the mount gets stored whenever we dismount
|
|
int petType = getPetType(playerCurrentMount);
|
|
if(isPetType(playerCurrentMount, PET_TYPE_MOUNT))
|
|
storePet(playerCurrentMount);
|
|
}
|
|
else
|
|
{
|
|
if(notify)
|
|
{
|
|
sendSystemMessage(riderId, pet_lib.SID_SYS_CANT_DISMOUNT);
|
|
}
|
|
}
|
|
|
|
return dismountOccurred;
|
|
}
|
|
|
|
boolean doDismountNow(obj_id player)
|
|
{
|
|
return doDismountNow(player, true);
|
|
}
|
|
|
|
void initPCDCraftedStats(obj_id device, obj_id deed)
|
|
{
|
|
setObjVar(device, "creature_attribs.level", getIntObjVar(deed, "creature_attribs.level"));
|
|
setObjVar(device, "creature_attribs.maxHealth", getIntObjVar(deed, "creature_attribs.maxHealth"));
|
|
setObjVar(device, "creature_attribs.maxConstitution", getIntObjVar(deed, "creature_attribs.maxConstitution"));
|
|
setObjVar(device, "creature_attribs.general_protection", getIntObjVar(deed, "creature_attribs.general_protection"));
|
|
setObjVar(device, "creature_attribs.toHitChance", getIntObjVar(deed, "creature_attribs.toHitChance"));
|
|
setObjVar(device, "creature_attribs.defenseValue", getIntObjVar(deed, "creature_attribs.defenseValue"));
|
|
setObjVar(device, "creature_attribs.minDamage", getIntObjVar(deed, "creature_attribs.minDamage"));
|
|
setObjVar(device, "creature_attribs.maxDamage", getIntObjVar(deed, "creature_attribs.maxDamage"));
|
|
setObjVar(device, "creature_attribs.aggroBonus", getFloatObjVar(deed, "creature_attribs.aggroBonus"));
|
|
setObjVar(device, "creature_attribs.critChance", getFloatObjVar(deed, "creature_attribs.critChance"));
|
|
setObjVar(device, "creature_attribs.critSave", getFloatObjVar(deed, "creature_attribs.critSave"));
|
|
setObjVar(device, "creature_attribs.scale", getFloatObjVar(deed, "creature_attribs.scale"));
|
|
setObjVar(device, "creature_attribs.stateResist", getFloatObjVar(deed, "creature_attribs.stateResist"));
|
|
|
|
setObjVar(device, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_1,
|
|
getIntObjVar(deed, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_1));
|
|
setObjVar(device, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_2,
|
|
getIntObjVar(deed, "creature_attribs." + bio_engineer.ATTRIB_DICT_SPECIAL_ATTACK_2));
|
|
setObjVar(device, "creature_attribs." + bio_engineer.ATTRIB_DICT_RANGED_WEAPON,
|
|
getStringObjVar(deed, "creature_attribs." + bio_engineer.ATTRIB_DICT_RANGED_WEAPON));
|
|
|
|
|
|
setObjVar(device, "postCuPCD", 2); //cheap designator, the lack of which tells a PCD upon load that it needs to rev itself to Post Combat Upgrade standards.
|
|
}
|
|
|
|
void createPetFromData(obj_id petControlDevice)
|
|
{
|
|
createPetFromData(petControlDevice, null);
|
|
}
|
|
|
|
void createPetFromData(obj_id petControlDevice, obj_id objContainer)
|
|
{
|
|
string creatureName = getStringObjVar(petControlDevice, "pet.creatureName");
|
|
|
|
if(creatureName == null || creatureName == "")
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id datapad = getContainedBy(petControlDevice);
|
|
|
|
if(!isIdValid(datapad))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id master = getContainedBy(datapad);
|
|
|
|
if(!isIdValid(master))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!hasScript(master, "ai.pet_master"))
|
|
{
|
|
attachScript(master, "ai.pet_master");
|
|
}
|
|
|
|
//BEGIN MOUNT-RELATED SCRIPT FUNCTIONALITY
|
|
if(getMountsEnabled() && (!hasObjVar(petControlDevice, "ai.petAdvance.cannotBeMount")))
|
|
{
|
|
if(hasObjVar(petControlDevice, "ai.petAdvance.freezegrowth"))
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** freezeGrowth objvar detected. Reseting growstage objvar to freezeGrowth value");
|
|
int freezeGrowthValue = getIntObjVar(petControlDevice,"ai.petAdvance.freezegrowth");
|
|
if(freezeGrowthValue == 10)
|
|
{
|
|
debugServerConsoleMsg(null, "PCD script: createPetFromData: ** skipping growthstage objvar reset/rewrite, as this creature is an adult, with freezegrowth value of 10");
|
|
}
|
|
else
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** creature had growth frozen before adult-hood. Re-writing growthstage to match freezegrowth value which is: " + freezeGrowthValue);
|
|
setObjVar(petControlDevice, "ai.petAdvance.growthStage", freezeGrowthValue);
|
|
}
|
|
}
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** getMountsEnabled returned true, and no ai.petAdvance.cannotBeMount objvar detected");
|
|
|
|
if((!hasObjVar(petControlDevice, "ai.petAdvance.letPetGrow")) && (!hasObjVar(petControlDevice, "ai.petAdvance.freezegrowth")))
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** no ai.petAdvance.letPetGrow or ai.petAdvance.freezegrowth objvars detected");
|
|
|
|
// doMountOutOfScaleCheck
|
|
// returns 13 ifthere was a data error
|
|
// returns 5 ifthe creature can NEVER be a mount
|
|
// returns 3 ifthe creature is OUT of scale to be a mount, TOO BIG. Do the growth menu
|
|
// returns 2 ifthe creature is OUT of scale to be a mount, TOO BIG. Do the growth menu
|
|
// returns 1 ifthe creature is IN scale to be a mount
|
|
int mountOutOfScaleReturnValue = pet_lib.doMountOutOfScaleCheck(petControlDevice, creatureName);
|
|
if(mountOutOfScaleReturnValue == 5)
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** doMountOutOfScaleCheck returned 5, meaning that the creature cannot ever be a mount.");
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** setting 'ai.petAdvance.cannotBeMount' objvar to avoid doing this test again");
|
|
setObjVar(petControlDevice,"ai.petAdvance.cannotBeMount",1);
|
|
}
|
|
else if(mountOutOfScaleReturnValue == 2)
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** pet_lib.doMountOutOfScaleCheck returned 2... the pet IS out of scale to be a mount, but can be one");
|
|
|
|
// do menu choice
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** calling up the growthChoice menu UI...");
|
|
//int growthChoice = pet_lib.showGrowthChoiceSui(master, master, creatureName);
|
|
utils.setScriptVar(master, "mountgrowthcheck.pcd", petControlDevice);
|
|
pet_lib.showGrowthChoiceSui(petControlDevice, master, creatureName);
|
|
return;
|
|
}
|
|
else if(mountOutOfScaleReturnValue == 1)
|
|
{
|
|
//debugServerConsoleMsg(null, "PCD script: createPetFromData: ** pet_lib.doMountOutOfScaleCheck returned 1... the pet should be able to be mounted.");
|
|
if(!hasObjVar(petControlDevice, "ai.petAdvance.growthStage"))
|
|
setObjVar(petControlDevice, "ai.petAdvance.freezegrowth", 10);
|
|
}
|
|
else if(mountOutOfScaleReturnValue == 3)
|
|
{
|
|
debugServerConsoleMsg(null, "PCD script: createPetFromData: ** pet_lib.doMountOutOfScaleCheck returned 3... the pet hasn't grown up enough yet to be a mount.");
|
|
}
|
|
else
|
|
debugServerConsoleMsg(null, "PCD script: createPetFromData: ** pet_lib.doMountOutOfScaleCheck FALLTHROUGH... Perhaps there was a data error. It didn't return 1, 2, 3, or 5.");
|
|
}
|
|
}
|
|
//END MOUNT-RELATED SCRIPT FUNCTIONALITY
|
|
|
|
obj_id pet = null;
|
|
|
|
if(getPosture(master) != POSTURE_UPRIGHT)
|
|
{
|
|
sendSystemMessage(master, SID_SYS_CANT_CALL);
|
|
return;
|
|
}
|
|
|
|
//this B.S. is used for creating droids in ships for JTL and it's TERRIBLE:
|
|
if(!isIdValid(objContainer))
|
|
{
|
|
// Move pet slightly off of the master
|
|
location loc = getLocation(master);
|
|
|
|
pet = create.object(creatureName, loc, true, true);
|
|
}
|
|
else
|
|
{
|
|
// it goes into a container
|
|
pet = create.object(creatureName, objContainer, false, true);
|
|
}
|
|
|
|
if(!isIdValid(pet))
|
|
{
|
|
sendSystemMessage(master, pet_lib.SID_PRIVATE_HOUSE);
|
|
return;
|
|
}
|
|
|
|
setObjVar(pet, "ai.pet", true);
|
|
|
|
callable.setCallableCD(pet, petControlDevice);
|
|
|
|
if(!hasScript(pet, "ai.pet"))
|
|
{
|
|
{
|
|
attachScript(pet, "ai.pet");
|
|
}
|
|
}
|
|
|
|
sendDirtyObjectMenuNotification(petControlDevice);
|
|
|
|
string masterFaction = factions.getFaction(master);
|
|
|
|
if(masterFaction != null && masterFaction != "")
|
|
{
|
|
if(masterFaction == "Rebel" || masterFaction == "Imperial")
|
|
{
|
|
setObjVar(pet, factions.FACTION, masterFaction);
|
|
}
|
|
}
|
|
|
|
pet_lib.makePet(pet, master);
|
|
|
|
if(hasObjVar(petControlDevice, "ai.petAdvance.growthStage"))
|
|
{
|
|
int growthStage = getIntObjVar(petControlDevice, "ai.petAdvance.growthStage");
|
|
|
|
if(hasObjVar(petControlDevice, "pet.crafted"))
|
|
{
|
|
pet_lib.setCraftedPetStatsByGrowth(petControlDevice, pet, growthStage);
|
|
}
|
|
else
|
|
{
|
|
pet_lib.setPetStatsByGrowth(pet, growthStage);
|
|
}
|
|
|
|
int level = getLevel(pet);
|
|
utils.setScriptVar(pet, "ai.level", level);
|
|
}
|
|
else
|
|
{
|
|
if(hasObjVar(petControlDevice, "pet.crafted"))
|
|
{
|
|
setCraftedPetStatsByGrowth(petControlDevice, pet, 10);
|
|
|
|
if(ai_lib.aiGetNiche(pet) == NICHE_DROID || ai_lib.aiGetNiche(pet) == NICHE_ANDROID)
|
|
{
|
|
initDroidCraftedInventoryDroid(pet, petControlDevice);
|
|
initDroidCraftedDatapadDroid(pet, petControlDevice);
|
|
initDroidPersonality(pet, petControlDevice);
|
|
initDroidMedicalPower(pet, petControlDevice);
|
|
initDroidCraftingPower(pet, petControlDevice, master);
|
|
initDroidRepairPower(pet, petControlDevice);
|
|
|
|
reviseCombatDroidPCD(petControlDevice, master, pet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setPetStatsByGrowth(pet, 10);
|
|
}
|
|
}
|
|
|
|
initializeTrainedPetAbilities(petControlDevice, pet);
|
|
|
|
// reduce stats due to vitality loss
|
|
pet_lib.reduceStatsForVitality(pet, petControlDevice);
|
|
|
|
//restore old damage and wounds:
|
|
pet_lib.restoreDamage(pet, petControlDevice);
|
|
|
|
//restore learned commands:
|
|
if(hasObjVar(petControlDevice, "ai.pet.command"))
|
|
{
|
|
initializePetCommandList(pet, petControlDevice);
|
|
}
|
|
|
|
if(hasObjVar(petControlDevice, "alreadyTrained"))
|
|
{
|
|
setObjVar(pet, "alreadyTrained", true);
|
|
}
|
|
|
|
if(hasObjVar(petControlDevice, "ai.pet.trainedMount"))
|
|
{
|
|
pet_lib.mountablePetInit(pet, master);
|
|
}
|
|
|
|
if(!ai_lib.isDroid(pet) && !ai_lib.isAndroid(pet))
|
|
{
|
|
restoreCustomization(pet, petControlDevice);
|
|
}
|
|
else if(hasObjVar(petControlDevice, pet_lib.VAR_PALVAR_BASE))
|
|
{
|
|
restoreCustomization(pet, petControlDevice);
|
|
|
|
if(hasObjVar(petControlDevice, pet_lib.VAR_PALVAR_CNT))
|
|
{
|
|
int cnt = getIntObjVar(petControlDevice, pet_lib.VAR_PALVAR_CNT);
|
|
|
|
if(cnt > -1)
|
|
{
|
|
cnt--;
|
|
setObjVar(petControlDevice, pet_lib.VAR_PALVAR_CNT, cnt);
|
|
|
|
if(cnt < 6)
|
|
{
|
|
switch(cnt)
|
|
{
|
|
case 5: customizationFadeToWhite(pet, 0.15f);
|
|
sendSystemMessage(master, SID_SYS_CUST_FADING);
|
|
break;
|
|
case 4: customizationFadeToWhite(pet, 0.20f);
|
|
sendSystemMessage(master, SID_SYS_CUST_FADING);
|
|
break;
|
|
case 3: customizationFadeToWhite(pet, 0.25f);
|
|
sendSystemMessage(master, SID_SYS_CUST_FADING);
|
|
break;
|
|
case 2: customizationFadeToWhite(pet, 0.30f);
|
|
sendSystemMessage(master, SID_SYS_CUST_FADING);
|
|
break;
|
|
case 1: customizationFadeToWhite(pet, 0.35f);
|
|
sendSystemMessage(master, SID_SYS_CUST_FADING);
|
|
break;
|
|
case 0: customizationFadeToWhite(pet, 0.40f);
|
|
sendSystemMessage(master, SID_SYS_CUST_GONE);
|
|
removeObjVar(petControlDevice, pet_lib.VAR_PALVAR_CNT);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Perform droid module initialization
|
|
if(ai_lib.isDroid(pet) || ai_lib.isAndroid(pet))
|
|
{
|
|
// Copy module data objvars.
|
|
copyObjVar(petControlDevice, pet, "module_data");
|
|
|
|
// Add any module scripts.
|
|
if(hasObjVar(petControlDevice, "module_data.merchant_barker"))
|
|
attachScript(pet, "systems.crafting.droid.modules.merchant_barker");
|
|
if(hasObjVar(petControlDevice, "module_data.bomb_level"))
|
|
attachScript(pet, "systems.crafting.droid.modules.droid_bomb");
|
|
if(hasObjVar(petControlDevice, "module_data.stimpack_capacity"))
|
|
attachScript(pet, "systems.crafting.droid.modules.stimpack_dispensor");
|
|
if(hasObjVar(petControlDevice, "module_data.auto_repair_power"))
|
|
attachScript(pet, "systems.crafting.droid.modules.auto_repair");
|
|
if(hasObjVar(petControlDevice, "module_data.playback.modules"))
|
|
attachScript(pet, "systems.crafting.droid.modules.playback");
|
|
if(hasObjVar(petControlDevice, "module_data.harvest_power"))
|
|
attachScript(pet, "systems.crafting.droid.modules.harvest_module");
|
|
if(hasObjVar(petControlDevice, "module_data.entertainer_effects"))
|
|
attachScript(pet, "systems.crafting.droid.modules.entertainer_effect");
|
|
if(hasObjVar(petControlDevice, "module_data.struct_maint"))
|
|
attachScript(pet, "systems.crafting.droid.modules.structure_maintenance");
|
|
if(hasObjVar(petControlDevice, "module_data.trap_bonus"))
|
|
attachScript(pet, "systems.crafting.droid.modules.trap_thrower");
|
|
if(hasObjVar(petControlDevice, "module_data.dancing_droid"))
|
|
attachScript(pet, "ai.dancing_droid");
|
|
if(hasObjVar(petControlDevice, "module_data.sampling_power"))
|
|
attachScript(pet, "ai.hand_sampling");
|
|
}
|
|
|
|
string name = getAssignedName(petControlDevice);
|
|
setName(pet, name);
|
|
|
|
callable.setCallableLinks(master, petControlDevice, pet);
|
|
}
|
|
|
|
void customizationFadeToWhite(obj_id pet, float percent) {
|
|
|
|
ranged_int_custom_var[] ricv = hue.getPalcolorVars(pet);
|
|
|
|
resizeable palcolor_custom_var[] pcv = new palcolor_custom_var[0];
|
|
|
|
if(ricv == null || ricv.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < ricv.length; i++)
|
|
{
|
|
if(ricv[i].isPalColor())
|
|
{
|
|
pcv = utils.addElement(pcv, (palcolor_custom_var)ricv[i]);
|
|
}
|
|
}
|
|
|
|
if(pcv == null || pcv.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int j = 0; j < pcv.length; j++)
|
|
{
|
|
color old_color = pcv[j].getSelectedColor();
|
|
|
|
int old_r = old_color.getR();
|
|
int old_g = old_color.getG();
|
|
int old_b = old_color.getB();
|
|
int old_a = old_color.getA();
|
|
|
|
float d_r = (255 - old_r) * percent;
|
|
float d_g = (255 - old_g) * percent;
|
|
float d_b = (255 - old_b) * percent;
|
|
float d_a = (255 - old_a) * percent;
|
|
|
|
int new_r = old_r + (int)d_r;
|
|
int new_g = old_g + (int)d_g;
|
|
int new_b = old_b + (int)d_b;
|
|
int new_a = old_a + (int)d_a;
|
|
|
|
color new_color = new color(new_r, new_g, new_b, new_a);
|
|
|
|
pcv[j].setToClosestColor(new_color);
|
|
}
|
|
}
|
|
|
|
int modifyPaletteIndex(int idx)
|
|
{
|
|
int series_idx = idx % 8;
|
|
|
|
if(series_idx == 0)
|
|
{
|
|
return idx;
|
|
}
|
|
|
|
return idx-1;
|
|
}
|
|
|
|
|
|
void initDroidCraftedInventoryDroid(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
obj_id inv = getObjectInSlot(petControlDevice, "inventory");
|
|
|
|
if(isIdValid(inv))
|
|
{
|
|
setObjVar(pet, "ai.pet.hasContainer", true);
|
|
}
|
|
else
|
|
{
|
|
LOG("LOG_CHANNEL", "droid_deed::initDroidCraftedInventoryDroid -- No inventory detected on PCD intangible object.");
|
|
}
|
|
}
|
|
|
|
void initDroidCraftedDatapadDroid(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
obj_id dpad = getObjectInSlot(petControlDevice, "datapad");
|
|
|
|
if(isIdValid(dpad))
|
|
{
|
|
setObjVar(pet, "ai.pet.hasDatapad", true);
|
|
}
|
|
}
|
|
|
|
void initDroidPersonality(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(hasObjVar(petControlDevice, "ai.diction"))
|
|
{
|
|
string personalityModuleType = getStringObjVar(petControlDevice, "ai.diction");
|
|
|
|
if(personalityModuleType != "droid_default")
|
|
{
|
|
setObjVar(pet, "ai.diction", personalityModuleType);
|
|
}
|
|
else
|
|
{
|
|
removeObjVar(petControlDevice, "ai.diction");
|
|
}
|
|
}
|
|
}
|
|
|
|
void initDroidMedicalPower(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(hasObjVar(petControlDevice, "medpower"))
|
|
{
|
|
float medModuleValue = getFloatObjVar(petControlDevice, "medpower");
|
|
setObjVar(pet, "medpower", medModuleValue);
|
|
}
|
|
}
|
|
|
|
void initDroidCraftingPower(obj_id pet, obj_id petControlDevice, obj_id master)
|
|
{
|
|
if(hasObjVar(petControlDevice, "craftingStation"))
|
|
{
|
|
setObjVar(pet, "crafting.station", 1);
|
|
setObjVar(pet, "crafting.private", 2);
|
|
|
|
/*
|
|
// 8-15-04
|
|
// It appears that this portion of code has never worked. The object is trying to be created into a slot that does not exist
|
|
// for droid type objects.
|
|
|
|
obj_id ingredientHoppper = createObject("object/tangible/hopper/crafting_station_ingredient_hopper_1.iff", pet, "ingredient_hopper");
|
|
|
|
if(isIdValid(ingredientHoppper))
|
|
{
|
|
setOwner(ingredientHoppper, master);
|
|
}
|
|
*/
|
|
if(hasObjVar(petControlDevice, "craftingStationSpace"))
|
|
{
|
|
if(hasObjVar(pet, "crafting.type"))
|
|
{
|
|
int craftingTypeScratchpad = getIntObjVar(pet, "crafting.type");
|
|
setObjVar(pet, "crafting.type", craftingTypeScratchpad | CT_space | CT_reverseEngineering);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(pet, "crafting.type", CT_space | CT_reverseEngineering);
|
|
}
|
|
}
|
|
if(hasObjVar(petControlDevice, "craftingStationStructure"))
|
|
{
|
|
if(hasObjVar(pet, "crafting.type"))
|
|
{
|
|
int craftingTypeScratchpad = getIntObjVar(pet, "crafting.type");
|
|
setObjVar(pet, "crafting.type", craftingTypeScratchpad | CT_installation | CT_furniture);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(pet, "crafting.type", CT_installation | CT_furniture);
|
|
}
|
|
}
|
|
if(hasObjVar(petControlDevice, "craftingStationClothing"))
|
|
{
|
|
if(hasObjVar(pet, "crafting.type"))
|
|
{
|
|
int craftingTypeScratchpad = getIntObjVar(pet, "crafting.type");
|
|
setObjVar(pet, "crafting.type", craftingTypeScratchpad | CT_clothing | CT_armor);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(pet, "crafting.type", CT_clothing | CT_armor);
|
|
}
|
|
}
|
|
if(hasObjVar(petControlDevice, "craftingStationFood"))
|
|
{
|
|
if(hasObjVar(pet, "crafting.type"))
|
|
{
|
|
int craftingTypeScratchpad = getIntObjVar(pet, "crafting.type");
|
|
setObjVar(pet, "crafting.type", craftingTypeScratchpad | CT_food | CT_chemical);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(pet, "crafting.type", CT_food | CT_chemical);
|
|
}
|
|
}
|
|
if(hasObjVar(petControlDevice, "craftingStationWeapon"))
|
|
{
|
|
if(hasObjVar(pet, "crafting.type"))
|
|
{
|
|
int craftingTypeScratchpad = getIntObjVar(pet, "crafting.type");
|
|
setObjVar(pet, "crafting.type", craftingTypeScratchpad | CT_weapon | CT_droid | CT_misc);
|
|
}
|
|
else
|
|
{
|
|
setObjVar(pet, "crafting.type", CT_weapon | CT_droid | CT_misc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void initDroidRepairPower(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(hasObjVar(petControlDevice, "ai.pet.isRepairDroid"))
|
|
{
|
|
setObjVar(pet, "ai.pet.isRepairDroid", true);
|
|
}
|
|
}
|
|
|
|
void restoreCustomization(obj_id pet, obj_id petControlDevice)
|
|
{
|
|
if(hasObjVar(petControlDevice, pet_lib.VAR_PALVAR_BASE))
|
|
{
|
|
obj_var_list ovl = getObjVarList(petControlDevice, pet_lib.VAR_PALVAR_VARS);
|
|
|
|
if(ovl != null)
|
|
{
|
|
int numItem = ovl.getNumItems();
|
|
|
|
for(int i = 0; i < numItem; i++)
|
|
{
|
|
obj_var ov = ovl.getObjVar(i);
|
|
string var = ov.getName();
|
|
int idx = ov.getIntData();
|
|
hue.setColor(pet, var, idx);
|
|
}
|
|
}
|
|
int idx = getIntObjVar(petControlDevice, VAR_PALVAR_VARS +"./private/index_texture_1");
|
|
setRangedIntCustomVarValue(pet, "/private/index_texture_1", idx);
|
|
}
|
|
}
|
|
|
|
void doTargetHarvest(obj_id pet, obj_id master)
|
|
{
|
|
dictionary parms = new dictionary();
|
|
parms.put("droid", pet);
|
|
parms.put("player", master);
|
|
parms.put("target", getLookAtTarget(master));
|
|
messageTo(pet, "doTargetHarvest", parms, 0, false);
|
|
|
|
return;
|
|
}
|
|
|
|
void doPetThrowTrap(obj_id pet, obj_id master, int commandNum)
|
|
{
|
|
dictionary params = new dictionary();
|
|
params.put("droid", pet);
|
|
params.put("player", master);
|
|
params.put("trapNum", commandNum);
|
|
messageTo(pet, "doPetThrowTrap", params, 0, false);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean isNewPlayerHelperDroid(obj_id pet)
|
|
{
|
|
if(isIdValid(pet))
|
|
{
|
|
string name = getCreatureName(pet);
|
|
|
|
if(name != null && name.equals("nhelper_droid"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(name.equals("tutorial_droid"))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
string[] getLightingEffects(obj_id droid)
|
|
{
|
|
// Returns the names of the entertainer lighting effects that the specified droid possesses.
|
|
if(!isIdValid(droid))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(!hasObjVar(droid, "module_data.entertainer_effects"))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int raw_effects = getIntObjVar(droid, "module_data.entertainer_effects");
|
|
|
|
if(raw_effects < 1)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int i = 1;
|
|
resizeable string[] available_effects = new string[0];
|
|
|
|
while(i <= LIGHTING_EFFECTS.length)
|
|
{
|
|
// Effects are decimal packed.
|
|
int result = (int)((raw_effects % Math.pow(10, i)) / Math.pow(10, i - 1));
|
|
|
|
if(result >= 1)
|
|
{
|
|
available_effects = utils.addElement(available_effects, LIGHTING_EFFECTS[i - 1]);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if(available_effects.length < 1)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return available_effects;
|
|
}
|
|
}
|
|
|
|
int calculateMaxTrapLoad(obj_id objItem)
|
|
{
|
|
if(hasObjVar(objItem, "module_data.trap_bonus"))
|
|
{
|
|
return (int) BASE_MAX_TRAP_LOAD + (getIntObjVar(objItem, "module_data.trap_bonus")/4);
|
|
}
|
|
|
|
return (int) BASE_MAX_TRAP_LOAD + (getIntObjVar(callable.getCallableCD(objItem), "module_data.trap_bonus")/4);
|
|
}
|
|
|
|
void cleanOldDroidDeed(obj_id deed) // This function examines a deed for a pet droid for obsolete (pre C.U.) data, and rebuilds the droid pet data ifit finds it
|
|
{
|
|
if(hasObjVar(deed,"armorEffectiveness"))
|
|
removeObjVar(deed,"armorEffectiveness");
|
|
if(hasObjVar(deed,"armorToughness"))
|
|
removeObjVar(deed,"armorToughness");
|
|
|
|
for(int attribNum = 0; attribNum < NUM_ATTRIBUTES; attribNum++)
|
|
{
|
|
if(hasObjVar (deed, "creature_attribs." + create.MAXATTRIBNAMES[attribNum]))
|
|
{
|
|
if(("creature_attribs." + create.MAXATTRIBNAMES[attribNum]) != "creature_attribs.maxHealth")
|
|
removeObjVar(deed, "creature_attribs." + create.MAXATTRIBNAMES[attribNum]);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int getDroidCapLevel(obj_id who, int level)
|
|
{
|
|
if(craftinglib.isTrader(who))
|
|
{
|
|
return 90;
|
|
}
|
|
|
|
return 60;
|
|
}
|
|
|
|
|
|
|
|
void initDroidDefaultStats(obj_id deed)
|
|
{
|
|
dictionary droidDefaultStatsDict = dataTableGetRow(TBL_MOB_STAT_BALANCE, DEFAULT_DROID_LEVEL-1);
|
|
|
|
int creature_attribs_level = DEFAULT_DROID_LEVEL;
|
|
int creature_attribs_maxHealth = droidDefaultStatsDict.getInt("HP");
|
|
int creature_attribs_maxConstitution = 0;
|
|
int creature_attribs_toHitChance = droidDefaultStatsDict.getInt("ToHit");
|
|
int creature_attribs_defenseValue = droidDefaultStatsDict.getInt("Def");
|
|
float creatureAttribs_dps = droidDefaultStatsDict.getFloat("damagePerSecond");
|
|
|
|
int creature_attribs_minDamage = (int)creatureAttribs_dps;
|
|
int creature_attribs_maxDamage = (int)creatureAttribs_dps * 3;
|
|
float creature_attribs_scale = 1.0f;
|
|
string creature_attribs_rangedWeapon = null;
|
|
|
|
int creature_attribs_general_protection = getIntObjVar(deed, "creature_attribs.general_protection");
|
|
|
|
if(creature_attribs_general_protection == 0)
|
|
{
|
|
creature_attribs_general_protection = droidDefaultStatsDict.getInt("Elite_Armor");
|
|
}
|
|
|
|
dictionary droidDeedStats = new dictionary();
|
|
droidDeedStats.put("creature_attribs.level", creature_attribs_level);
|
|
droidDeedStats.put("creature_attribs.maxHealth", creature_attribs_maxHealth);
|
|
droidDeedStats.put("creature_attribs.maxConstitution", creature_attribs_maxConstitution);
|
|
droidDeedStats.put("creature_attribs.general_protection", creature_attribs_general_protection);
|
|
droidDeedStats.put("creature_attribs.toHitChance", creature_attribs_toHitChance);
|
|
droidDeedStats.put("creature_attribs.defenseValue", creature_attribs_defenseValue);
|
|
droidDeedStats.put("creature_attribs.minDamage", creature_attribs_minDamage);
|
|
droidDeedStats.put("creature_attribs.maxDamage", creature_attribs_maxDamage);
|
|
droidDeedStats.put("creature_attribs.rangedWeapon", "");
|
|
|
|
writeOutNewDroidDeedAttribs(deed, droidDeedStats);
|
|
}
|
|
|
|
|
|
void initDroidCombatStats(obj_id deed, dictionary droidCombatStats)
|
|
{
|
|
int tempCreatureLevel = 0;
|
|
int tempCreatureDamageLevel = 0;
|
|
int tempCreatureDefenseLevel = 0;
|
|
int tempCreatureProtectionLevel = 0;
|
|
|
|
int tempArmorModule = droidCombatStats.getInt("armorModuleValue");
|
|
|
|
if(tempArmorModule > 0)
|
|
{
|
|
if(tempArmorModule <= 2)
|
|
{
|
|
tempCreatureLevel = 5;
|
|
tempCreatureDefenseLevel = 5;
|
|
tempCreatureProtectionLevel = DEFENSE_MODULE_PROTECT_LVL;
|
|
}
|
|
else if(tempArmorModule <= 4)
|
|
{
|
|
tempCreatureLevel = 10;
|
|
tempCreatureDefenseLevel = 10;
|
|
tempCreatureProtectionLevel = (2 * DEFENSE_MODULE_PROTECT_LVL);
|
|
}
|
|
else if(tempArmorModule <= 6)
|
|
{
|
|
tempCreatureLevel = 15;
|
|
tempCreatureDefenseLevel = 15;
|
|
tempCreatureProtectionLevel = (3 * DEFENSE_MODULE_PROTECT_LVL);
|
|
}
|
|
else if(tempArmorModule <= 8)
|
|
{
|
|
tempCreatureLevel = 20;
|
|
tempCreatureDefenseLevel = 20;
|
|
tempCreatureProtectionLevel = (4 * DEFENSE_MODULE_PROTECT_LVL);
|
|
}
|
|
else if(tempArmorModule <= 10)
|
|
{
|
|
tempCreatureLevel = 25;
|
|
tempCreatureDefenseLevel = 25;
|
|
tempCreatureProtectionLevel = (5 * DEFENSE_MODULE_PROTECT_LVL);
|
|
}
|
|
else
|
|
{
|
|
tempCreatureLevel = 30;
|
|
tempCreatureDefenseLevel = 30;
|
|
tempCreatureProtectionLevel = (6 * DEFENSE_MODULE_PROTECT_LVL);
|
|
}
|
|
}
|
|
|
|
int tempCombatModule = droidCombatStats.getInt("combatModuleValue");
|
|
|
|
if(tempCombatModule > 0)
|
|
{
|
|
if(tempCombatModule <= 50)
|
|
{
|
|
tempCreatureLevel += 5;
|
|
tempCreatureDamageLevel += 5;
|
|
tempCreatureProtectionLevel += COMBAT_MODULE_PROTECT_LVL;
|
|
}
|
|
else
|
|
{
|
|
tempCreatureLevel += tempCombatModule / 10;
|
|
tempCreatureDamageLevel += tempCombatModule / 10;
|
|
tempCreatureProtectionLevel += (COMBAT_MODULE_PROTECT_LVL * (int)(tempCombatModule / 100));
|
|
}
|
|
}
|
|
|
|
//Normalize any off-kilter creature level values. ifwe're a non-combat droid, use level 1. ifwe're a combat droid, then make sure we didn't go over level 30.
|
|
if(tempCreatureLevel < 1 || tempCreatureDamageLevel < 0)
|
|
{
|
|
tempCreatureLevel = 1;
|
|
tempCreatureDamageLevel = 1;
|
|
}
|
|
else if(tempCreatureLevel > 90 || tempCreatureDamageLevel > 90)
|
|
{
|
|
tempCreatureLevel = 90;
|
|
tempCreatureDamageLevel = 90;
|
|
}
|
|
|
|
dictionary droidDefaultStatsDict = dataTableGetRow(TBL_MOB_STAT_BALANCE, tempCreatureLevel - 1);
|
|
|
|
int creature_attribs_level = tempCreatureLevel;
|
|
int creature_attribs_maxHealth = droidDefaultStatsDict.getInt("HP");
|
|
int creature_attribs_toHitChance = droidDefaultStatsDict.getInt("ToHit");
|
|
int creature_attribs_defenseValue = droidDefaultStatsDict.getInt("Def");
|
|
float creature_attribs_dps = droidDefaultStatsDict.getFloat("damagePerSecond");
|
|
int creature_attribs_minDamage = Math.round(creature_attribs_dps);
|
|
int creature_attribs_maxDamage = Math.round(creature_attribs_dps * 3.0f);
|
|
|
|
int creature_attribs_general_protection = droidCombatStats.getInt("creature_attribs.general_protection");
|
|
|
|
if(creature_attribs_general_protection == 0)
|
|
{
|
|
creature_attribs_general_protection = droidDefaultStatsDict.getInt("Elite_Armor");
|
|
}
|
|
|
|
string creature_attribs_rangedWeapon = null;
|
|
|
|
|
|
if(tempCreatureLevel > 1) // This is a combat droid. Initialize stats appropriate to a combat droid
|
|
{
|
|
setObjVar(deed, "combatModule", tempCombatModule); //Not sure why we still do this, but I'm afraid to remove it.
|
|
|
|
creature_attribs_maxHealth = dataTableGetInt(TBL_MOB_STAT_BALANCE, tempCreatureLevel - 1, "HP");
|
|
|
|
if(tempCreatureDefenseLevel > 1)
|
|
{
|
|
int temp_droid_attribs_defenseValue = dataTableGetInt(TBL_MOB_STAT_BALANCE, tempCreatureDefenseLevel - 1, "Def");
|
|
|
|
if(temp_droid_attribs_defenseValue > creature_attribs_defenseValue)
|
|
creature_attribs_defenseValue = temp_droid_attribs_defenseValue;
|
|
}
|
|
|
|
if(tempCreatureProtectionLevel > creature_attribs_general_protection)
|
|
{
|
|
creature_attribs_general_protection = tempCreatureProtectionLevel;
|
|
}
|
|
|
|
if(tempCreatureDamageLevel > 1)
|
|
{
|
|
if(hasObjVar(deed, "pet.nonCombatDroid")) //ifit's a combat version of the DZ-70 or Probe Droid,
|
|
{
|
|
removeObjVar (deed, "pet.nonCombatDroid"); //the re-enable it's combat capabilities
|
|
}
|
|
|
|
setObjVar(deed, "pet.combatDroid", 1);
|
|
|
|
string droidName = getStringObjVar(deed, "creature_attribs.type");
|
|
dictionary droidModDamageStatsDict = dataTableGetRow(TBL_MOB_STAT_BALANCE, tempCreatureDamageLevel-1);
|
|
int temp_droidMod_MinDamage = Math.round(droidModDamageStatsDict.getFloat("damagePerSecond"));
|
|
|
|
if(temp_droidMod_MinDamage > creature_attribs_minDamage)
|
|
{
|
|
creature_attribs_minDamage = temp_droidMod_MinDamage;
|
|
}
|
|
|
|
int temp_droidMod_MaxDamage = Math.round(droidModDamageStatsDict.getFloat("damagePerSecond") * 3.0f);
|
|
|
|
if(temp_droidMod_MaxDamage > creature_attribs_maxDamage)
|
|
{
|
|
creature_attribs_maxDamage = temp_droidMod_MaxDamage;
|
|
}
|
|
|
|
int temp_droidMod_ToHit = droidModDamageStatsDict.getInt("ToHit");
|
|
|
|
if(temp_droidMod_ToHit > creature_attribs_toHitChance)
|
|
{
|
|
creature_attribs_toHitChance = temp_droidMod_ToHit;
|
|
}
|
|
|
|
//dictionary droidDict = utils.dataTableGetRow(TBL_DROID_COMBAT, droidName);
|
|
int droidRangedCapable = utils.dataTableGetInt(TBL_DROID_COMBAT, droidName, "hasRangedAttack");
|
|
|
|
if(droidRangedCapable == 1)
|
|
{
|
|
creature_attribs_rangedWeapon = DROID_RANGED_WEAPON;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setObjVar(deed, "pet.nonCombatDroid", 1);
|
|
}
|
|
|
|
dictionary droidDeedStats = new dictionary();
|
|
droidDeedStats.put("creature_attribs.level", creature_attribs_level);
|
|
droidDeedStats.put("creature_attribs.maxHealth", creature_attribs_maxHealth);
|
|
droidDeedStats.put("creature_attribs.general_protection", creature_attribs_general_protection);
|
|
droidDeedStats.put("creature_attribs.toHitChance", creature_attribs_toHitChance);
|
|
droidDeedStats.put("creature_attribs.defenseValue", creature_attribs_defenseValue);
|
|
droidDeedStats.put("creature_attribs.minDamage", creature_attribs_minDamage);
|
|
droidDeedStats.put("creature_attribs.maxDamage", creature_attribs_maxDamage);
|
|
|
|
if(creature_attribs_rangedWeapon != null && !creature_attribs_rangedWeapon.equals(""))
|
|
{
|
|
droidDeedStats.put("creature_attribs.rangedWeapon", creature_attribs_rangedWeapon);
|
|
}
|
|
|
|
writeOutNewDroidDeedAttribs(deed, droidDeedStats);
|
|
return;
|
|
}
|
|
|
|
|
|
void writeOutNewDroidDeedAttribs(obj_id deed, dictionary droidDeedStats)
|
|
{
|
|
int creature_attribs_level = droidDeedStats.getInt("creature_attribs.level");
|
|
int creature_attribs_maxHealth = droidDeedStats.getInt("creature_attribs.maxHealth");
|
|
int creature_attribs_general_protection = droidDeedStats.getInt("creature_attribs.general_protection");
|
|
int creature_attribs_toHitChance = droidDeedStats.getInt("creature_attribs.toHitChance");
|
|
int creature_attribs_defenseValue = droidDeedStats.getInt("creature_attribs.defenseValue");
|
|
int creature_attribs_minDamage = droidDeedStats.getInt("creature_attribs.minDamage");
|
|
int creature_attribs_maxDamage = droidDeedStats.getInt("creature_attribs.maxDamage");
|
|
string creature_attribs_rangedWeapon = droidDeedStats.getString("creature_attribs.rangedWeapon");
|
|
|
|
setObjVar(deed, "creature_attribs.level", creature_attribs_level);
|
|
setObjVar(deed, "creature_attribs.maxHealth", creature_attribs_maxHealth);
|
|
setObjVar(deed, "creature_attribs.maxConstitution", 0);
|
|
setObjVar(deed, "creature_attribs.general_protection", creature_attribs_general_protection);
|
|
setObjVar(deed, "creature_attribs.toHitChance", creature_attribs_toHitChance);
|
|
setObjVar(deed, "creature_attribs.defenseValue", creature_attribs_defenseValue);
|
|
setObjVar(deed, "creature_attribs.minDamage", creature_attribs_minDamage);
|
|
setObjVar(deed, "creature_attribs.maxDamage", creature_attribs_maxDamage);
|
|
setObjVar(deed, "creature_attribs.aggroBonus", 0.0f);
|
|
setObjVar(deed, "creature_attribs.critChance", 0.0f);
|
|
setObjVar(deed, "creature_attribs.critSave", 0.0f);
|
|
setObjVar(deed, "creature_attribs.scale", 1.0f);
|
|
setObjVar(deed, "creature_attribs.stateResist", 0.0f);
|
|
|
|
if(creature_attribs_rangedWeapon != null && !creature_attribs_rangedWeapon.equals(""))
|
|
{
|
|
setObjVar(deed, "creature_attribs.rangedWeapon", creature_attribs_rangedWeapon);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
void reviseCombatDroidPCD(obj_id petControlDevice, obj_id master, obj_id pet)
|
|
{
|
|
|
|
if(!hasObjVar(petControlDevice, "postCuPCD"))
|
|
{
|
|
// --------------------------------------------------- REV PCD to new post-cu standards. NEXT time the pet is called, it will have current stats.
|
|
int combatStrength = 0;
|
|
|
|
if(hasObjVar(petControlDevice, "pet.combatDroid"))
|
|
{
|
|
combatStrength = (int)(getFloatObjVar(petControlDevice, "pet.combatDroid"));
|
|
|
|
cleanOldDroidDeed(petControlDevice);
|
|
|
|
dictionary droidCombatStats = new dictionary();
|
|
|
|
if(hasObjVar(petControlDevice, "armorModule"))
|
|
{
|
|
droidCombatStats.put("armorModuleValue", getIntObjVar(petControlDevice,"armorModule"));
|
|
}
|
|
|
|
if(combatStrength > 0)
|
|
{
|
|
droidCombatStats.put("combatModuleValue", combatStrength);
|
|
}
|
|
|
|
initDroidCombatStats(petControlDevice, droidCombatStats);
|
|
|
|
messageTo(pet, "handlePackRequest", null, 1, false); //since the pet is already out with wrong stats, store it with good stats
|
|
dictionary respawnParams = new dictionary();
|
|
respawnParams.put("master", master);
|
|
messageTo(petControlDevice, "respawnDroid", respawnParams, 5, false); //respawn the droid after a few seconds, hopefully with new stats
|
|
}
|
|
else
|
|
{
|
|
initDroidDefaultStats(petControlDevice);
|
|
}
|
|
|
|
setObjVar(petControlDevice, "postCuPCD", 2);
|
|
}
|
|
else if(getIntObjVar(petControlDevice, "postCuPCD") == 1)
|
|
{
|
|
if(hasObjVar(petControlDevice, "pet.nonCombatDroid"))
|
|
{
|
|
dictionary droidCombatStats = new dictionary();
|
|
|
|
if(hasObjVar(petControlDevice, "armorModule"))
|
|
{
|
|
droidCombatStats.put("armorModuleValue", getIntObjVar(petControlDevice,"armorModule"));
|
|
}
|
|
|
|
droidCombatStats.put("combatModuleValue", 300);
|
|
|
|
initDroidCombatStats(petControlDevice, droidCombatStats);
|
|
|
|
messageTo(pet, "handlePackRequest", null, 1, false); //since the pet is already out with wrong stats, store it with good stats
|
|
dictionary respawnParams = new dictionary();
|
|
respawnParams.put("master", master);
|
|
messageTo(petControlDevice, "respawnDroid", respawnParams, 5, false); //respawn the droid after a few seconds, hopefully with new stats
|
|
}
|
|
|
|
setObjVar(petControlDevice, "postCuPCD", 2);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void openLearnCommandSui(obj_id pet, obj_id player)
|
|
{
|
|
if(!pet_lib.getLearnableCommandList(pet))
|
|
{
|
|
sendSystemMessage(player, new string_id("pet/pet_Ability", "no_learnable_commands"));
|
|
return;
|
|
}
|
|
|
|
string title = utils.packStringId (new string_id("pet/pet_ability", "learn_command_title"));
|
|
string prompt = utils.packStringId (new string_id("pet/pet_ability", "learn_command_header"));
|
|
|
|
int pid = createSUIPage(sui.SUI_LISTBOX, pet, player, "handleLearnCommandDialog");
|
|
|
|
setSUIProperty(pid, "", "Size", "450,575");
|
|
setSUIProperty(pid, sui.LISTBOX_TITLE, sui.PROP_TEXT, title);
|
|
setSUIProperty(pid, sui.LISTBOX_PROMPT, sui.PROP_TEXT, prompt);
|
|
|
|
// Add buttons.
|
|
sui.listboxButtonSetup(pid, sui.OK_CANCEL);
|
|
setSUIProperty(pid, sui.LISTBOX_BTN_OK, sui.PROP_TEXT, "@train");
|
|
setSUIProperty(pid, sui.LISTBOX_BTN_CANCEL, sui.PROP_TEXT, "@cancel");
|
|
|
|
clearSUIDataSource(pid, sui.LISTBOX_DATASOURCE);
|
|
|
|
resizeable string[] commandList = utils.getResizeableStringArrayScriptVar(pet, "ai.learnCommand.commandNameList");
|
|
|
|
for(int i = 0; i < commandList.size(); i++)
|
|
{
|
|
addSUIDataItem(pid, sui.LISTBOX_DATASOURCE, "" + i);
|
|
setSUIProperty(pid, sui.LISTBOX_DATASOURCE + "." + i, sui.PROP_TEXT, commandList[i]);
|
|
}
|
|
|
|
//subscribeToSUIEvent(pid, sui_event_type.SET_onGenericSelection, sui.LISTBOX_LIST, "handleLearnCommandUpdate");
|
|
//subscribeToSUIPropertyForEvent(pid, sui_event_type.SET_onGenericSelection, sui.LISTBOX_LIST, sui.LISTBOX_LIST, sui.PROP_SELECTEDROW);
|
|
|
|
subscribeToSUIProperty(pid, sui.LISTBOX_LIST, sui.PROP_SELECTEDROW);
|
|
subscribeToSUIProperty(pid, sui.LISTBOX_TITLE, sui.PROP_TEXT);
|
|
|
|
showSUIPage(pid);
|
|
flushSUIPage(pid);
|
|
|
|
utils.setScriptVar(pet, "ai.learnCommand.pid", pid);
|
|
utils.setScriptVar(pet, "ai.learnCommand.pet", pet);
|
|
}
|
|
|
|
boolean hasPetControlDevice(obj_id pet)
|
|
{
|
|
if(isValidId(pet) && exists(pet))
|
|
{
|
|
return callable.hasCallableCD(pet);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
obj_id getPetControlDevice(obj_id pet)
|
|
{
|
|
obj_id petControlDevice = obj_id.NULL_ID;
|
|
|
|
if(exists(pet) && isValidId(pet) && callable.hasCallableCD(pet))
|
|
{
|
|
petControlDevice = callable.getCallableCD(pet);
|
|
}
|
|
|
|
return petControlDevice;
|
|
}
|
|
|
|
void destroyOfficerPets(obj_id player)
|
|
{
|
|
obj_id datapad = utils.getPlayerDatapad(player);
|
|
|
|
if(!isIdValid(datapad) || !exists(datapad))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id[] dataItems = getContents(datapad);
|
|
|
|
if(dataItems == null || dataItems.length < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < dataItems.length; i++)
|
|
{
|
|
if(hasObjVar(dataItems[i], "pet.creatureName"))
|
|
{
|
|
if(getStringObjVar(dataItems[i], "pet.creatureName").startsWith("officer_reinforcement_"))
|
|
{
|
|
destroyObject(dataItems[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void addToHarvestDroidArray(obj_id target, obj_id[] players)
|
|
{
|
|
if(!isIdValid(target) || players == null || players.length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for(int i=0;i < players.length; i++)
|
|
{
|
|
if(callable.hasCallable(players[i], callable.CALLABLE_TYPE_COMBAT_PET))
|
|
{
|
|
obj_id objCallable = callable.getCallable(players[i], callable.CALLABLE_TYPE_COMBAT_PET);
|
|
|
|
if(!isIdValid(objCallable) || !exists(objCallable) || !hasScript(objCallable, "systems.crafting.droid.modules.harvest_module"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
resizeable obj_id[] toHarvest = new obj_id[0];
|
|
|
|
if(utils.hasScriptVar(objCallable, DROID_HARVEST_ARRAY))
|
|
{
|
|
toHarvest = utils.getResizeableObjIdArrayScriptVar(objCallable, DROID_HARVEST_ARRAY);
|
|
}
|
|
|
|
utils.addElement(toHarvest, target);
|
|
|
|
utils.setScriptVar(objCallable, DROID_HARVEST_ARRAY, toHarvest);
|
|
messageTo(objCallable, "runHarvestRoutine", null, 1.0f, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean hasMountName(obj_id pcd)
|
|
{
|
|
// The name exists if the pcd is no longer valid
|
|
if(!isIdValid(pcd) || !exists(pcd))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(!hasObjVar(pcd, "pet.mountName"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void setMountName(obj_id pcd, string mountName)
|
|
{
|
|
// The name exists if the pcd is no longer valid
|
|
if(!isIdValid(pcd) || !exists(pcd))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setName(pcd, mountName);
|
|
|
|
setObjVar(pcd, "pet.mountName", mountName);
|
|
|
|
obj_id yourPad = getContainedBy(pcd);
|
|
obj_id player = getContainedBy(yourPad);
|
|
|
|
if(!isIdValid(player) || !exists(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id mount = callable.getCDCallable(pcd);
|
|
|
|
if(!isIdValid(mount) || !exists(mount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setName(mount, mountName);
|
|
}
|
|
|
|
string getMountName(obj_id pcd)
|
|
{
|
|
if(hasObjVar(pcd, "pet.mountName"))
|
|
{
|
|
return getStringObjVar(pcd, "pet.mountName");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
//Pass in the player and through magic determine if they are mounted!
|
|
boolean isMounted(obj_id player)
|
|
{
|
|
if(isIdValid(player))
|
|
{
|
|
if(getState(player, STATE_RIDING_MOUNT) > 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
obj_id[] getPcdsForType(obj_id master, int type)
|
|
{
|
|
obj_id pDataPad = utils.getPlayerDatapad(master);
|
|
obj_id[] dPadContents = utils.getContents(pDataPad, false);
|
|
|
|
resizeable obj_id[] resizedContents = new obj_id[0];
|
|
|
|
for(int i = 0; i < dPadContents.length; ++i)
|
|
{
|
|
if(isPetType(dPadContents[i], type))
|
|
utils.addElement(resizedContents, dPadContents[i]);
|
|
}
|
|
|
|
obj_id[] pcdsForType = new obj_id[resizedContents.size()];
|
|
resizedContents.toArray(pcdsForType);
|
|
return pcdsForType;
|
|
}
|
|
|
|
obj_id validateDroidCommand(obj_id player)
|
|
{
|
|
obj_id droid = callable.getCallable(player, callable.CALLABLE_TYPE_COMBAT_PET);
|
|
|
|
if(!isIdValid(droid) || !exists(droid))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(!pet_lib.isDroidPet(droid))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(isIncapacitated(droid) || isDead(droid))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_id petControlDevice = callable.getCallableCD(droid);
|
|
|
|
if(!isIdValid(petControlDevice) || !exists(petControlDevice))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int powerLevel = getIntObjVar(petControlDevice, "ai.pet.powerLevel");
|
|
|
|
if(powerLevel >= OUT_OF_POWER)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return droid;
|
|
}
|