diff --git a/sku.0/sys.server/compiled/game/datatables/spawning/patrol_spawner.tab b/sku.0/sys.server/compiled/game/datatables/spawning/patrol_spawner.tab new file mode 100755 index 000000000..dd0d7ee24 --- /dev/null +++ b/sku.0/sys.server/compiled/game/datatables/spawning/patrol_spawner.tab @@ -0,0 +1,24 @@ +faction leader squadClass template minSpawnChance maxSpawnChance +s i s s i i +imperial 0 armored stormtrooper 0 50 +imperial 1 armored stormtrooper_squad_leader 51 75 +imperial 0 armored stormtrooper_captain 76 85 +imperial 0 armored stormtrooper_major 86 90 +imperial 0 armored stormtrooper_medic 91 100 +imperial 0 plain imperial_private 0 50 +imperial 0 plain imperial_corporal 51 75 +imperial 0 plain imperial_sergeant_major 76 85 +imperial 0 plain imperial_staff_sergeant 86 90 +imperial 1 plain imperial_master_sergeant 91 100 +rebel 0 armored rebel_scout 0 50 +rebel 1 armored rebel_trooper 51 75 +rebel 0 armored rebel_commando 76 85 +rebel 0 armored rebel_major 86 90 +rebel 0 armored rebel_medic 91 100 +rebel 0 plain rebel_corporal 0 50 +rebel 0 plain rebel_lance_corporal 51 75 +rebel 0 plain rebel_sergeant 76 85 +rebel 0 plain rebel_staff_sergeant 86 90 +rebel 1 plain rebel_master_sergeant 91 100 +neutral 0 armored mercenary 0 50 +neutral 1 armored mercenary_aggro 51 100 diff --git a/sku.0/sys.server/compiled/game/object/tangible/poi/spawnegg/patrol_spawnegg.tpf b/sku.0/sys.server/compiled/game/object/tangible/poi/spawnegg/patrol_spawnegg.tpf new file mode 100755 index 000000000..dc6dd54b2 --- /dev/null +++ b/sku.0/sys.server/compiled/game/object/tangible/poi/spawnegg/patrol_spawnegg.tpf @@ -0,0 +1,12 @@ +@base object/tangible/poi/base/poi_base.iff + +@class tangible_object_template 0 + + +@class object_template 8 + + visibleFlags = [VF_gm] + + persistByDefault = false + + scripts =+ [ "theme_park.patrols.patrol_controller"] \ No newline at end of file diff --git a/sku.0/sys.server/compiled/game/script/systems/spawning/spawner_patrol.java b/sku.0/sys.server/compiled/game/script/systems/spawning/spawner_patrol.java index 7b52ec155..158a20dd8 100755 --- a/sku.0/sys.server/compiled/game/script/systems/spawning/spawner_patrol.java +++ b/sku.0/sys.server/compiled/game/script/systems/spawning/spawner_patrol.java @@ -63,18 +63,19 @@ public class spawner_patrol extends script.base_script if ((strSpawns == null) || (strSpawns.length == 0)) { LOG("spawning", "NO SPAWNS IN FILE " + strFileName); - setName(self, "Mangled spawner. strFileName is " + strFileName + " I couldnt find any spawns in that file."); + setName(self, "Mangled spawner: strFileName is " + strFileName + " I couldnt find any spawns in that file."); + return SCRIPT_CONTINUE; } if (fltSizes.length == 0) { LOG("spawning", "BAD NUMBER OF SIZES !@!@@ IN FILE " + strFileName); - setName(self, "BAD NUMBER OF SIZES !@!@@ IN FILE " + strFileName); + setName(self, "Mangled spawner: BAD NUMBER OF SIZES !@!@@ IN FILE " + strFileName); return SCRIPT_CONTINUE; } if (fltSizes.length != strSpawns.length) { LOG("spawning", "Missing either spawns or sizes in " + strFileName); - setName(self, "Missing either spawns or sizes in " + strFileName); + setName(self, "Mangled spawner: Missing either spawns or sizes in " + strFileName); return SCRIPT_CONTINUE; } } @@ -82,12 +83,14 @@ public class spawner_patrol extends script.base_script if (patrolPoints == null) { LOG("spawning", "ERROR: patrolArray script var not defined."); + setName(self, "Mangled spawner: no patrol points could be identified via patrolArray."); return SCRIPT_CONTINUE; } int maxSpawns = getIntObjVar(self, "intSpawnCount"); if (maxSpawns >= patrolPoints.length) { LOG("spawning", "ERROR: Attempting to spawn more creatures than patrol points."); + setName(self, "Mangled spawner: there are more creatures than patrol points."); return SCRIPT_CONTINUE; } Vector goodSpawns = new Vector(); @@ -113,7 +116,7 @@ public class spawner_patrol extends script.base_script if (goodSpawns.size() == 0) { LOG("spawning", "All entries in table " + strFileName + " are bad!!!"); - setName(self, "No valid spawners!"); + setName(self, "Mangled spawner: No valid spawners!"); return SCRIPT_CONTINUE; } } @@ -128,36 +131,43 @@ public class spawner_patrol extends script.base_script return SCRIPT_CONTINUE; } int intGoodLocationSpawner = getIntObjVar(self, "intGoodLocationSpawner"); - float fltRadius = getFloatObjVar(self, "fltRadius"); - String strSpawnType = getStringObjVar(self, "strSpawns"); - float fltSize = 8.0f; - String strSpawn = strSpawnType; - String strFileName = "datatables/spawning/ground_spawning/types/" + strSpawnType + ".iff"; + float fltSize; + String strSpawn = getStringObjVar(self, "strSpawns"); + if (strSpawn == null || strSpawn.length() < 1) + { + setName(self, "Mangled spawner: strSpawn is null or empty."); + return SCRIPT_CONTINUE; + } + String strFileName = "datatables/spawning/ground_spawning/types/" + strSpawn + ".iff"; if (dataTableOpen(strFileName)) { String[] strSpawns = dataTableGetStringColumnNoDefaults(strFileName, "strItem"); float[] fltSizes = dataTableGetFloatColumn(strFileName, "fltSize"); if ((strSpawns == null) || (strSpawns.length == 0)) { - setName(self, "Mangled spawner. strFileName is " + strFileName + " I couldn't find any spawns in that file."); + setName(self, "Mangled spawner: strFileName is " + strFileName + " I couldn't find any spawns in that file."); + return SCRIPT_CONTINUE; + } + else if (fltSizes.length == 0) + { + setName(self, "Mangled spawner: BAD NUMBER OF SIZES !@!@@ IN FILE " + strFileName); + return SCRIPT_CONTINUE; + } + else if (fltSizes.length != strSpawns.length) + { + setName(self, "Mangled spawner: Missing either spawns or sizes in " + strFileName); + return SCRIPT_CONTINUE; } int intRoll = rand(0, strSpawns.length - 1); - if (fltSizes.length == 0) - { - setName(self, "BAD NUMBER OF SIZES !@!@@ IN FILE " + strFileName); - } - if (fltSizes.length != strSpawns.length) - { - setName(self, "Missing either spawns or sizes in " + strFileName); - } fltSize = fltSizes[intRoll]; strSpawn = strSpawns[intRoll]; } - if (strSpawn == null || strSpawn.length() < 1) + else { - setName(self, "Mangled spawner. strSpawn is " + strSpawn + "."); + setName(self, "Mangled spawner: strSpawn is " + strSpawn + " and datatable could not be opened."); return SCRIPT_CONTINUE; } + float fltRadius = getFloatObjVar(self, "fltRadius"); location locTest = spawning.getRandomLocationInCircle(getLocation(self), fltRadius); fltSize = getClosestSize(fltSize); if (intGoodLocationSpawner > 0) diff --git a/sku.0/sys.server/compiled/game/script/theme_park/dantooine/imperial_outpost/masterspawner.java b/sku.0/sys.server/compiled/game/script/theme_park/dantooine/imperial_outpost/masterspawner.java index d64ab0ef0..4149f77e7 100755 --- a/sku.0/sys.server/compiled/game/script/theme_park/dantooine/imperial_outpost/masterspawner.java +++ b/sku.0/sys.server/compiled/game/script/theme_park/dantooine/imperial_outpost/masterspawner.java @@ -46,6 +46,22 @@ public class masterspawner extends script.base_script {-4173.8f, 3.0f, -2407.9f}, {-4160.9f, 3.0f, -2407.9f} }; + private float[][] smallSquadThreePoints = { + {-4287.9f, 3.0f, -2407.9f}, + {-4277.5f, 3.0f, -2407.9f}, + {-4272.7f, 3.0f, -2395.9f}, + {-4232.2f, 3.0f, -2395.9f}, + {-4204.6f, 3.0f, -2387.4f}, + {-4204.6f, 3.0f, -2349.8f} + }; + private float[][] smallSquadFourPoints = { + {-4160.9f, 3.0f, -2407.9f}, + {-4173.8f, 3.0f, -2407.9f}, + {-4176.7f, 3.0f, -2396.0f}, + {-4216.5f, 3.0f, -2396.0f}, + {-4253.7f, 3.0f, -2379.9f}, + {-4253.7f, 3.0f, -2349.8f} + }; private float[][] largeSquadOnePoints = { {-4272.7f, 3.0f, -2395.1f}, {-4272.7f, 3.0f, -2407.1f}, @@ -76,7 +92,6 @@ public class masterspawner extends script.base_script {-4272.7f, 3.0f, -2407.1f}, {-4272.7f, 3.0f, -2395.1f} }; - private location[] patrolPoints; // These are the different types of droids that can spawn. // The secondary value is the speed at which the droid travels @@ -89,32 +104,6 @@ public class masterspawner extends script.base_script {"r4","1.1"}, {"r5","1.2"} }; - // These are the different types of NPC's that can be spawned - // into a patrol. The second value is a lower bound for the - // random number, the third value is an upper bound for the - // random number. - private String[][] patrolNpcTypes = { - {"stormtrooper", "0", "50"}, - {"stormtrooper_squad_leader", "51", "75"}, - {"stormtrooper_captain", "76", "85"}, - {"stormtrooper_major", "86", "90"}, - {"stormtrooper_medic", "91", "100"} - }; - private String[][] officerPatrolTypes = { - {"stormtrooper", "0", "25"}, - {"imperial_private", "26", "50"}, - {"imperial_sergeant", "51", "60"}, - {"imperial_staff_sergeant", "61", "68"}, - {"imperial_staff_corporal", "69", "75"}, - {"imperial_first_lieutenant", "76", "81"}, - {"imperial_second_lieutenant", "82", "86"}, - {"imperial_major", "87", "90"}, - {"imperial_colonel", "91", "93"}, - {"imperial_brigadier_general", "94", "96"}, - {"imperial_major_general", "97", "98"}, - {"imperial_lieutenant_general", "99", "100"}, - {"imperial_general", "101", "101"} - }; public masterspawner() { @@ -134,19 +123,17 @@ public class masterspawner extends script.base_script { spawnCommoners(); spawnRecruiters(); - spawnGuardPatrol(self, "officers", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(officerPatrolPoints), "officer", false); - spawnGuardPatrol(self, "squadOne", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(smallSquadOnePoints), "trooper", true); - spawnGuardPatrol(self, "squadTwo", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(smallSquadTwoPoints), "trooper", true); - spawnGuardPatrol(self, "squadThree", new location(-4277.5f, 3.0f, -2407.9f), 3, getPatrolPoints(smallSquadOnePoints), "trooper", true); - spawnGuardPatrol(self, "squadFour", new location(-4176.7f, 3.0f, -2405.1f), 3, getPatrolPoints(smallSquadTwoPoints), "trooper", true); - spawnGuardPatrol(self, "largeSquadOne", new location(-4272.7f, 3.0f, -2395.1f), 5, getPatrolPoints(largeSquadOnePoints), "trooper", false); - spawnGuardPatrol(self, "largeSquadTwo", new location(-4176.7f, 3.0f, -2395.1f), 5, getPatrolPoints(largeSquadTwoPoints), "trooper", false); + spawnGuardPatrol(self, "officers", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(officerPatrolPoints), "plain", false); + spawnGuardPatrol(self, "squadOne", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(smallSquadOnePoints), "armored", true); + spawnGuardPatrol(self, "squadTwo", new location(-4272.7f, 3.0f, -2397.1f), 3, getPatrolPoints(smallSquadTwoPoints), "armored", true); + spawnGuardPatrol(self, "squadThree", new location(-4277.5f, 3.0f, -2407.9f), 3, getPatrolPoints(smallSquadThreePoints), "armored", true); + spawnGuardPatrol(self, "squadFour", new location(-4176.7f, 3.0f, -2405.1f), 3, getPatrolPoints(smallSquadFourPoints), "armored", true); + spawnGuardPatrol(self, "largeSquadOne", new location(-4272.7f, 3.0f, -2395.1f), 5, getPatrolPoints(largeSquadOnePoints), "armored", false); + spawnGuardPatrol(self, "largeSquadTwo", new location(-4176.7f, 3.0f, -2395.1f), 5, getPatrolPoints(largeSquadTwoPoints), "armored", false); spawnSG567(); spawnLX466(); //spawnSocialGroups(self); - patrolPoints = getPatrolPoints(droidPatrolCoords); - messageTo(self, "spawnDroidPatrol", null, 10, false); } private void spawnSG567() throws InterruptedException{ @@ -200,6 +187,7 @@ public class masterspawner extends script.base_script // Sets the number of droids that will spawn on the outpost grounds. int maxDroids = 4; int currentDroids = getIntObjVar(self, "current.droids"); + location[] points = getPatrolPoints(droidPatrolCoords); if(currentDroids < maxDroids){ int spawnPoint = 1; @@ -207,12 +195,12 @@ public class masterspawner extends script.base_script String [] droid = droidTypes[rand(0, droidTypes.length - 1)]; // spawn the droid and assign the obj_id to the droidSpawn var. - obj_id droidSpawn = spawn(droid[0], patrolPoints[spawnPoint].x, patrolPoints[spawnPoint].y, patrolPoints[spawnPoint].z, 0, null, "", true); + obj_id droidSpawn = spawn(droid[0], points[spawnPoint].x, points[spawnPoint].y, points[spawnPoint].z, 0, null, "", true); // handle AI features - make the droid movable, set their speed and set their path. setCreatureStatic(droidSpawn, false); setMovementPercent(droidSpawn, Float.parseFloat(droid[1])); - ai_lib.setPatrolPath(droidSpawn, patrolPoints); + ai_lib.setPatrolPath(droidSpawn, points); setObjVar(self, "current.droids", currentDroids + 1); } @@ -222,66 +210,23 @@ public class masterspawner extends script.base_script private void spawnGuardPatrol(obj_id self, String patrolName, location spawnLocation, int totalMembers, location [] patrolPoints, String npcType, boolean flip) throws InterruptedException{ String pName = "patrols." + patrolName; setObjVar(self, pName + ".startLocation", spawnLocation); - setObjVar(self, pName + ".totalMembers", totalMembers); - setObjVar(self, pName + ".points", patrolPoints); - setObjVar(self, pName + ".flipPaths", flip); - setObjVar(self, pName + ".types", npcType); - spawnGuardPatrolMember(self, pName); + //spawnGuardPatrolMember(self, pName); + obj_id patrolSpawnEgg = createObject("object/tangible/poi/spawnegg/patrol_spawnegg.iff", spawnLocation); - dictionary params = new dictionary(); - params.put("name", pName); - params.put("size", totalMembers); - params.put("type", npcType); - messageTo(self, "spawnMoreGuards", params, 10, false); + setObjVar(patrolSpawnEgg, "name", patrolName); + setObjVar(patrolSpawnEgg, "size", totalMembers); + setObjVar(patrolSpawnEgg, "patrolPoints", patrolPoints); + setObjVar(patrolSpawnEgg, "flipPaths", flip); + setObjVar(patrolSpawnEgg, "faction", "imperial"); + setObjVar(patrolSpawnEgg, "invulnerable", false); + setObjVar(patrolSpawnEgg, "respawnTime", 5.0f); + setObjVar(patrolSpawnEgg, "patrolLevel", 66); + setObjVar(patrolSpawnEgg, "squadClass", npcType); + + messageTo(patrolSpawnEgg, "beginSpawn", null, 10, false); } - private void spawnGuardPatrolMember(obj_id self, String patrolName) throws InterruptedException{ - location start = getLocationObjVar(self, patrolName + ".startLocation"); - obj_id guardSpawn = spawn(npcToSpawn(getStringObjVar(self, patrolName + ".types")), start.x, start.y, start.z, 0f, null, null, false); - - if(getBooleanObjVar(self, patrolName + ".flipPaths")) - ai_lib.setPatrolFlipPath(guardSpawn, getLocationArrayObjVar(self, patrolName + ".points")); - else - ai_lib.setPatrolPath(guardSpawn, getLocationArrayObjVar(self, patrolName + ".points")); - - int currentPop = getIntObjVar(self, patrolName + ".current.guards") + 1; - - if (currentPop == 1) - { - setObjVar(self, patrolName + ".leader", guardSpawn); - } - else - { - obj_id leader = getObjIdObjVar(self, patrolName + ".leader"); - ai_lib.followInFormation(guardSpawn, leader, ai_lib.FORMATION_COLUMN, currentPop - 1); - setMovementPercent(guardSpawn, 1.2f); - } - - setCreatureStatic(guardSpawn, false); - setObjVar(self, patrolName + ".current.guards", currentPop); - } - public int spawnMoreGuards(obj_id self, dictionary params) throws InterruptedException{ - String patrolName = params.getString("name"); - int maxGuardsToSpawn = params.getInt("size"); - String currentPopulation = patrolName + ".current.guards"; - - if (!hasObjVar(self, currentPopulation)) - { - setObjVar(self, currentPopulation, 0); - spawnGuardPatrolMember(self, patrolName); - } - else - { - if (getIntObjVar(self, currentPopulation) < maxGuardsToSpawn) - { - spawnGuardPatrolMember(self, patrolName); - } - } - - messageTo(self, "spawnMoreGuards", params, 10, false); - return SCRIPT_CONTINUE; - } - private location[] getPatrolPoints(float[][] patrolCoords){ + public location[] getPatrolPoints(float[][] patrolCoords){ location[] patrolPoints = new location[patrolCoords.length]; // take the patrol coordinates and turn them into location objects such that we can // pass the location objects to the AI and have it traverse those locations. @@ -291,22 +236,6 @@ public class masterspawner extends script.base_script return patrolPoints; } - public String npcToSpawn(String npcType){ - // get NPC type: - int roll = rand(0,100); - String [][] npcTypes = patrolNpcTypes; - - if(npcType != null && npcType.equals("officer")){ - npcTypes = officerPatrolTypes; - } - - for(String[] npcConfig : npcTypes){ - if(roll >= Integer.parseInt(npcConfig[1]) && roll <= Integer.parseInt(npcConfig[2])) - return npcConfig[0]; - } - - return patrolNpcTypes[0][0]; - } public obj_id spawn(String obj, float x, float y, float z, float yaw, obj_id cell, String mood, boolean invuln) throws InterruptedException{ obj_id spawnedObject; @@ -316,6 +245,9 @@ public class masterspawner extends script.base_script else{ spawnedObject = create.object(obj, new location(x, y, z, "dantooine", cell)); } + + setObjVar(spawnedObject, "spawner", getSelf()); + attachScript(spawnedObject, "theme_park.patrol_member"); setCreatureStatic(spawnedObject, true); setInvulnerable(spawnedObject, invuln); setLevel(spawnedObject, rand(65,69)); diff --git a/sku.0/sys.server/compiled/game/script/theme_park/patrol_member.java b/sku.0/sys.server/compiled/game/script/theme_park/patrol_member.java new file mode 100755 index 000000000..f5f28cafe --- /dev/null +++ b/sku.0/sys.server/compiled/game/script/theme_park/patrol_member.java @@ -0,0 +1,33 @@ +package script.theme_park; + +import script.dictionary; +import script.library.ai_lib; +import script.location; +import script.obj_id; + +public class patrol_member extends script.base_script +{ + public patrol_member(){} + public void startPatrol(obj_id self, dictionary params) throws InterruptedException{ + if(!isValidId(self) || !exists(self) || !getBooleanObjVar(self, "isLeader")) return; + location[] patrolPts = params.getLocationArray("patrolPoints"); + + if(params.getBoolean("flipPaths")){ + ai_lib.setPatrolFlipPath(self, patrolPts); + } + else { + ai_lib.setPatrolPath(self, patrolPts); + } + } + public int OnDestroy(obj_id self) throws InterruptedException + { + obj_id spawner = getObjIdObjVar(self, "spawner"); + + dictionary dict = new dictionary(); + dict.put("isLeader", getBooleanObjVar(self, "isLeader")); + dict.put("self", self); + + messageTo(spawner, "patrolMemberDied", dict, 20, true); + return SCRIPT_CONTINUE; + } +} diff --git a/sku.0/sys.server/compiled/game/script/theme_park/patrols/patrol_controller.java b/sku.0/sys.server/compiled/game/script/theme_park/patrols/patrol_controller.java new file mode 100755 index 000000000..5b2089fdc --- /dev/null +++ b/sku.0/sys.server/compiled/game/script/theme_park/patrols/patrol_controller.java @@ -0,0 +1,225 @@ +package script.theme_park.patrols; + +import script.dictionary; +import script.library.ai_lib; +import script.library.create; +import script.location; +import script.obj_id; + +public class patrol_controller extends script.base_script { + private obj_id[] formationSlots; + + public String faction = "neutral"; + public String strFileName = "datatables/spawning/patrol_spawner.iff"; + + // type is the type of the squad. + // "plain" type indicates unarmored npcs () of the given faction (irrelevant for neutral) + // "armored" type indicates armored npcs (troopers) only of the given faction. + // "mixed" type indicates a mixture of officers and troopers + + private String squadClass = "armored"; + + private int patrolLevel = 25; + // patrolLevelTolerance is the span above the patrol level that members can be. + // for example, if the patrolLevel is set to 66 and the patrolLevelTolerance is 2, patrol + // members can be 2 levels above level 66. Therefore, patrol members + // will be in the range of 66 and 68. + private int patrolLevelTolerance = 3; + + public patrol_controller(){} + + public int OnAttach(obj_id self) throws InterruptedException{ + setObjVar(self, "currentSize", 0); + return SCRIPT_CONTINUE; + } + public int OnInitialize(obj_id self) throws InterruptedException { + setObjVar(self, "currentSize", 0); + return SCRIPT_CONTINUE; + } + public int beginSpawn(obj_id self, dictionary params) throws InterruptedException{ + messageTo(self, "spawnGuardPatrol", null, 2, false); + return SCRIPT_CONTINUE; + } + public int spawnGuardPatrol(obj_id self, dictionary params) throws InterruptedException{ + String patrolName = getStringObjVar(self, "name"); + int maxSize = getIntObjVar(self, "size"); + int currentSize = getIntObjVar(self, "currentSize"); + location spawnStart = getLocationArrayObjVar(self, "patrolPoints")[0]; + if(hasObjVar(self, "patrolLevel")) patrolLevel = getIntObjVar(self, "patrolLevel"); + if(hasObjVar(self, "patrolLevelTolerance")) patrolLevelTolerance = getIntObjVar(self, "patrolLevelTolerance"); + if(hasObjVar(self, "faction")) faction = getStringObjVar(self, "faction"); + formationSlots = new obj_id[getIntObjVar(self, "size")]; + setObjVar(self, "formationSlots", formationSlots); + float respawnTime = getFloatObjVar(self, "respawnTime"); + squadClass = getStringObjVar(self, "squadClass"); + + setName(self, patrolName); + + formationSlots = getObjIdArrayObjVar(getSelf(), "formationSlots"); + for (int i = currentSize; i < maxSize; i++) { + spawnPatrolMember(self, getOpenFormationPosition(maxSize), i, spawnStart); + } + setObjVar(self, "formationSlots", formationSlots); + messageTo(self, "spawnGuardPatrol", null, respawnTime, false); + return SCRIPT_CONTINUE; + } + private int getOpenFormationPosition(int maxSize){ + for(int i = 0; i < formationSlots.length; i++){ + if(formationSlots[i] == null || formationSlots[i] == obj_id.NULL_ID){ + return i; + } + } + return maxSize - 1; + } + private void spawnPatrolMember(obj_id self, int position, int currentSize, location spawnStart) throws InterruptedException{ + obj_id patrolMember; + String memberTemplate = npcToSpawn(self); + if(memberTemplate == null){ + setName(self, "BROKEN SPAWNER: Can't get squad member template!"); + return; + } + if(currentSize == 0 || needsLeader()){ + String npc = getLeaderTemplate(self); + if(npc == null){ + setName(self, "BROKEN SPAWNER: Can't get squad leader template!"); + return; + } + patrolMember = spawn(npc, spawnStart.x, spawnStart.y, spawnStart.z, 0f, null, null); + setObjVar(patrolMember, "isLeader", true); + + dictionary params = new dictionary(); + params.put("patrolPoints", getLocationArrayObjVar(self, "patrolPoints")); + params.put("flipPaths", getBooleanObjVar(self, "flipPaths")); + + messageTo(patrolMember, "startPatrol", params, 60, false); + } + else{ + // spawn member + patrolMember = spawn(memberTemplate, spawnStart.x, spawnStart.y, spawnStart.z, 0f, null, null); + ai_lib.followInFormation(patrolMember, formationSlots[0], ai_lib.FORMATION_COLUMN, position); + setMovementPercent(patrolMember, 1.2f); + setObjVar(patrolMember, "isLeader", false); + } + + formationSlots[position] = patrolMember; + + setLevel(patrolMember, rand(patrolLevel, patrolLevel + patrolLevelTolerance)); + + setObjVar(self, "currentSize", currentSize + 1); + } + private boolean needsLeader() throws InterruptedException { + obj_id leader = formationSlots[0]; + + return leader == null || leader == obj_id.NULL_ID || isDead(leader) || isIncapacitated(leader) || !isIdValid(leader) || !exists(leader); + } + public void patrolMemberDied(obj_id self, dictionary params) throws InterruptedException{ + // drop the size of the patrol by one. + int currentSize = getIntObjVar(self, "currentSize"); + if(currentSize <= 0) currentSize = 1; + setObjVar(self, "currentSize", currentSize - 1); + obj_id deadMember = params.getObjId("self"); + formationSlots = getObjIdArrayObjVar(self, "formationSlots"); + + // check if this patrol member was a leader or not. Spawn new leader if so. + if(params.getBoolean("isLeader")){ + setObjVar(self, "leader", obj_id.NULL_ID); + spawnPatrolMember(self, 0, 0, getLocationArrayObjVar(self, "patrolPoints")[0]); + tellSquadAboutNewLeader(); + } + else{ + for(int i=0; i < formationSlots.length; i++){ + if(formationSlots[i] == deadMember){ + formationSlots[i] = obj_id.NULL_ID; + break; + } + } + setObjVar(self, "formationSlots", formationSlots); + } + } + private void tellSquadAboutNewLeader() throws InterruptedException{ + for(int i = 1; i < formationSlots.length; i++){ + ai_lib.followInFormation(formationSlots[i], formationSlots[0], ai_lib.FORMATION_COLUMN, i); + setMovementPercent(formationSlots[i], 1.2f); + } + } + private String getLeaderTemplate(obj_id self){ + if(!dataTableOpen(strFileName)){ + setName(self, "BROKEN SPAWNER: Can't open datatable to retrieve Squad Leader Template!"); + return null; + } + int rowCount = dataTableGetNumRows(strFileName); + squadClass = getStringObjVar(self, "squadClass"); + String tempSquadClass = squadClass; + + dictionary row; + for(int i=0; i < rowCount; i++){ + row = dataTableGetRow(strFileName, i); + if(row.getInt("leader") != 1 || !row.getString("faction").equals(faction)){ + continue; + } + if(squadClass.equals("mixed") && rand(1,2) == 2) tempSquadClass = "plain"; + + if(row.getString("faction").equals(faction) && row.getInt("leader") == 1 && row.getString("squadClass").equals(tempSquadClass)){ + return row.getString("template"); + } + } + return null; + } + public String npcToSpawn(obj_id self){ + // get NPC type: + int roll = rand(1,100); + + if(!dataTableOpen(strFileName)){ + setName(self, "BROKEN SPAWNER: Can't open datatable!"); + return null; + } + int rowCount = dataTableGetNumRows(strFileName); + if (rowCount <= 0) + { + setName(self, "BROKEN SPAWNER: Can't get datatable rows!"); + return null; + } + dictionary row; + squadClass = getStringObjVar(self, "squadClass"); + String tempSquadClass = squadClass; + + for(int i=0; i < rowCount; i++){ + row = dataTableGetRow(strFileName, i); + if(!row.getString("faction").equals(faction)){ + continue; + } + if(squadClass.equals("mixed") && rand(1,2) == 2) tempSquadClass = "plain"; + + if(row.getString("faction").equals(faction) && row.getString("squadClass").equals(tempSquadClass) && roll >= row.getInt("minSpawnChance") && roll <= row.getInt("maxSpawnChance")){ + return row.getString("template"); + } + } + return null; + } + public obj_id spawn(String obj, float x, float y, float z, float yaw, obj_id cell, String mood) throws InterruptedException{ + obj_id spawnedObject; + String area = getLocation(getSelf()).area; + + if(obj.contains(".iff")){ + spawnedObject = createObject(obj, new location(x, y, z, area, cell)); + } + else{ + spawnedObject = create.object(obj, new location(x, y, z, area, cell)); + } + obj_id self = getSelf(); + setObjVar(spawnedObject, "spawner", self); + attachScript(spawnedObject, "theme_park.patrol_member"); + setCreatureStatic(spawnedObject, false); + setInvulnerable(spawnedObject, getBooleanObjVar(self, "invulnerable")); + setLevel(spawnedObject, rand(65,69)); + + if(yaw > 0){ + setYaw(spawnedObject, yaw); + } + + ai_lib.setDefaultCalmMood(spawnedObject, mood); + + return spawnedObject; + } + +}