Files
dsrc/sku.0/sys.server/compiled/game/script/systems/turret/turret_ai.script
T

780 lines
22 KiB
Plaintext

/***** INCLUDES ********************************************************/
include library.ai_lib;
include library.attrib;
include library.battlefield;
include library.combat;
include library.factions;
include library.hq;
include library.list;
include library.pclib;
include library.pet_lib;
include library.player_structure;
include library.turret;
include library.utils;
include library.xp;
inherits systems.combat.combat_base_old;
/***** TRIGGERS ********************************************************/
trigger OnAttach()
{
turret.activateTurret(self);
ai_lib.setAttackable(self, true);
return SCRIPT_CONTINUE;
}
trigger OnInitialize()
{
/* Advanced turrets still get this script attached to them, but it is
detached on initialization. However, this trigger still goes off
and we don't want them to be blowed up.
*/
string template = getTemplateName(self);
if(template.indexOf("adv") > -1)
return SCRIPT_CONTINUE;
explodeTurret(self, self);
/* LEGACY TURRET, USE ADVANCED_TURRET_AI FROM NOW ON.
string factionName = factions.getFaction( self );
if ( factionName != null )
factions.setFaction( self, factionName );
if ( getHitpoints(self) < 1 )
setHitpoints(self, 1);
utils.setScriptVar(self, "ai.level", 100);
int tFac = pvpGetAlignedFaction(self);
setTurretAttributes(self, tFac);
setWantSawAttackTriggers( self, true );
if( hasObjVar(self, "objWeapon") )
{
obj_id wpn = getObjIdObjVar(self, "objWeapon");
if ( !isIdValid(wpn) )
{
removeObjVar(self, "objWeapon");
turret.createWeapon(self);
}
else
{
if ( !utils.isNestedWithin(wpn, self) )
{
removeObjVar(self, "objWeapon");
turret.createWeapon(self);
}
}
}
else
{
turret.createWeapon(self);
}
setTurretAttributes(self, tFac);
*/
return SCRIPT_CONTINUE;
}
trigger OnPvpFactionChanged(int oldFaction, int newFaction)
{
//debugSpeakMsg(self, "faction updated: "+oldFaction+" -> "+newFaction);
setTurretAttributes(self, newFaction);
return SCRIPT_CONTINUE;
}
void setTurretAttributes(obj_id self, int turretFaction)
{
if ( hasTriggerVolume(self, turret.ALERT_VOLUME_NAME) )
removeTriggerVolume(turret.ALERT_VOLUME_NAME);
if ( hasTriggerVolume(self, turret.VOL_TOO_CLOSE) )
removeTriggerVolume(turret.VOL_TOO_CLOSE);
//debugSpeakMsg(self, "clearing interested attribs");
clearAttributeInterested(self, attrib.ALL);
clearAttributeInterested(self, attrib.IMPERIAL);
clearAttributeInterested(self, attrib.REBEL);
clearAttributeInterested(self, attrib.HUTT);
createTriggerVolume( turret.ALERT_VOLUME_NAME, turret.ALERT_VOLUME_SIZE, true );
//createTriggerVolume( turret.VOL_TOO_CLOSE, turret.RANGE_TOO_CLOSE, true );
//debugSpeakMsg(self, "setting interest attribs for faction: "+newFaction);
switch ( turretFaction )
{
case ##"rebel":
setAttributeInterested( self, attrib.ALL );
setAttributeInterested( self, attrib.IMPERIAL );
setAttributeInterested( self, attrib.HUTT );
pvpSetAlignedFaction(self, ##"Rebel");
pvpMakeCovert(self);
break;
case ##"imperial":
setAttributeInterested( self, attrib.ALL );
setAttributeInterested( self, attrib.REBEL );
setAttributeInterested( self, attrib.HUTT );
pvpSetAlignedFaction(self, ##"Imperial");
pvpMakeCovert(self);
break;
default:
setAttributeInterested( self, attrib.ALL );
break;
}
}
trigger OnRemovingFromWorld()
{
if ( hasObjVar( self, "lair.deadLair" ))
destroyObject( self );
return SCRIPT_CONTINUE;
}
trigger OnAboutToOpenContainer(obj_id who)
{
if ( isIdValid(who) && !isGod(who) )
return SCRIPT_OVERRIDE;
return SCRIPT_CONTINUE;
}
trigger OnAboutToLoseItem(obj_id destContainer, obj_id transferer, obj_id item)
{
if ( isIdValid(transferer) && !isGod(transferer) )
return SCRIPT_OVERRIDE;
return SCRIPT_CONTINUE;
}
trigger OnAboutToReceiveItem(obj_id srcContainer, obj_id transferer, obj_id item)
{
if ( isIdValid(transferer) && !isGod(transferer) )
return SCRIPT_OVERRIDE;
return SCRIPT_CONTINUE;
}
trigger OnTriggerVolumeEntered( string volumeName, obj_id who )
{
if (!isIdValid(who))
return SCRIPT_CONTINUE;
int curHP = getHitpoints (self);
if ( curHP < 1 )
{
explodeTurret( self, who);
return SCRIPT_CONTINUE;
}
if ( !pvpCanAttack(self, who) )
{
//debugSpeakMsg(who, "cannot attack this PVP target");
return SCRIPT_CONTINUE;
}
//LOG("turret","***** OnVolEntered: self = " + self + " now = " + getGameTime() + " ********");
//LOG("turret"," - vol = " + volumeName + " who = " + who);
if ( volumeName.equals( turret.ALERT_VOLUME_NAME ) && who != self )
{
//debugSpeakMsg(who, "breached by " + who);
turret.addTarget(self, who);
}
else if ( volumeName.equals( turret.VOL_TOO_CLOSE ) && who != self )
{
turret.removeTarget(self, who);
}
return SCRIPT_CONTINUE;
}
trigger OnTriggerVolumeExited( string volumeName, obj_id who )
{
//LOG("turret","***** OnVolExited: self = " + self + " now = " + getGameTime() + " ********");
//LOG("turret"," - vol = " + volumeName + " target = " + who);
if ( volumeName.equals( turret.ALERT_VOLUME_NAME ) && who != self )
{
turret.removeTarget(self, who);
if (turret.isEngaged(self))
{
obj_id target = utils.getObjIdScriptVar(self, turret.SCRIPTVAR_ENGAGED);
if (target == who)
{
turret.disengage(self);
}
}
}
else if ( volumeName.equals( turret.VOL_TOO_CLOSE ) && who != self )
{
turret.addTarget(self, who);
}
return SCRIPT_CONTINUE;
}
trigger OnObjectDamaged(obj_id attacker, obj_id weapon, int damage)
{
//LOG("turret","***** OnObjectDamaged: self = " + self + " now = " + getGameTime() + " ********");
int curHP = getHitpoints (self);
if ( curHP < 1 )
{
explodeTurret( self, attacker );
return SCRIPT_CONTINUE;
}
turret.addTarget(self, attacker);
if (!utils.hasScriptVar (self, "playingEffect"))
{
int smolder = 2000;
int fire = 1000;
if (curHP < smolder)
{
if (curHP < fire)
{
location death = getLocation (self);
playClientEffectLoc(attacker, "clienteffect/lair_hvy_damage_fire.cef" , death, 0);
utils.setScriptVar (self, "playingEffect", 1);
messageTo (self, "effectManager", null, 15, true);
}
else
{
location death = getLocation (self);
playClientEffectLoc(attacker, "clienteffect/lair_med_damage_smoke.cef" , death, 0);
utils.setScriptVar (self, "playingEffect", 1);
messageTo (self, "effectManager", null, 15, true);
}
}
}
return SCRIPT_CONTINUE;
}
trigger OnSawAttack(obj_id defender, obj_id[] attackers)
{
if ( getConfigSetting("GameServer", "disableAICombat")!=null )
{
//LOG("turret","***GameServer:disableAICombat engaged -> setting saw attack to FALSE");
setWantSawAttackTriggers(self, false);
return SCRIPT_CONTINUE;
}
int numAtt = attackers.length;
for ( int i = 0; i < numAtt; i++ )
{
obj_id attacker = attackers[i];
if(pvpCanAttack(self, attacker))
{
turret.addTarget(self, attacker);
}
}
return SCRIPT_CONTINUE;
}
/***** FUNCTIONS *******************************************************/
obj_id getGoodTurretTarget()
{
//LOG("turret","getGoodTurretTarget: entered...");
obj_id self = getSelf();
if ( !utils.hasScriptVar(self, turret.SCRIPTVAR_TARGETS) )
{
//LOG("turret","getGoodTurretTarget: no target list!");
return null;
}
//remove incapacitated targets:
obj_id[] old_targets = utils.getObjIdBatchScriptVar(self, turret.SCRIPTVAR_TARGETS);
if ( (old_targets == null) || (old_targets.length == 0) )
{
//LOG("turret","getGoodTurretTarget: invalid target list!");
utils.removeBatchScriptVar(self, turret.SCRIPTVAR_TARGETS);
return null;
}
obj_id[] good_targets = cullInvalidTargets(self, old_targets);
//LOG("turret","getGoodTurretTarget: removing incap targets...");
obj_id[] targets = removeIncapacitatedTargets( self, old_targets );
//if all my targets are incapacitated or dead or out of range:
if ( targets.length == 0 )
{
//LOG("turret","getGoodTurretTarget: all targets are incap'd!!");
//reload the list of targets:
targets = good_targets;
//find an incapacitated one and kill him:
for ( int i = 0; i < targets.length; i++ )
{
if ( isIncapacitated( targets[i] ) && !isDead( targets[i] ) && getDistance(self, targets[i]) <= turret.FACTION_TURRET_RANGE && !pet_lib.isPet(targets[i]))
{
//doAttack(targets[i]);
//LOG("turret","* KILLING TARGET: " + targets[i]);
pclib.coupDeGrace(targets[i], self);
//turret.removeTarget( self, targets[i] );
//return null;
}
}
//they must all be dead or out of range:
//utils.removeScriptVar(self, turret.SCRIPTVAR_ENGAGED);
//utils.removeBatchScriptVar(self, turret.SCRIPTVAR_TARGETS);
return obj_id.NULL_ID;
}
if ( utils.hasScriptVar(self, xp.VAR_ATTACKER_LIST) )
{
resizeable obj_id[] attackerList = utils.getResizeableObjIdBatchScriptVar(self, xp.VAR_ATTACKER_LIST);
if ( attackerList != null && attackerList.length > 0 )
{
obj_var[] ovs = new obj_var[attackerList.length];
for ( int i = 0; i < attackerList.length; i++ )
{
string scriptVarPath = xp.VAR_ATTACKER_LIST + "." + attackerList[i] + ".damage";
ovs[i] = new obj_var(attackerList.elementAt(i).toString(), utils.getIntScriptVar(self, scriptVarPath));
}
ovs = list.quickSort(0, ovs.length-1, ovs);
if ( (ovs != null) && (ovs.length > 0) )
{
for ( int i = 0; i < ovs.length; i++ )
{
string ovName = ovs[i].getName();
obj_id tmp = utils.stringToObjId(ovName);
if ( isIdValid(tmp) )
{
if ( utils.getElementPositionInArray(targets, tmp) > -1 )
{
//LOG("turret","getGoodTurretTarget: returning tracked damage target = " + tmp);
if ( canSee(self, tmp) )
return tmp;
}
}
}
}
}
}
//LOG("turret","getGoodTurretTarget: unable to sort damage tracking array for valid target...");
//pick one at random:
int idx = rand(0,targets.length-1);
//LOG("turret","getGoodTurretTarget: returning random (" + idx + ") target = " + targets[idx]);
if ( !turret.isValidTarget(self, targets[idx]) )
{
turret.removeTarget(self, targets[idx]);
return self;
}
return targets[idx];
}
obj_id[] cullInvalidTargets(obj_id self, obj_id[] old_targets)
{
if ( old_targets == null || old_targets.length == 0 )
return null;
resizeable obj_id[] toRemove = new obj_id[0];
for ( int i = 0; i < old_targets.length; i++ )
{
if ( !turret.isValidTarget(self, old_targets[i]) || getDistance(self, old_targets[i]) > turret.FACTION_TURRET_RANGE )
toRemove = utils.addElement(toRemove, old_targets[i]);
}
if ( toRemove != null && toRemove.length > 0 )
{
resizeable obj_id[] targets = new Vector(Arrays.asList(old_targets));
targets = utils.removeElements(targets, toRemove);
return targets;
}
return old_targets;
}
//return a list of targets that are neither dead nor incapacitated nor out of range:
obj_id[] removeIncapacitatedTargets( obj_id self, obj_id[] targets )
{
if ( !isIdValid(self) || (targets == null) || (targets.length == 0) )
return null;
resizeable obj_id[] newTargets = new obj_id[0];
for ( int i = 0; i < targets.length; i++ )
{
if (!ai_lib.isAiDead(targets[i]) && getDistance(self, targets[i]) <= turret.FACTION_TURRET_RANGE )
newTargets = utils.addElement( newTargets, targets[i] );
}
return newTargets;
}
void explodeTurret( obj_id turretid, obj_id killer )
{
obj_id base = getObjIdObjVar(turretid, hq.VAR_DEFENSE_PARENT);
obj_id[] enemies = getWhoIsTargetingMe( turretid );
if ( enemies != null )
{
for ( int i = 0; i < enemies.length; i++ )
{
queueClearCommandsFromGroup(enemies[i], ##"combat_general");
queueClearCommandsFromGroup(enemies[i], ##"combat_melee");
queueClearCommandsFromGroup(enemies[i], ##"combat_ranged");
setTarget( enemies[i], null );
}
}
location death = getLocation(turretid);
playClientEffectLoc(killer, "clienteffect/combat_explosion_lair_large.cef", death, 0);
if ( hasObjVar(turretid, player_structure.VAR_PLAYER_STRUCTURE) )
{
turret.deactivateTurret(turretid);
player_structure.destroyStructure(turretid, false);
}
else
{
turret.deactivateTurret(turretid);
hq.validateDefenseTracking(base);
messageTo(turretid, "handleDestroyTurret", null, 2, false);
}
hq.validateDefenseTracking(base);
messageTo(turretid, "handleDestroyTurret", null, 10f, false);
}
/***********************************************************************
* @brief performs an attach from the turret to the target
*
* @return void
***********************************************************************/
float doAttack()
{
//LOG("turret","*** doAttack(): entered...");
obj_id self = getSelf();
obj_id base = getObjIdObjVar(self, hq.VAR_DEFENSE_PARENT);
if ( !turret.isActive(self) )
{
return -1.0f;
}
int curHP = getHitpoints (self);
if ( curHP < 1 )
{
hq.validateDefenseTracking(base);
messageTo(self, "handleDestroyTurret", null, 2, false);
return -1.0f;
}
obj_id target = utils.getObjIdScriptVar(self, turret.SCRIPTVAR_ENGAGED);
if ( !isIdValid(target) || !target.isLoaded() || (ai_lib.isAiDead(target)) || !canSee(self, target) )
{
obj_id tmptarget = getGoodTurretTarget();
//LOG("turret","doAttack:returned getGoodTurretTarget = " + target);
if ( tmptarget == null )
{
//LOG("turret","doAttack: unable to locate a valid target... sleeping...");
return -2.0f;
}
else if ( tmptarget == obj_id.NULL_ID ) //can't find a valid non-incap target
{
if (utils.hasScriptVar(self,"NULL_ID Counter"))
{
if(utils.getIntScriptVar(self,"NULL_ID Counter") > 5) //if we've already tried a bit, abort the loops
{
return -2.0f; //I dunno why -2.0f. I'm just mimicing what was done int he block directly above. Probably a personal debug value used by RPalacio back in the day.
}
else
{
int searchLoopCounter = utils.getIntScriptVar(self,"NULL_ID Counter");
utils.setScriptVar(self,"NULL_ID Counter", ++searchLoopCounter);
}
}
else
{
utils.setScriptVar(self,"NULL_ID Counter",1) ;
}
//("turret","doAttack: unable to locate a valid NON-INCAP target... waiting...");
return 5.0f;
}
else if ( tmptarget == self )
{
//LOG("turret","Broke! This should probably be a NEGATIVE delay.");
return 1f;
}
else if ( tmptarget == target )
{
return 5.0f;
}
target = tmptarget;
turret.engageTarget(self, target);
}
return doAttack(target);
}
float doAttack(obj_id target)
{
//LOG("turret","*** doAttack(target): entered...");
obj_id self = getSelf();
obj_id base = getObjIdObjVar(self, hq.VAR_DEFENSE_PARENT);
if ( !isIdValid(self) || !isIdValid(target) )
{
//LOG("turret","doAttack(target): invalid params!!");
return -1.0f;
}
int curHP = getHitpoints (self);
if ( curHP < 1 )
{
hq.validateDefenseTracking(base);
messageTo(self, "handleDestroyTurret", null, 2, false);
return -1.0f;
}
//waitasec (or 5) - if we have no weapon yet.
if(!hasObjVar(self, "objWeapon"))
{
//LOG("turret","doAttack(target): unable to locate weapon objvar!!");
return -1.0f;
}
if ( !canSee(self, target) )
{
//LOG("turret","doAttack(target): turret(" + self + ") unable see target(" + target + ")!!");
obj_id tmptarget = getGoodTurretTarget();
if ( isIdValid(tmptarget) && tmptarget != target )
{
target = tmptarget;
turret.engageTarget(self, target);
}
else
{
return 2.0f;
}
}
if ( !turret.isValidTarget(self, target) )
{
//LOG("turret","doAttack(target): turret(" + self + ") targetting invalid target(" + target + ")!!");
turret.removeTarget(self, target);
return 2.0f;
}
messageTo(self, "reconfirmTarget", null, 5, false);
const float DAMAGE_MODIFIER = 2.0f; // modifier for damage from basic attack
// attrib cost mods
const float HEALTH_COST_MODIFIER = 0;;
const float ACTION_COST_MODIFIER = 0;
const int BASE_TO_HIT_MODIFIER = 0; // how far off of 75 do we want the base to hit roll
const float AMMO_COST_MODIFIER = 1.0f; // modifier for ammo cost
string[] strTimeMods = {""};
string[] strDamageMods = {""};
string[] strCostMods = {};
string[] strToHitMods= {""};
string[] strBlockMods = {""};
string[] strEvadeMods = {""};
string[] strCounterAttackMods = {""};
// special response information
int intBlockMod = 1000; //The larger number makes it more difficult to block
int intEvadeMod = 1000; //The larger number makes it more difficult to evade
int intCounterAttackMod = 1000; //The larger number makes it more difficult to counter
int intAttackerEndPosture = POSTURE_NONE;
int intDefenderEndPosture = POSTURE_NONE; // DON'T MIX THESE UP!
string strPlaybackAction = "fire_turret";
int[] intEffects = {
}; // const int's defined in base_class
float[] fltEffectDurations = {
};
int intChanceToApplyEffect= 0; // 1-100 rating for whether we apply a state
obj_id objWeapon = getObjIdObjVar(self, "objWeapon");
//LOG("combat_spam", "weapon is "+objWeapon);
attacker_data cbtAttackerData = new attacker_data();
weapon_data cbtWeaponData = new weapon_data();
obj_id[] objDefenders = new obj_id[1];
objDefenders[0] = target;
defender_data[] cbtDefenderData = new defender_data[objDefenders.length]; // ALWAYS AN ARRAY DO NOT EVER CHANGE DO NOT NEVER!
if (!getCombatData(self, objDefenders, cbtAttackerData, cbtDefenderData, cbtWeaponData))
{
//LOG("Combat", "WARNING: turret_ai.doAttack call to getCombatData returned false");
return -1.0f;
}
// begin giant hack!
//LOG("turret_spam", "objWeapon is "+objWeapon);
cbtWeaponData = getWeaponData(objWeapon);
hit_result[] cbtHitData = new hit_result[1];
cbtHitData[0] = calculateHit(cbtAttackerData, cbtDefenderData[0], cbtWeaponData);
//LOG("combat_spam","Got hit engine results!");
attacker_results cbtAttackerResults = new attacker_results(); // blank data for attacker information
cbtAttackerResults.id = self; // MUST DO THIS!
defender_results[] cbtDefenderResults = new defender_results[1];
cbtDefenderResults[0] = new defender_results();
// pass by reference below, so be careful
//LOG("combat_spam","Setting Combat Results!");
//setDefenderCombatResults(cbtAttackerData, cbtAttackerResults, cbtHitData, cbtDefenderData, cbtDefenderResults, strBlockMods, intBlockMod, strEvadeMods, intEvadeMod, strCounterAttackMods, intCounterAttackMod, true); // sorry this function sucks so much
cbtAttackerResults.endPosture = -1;
debugServerConsoleMsg(self, "posture set");
cbtAttackerResults.weapon = objWeapon;
debugServerConsoleMsg(self, "weapon");
//LOG("combat_spam", "My target is now "+target);
cbtDefenderResults[0].id = target;
debugServerConsoleMsg(self, "target");
cbtDefenderResults[0].endPosture = getPosture(target);
debugServerConsoleMsg(self, "defender posture");
if(cbtHitData[0].success)
{
cbtDefenderResults[0].result = COMBAT_RESULT_HIT;
}
else
{
cbtDefenderResults[0].result = COMBAT_RESULT_MISS;
}
debugServerConsoleMsg(self, "hitdata");
//LOG("combat_spam","Applying Postures!");
//applyPostures(cbtAttackerData, cbtAttackerResults, cbtHitData, cbtDefenderData, cbtDefenderResults, intAttackerEndPosture, intDefenderEndPosture, cbtWeaponData);
//LOG("combat_spam","Applying States");
// because these are negative states, we'll alter our list and just pick one..
//applyStates(cbtDefenderData, cbtHitData, intChanceToApplyEffect, intEffects, fltEffectDurations); // applies states
//LOG("combat_spam","Finalizing Damage");
finalizeDamage(cbtAttackerData.id, cbtWeaponData, cbtDefenderData, cbtHitData, cbtDefenderResults, null); // applies all of the necessary damage
//LOG("combat_spam","doing combat results!");
//LOG("combat_spam","Attacker final posture before docombatresults is "+cbtAttackerResults.endPosture);
string[] strPlaybackNames = makePlaybackNames("fire_turret", cbtHitData, cbtWeaponData, cbtDefenderResults);
doCombatResults(strPlaybackNames[0], cbtAttackerResults, cbtDefenderResults);
combat.doBasicCombatSpam("shoot", cbtAttackerResults, cbtDefenderResults, cbtHitData);
return cbtWeaponData.attackSpeed+rand(0,2);
}
/***** MESSAGEHANDLERS *************************************************/
messageHandler handleTurretAttack()
{
//LOG("turret","turret attack reqested...");
if (utils.hasScriptVar(self,"messageToSerialNum"))
{
int storedSerialNumber = utils.getIntScriptVar(self,"messageToSerialNum");
int sentSerialNumber = params.getInt("messageToSerialNum");
if (storedSerialNumber != sentSerialNumber)
{
//LOG("turret","handleTurretAttack entry: got a different messageTo than the one we were waiting for...ABORTING THIS MESSAGEHANDLER");
return SCRIPT_CONTINUE;
}
}
if ( !turret.isActive(self) )
{
//LOG("turret","turret inactive: ignoring turret attack request...");
return SCRIPT_CONTINUE;
}
float delay = doAttack();
//LOG("turret","turret attack: returned delay(" + getGameTime() + ") = " + delay);
if ( delay > 0f )
{
int serialNumber = (int) rand(0,100);
params.put("messageToSerialNum", serialNumber);
utils.setScriptVar(self,"messageToSerialNum",serialNumber);
messageTo( self, "handleTurretAttack", params, delay, false );
}
else
{
utils.removeScriptVar(self, turret.SCRIPTVAR_ENGAGED);
utils.removeScriptVar(self, "messageToSerialNum");
utils.removeScriptVar( self, "ai.combat.isInCombat");
}
return SCRIPT_CONTINUE;
}
/*This message handler is just the delay for the client effects. It makes
sure that multiple effects aren't playing at the same time and eating up
the frame rate. */
messageHandler effectManager ()
{
utils.removeScriptVar (self, "playingEffect");
return SCRIPT_CONTINUE;
}
messageHandler expireLair()
{
destroyObject( self );
return SCRIPT_CONTINUE;
}
messageHandler enemyDecloaked()
{
//LOG("turret","enemy decloaked entered...");
if ( (params == null) || (params.isEmpty()) )
{
return SCRIPT_CONTINUE;
}
obj_id target = params.getObjId("target");
//LOG("turret","decloaked enemy target = " + target + " " + getEncodedName(target));
if ( isIdValid(target) )
{
if ( getDistance(self, target) <= turret.FACTION_TURRET_RANGE )
{
turret.addTarget(self, target);
}
}
return SCRIPT_CONTINUE;
}
messageHandler handleDestroyTurret()
{
destroyObject(self);
return SCRIPT_CONTINUE;
}