Files
dsrc/sku.0/sys.server/compiled/game/script/library/fs_dyn_village.scriptlib
T

1346 lines
40 KiB
Plaintext

include library.utils;
include java.lang.Math;
include library.trace;
include library.create;
include library.quests;
include library.player_structure;
include library.turret;
include library.fs_counterstrike;
// Data tables used
const string CITY_STAGES_DATATABLE = "datatables/fs_quests/fs_dyn_city.iff";
const string CITY_BAD_GUY_SPAWN_TABLE = "datatables/quest/force_sensitive/base_spawn.iff";
const string CITY_DESTRUCTION_COORDS = "datatables/quest/force_sensitive/village_destruction_coords.iff";
const string CITY_DEFENSES_DATATABLE = "datatables/fs_quests/fs_defenses.iff";
// Columns for data tables
const string DT_COL_PHASE = "phase";
const string DT_COL_AREA = "area";
const string DT_COL_TEMPLATE = "object_template";
const string DT_COL_OBJECT_X = "object_x";
const string DT_COL_OBJECT_Y = "object_y";
const string DT_COL_OBJECT_Z = "object_z";
const string DT_COL_IS_DELTA = "is_coord_delta";
const string DT_COL_HEADING = "heading";
const string DT_COL_GAME_CRITICAL = "game_critical";
const string DT_COL_SCRIPT = "script";
const string DT_COL_PHASE_NAME = "phase_name";
const string DT_COL_SPECIAL_OBJ_MOD = "special_obj_mod";
const string DT_COL_WAVE_PREFIX = "fs_village_";
const string DT_COL_WAVE_SMALL = "small";
const string DT_COL_WAVE_MEDIUM = "medium";
const string DT_COL_WAVE_LARGE = "large";
const string DT_COL_WAVE_EASY = "easy";
const string DT_COL_WAVE_NORMAL = "normal";
const string DT_COL_WAVE_HARD = "hard";
const string DT_COL_WAVE_ABYSS = "abyss";
// string constants
const string START_LOC = "START_LOC";
const string LOG_CHAN = "fs_quests";
const string OBJ_MOD_IS_SPAWNER = "spawner";
const string OBJ_MOD_IS_HEALING_STRUCT = "healing";
const string DYNVILLAGE_CLUSTER_NAME = "cluster.dynVillage";
const string CLUSTER_SET_FS = "cluster.dynVillage.fs_data";
const string CLUSTER_OBJID_KEY_MASTER = "cluster.dynVillage.masterObject";
const string CLUSTER_INT_KEY_CUR_PHASE = "cluster.dynVillage.curPhase";
const string CLUSTER_INT_KEY_PHASE_UID = "cluster.dynVillage.curPhaseUID";
// Numeric constants
const int DATAMODE_ADD_OBJID = 1;
const int DATAMODE_GET_OBJID = 2;
const int DATAMODE_ADD_INT = 3;
const int DATAMODE_GET_INT = 4;
const int VILLAGE_MIN_ENEMY_RADIUS = 150;
const int VILLAGE_MAX_ENEMY_RADIUS = 250;
const int VILLAGE_SMALL_WAVE = 1;
const int VILLAGE_MEDIUM_WAVE = 2;
const int VILLAGE_LARGE_WAVE = 3;
const int VILLAGE_UBER_WAVE = 4;
const int VILLAGE_MEDIUM_THRESHOLD = 10;
const int VILLAGE_LARGE_THRESHOLD = 20;
const int VILLAGE_UBER_THRESHOLD = 35;
const int VILLAGE_MIN_ENEMY_SPAWN_PULSE = 5000; // ~83 minutes
const int VILLAGE_MAX_ENEMY_SPAWN_PULSE = 7000; // ~116 minutes
const int VILLAGE_ENEMY_MAX_LIFE = 1200; // 20 minute max lifetime before self-destruct
const int DEFAULT_PHASE_DURATION = 1814400; // 3 weeks.
const int MAX_PHASE_NUMBER = 4; // 4 phases (1-4)
// template constants
const string TEMPLATE_NO_DRAW = "object/tangible/spawning/fs_village_npc_spawner.iff";
// script name constants
const string SCRIPT_VILLAGE_NPC_SPAWNER = "systems.fs_quest.fs_village_npc_spawner";
const string SCRIPT_DYN_VILLAGE_THING = "systems.fs_quest.fs_dynamic_village_object";
const string SCRIPT_FS_DATAHANDLER = "systems.fs_quest.fs_datahandler";
const string SCRIPT_FS_VILLAGE_MASTER = "systems.fs_quest.fs_village_master";
// objvar constants
const string OBJVAR_CRITICAL_OBJECT = "fs_quest.isGameCritical";
const string OBJVAR_IS_HEALING_STRUCT = "healing.canhealwound";
const string OBJVAR_CURRENT_PHASE = "fs_quest.phase.current";
const string OBJVAR_NEXT_PHASE = "fs_quest.phase.next";
const string OBJVAR_PHASE_VERSION = "fs_quest.phase.version";
const string OBJVAR_PHASE_START_TIME = "fs_quest.phase.startTime";
const string OBJVAR_MY_SPAWNER = "fs_quest.mySpawner";
const string OBJVAR_MY_NPC_TEMPLATE = "fs_quest.myNpcTemplate";
const string OBJVAR_MY_NPC_SCRIPTS = "fs_quest.myNpcScripts";
const string OBJVAR_MY_SPAWNED_NPC = "fs_quest.mySpawnedNpc";
const string OBJVAR_VILLAGE_SPAWNERS = "fs_quest.spawners";
const string OBJVAR_VILLAGE_OBJECTS = "fs_quest.objects";
const string OBJVAR_MY_MASTER_OBJECT = "fs_quest.myMaster";
const string OBJVAR_LOCK_VILLAGE = "fs_quest.village.locked";
const string OBJVAR_CAN_ATTACK = "turret.validTargetObjVar"; // this is the objvar on our turrets
const string OBJVAR_TURRET_TARGET = "fs_quest.turret.isTarget"; // gets attached to player so turrets can attack
const string OBJVAR_PLAYER_COUNT = "fs_quest.players.count"; // the number of players thought to be in the village at this time
const string OBJVAR_VILLAGE_DEFENSES = "crafted_village_defenses";
const string OBJVAR_VILLAGE_SHIELDS = "crafted_village_shields";
const string OBJVAR_TURRET_ACCURACY = "turret_accuracy";
const string OBJVAR_TURRET_SPEED = "turret_speed";
const string OBJVAR_TURRET_POWER = "turret_power";
// scriptvar constants
const string SCRIPT_VAR_DATA_REQUEST = "fs_quest.data_request";
const string SCRIPT_VAR_PARAMS_REQUEST = "fs_quest.data_request_params";
const string SCRIPT_VAR_HANDLER_REQ = "fs_quest.handler_request";
//
// returns an array of city destruction coords from the CITY_DESTRUCTION_COORDS data table using @id as the
// key for the coord set. the returned vector contains loc/yaw pairs
//
Vector getCityDestructionCoords(string id)
{
int num_items = dataTableGetNumRows(CITY_DESTRUCTION_COORDS);
Vector loc = new Vector();
int idx = dataTableSearchColumnForString(id, "coord_type", CITY_DESTRUCTION_COORDS);
dictionary line = null;
float yaw = 0.0f;
for (int i = idx + 1; i < num_items; i++)
{
dictionary row = dataTableGetRow(CITY_DESTRUCTION_COORDS, i);
// When the next spawner name is reached, we've gotten everything there is to get.
if (row.getString("coord_type").length() > 0)
{
break;
}
else
{
line = dataTableGetRow(CITY_DESTRUCTION_COORDS, i);
if(line == null || !line.containsKey("x") || !line.containsKey("y") || !line.containsKey("z"))
{
continue;
}
else
{
if(line.containsKey("yaw"))
{
yaw = line.getFloat("yaw");
}
loc.add(new location(line.getFloat("x"), line.getFloat("y"), line.getFloat("z")));
loc.add(new Float(yaw));
}
}
}
return loc;
}
//
// returns the village entry points as defined in CITY_DESTRUCTION_COORDS
//
location[] getVillageLocs(string id)
{
Vector locs = getCityDestructionCoords(id);
if(locs.size() < 1 || locs.size() % 2 != 0)
{
return new location[0];
}
location[] aLocs = new location[locs.size() / 2];
int count = 0;
for(int i = 0; i < locs.size(); i += 2)
{
aLocs[count] = (location)locs.get(i);
count++;
}
return aLocs;
}
//
// checks the phase duration against the phase start time and sets the next phase, if necessary
//
void checkPhaseExpiration(obj_id master)
{
if(!isIdValid(master) || !exists(master))
{
trace.log(LOG_CHAN, "fs_dyn_village::checkPhaseExpiration: -> Village master object (" + master + ") is not valid / doesn't exist.");
return;
}
if(!hasObjVar(master, OBJVAR_PHASE_START_TIME))
{
trace.log(LOG_CHAN, "fs_dyn_village::checkPhaseExpiration: -> Missing phase start time. Adding now, but will probably be incorrect.");
setObjVar(master, OBJVAR_PHASE_START_TIME, getGameTime());
return;
}
int forcedPhase = getStartupPhaseFromConfig();
if(forcedPhase > 0)
{
trace.log(LOG_CHAN, "fs_dyn_village::checkPhaseExpiration: -> Village has been forced into phase " + forcedPhase + " via server config.");
setNextPhaseAuth(master, forcedPhase);
return;
}
int curPhase = getCurrentPhaseAuth(master);
int duration = getPhaseDuration(curPhase);
int startTime = getIntObjVar(master, OBJVAR_PHASE_START_TIME);
if((startTime + duration) <= getGameTime())
{
trace.log(LOG_CHAN, "fs_dyn_village::checkPhaseExpiration: -> Phase " + curPhase + " has expired. Moving to next phase.");
int nextPhase = curPhase + 1;
if(nextPhase > MAX_PHASE_NUMBER)
{
nextPhase = 1;
}
setNextPhaseAuth(master, nextPhase);
}
return;
}
//
// returns the default phase duration or the phase duration from a server config, if present.
//
int getPhaseDuration(int phase)
{
if(phase < 1 || phase > MAX_PHASE_NUMBER)
{
trace.log(LOG_CHAN, "fs_dyn_village::getPhaseDuration: -> Invalid phase passed in (" + phase + "). Bailing.", null, trace.TL_ERROR_LOG);
return -1;
}
int duration = 0;
// see if there is a specific duration value for this phase
string cfgDuration = getConfigSetting("GameServer", "FsVillagePhaseDuration_Phase_" + phase);
if(cfgDuration != null && cfgDuration.length() > 0)
{
duration = utils.stringToInt(cfgDuration);
}
if(duration <= 0)
{
// see if we have a general setting for all phases
cfgDuration = getConfigSetting("GameServer", "FsVillagePhaseDuration");
if(cfgDuration != null && cfgDuration.length() > 0)
{
duration = utils.stringToInt(cfgDuration);
if(duration < 1)
{
duration = DEFAULT_PHASE_DURATION;
}
}
}
if(duration <= 0)
{
duration = DEFAULT_PHASE_DURATION;
}
//trace.log(LOG_CHAN, "fs_dyn_village::getPhaseDuration: -> Phase duration for phase " + phase + " is " +
// player_structure.assembleTimeRemaining(player_structure.convertSecondsTime(duration)));
return duration;
}
int getStartupPhaseFromConfig()
{
string cfgPhase = getConfigSetting("GameServer", "FsVillage_ForcePhase");
int phase = -1;
if(cfgPhase != null && cfgPhase.length() > 0)
{
phase = utils.stringToInt(cfgPhase);
}
if(phase < 1 || phase > MAX_PHASE_NUMBER)
{
phase = -1;
}
return phase;
}
//
// destroys the NPC that was associated with the @spawner
//
void destroySpawnersNpc(obj_id spawner)
{
if(!isIdValid(spawner) || !exists(spawner))
{
trace.log(LOG_CHAN, "fs_dyn_village::destroySpawnersNpc: -> Spawner " + spawner + " not valid / doesn't exist. Can't destroy npc.", spawner, trace.TL_WARNING);
return;
}
if(hasObjVar(spawner, OBJVAR_MY_SPAWNED_NPC))
{
obj_id npc = getObjIdObjVar(spawner, OBJVAR_MY_SPAWNED_NPC);
destroyObject(npc);
}
return;
}
//
// creates an instance of the NPC that is assigned the given @spawner. If the NPC already exists and @createIfExists is true, another copy
// of the NPC will be create. If an additional copy of the NPC is created and @destroyOldIfExists is true, the first copy will be destroyed
//
boolean spawnNpcAuth(obj_id spawner, boolean createIfExists, boolean destroyOldIfExists)
{
if(!isIdValid(spawner) || !exists(spawner))
{
trace.log(LOG_CHAN, "fs_dyn_village::spawnNpcAuth: -> Spawner " + spawner + " not valid / doesn't exist. Can't create npc.", spawner, trace.TL_ERROR_LOG);
return false;
}
if(!hasObjVar(spawner, OBJVAR_MY_NPC_TEMPLATE) || !hasObjVar(spawner, OBJVAR_MY_NPC_SCRIPTS))
{
trace.log(LOG_CHAN, "fs_dyn_village::spawnNpcAuth: -> Spawner " + spawner + " missing " + OBJVAR_MY_NPC_TEMPLATE + " or " + OBJVAR_MY_NPC_SCRIPTS + " objvar(s). Can't create npc.", spawner, trace.TL_ERROR_LOG);
return false;
}
// check if an instance of the NPC is already in the world
if(hasObjVar(spawner, OBJVAR_MY_SPAWNED_NPC))
{
obj_id curMob = getObjIdObjVar(spawner, OBJVAR_MY_SPAWNED_NPC);
if(isIdValid(curMob) && exists(curMob))
{
// instance already in the world
if(createIfExists)
{
if(destroyOldIfExists)
{
destroyObject(curMob);
}
}
else
{
// the npc is in the world
return true;
}
}
}
string template = getStringObjVar(spawner, OBJVAR_MY_NPC_TEMPLATE);
//trace.log(LOG_CHAN, "fs_dyn_village::spawnNpcAuth: -> Spawning creature " + template);
obj_id mob = create.object(template, getLocation(spawner));
if (!isIdValid(mob))
{
trace.log(LOG_CHAN, "fs_dyn_village::spawnNpcAuth: -> Unable to spawn " + template + " from spawner " + spawner, spawner, trace.TL_ERROR_LOG);
return false;
}
setObjVar(mob, OBJVAR_MY_SPAWNER, spawner);
setObjVar(spawner, OBJVAR_MY_SPAWNED_NPC, mob);
setObjVar(mob, OBJVAR_MY_MASTER_OBJECT, getObjIdObjVar(spawner, OBJVAR_MY_MASTER_OBJECT));
string scriptDat = getStringObjVar(spawner, OBJVAR_MY_NPC_SCRIPTS);
scriptDat = scriptDat.trim(); // eat any whitespaces that might be in there from faulty data entry
if(scriptDat != null && scriptDat.length() > 0)
{
string[] scripts = null;
if(scriptDat.indexOf("|") > -1)
{
scripts = split(scriptDat, '|');
}
else
{
scripts = new string[]{scriptDat};
}
for(int i = 0; i < scripts.length; i++)
{
//trace.log(LOG_CHAN, "fs_dyn_village::spawnNpcAuth: -> Attaching " + scripts[i] + " to " + mob);
if(scripts[i] != null && scripts[i].length() > 0 && !scripts[i].equals("none"))
{
attachScript(mob, scripts[i]);
}
}
}
setHomeLocation(mob);
return true;
}
//
// spawns the turret defenses for the village
//
void spawnTurrets(obj_id master)
{
const float MAX_ATTRIB_VALUE = 1000.0f;
boolean useAttributes = false;
float kinetic_resistance = 0;
float armor_integrity = 0;
float armor_effectiveness = 0;
float accuracy = 0;
float speed = 0;
float power = 0;
if ( hasObjVar(master, OBJVAR_VILLAGE_DEFENSES) )
{
obj_var_list defenses = getObjVarList(master, OBJVAR_VILLAGE_DEFENSES);
kinetic_resistance = defenses.getFloatObjVar("kinetic_resistance") / MAX_ATTRIB_VALUE;
if ( kinetic_resistance > 1.0f )
kinetic_resistance = 1.0f;
armor_integrity = defenses.getFloatObjVar("armor_integrity") / MAX_ATTRIB_VALUE;
if ( armor_integrity > 1.0f )
armor_integrity = 1.0f;
armor_effectiveness = defenses.getFloatObjVar("armor_effectiveness") / MAX_ATTRIB_VALUE;
if ( armor_effectiveness > 1.0f )
armor_effectiveness = 1.0f;
accuracy = defenses.getFloatObjVar("accuracy") / MAX_ATTRIB_VALUE;
if ( accuracy > 1.0f )
accuracy = 1.0f;
speed = defenses.getFloatObjVar("speed") / MAX_ATTRIB_VALUE;
if ( speed > 1.0f )
speed = 1.0f;
power = defenses.getFloatObjVar("power") / MAX_ATTRIB_VALUE;
if ( power > 1.0f )
power = 1.0f;
int[] attribNames = dataTableGetIntColumn(CITY_DEFENSES_DATATABLE, "attribute");
float[] attribMins = dataTableGetFloatColumn(CITY_DEFENSES_DATATABLE, "min");
float[] attribMaxs = dataTableGetFloatColumn(CITY_DEFENSES_DATATABLE, "max");
if ( attribNames != null && attribMins != null && attribMaxs != null && attribNames.length == attribMins.length &&
attribNames.length == attribMaxs.length)
{
int count = 0;
for ( int i = 0; i < attribNames.length; ++i )
{
switch ( attribNames[i] )
{
case ##"kinetic_resistance" :
kinetic_resistance = attribMins[i] + (attribMaxs[i] - attribMins[i]) * kinetic_resistance;
LOG("fs_village", "village defenses phase 3,4 kinetic resistance = " + kinetic_resistance);
++count;
break;
case ##"armor_integrity" :
armor_integrity = attribMins[i] + (attribMaxs[i] - attribMins[i]) * armor_integrity;
LOG("fs_village", "village defenses phase 3,4 armor integrity = " + armor_integrity);
++count;
break;
case ##"armor_effectiveness" :
armor_effectiveness = attribMins[i] + (attribMaxs[i] - attribMins[i]) * armor_effectiveness;
LOG("fs_village", "village defenses phase 3,4 armor effectiveness = " + armor_effectiveness);
++count;
break;
case ##"accuracy" :
accuracy = attribMins[i] + (attribMaxs[i] - attribMins[i]) * accuracy;
LOG("fs_village", "village defenses phase 3,4 accuracy = " + accuracy);
++count;
break;
case ##"speed" :
speed = attribMins[i] + (attribMaxs[i] - attribMins[i]) * speed;
LOG("fs_village", "village defenses phase 3,4 speed = " + speed);
++count;
break;
case ##"power" :
power = attribMins[i] + (attribMaxs[i] - attribMins[i]) * power;
LOG("fs_village", "village defenses phase 3,4 power = " + power);
++count;
break;
default:
CustomerServiceLog("fs_village", "WARNING: Defenses datatable has unknown attribute at index " + i);
break;
}
}
if ( count == attribNames.length )
useAttributes = true;
}
}
if (!useAttributes)
{
CustomerServiceLog("fs_village", "WARNING: Creating village turrets with default attributes");
}
Vector turretLocs = getCityDestructionCoords("village_turrets");
if (turretLocs.size() < 2 || turretLocs.size() % 2 != 0)
{
return;
}
for (int i = 0; i < turretLocs.size(); i += 2)
{
obj_id oturret = createObject("object/installation/turret/fs_village_turret.iff", (location)turretLocs.get(i));
if (isIdValid(oturret))
{
setObjVar(oturret, turret.OBJVAR_CAN_ATTACK, fs_dyn_village.OBJVAR_TURRET_TARGET);
float yaw = ((Float)turretLocs.get(i+1)).floatValue();
setYaw(oturret, yaw);
if ( useAttributes )
{
// clear out any previous armor
armor.removeAllArmorData(oturret);
armor.setAbsoluteArmorData(oturret, AL_advanced, AC_assault, (int)armor_effectiveness, (int)armor_integrity);
armor.setArmorSpecialProtectionPercent(oturret, 0, kinetic_resistance / 100.0f);
setObjVar(oturret, OBJVAR_TURRET_ACCURACY, accuracy);
setObjVar(oturret, OBJVAR_TURRET_SPEED, speed);
setObjVar(oturret, OBJVAR_TURRET_POWER, power);
}
}
}
return;
}
//
// creates spawn wave from data table. @struct_master is the center of the target
// @startRadius is the possible starting point of the spawner drop
// @endRadius is the end point of the spawner drops
// @spawnWaves is an array of all spawn lists that will be pulled out of base_spawn.tab every
// time a spawner is dropped down
//
void doEnemySpawn(obj_id struct_master, float minDistance, float maxDistance, string[] spawnWaves)
{
if(!isIdValid(struct_master) || !exists(struct_master))
{
trace.log(LOG_CHAN, "fs_dyn_village::doEnemySpawn: -> Error creating enemy spawn. Structure master (" + struct_master + ") was invalid or didn't exist.", struct_master, trace.TL_ERROR_LOG);
return;
}
if(spawnWaves == null || spawnWaves.length < 1)
{
trace.log(LOG_CHAN, "fs_dyn_village::doEnemySpawn: -> Error creating enemy spawn. SpawnWaves is not defined.", struct_master, trace.TL_ERROR_LOG);
return;
}
string wave = "";
float heading = 0.0f;
location spawnerLoc = null;
location masterLoc = getLocation(struct_master);
obj_id spawner = null;
resizeable location[] spawnLocations = new Vector();
spawnLocations.add(new location());
spawnLocations[0].area = "dathomir";
spawnLocations[0].x = 5240;
spawnLocations[0].z = -4335;
spawnLocations.add(new location());
spawnLocations[1].area = "dathomir";
spawnLocations[1].x = 5374;
spawnLocations[1].z = -3947;
spawnLocations.add(new location());
spawnLocations[2].area = "dathomir";
spawnLocations[2].x = 5415;
spawnLocations[2].z = -3959;
spawnLocations.add(new location());
spawnLocations[3].area = "dathomir";
spawnLocations[3].x = 5331;
spawnLocations[3].z = -4361;
spawnLocations.add(new location());
spawnLocations[4].area = "dathomir";
spawnLocations[4].x = 5187;
spawnLocations[4].z = 4369;
resizeable obj_id[] spawners = null;
if(hasObjVar(struct_master, "attack_spawners"))
{
spawners = getResizeableObjIdArrayObjVar(struct_master, "attack_spawners");
}
else
{
spawners = new Vector();
}
for(int i = 0; i < spawnWaves.length; i++)
{
if( i >= spawnLocations.length)
break;
wave = spawnWaves[i];
spawnerLoc = spawnLocations[i];
//spawnerLoc = utils.getRandomAwayLocation(masterLoc, minDistance, maxDistance);
LOG("fs_quest", "Sending enemy spawner to " + spawnerLoc.toString());
spawners.add(quests.createSpawner(wave, spawnerLoc, CITY_BAD_GUY_SPAWN_TABLE, struct_master));
}
if(spawners.length > 0)
{
setObjVar(struct_master, "attack_spawners", spawners);
}
else
{
removeObjVar(struct_master, "attack_spawners");
}
return;
}
//
// determines the necessary enemy wave spawn level
//
int getNecessarySpawnLevel(obj_id master)
{
int numPlayers = numPlayersInVillage(master);
int level = VILLAGE_SMALL_WAVE;
if(numPlayers > VILLAGE_MEDIUM_THRESHOLD)
{
level = VILLAGE_MEDIUM_WAVE;
}
if(numPlayers > VILLAGE_LARGE_THRESHOLD)
{
level = VILLAGE_LARGE_WAVE;
}
if(numPlayers > VILLAGE_UBER_THRESHOLD)
{
level = VILLAGE_UBER_WAVE;
}
return level;
}
//
// returns the number of players based on the obj_id in the master object
//
int numPlayersInVillage(obj_id struct_master)
{
int num = 0;
if(!hasObjVar(struct_master, OBJVAR_PLAYER_COUNT))
{
return num;
}
return getIntObjVar(struct_master, OBJVAR_PLAYER_COUNT);
}
//
// sends a message to the spawner to initiate a NPC spawn
//
void spawnNpcAsynch(obj_id spawner, boolean createIfExists, boolean destroyOldIfExists)
{
dictionary d = new dictionary();
d.put("createIfExists", createIfExists);
d.put("destroyOldIfExists", destroyOldIfExists);
messageTo(spawner, "msgSpawnNpc", d, 0.0f, false);
return;
}
//
// sends a message to the village npc @spawner that is responsible for an NPC death
//
void notifySpawnerOfDeath(obj_id deadNpc)
{
if(!hasObjVar(deadNpc, OBJVAR_MY_SPAWNER))
{
trace.log(LOG_CHAN, "fs_village_npc::notifyMasterOfDeath: -> Missing " + OBJVAR_MY_SPAWNER + " objvar. Can't notify spawner to create a new version of " + getTemplateName(deadNpc), deadNpc, trace.TL_ERROR_LOG);
return;
}
obj_id mySpawner = getObjIdObjVar(deadNpc, OBJVAR_MY_SPAWNER);
dictionary d = new dictionary();
d.put("victim", deadNpc);
messageTo(mySpawner, "msgNpcDestroyed", d, 0.0f, false);
return;
}
//
// Sets the next phase objvar on the village @village_master to @value
//
void setNextPhaseAuth(obj_id village_master, int value)
{
if(!isIdValid(village_master) || !exists(village_master))
{
trace.log(LOG_CHAN, "fs_dyn_village::setNextPhaseAuth: -> Village master object (" + village_master + ") is not valid / doesn't exist.");
return;
}
setObjVar(village_master, OBJVAR_NEXT_PHASE, value);
return;
}
//
// Extracts the phase number from the uid
//
int getPhaseFromVersionUid(int uid)
{
string suid = "" + uid;
int phase = utils.stringToInt(suid.substring(0,1));
if(phase < 1 || phase > MAX_PHASE_NUMBER)
{
return 0;
}
return phase;
}
//
// Extracts the version number from the uid
//
int getVersionFromVersionUid(int uid)
{
string suid = "" + uid;
int ver = utils.stringToInt(suid.substring(1));
return ver;
}
//
// gets the current version number of the village cycle
//
int getCurrentVersionAuth(obj_id village_master)
{
int curVersion = 0;
if(!isIdValid(village_master) || !exists(village_master))
{
return curVersion;
}
if(hasObjVar(village_master, OBJVAR_PHASE_VERSION))
{
curVersion = getIntObjVar(village_master, OBJVAR_PHASE_VERSION);
}
return curVersion;
}
//
// grabs the current phase from the village_master object
//
int getCurrentPhaseAuth(obj_id village_master)
{
if(!isIdValid(village_master) || !exists(village_master))
{
trace.log(LOG_CHAN, "fs_dyn_village::getCurrentPhase: -> Village master object (" + village_master + ") is not valid / doesn't exist.");
return -1;
}
if(!hasObjVar(village_master, OBJVAR_CURRENT_PHASE))
{
return -1;
}
return getIntObjVar(village_master, OBJVAR_CURRENT_PHASE);
}
//
// grabs the current uid from the village_master object
//
int getCurrentUidAuth(obj_id village_master)
{
if(!isIdValid(village_master) || !exists(village_master))
{
trace.log(LOG_CHAN, "fs_dyn_village::getCurrentUidAuth: -> Village master object (" + village_master + ") is not valid / doesn't exist.");
return -1;
}
int phase = getCurrentPhaseAuth(village_master);
int version = getCurrentVersionAuth(village_master);
if (phase == -1 || version == -1)
{
return -1;
}
string phaseString = Integer.toString(phase);
string versionString = Integer.toString(version);
string uidString = phaseString + versionString; // example: 423 = phase 4, version 23.
int uid = utils.stringToInt(uidString);
return uid;
}
//
// (re)starts a spawn pulse cycle and does an enemy spawn
//
void doEnemySpawnPulse(obj_id master, string msgHandler)
{
int level = getNecessarySpawnLevel(master);
string[] spawnWaves = null;
// construct the spawn waves definition
/*
SMALL // max population of 10
MEDIUM // max population of 20
LARGE // max population of 30
*/
String enemyLevelString = getConfigSetting("GameServer", "fsVillageEnemyDifficulty");
if(enemyLevelString != null && enemyLevelString.length() > 0)
{
level = utils.stringToInt(enemyLevelString);
}
switch(level)
{
case VILLAGE_SMALL_WAVE:
spawnWaves = new string[]
{"fs_village_easy_small",
"fs_village_easy_small",
"fs_village_easy_small"};
break;
case VILLAGE_MEDIUM_WAVE:
spawnWaves = new string[]
{"fs_village_easy_medium",
"fs_village_normal_medium",
"fs_village_normal_medium",
"fs_village_abyss_medium"};
break;
case VILLAGE_LARGE_WAVE:
spawnWaves = new string[]
{"fs_village_normal_large",
"fs_village_normal_large",
"fs_village_hard_large",
"fs_village_abyss_large"};
break;
case VILLAGE_UBER_WAVE:
spawnWaves = new string[]
{"fs_village_normal_large",
"fs_village_hard_large",
"fs_village_hard_large",
"fs_village_abyss_large",
"fs_village_abyss_large"};
break;
default:
spawnWaves = new string[]
{"fs_village_normal_large",
"fs_village_hard_large",
"fs_village_hard_large",
"fs_village_abyss_large",
"fs_village_abyss_large"};
break;
}
int minDistance = VILLAGE_MIN_ENEMY_RADIUS;
int maxDistance = VILLAGE_MAX_ENEMY_RADIUS;
if(spawnWaves != null)
{
doEnemySpawn(master, minDistance, maxDistance, spawnWaves);
spawnVillageVictims();
}
// restart the spawn pulse
String spawnPulseSecondsString = getConfigSetting("GameServer", "fsVillageEnemyRespawnPulseTime");
float spawnPulseSeconds = (float)rand(VILLAGE_MIN_ENEMY_SPAWN_PULSE, VILLAGE_MAX_ENEMY_SPAWN_PULSE);
if(spawnPulseSecondsString != null && spawnPulseSecondsString.length() > 0)
{
spawnPulseSeconds = utils.stringToFloat(spawnPulseSecondsString);
if(spawnPulseSeconds < 2500.0f)
spawnPulseSeconds = 2500.0f;
}
messageTo(master, msgHandler, null, spawnPulseSeconds, false);
return;
}
void spawnVillageVictims()
{
LOG("quests", "fs_dyn_village.spawnVillageVictims()");
location[] victimLocations = getVillageLocs("village_victims");
if(victimLocations != null)
{
int iter = 0;
for(iter = 0; iter < victimLocations.length; ++iter)
{
LOG("quests", "fs_dyn_village.spawnVillageVictims() - spawning at " + victimLocations[iter]);
quests.createSpawner("fs_villagers", victimLocations[iter], CITY_BAD_GUY_SPAWN_TABLE);
}
}
}
//
// any tasks that need to be performed after the phase initialization
//
void doPostPhaseInitTasks(obj_id master)
{
if(hasObjVar(master, OBJVAR_LOCK_VILLAGE))
{
trace.log(LOG_CHAN, "fs_dyn_village::doPostPhaseInitTasks: -> Master object locked. Not performing tasks.", master, trace.TL_ERROR_LOG);
return;
}
int curPhase = getCurrentPhaseAuth(master);
if (curPhase == 3 || curPhase == 4)
{
spawnTurrets(master);
if (curPhase == 4)
doEnemySpawnPulse(master, "msgEnemySpawnPulse");
}
return;
}
//
// destroys phase camps if they exist
//
void destroyPhase3Camps(obj_id villageMaster)
{
LOG("fs_quest", "DESTROY PHASE 3 CAMPS");
obj_id[] campIds = null;
if(utils.hasScriptVar(villageMaster, fs_counterstrike.OBJVAR_CREATED_CAMP_IDS))
{
campIds = utils.getObjIdArrayScriptVar(villageMaster, fs_counterstrike.OBJVAR_CREATED_CAMP_IDS);
}
if(campIds != null && campIds.length > 0)
{
for(int i = 0; i < campIds.length; i++)
{
if(campIds[i] == null)
{
continue;
}
messageTo(campIds[i], "msgSelfDestruct", null, 0.0f, false);
}
}
removeObjVar(villageMaster, "fs_cs");
utils.removeScriptVarTree(villageMaster, "fs_cs");
return;
}
//
// runs before the village is shifted from one phase to another - only runs one time at phase change
//
void doPrePhaseShiftCleanup(int oldPhase, int newPhase, obj_id villageMaster)
{
trace.log(LOG_CHAN, "****fs_dyn_village::doPrePhaseShiftCleanup; -> previous phase was " + oldPhase + ", new phase is " + newPhase);
// kill the phase 3 enemy camps
destroyPhase3Camps(villageMaster);
/*
if(newPhase != 3)
{
destroyPhase3Camps(villageMaster);
}
else
{
fs_counterstrike.initializeEnemyCamps(villageMaster);
}
*/
return;
}
//
// pushes the OBJVAR_NEXT_PHASE value to OBJVAR_CURRENT_PHASE and initiates the phase object creation for phase OBJVAR_CURRENT_PHASE
// -- also increments the OBJVAR_PHASE_VERSION value if we're flipping to a lower numbered phase
// -- @returns false if there was a problem pushing to the specified phase (no data foundin data table most likely)
//
boolean pushAndInitPhase(obj_id master)
{
trace.log(LOG_CHAN, "---!!! pushAndInitPhase !!!---");
// if it's time to change phases, we need to synch up the next/current phase objvars
if(hasObjVar(master, OBJVAR_NEXT_PHASE))
{
int curPhase = getIntObjVar(master, OBJVAR_CURRENT_PHASE);
int nextPhase = getIntObjVar(master, OBJVAR_NEXT_PHASE);
// make sure nextPhase has a place in the data table
int nextRow = getRowIndexForPhase(nextPhase);
if(nextRow > -1)
{
// check if we need to increment the phase version
if(nextPhase < curPhase)
{
int curVersion = 0;
if(hasObjVar(master, OBJVAR_PHASE_VERSION))
{
curVersion = getIntObjVar(master, OBJVAR_PHASE_VERSION);
}
curVersion++;
trace.log(LOG_CHAN, "fs_dyn_village::pushAndInitPhase: -> FS Village Version advanced to " + curVersion, master, trace.TL_CS_LOG);
setObjVar(master, OBJVAR_PHASE_VERSION, curVersion);
}
if(curPhase != nextPhase)
{
doPrePhaseShiftCleanup(curPhase, nextPhase, master);
setObjVar(master, OBJVAR_PHASE_START_TIME, getGameTime());
}
setObjVar(master, OBJVAR_CURRENT_PHASE, nextPhase);
}
else
{
trace.log(LOG_CHAN, "fs_dyn_village::pushAndInitPhase: -> Specified 'nextPhase' (" + nextPhase + ") does not have any build data in data table. Not pushing to nextPhase.",
master, trace.TL_CS_LOG);
//setObjVar(master, OBJVAR_NEXT_PHASE, curPhase);
}
}
int phaseAuth = getCurrentPhaseAuth(master);
int uidAuth = getCurrentUidAuth(master);
// save current phase in cluster data
registerIntegerInClusterWideData(phaseAuth, CLUSTER_INT_KEY_CUR_PHASE, "msgIntRegisteredPhase", master);
registerIntegerInClusterWideData(uidAuth, CLUSTER_INT_KEY_PHASE_UID, "msgIntRegisteredUID", master);
if(hasObjVar(master, OBJVAR_LOCK_VILLAGE))
{
trace.log(LOG_CHAN, "fs_dyn_village::pushAndInitPhase: -> Master object locked. Not creating phase objects.", master, trace.TL_ERROR_LOG);
return false;
}
int row = getRowIndexForPhase(phaseAuth);
if(row < 0)
{
trace.log(LOG_CHAN, "fs_dyn_village::pushAndInitPhase: -> Unable to find data entry point for phase " +
phaseAuth + ". Can't create phase objects.", master, trace.TL_ERROR_LOG);
return false;
}
if (!createPhaseObjects(master, row))
{
return false;
}
// any post-init tasks; this includes starting the enemy spawns for phase 4, for instance
doPostPhaseInitTasks(master);
return true;
}
//
// will create all objects listed in a data table starting at @tableRowIdxStart
// method stops creating items once it reaches either the next phase in the table OR the end of the table
// created items are registered with the @master_object
//
// @returns false if the creation failed as a whole - most likely no table data found for @tableRowIdxStart
boolean createPhaseObjects(obj_id master_object, int tableRowIdxStart)
{
// destroy any existing objects, although in a production environment there should never be any
// since this method should only ever be called as the cluster comes up
destroyDynamicVillage(master_object);
int totalRows = dataTableGetNumRows(CITY_STAGES_DATATABLE);
if(totalRows < tableRowIdxStart + 1)
{
trace.log(LOG_CHAN, "fs_dyn_city::createPhaseObjects: -> Gave starting row of " + tableRowIdxStart + ", but " + CITY_STAGES_DATATABLE + " only has " + totalRows + " rows.", null,
trace.TL_ERROR_LOG);
return false;
}
string marker = dataTableGetString(CITY_STAGES_DATATABLE, tableRowIdxStart, DT_COL_TEMPLATE);
if(!marker.equals(START_LOC))
{
trace.log(LOG_CHAN, "fs_dyn_city::createPhaseObjects: -> Row Index " + tableRowIdxStart + " is not the starting row for any phase.", null, trace.TL_ERROR_LOG);
return false;
}
Vector villageObjects = new Vector();
Vector villageSpawners = new Vector();
// Look for objects immediately after the start row
// continue looping through the rows, until we're done or we hit the next START_LOC in the DT_COL_TEMPLATE column
for(int i = tableRowIdxStart + 1; i < totalRows; i++)
{
marker = dataTableGetString(CITY_STAGES_DATATABLE, i, DT_COL_TEMPLATE);
// There are no more objects if the next phase is reached
if(marker.equals(START_LOC))
{
trace.log(LOG_CHAN, "fs_dyn_scriptlib::createPhaseObjects: -> Done creating items for this phase. Stopped at data table row " + i);
break;
}
// Grab the entire row
dictionary object_row = dataTableGetRow(CITY_STAGES_DATATABLE, i);
// Extract the data from the row
string obj_template = object_row.getString(DT_COL_TEMPLATE);
float x = object_row.getFloat(DT_COL_OBJECT_X);
float y = object_row.getFloat(DT_COL_OBJECT_Y);
float z = object_row.getFloat(DT_COL_OBJECT_Z);
int delta = object_row.getInt(DT_COL_IS_DELTA);
float heading = object_row.getFloat(DT_COL_HEADING);
int game_critical = object_row.getInt(DT_COL_GAME_CRITICAL);
string script = object_row.getString(DT_COL_SCRIPT);
string obj_mod = object_row.getString(DT_COL_SPECIAL_OBJ_MOD);
string planet = getCurrentSceneName();
location obj_loc = null;
obj_id createdObj = null;
boolean isSpawner = false;
// figure out the location
if(delta == 1)
{
// Use delta coordinates from the @master_object.
location city_loc = getLocation(master_object);
obj_loc = new location(city_loc.x - x, city_loc.y - y, city_loc.z - z, planet);
}
else
{
obj_loc = new location(x, y, z, planet);
}
// is it a spawner? if so, we actually create a known spawner template (TEMPLATE_NO_DRAW) and set it up with @obj_template info
// instead of creating @obj_template directly. If not a spawner, then just create the object outright.
if(obj_mod != null && toLower(obj_mod).equals(OBJ_MOD_IS_SPAWNER))
{
//trace.log(LOG_CHAN, "Creating spawner");
createdObj = createObject(TEMPLATE_NO_DRAW, obj_loc);
isSpawner = true;
}
else
{
//trace.log(LOG_CHAN, "Creating normal object");
createdObj = createObject(obj_template, obj_loc);
}
if(createdObj == null)
{
if(isSpawner)
{
trace.log(LOG_CHAN, "fs_dyn_city::createPhaseObjects: -> Null Object Pointer -> Unable to create NPC spawner " + TEMPLATE_NO_DRAW, master_object, trace.TL_ERROR_LOG);
}
else
{
trace.log(LOG_CHAN, "fs_dyn_city::createPhaseObjects: -> Null Object Pointer -> Unable to create object " + obj_template, master_object, trace.TL_ERROR_LOG);
}
continue;
}
else // object creation successful!
{
//trace.log(LOG_CHAN, " createdObj = " + createdObj);
setObjVar(createdObj, OBJVAR_MY_MASTER_OBJECT, master_object);
// if we created a spawner, tag it with the necessary stuff
if(isSpawner)
{
villageSpawners.add(createdObj);
// objvars MUST be set before the script is attached
setObjVar(createdObj, OBJVAR_MY_NPC_TEMPLATE, obj_template);
setObjVar(createdObj, OBJVAR_MY_NPC_SCRIPTS, script);
attachScript(createdObj, SCRIPT_VILLAGE_NPC_SPAWNER);
}
else
{
villageObjects.add(createdObj);
}
if(heading != 0.0f);
{
setYaw(createdObj, heading);
}
// Check for special object modifiers from the data table
if(obj_mod != null && obj_mod.length() > 0)
{
if(toLower(obj_mod).equals(OBJ_MOD_IS_HEALING_STRUCT))
{
// This is a healing station.
setObjVar(createdObj, OBJVAR_IS_HEALING_STRUCT, 1);
}
}
// Add objvars and scripts to game critical objects
if(game_critical == 1)
{
setObjVar(createdObj, OBJVAR_CRITICAL_OBJECT, 1);
}
// Every spawned object gets this script
attachScript(createdObj, SCRIPT_DYN_VILLAGE_THING);
}
}
utils.setResizeableBatchObjVar(master_object, OBJVAR_VILLAGE_OBJECTS, villageObjects);
utils.setResizeableBatchObjVar(master_object, OBJVAR_VILLAGE_SPAWNERS, villageSpawners);
return true;
}
//
// Inits cluster wide data with initial blank data so we can update it later
//
void initFsClusterData()
{
// objvar dataset
replaceClusterWideData(DYNVILLAGE_CLUSTER_NAME, CLUSTER_SET_FS, new dictionary(), false, -1);
return;
}
//
// registers an objvar @id along with a @key withthe cluster data.
//
boolean registerObjIdInClusterWideData(obj_id id, string key, string msgHandler, obj_id source)
{
return _updateClusterData(source, DATAMODE_ADD_OBJID, key + "|" + id.toString(), msgHandler, true);
}
//
// pulls out an objvar from the cluster data using @key that was set in registerObjIdInClusterWideData
//
boolean getRegisteredObjIdFromClusterWideData(string key, string msgHandler, obj_id source)
{
return _updateClusterData(source, DATAMODE_GET_OBJID, key, msgHandler, false);
}
//
// registers an integer @value along with a @key with the cluster data.
//
boolean registerIntegerInClusterWideData(int value, string key, string msgHandler, obj_id source)
{
return _updateClusterData(source, DATAMODE_ADD_INT, key + "|" + value, msgHandler, true);
}
//
// pulls out an objvar from the cluster data using @key that was set in registerObjIdInClusterWideData
//
boolean getRegisteredIntegerFromClusterWideData(string key, string msgHandler, obj_id source)
{
return _updateClusterData(source, DATAMODE_GET_INT, key, msgHandler, false);
}
//
// private function that routs cluster data updates / requests
//
boolean _updateClusterData(obj_id source, int data_mode, string params, string handler, boolean lock_data)
{
//trace.log(LOG_CHAN, "fs_dyn_village::_updateClusterData: --> data_mode="+data_mode +", handler="+handler + ", params=" + params);
if(!isIdValid(source))
{
trace.log(LOG_CHAN, "fs_dyn_village::_updateClusterData: -> source is invalid.", source, trace.TL_ERROR_LOG);
return false;
}
if(params == null)
{
trace.log(LOG_CHAN, "fs_dyn_village::_updateClusterData: -> params is null.", source, trace.TL_ERROR_LOG);
return false;
}
/*
if(handler == null || handler.length() < 1)
{
trace.log(LOG_CHAN, "fs_dyn_village::_updateClusterData: -> handler is null/empty.", source, trace.TL_ERROR_LOG);
return false;
}
*/
// Update the Clusterwide data.
//trace.log(LOG_CHAN, "Script " + SCRIPT_FS_DATAHANDLER + " attached to " + source + "? ->" + hasScript(source, SCRIPT_FS_DATAHANDLER));
if(!hasScript(source, SCRIPT_FS_DATAHANDLER))
{
//trace.log(LOG_CHAN, "Attaching script " + SCRIPT_FS_DATAHANDLER + " to " + source + "...");
attachScript(source, SCRIPT_FS_DATAHANDLER);
}
int request_id = getClusterWideData(DYNVILLAGE_CLUSTER_NAME, CLUSTER_SET_FS, lock_data, source);
utils.setScriptVar(source, SCRIPT_VAR_DATA_REQUEST + "." + request_id, data_mode);
utils.setScriptVar(source, SCRIPT_VAR_PARAMS_REQUEST + "." + request_id, params);
utils.setScriptVar(source, SCRIPT_VAR_HANDLER_REQ + "." + request_id, handler);
return true;
}
//
// searches column DT_COL_PHASE in @table for @phase and returns the row index.
// this is theoretically the first row for the given phase
//
int getRowIndexForPhase(int phase)
{
if(phase < 1)
{
return -1;
}
return dataTableSearchColumnForInt(phase, DT_COL_PHASE, CITY_STAGES_DATATABLE);
}
//
// attempts to destroy everything in the village that was dynamically created by the @villageMaster object
//
void destroyDynamicVillage(obj_id villageMaster)
{
destroyDynamicVillageObjects(villageMaster);
destroyDynamicVillageSpawners(villageMaster);
return;
}
//
// attempts to destroy any non-spawner objects created by the @villageMaster object
//
void destroyDynamicVillageObjects(obj_id villageMaster)
{
obj_id[] objs = new obj_id[0];
if(hasObjVar(villageMaster, OBJVAR_VILLAGE_OBJECTS))
{
objs = utils.getObjIdBatchObjVar(villageMaster, OBJVAR_VILLAGE_OBJECTS);
}
for(int i = 0; i < objs.length; i++)
{
if(isIdValid(objs[i]) && exists(objs[i]))
{
destroyObject(objs[i]);
}
}
removeObjVar(villageMaster, OBJVAR_VILLAGE_OBJECTS);
return;
}
//
// attempts to destroy npc spawner objects created by the @villageMaster object
//
void destroyDynamicVillageSpawners(obj_id villageMaster)
{
obj_id[] objs = new obj_id[0];
if(hasObjVar(villageMaster, OBJVAR_VILLAGE_SPAWNERS))
{
objs = utils.getObjIdBatchObjVar(villageMaster, OBJVAR_VILLAGE_SPAWNERS);
}
for(int i = 0; i < objs.length; i++)
{
if(isIdValid(objs[i]) && exists(objs[i]))
{
destroyObject(objs[i]);
}
}
removeObjVar(villageMaster, OBJVAR_VILLAGE_SPAWNERS);
return;
}