include library.ai_lib; include library.pet_lib; include library.beast_lib; include library.utils; include library.vehicle; const int CALLABLE_TYPE_UNKNOWN = -1; const int CALLABLE_TYPE_COMBAT_OTHER = 0; const int CALLABLE_TYPE_COMBAT_PET = 1; const int CALLABLE_TYPE_FAMILIAR = 2; const int CALLABLE_TYPE_RIDEABLE = 3; const int CALLABLE_TYPE_SHIP = 4; const string OBJVAR_CALLABLE_TYPE_COMBAT_OTHER = "callable.type.combat_other"; const string OBJVAR_CALLABLE_TYPE_COMBAT_PET = "callable.type.combat_pet"; const string OBJVAR_CALLABLE_TYPE_FAMILIAR = "callable.type.familiar"; const string OBJVAR_CALLABLE_TYPE_RIDEABLE = "callable.type.rideable"; const string OBJVAR_CALLABLE_CALLED = "pet.called"; const string OBJVAR_CALLABLE_CONTROL_DEVICE = "pet.controlDevice"; const string SCRIPTVAR_RIDEABLE_PACKED_CALLABLE = "callable.recently_packed"; const int MAX_STORED_COMBAT_PETS = 20; const int MAX_STORED_FAMILIARS = 40; // Base Number of Rideables a player may have in their datapad. This number can be added to via an objvar. const int BASE_MAX_STORED_RIDEABLES = 40; // Overall Max Number of Rideables a player may have in their datapad. This is the hard cap. const int ABSOLUTE_MAX_STORED_RIDEABLES = 150; // The value of this objvar can increase the max number of rideables a player may have in their datapad. const string ADDON_MAX_STORED_RIDEABLES_OBJVAR = "rideables.addonMaxStoredRideables"; void clog(string text) { if(text != null) { LOG("callable beasts", text); } } int getControlDeviceType(obj_id cd) { if(beast_lib.isValidBCD(cd)) { return CALLABLE_TYPE_COMBAT_PET; } if(hasObjVar(cd, "ai.pet.trainedMount") || hasScript(cd, "systems.vehicle_system.vehicle_control_device")) { return CALLABLE_TYPE_RIDEABLE; } if(hasScript(cd, "space.ship_control_device.ship_control_device")) { return CALLABLE_TYPE_SHIP; } int petType = -1; if(hasObjVar(cd, "ai.pet.type")) { petType = getIntObjVar(cd, "ai.pet.type"); } switch(petType) { case pet_lib.PET_TYPE_NON_AGGRO: case pet_lib.PET_TYPE_AGGRO: case pet_lib.PET_TYPE_NPC: case pet_lib.PET_TYPE_DROID: return CALLABLE_TYPE_COMBAT_PET; case pet_lib.PET_TYPE_FAMILIAR: return CALLABLE_TYPE_FAMILIAR; } return CALLABLE_TYPE_UNKNOWN; } int getCallableType(obj_id objCallable) { if(!isIdValid(objCallable) || !exists(objCallable)) { return CALLABLE_TYPE_UNKNOWN; } if((beast_lib.isBeast(objCallable) || pet_lib.isDroidPet(objCallable) || ai_lib.aiGetNiche(objCallable) == NICHE_NPC) && pet_lib.getPetType(objCallable) != pet_lib.PET_TYPE_FAMILIAR) { return CALLABLE_TYPE_COMBAT_PET; } if( pet_lib.isFamiliarPetType(objCallable) ) { return CALLABLE_TYPE_FAMILIAR; } if(hasCallableCD(objCallable)) { obj_id petControlDevice = getCallableCD(objCallable); if((isValidId(petControlDevice) && exists(petControlDevice) && hasObjVar(petControlDevice, "ai.pet.trainedMount")) || pet_lib.isVehiclePet(objCallable)) { return CALLABLE_TYPE_RIDEABLE; } } if(hasObjVar(objCallable, "ai.pet.buddy")) { return CALLABLE_TYPE_COMBAT_OTHER; } return CALLABLE_TYPE_UNKNOWN; } string getCallableTypeObjVar(int type) { switch(type) { case CALLABLE_TYPE_COMBAT_OTHER: return OBJVAR_CALLABLE_TYPE_COMBAT_OTHER; case CALLABLE_TYPE_COMBAT_PET: return OBJVAR_CALLABLE_TYPE_COMBAT_PET; case CALLABLE_TYPE_FAMILIAR: return OBJVAR_CALLABLE_TYPE_FAMILIAR; case CALLABLE_TYPE_RIDEABLE: return OBJVAR_CALLABLE_TYPE_RIDEABLE; default: // Unknown type return null; } } obj_id getCallable(obj_id master, int objCallableType) { if(!beast_lib.isValidPlayer(master)) { return null; } string strObjVar = getCallableTypeObjVar(objCallableType); if(strObjVar == null) { return null; } if(utils.hasScriptVar(master, strObjVar)) { obj_id objCallable = utils.getObjIdScriptVar(master, strObjVar); if(isIdValid(objCallable) && exists(objCallable)) { return objCallable; } } return null; } obj_id[] getCallables(obj_id master) { int [] callableTypes = { CALLABLE_TYPE_COMBAT_OTHER, CALLABLE_TYPE_COMBAT_PET, CALLABLE_TYPE_FAMILIAR, CALLABLE_TYPE_RIDEABLE }; resizeable obj_id[] callables = new obj_id[0]; for(int i = 0; i < callableTypes.length; i++) { obj_id objCallable = getCallable(master, callableTypes[i]); if(isIdValid(objCallable) && exists(objCallable)) { callables = utils.addElement(callables, objCallable); } } return callables; } obj_id[] getDatapadCallablesByType(obj_id master, int callableType) { resizeable obj_id[] callables = new obj_id[0]; obj_id datapad = utils.getPlayerDatapad(master); if(!isIdValid(datapad)) { return null; } obj_id[] dataItems = getContents(datapad); for(int i = 0; i < dataItems.length; i++) { if(getControlDeviceType(dataItems[i]) == callableType) { callables = utils.addElement(callables, dataItems[i]); } } return callables; } boolean hasCallable(obj_id master, int objCallableType) { return hasCallable(master, objCallableType, obj_id.NULL_ID); } boolean hasCallable(obj_id master, int objCallableType, obj_id beast) { if(!beast_lib.isValidPlayer(master)) { return true; } string strObjVar = getCallableTypeObjVar(objCallableType); if(strObjVar == null) { return true; } if(utils.hasScriptVar(master, strObjVar)) { obj_id objCallable = utils.getObjIdScriptVar(master, strObjVar); if(isIdValid(beast)) { if(isIdValid(objCallable) && exists(objCallable) && beast != objCallable) { return true; } } else { if(isIdValid(objCallable) && exists(objCallable)) { return true; } } } return false; } boolean hasAnyCallable(obj_id master) { if(hasCallable(master, CALLABLE_TYPE_COMBAT_OTHER) || hasCallable(master, CALLABLE_TYPE_COMBAT_PET) || hasCallable(master, CALLABLE_TYPE_FAMILIAR) || hasCallable(master, CALLABLE_TYPE_RIDEABLE)) { return true; } return false; } boolean setCallable(obj_id master, obj_id objCallable, int objCallableType) { if(!beast_lib.isValidPlayer(master)) { return false; } if(objCallableType == -1 && isIdValid(objCallable) && exists(objCallable)) { objCallableType = getCallableType(objCallable); } string strObjVar = getCallableTypeObjVar(objCallableType); if(strObjVar == null) { return false; } // If the objCallable is null, then remove the script var. if(objCallableType != -1 && !isIdValid(objCallable)) { utils.removeScriptVar(master, strObjVar); return true; } if(hasCallable(master, objCallableType)) { return false; } utils.setScriptVar(master, strObjVar, objCallable); return true; } obj_id getCDCallable(obj_id cd) { if(!isIdValid(cd) || !exists(cd)) { return null; } if(utils.hasScriptVar(cd, OBJVAR_CALLABLE_CALLED)) { obj_id objCallable = utils.getObjIdScriptVar(cd, OBJVAR_CALLABLE_CALLED); if(isIdValid(objCallable) && exists(objCallable)) { return objCallable; } } return null; } boolean hasCDCallable(obj_id cd) { if(!isIdValid(cd) || !exists(cd)) { return false; } if(utils.hasScriptVar(cd, OBJVAR_CALLABLE_CALLED)) { obj_id objCallable = utils.getObjIdScriptVar(cd, OBJVAR_CALLABLE_CALLED); if(isIdValid(objCallable) && exists(objCallable)) { return true; } } return false; } boolean setCDCallable(obj_id cd, obj_id objCallable) { if(!isIdValid(cd) || !exists(cd)) { return false; } if(isIdValid(objCallable) && exists(objCallable)) { utils.setScriptVar(cd, OBJVAR_CALLABLE_CALLED, objCallable); } else { utils.removeScriptVar(cd, OBJVAR_CALLABLE_CALLED); } return true; } obj_id getCallableCD(obj_id objCallable) { if(!isIdValid(objCallable) || !exists(objCallable)) { return null; } if(hasObjVar(objCallable, OBJVAR_CALLABLE_CONTROL_DEVICE)) { obj_id cd = getObjIdObjVar(objCallable, OBJVAR_CALLABLE_CONTROL_DEVICE); if(isIdValid(cd) && exists(cd)) { return cd; } } return null; } boolean hasCallableCD(obj_id objCallable) { if(!isIdValid(objCallable) || !exists(objCallable)) { return false; } if(hasObjVar(objCallable, OBJVAR_CALLABLE_CONTROL_DEVICE)) { obj_id cd = getObjIdObjVar(objCallable, OBJVAR_CALLABLE_CONTROL_DEVICE); if(isIdValid(cd) && exists(cd)) { return true; } } return false; } boolean setCallableCD(obj_id objCallable, obj_id cd) { if(!isIdValid(cd) || !exists(cd) || !isIdValid(objCallable) || !exists(objCallable)) { return false; } setObjVar(objCallable, OBJVAR_CALLABLE_CONTROL_DEVICE, cd); return true; } void setCallableLinks(obj_id master, obj_id cd, obj_id objCallable) { setCallable(master, objCallable, getCallableType(objCallable)); setCDCallable(cd, objCallable); setCallableCD(objCallable, cd); setMaster(objCallable, master); //DO NOT SET THE MASTER AS THE OWNER OF AN OBJECT OR YOU OWN ITS INVENTORY THIS INCLUDES WEAPONS AND STUFF //we only need to set the master as the owner if we are a droid with an inventory module if(pet_lib.isDroidPet(objCallable)) { obj_id inventory = utils.getInventoryContainer(objCallable); //this should prevent opening combat droids inventories if(isIdValid(inventory)) setOwner(inventory, master); } } void storeCallable(obj_id master, obj_id objCallable) { if(!isIdValid(objCallable) || !exists(objCallable)) { return; } if(beast_lib.isBeast(objCallable)) { beast_lib.storeBeast(beast_lib.getBeastBCD(objCallable)); } else { int callableType = getCallableType(objCallable); clog("callableType: " + callableType + " pet_lib.isVehiclePet(objCallable): " + pet_lib.isVehiclePet(objCallable)); if(pet_lib.isVehiclePet(objCallable)) { vehicle.storeVehicle(getCallableCD(objCallable), master, false); } else { pet_lib.storePet(objCallable, master); } setCallable(master, null, callableType); } } void storeCallables(obj_id master) { if(!isIdValid(master) || !exists(master) || !hasAnyCallable(master)) { return; } int [] callableTypes = { CALLABLE_TYPE_COMBAT_OTHER, CALLABLE_TYPE_COMBAT_PET, CALLABLE_TYPE_FAMILIAR, CALLABLE_TYPE_RIDEABLE }; for(int i = 0; i < callableTypes.length; i++) { obj_id objCallable = getCallable(master, callableTypes[i]); if(isIdValid(objCallable) && exists(objCallable)) { storeCallable(master, objCallable); } } } void restoreCallable(obj_id master) { if(!isIdValid(master) || !exists(master) || hasCallable(master, CALLABLE_TYPE_COMBAT_PET) || !utils.hasScriptVar(master, SCRIPTVAR_RIDEABLE_PACKED_CALLABLE)) { return; } obj_id cd = utils.getObjIdScriptVar(master, SCRIPTVAR_RIDEABLE_PACKED_CALLABLE); if(beast_lib.isValidBCD(cd)) { beast_lib.createBeastFromBCD(master, cd); } utils.removeScriptVar(master, SCRIPTVAR_RIDEABLE_PACKED_CALLABLE); } void killCallable(obj_id objCallable, obj_id killer) { if(!isIdValid(objCallable) || !exists(objCallable)) { return; } if(beast_lib.isBeast(objCallable)) { if(!isDead(objCallable)) { beast_lib.killBeast(objCallable, killer); } } else { if(objCallable.isLoaded() && ai_lib.aiIsDead(objCallable) && !isDead(objCallable)) { utils.setScriptVar(objCallable, "killer", killer); pet_lib.killPet(objCallable); } } } void killCallables(obj_id master, obj_id killer) { if(!isIdValid(master) || !exists(master) || !hasAnyCallable(master)) { return; } int [] callableTypes = { CALLABLE_TYPE_COMBAT_OTHER, CALLABLE_TYPE_COMBAT_PET, CALLABLE_TYPE_FAMILIAR, CALLABLE_TYPE_RIDEABLE }; for(int i = 0; i < callableTypes.length; i++) { obj_id objCallable = getCallable(master, callableTypes[i]); if(isIdValid(objCallable) && exists(objCallable)) { killCallable(objCallable, killer); } } } int getNumStoredCDByType(obj_id master, int CDType) { if(!isIdValid(master) || !exists(master)) { return 0; } obj_id datapad = utils.getPlayerDatapad(master); if(!isIdValid(datapad)) { return 0; } obj_id[] dataItems = getContents(datapad); if(dataItems == null || dataItems.length < 1) { return 0; } int numStored = 0; for(int i = 0; i < dataItems.length; i++) { if(getControlDeviceType(dataItems[i]) == CDType) { numStored++; } } return numStored; } // Returns the number of rideables (vehicles and mounts) that the player may have in their datapad. // These are "active" rideables that a player can use, so does not include any stored in a barn or garage. int getMaxAllowedStoredRideables(obj_id player) { int totalMaxStoredRideables = BASE_MAX_STORED_RIDEABLES; // Check for an addon to the max amount of stored rideables on this player over the base amount. int addonMaxStoredRideables = getAddonMaxAllowedStoredRideables(player); if ( addonMaxStoredRideables < 0 ) addonMaxStoredRideables = 0; totalMaxStoredRideables += addonMaxStoredRideables; // the total cannot be more than the absolute cap if ( totalMaxStoredRideables > ABSOLUTE_MAX_STORED_RIDEABLES ) totalMaxStoredRideables = ABSOLUTE_MAX_STORED_RIDEABLES; return totalMaxStoredRideables; } int getAddonMaxAllowedStoredRideables(obj_id player) { int addonAmount = 0; if ( hasObjVar(player, ADDON_MAX_STORED_RIDEABLES_OBJVAR) ) { addonAmount = getIntObjVar(player, ADDON_MAX_STORED_RIDEABLES_OBJVAR); } return addonAmount; } // Returns true or false based on whether the player already had the maximum number of rideables (vehicles and mounts) boolean hasMaxStoredRideables(obj_id player) { int totalMaxStoredRideables = getMaxAllowedStoredRideables(player); if( getNumStoredCDByType(player, CALLABLE_TYPE_RIDEABLE) >= totalMaxStoredRideables ) { return true; } return false; } // Increases the addon number of rideables (vehicles and mounts) for the player over the base amount. // Logs success or failure, plus the amount added. // Logs the source if there is one. boolean increaseAddonMaxStoredRideables(obj_id player, int amountIncreased) { return increaseAddonMaxStoredRideables(player, amountIncreased, obj_id.NULL_ID); } boolean increaseAddonMaxStoredRideables(obj_id player, int amountIncreased, obj_id source) { int currentAddonAmount = getAddonMaxAllowedStoredRideables(player); int newAddonMaxAmount = currentAddonAmount + amountIncreased; if ( newAddonMaxAmount < 0 ) { string negativeAddonLog = "Increased Addon for Max Stored Rideables *FAILED*: ("+player+") "+getName(player)+": has failed to increase max stored rideables - would be negative addon amount: attempted new addon = "+newAddonMaxAmount+"(current addon ["+currentAddonAmount+"] + attempted increase to addon["+amountIncreased+"])."; if ( isIdValid(source) ) { negativeAddonLog += " Source of Increase: "+getEncodedName(source)+" ("+source+")."; } CustomerServiceLog("max_stored_rideables", negativeAddonLog); return false; } int currentMaxAllowedStoredRideables = getMaxAllowedStoredRideables(player); int newMaxAllowedStoredRideables = BASE_MAX_STORED_RIDEABLES + newAddonMaxAmount; if ( newMaxAllowedStoredRideables > ABSOLUTE_MAX_STORED_RIDEABLES ) { string overCapLog = "Increased Addon for Max Stored Rideables *FAILED*: ("+player+") "+getName(player)+": has failed to increase max stored rideables - would be over the cap: attempted new max = "+newMaxAllowedStoredRideables+"(Base Amount ["+BASE_MAX_STORED_RIDEABLES+"] + current addon ["+currentAddonAmount+"] + attempted increase to addon["+amountIncreased+"])."; if ( isIdValid(source) ) { overCapLog += " Source of Increase: "+getEncodedName(source)+" ("+source+")."; } CustomerServiceLog("max_stored_rideables", overCapLog); return false; } setObjVar(player, ADDON_MAX_STORED_RIDEABLES_OBJVAR, newAddonMaxAmount); string successLog = "Increased Addon for Max Stored Rideables *SUCCESS*: ("+player+") "+getName(player)+": successfully increased max stored rideables: new max = "+newMaxAllowedStoredRideables+" (Base Amount ["+BASE_MAX_STORED_RIDEABLES+"] + current addon ["+currentAddonAmount+"] + increase to addon["+amountIncreased+"])."; if ( isIdValid(source) ) { successLog += " Source of Increase: "+getEncodedName(source)+" ("+source+")."; } CustomerServiceLog("max_stored_rideables",successLog); return true; } boolean hasMaxStoredCombatPets(obj_id master) { if(getNumStoredCDByType(master, CALLABLE_TYPE_COMBAT_PET) >= MAX_STORED_COMBAT_PETS) { return true; } return false; }