Files
dsrc/sku.0/sys.server/compiled/game/script/library/stealth.scriptlib
T
2013-09-10 23:17:15 -07:00

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