mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-31 00:15:54 -04:00
4777 lines
126 KiB
Plaintext
4777 lines
126 KiB
Plaintext
include java.lang.Math;
|
|
include java.util.StringTokenizer;
|
|
include library.ai_lib;
|
|
include library.beast_lib;
|
|
include library.buff;
|
|
include library.city;
|
|
include library.colors;
|
|
include library.combat;
|
|
include library.dot;
|
|
include library.factions;
|
|
include library.group;
|
|
include library.locations;
|
|
include library.loot;
|
|
include library.luck;
|
|
include library.money;
|
|
include library.pet_lib;
|
|
include library.prose;
|
|
include library.static_item;
|
|
include library.sui;
|
|
include library.utils;
|
|
include library.vehicle;
|
|
include library.xp;
|
|
include library.weapons;
|
|
|
|
const java.text.NumberFormat floatFormat = new java.text.DecimalFormat("###.##");
|
|
|
|
// stealth script lib...
|
|
// Hiding and stealth functionality
|
|
// Hiding is defined as stationary invisibility
|
|
// Stealth is defined as invisibility that allows movement
|
|
|
|
const int MAX_BUILDINGS_FOR_WILDNERNESS = 5; // the max number of buildings that can be in the immediate area for it to be considered "wildnerness"
|
|
const int MIN_BLENDIN_CROWD_SIZE = 10;
|
|
const string CAMO_KIT_ID = "camoKit";
|
|
const string ACTIVE_HEP = "active_hep";
|
|
const int MIN_MOVEMENT_PRIORITY = 3; // minimum invis buff priority to allow movement
|
|
const int MIN_POSTURE_CHANGE_PRIORITY = 4; // minimum invis buff priority to allow posture changes
|
|
|
|
// Cover defines
|
|
const string INVIS_BREAK_RADIUS_FAR = "invis_break_far";
|
|
const string INVIS_BREAK_RADIUS_NEAR = "invis_break__near";
|
|
const string INVIS_UPKEEP_MSG_DISPATCHED = "invis_upkeep_dispatched";
|
|
const string TRAP_PROXIMITY_VOLUME = "trap_proximity";
|
|
const string SENSOR_PROXIMITY_VOLUME = "sensor_proximity";
|
|
|
|
const float INVIS_BREAK_MAX_FAR_DISTANCE = 20f;
|
|
const float INVIS_BREAK_NEAR_DISTANCE = 8f;
|
|
|
|
const int PASSIVE_BREACH_FAR =2;
|
|
const int PASSIVE_BREACH_NEAR =1;
|
|
|
|
const float PASSIVE_DETECT_CHANCE_FAR =15.0f; //15% base passive chance to detect given same level and mods
|
|
const float PASSIVE_DETECT_CHANCE_NEAR =45.0f; //45% base passive chance to detect given same level and mods
|
|
const float PASSIVE_DETECT_CHANCE_MOB =20.0f; //20% base passive chance to detect given same level and mods
|
|
const string INCOGNITO_OLD_FACTION = "stealth.incognitoFaction";
|
|
|
|
const float MIN_CHANCE_TO_DETECT_HIDDEN = 1f;
|
|
const float MAX_CHANCE_TO_DETECT_HIDDEN = 99f;
|
|
const float PASSIVE_DETECT_CHANCE = 20f;
|
|
const float ACTIVE_DETECT_CHANCE = 50f;
|
|
const float MAX_HIDING_DETECT_SCORE = 220;
|
|
|
|
const int HEP_UPKEEP_COST = 10; // 1 power used per second b/c the upkeep tick is 10 seconds
|
|
const string HEP_BASE_POWER = "basePower";
|
|
const string HEP_EFFECTIVENESS = "effectiveness";
|
|
|
|
const int STEAL_NOTHING = 0; // can't steal from Mob
|
|
const int STEAL_CREDITS = 1; // steal credits from mob
|
|
const int STEAL_LOOT = 2; // generate a(n) item from the mark's loot table
|
|
const int STEAL_TEMPLATE = 4; // steal an item based on a template stored in an objvar on the mark
|
|
const int STEAL_MARKED_ITEMS = 8; // steal an eligible item from the mark's inventory, if it is marked for theft
|
|
const int STEAL_STEALING_TABLE = 16; // steal stuff as its listed in the stealingloot.tab
|
|
|
|
const float STEAL_LOOT_CHANCE_MOD = -0.90f; // stealing has 90% less chance to generate an item over killing the mob
|
|
const int STEAL_FAIL_CHANCE = 50; // 50% chance of stealing an item
|
|
const int STEAL_REGULAR_ITEM = 75;
|
|
const int STEAL_RARE_ITEM = 98;
|
|
const string STEAL_TYPE = "stealing.type";
|
|
const string STEAL_TEMPLATE_ITEMS = "stealing.availableLoot";
|
|
const string STEAL_ITEM_IS_STEALABLE = "stealing.isStealable";
|
|
|
|
const string TRAP_TR_ACCURACY = "trap.accuracy";
|
|
const string TRAP_POWER = "trap.power";
|
|
const string TRIGGER_TYPE = "trap.triggerType";
|
|
const string TRAP_TYPE = "trap.trapType";
|
|
const string TIMER_TIME = "trap.detonateTime";
|
|
const string PROXIMITY_RANGE = "trap.proximityRange";
|
|
const string TRAP_TRIGGER_ID = "trap.triggerId";
|
|
const string TRAP_MY_TRAP = "trap.myTrap";
|
|
const string TRAP_MY_PROXIMITY_TRIGGER = "trap.myTrap";
|
|
const string TRAP_FACTION = "trap.faction";
|
|
const string TRAP_PVP_TYPE = "trap.pvpType";
|
|
const string TRAP_DIFFICULTY = "trap.difficulty";
|
|
const string TRAP_FREQUENCY = "trap.frequency";
|
|
const string TRAP_GROUP = "trap.groupObject";
|
|
const string DETECT_EFFECTIVENESS = "trap.detect.effectiveness";
|
|
const string CONCEALABLE_DEVICE = "concealable";
|
|
const string CONCEALED_OBJECT = "isObjectConcealed";
|
|
const string CAMO_SKILL = "camouflageSkill";
|
|
const string DETECT_ATTEMPTS = "detectAttempts";
|
|
const string DETECT_TIMES = "detectTimes";
|
|
const string CAMOUFLAGED_AT_LEVEL = "camouflagedAtLevel";
|
|
const string TRAP_LEVEL = "levelOfTrap";
|
|
const string BIO_PROBE_TARGET_NAME = "trap.targetName";
|
|
const string BIO_PROBE_TRAP_TARGET = "trap.probeTarget"; // need this so the trap script will accept the probe
|
|
const string BIO_PROBE_DECAYED = "probe.decayed";
|
|
const string BIO_PROBE_SAMPLE_TIME = "probe.sampleTime";
|
|
const string BIO_PROBE_STORAGE_TIME = "probe.storageTime";
|
|
const string BEACON_BATTERY = "beacon.battery";
|
|
const string BEACON_LOCATION = "beacon.location";
|
|
const string BEACON_TARGET = "beacon.target";
|
|
const string BEACON_TARGET_NAME = "beacon.targetName";
|
|
const string BEACON_MSG_DISPATCHED = "beacon.msgDispatched";
|
|
const string BEACON_SHUTDOWN_PENDING = "beacon.shutdownPending";
|
|
const string BEACON_LAST_KNOWN_LOC = "beacon.lastKnownLoc";
|
|
const string BEACON_LAST_KNOWN_TARGET_NAME = "beacon.lastKnownTargetName";
|
|
const string BEACON_LAST_KNOWN_TARGET = "beacon.lastKnownTarget";
|
|
const string SENSOR_ARMED_TIME = "sensor.armedTime";
|
|
const string BUFF_NO_BREAK_INVIS = "no_break_invis";
|
|
|
|
|
|
const float SENSOR_RANGE = 10f; // 10 meters
|
|
const float BEACON_INTERVAL = 20f; // 20 seconds
|
|
|
|
const int TRIGGER_TYPE_TIMER = 1;
|
|
const int TRIGGER_TYPE_REMOTE = 2;
|
|
const int TRIGGER_TYPE_PROXIMITY = 3;
|
|
|
|
const int TRAP_CALTROP = 1;
|
|
const int TRAP_FLASHBANG = 2;
|
|
const int TRAP_HX2 = 3;
|
|
const int TRAP_KAMINODART = 4;
|
|
|
|
const int[] ALL_TRAP_TYPES = new int[]{TRAP_CALTROP, TRAP_FLASHBANG, TRAP_HX2, TRAP_KAMINODART};
|
|
const int[] ALL_TRIGGER_TYPES = new int[]{TRIGGER_TYPE_TIMER, TRIGGER_TYPE_REMOTE, TRIGGER_TYPE_PROXIMITY};
|
|
|
|
|
|
const float TRAP_MIN_DISTANCE_BETWEEN = 10f; // closes trap must be at least 10 meters away
|
|
const float TRAP_SELF_DESTRUCT_TIMEOUT = 60 * 10; // 10 minutes
|
|
const float TRAP_HX2_BLAST_RADIUS = 15f;
|
|
const float TRAP_FLASHBANG_BLAST_RADIUS = 20f;
|
|
const float TRAP_CALTROP_BLAST_RADIUS = 10f;
|
|
const float TRAP_KAMINODART_BLAST_RADIUS = 10f;
|
|
|
|
// Trap Effectiveness Defines
|
|
const float KAMINODART_MAX_DURATION = 30;
|
|
const float KAMINODART_MAX_POTENCY = 175;
|
|
const float KAMINODART_MAX_STRENGTH = 175;
|
|
|
|
const int CALTROP_MAX_SNARE = 65; // 65%
|
|
const int CALTROP_MAX_DURATION = 30; // 30 seconds
|
|
|
|
const int FLASHBANG_MAX_COMBAT_SLOW = -100;
|
|
const int FLASHBANG_MAX_DURATION = 60;
|
|
|
|
const int TRAP_HX2_MIN_BASE_DAMAGE = 4000;
|
|
const int TRAP_HX2_MAX_BASE_DAMAGE = 5500;
|
|
|
|
// Detect Hidden defines
|
|
const float BASE_DETECT_CAMOUFLAGE_DISTANCE = 20;
|
|
const int PLAYER_DETECT_SAFETY_INTERVAL = 60; // one minute
|
|
const int MAX_INVIS_BUFF_TO_DETECT = 5; // set to 5 to include forceCloak and without a trace
|
|
|
|
// Disarm Trap
|
|
const float BASE_DISARM_CHANCE = 55f; // 55% -- (was 30%) changed by EJ
|
|
const float MAX_CHANCE_TO_DISARM = 95f;
|
|
const float MIN_CHANCE_TO_DISARM = 10f;
|
|
const float PERCENT_EFFECT_MOD_DISARM_FAIL = 2f; // 200% effect penalty when you fail to disarm a trap (double damage, snare, etc)
|
|
const float CHAFF_FLARE_DISTANCE = 15f; // 15 feet
|
|
const float MAX_BIO_PROBE_STORAGE_TIME = 3600; // 60 minutes
|
|
const float MAX_PROBE_DISTANCE = 10f;
|
|
|
|
// A list of regions in which the use of traps are restricted.
|
|
// dathomir_fs_village_unpassable = the township of Aurilia
|
|
const string[] TRAP_RESTRICTED_REGIONS = { "dathomir_fs_village_unpassable" };
|
|
|
|
|
|
/*
|
|
Buff Priority Movement Allowed Posture Change Allowed
|
|
---- -------- ---------------- ----------------------
|
|
invis_blendIn 1 None None
|
|
invis_camouflage 2 None None
|
|
invis_cover 3 Crawl (Implied) None
|
|
invis_urbanStealth 4 All (snared) All
|
|
invis_wildernessStealth 4 All (snared) All
|
|
invis_forceCloak 5 All All
|
|
|
|
*/
|
|
|
|
void setBioProbeData(obj_id trap, obj_id target, string targetName)
|
|
{
|
|
}
|
|
|
|
void setTriggerData(obj_id trap, int triggerType, int accuracy, int param)
|
|
{
|
|
setObjVar(trap, TRAP_TR_ACCURACY, accuracy);
|
|
setObjVar(trap, TRIGGER_TYPE, triggerType);
|
|
|
|
switch(triggerType)
|
|
{
|
|
case TRIGGER_TYPE_TIMER:
|
|
setObjVar(trap, TIMER_TIME, param);
|
|
break;
|
|
case TRIGGER_TYPE_REMOTE:
|
|
string frequency = "" + rand(0, 999) + "." + rand(0, 999) + "." + rand(0, 999);
|
|
setObjVar(trap, stealth.TRAP_FREQUENCY, frequency);
|
|
break;
|
|
case TRIGGER_TYPE_PROXIMITY:
|
|
setObjVar(trap, PROXIMITY_RANGE, param);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void createRangerLoot(int level, string object, obj_id container, int count)
|
|
{
|
|
object = toLower(object);
|
|
string template = "";
|
|
int type = 1;
|
|
int trigType = 1;
|
|
int power = getPowerLevel(level, false);
|
|
|
|
if(count < 0)
|
|
{
|
|
count = getNumLootItems(level, container);
|
|
}
|
|
|
|
int trigHash = getStringCrc(object.substring(0, 3));
|
|
int trapHash = getStringCrc(object.substring(4));
|
|
|
|
switch(trigHash)
|
|
{
|
|
case ##"prx":
|
|
trigType = TRIGGER_TYPE_PROXIMITY;
|
|
break;
|
|
case ##"tmr":
|
|
trigType = TRIGGER_TYPE_TIMER;
|
|
break;
|
|
case ##"rmt":
|
|
trigType = TRIGGER_TYPE_REMOTE;
|
|
break;
|
|
default:
|
|
trapHash = getStringCrc(object);
|
|
break;
|
|
}
|
|
|
|
switch(trapHash)
|
|
{
|
|
case ##"trap":
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
type = ALL_TRAP_TYPES[rand(0, ALL_TRAP_TYPES.length-1)];
|
|
trigType = ALL_TRIGGER_TYPES[rand(0, ALL_TRIGGER_TYPES.length-1)];
|
|
createTrapObject(container, getPowerLevel(level, true), type, 1, trigType, getPowerLevel(level, true), rand(5, 20));
|
|
}
|
|
break;
|
|
case ##"sensor":
|
|
createMotionSensorObject(container, power, count);
|
|
break;
|
|
case ##"flare":
|
|
createChaffFlareObject(container, power, count);
|
|
break;
|
|
case ##"hep":
|
|
createHEPObject(container, power, count);
|
|
break;
|
|
case ##"bioprobe":
|
|
createBioProbeObject(container, power, count);
|
|
break;
|
|
case ##"trackbeacon":
|
|
createTrackingBeaconObject(container, power, count);
|
|
break;
|
|
case ##"trapcaltrop":
|
|
createTrapObject(container, power, TRAP_CALTROP, count, trigType, power, 10);
|
|
break;
|
|
case ##"trapflashbang":
|
|
createTrapObject(container, power, TRAP_FLASHBANG, count, trigType, power, 10);
|
|
break;
|
|
case ##"traphx2":
|
|
createTrapObject(container, power, TRAP_HX2, count, trigType, power, 10);
|
|
break;
|
|
case ##"trapkamino":
|
|
createTrapObject(container, power, TRAP_KAMINODART, count, trigType, power, 10);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void createRangerLoot(obj_id mob, int level, string object, obj_id container)
|
|
{
|
|
createRangerLoot(level, object, container, -1);
|
|
return;
|
|
}
|
|
|
|
int getNumLootItems(int level, obj_id destContainer)
|
|
{
|
|
float lvl = level;
|
|
int numItems = 1 + (int)Math.ceil(level/30);
|
|
|
|
int free = getVolumeFree(destContainer);
|
|
if(free < numItems)
|
|
{
|
|
numItems = free;
|
|
}
|
|
|
|
return numItems;
|
|
}
|
|
|
|
int getPowerLevel(int level, boolean includeVariance)
|
|
{
|
|
if(includeVariance)
|
|
{
|
|
return loot.getCalculatedAttribute(500, 1100, level, loot.MIN_CREATURE_LEVEL, loot.MAX_CREATURE_LEVEL);
|
|
}
|
|
|
|
float minPower = 500;
|
|
float maxPower = 1000;
|
|
|
|
float difference = maxPower - minPower;
|
|
float ratio = level/100f;
|
|
|
|
return (int)Math.ceil(minPower + (difference * ratio));
|
|
}
|
|
|
|
obj_id createHEPObject(obj_id container, int power, int count)
|
|
{
|
|
string template = "object/tangible/scout/misc/hep.iff";
|
|
|
|
obj_id hep = null;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
hep = createObject(template, container, "");
|
|
if(!isIdValid(hep))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(rand(0, 1) == 0)
|
|
{
|
|
setObjVar(hep, HEP_BASE_POWER, power);
|
|
setCount(hep, power);
|
|
setObjVar(hep, HEP_EFFECTIVENESS, Math.round(power / 2));
|
|
}
|
|
else
|
|
{
|
|
setObjVar(hep, HEP_EFFECTIVENESS, power);
|
|
power = Math.round(power/2);
|
|
setObjVar(hep, HEP_BASE_POWER, power);
|
|
setCount(hep, power);
|
|
}
|
|
}
|
|
return hep;
|
|
}
|
|
|
|
obj_id createTrackingBeaconObject(obj_id container, float power, int count)
|
|
{
|
|
string template = "object/tangible/scout/misc/trackingbeacon.iff";
|
|
|
|
obj_id probe = null;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
probe = createObject(template, container, "");
|
|
if(!isIdValid(probe))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
setObjVar(probe, BEACON_BATTERY, power * 3);
|
|
setObjVar(probe, DETECT_EFFECTIVENESS, power);
|
|
setObjVar(probe, BIO_PROBE_STORAGE_TIME, MAX_BIO_PROBE_STORAGE_TIME);
|
|
|
|
}
|
|
return probe;
|
|
}
|
|
|
|
obj_id createBioProbeObject(obj_id container, float power, int count)
|
|
{
|
|
string template = "object/tangible/scout/misc/biosensorprobe.iff";
|
|
|
|
obj_id probe = null;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
|
|
probe = createObject(template, container, "");
|
|
if(!isIdValid(probe))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
setObjVar(probe, BIO_PROBE_STORAGE_TIME, power / 1000 * MAX_BIO_PROBE_STORAGE_TIME);
|
|
}
|
|
return probe;
|
|
}
|
|
|
|
obj_id createMotionSensorObject(obj_id container, float batteryPower, int count)
|
|
{
|
|
string template = "object/tangible/scout/misc/motionsensor.iff";
|
|
|
|
obj_id sensor = null;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
sensor = createObject(template, container, "");
|
|
if(!isIdValid(sensor))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
setObjVar(sensor, BEACON_BATTERY, batteryPower);
|
|
}
|
|
return sensor;
|
|
}
|
|
|
|
obj_id createChaffFlareObject(obj_id container, int power, int count)
|
|
{
|
|
string template = "object/tangible/scout/misc/chaffflare.iff";
|
|
|
|
obj_id flare = null;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
flare = createObject(template, container, "");
|
|
if(!isIdValid(flare))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
setObjVar(flare, DETECT_EFFECTIVENESS, power);
|
|
}
|
|
|
|
return flare;
|
|
}
|
|
|
|
obj_id createTrapObject(obj_id container, int power, int trapType, int count, int triggerType, int triggerAccuracy, int triggerParam)
|
|
{
|
|
string template = "";
|
|
switch(trapType)
|
|
{
|
|
case TRAP_CALTROP:
|
|
template = "object/tangible/scout/trap/trap_ap_caltrop.iff";
|
|
break;
|
|
case TRAP_FLASHBANG:
|
|
template = "object/tangible/scout/trap/trap_ap_flashbang.iff";
|
|
break;
|
|
case TRAP_HX2:
|
|
template = "object/tangible/scout/trap/trap_ap_hx2mine.iff";
|
|
break;
|
|
case TRAP_KAMINODART:
|
|
template = "object/tangible/scout/trap/trap_ap_kaminoandart.iff";
|
|
break;
|
|
default:
|
|
template = "object/tangible/scout/trap/trap_ap_hx2mine.iff";
|
|
break;
|
|
}
|
|
|
|
obj_id trap = null;
|
|
trap = createObject(template, container, "");
|
|
if(!isIdValid(trap))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
setObjVar(trap, TRAP_POWER, power);
|
|
setTriggerData(trap, triggerType, triggerAccuracy, triggerParam);
|
|
setCount(trap, count);
|
|
|
|
return trap;
|
|
}
|
|
|
|
boolean canPlaceMotionSensor(obj_id player, obj_id sensor)
|
|
{
|
|
obj_id[] stuff = getNonCreaturesInRange(getLocation(player), TRAP_MIN_DISTANCE_BETWEEN * 2);
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(hasObjVar(stuff[i], TRAP_TYPE) || hasObjVar(stuff[i], BEACON_BATTERY))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "other_trap"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void placeMotionSensor(obj_id player, obj_id sensor)
|
|
{
|
|
location loc = getLocation(player);
|
|
if(!setLocation(sensor, loc))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setObjVar(sensor, "armed", 1);
|
|
messageTo(sensor, "msgBatteryDead", null, getFloatObjVar(sensor, BEACON_BATTERY), true);
|
|
setObjVar(sensor, SENSOR_ARMED_TIME, getGameTime());
|
|
doAnimationAction(player, "point_down");
|
|
setOwner(sensor, player);
|
|
playClientEffectLoc(sensor, "clienteffect/frs_light_vigilance.cef", getLocation(sensor), 0.0f);
|
|
messageTo(sensor, "msgDeploy", null, 1, false);
|
|
}
|
|
|
|
void tripMotionSensor(obj_id sensor, obj_id breacher, boolean wasFiltered)
|
|
{
|
|
obj_id owner = getOwner(sensor);
|
|
if(!isIdValid(owner))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(hasObjVar(sensor, "lastSent"))
|
|
{
|
|
int lastTime = getIntObjVar(sensor, "lastSent");
|
|
if(lastTime + 30 > getGameTime())
|
|
{
|
|
return; // only send notification every 30 seconds
|
|
}
|
|
}
|
|
|
|
setObjVar(sensor, "lastSent", getGameTime());
|
|
dictionary dic = new dictionary();
|
|
dic.put("sensor", sensor);
|
|
dic.put("breacherId", breacher);
|
|
dic.put("breacherName", isPlayer(breacher)? getName(breacher) : getCreatureName(breacher));
|
|
dic.put("wasFiltered", wasFiltered);
|
|
dic.put("sensorLoc", getLocation(sensor));
|
|
|
|
messageTo(owner, "msgMotionSensorTripped", dic, 1, false);
|
|
|
|
return;
|
|
}
|
|
|
|
void cleanComponentFromContainer(obj_id component, obj_id container)
|
|
{
|
|
if(!isIdValid(container))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!hasObjVar(container, DETECT_EFFECTIVENESS) && !hasObjVar(container, TRAP_TYPE) && !hasObjVar(container, BEACON_BATTERY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_var_list ovl = getObjVarList(component, "trap");
|
|
if(ovl == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int numItems = ovl.getNumItems();
|
|
string name = "";
|
|
for(int i = 0; i < numItems; i++)
|
|
{
|
|
obj_var ov = ovl.getObjVar(i);
|
|
if(ov != null)
|
|
{
|
|
name = "trap." + ov.getName();
|
|
removeObjVar(container, name);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean canActivateTrackingBeacon(obj_id player, obj_id beacon, boolean silent)
|
|
{
|
|
if(!utils.testItemClassRequirements(player, beacon, false, "") ||
|
|
!utils.testItemLevelRequirements(player, beacon, false, ""))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
obj_id target = getBioProbeTarget(beacon);
|
|
if(!isIdValid(target))
|
|
{
|
|
if(!silent)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "bio_sample_bad"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(getFloatObjVar(beacon, BEACON_BATTERY) < BEACON_INTERVAL)
|
|
{
|
|
if(!silent)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "traker_power_low"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(hasObjVar(beacon, BEACON_TARGET))
|
|
{
|
|
if(getObjIdObjVar(beacon, BEACON_TARGET) != target)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "different_sample"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void startTrackingBeacon(obj_id player, obj_id beacon)
|
|
{
|
|
obj_id target = getObjIdObjVar(beacon, BIO_PROBE_TRAP_TARGET);
|
|
if(!isIdValid(target))
|
|
{
|
|
return;
|
|
}
|
|
|
|
dictionary dic = new dictionary();
|
|
dic.put("sendingPlayer", player);
|
|
dic.put("sendingBeacon", beacon);
|
|
|
|
sendSystemMessage(player, new string_id("spam", "uploading_tracking"));
|
|
|
|
removeObjVar(beacon, BEACON_SHUTDOWN_PENDING);
|
|
if(!hasObjVar(beacon, BEACON_MSG_DISPATCHED))
|
|
{
|
|
setObjVar(beacon, BEACON_MSG_DISPATCHED, 1);
|
|
// message to self to update the beacon
|
|
messageTo(beacon, "msgTrackingBeaconUpdate", dic, BEACON_INTERVAL, true);
|
|
messageTo(target, "msgTrackingBeaconLocationRequest", dic, 0, false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void stopTrackingBeacon(obj_id beacon, obj_id player)
|
|
{
|
|
if(hasObjVar(beacon, BEACON_MSG_DISPATCHED))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "requestin_disconnect"));
|
|
setObjVar(beacon, BEACON_SHUTDOWN_PENDING, 1);
|
|
}
|
|
else if(isIdValid(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "connection_severed"));
|
|
removeObjVar(beacon, BEACON_LOCATION);
|
|
removeObjVar(beacon, BEACON_TARGET);
|
|
removeObjVar(beacon, BEACON_TARGET_NAME);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void updateTrackingBeaconDisplay(obj_id beacon)
|
|
{
|
|
obj_id player = utils.getContainingPlayer(beacon);
|
|
removeObjVar(beacon, BEACON_MSG_DISPATCHED);
|
|
if(hasObjVar(beacon, BEACON_SHUTDOWN_PENDING))
|
|
{
|
|
stopTrackingBeacon(beacon, player);
|
|
return;
|
|
}
|
|
|
|
float power = getFloatObjVar(beacon, BEACON_BATTERY);
|
|
power -= BEACON_INTERVAL;
|
|
setObjVar(beacon, BEACON_BATTERY, power);
|
|
|
|
if(!isIdValid(player))
|
|
{
|
|
stopTrackingBeacon(beacon, player);
|
|
return;
|
|
}
|
|
|
|
obj_id target = getObjIdObjVar(beacon, BEACON_TARGET);
|
|
if(!isIdValid(target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "tracking_failed"));
|
|
}
|
|
else
|
|
{
|
|
setObjVar(beacon, BEACON_LAST_KNOWN_LOC, getLocationObjVar(beacon, BEACON_LOCATION));
|
|
setObjVar(beacon, BEACON_LAST_KNOWN_TARGET_NAME, getStringObjVar(beacon, BEACON_TARGET_NAME));
|
|
setObjVar(beacon, BEACON_LAST_KNOWN_TARGET, target);
|
|
|
|
prose_package pp = prose.getPackage(new string_id("spam", "tracker_updated"));
|
|
pp = prose.setTT(pp, target);
|
|
sendSystemMessageProse(player, pp);
|
|
}
|
|
|
|
// try to reactivate the beacon
|
|
if(canActivateTrackingBeacon(player, beacon, false))
|
|
{
|
|
if(!hasObjVar(beacon, BEACON_MSG_DISPATCHED))
|
|
{
|
|
dictionary dic = new dictionary();
|
|
dic.put("sendingPlayer", player);
|
|
dic.put("sendingBeacon", beacon);
|
|
sendSystemMessage(player, new string_id("spam", "requesting_target"));
|
|
setObjVar(beacon, BEACON_MSG_DISPATCHED, 1);
|
|
messageTo(beacon, "msgTrackingBeaconUpdate", dic, BEACON_INTERVAL, true);
|
|
messageTo(getObjIdObjVar(beacon, BEACON_TARGET), "msgTrackingBeaconLocationRequest", dic, 0, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stopTrackingBeacon(beacon, player);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void updateTrackingBeaconData(obj_id beacon, obj_id target, location targetLoc, string targetName)
|
|
{
|
|
setObjVar(beacon, BEACON_LOCATION, targetLoc);
|
|
setObjVar(beacon, BEACON_TARGET, target);
|
|
setObjVar(beacon, BEACON_TARGET_NAME, targetName);
|
|
}
|
|
|
|
obj_id getBioProbeTarget(obj_id thing)
|
|
{
|
|
obj_id target = null;
|
|
if((hasObjVar(thing, TRAP_TYPE) || hasObjVar(thing, BEACON_BATTERY)) && hasObjVar(thing, BIO_PROBE_TRAP_TARGET))
|
|
{
|
|
if(hasObjVar(thing, BIO_PROBE_STORAGE_TIME))
|
|
{
|
|
// found a bio probe
|
|
if(!hasObjVar(thing, BIO_PROBE_DECAYED))
|
|
{
|
|
target = getObjIdObjVar(thing, BIO_PROBE_TRAP_TARGET);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
boolean canBioProbe(obj_id player, obj_id probe, obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
{
|
|
showFlyTextPrivate(player, player, new string_id("spam", "bad_target"), 1.5f, colors.TOMATO);
|
|
return false;
|
|
}
|
|
|
|
if(!ai_lib.isHumanoid(target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "sample_humanoid"));
|
|
return false;
|
|
}
|
|
|
|
if(!pvpCanAttack(player, target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_sample"));
|
|
return false;
|
|
}
|
|
|
|
if(getDistance(getLocation(player), getLocation(target)) > MAX_PROBE_DISTANCE)
|
|
{
|
|
showFlyTextPrivate(player, player, new string_id("spam", "too_far"), 1.5f, colors.TOMATO);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void bioProbe(obj_id player, obj_id probe, obj_id target)
|
|
{
|
|
float effectiveness = getIntObjVar(probe, DETECT_EFFECTIVENESS);
|
|
float timeToDecay = getFloatObjVar(probe, BIO_PROBE_STORAGE_TIME);
|
|
|
|
setObjVar(probe, BIO_PROBE_SAMPLE_TIME, getGameTime());
|
|
messageTo(probe, "msgDecaySample", null, timeToDecay, true);
|
|
|
|
doAnimationAction(player, "heal_other");
|
|
setObjVar(probe, BIO_PROBE_TRAP_TARGET, target);
|
|
setObjVar(probe, BIO_PROBE_TARGET_NAME, isPlayer(target)? getName(target) : "a " + getCreatureName(target));
|
|
|
|
if(isPlayer(target))
|
|
{
|
|
prose_package pp = prose.getPackage(new string_id("spam", "feel_twinge"));
|
|
pp = prose.setTT(pp, player);
|
|
sendSystemMessageProse(target, pp);
|
|
}
|
|
|
|
prose_package pp = prose.getPackage(new string_id("spam", "got_probe"));
|
|
pp = prose.setTT(pp, target);
|
|
sendSystemMessageProse(player, pp);
|
|
|
|
return;
|
|
}
|
|
|
|
void doChaffFlareEffect(obj_id player, obj_id flare)
|
|
{
|
|
doAnimationAction(player, "manipulate_high");
|
|
playClientEffectLoc(player, "appearance/pt_sparking_blast_md.prt", getLocation(player), 0.0f);
|
|
float flareDistance = CHAFF_FLARE_DISTANCE;
|
|
float detectSkill = getEnhancedSkillStatisticModifier(player, "detect_hidden");
|
|
obj_id[] stuff = getObjectsInRange(player, flareDistance);
|
|
boolean foundSomething = false;
|
|
prose_package pp = prose.getPackage(new string_id("spam", "chaff_uncover"));
|
|
flareDistance += (detectSkill / 10);
|
|
|
|
pp = prose.setTT(pp, player);
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(!hasObjVar(stuff[i], TRAP_TYPE) || !validateTrapTarget(stuff[i], player))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int hidePower = getIntObjVar(stuff[i], CAMO_SKILL);
|
|
int chaffPower = getIntObjVar(flare, DETECT_EFFECTIVENESS);
|
|
|
|
if(hidePower > chaffPower)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pp = prose.setTU(pp, stuff[i]);
|
|
|
|
foundSomething = true;
|
|
unconcealDevice(player, stuff[i]);
|
|
}
|
|
|
|
if(foundSomething)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "chaff_foundsomething"));
|
|
doAnimationAction(player, "point_forward");
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "chaff_nothing"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean canDisarmTrap(obj_id player, obj_id trap)
|
|
{
|
|
if(!hasObjVar(trap, TRAP_TYPE) && !hasObjVar(trap, BEACON_BATTERY))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "bad_disarm_target"));
|
|
return false;
|
|
}
|
|
|
|
if(!validateTrapTarget(trap, player) && getOwner(trap) != player)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_disarm"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void disarmTrap(obj_id player, obj_id trap)
|
|
{
|
|
// 100% success disarming your own traps
|
|
if(getOwner(trap) == player || hasObjVar(trap, BEACON_BATTERY))
|
|
{
|
|
showFlyText(trap, new string_id("spam", "disarm_go"), 1.5f, colors.TOMATO);
|
|
sendSystemMessage(player, new string_id("spam", "disarm"));
|
|
playTrapFizzle(trap);
|
|
destroyObject(trap);
|
|
return;
|
|
}
|
|
|
|
float baseChanceToDisarm = BASE_DISARM_CHANCE;
|
|
|
|
// Modify the base chance to detect based on the level & skill mod disparity of the two creatures
|
|
int trapLevel = getIntObjVar(trap, TRAP_LEVEL);
|
|
int trapSkillMod = getIntObjVar(trap, TRAP_DIFFICULTY);
|
|
float trapperScore = trapLevel + trapSkillMod;
|
|
|
|
int diffuserLevel = getLevel(player);
|
|
int diffuserSkillMod = getEnhancedSkillStatisticModifier(player, "ranger_trap");
|
|
int spyBonus = utils.isProfession(player, utils.SPY)? 30 : 0;
|
|
float diffuserScore = diffuserLevel + diffuserSkillMod + spyBonus;
|
|
|
|
|
|
float totalDisparity = diffuserScore - trapperScore;
|
|
float chanceToDetect = baseChanceToDisarm;
|
|
float detectRange = MAX_CHANCE_TO_DISARM - MIN_CHANCE_TO_DISARM;
|
|
|
|
// negative disparity is in favor of the trapper
|
|
// positive disparity is in favor of the diffuser
|
|
|
|
if(totalDisparity > 0) // negative or zero disparity just means the hider had a higher score and the detect chance stays at base chance
|
|
{
|
|
chanceToDetect += (totalDisparity / MAX_HIDING_DETECT_SCORE) * detectRange;
|
|
}
|
|
|
|
// Clamp the values
|
|
if(chanceToDetect > MAX_CHANCE_TO_DISARM)
|
|
{
|
|
chanceToDetect = MAX_CHANCE_TO_DISARM;
|
|
}
|
|
|
|
float roll = rand(0f, 100f);
|
|
// LOG("stealth", "disarmTrap(" + trap + ") " + getName(trap) + ", " + getName(player) + ") -Rolled " + roll + " with a " + chanceToDetect + " detect chance.");
|
|
|
|
if(roll < chanceToDetect)
|
|
{
|
|
//If we're a remote activated trap, message our remote to delete.
|
|
if(hasObjVar(trap, stealth.TRAP_FREQUENCY) )
|
|
{
|
|
obj_id myRemote = getObjIdObjVar(trap, stealth.TRAP_MY_PROXIMITY_TRIGGER);
|
|
|
|
if(exists(myRemote) )
|
|
{
|
|
messageTo(myRemote, "trapDisarmed", null, 1, false);
|
|
}
|
|
}
|
|
|
|
showFlyText(trap, new string_id("spam", "disarm_go"), 1.5f, colors.TOMATO);
|
|
sendSystemMessage(player, new string_id("spam", "disarm"));
|
|
playTrapFizzle(trap);
|
|
destroyObject(trap);
|
|
}
|
|
else
|
|
{
|
|
//If we're a remote activated trap, message our remote to delete.
|
|
if(hasObjVar(trap, stealth.TRAP_FREQUENCY) )
|
|
{
|
|
obj_id myRemote = getObjIdObjVar(trap, stealth.TRAP_MY_PROXIMITY_TRIGGER);
|
|
|
|
if(exists(myRemote) )
|
|
{
|
|
messageTo(myRemote, "trapDisarmed", null, 1, false);
|
|
}
|
|
}
|
|
|
|
Vector target = new Vector();
|
|
target.add(player);
|
|
doTrapDetonateEffect(trap, getIntObjVar(trap, TRAP_TYPE), target, PERCENT_EFFECT_MOD_DISARM_FAIL);
|
|
sendSystemMessage(player, new string_id("spam", "fail_disarm"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean canDetectCamouflage(obj_id player)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void detectCamouflage(obj_id player, boolean findMobiles, boolean findInanimate, float distance, float baseChanceToDetect)
|
|
{
|
|
obj_id[] stuff = getObjectsInRange(player, distance);
|
|
Vector detected = new Vector();
|
|
Vector times = new Vector();
|
|
int now = getGameTime();
|
|
boolean foundSomething = false;
|
|
obj_id target = null;
|
|
prose_package pp = prose.getPackage(new string_id("spam", "revealed_by"));
|
|
string invis = "";
|
|
boolean objIsMobile = false;
|
|
location playerLoc = getLocation(player);
|
|
|
|
playClientEffectLoc(getPlayerCreaturesInRange(playerLoc, 100.0f), "appearance/pt_spy_reveal_wave.prt", playerLoc, 0.0f);
|
|
|
|
// Play different particle effect if its a spy
|
|
if(utils.isProfession(player, utils.SPY))
|
|
{
|
|
invis = getInvisBuff(player);
|
|
|
|
if(invis == null)
|
|
{
|
|
playClientEffectLoc(player, "appearance/pt_spy_expose_shadows.prt", getLocation(player), 0.0f);
|
|
}
|
|
else
|
|
{
|
|
resizeable obj_id[] clients = new Vector();
|
|
clients = utils.addElement(clients, player);
|
|
|
|
dictionary revealList = getPassiveRevealList(player);
|
|
|
|
if (revealList != null)
|
|
{
|
|
resizeable obj_id[] ids = revealList.getResizeableObjIdArray("id");
|
|
if ( (ids != null) && (ids.length != 0) )
|
|
{
|
|
clients = utils.concatArrays(clients, ids);
|
|
}
|
|
}
|
|
|
|
playClientEffectLoc(utils.toStaticObjIdArray(clients), "appearance/pt_spy_expose_shadows.prt", getLocation(player), 0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
playClientEffectLoc(player, "appearance/pt_fireworks_secondary_shockwave2.prt", getLocation(player), 0.0f);
|
|
}
|
|
//LOG("stealth", "Found " + stuff.length + " objects to detect on...");
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
objIsMobile = false;
|
|
target = stuff[i];
|
|
|
|
if(!isIdValid(target))
|
|
continue;
|
|
|
|
// if target is a player, we need to see if we have tried (and failed) to detect them earlier.
|
|
// we don't allow players do detect a player repeatedly. if you fail to detect once, that player
|
|
// is safe from being detected by you for a period of time. This is to deter "detect bots"
|
|
if(isPlayer(target) || ai_lib.isHumanoid(target) || isMob(target))
|
|
{
|
|
objIsMobile = true;
|
|
if(!pvpCanAttack(player, target))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
invis = getInvisBuff(target);
|
|
if(invis == null)
|
|
{
|
|
continue;
|
|
}
|
|
else if(buff.getPriority(invis) > MAX_INVIS_BUFF_TO_DETECT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// force cloak check
|
|
/*if(invis.startsWith("invis_fs_buff_invis"))
|
|
{
|
|
if(!utils.isProfession(player, utils.FORCE_SENSITIVE))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
*/
|
|
// Spy Without a trace
|
|
|
|
if(invis.startsWith("invis_sp_buff_invis_notrace") || invis.startsWith("invis_sp_diversion_stealth"))
|
|
{
|
|
continue;
|
|
|
|
}
|
|
|
|
detected = utils.getResizeableStringBatchObjVar(target, DETECT_ATTEMPTS);
|
|
times = utils.getResizeableStringBatchObjVar(target, DETECT_TIMES);
|
|
if(detected == null)
|
|
{
|
|
detected = new Vector();
|
|
}
|
|
|
|
if(times == null)
|
|
{
|
|
times = new Vector();
|
|
}
|
|
|
|
if(detected.size() != times.size())
|
|
{
|
|
// crap...
|
|
continue;
|
|
}
|
|
|
|
int idx = detected.indexOf(player);
|
|
if(idx > -1)
|
|
{
|
|
int when = ((Integer)times.get(idx)).intValue();
|
|
if(when - now < PLAYER_DETECT_SAFETY_INTERVAL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
times.setElementAt(new Integer(now), idx);
|
|
}
|
|
else
|
|
{
|
|
detected.add(player);
|
|
times.add(new Integer(now));
|
|
}
|
|
|
|
utils.setResizeableBatchObjVar(target, DETECT_ATTEMPTS, detected);
|
|
utils.setResizeableBatchObjVar(target, DETECT_TIMES, times);
|
|
}
|
|
else if(!hasObjVar(target, CONCEALED_OBJECT) || !validateTrapTarget(target, player))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if this code executes we should have either a hidden mobile or trap
|
|
|
|
if (activeDetectHiddenTarget(target, player, baseChanceToDetect))
|
|
{
|
|
if(objIsMobile)
|
|
{
|
|
checkForAndMakeVisible(target);
|
|
}
|
|
else
|
|
{
|
|
unconcealDevice(player, target);
|
|
}
|
|
foundSomething = true;
|
|
playClientEffectLoc(target, "clienteffect/lair_med_damage_smoke.cef", getLocation(target), 0.0f);
|
|
}
|
|
|
|
/*//if(detectHiddenTarget(true, target, player, findMobiles, findInanimate, 0))
|
|
{
|
|
if(objIsMobile)
|
|
{
|
|
buff.removeBuff(target, invis);
|
|
}
|
|
else
|
|
{
|
|
unconcealDevice(player, target);
|
|
}
|
|
|
|
foundSomething = true;
|
|
playClientEffectLoc(target, "clienteffect/lair_med_damage_smoke.cef", getLocation(target), 0.0f);
|
|
}
|
|
*/
|
|
}
|
|
|
|
if(foundSomething)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "detected_something"));
|
|
doAnimationAction(player, "point_forward");
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "no_detect"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean canConcealDevice(obj_id player, obj_id target)
|
|
{
|
|
if(!isIdValid(target) || !exists(target))
|
|
{
|
|
//LOG("stealth", "Conceal target not valid");
|
|
return false;
|
|
}
|
|
|
|
if(!hasObjVar(target, CONCEALABLE_DEVICE))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_conceal_that"));
|
|
return false;
|
|
}
|
|
|
|
if(hasObjVar(target, CONCEALED_OBJECT))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "already_concealed"));
|
|
return false;
|
|
}
|
|
|
|
obj_id owner = getOwner(target);
|
|
if(owner != player)
|
|
{
|
|
if(!group.inSameGroup(player, owner))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_conceal_owner"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(getDistance(getLocation(target), getLocation(player)) > 15) //changed from 5 to 15m for distance to conceal
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "too_far_conceal"));
|
|
return false;
|
|
}
|
|
|
|
//LOG("stealth", "Can Conceal " + getName(target));
|
|
return true;
|
|
}
|
|
|
|
void concealDevice(obj_id player, obj_id target)
|
|
{
|
|
int camoSkill = getEnhancedSkillStatisticModifier(player, "camouflage");
|
|
camoSkill += getEnhancedSkillStatisticModifier(player, "stealth");
|
|
setObjVar(target, CONCEALED_OBJECT, 1);
|
|
setObjVar(target, CAMO_SKILL, camoSkill);
|
|
setObjVar(target, CAMOUFLAGED_AT_LEVEL, getLevel(player));
|
|
|
|
hideFromClient(target, true);
|
|
}
|
|
|
|
void unconcealDevice(obj_id player, obj_id target)
|
|
{
|
|
removeObjVar(target, CONCEALED_OBJECT);
|
|
removeObjVar(target, CAMO_SKILL);
|
|
removeObjVar(target, CAMOUFLAGED_AT_LEVEL);
|
|
hideFromClient(target, false);
|
|
//string name = getName(target);
|
|
//setName(target, name.substring(0, name.indexOf(" [")));
|
|
location loc = getLocation(target);
|
|
loc.y -= 10;
|
|
playClientEffectLoc(player, "clienteffect/frs_dark_vengeance.cef" , loc, 0);
|
|
stopClientEffectObjByLabel(target, "trapConceal");
|
|
doAnimationAction(player, "point_down");
|
|
}
|
|
|
|
boolean canSetTrap(obj_id player, obj_id trap)
|
|
{
|
|
if(!isIdValid(trap) || !hasScript(trap, "systems.skills.stealth.trap"))
|
|
{
|
|
// doesn't happen unless player just types slash command, which they shouldnt see in the first place,
|
|
// so no error msg needed
|
|
return false;
|
|
}
|
|
|
|
//no placing traps while in combat state
|
|
if(ai_lib.isInCombat(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_not_in_combat"));
|
|
return false;
|
|
}
|
|
|
|
if(!utils.isNestedWithin(trap, player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_not_possession"));
|
|
return false;
|
|
}
|
|
|
|
if(isPvPTrap(trap) && !factions.isDeclared(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_not_declared"));
|
|
return false;
|
|
}
|
|
|
|
if(pet_lib.isMounted(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_not_while_mounted"));
|
|
return false;
|
|
}
|
|
|
|
if(!hasObjVar(trap, TRIGGER_TYPE))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_no_trigger_installed"));
|
|
return false;
|
|
}
|
|
|
|
if(locations.isInCity(getLocation(player)))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "no_trap_city"));
|
|
return false;
|
|
}
|
|
|
|
if( inTrapRestrictedRegion(player) )
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_restricted_region"));
|
|
return false;
|
|
}
|
|
|
|
if(utils.isInHouseCellSpace(player))
|
|
{
|
|
obj_id house = getTopMostContainer(player);
|
|
if(hasObjVar( house, "player_structure.admin.adminList" ))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "trap_not_house"));
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
obj_id[] stuff = getNonCreaturesInRange(getLocation(player), TRAP_MIN_DISTANCE_BETWEEN);
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(hasObjVar(stuff[i], TRAP_TYPE))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "other_trap"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean inTrapRestrictedRegion(obj_id player)
|
|
{
|
|
for ( int i = 0; i < TRAP_RESTRICTED_REGIONS.length; i++ )
|
|
{
|
|
if ( groundquests.isInNamedRegion(player, TRAP_RESTRICTED_REGIONS[i]) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void onGetTrapAttributes(obj_id self, obj_id player, string[] names, string[] attribs, int free)
|
|
{
|
|
//int free = getFirstFreeIndex(names);
|
|
if (free == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(hasObjVar(self, TRAP_TYPE))
|
|
{
|
|
int trapTypeInt = getIntObjVar(self, TRAP_TYPE);
|
|
if(trapTypeInt == TRAP_CALTROP)
|
|
{
|
|
float trapRadiusFlt = TRAP_CALTROP_BLAST_RADIUS;
|
|
names[free] = "trap_radius";
|
|
attribs[free++] = "" + trapRadiusFlt + "m";
|
|
}
|
|
|
|
if(trapTypeInt == TRAP_FLASHBANG)
|
|
{
|
|
float trapRadiusFlt = TRAP_FLASHBANG_BLAST_RADIUS;
|
|
names[free] = "trap_radius";
|
|
attribs[free++] = "" + trapRadiusFlt + "m";
|
|
}
|
|
|
|
if(trapTypeInt == TRAP_HX2)
|
|
{
|
|
float trapRadiusFlt = TRAP_HX2_BLAST_RADIUS;
|
|
names[free] = "trap_radius";
|
|
attribs[free++] = "" + trapRadiusFlt + "m";
|
|
}
|
|
|
|
|
|
if(trapTypeInt == TRAP_KAMINODART)
|
|
{
|
|
float trapRadiusFlt = TRAP_KAMINODART_BLAST_RADIUS;
|
|
names[free] = "trap_radius";
|
|
attribs[free++] = "" + trapRadiusFlt + "m";
|
|
}
|
|
}
|
|
|
|
if(hasObjVar(self, TRAP_POWER))
|
|
{
|
|
names[free] = "trap_power";
|
|
attribs[free++] = "" + floatFormat.format(getIntObjVar(self, TRAP_POWER) / 10) + "%";
|
|
}
|
|
/*********** No more accuracy info ***********
|
|
if(hasObjVar(self, TRAP_TR_ACCURACY))
|
|
{
|
|
names[free] = "trigger_accuracy";
|
|
attribs[free++] = "" + floatFormat.format(getIntObjVar(self, TRAP_TR_ACCURACY) / 10) + "%";
|
|
}
|
|
else
|
|
{
|
|
names[free] = "trigger_accuracy";
|
|
attribs[free++] = "@spam:no_trig";
|
|
}
|
|
*********************************************/
|
|
if(hasObjVar(self, TIMER_TIME) && (utils.isNestedWithin(self, player) || getOwner(self) == player))
|
|
{
|
|
names[free] = "trigger_time";
|
|
attribs[free++] = "" + getIntObjVar(self, TIMER_TIME) + " seconds";
|
|
|
|
names[free] = "tooltip.trap_type";
|
|
attribs[free++] = "@spam:trap_trigger_timed";
|
|
|
|
names[free] = "trap_type";
|
|
attribs[free++] = "@spam:trap_trig_desc_timed";
|
|
}
|
|
|
|
if(hasObjVar(self, PROXIMITY_RANGE) && (utils.isNestedWithin(self, player) || getOwner(self) == player))
|
|
{
|
|
names[free] = "trigger_proximity";
|
|
|
|
int proximityRange = getIntObjVar(self, PROXIMITY_RANGE);
|
|
string meterPluralization = " meters";
|
|
|
|
if(proximityRange == 1)
|
|
{
|
|
meterPluralization = " meter";
|
|
}
|
|
|
|
attribs[free++] = "" + proximityRange + meterPluralization;
|
|
|
|
names[free] = "tooltip.trap_type";
|
|
attribs[free++] = "@spam:trap_trigger_proximity";
|
|
|
|
names[free] = "trap_type";
|
|
attribs[free++] = "@spam:trap_trig_desc_proximity";
|
|
}
|
|
|
|
if(hasObjVar(self, TRAP_FREQUENCY) && (utils.isNestedWithin(self, player) || getOwner(self) == player))
|
|
{
|
|
names[free] = "tooltip.trap_type";
|
|
attribs[free++] = "@spam:trap_trigger_remote";
|
|
|
|
names[free] = "trap_type";
|
|
attribs[free++] = "@spam:trap_trig_desc_remote";
|
|
}
|
|
|
|
if(hasObjVar(self, TRAP_FREQUENCY) && hasObjVar(self, "armed"))
|
|
{
|
|
names[free] = "trap_frequency";
|
|
attribs[free++] = "" + getStringObjVar(self, TRAP_FREQUENCY) + " mHz";
|
|
}
|
|
|
|
if(hasObjVar(self, BIO_PROBE_TRAP_TARGET))
|
|
{
|
|
names[free] = "trigger_filter";
|
|
attribs[free++] = "" + getStringObjVar(self, BIO_PROBE_TARGET_NAME);
|
|
}
|
|
|
|
free = utils.addClassRequirementAttributes(player, self, names, attribs, free, "trap.");
|
|
free = utils.addClassRequirementAttributes(player, self, names, attribs, free, "trap.trigger.");
|
|
|
|
return;
|
|
}
|
|
|
|
boolean tryDetonateTrap(obj_id trap, obj_id target)
|
|
{
|
|
if(!hasObjVar(trap, "armed"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int trapType = getIntObjVar(trap, TRAP_TYPE);
|
|
int triggerAccuracy = getIntObjVar(trap, TRAP_TR_ACCURACY);
|
|
boolean success = rand(1, 100) <= triggerAccuracy;
|
|
if(!success)
|
|
{
|
|
playTrapFizzle(trap);
|
|
// timer traps are the only ones we need to re-arm. proximity has to be re-breached and remote has to be activated again
|
|
if(getIntObjVar(trap, TRIGGER_TYPE) == TRIGGER_TYPE_TIMER)
|
|
{
|
|
dictionary parms = new dictionary();
|
|
int time = getGameTime();
|
|
parms.put("armed", time);
|
|
float newTime = getIntObjVar(trap, TIMER_TIME) * rand(0.1f, 0.25f);
|
|
messageTo(trap, "msgTryDetonateTimer", parms, newTime, false);
|
|
setObjVar(trap, "armed", time);
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Vector targets = getTrapTargets(trap, trapType);
|
|
if(validateTrapTarget(trap, target) && targets.indexOf(target) == -1)
|
|
{
|
|
targets.add(target);
|
|
}
|
|
|
|
hideFromClient(trap, false);
|
|
doTrapDetonateEffect(trap, trapType, targets, 0, target);
|
|
|
|
return true;
|
|
}
|
|
|
|
Vector getTrapTargets(obj_id trap, int trapType)
|
|
{
|
|
obj_id[] targets = new obj_id[0];
|
|
switch(trapType)
|
|
{
|
|
case TRAP_HX2:
|
|
targets = getCreaturesInRange(getLocation(trap), TRAP_HX2_BLAST_RADIUS);
|
|
break;
|
|
case TRAP_FLASHBANG:
|
|
targets = getCreaturesInRange(getLocation(trap), TRAP_FLASHBANG_BLAST_RADIUS);
|
|
break;
|
|
case TRAP_CALTROP:
|
|
targets = getCreaturesInRange(getLocation(trap), TRAP_CALTROP_BLAST_RADIUS);
|
|
break;
|
|
case TRAP_KAMINODART:
|
|
targets = getCreaturesInRange(getLocation(trap), TRAP_KAMINODART_BLAST_RADIUS);
|
|
break;
|
|
}
|
|
|
|
Vector finalTargets = new Vector();
|
|
obj_id filter = getBioProbeTarget(trap);
|
|
|
|
for(int i = 0; i < targets.length; i++)
|
|
{
|
|
if(!isIdValid(filter) || targets[i] == filter)
|
|
{
|
|
if(validateTrapTarget(trap, targets[i]))
|
|
{
|
|
finalTargets.add(targets[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return finalTargets;
|
|
}
|
|
|
|
void doTrapDetonateEffect(obj_id trap, int trapType, Vector targets, float percentMod, obj_id breacher)
|
|
{
|
|
if(targets.size() > 0)
|
|
{
|
|
|
|
switch(trapType)
|
|
{
|
|
case TRAP_HX2:
|
|
dictionary params = new dictionary();
|
|
obj_id owner = getOwner(trap);
|
|
|
|
if(!isIdValid(breacher))
|
|
{
|
|
breacher = (obj_id)targets.elementAt(0);
|
|
if(!isIdValid(breacher))
|
|
break;
|
|
}
|
|
|
|
params.put("target", breacher);
|
|
params.put("owner", owner);
|
|
messageTo(trap, "co_mine_hx2", params, 0, false);
|
|
break;
|
|
case TRAP_FLASHBANG:
|
|
doFlashBangDetonate(trap, targets, percentMod);
|
|
break;
|
|
case TRAP_CALTROP:
|
|
doCaltropDetonate(trap, targets, percentMod);
|
|
break;
|
|
case TRAP_KAMINODART:
|
|
doKaminoDartDetonate(trap, targets, percentMod);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
playClientEffectLoc(trap, "clienteffect/lair_damage_heavy.cef", getLocation(trap), 0.0f);
|
|
}
|
|
|
|
destroyObject(trap);
|
|
}
|
|
|
|
void doTrapDetonateEffect(obj_id trap, int trapType, Vector targets)
|
|
{
|
|
doTrapDetonateEffect(trap, trapType, targets, 0);
|
|
}
|
|
|
|
void doTrapDetonateEffect(obj_id trap, int trapType, Vector targets, float percentMod)
|
|
{
|
|
doTrapDetonateEffect(trap, trapType, targets, 0, null);
|
|
}
|
|
|
|
void doKaminoDartDetonate(obj_id trap, Vector targets)
|
|
{
|
|
doKaminoDartDetonate(trap, targets, 0);
|
|
}
|
|
|
|
void doKaminoDartDetonate(obj_id trap, Vector targets, float percentMod)
|
|
{
|
|
// health DOT (poison)
|
|
float trapPower = getIntObjVar(trap, TRAP_POWER);
|
|
int potency = (int) (KAMINODART_MAX_POTENCY * (trapPower/1000));
|
|
int duration = (int) (KAMINODART_MAX_DURATION * (trapPower/1000));
|
|
int strength = (int) (KAMINODART_MAX_STRENGTH * (trapPower/1000));
|
|
obj_id owner = getOwner(trap);
|
|
boolean isInvis = false;
|
|
string invis = null;
|
|
boolean attackerPresent = false;
|
|
if(isIdValid(owner) && exists(owner))
|
|
{
|
|
attackerPresent = true;
|
|
invis = getInvisBuff(getOwner(trap));
|
|
}
|
|
|
|
isInvis = invis != null && invis.length() > 0;
|
|
duration += duration * percentMod;
|
|
strength += strength * percentMod;
|
|
|
|
obj_id curObj = null;
|
|
prose_package pp = prose.getPackage(new string_id("spam", "kamino_trap_hit"));
|
|
playClientEffectLoc(trap, "clienteffect/lair_damage_light.cef", getLocation(curObj), 0.0f);
|
|
|
|
|
|
for(int i = 0; i < targets.size(); i++)
|
|
{
|
|
curObj = (obj_id)targets.get(i);
|
|
playClientEffectLoc(curObj, "clienteffect/lair_damage_light.cef", getLocation(curObj), 0.0f);
|
|
dot.applyDotEffect(curObj, owner, dot.DOT_POISON, "rangerTrapKaminoDart", HEALTH, potency, strength, duration, true, null);
|
|
pp = prose.setTT(pp, curObj);
|
|
|
|
// chance to start combat
|
|
if(attackerPresent)
|
|
{
|
|
if(isInvis && rand(1, 100) < 50)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
startCombat(curObj, owner);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void doCaltropDetonate(obj_id trap, Vector targets)
|
|
{
|
|
doCaltropDetonate(trap, targets, 0);
|
|
}
|
|
|
|
void doCaltropDetonate(obj_id trap, Vector targets, float percentMod)
|
|
{
|
|
// Movement snare
|
|
float trapPower = getIntObjVar(trap, TRAP_POWER);
|
|
float duration = CALTROP_MAX_DURATION * (trapPower / 1000);
|
|
float snareAmount = CALTROP_MAX_SNARE * (trapPower / 1000);
|
|
obj_id curObj = null;
|
|
prose_package pp = prose.getPackage(new string_id("spam", "caltrop_trap_hit"));
|
|
obj_id owner = getOwner(trap);
|
|
boolean isInvis = false;
|
|
string invis = null;
|
|
boolean attackerPresent = false;
|
|
|
|
if(isIdValid(owner) && exists(owner))
|
|
{
|
|
attackerPresent = true;
|
|
invis = getInvisBuff(getOwner(trap));
|
|
}
|
|
|
|
isInvis = invis != null && invis.length() > 0;
|
|
|
|
|
|
playClientEffectLoc(trap, "clienteffect/lair_med_damage_smoke.cef", getLocation(trap), 0.0f);
|
|
|
|
duration += duration * percentMod;
|
|
snareAmount += snareAmount * percentMod;
|
|
if(snareAmount > 95)
|
|
{
|
|
snareAmount = 95;
|
|
}
|
|
for(int i = 0; i < targets.size(); i++)
|
|
{
|
|
curObj = (obj_id)targets.get(i);
|
|
if(buff.canApplyBuff(curObj, "caltropSnare"))
|
|
{
|
|
playClientEffectLoc(curObj, "clienteffect/lair_damage_light.cef", getLocation(curObj), 0.0f);
|
|
buff.applyBuff(curObj, "caltropSnare", duration, snareAmount);
|
|
|
|
// chance to start combat
|
|
if(attackerPresent)
|
|
{
|
|
if(isInvis && rand(1, 100) < 50)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
startCombat(curObj, owner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void doFlashBangDetonate(obj_id trap, Vector targets)
|
|
{
|
|
doFlashBangDetonate(trap, targets, 0);
|
|
}
|
|
|
|
void doFlashBangDetonate(obj_id trap, Vector targets, float percentMod)
|
|
{
|
|
// Combat slows no longer exist. FlashBang is now a -PRE/-STR debuff.
|
|
// Stun, Knockdown, Blind
|
|
float trapPower = getIntObjVar(trap, TRAP_POWER);
|
|
float duration = FLASHBANG_MAX_DURATION * (trapPower / 1000);
|
|
float amount = FLASHBANG_MAX_COMBAT_SLOW * (trapPower / 1000);
|
|
obj_id curObj = null;
|
|
prose_package pp = prose.getPackage(new string_id("spam", "flashbang_trap_hit"));
|
|
obj_id owner = getOwner(trap);
|
|
boolean isInvis = false;
|
|
string invis = null;
|
|
boolean attackerPresent = false;
|
|
|
|
if(isIdValid(owner) && exists(owner))
|
|
{
|
|
attackerPresent = true;
|
|
invis = getInvisBuff(getOwner(trap));
|
|
}
|
|
|
|
isInvis = invis != null && invis.length() > 0;
|
|
|
|
playClientEffectLoc(trap, "clienteffect/int_camshake_light.cef", getLocation(trap), 0.0f);
|
|
playClientEffectLoc(trap, "appearance/pt_flash_bomb.prt", getLocation(trap), 0.0f);
|
|
|
|
duration += duration * percentMod;
|
|
amount += amount * percentMod;
|
|
|
|
for(int i = 0; i < targets.size(); i++)
|
|
{
|
|
curObj = (obj_id)targets.get(i);
|
|
if(buff.canApplyBuff(curObj, "flashBang"))
|
|
{
|
|
if(isPlayer(curObj))
|
|
{
|
|
doAnimationAction(curObj, "cover_ears_mocking");
|
|
}
|
|
buff.applyBuff(curObj, "flashBang", duration, amount);
|
|
// chance to start combat
|
|
if(attackerPresent)
|
|
{
|
|
if(isInvis && rand(1, 100) < 50)
|
|
{
|
|
continue;
|
|
}
|
|
startCombat(curObj, owner);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//LOG("stealth", "canApply flashBang buff = false");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean safePvPCanAttack(obj_id trap, obj_id target)
|
|
{
|
|
obj_id trapOwner = getOwner(trap);
|
|
if(isIdValid(trapOwner) && exists(trapOwner))
|
|
{
|
|
return pvpCanAttack(trapOwner, target);
|
|
}
|
|
|
|
if(isInvulnerable(target))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(trapOwner == target)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Note: Bounties have to be ignored for unreachable trap owners
|
|
// Note: Duel Flags have to be ignored for unreachable trap owners
|
|
|
|
// manual pvp check,
|
|
boolean actorIsPlayer = true;
|
|
int actorFaction = getIntObjVar(trap, TRAP_FACTION);
|
|
int actorPvPType = getIntObjVar(trap, TRAP_PVP_TYPE);
|
|
|
|
int targetFaction = pvpGetAlignedFaction(target);
|
|
boolean targetIsPlayer = isPlayer(target);
|
|
int targetPvPType = pvpGetType(target);
|
|
|
|
// if targeting a player, we can only possibly attack if the trap owner is declared
|
|
if(targetIsPlayer && actorPvPType != PVPTYPE_DECLARED)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(targetIsPlayer && actorIsPlayer)
|
|
{
|
|
// noone may attack someone in their own group
|
|
//if (PvpInternal::inSameGroup(actor, target)) can't deal with groups for offline ppl
|
|
if(pvpAreFactionsOpposed(actorFaction, targetFaction))
|
|
{
|
|
//IF I'M DECLARED AND THEY ARE AS WELL, WE CAN ATTACK.
|
|
//if(PvpInternal::isDeclared(actor) && PvpInternal::isDeclared(target))
|
|
if(actorPvPType == PVPTYPE_DECLARED && targetPvPType == PVPTYPE_DECLARED)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// anyone may attack someone with a PEF against them (unless they are unattackable)
|
|
//if (PvpInternal::hasPersonalEnemyFlag(target, actor.getNetworkId()))
|
|
// Can't do this, b/c actor is not authoritative : pvpHasPersonalEnemyFlag(obj_id actor, obj_id target)
|
|
|
|
// player vs npc
|
|
if(actorIsPlayer && !targetIsPlayer)
|
|
{
|
|
// PC attacker rules
|
|
// anyone may attack npc targets with no faction opponents
|
|
if(targetFaction != 0)
|
|
{
|
|
// so they're factional
|
|
if(actorFaction != targetFaction)
|
|
{
|
|
//they have a differnent faction than me
|
|
return pvpAreFactionsOpposed(actorFaction, targetFaction);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
boolean validateTrapTarget(obj_id trap, obj_id target)
|
|
{
|
|
if(!isIdValid(target) || trap == target)
|
|
{
|
|
//LOG("stealth", "Trap Target " + target + "[" + getName(target) + "] is the ");
|
|
return false;
|
|
}
|
|
|
|
// death prevents damage, feign death does not - due to the nature of the trap
|
|
if(isDead(target))
|
|
{
|
|
//LOG("stealth", "Trap Target " + target + "[" + getName(target) + "] isDead check.");
|
|
return false;
|
|
}
|
|
|
|
// can't damage incap'd target
|
|
if(isIncapacitated(target))
|
|
{
|
|
//LOG("stealth", "Trap Target " + target + "[" + getName(target) + "] failed incapacitated check.");
|
|
return false;
|
|
}
|
|
|
|
if(!safePvPCanAttack(trap, target))
|
|
{
|
|
//LOG("stealth", "Trap Target " + target + "[" + getName(target) + "] failed safePvPCanAttack");
|
|
return false;
|
|
}
|
|
|
|
// LOS
|
|
location trapLoc = getLocation( trap );
|
|
trapLoc.y += 0.5f;
|
|
if(!canSee(target, trapLoc))
|
|
{
|
|
//LOG("stealth", "Trap Target " + target + "[" + getName(target) + "] failed LOS check.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void playTrapFizzle(obj_id trap)
|
|
{
|
|
playClientEffectLoc(trap, "clienteffect/frs_light_serenity.cef", getLocation(trap), 0.0f);
|
|
}
|
|
|
|
void playSelfDestruct(obj_id trap)
|
|
{
|
|
playClientEffectLoc(trap, "clienteffect/combat_grenade_thermal_detonator.cef", getLocation(trap), 0.0f);
|
|
}
|
|
|
|
boolean setTrap(obj_id player, obj_id invTrap)
|
|
{
|
|
if(!hasObjVar(invTrap, TRIGGER_TYPE))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int count = getCount(invTrap);
|
|
if(count < 1)
|
|
{
|
|
destroyObject(invTrap);
|
|
return false;
|
|
}
|
|
|
|
location loc = getLocation(player);
|
|
int triggerType = getIntObjVar(invTrap, TRIGGER_TYPE);
|
|
string template = getTemplateName(invTrap);
|
|
obj_id trap = utils.cloneObject(invTrap, loc);
|
|
int time = getGameTime();
|
|
|
|
if(!isIdValid(trap))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch(triggerType)
|
|
{
|
|
case TRIGGER_TYPE_TIMER:
|
|
dictionary parms = new dictionary();
|
|
parms.put("armed", time);
|
|
messageTo(trap, "msgTryDetonateTimer", parms, getIntObjVar(trap, TIMER_TIME), false);
|
|
setObjVar(trap, "armed", time);
|
|
break;
|
|
case TRIGGER_TYPE_REMOTE:
|
|
// nothing to do for remote detonator
|
|
obj_id pInv = utils.getInventoryContainer(player);
|
|
int free = getVolumeFree(pInv);
|
|
if(free < 1)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "no_room_remote_trigger"));
|
|
return false;
|
|
}
|
|
string remote = "object/tangible/scout/trap/trap_ap_tr_remote.iff";
|
|
obj_id remoteTrigger = createObject(remote, pInv, "");
|
|
|
|
if(!isIdValid(remoteTrigger))
|
|
{
|
|
destroyObject(trap);
|
|
return false;
|
|
}
|
|
|
|
string frequency = "" + rand(0, 999) + "." + rand(0, 999) + "." + rand(0, 999);;
|
|
setObjVar(remoteTrigger, stealth.TRAP_FREQUENCY, frequency);
|
|
setObjVar(trap, stealth.TRAP_FREQUENCY, frequency);
|
|
|
|
setObjVar(trap, stealth.TRAP_MY_PROXIMITY_TRIGGER, remoteTrigger);
|
|
setObjVar(remoteTrigger, stealth.TRAP_MY_TRAP, trap);
|
|
|
|
sendSystemMessage(player, new string_id("spam", "trap_keyed_remote"));
|
|
setObjVar(trap, "armed", time);
|
|
setObjVar(remoteTrigger, "armed", true);
|
|
break;
|
|
case TRIGGER_TYPE_PROXIMITY:
|
|
dictionary timeArmed = new dictionary();
|
|
timeArmed.put("armed", time);
|
|
messageTo(trap, "msgArmProximity", timeArmed, 0, false);
|
|
break;
|
|
}
|
|
|
|
|
|
playClientEffectObj(trap, "appearance/pt_trap_reticule.prt", trap, "", null, "trapReticle");
|
|
doAnimationAction(player, "point_down");
|
|
setOwner(trap, player);
|
|
setObjVar(trap, TRAP_FACTION, pvpGetAlignedFaction(player));
|
|
setObjVar(trap, TRAP_PVP_TYPE, pvpGetType(player));
|
|
setObjVar(trap, TRAP_DIFFICULTY, getEnhancedSkillStatisticModifier(player, "ranger_trap"));
|
|
setObjVar(trap, TRAP_LEVEL, getLevel(player));
|
|
|
|
if(hasObjVar(trap, "concealable"))
|
|
{
|
|
if(buff.hasBuff(player, "conceal_trap"))
|
|
{
|
|
stealth.concealDevice(player, trap);
|
|
}
|
|
}
|
|
|
|
//add combat data to trap object
|
|
setWeaponMinDamage(trap, 1);
|
|
setWeaponMaxDamage(trap, 1);
|
|
setWeaponAttackSpeed(trap, 0.25f);
|
|
|
|
weapons.setWeaponData(trap);
|
|
|
|
messageTo(trap, "msgSelfDestructTimeout", null, TRAP_SELF_DESTRUCT_TIMEOUT, false);
|
|
count--;
|
|
if(count < 1)
|
|
{
|
|
destroyObject(invTrap);
|
|
}
|
|
else
|
|
{
|
|
setCount(invTrap, count);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
obj_id getTrigger(obj_id trap)
|
|
{
|
|
if(hasObjVar(trap, TRAP_MY_PROXIMITY_TRIGGER))
|
|
{
|
|
return getObjIdObjVar(trap, TRAP_MY_PROXIMITY_TRIGGER);
|
|
}
|
|
|
|
obj_id[] stuff = getContents(trap);
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(hasScript(stuff[i], "systems.skills.stealth.traptrigger"))
|
|
{
|
|
return stuff[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
//
|
|
// Evaluates the mark and generates loot based on various parameters
|
|
// Thomas thinks this is a baaaaad Idea
|
|
//
|
|
|
|
|
|
//sendSystemMessage(self, "Inside Cyber branch", null);
|
|
boolean doTheftLoot(obj_id thief, obj_id mark)
|
|
{
|
|
obj_id thiefInventory = utils.getInventoryContainer(thief);
|
|
int free = getVolumeFree(thiefInventory);
|
|
prose_package pp = new prose_package();
|
|
if(free < 1)
|
|
{
|
|
sendSystemMessage(thief, new string_id("spam", "cant_steal_inventory_full"));
|
|
return false;
|
|
}
|
|
|
|
int stealFlags = stealFlags = STEAL_CREDITS | STEAL_LOOT; // default to some credits and some loot;
|
|
|
|
// see if we have special flags to override the defaults
|
|
if(utils.hasScriptVar(mark, STEAL_TYPE))
|
|
{
|
|
stealFlags = utils.getIntScriptVar(mark, STEAL_TYPE);
|
|
}
|
|
|
|
if(isPlayer(mark))
|
|
{
|
|
stealFlags = STEAL_MARKED_ITEMS;
|
|
}
|
|
else if(hasStealingLootTableEntry(mark))
|
|
{
|
|
stealFlags |= STEAL_STEALING_TABLE;
|
|
}
|
|
|
|
Vector types = new Vector();
|
|
if(isFlagSet(stealFlags, STEAL_CREDITS))
|
|
{
|
|
types.add(new Integer(STEAL_CREDITS));
|
|
}
|
|
|
|
if(isFlagSet(stealFlags, STEAL_LOOT))
|
|
{
|
|
types.add(new Integer(STEAL_LOOT));
|
|
}
|
|
|
|
if(isFlagSet(stealFlags, STEAL_TEMPLATE))
|
|
{
|
|
types.add(new Integer(STEAL_TEMPLATE));
|
|
}
|
|
|
|
if(isFlagSet(stealFlags, STEAL_MARKED_ITEMS))
|
|
{
|
|
types.add(new Integer(STEAL_MARKED_ITEMS));
|
|
}
|
|
|
|
if(isFlagSet(stealFlags, STEAL_STEALING_TABLE))
|
|
{
|
|
types.add(new Integer(STEAL_STEALING_TABLE));
|
|
}
|
|
|
|
// select which one you get for this attempt
|
|
boolean gotSomething = false;
|
|
while(types.size() > 0 && !gotSomething)
|
|
{
|
|
int roll = rand(0, types.size()-1);
|
|
int flag = ((Integer)types.get(roll)).intValue();
|
|
int markLevel = getLevel(mark);
|
|
int thiefLevel = getLevel(thief);
|
|
int stealRoll = rand(1,100);
|
|
if(STEAL_FAIL_CHANCE < stealRoll)
|
|
{
|
|
|
|
switch(flag)
|
|
{
|
|
case STEAL_CREDITS:
|
|
if(isPlayer(mark))
|
|
{
|
|
break;
|
|
}
|
|
int amt = (int)(getNpcCash(mark) * .2f); // 20% of what the creature would yield normally during the loot stage
|
|
if(amt > 0)
|
|
{
|
|
dictionary d = new dictionary();
|
|
d.put(money.DICT_AMOUNT, amt);
|
|
boolean boolReturn = transferBankCreditsFromNamedAccount(money.ACCT_NPC_LOOT, thief, amt, "handleStealCashSuccess", "handleStealCashFail", d);
|
|
utils.moneyInMetric(mark, money.ACCT_NPC_LOOT, amt);
|
|
gotSomething = true;
|
|
|
|
}
|
|
break;
|
|
|
|
case STEAL_LOOT:
|
|
if(isPlayer(mark))
|
|
{
|
|
break;
|
|
}
|
|
|
|
int lootRoll = rand(1, 100);
|
|
|
|
if (lootRoll >= STEAL_RARE_ITEM)
|
|
{
|
|
//sendSystemMessageTestingOnly(thief, "Inside steal rare");
|
|
if(thiefLevel <= (markLevel + 10))
|
|
{
|
|
if(!hasCompletedCollection(thief, "spy_unarmed_01"))
|
|
{
|
|
long slotValueOne = getCollectionSlotValue(thief, "col_spy_unarmed_01_01");
|
|
long slotValueTwo = getCollectionSlotValue(thief, "col_spy_unarmed_01_02");
|
|
long slotValueThree = getCollectionSlotValue(thief, "col_spy_unarmed_01_03");
|
|
long slotValueFour = getCollectionSlotValue(thief, "col_spy_unarmed_01_04");
|
|
|
|
if (slotValueOne == 0)
|
|
{
|
|
modifyCollectionSlotValue(thief, "col_spy_unarmed_01_01", 1);
|
|
}
|
|
|
|
else if (slotValueTwo == 0)
|
|
{
|
|
modifyCollectionSlotValue(thief, "col_spy_unarmed_01_02", 1);
|
|
}
|
|
|
|
else if (slotValueThree == 0)
|
|
{
|
|
modifyCollectionSlotValue(thief, "col_spy_unarmed_01_03", 1);
|
|
}
|
|
|
|
else if (slotValueFour == 0)
|
|
{
|
|
modifyCollectionSlotValue(thief, "col_spy_unarmed_01_04", 1);
|
|
}
|
|
}
|
|
//sendSystemMessage(thief, "Got rare loot", null);
|
|
gotSomething = loot.generateTheftLootRare(thiefInventory, mark, 1, thief);
|
|
break;
|
|
}
|
|
|
|
gotSomething = loot.generateTheftLoot(thiefInventory, mark, STEAL_LOOT_CHANCE_MOD, 1);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (lootRoll >= STEAL_REGULAR_ITEM && lootRoll < STEAL_RARE_ITEM)
|
|
{
|
|
if(thiefLevel <= (markLevel + 10))
|
|
{
|
|
//sendSystemMessage(thief, "Inside collection roll", null);
|
|
boolean gotCollectionItem = false;
|
|
gotCollectionItem = loot.addCollectionLoot(mark, true, thief);
|
|
gotSomething = gotCollectionItem;
|
|
|
|
if (!gotCollectionItem)
|
|
{
|
|
gotSomething = loot.generateTheftLoot(thiefInventory, mark, STEAL_LOOT_CHANCE_MOD, 1);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
else
|
|
{
|
|
gotSomething = loot.generateTheftLoot(thiefInventory, mark, STEAL_LOOT_CHANCE_MOD, 1);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lootRoll < STEAL_REGULAR_ITEM)
|
|
{
|
|
gotSomething = loot.generateTheftLoot(thiefInventory, mark, STEAL_LOOT_CHANCE_MOD, 1);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case STEAL_TEMPLATE:
|
|
Vector itms = getStealableTemplates(mark);
|
|
if(itms.size() < 1)
|
|
{
|
|
break;
|
|
}
|
|
string templateLoot = (string)itms.get(rand(0, itms.size()-1));
|
|
if(templateLoot == null || templateLoot.length() < 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
obj_id templateObj = null;
|
|
if(templateLoot.startsWith("static:"))
|
|
{
|
|
java.util.StringTokenizer st = new java.util.StringTokenizer(templateLoot, ":");
|
|
if( st.countTokens() == 2 )
|
|
{
|
|
st.nextToken(); //ignore first token
|
|
string itemName = st.nextToken();
|
|
templateObj = static_item.createNewItemFunction(templateLoot, thiefInventory);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
templateObj = createObjectOverloaded(templateLoot, thiefInventory);
|
|
}
|
|
|
|
gotSomething = templateObj != null;
|
|
if(gotSomething)
|
|
{
|
|
utils.setScriptVar(thiefInventory, "theft_in_progress", 1);
|
|
loot.notifyThiefOfItemStolen(thiefInventory, templateObj);
|
|
}
|
|
break;
|
|
|
|
case STEAL_MARKED_ITEMS:
|
|
obj_id[] stuff = utils.getFilteredPlayerContents(mark);
|
|
Vector eligibleItems = new Vector();
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(isItemStealable(stuff[i]))
|
|
{
|
|
eligibleItems.add(stuff[i]);
|
|
}
|
|
}
|
|
|
|
if(eligibleItems.size() < 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
obj_id markedObj = (obj_id)eligibleItems.get(rand(0, eligibleItems.size()-1));
|
|
gotSomething = putIn(markedObj, thiefInventory);
|
|
if(gotSomething)
|
|
{
|
|
CustomerServiceLog("stealing", "%TU stole an item (" + markedObj + " - " + getName(markedObj) +
|
|
") from player %TT" , thief, mark);
|
|
utils.setScriptVar(thiefInventory, "theft_in_progress", 1);
|
|
loot.notifyThiefOfItemStolen(thiefInventory, markedObj);
|
|
}
|
|
break;
|
|
|
|
case STEAL_STEALING_TABLE:
|
|
string tableTemplate = getStealableTemplateFromTable(mark);
|
|
if(tableTemplate == null || tableTemplate.length() < 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
obj_id tableObj = null;
|
|
if(tableTemplate.startsWith("static:"))
|
|
{
|
|
java.util.StringTokenizer st = new java.util.StringTokenizer(tableTemplate, ":");
|
|
if( st.countTokens() == 2 )
|
|
{
|
|
st.nextToken(); //ignore first token
|
|
string itemName = st.nextToken();
|
|
tableObj = static_item.createNewItemFunction(tableTemplate, thiefInventory);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tableObj = createObjectOverloaded(tableTemplate, thiefInventory);
|
|
}
|
|
gotSomething = tableObj != null;
|
|
if(gotSomething)
|
|
{
|
|
utils.setScriptVar(thiefInventory, "theft_in_progress", 1);
|
|
loot.notifyThiefOfItemStolen(thiefInventory, tableObj);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
stealFlags &= ~flag;
|
|
types.removeElementAt(roll);
|
|
}
|
|
|
|
if(!isPlayer(mark))
|
|
{
|
|
utils.setScriptVar(mark, STEAL_TYPE, stealFlags);
|
|
}
|
|
return gotSomething;
|
|
}
|
|
|
|
boolean hasStealingLootTableEntry(obj_id mark)
|
|
{
|
|
string name = getCreatureName(mark);
|
|
string type = loot.getCreatureType(name);
|
|
string table = "datatables/loot/stealing_loot.iff";
|
|
int row = dataTableSearchColumnForInt(getStringCrc(type), "npc_type", table);
|
|
|
|
if(row < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
string getStealableTemplateFromTable(obj_id mark)
|
|
{
|
|
Vector stuff = new Vector();
|
|
Vector weights = new Vector();
|
|
|
|
string name = getCreatureName(mark);
|
|
//string type = loot.getCreatureType(name);
|
|
string table = "datatables/loot/stealing_loot.iff";
|
|
int row = dataTableSearchColumnForInt(getStringCrc(name), "npc_type", table);
|
|
|
|
if(row < 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
int rows = dataTableGetNumRows(table);
|
|
while(true)
|
|
{
|
|
stuff.add(dataTableGetString(table, row, "stealableObjects"));
|
|
weights.add(new Integer(dataTableGetInt(table, row, "weight")));
|
|
|
|
row++;
|
|
|
|
if(row >= rows || dataTableGetInt(table, row, "npc_type") != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
int total = 0;
|
|
int[] weightArray = new int[weights.size()];
|
|
for(int i = 0; i < weightArray.length; i++)
|
|
{
|
|
weightArray[i] = ((Integer)weights.get(i)).intValue();
|
|
total += weightArray[i];
|
|
}
|
|
|
|
if(total < 1)
|
|
{
|
|
total = 1;
|
|
}
|
|
|
|
int roll = rand(1, total);
|
|
|
|
int idx = -1;
|
|
int low_range = 0;
|
|
for (int j = 0; j < weightArray.length; j++)
|
|
{
|
|
int high_range = low_range + weightArray[j];
|
|
|
|
if(roll > low_range && roll <= high_range)
|
|
{
|
|
idx = j;
|
|
break;
|
|
}
|
|
low_range = high_range;
|
|
}
|
|
|
|
if(idx < stuff.size())
|
|
{
|
|
return (string)stuff.get(idx);
|
|
}
|
|
return (string)stuff.get(rand(0, stuff.size()-1));
|
|
}
|
|
|
|
void flagItemAsStealable(obj_id item)
|
|
{
|
|
setObjVar(item, STEAL_ITEM_IS_STEALABLE, 1);
|
|
}
|
|
|
|
boolean isItemStealable(obj_id item)
|
|
{
|
|
return hasObjVar(item, STEAL_ITEM_IS_STEALABLE);
|
|
}
|
|
|
|
void unflagItemAsStealable(obj_id item)
|
|
{
|
|
removeObjVar(item, STEAL_ITEM_IS_STEALABLE);
|
|
}
|
|
|
|
void addStealableTemplate(obj_id mark, string template)
|
|
{
|
|
Vector itms = getStealableTemplates(mark);
|
|
itms.add(template);
|
|
utils.setResizeableBatchObjVar(mark, STEAL_TEMPLATE_ITEMS, itms);
|
|
}
|
|
|
|
Vector getStealableTemplates(obj_id mark)
|
|
{
|
|
return utils.getResizeableStringBatchObjVar(mark, STEAL_TEMPLATE_ITEMS);
|
|
}
|
|
|
|
int getNpcCash(obj_id mark)
|
|
{
|
|
string mobType = ai_lib.getCreatureName(mark);
|
|
if(mobType == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int level = ai_lib.getLevel(mark);
|
|
int cash = loot.getCashForLevel(mobType, level);
|
|
|
|
if(cash > 0)
|
|
{
|
|
return cash;
|
|
}
|
|
|
|
cash = rand (1, 10);
|
|
cash = cash * level;
|
|
|
|
return cash;
|
|
}
|
|
|
|
boolean canSteal(obj_id thief, obj_id mark)
|
|
{
|
|
if(!pvpCanAttack(thief, mark))
|
|
{
|
|
sendSystemMessage(thief, new string_id("spam", "steal_cant_pvp"));
|
|
return false;
|
|
}
|
|
|
|
if(!ai_lib.isHumanoid(mark))
|
|
{
|
|
sendSystemMessage(thief, new string_id("spam", "steal_not_humanoid"));
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(mark, STEAL_TYPE))
|
|
{
|
|
int stealFlags = utils.getIntScriptVar(mark, STEAL_TYPE);
|
|
if(stealFlags == STEAL_NOTHING)
|
|
{
|
|
sendSystemMessage(thief, new string_id("spam", "stolen_got_nothing"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean steal(obj_id thief, obj_id mark)
|
|
{
|
|
if(!luck.isLucky(thief, 0.01f))
|
|
{
|
|
if(isCoughtWhileStealing(thief, mark) || luck.isLucky(mark, 0.01f))
|
|
{
|
|
sendSystemMessageTestingOnly(thief, "STEALING CHECK FAILED: CAUGHT");
|
|
showFlyText(thief, new string_id("spam", "stealing_cought"), 1.5f, colors.TOMATO);
|
|
sendSystemMessage(thief, new string_id("spam", "stolen_cought"));
|
|
string invis = getInvisBuff(thief);
|
|
if(invis != null && invis.length() > 0)
|
|
{
|
|
buff.removeBuff(thief, invis);
|
|
}
|
|
if(isPlayer(mark))
|
|
{
|
|
prose_package pp = prose.getPackage(new string_id("spam", "almost_got_item_stolen"), thief);
|
|
sendSystemMessageProse(mark, pp);
|
|
}
|
|
else
|
|
{
|
|
startCombat(mark, thief);
|
|
startCombat(thief, mark);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
showFlyTextPrivate(thief, thief, new string_id("spam", "stealin_on"), 1.5f, colors.TOMATO);
|
|
if(doTheftLoot(thief, mark))
|
|
{
|
|
// sendSystemMessage(thief, new string_id("spam", "stolen_item"));
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(thief, new string_id("spam", "stolen_nothing"));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean isCoughtWhileStealing(obj_id thief, obj_id mark)
|
|
{
|
|
return passiveDetectHiddenTarget(thief, mark, PASSIVE_BREACH_NEAR);
|
|
}
|
|
|
|
//
|
|
// Does a ranger related stealth/hiding skill check. penalizes for witnesses
|
|
//
|
|
boolean witnessModifiedSkillCheck(obj_id player, string skillMod, float distance, int witnessPenalty, int skillModMod)
|
|
{
|
|
int skill = getEnhancedSkillStatisticModifier(player, skillMod) + skillModMod;
|
|
if(skill < 0)
|
|
{
|
|
skill = 0;
|
|
}
|
|
|
|
obj_id[] stuff = getCreaturesInRange(player, distance);
|
|
|
|
// Get number of non-allies in the area
|
|
int cnt = 0;
|
|
int sGuildId = getGuildId(player);
|
|
|
|
if(stuff.length > 0)
|
|
{
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(ai_lib.isHumanoid(stuff[i]))
|
|
{
|
|
boolean isInSameGuild = (sGuildId != 0) && (getGuildId(stuff[i]) == sGuildId);
|
|
if(!isInSameGuild && !group.inSameGroup(player, stuff[i]))
|
|
{
|
|
skill -= witnessPenalty; // every humanoid in the area makes it N% harder
|
|
}
|
|
}
|
|
}
|
|
|
|
// 33% chance of failure at 10 skill, 8% chance of failure at 100 skill.
|
|
// 5% chance of failure at 200 skill. 2% chance of failure at 500 skill
|
|
// 5 skill point penalty for every humanoid in the area
|
|
float rslt = (1/ (1+ ((skill+10) / 10)));
|
|
return (rand(0, 1f) > rslt);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void invisBuffRemoved(obj_id player, string sbuff)
|
|
{
|
|
//check to see if they have the 1 second buff that blocks removal
|
|
//if they do, we dont break stealth
|
|
if(buff.hasBuff(player, BUFF_NO_BREAK_INVIS))
|
|
return;
|
|
|
|
if(buff.hasBuff(player, "sp_burst_shadows"))
|
|
{
|
|
buff.removeBuff(player, "sp_burst_shadows");
|
|
}
|
|
|
|
_makeVisible(player, new string_id("spam", "stealthoff"), null);
|
|
|
|
//Attempts to add any wearable appearance item with particle.
|
|
messageTo(player, "addAppearanceItemEffect", null, 1, false);
|
|
|
|
}
|
|
|
|
void OnLocomotionChange(obj_id player, int oldLoco, int newLoco)
|
|
{
|
|
string invis = getInvisBuff(player);
|
|
if(invis == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int pri = buff.getPriority(invis);
|
|
if(pri < MIN_MOVEMENT_PRIORITY)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "posturechangereveal"));
|
|
buff.removeBuff(player, invis);
|
|
}
|
|
}
|
|
|
|
void OnPostureChanged(obj_id player, int before, int after)
|
|
{
|
|
string invis = getInvisBuff(player);
|
|
if(invis == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int pri = buff.getPriority(invis);
|
|
if(pri < MIN_POSTURE_CHANGE_PRIORITY)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "posturechangereveal"));
|
|
buff.removeBuff(player, invis);
|
|
}
|
|
}
|
|
|
|
int getApplicableInvisSkillMod(obj_id thing, string buffName)
|
|
{
|
|
string modName = "camouflage";
|
|
switch(getStringCrc(buffName))
|
|
{
|
|
// Spy Stealth
|
|
case ##"invis_sp_buff":
|
|
modName = "camouflage";
|
|
break;
|
|
case ##"invis_bothan_ability_1":
|
|
modName = "camouflage";
|
|
break;
|
|
case ##"invis_blendIn":
|
|
case ##"invis_camouflage":
|
|
break;
|
|
case ##"invis_cover":
|
|
modName = "cover";
|
|
break;
|
|
case ##"invis_urbanStealth":
|
|
case ##"invis_wildernessStealth":
|
|
modName = "stealth";
|
|
break;
|
|
case ##"invis_forceCloak":
|
|
modName = "";
|
|
break;
|
|
}
|
|
|
|
int mod = 0;
|
|
if(modName.length() > 0)
|
|
{
|
|
mod = getEnhancedSkillStatisticModifierUncapped(thing, modName);
|
|
|
|
if(modName.equals("camouflage"))
|
|
mod += getEnhancedSkillStatisticModifierUncapped(thing, "stealth");
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
int getApplicableInvisSkillMod(obj_id thing)
|
|
{
|
|
int mod = 0;
|
|
if(isPlayer(thing))
|
|
{
|
|
string invis = getInvisBuff(thing);
|
|
if(invis == null || invis.length() < 1)
|
|
{
|
|
return mod;
|
|
}
|
|
|
|
mod = getApplicableInvisSkillMod(thing, invis);
|
|
}
|
|
else
|
|
{
|
|
mod = getIntObjVar(thing, "camouflageMod");
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
// getDetectChanceWithDetailedOutput() and getDetectChance()
|
|
// ***MUST*** be kept in sync
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
float getDetectChance(obj_id target, obj_id detector, float baseChanceToDetect)
|
|
{
|
|
string invis = getInvisBuff(target);
|
|
|
|
if(beast_lib.isBeast(target))
|
|
{
|
|
invis = "invis";
|
|
}
|
|
|
|
if(invis != null && invis.length() > 0)
|
|
{
|
|
if(buff.getPriority(invis) > MAX_INVIS_BUFF_TO_DETECT)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
if(invis.startsWith("invis_sp_buff_invis_notrace"))
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
float finalChanceToDetect = 0.0f;
|
|
|
|
int stealtherLevel = getLevel(target);
|
|
int detectorLevel = getLevel(detector);
|
|
|
|
float levelChanceToDetectMod = 0.0f;
|
|
|
|
if(stealtherLevel > detectorLevel)
|
|
{
|
|
float levelDiff = stealtherLevel - detectorLevel;
|
|
if (baseChanceToDetect > 30)
|
|
{
|
|
levelChanceToDetectMod = (levelDiff *5.0f);// On Active checks we really need to crank up the penalty based on level since the default chance is high
|
|
}
|
|
else
|
|
{
|
|
levelChanceToDetectMod = (levelDiff *1.5f);
|
|
}
|
|
finalChanceToDetect = baseChanceToDetect - levelChanceToDetectMod;
|
|
}
|
|
|
|
if (stealtherLevel < detectorLevel)
|
|
{
|
|
float levelDiff = detectorLevel - stealtherLevel;
|
|
levelChanceToDetectMod = (levelDiff*2.5f);
|
|
finalChanceToDetect = baseChanceToDetect + levelChanceToDetectMod;
|
|
}
|
|
|
|
if (stealtherLevel == detectorLevel)
|
|
{
|
|
finalChanceToDetect = baseChanceToDetect;
|
|
}
|
|
|
|
// Get the dettect hidden and camouflage mods of the stealther and detector
|
|
|
|
float modChanceToDetectMod = 0.0f;
|
|
|
|
int stealtherSkillMod = getApplicableInvisSkillMod(target);
|
|
|
|
if(beast_lib.isBeast(target))
|
|
{
|
|
obj_id master = getMaster(target);
|
|
|
|
if(isIdValid(master) && exists(master))
|
|
{
|
|
stealtherSkillMod = getApplicableInvisSkillMod(master);
|
|
}
|
|
}
|
|
|
|
int detectorSkillMod = getEnhancedSkillStatisticModifierUncapped(detector, "detect_hidden");
|
|
|
|
|
|
if (stealtherSkillMod > detectorSkillMod)
|
|
{
|
|
float modDiff = (stealtherSkillMod - detectorSkillMod);
|
|
modChanceToDetectMod = modDiff/9.0f;
|
|
finalChanceToDetect = finalChanceToDetect - modChanceToDetectMod;
|
|
}
|
|
|
|
if (stealtherSkillMod < detectorSkillMod )
|
|
{
|
|
float modDiff = (detectorSkillMod - stealtherSkillMod);
|
|
modChanceToDetectMod = modDiff/4.0f;
|
|
finalChanceToDetect = finalChanceToDetect + modChanceToDetectMod;
|
|
}
|
|
|
|
if(invis.startsWith("invis_forceCloak") || (invis.startsWith("invis_fs_buff_invis") ))
|
|
{
|
|
// Give Jedi 10% Chance to avoid detection
|
|
finalChanceToDetect = (finalChanceToDetect - 10.0f);
|
|
}
|
|
|
|
if (!isPlayer(detector))
|
|
{
|
|
string name = getCreatureName(detector);
|
|
int difficultyClass = dataTableGetInt ("datatables/mob/creatures.iff", name, "difficultyClass");
|
|
if (difficultyClass == 1)
|
|
{
|
|
//Elite
|
|
finalChanceToDetect = (finalChanceToDetect +5.0f);
|
|
}
|
|
if (difficultyClass == 2)
|
|
{
|
|
//BOSS
|
|
finalChanceToDetect = (finalChanceToDetect +10.0f);
|
|
}
|
|
}
|
|
// Apply the min and max detecting chances here
|
|
//95% maximum chance to detect
|
|
// 1% minimum chance to detect
|
|
|
|
if (finalChanceToDetect > MAX_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
finalChanceToDetect = MAX_CHANCE_TO_DETECT_HIDDEN;
|
|
}
|
|
|
|
if (finalChanceToDetect < MIN_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
if (isPlayer(detector))
|
|
{
|
|
finalChanceToDetect = MIN_CHANCE_TO_DETECT_HIDDEN;
|
|
}
|
|
}
|
|
|
|
return finalChanceToDetect;
|
|
}
|
|
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
// getDetectChanceWithDetailedOutput() and getDetectChance()
|
|
// ***MUST*** be kept in sync
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
float getDetectChanceWithDetailedOutput(obj_id target, obj_id detector, float baseChanceToDetect, obj_id objectToReceiveDetailedOutput)
|
|
{
|
|
string invis = getInvisBuff(target);
|
|
if(invis != null && invis.length() > 0)
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealther=" + target + ", detector=" + detector + ", baseChanceToDetect=" + baseChanceToDetect + ", stealther invisBuff=" + invis);
|
|
|
|
if(buff.getPriority(invis) > MAX_INVIS_BUFF_TO_DETECT)
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: buff.getPriority(stealther invisBuff)=" + buff.getPriority(invis) + " which is > " + MAX_INVIS_BUFF_TO_DETECT + " so setting finalChanceToDetect to 0.0");
|
|
return 0.0f;
|
|
}
|
|
|
|
if(invis.startsWith("invis_sp_buff_invis_notrace"))
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealther invisBuff is invis_sp_buff_invis_notrace* so setting finalChanceToDetect to 0.0");
|
|
return 0.0f;
|
|
}
|
|
}
|
|
float finalChanceToDetect = 0.0f;
|
|
|
|
int stealtherLevel = getLevel(target);
|
|
int detectorLevel = getLevel(detector);
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel=" + stealtherLevel + ", detectorLevel=" + detectorLevel);
|
|
|
|
float levelChanceToDetectMod = 0.0f;
|
|
|
|
if (stealtherLevel > detectorLevel)
|
|
{
|
|
float levelDiff = stealtherLevel - detectorLevel;
|
|
if (baseChanceToDetect > 30)
|
|
{
|
|
levelChanceToDetectMod = (levelDiff *5.0f);// On Active checks we really need to crank up the penalty based on level since the default chance is high
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel > detectorLevel, levelDiff=" + levelDiff + ", baseChanceToDetect > 30, levelChanceToDetectMod (levelDiff * 5.0f)=" + levelChanceToDetectMod);
|
|
}
|
|
else
|
|
{
|
|
levelChanceToDetectMod = (levelDiff *1.5f);
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel > detectorLevel, levelDiff=" + levelDiff + ", baseChanceToDetect <= 30, levelChanceToDetectMod (levelDiff * 1.5f)=" + levelChanceToDetectMod);
|
|
}
|
|
finalChanceToDetect = baseChanceToDetect - levelChanceToDetectMod;
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel > detectorLevel, finalChanceToDetect (baseChanceToDetect - levelChanceToDetectMod)=" + finalChanceToDetect);
|
|
}
|
|
|
|
if (stealtherLevel < detectorLevel)
|
|
{
|
|
float levelDiff = detectorLevel - stealtherLevel;
|
|
levelChanceToDetectMod = (levelDiff*2.5f);
|
|
finalChanceToDetect = baseChanceToDetect + levelChanceToDetectMod;
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel < detectorLevel, levelDiff=" + levelDiff + ", levelChanceToDetectMod (levelDiff * 2.5f)=" + levelChanceToDetectMod + ", finalChanceToDetect (baseChanceToDetect + levelChanceToDetectMod)=" + finalChanceToDetect);
|
|
}
|
|
|
|
if (stealtherLevel == detectorLevel)
|
|
{
|
|
finalChanceToDetect = baseChanceToDetect;
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherLevel = detectorLevel, finalChanceToDetect (baseChanceToDetect)=" + finalChanceToDetect);
|
|
}
|
|
|
|
// Get the dettect hidden and camouflage mods of the stealther and detector
|
|
|
|
float modChanceToDetectMod = 0.0f;
|
|
|
|
int stealtherSkillMod = getApplicableInvisSkillMod(target);
|
|
|
|
int detectorSkillMod = getEnhancedSkillStatisticModifierUncapped(detector, "detect_hidden");
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherSkillMod=" + stealtherSkillMod + ", detectorSkillMod (detect_hidden)=" + detectorSkillMod);
|
|
|
|
if (stealtherSkillMod > detectorSkillMod)
|
|
{
|
|
float modDiff = (stealtherSkillMod - detectorSkillMod);
|
|
modChanceToDetectMod = modDiff/9.0f;
|
|
finalChanceToDetect = finalChanceToDetect - modChanceToDetectMod;
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherSkillMod > detectorSkillMod, modDiff=" + modDiff + ", modChanceToDetectMod (modDiff / 9.0f)=" + modChanceToDetectMod + ", finalChanceToDetect (finalChanceToDetect - modChanceToDetectMod)=" + finalChanceToDetect);
|
|
}
|
|
|
|
if (stealtherSkillMod < detectorSkillMod )
|
|
{
|
|
float modDiff = (detectorSkillMod - stealtherSkillMod);
|
|
modChanceToDetectMod = modDiff/4.0f;
|
|
finalChanceToDetect = finalChanceToDetect + modChanceToDetectMod;
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealtherSkillMod < detectorSkillMod, modDiff=" + modDiff + ", modChanceToDetectMod (modDiff / 4.0f)=" + modChanceToDetectMod + ", finalChanceToDetect (finalChanceToDetect + modChanceToDetectMod)=" + finalChanceToDetect);
|
|
}
|
|
|
|
if(invis.startsWith("invis_forceCloak") || (invis.startsWith("invis_fs_buff_invis") ))
|
|
{
|
|
// Give Jedi 10% Chance to avoid detection
|
|
finalChanceToDetect = (finalChanceToDetect - 10.0f);
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: Jedi with invis_forceCloak* or invis_fs_buff_invis*, finalChanceToDetect (finalChanceToDetect - 10.0f)=" + finalChanceToDetect);
|
|
}
|
|
|
|
if (!isPlayer(detector))
|
|
{
|
|
string name = getCreatureName(detector);
|
|
int difficultyClass = dataTableGetInt ("datatables/mob/creatures.iff", name, "difficultyClass");
|
|
if (difficultyClass == 1)
|
|
{
|
|
//Elite
|
|
finalChanceToDetect = (finalChanceToDetect +5.0f);
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: detector (" + name + ", difficultyClass=" + difficultyClass + ") is elite NPC, finalChanceToDetect (finalChanceToDetect + 5.0f)=" + finalChanceToDetect);
|
|
}
|
|
if (difficultyClass == 2)
|
|
{
|
|
//BOSS
|
|
finalChanceToDetect = (finalChanceToDetect +10.0f);
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: detector (" + name + ", difficultyClass=" + difficultyClass + ") is boss NPC, finalChanceToDetect (finalChanceToDetect + 10.0f)=" + finalChanceToDetect);
|
|
}
|
|
}
|
|
// Apply the min and max detecting chances here
|
|
//95% maximum chance to detect
|
|
// 1% minimum chance to detect
|
|
|
|
if (finalChanceToDetect > MAX_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
finalChanceToDetect = MAX_CHANCE_TO_DETECT_HIDDEN;
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: finalChanceToDetect > " + MAX_CHANCE_TO_DETECT_HIDDEN + " so finalChanceToDetect set to " + finalChanceToDetect);
|
|
}
|
|
|
|
if (finalChanceToDetect < MIN_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
if (isPlayer(detector))
|
|
{
|
|
finalChanceToDetect = MIN_CHANCE_TO_DETECT_HIDDEN;
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: finalChanceToDetect < " + MIN_CHANCE_TO_DETECT_HIDDEN + " and detector is player so finalChanceToDetect set to " + finalChanceToDetect);
|
|
}
|
|
}
|
|
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: ****FINAL CALCULATED*** finalChanceToDetect=" + finalChanceToDetect);
|
|
return finalChanceToDetect;
|
|
}
|
|
|
|
boolean activeDetectHiddenTarget(obj_id target, obj_id detector, float baseChanceToDetect)
|
|
{
|
|
float finalChanceToDetect = getDetectChance(target, detector, baseChanceToDetect);
|
|
float roll = rand(0f, 100f);
|
|
return (roll < finalChanceToDetect);
|
|
}
|
|
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
// passiveDetectHiddenTargetWithDetailedOutput() and passiveDetectHiddenTarget()
|
|
// ***MUST*** be kept in sync
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
boolean passiveDetectHiddenTarget(obj_id target, obj_id detector, int volume)
|
|
{
|
|
//sendSystemMessageTestingOnly(target, "CHECKING REVEAL");
|
|
|
|
if (isPlayer(detector))
|
|
{
|
|
if(!pvpCanAttack(detector, target))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ai_lib.isAggroToward(detector, target))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
float baseChanceToDetect = PASSIVE_DETECT_CHANCE_MOB; // Set it to the Mob default chance
|
|
|
|
if (volume == PASSIVE_BREACH_FAR)
|
|
{
|
|
baseChanceToDetect = PASSIVE_DETECT_CHANCE_FAR;
|
|
}
|
|
if (volume == PASSIVE_BREACH_NEAR)
|
|
{
|
|
baseChanceToDetect = PASSIVE_DETECT_CHANCE_NEAR;
|
|
}
|
|
|
|
|
|
float finalChanceToDetect = getDetectChance(target, detector, baseChanceToDetect);
|
|
//sendSystemMessageTestingOnly(target, "Final Chance was: " +finalChanceToDetect);
|
|
|
|
float roll = rand(0.0f, 100.0f);
|
|
|
|
return (roll < finalChanceToDetect);
|
|
}
|
|
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
// passiveDetectHiddenTargetWithDetailedOutput() and passiveDetectHiddenTarget()
|
|
// ***MUST*** be kept in sync
|
|
//
|
|
// ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT*** ***IMPORTANT***
|
|
//
|
|
boolean passiveDetectHiddenTargetWithDetailedOutput(obj_id target, obj_id detector, int volume, obj_id objectToReceiveDetailedOutput)
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealther=" + target + ", detector=" + detector + ", volume=" + volume);
|
|
|
|
if (isPlayer(detector))
|
|
{
|
|
if(!pvpCanAttack(detector, target))
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealther=" + target + ", detector=" + detector + ", volume=" + volume + ", isPlayer(detector) and !pvpCanAttack(detector, stealther) so returning detection FAIL");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ai_lib.isAggroToward(detector, target))
|
|
{
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: stealther=" + target + ", detector=" + detector + ", volume=" + volume + ", !isPlayer(detector) and !ai_lib.isAggroToward(detector, stealther) so returning detection FAIL");
|
|
return false;
|
|
}
|
|
}
|
|
float baseChanceToDetect = PASSIVE_DETECT_CHANCE_MOB; // Set it to the Mob default chance
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: setting baseChanceToDetect to default value of PASSIVE_DETECT_CHANCE_MOB(" + baseChanceToDetect + ")");
|
|
|
|
if (volume == PASSIVE_BREACH_FAR)
|
|
{
|
|
baseChanceToDetect = PASSIVE_DETECT_CHANCE_FAR;
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: volume is PASSIVE_BREACH_FAR so setting baseChanceToDetect to PASSIVE_DETECT_CHANCE_FAR(" + baseChanceToDetect + ")");
|
|
}
|
|
if (volume == PASSIVE_BREACH_NEAR)
|
|
{
|
|
baseChanceToDetect = PASSIVE_DETECT_CHANCE_NEAR;
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: volume is PASSIVE_BREACH_NEAR so setting baseChanceToDetect to PASSIVE_DETECT_CHANCE_NEAR(" + baseChanceToDetect + ")");
|
|
}
|
|
|
|
float finalChanceToDetect = getDetectChanceWithDetailedOutput(target, detector, baseChanceToDetect, objectToReceiveDetailedOutput);
|
|
float roll = rand(0.0f, 100.0f);
|
|
|
|
if (roll < finalChanceToDetect)
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: getDetectChanceWithDetailedOutput() returned finalChanceToDetect=" + finalChanceToDetect + ", dice roll=" + roll + ", dice roll < finalChanceToDetect so returning detection SUCCESS");
|
|
else
|
|
sendConsoleMessage(objectToReceiveDetailedOutput, "passiveDetectInfo: getDetectChanceWithDetailedOutput() returned finalChanceToDetect=" + finalChanceToDetect + ", dice roll=" + roll + ", dice roll >= finalChanceToDetect so returning detection FAIL");
|
|
|
|
return (roll < finalChanceToDetect);
|
|
}
|
|
|
|
// This function is depricated - see above passive and active checks with new logic
|
|
/*boolean detectHiddenTarget(boolean activeAttempt, obj_id target, obj_id detector, boolean findMobiles, boolean findInanimate, int volume)
|
|
{
|
|
// Base chance to detect is based on whether there is a trigger volume breach or a detect hidden usage
|
|
float baseChanceToDetect = activeAttempt? ACTIVE_DETECT_CHANCE : PASSIVE_DETECT_CHANCE;
|
|
boolean isInanimate = hasObjVar(target, CONCEALED_OBJECT);
|
|
|
|
if(isInanimate && !findInanimate)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!isInanimate && !findMobiles)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//early opt-out for detectors that are non-aggressive to the target, and not an active detect
|
|
if(!activeAttempt && !isInanimate && !(ai_lib.isAggroToward(detector, target)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Modify the base chance to detect based on the level & skill mod disparity of the two creatures
|
|
int hiddenLevel = isInanimate? getIntObjVar(target, CAMOUFLAGED_AT_LEVEL) : getLevel(target);
|
|
int hiddenSkillMod = isInanimate? getIntObjVar(target, CAMO_SKILL) : getApplicableInvisSkillMod(target);
|
|
int hiddenLocomotion = isInanimate? LOCOMOTION_STANDING : getLocomotion(target);
|
|
int hiddenLocomotionBonus = (hiddenLocomotion == LOCOMOTION_STANDING || hiddenLocomotion == LOCOMOTION_PRONE) ? 25 : 0;
|
|
float hiderScore = hiddenLevel + hiddenSkillMod + hiddenLocomotionBonus;
|
|
|
|
int detectorLevel = getLevel(detector);
|
|
int detectorSkillMod = getEnhancedSkillStatisticModifier(detector, "detect_hidden");
|
|
int detectorLocomotion = getLocomotion(detector);
|
|
int detectorLocomotionBonus = (detectorLocomotion == LOCOMOTION_STANDING || detectorLocomotion == LOCOMOTION_PRONE) ? 25 : 0;
|
|
float detectorScore = detectorLevel + detectorSkillMod + detectorLocomotionBonus;
|
|
|
|
float totalDisparity = detectorScore - hiderScore;
|
|
float chanceToDetect = baseChanceToDetect;
|
|
float detectRange = MAX_CHANCE_TO_DETECT_HIDDEN - MIN_CHANCE_TO_DETECT_HIDDEN;
|
|
|
|
// negative disparity is in favor of the hider
|
|
// positive disparity is in favor of the detector
|
|
|
|
if (totalDisparity > 0)
|
|
chanceToDetect += (totalDisparity / MAX_HIDING_DETECT_SCORE) * detectRange;
|
|
else
|
|
chanceToDetect += ((totalDisparity*0.65) / MAX_HIDING_DETECT_SCORE) * detectRange;
|
|
|
|
// Clamp the values
|
|
if(chanceToDetect > MAX_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
chanceToDetect = MAX_CHANCE_TO_DETECT_HIDDEN;
|
|
}
|
|
else if(chanceToDetect < MIN_CHANCE_TO_DETECT_HIDDEN)
|
|
{
|
|
chanceToDetect = MIN_CHANCE_TO_DETECT_HIDDEN;
|
|
}
|
|
|
|
//LOG("stealth", "totalDisparity=" + totalDisparity + ", chanceToDetect=" + chanceToDetect);
|
|
float roll = rand(0f, 100f);
|
|
//LOG("stealth", "detectHiddenTarget(" + activeAttempt + ", (" + target + ") " + getName(target) + ", " + getName(detector) + ") -Rolled " + roll + " with a " + chanceToDetect + " detect chance.");
|
|
return (roll < chanceToDetect);
|
|
}
|
|
*/
|
|
|
|
boolean testBubbleBreached(obj_id bubbleHolder, obj_id breacher, int volume)
|
|
{
|
|
if (hasObjVar(bubbleHolder, "debuggingPassiveDetectDetailedOutput"))
|
|
{
|
|
obj_id objectToReceiveDetailedOutput = bubbleHolder;
|
|
|
|
if (hasObjVar(bubbleHolder, "debuggingPassiveDetectDetailedOutputObject"))
|
|
{
|
|
string objVarValue = getStringObjVar(bubbleHolder, "debuggingPassiveDetectDetailedOutputObject");
|
|
if (objVarValue != null && objVarValue.length() > 0)
|
|
{
|
|
obj_id objVarValueAsOid = utils.stringToObjId(objVarValue.substring(1));
|
|
if (isValidId(objVarValueAsOid) && exists(objVarValueAsOid))
|
|
objectToReceiveDetailedOutput = objVarValueAsOid;
|
|
}
|
|
}
|
|
|
|
return passiveDetectHiddenTargetWithDetailedOutput(bubbleHolder, breacher, volume, objectToReceiveDetailedOutput);
|
|
}
|
|
else
|
|
{
|
|
return passiveDetectHiddenTarget(bubbleHolder, breacher, volume);
|
|
}
|
|
|
|
//return detectHiddenTarget(false, bubbleHolder, breacher, true, true, volume);
|
|
}
|
|
|
|
void testUseAction(obj_id player, obj_id object, int menuItem)
|
|
{
|
|
string invis = getInvisBuff(player);
|
|
if(invis != null && invis.length() > 0)
|
|
{
|
|
buff.removeBuff(player, invis);
|
|
}
|
|
}
|
|
|
|
void testInvisNonCombatAction(obj_id player, obj_id target)
|
|
{
|
|
testInvisCombatAction(player, target, -1);
|
|
}
|
|
|
|
void testInvisNonCombatAction(obj_id player, obj_id target, combat_data combatData)
|
|
{
|
|
// Procs and reactives should not bring you out of stealth.
|
|
if(!combatData.specialLine.equals("no_proc"))
|
|
testInvisCombatAction(player, target, combatData.minInvisLevelRequired);
|
|
}
|
|
|
|
void testInvisNonCombatAction(obj_id player, obj_id target, int minInvisRequiredForAction)
|
|
{
|
|
if(isIdValid(player) && exists(player) && beast_lib.isBeast(player))
|
|
{
|
|
player = getMaster(player);
|
|
}
|
|
|
|
string invis = getInvisBuff(player);
|
|
//LOG("stealth", "player " + player + " has invis buff on them of '" + invis + "'.");
|
|
if(invis != null && !invis.startsWith("invis_"))
|
|
{
|
|
int pri = buff.getPriority(invis);
|
|
if(minInvisRequiredForAction == -1 || pri < minInvisRequiredForAction)
|
|
{
|
|
if(!buff.hasBuff(player, BUFF_NO_BREAK_INVIS))
|
|
buff.removeBuff(player, invis);
|
|
}
|
|
}
|
|
}
|
|
|
|
void testInvisCombatAction(obj_id player, obj_id target)
|
|
{
|
|
testInvisCombatAction(player, target, -1);
|
|
}
|
|
|
|
void testInvisCombatAction(obj_id player, obj_id target, combat_data combatData)
|
|
{
|
|
// Procs and reactives should not bring you out of stealth.
|
|
if(!combatData.specialLine.equals("no_proc"))
|
|
testInvisCombatAction(player, target, combatData.minInvisLevelRequired);
|
|
}
|
|
|
|
void testInvisCombatAction(obj_id player, obj_id target, int minInvisRequiredForAction)
|
|
{
|
|
if(isIdValid(player) && exists(player) && beast_lib.isBeast(player))
|
|
{
|
|
player = getMaster(player);
|
|
}
|
|
|
|
string invis = getInvisBuff(player);
|
|
|
|
if(invis != null)
|
|
{
|
|
int pri = buff.getPriority(invis);
|
|
|
|
if(minInvisRequiredForAction == -1 || pri < minInvisRequiredForAction)
|
|
{
|
|
if(!buff.hasBuff(player, BUFF_NO_BREAK_INVIS))
|
|
{
|
|
combat.combatLog(player, target, "combatStandardAction", "Invisible - Clearing invisibility");
|
|
|
|
if(isPlayer(player))
|
|
{
|
|
utils.setScriptVar(player, "stealth.invisRemoved", invis);
|
|
utils.setScriptVar(player, "stealth.invisRemovedTime", getGameTime());
|
|
utils.setScriptVar(player, "stealth.invisRemovedTimeLeft", buff.getBuffTimeRemaining(player, invis));
|
|
}
|
|
|
|
buff.removeBuff(player, invis);
|
|
buff.applyBuff(player, "sp_attack_invis_recourse");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void reinstateInvisFromCombat(obj_id player)
|
|
{
|
|
if(!isIdValid(player) || !exists(player) || !isPlayer(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
buff.removeBuff(player, "sp_attack_invis_recourse");
|
|
|
|
int invisTime = utils.getIntScriptVar(player, "stealth.invisRemovedTime");
|
|
string invisName = utils.getStringScriptVar(player, "stealth.invisRemoved");
|
|
float timeLeft = utils.getFloatScriptVar(player, "stealth.invisRemovedTimeLeft");
|
|
|
|
if(invisTime > 0 && invisName != null && invisName.length() > 0 && invisTime - getGameTime() < 2)
|
|
{
|
|
if(timeLeft > 0)
|
|
{
|
|
buff.applyBuff(player, invisName, timeLeft);
|
|
}
|
|
else
|
|
{
|
|
float duration = buff.getDuration(invisName);
|
|
|
|
if(duration < 0)
|
|
{
|
|
buff.applyBuff(player, invisName, -1);
|
|
}
|
|
else
|
|
{
|
|
clearPreviousInvis(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void clearPreviousInvis(obj_id player)
|
|
{
|
|
if(!isPlayer(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
utils.removeScriptVar(player, "stealth.invisRemoved");
|
|
utils.removeScriptVar(player, "stealth.invisRemovedTime");
|
|
}
|
|
|
|
float calcBreachDistanceForSkill(obj_id player, string skillMod)
|
|
{
|
|
int mod = getEnhancedSkillStatisticModifier(player, skillMod);
|
|
float difference = INVIS_BREAK_MAX_FAR_DISTANCE - INVIS_BREAK_NEAR_DISTANCE;
|
|
return INVIS_BREAK_NEAR_DISTANCE + (difference / (1+ ((mod) / 100)));
|
|
}
|
|
|
|
void cleanupHate(obj_id player)
|
|
{
|
|
if(!isIdValid(player) || !exists(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id[] hateList = getHateList(player);
|
|
|
|
// clear @player from all hate lists he's on
|
|
|
|
for(int i = 0; i < hateList.length; i++)
|
|
{
|
|
if(isIdValid(hateList[i]) && exists(hateList[i]))
|
|
{
|
|
removeHateTarget(hateList[i], player);
|
|
|
|
removeHateTarget(player, hateList[i]);
|
|
}
|
|
}
|
|
|
|
clearHateList(player);
|
|
}
|
|
|
|
obj_id makeBeastInvisible(obj_id player, string clientEffect)
|
|
{
|
|
if(!isIdValid(player) || !exists(player) || !beast_lib.isBeastMaster(player))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_id beast = beast_lib.getBeastOnPlayer(player);
|
|
|
|
if(!isIdValid(beast) || !exists(beast))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
attachScript(beast, "systems.skills.stealth.stealth_monitor");
|
|
messageTo(beast, "updatePassiveRevealList", null, 10, false);
|
|
setState(beast, STATE_COVER, true);
|
|
setCreatureCoverVisibility(beast, false);
|
|
|
|
cleanupHate(beast);
|
|
stopCombat(beast);
|
|
utils.removeScriptVar(beast, "ai.combat.target");
|
|
|
|
addPassiveReveal(beast, player, 1);
|
|
makePassiveRevealed(beast);
|
|
|
|
if(clientEffect == null || clientEffect.length() <= 0)
|
|
{
|
|
clientEffect = "appearance/pt_smoke_puff.prt";
|
|
}
|
|
|
|
playClientEffectObj(beast, clientEffect, beast, "");
|
|
|
|
int covert_mastery = getEnhancedSkillStatisticModifierUncapped(player, "expertise_covert_mastery");
|
|
|
|
if(covert_mastery > 0)
|
|
{
|
|
buff.applyBuff(beast, "sp_covert_mastery");
|
|
}
|
|
|
|
return beast;
|
|
}
|
|
|
|
boolean canBeastStealth(obj_id beast)
|
|
{
|
|
obj_id master = getMaster(beast);
|
|
|
|
if(isDead(beast) || ai_lib.isInCombat(beast))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isIdValid(master) && exists(master) && getState(master, STATE_COVER) == 1)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void makeCreatureVisible(obj_id creature)
|
|
{
|
|
if(!isIdValid(creature) || !exists(creature))
|
|
{
|
|
return;
|
|
}
|
|
|
|
detachScript(creature, "systems.skills.stealth.stealth_monitor");
|
|
|
|
setState(creature, STATE_COVER, false);
|
|
|
|
clearPassiveRevealList(creature);
|
|
setCreatureCoverVisibility(creature, true);
|
|
playClientEffectObj(creature, "appearance/pt_sonic_pulse.prt", creature, "");
|
|
}
|
|
|
|
void makeBeastVisible(obj_id player)
|
|
{
|
|
if(!isIdValid(player) || !exists(player) || !beast_lib.isBeastMaster(player))
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj_id beast = beast_lib.getBeastOnPlayer(player);
|
|
|
|
makeCreatureVisible(beast);
|
|
}
|
|
|
|
void makePassiveRevealed(obj_id target)
|
|
{
|
|
obj_id beast = null;
|
|
|
|
if(isIdValid(target) && exists(target) && beast_lib.isBeast(target))
|
|
{
|
|
obj_id master = getMaster(target);
|
|
|
|
if(isIdValid(master) && exists(master))
|
|
{
|
|
target = master;
|
|
}
|
|
}
|
|
|
|
if(isPlayer(target))
|
|
{
|
|
beast = beast_lib.getBeastOnPlayer(target);
|
|
}
|
|
|
|
if(group.isGrouped(target))
|
|
{
|
|
obj_id groupObj = getGroupObject(target);
|
|
obj_id[] groupMembers = getGroupMemberIds(groupObj);
|
|
int numGroupMembers = groupMembers.length;
|
|
|
|
for(int f = 0; f < numGroupMembers; f++)
|
|
{
|
|
obj_id groupie = groupMembers[f];
|
|
|
|
if(isIdValid(groupie) && exists(groupie) && groupie != target)
|
|
{
|
|
// Add group members to the passive reveal list
|
|
addPassiveReveal(target, groupie, 1);
|
|
|
|
if(isIdValid(beast) && exists(beast))
|
|
{
|
|
addPassiveReveal(beast, groupie, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _makeInvisible(obj_id player, string_id flyText, string clientEffect, string_id msg, float breachDistance)
|
|
{
|
|
if(breachDistance <= INVIS_BREAK_NEAR_DISTANCE)
|
|
{
|
|
breachDistance = INVIS_BREAK_NEAR_DISTANCE + 1f;
|
|
}
|
|
|
|
attachScript(player, "systems.skills.stealth.stealth_monitor");
|
|
messageTo(player, "updatePassiveRevealList", null, 10, false);
|
|
setState(player, STATE_COVER, true);
|
|
setCreatureCoverVisibility(player, false);
|
|
|
|
cleanupHate(player);
|
|
|
|
obj_id beast = makeBeastInvisible(player, clientEffect);
|
|
|
|
makePassiveRevealed(player);
|
|
|
|
if(flyText != null)
|
|
{
|
|
showFlyTextPrivate(player, player, flyText, 1.5f, colors.TOMATO);
|
|
}
|
|
|
|
if(clientEffect != null && clientEffect.length() > 0)
|
|
{
|
|
playClientEffectObj(player, clientEffect, player, "");
|
|
}
|
|
|
|
if(msg != null)
|
|
{
|
|
sendSystemMessage(player, msg);
|
|
}
|
|
|
|
if(!utils.hasScriptVar(player, INVIS_UPKEEP_MSG_DISPATCHED))
|
|
{
|
|
messageTo(player, "invisibilityUpkeep", null, 1, false);
|
|
}
|
|
|
|
if(breachDistance > 0)
|
|
{
|
|
createTriggerVolume(INVIS_BREAK_RADIUS_FAR, breachDistance, true );
|
|
createTriggerVolume(INVIS_BREAK_RADIUS_NEAR, INVIS_BREAK_NEAR_DISTANCE, true );
|
|
}
|
|
|
|
}
|
|
|
|
void _makeVisible(obj_id player, string_id flyText, string_id msg)
|
|
{
|
|
//Thread.dumpStack();
|
|
// remove the trigger volumes even if we're switching into another form of stealth ("supressVisible")
|
|
// because im not sure if the trigger volume values get updated if we reset them in _makeInvisible
|
|
removeTriggerVolume(INVIS_BREAK_RADIUS_FAR);
|
|
removeTriggerVolume(INVIS_BREAK_RADIUS_NEAR);
|
|
clearPassiveRevealList(player);
|
|
detachScript(player, "systems.skills.stealth.stealth_monitor");
|
|
|
|
if(!utils.hasScriptVar(player, "supressVisible"))
|
|
{
|
|
setState(player, STATE_COVER, false);
|
|
|
|
setCreatureCoverVisibility(player, true);
|
|
|
|
makeBeastVisible(player);
|
|
|
|
if(flyText != null)
|
|
{
|
|
showFlyTextPrivate(player, player, flyText, 1.5f, colors.TOMATO);
|
|
}
|
|
|
|
if(msg != null)
|
|
{
|
|
sendSystemMessage(player, msg);
|
|
}
|
|
|
|
playClientEffectObj(player, "appearance/pt_sonic_pulse.prt", player, "");
|
|
}
|
|
else
|
|
{
|
|
utils.removeScriptVar(player, "supressVisible");
|
|
}
|
|
}
|
|
|
|
|
|
boolean checkRevealRecourse(obj_id player)
|
|
{
|
|
if(buff.hasBuff(player, "sp_sneak_recourse"))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
boolean checkAttackRevealRecourse(obj_id player)
|
|
{
|
|
if(buff.hasBuff(player, "sp_attack_invis_recourse"))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Jedi "Force Cloak" ability. Allows you to vanish for a brief period of time
|
|
//
|
|
boolean canPerformForceCloak(obj_id player)
|
|
{
|
|
if(combat.isInCombat(player) && (int)getSkillStatisticModifier(player, "expertise_force_cloak_combat_escape") <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "petCreationPending") && utils.getIntScriptVar(player, "petCreationPending") > getGameTime())
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cannot_perform_calling_companion"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Smuggler "Hide" ability. Allows you to vanish for a brief period of time
|
|
//
|
|
boolean canPerformHide(obj_id player)
|
|
{
|
|
return canPerformStationaryInvis(player);
|
|
}
|
|
|
|
|
|
void forceCloak(obj_id player)
|
|
{
|
|
if(buff.hasBuff(player, "fs_force_run"))
|
|
{
|
|
buff.removeBuff(player, "fs_force_run");
|
|
}
|
|
|
|
_makeInvisible(player, new string_id("combat_effects", "go_cloak"),
|
|
"clienteffect/combat_special_attacker_cover.cef", null, -1);
|
|
}
|
|
|
|
void hide(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "hide_go"),
|
|
"clienteffect/combat_special_attacker_cover.cef", null, -1);
|
|
}
|
|
|
|
boolean canPerformSmokeGrenade(obj_id player)
|
|
{
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_sp_buff_invis_1"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "petCreationPending") && utils.getIntScriptVar(player, "petCreationPending") > getGameTime())
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cannot_perform_calling_companion"));
|
|
return false;
|
|
}
|
|
if (checkAttackRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_attack_recourse"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean canPerformWithoutTrace(obj_id player)
|
|
{
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_sp_buff_invis_notrace_1"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "petCreationPending") && utils.getIntScriptVar(player, "petCreationPending") > getGameTime())
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cannot_perform_calling_companion"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean canPerformStationaryInvis(obj_id player)
|
|
{
|
|
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_blendIn"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "petCreationPending") && utils.getIntScriptVar(player, "petCreationPending") > getGameTime())
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cannot_perform_calling_companion"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void smokeGrenade(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "smokegrenade"), "appearance/pt_smoke_puff.prt", null, -1);
|
|
buff.applyBuff(player, BUFF_NO_BREAK_INVIS);
|
|
}
|
|
|
|
void bothanInnate(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "fly_bothan_ability_1"),
|
|
"appearance/pt_smoke_puff.prt", null, -1);
|
|
}
|
|
|
|
//
|
|
// Ranger "Stealth" ability. Allows you to vanish for a brief period of time
|
|
//
|
|
boolean canPerformStealth(obj_id player)
|
|
{
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if (ai_lib.isInCombat(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_combat"));
|
|
return false;
|
|
}
|
|
|
|
string invis = getInvisBuff(player);
|
|
|
|
if(invis != null)
|
|
{
|
|
if(invis.startsWith("invis_sp_buff_invis_notrace"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!hasServerCoverState(player))
|
|
{
|
|
if (checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
/*obj_id[] stuff = getCreaturesInRange(player, 5f);
|
|
|
|
// Get number of non-allies in the area
|
|
int cnt = 0;
|
|
int sGuildId = getGuildId(player);
|
|
|
|
if(stuff.length > 0)
|
|
{
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(ai_lib.isHumanoid(stuff[i]) && !isDead(stuff[i]) && !vehicle.isVehicle(stuff[i]))
|
|
{
|
|
boolean isInSameGuild = (sGuildId != 0) && (getGuildId(stuff[i]) == sGuildId);
|
|
if(!isInSameGuild && !group.inSameGroup(player, stuff[i]))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "stealth_people_around"));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
if(utils.hasScriptVar(player, "petCreationPending") && utils.getIntScriptVar(player, "petCreationPending") > getGameTime())
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cannot_perform_calling_companion"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void stealth(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "stealth"),"appearance/pt_smoke_puff.prt", null, calcBreachDistanceForSkill(player, "camouflage"));
|
|
buff.applyBuff(player, BUFF_NO_BREAK_INVIS);
|
|
}
|
|
|
|
void withoutTrace(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "without_trace"),"appearance/pt_smoke_puff.prt", null, -1);
|
|
int covert_mastery = getEnhancedSkillStatisticModifierUncapped(player, "expertise_covert_mastery");
|
|
|
|
if(covert_mastery > 0)
|
|
{
|
|
buff.applyBuff(player, "sp_covert_mastery");
|
|
}
|
|
|
|
buff.applyBuff(player, BUFF_NO_BREAK_INVIS);
|
|
}
|
|
|
|
void decoyStealth(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "decoy_stealth"),"", null, -1);
|
|
buff.applyBuff(player, BUFF_NO_BREAK_INVIS);
|
|
|
|
}
|
|
|
|
void invisBuffAdded(obj_id mobile, string effectName)
|
|
{
|
|
effectName = toLower(effectName);
|
|
|
|
//Removes any wearable appearance item with particle.
|
|
messageTo(mobile, "addAppearanceItemEffect", null, 1, false);
|
|
|
|
if(effectName.startsWith("invis_sp_buff_invis_notrace"))
|
|
{
|
|
withoutTrace(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_sp_buff_invis"))
|
|
{
|
|
smokeGrenade(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_bothan_ability_1") )
|
|
{
|
|
bothanInnate(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_sp_buff_stealth"))
|
|
{
|
|
stealth(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_fs_buff_invis"))
|
|
{
|
|
forceCloak(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_sm_buff_invis"))
|
|
{
|
|
stealth(mobile);
|
|
}
|
|
else if(effectName.startsWith("invis_sp_diversion_stealth"))
|
|
{
|
|
decoyStealth(mobile);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ranger Camouflageplayer ability. Allows you to vanish in the wilderness only, provided a proper camo kit is available.
|
|
//
|
|
boolean canPerformCamouflageSelf(obj_id player)
|
|
{
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if (checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_camouflage"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isInUrban(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_self_urban"));
|
|
return false;
|
|
}
|
|
|
|
obj_id kit = getCamoKitForCurrentPlanet(player);
|
|
if(!isIdValid(kit))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_self_no_kit"));
|
|
return false;
|
|
}
|
|
|
|
utils.setScriptVar(player, CAMO_KIT_ID, kit);
|
|
return true;
|
|
}
|
|
|
|
void camouflageSelf(obj_id player)
|
|
{
|
|
obj_id kit = utils.getObjIdScriptVar(player, CAMO_KIT_ID);
|
|
if(!isIdValid(kit))
|
|
{
|
|
return;
|
|
}
|
|
|
|
consumeCamoKit(kit);
|
|
|
|
// (obj_id player, string skillMod, float distance, int witnessPenalty, int skillModMod)
|
|
boolean success = witnessModifiedSkillCheck(player, "camouflage", 15, 5, 0);
|
|
if(success)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "camouflaged"),
|
|
"appearance/pt_smoke_puff.prt", null, calcBreachDistanceForSkill(player, "camouflage"));
|
|
}
|
|
else
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "failedcamo"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ranger CamouflageAlly ability. Allows you to hide an allied player (group or PA), provided a proper camo kit is available
|
|
//
|
|
boolean canPerformCamouflageAlly(obj_id player, obj_id target)
|
|
{
|
|
if(!isIdValid(target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "no_target"));
|
|
return false;
|
|
}
|
|
|
|
if(!isPlayer(target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "bad_target"));
|
|
return false;
|
|
}
|
|
|
|
//Can't make an ally in a costume invis.
|
|
int shapechange = buff.getBuffOnTargetFromGroup(target, "shapechange");
|
|
|
|
if(shapechange != 0)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "not_while_shapechanged"));
|
|
return false;
|
|
}
|
|
|
|
if(target == player)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_self"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(target, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(getDistance(player, target) > 46)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_ally_far"));
|
|
return false;
|
|
}
|
|
|
|
if(!pvpCanHelp(player, target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_ally_faction"));
|
|
return false;
|
|
}
|
|
|
|
if (checkRevealRecourse(target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse_other"));
|
|
return false;
|
|
}
|
|
|
|
int tGuildId = getGuildId(target);
|
|
int sGuildId = getGuildId(player);
|
|
boolean isInSameGuild = (tGuildId != 0) && (tGuildId == sGuildId);
|
|
|
|
if(!isInSameGuild && !group.inSameGroup(player, target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_ally_not_allied"));
|
|
return false;
|
|
}
|
|
|
|
if(pvpIsDueling(player, target))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_camo_ally_dueling"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Ranger UrbanStealth ability. Allows you to move about hidden in an urban environment, provided you have a
|
|
// Holographic Environment Projector (HEP) available. To activate the HEP, the ranger must either be "blended in"
|
|
// or there must not be any humanoids in the area
|
|
//
|
|
boolean canPerformUrbanStealth(obj_id player, obj_id possibleHep)
|
|
{
|
|
if(buff.hasBuff(player, "forceRun") || buff.hasBuff(player, "forceRun_1") || buff.hasBuff(player, "forceRun_2"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_fc_run"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if (checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_urbanStealth"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(!stealth.isInUrban(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_urban_stealth_environment"));
|
|
return false;
|
|
}
|
|
|
|
// Must have a HEP to do any kind of urban stealth/hiding
|
|
obj_id hep = stealth.getHEPObj(player, possibleHep);
|
|
if(!isIdValid(hep))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_urban_stealth_no_hep"));
|
|
return false;
|
|
}
|
|
|
|
// must either be "blended in" or be in an area void of humanoids to activate a HEP
|
|
if(!buff.hasBuff(player, "invis_blendIn"))
|
|
{
|
|
int sGuildId = getGuildId(player);
|
|
boolean isInSameGuild = false;
|
|
obj_id[] stuff = getCreaturesInRange(player, 20f);
|
|
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(isPlayer(stuff[i]) || ai_lib.isNpc(stuff[i]))
|
|
{
|
|
isInSameGuild = (sGuildId != 0) && (getGuildId(stuff[i]) == sGuildId);
|
|
if(!isInSameGuild && !group.inSameGroup(player, stuff[i]))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_urban_stealth_people_in_area"));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
utils.setScriptVar(player, CAMO_KIT_ID, hep);
|
|
return true;
|
|
}
|
|
|
|
void urbanStealth(obj_id player)
|
|
{
|
|
obj_id hep = utils.getObjIdScriptVar(player, CAMO_KIT_ID);
|
|
if(!isIdValid(hep))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!testAndDoHepUpkeep(hep, player, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't want this thing traded after its activated
|
|
utils.makeItemNoDrop(hep);
|
|
setObjVar(player, ACTIVE_HEP, hep);
|
|
if(hasInvisibleBuff(player))
|
|
{
|
|
// this is necessary, b/c when the camouflage buff is removed to apply the stealth buff,
|
|
// the player will be made visible for a brief moment if this script var is not present.
|
|
utils.setScriptVar(player, "supressVisible", 1);
|
|
}
|
|
|
|
// We know that the player has a HEP and that they are either blended in, or there are no people about
|
|
// because this function doesn't get called unless canPerformUrbanStealth (listed above) returned true,
|
|
// so just make 'em invisible
|
|
_makeInvisible(player, new string_id("spam", "stealthon"), "appearance/pt_detector.prt", null, calcBreachDistanceForSkill(player, "stealth"));
|
|
}
|
|
|
|
//
|
|
// Ranger WildernessStealth ability. Allows you to move about hidden in a wilderness setting, provided you are either already
|
|
// camouflaged, or you have a proper camo kit available. Going straight into stealth without being camouflaged first, has a
|
|
// high probability of failing at the early levels
|
|
//
|
|
boolean canPerformWildernessStealth(obj_id player)
|
|
{
|
|
if(buff.hasBuff(player, "forceRun") || buff.hasBuff(player, "forceRun_1") || buff.hasBuff(player, "forceRun_2"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_fc_run"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if (checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
if(!buff.canApplyBuff(player, "invis_wildernessStealth"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
if(!stealth.isInWilderness(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_wilderness_stealth_environment"));
|
|
return false;
|
|
}
|
|
|
|
boolean isHidden = hasInvisibleBuff(player);
|
|
if(!isHidden)
|
|
{
|
|
obj_id kit = getCamoKitForCurrentPlanet(player);
|
|
if(!isIdValid(kit))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_wilderness_stealth_no_kit"));
|
|
return false;
|
|
}
|
|
|
|
utils.setScriptVar(player, CAMO_KIT_ID, kit);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void wildernessStealth(obj_id player)
|
|
{
|
|
obj_id kit = utils.getObjIdScriptVar(player, CAMO_KIT_ID);
|
|
|
|
// (obj_id player, string skillMod, float distance, int witnessPenalty, int skillModMod)
|
|
int penalty = 0;
|
|
if(!hasInvisibleBuff(player))
|
|
{
|
|
if(!isIdValid(kit))
|
|
{
|
|
return;
|
|
}
|
|
|
|
consumeCamoKit(kit);
|
|
penalty = 50;
|
|
}
|
|
else
|
|
{
|
|
// this is necessary, b/c when the camouflage buff is removed to apply the stealth buff,
|
|
// the player will be made visible for a brief moment if this script var is present.
|
|
utils.setScriptVar(player, "supressVisible", 1);
|
|
}
|
|
|
|
boolean success = witnessModifiedSkillCheck(player, "stealth", 20, 10, penalty);
|
|
if(success)
|
|
{
|
|
_makeInvisible(player, new string_id("spam", "stealthon"),
|
|
"appearance/pt_smoke_puff.prt", null, calcBreachDistanceForSkill(player, "stealth"));
|
|
}
|
|
else
|
|
{
|
|
utils.removeScriptVar(player, "supressVisible");
|
|
sendSystemMessage(player, new string_id("spam", "stealthfail"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean canPerformCover(obj_id player)
|
|
{
|
|
if(isDead(player) || isIncapacitated(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_state"));
|
|
return false;
|
|
}
|
|
|
|
if(buff.hasBuff(player, "battlefield_communication_run"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (checkRevealRecourse(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_do_it_recourse"));
|
|
return false;
|
|
}
|
|
|
|
|
|
if(!buff.canApplyBuff(player, "invis_cover"))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_apply_invis_buff"));
|
|
return false;
|
|
}
|
|
|
|
// obj_id[] objects = getObjectsInRange(player, INVIS_BREAK_MAX_FAR_DISTANCE);
|
|
|
|
// Check for people too close
|
|
/*if(objects != null && objects.length > 0)
|
|
{
|
|
for (int i = 0; i < objects.length; i++)
|
|
{
|
|
if (isPlayer(objects[i]) || isMob(objects[i]))
|
|
{
|
|
if (objects[i] == player)
|
|
continue;
|
|
|
|
if (isDead(objects[i]) || isIncapacitated(objects[i]))
|
|
continue;
|
|
|
|
if(group.inSameGroup(player, objects[i])) // Group members don't prevent/break cover
|
|
continue;
|
|
|
|
// Something too close fail cover ability
|
|
sendSystemMessage(player, new string_id("cbt_spam", "cover_fail_target_near"));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
void cover(obj_id player)
|
|
{
|
|
_makeInvisible(player, new string_id("combat_effects", "go_cover"),
|
|
"clienteffect/combat_special_attacker_cover.cef", null, calcBreachDistanceForSkill(player, "camouflage"));
|
|
sendSystemMessage(player, new string_id("cbt_spam", "cover_success_single"));
|
|
}
|
|
|
|
|
|
void disableHep(obj_id hep, obj_id player)
|
|
{
|
|
if(isIdValid(hep) && exists(hep))
|
|
{
|
|
int power = getCount(hep);
|
|
float multiplier = .5f; // power cell down by 50% if you let it run out
|
|
if(power > HEP_UPKEEP_COST) // turned it off before it was depleted
|
|
{
|
|
multiplier = .95f; // power down by 5% if you turn it off before the juice runs out
|
|
}
|
|
|
|
power = (int)(getIntObjVar(hep, HEP_BASE_POWER) * multiplier);
|
|
setCount(hep, power);
|
|
setObjVar(hep, HEP_BASE_POWER, power);
|
|
|
|
utils.clearNoDropFromItem(hep);
|
|
|
|
if(power < HEP_UPKEEP_COST)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "hep_low_power"));
|
|
setCondition(hep, CONDITION_DISABLED);
|
|
}
|
|
}
|
|
|
|
if(isIdValid(player))
|
|
{
|
|
removeObjVar(player, ACTIVE_HEP);
|
|
}
|
|
}
|
|
|
|
|
|
boolean testAndDoHepUpkeep(obj_id hep, obj_id player, boolean silent)
|
|
{
|
|
int power = getCount(hep);
|
|
if(power < HEP_UPKEEP_COST)
|
|
{
|
|
if(!silent)
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "hep_out_of_juice"));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
power = getCount(hep) - HEP_UPKEEP_COST;
|
|
setCount(hep, power);
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean checkSpyStealthUpkeep(obj_id player)
|
|
{
|
|
float camouflage = getEnhancedSkillStatisticModifierUncapped(player, "camouflage");
|
|
camouflage += getEnhancedSkillStatisticModifierUncapped(player, "stealth");
|
|
|
|
float drainRatio = 10.0f;
|
|
if (camouflage < 100.0f)
|
|
{
|
|
drainRatio = 7.91f - (.1f / (.1f + ((camouflage) / 100.0f))); // diminishing "camouflage" returns. ratio is somewhere between 6.91 & 7.81 for 0 to 90 camo.
|
|
}
|
|
int actionCost = (int)(getMaxAction(player) / drainRatio);
|
|
int action = testDrainAttribute(player, ACTION, actionCost);
|
|
if(action < 0 || !drainAttributes(player, actionCost, 0))
|
|
{
|
|
checkForAndMakeVisible(player);
|
|
return false;
|
|
}
|
|
|
|
string_id strSpam = new string_id("combat_effects", "damage_fly");
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, strSpam);
|
|
pp = prose.setDI(pp, -actionCost);
|
|
showFlyText(player, pp, 1f, colors.GREEN);
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean checkUrbanStealthUpkeep(obj_id player)
|
|
{
|
|
if(!isInUrban(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_urban_stealth_environment"));
|
|
buff.removeBuff(player, "invis_urbanStealth");
|
|
return false;
|
|
}
|
|
|
|
obj_id hep = getObjIdObjVar(player, ACTIVE_HEP);
|
|
if(!isIdValid(hep) || !exists(hep))
|
|
{
|
|
// 0mg! H4x!?!?
|
|
buff.removeBuff(player, "invis_urbanStealth");
|
|
return false;
|
|
}
|
|
|
|
if(!testAndDoHepUpkeep(hep, player, false))
|
|
{
|
|
buff.removeBuff(player, "invis_urbanStealth");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean checkWildernessStealthUpkeep(obj_id player)
|
|
{
|
|
if(!isInWilderness(player))
|
|
{
|
|
sendSystemMessage(player, new string_id("spam", "cant_wilderness_stealth_environment"));
|
|
buff.removeBuff(player, "invis_wildernessStealth");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
boolean checkForceCloakUpkeep(obj_id player)
|
|
{
|
|
float forceCost = dataTableGetFloat(jedi.JEDI_ACTIONS_FILE, "forceCloak", "extraForceCost");
|
|
|
|
obj_id[] players = getPlayerCreaturesInRange(player, stealth.INVIS_BREAK_MAX_FAR_DISTANCE);
|
|
obj_id[] npcs = getNPCsInRange(player, stealth.INVIS_BREAK_MAX_FAR_DISTANCE);
|
|
int nearCount = players.length + npcs.length;
|
|
|
|
int i = 0;
|
|
for(i = 0; i < players.length; i++)
|
|
{
|
|
if(group.inSameGroup(player, players[i]))
|
|
{
|
|
nearCount--;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < npcs.length; i++)
|
|
{
|
|
if(group.inSameGroup(player, npcs[i]))
|
|
{
|
|
nearCount--;
|
|
}
|
|
}
|
|
|
|
// just to make sure.
|
|
if(nearCount < 0)
|
|
nearCount = 0;
|
|
|
|
forceCost *= nearCount;
|
|
|
|
if (!jedi.drainForcePower(player, (int)forceCost))
|
|
{
|
|
buff.removeBuff(player, "invi_forceCloak");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean checkForAndMakeVisible(obj_id player)
|
|
{
|
|
string ibuff = getInvisBuff(player);
|
|
if(ibuff == null || ibuff.length() < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
buff.applyBuff(player, "sp_sneak_recourse");
|
|
buff.removeBuff(player, ibuff);
|
|
return true;
|
|
}
|
|
|
|
boolean checkForAndMakeVisibleNoRecourse(obj_id player)
|
|
{
|
|
string ibuff = getInvisBuff(player);
|
|
if(ibuff == null || ibuff.length() < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
buff.removeBuff(player, ibuff);
|
|
return true;
|
|
}
|
|
|
|
boolean hasInvisibleBuff(obj_id object)
|
|
{
|
|
return getInvisBuff(object) != null || (isMob(object) && !getCreatureCoverVisibility(object));
|
|
}
|
|
|
|
string getInvisBuff(obj_id player)
|
|
{
|
|
int[] buffs = buff.getAllBuffs(player);
|
|
if(buffs == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string curBuff = "";
|
|
for(int i = 0; i < buffs.length; i++)
|
|
{
|
|
curBuff = buff.getBuffNameFromCrc(buffs[i]);
|
|
if(curBuff != null && curBuff.startsWith("invis_"))
|
|
{
|
|
return curBuff;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
boolean hasServerCoverState(obj_id player)
|
|
{
|
|
return getState(player, STATE_COVER) > 0;
|
|
}
|
|
|
|
obj_id getHEPObj(obj_id player, obj_id possibleHep)
|
|
{
|
|
int power = 0;
|
|
if(hasObjVar(possibleHep, "isHEP" ))
|
|
{
|
|
power = getCount(possibleHep);
|
|
if(power >= HEP_UPKEEP_COST)
|
|
{
|
|
// This is a Holographic Environment Projector with enough juice to activate
|
|
return possibleHep;
|
|
}
|
|
}
|
|
|
|
obj_id pInv = utils.getInventoryContainer(player);
|
|
if (!isIdValid(pInv))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_id[] contents = getContents(pInv);
|
|
obj_id hep = null;
|
|
for(int i = 0; i < contents.length; i++)
|
|
{
|
|
if(hasObjVar(contents[i], "isHEP" ))
|
|
{
|
|
power = getCount(contents[i]);
|
|
if(power >= HEP_UPKEEP_COST)
|
|
{
|
|
// This is a Holographic Environment Projector with enough juice to activate
|
|
hep = contents[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hep;
|
|
}
|
|
|
|
obj_id getCamoKitForCurrentPlanet(obj_id player)
|
|
{
|
|
// Check to see if we have a camo kit for this planet.
|
|
string planetName = getCurrentSceneName();
|
|
if(planetName.length() > 7)
|
|
{
|
|
string sceneSubString = planetName.substring(0,8);
|
|
// All of Kashyyyk takes the same kit
|
|
if(sceneSubString.equals("kashyyyk"))
|
|
{
|
|
planetName = "kashyyyk";
|
|
}
|
|
}
|
|
|
|
obj_id pInv = utils.getInventoryContainer(player);
|
|
if (!isIdValid(pInv))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
obj_id[] contents = getContents(pInv);
|
|
obj_id kit = null;
|
|
for(int i=0; i<contents.length; i++)
|
|
{
|
|
if(hasObjVar( contents[i], "camokit" ))
|
|
{
|
|
// This is a camo kit.
|
|
string kitPlanet = getStringObjVar(contents[i], "camokit");
|
|
if(kitPlanet == planetName)
|
|
{
|
|
kit = contents[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kit;
|
|
}
|
|
|
|
void consumeCamoKit(obj_id kit)
|
|
{
|
|
int count = getCount( kit );
|
|
if( count <= 1 )
|
|
{
|
|
destroyObject( kit );
|
|
}
|
|
else
|
|
{
|
|
count--;
|
|
setCount( kit, count );
|
|
}
|
|
}
|
|
|
|
boolean isInWilderness(obj_id player)
|
|
{
|
|
// not outside?
|
|
obj_id container = getTopMostContainer(player);
|
|
if(isIdValid(container) && isGameObjectTypeOf(container, GOT_building))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
location loc = getLocation(player);
|
|
// is in static city or player city
|
|
if(locations.isInCity(loc) || city.isInCity(loc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// see if there's a bunch of buildings around the area
|
|
obj_id[] stuff = getNonCreaturesInRange(loc, 50f);
|
|
int numStructures = 0;
|
|
for(int i = 0; i < stuff.length; i++)
|
|
{
|
|
if(isGameObjectTypeOf(stuff[i], GOT_building ) || isGameObjectTypeOf(stuff[i], GOT_installation))
|
|
{
|
|
numStructures++;
|
|
}
|
|
}
|
|
|
|
if(numStructures > MAX_BUILDINGS_FOR_WILDNERNESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean isInUrban(obj_id player)
|
|
{
|
|
return (!isInWilderness(player));
|
|
}
|
|
|
|
// tests bitmask for flag
|
|
//
|
|
boolean isFlagSet(int mask, int flag)
|
|
{
|
|
if((mask & flag) == flag)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//proximity mines are pvp mines
|
|
boolean isPvPTrap(obj_id trap)
|
|
{
|
|
int triggerTypeInt = getIntObjVar(trap, TRIGGER_TYPE);
|
|
if(triggerTypeInt == TRIGGER_TYPE_PROXIMITY)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
obj_id createDecoy(obj_id spy)
|
|
{
|
|
location myLoc = getLocation(spy);
|
|
int species = getSpecies(spy);
|
|
int gender = getGender(spy);
|
|
|
|
string template = "object/mobile/hologram/";
|
|
|
|
string speciesString = "human";
|
|
string genderString = "male";
|
|
|
|
switch(species)
|
|
{
|
|
case SPECIES_RODIAN: speciesString = "rodian";
|
|
break;
|
|
case SPECIES_TRANDOSHAN: speciesString = "trandoshan";
|
|
break;
|
|
case SPECIES_MON_CALAMARI: speciesString = "moncal";
|
|
break;
|
|
case SPECIES_WOOKIEE: speciesString = "wookiee";
|
|
break;
|
|
case SPECIES_BOTHAN: speciesString = "bothan";
|
|
break;
|
|
case SPECIES_TWILEK: speciesString = "twilek";
|
|
break;
|
|
case SPECIES_ZABRAK: speciesString = "zabrak";
|
|
break;
|
|
case SPECIES_ITHORIAN: speciesString = "ithorian";
|
|
break;
|
|
case SPECIES_SULLUSTAN: speciesString = "sullustan";
|
|
break;
|
|
}
|
|
|
|
if(gender == GENDER_FEMALE)
|
|
genderString = "female";
|
|
|
|
template += speciesString + "_" + genderString + ".iff";
|
|
|
|
obj_id hologram = createObject(template, myLoc);
|
|
|
|
if(isIdValid(hologram))
|
|
{
|
|
setMaster(hologram, spy);
|
|
setScale(hologram, getScale(spy));
|
|
setYaw(hologram, getYaw(spy));
|
|
setName(hologram, getName(spy));
|
|
setDescriptionStringId(hologram, new string_id("mob/creature_names", "decoy_bio"));
|
|
setInvulnerable(hologram, false);
|
|
factions.setFaction(hologram, factions.getFaction(spy));
|
|
setDecoyOrigin(hologram, spy);
|
|
|
|
|
|
custom_var[] var_list = getAllCustomVars(spy);
|
|
|
|
if(var_list != null && var_list.length > 0)
|
|
{
|
|
for(int i = 0; i < var_list.length; i++)
|
|
{
|
|
ranged_int_custom_var ricv = (ranged_int_custom_var)var_list[i];
|
|
|
|
string var = ricv.getVarName();
|
|
int value = ricv.getValue();
|
|
|
|
if(value != 0)
|
|
{
|
|
setRangedIntCustomVarValue(hologram, var, value);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
obj_id[] contents = getAllWornItems(spy, false);
|
|
obj_id headSlotItem = getObjectInSlot(spy, "hat");
|
|
|
|
utils.setObjVar(hologram, "hologram_performer", 1); // objvar checked in c code to allow transfer of biolinked items to holograms
|
|
if(contents != null && contents.length > 0)
|
|
{
|
|
for(int i = 0; i < contents.length; i++)
|
|
{
|
|
if(isIdValid(contents[i]))
|
|
{
|
|
if(getContainerType(contents[i]) == 0)
|
|
{
|
|
if (!getTemplateName(contents[i]).endsWith("player.iff"))
|
|
{
|
|
|
|
if(headSlotItem == contents[i] && isPlayerHelmetHidden(spy))
|
|
continue;
|
|
|
|
if(getGameObjectType(contents[i]) == GOT_misc_appearance_only_invisible)
|
|
continue;
|
|
|
|
custom_var[] object_var = getAllCustomVars(contents[i]);
|
|
|
|
obj_id newObject = createObject(getTemplateName(contents[i]), hologram, "");
|
|
|
|
if (object_var != null && isIdValid(newObject))
|
|
{
|
|
for (int q=0;q<object_var.length;q++)
|
|
{
|
|
ranged_int_custom_var ricv = (ranged_int_custom_var)object_var[q];
|
|
|
|
string var = ricv.getVarName();
|
|
int value = ricv.getValue();
|
|
|
|
if(value != 0)
|
|
{
|
|
setRangedIntCustomVarValue(newObject, var, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//add the spys backpack
|
|
obj_id backpack = getObjectInSlot(spy, "back");
|
|
if(isIdValid(backpack) && exists(backpack) && !isPlayerBackpackHidden(spy))
|
|
{
|
|
custom_var[] object_var = getAllCustomVars(backpack);
|
|
|
|
obj_id newObject = createObject(getTemplateName(backpack), hologram, "back");
|
|
|
|
if (object_var != null && isIdValid(newObject))
|
|
{
|
|
for (int q=0;q<object_var.length;q++)
|
|
{
|
|
ranged_int_custom_var ricv = (ranged_int_custom_var)object_var[q];
|
|
|
|
string var = ricv.getVarName();
|
|
int value = ricv.getValue();
|
|
|
|
if(value != 0)
|
|
{
|
|
setRangedIntCustomVarValue(newObject, var, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obj_id spyWeapon = getCurrentWeapon(spy);
|
|
weapon_data weaponData = weapons.getNewWeaponData(spyWeapon);
|
|
obj_id holoWeapon = null;
|
|
|
|
if (isDefaultWeapon(spyWeapon))
|
|
holoWeapon = getCurrentWeapon(hologram);
|
|
else
|
|
holoWeapon = createObject(getTemplateName(spyWeapon), hologram, "");
|
|
|
|
setObjVar(hologram, "intCombatDifficulty", getLevel(spy));
|
|
setLevel(hologram, getLevel(spy));
|
|
setObjVar(hologram, "difficultyClass", 0);
|
|
|
|
if (isIdValid(holoWeapon))
|
|
{
|
|
setWeaponAttackSpeed(holoWeapon, 1.0f);
|
|
setWeaponMaxDamage(holoWeapon, 2);
|
|
setWeaponMinDamage(holoWeapon, 1);
|
|
weapons.setWeaponData(holoWeapon);
|
|
|
|
utils.setScriptVar(holoWeapon, "isCreatureWeapon", 1);
|
|
|
|
}
|
|
|
|
setCurrentWeapon(hologram, holoWeapon);
|
|
|
|
setMaxAttrib(hologram, HEALTH, getMaxAttrib(spy, HEALTH) );
|
|
setAttrib( hologram, HEALTH, getMaxAttrib(spy, HEALTH) );
|
|
|
|
utils.setScriptVar(spy, combat.DAMAGE_REDIRECT, hologram);
|
|
|
|
attachScript(hologram, "ai.ai");
|
|
attachScript(hologram, "ai.creature_combat");
|
|
attachScript(hologram, "systems.combat.combat_actions");
|
|
attachScript(hologram, "ai.spy_decoy");
|
|
|
|
//set armor rating on decoy
|
|
int armorRating = utils.getIntScriptVar(spy, "armor.cache.generalProtection");
|
|
utils.setScriptVar(hologram, "armor.cache.generalProtection", armorRating);
|
|
|
|
int[] buffCrc = buff.getAllBuffs(spy);
|
|
|
|
if (buffCrc != null && buffCrc.length > 0)
|
|
{
|
|
for (int i=0;i<buffCrc.length;i++)
|
|
{
|
|
string curBuff = buff.getBuffNameFromCrc(buffCrc[i]);
|
|
if(curBuff != null && curBuff.startsWith("invis_"))
|
|
continue;
|
|
|
|
buff.applyBuff(hologram, buffCrc[i], 360.0f);
|
|
}
|
|
}
|
|
|
|
|
|
float dodgeChance = combat.getDefenderDodgeChance(spy);
|
|
float parryChance = combat.getDefenderParryChance(spy);
|
|
float blockChance = combat.getDefenderBlockChance(spy);
|
|
float glanceChance = combat.getDefenderGlancingBlowChance(spy);
|
|
float evadeChance = combat.getDefenderEvasionChance(spy);
|
|
|
|
addSkillModModifier(hologram, "expertise_dodge", "expertise_dodge", (int)dodgeChance, 20.0f, false, true);
|
|
addSkillModModifier(hologram, "expertise_parry", "expertise_parry", (int)parryChance, 20.0f, false, true);
|
|
addSkillModModifier(hologram, "expertise_block_chance", "expertise_block_chance", (int)blockChance, 20.0f, false, true);
|
|
addSkillModModifier(hologram, "expertise_glancing_blow_all", "expertise_glancing_blow_all", (int)glanceChance, 20.0f, false, true);
|
|
addSkillModModifier(hologram, "expertise_evasion_chance", "expertise_evasion_chance", (int)evadeChance, 20.0f, false, true);
|
|
|
|
|
|
if (hasCommand(spy, "sp_avoid_damage"))
|
|
{
|
|
messageTo(hologram, "queue_ad", null, 1.0f, false);
|
|
}
|
|
|
|
int diversionLevel = getEnhancedSkillStatisticModifierUncapped(spy, "expertise_improved_decoy");
|
|
|
|
if (diversionLevel > 0)
|
|
utils.setScriptVar(hologram, "flank", true);
|
|
else
|
|
utils.setScriptVar(hologram, "flank", false);
|
|
|
|
return hologram;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
boolean isDecoy(obj_id object)
|
|
{
|
|
return utils.hasScript(object, "ai.spy_decoy");
|
|
}
|