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 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 0) { for (int i=0;i 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"); }