mirror of
https://bitbucket.org/seefoe/dsrc.git
synced 2026-07-31 01:15:55 -04:00
1633 lines
50 KiB
Plaintext
1633 lines
50 KiB
Plaintext
include library.utils;
|
|
include library.hue;
|
|
include library.ai_lib;
|
|
include ai.ai_combat;
|
|
include library.factions;
|
|
include library.dressup;
|
|
include library.attrib;
|
|
include library.weapons;
|
|
include library.stealth;
|
|
|
|
const string CREATURE_TABLE = "datatables/mob/creatures.iff";
|
|
const string STAT_BALANCE_TABLE = "datatables/mob/stat_balance.iff";
|
|
const string VEHICLE_TABLE = "datatables/vehicle/vehicle_template.iff";
|
|
const string TEMPLATE_PREFIX = "object/mobile/";
|
|
const string CREATURE_NAME_FILE = "mob/creature_names";
|
|
const string NPC_CUSTOMIZATION_PREFIX = "datatables/npc_customization/";
|
|
const string DRESSED_NPC_TABLE = "datatables/npc_customization/dressed_species.iff";
|
|
const string DRESSED_NPC_TABLE_ROOT = "datatables/npc_customization/dressed_species_";
|
|
const string BIOLINK_SCRIPT = "item.armor.biolink_item_non_faction";
|
|
|
|
const float MELEE_SPEED_MOD = 1.25f; // melee mobs gets X% movement speed bonus
|
|
const float MELEE_HP_MOD = 1.15f; // melee mobs get X% hitpoint bonus
|
|
|
|
const string INITIALIZE_CREATURE_DO_NOT_SCALE_OBJVAR = "create_doNotRescale";
|
|
|
|
//const string NPC_DATATABLE = "datatables/npc_customization/npc.iff";
|
|
|
|
const string[] MINATTRIBNAMES = {
|
|
"minHealth",
|
|
"minConstitution",
|
|
"minAction",
|
|
"minStamina",
|
|
"minMind",
|
|
"minWillpower"
|
|
};
|
|
|
|
const string[] MAXATTRIBNAMES = {
|
|
"maxHealth",
|
|
"maxConstitution",
|
|
"maxAction",
|
|
"maxStamina",
|
|
"maxMind",
|
|
"maxWillpower"
|
|
};
|
|
|
|
/**
|
|
* Creates a creature without attaching AI or combat scripts to the creature
|
|
* and sets the creature Static:
|
|
*
|
|
* @params creatureName the name of the creature to create
|
|
* @params spawnLocation the location at which to create the creature
|
|
*
|
|
* @returns the obj_id of the creature created
|
|
*/
|
|
obj_id staticObject( string objectName, location spawnLocation )
|
|
{
|
|
|
|
if ( objectName == null )
|
|
{
|
|
LOG("create", "create.staticObject called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
|
|
obj_id creature = create.object( objectName, spawnLocation, false);
|
|
if ( creature != null )
|
|
{
|
|
setCreatureStatic(creature, true);
|
|
setWantSawAttackTriggers( creature, false );
|
|
setInvulnerable( creature, true );
|
|
}
|
|
return creature;
|
|
}
|
|
|
|
/* -----------------12/5/2002 2:49PM-----------------
|
|
* creates an object from template, creature datatable or npc datatable
|
|
*
|
|
* @param objectName the templatename, creature or NPC type to create
|
|
* @param spawnLocation the location to create the object
|
|
*
|
|
* @returns the obj_id of the created object
|
|
* --------------------------------------------------*/
|
|
obj_id object( string objectName, location spawnLocation )
|
|
{
|
|
return object( objectName, spawnLocation, -1 );
|
|
}
|
|
|
|
obj_id object( string objectName, location spawnLocation, int level )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG("create", "create.object called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
|
|
return create.object( objectName, spawnLocation, level, true);
|
|
}
|
|
|
|
obj_id object( string objectName, location spawnLocation, boolean withAi )
|
|
{
|
|
return object( objectName, spawnLocation, -1, withAi );
|
|
}
|
|
|
|
obj_id object( string objectName, location spawnLocation, int level, boolean withAi )
|
|
{
|
|
return create.object( objectName, spawnLocation, level, withAi, false );
|
|
}
|
|
|
|
obj_id object( string objectName, location spawnLocation, boolean withAi, boolean isPet )
|
|
{
|
|
return object( objectName, spawnLocation, -1, withAi, isPet );
|
|
}
|
|
|
|
obj_id object( string objectName, location spawnLocation, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG( "create", "create.object called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
if ( objectName.endsWith(".iff") )
|
|
{
|
|
LOG( "create", getName( getSelf() ) + " creating: " + objectName + " at " + spawnLocation );
|
|
obj_id creature = createObject( objectName, spawnLocation );
|
|
if ( isIdValid(creature) )
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
return creature;
|
|
}
|
|
else
|
|
{
|
|
obj_id creature = createCreature( objectName, spawnLocation, level, withAi, isPet );
|
|
if ( isIdValid(creature) )
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
return creature;
|
|
}
|
|
}
|
|
|
|
obj_id object( string objectName, obj_id objContainer, boolean withAi, boolean isPet )
|
|
{
|
|
obj_id creature = object( objectName, objContainer, -1, withAi, isPet );
|
|
if ( isIdValid(creature) )
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
return creature;
|
|
}
|
|
|
|
obj_id object( string objectName, obj_id objContainer, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG( "create", "create.object called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
if ( objectName.endsWith(".iff") )
|
|
{
|
|
LOG( "create", getName( getSelf() ) + " creating: " + objectName + " at " + objContainer);
|
|
obj_id creature = createObject( objectName, objContainer, "" );
|
|
if ( isIdValid(creature) )
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
return creature;
|
|
}
|
|
else
|
|
{
|
|
obj_id creature = createCreature( objectName, objContainer, level, withAi, isPet );
|
|
if ( isIdValid(creature) )
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
return creature;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an object at an x/z-offset of the parent object's location
|
|
* The parent object is the object to which this script is attached
|
|
*
|
|
* @params objectName the name of the thing to make
|
|
* @params xOffset the float x-offset at which to create the object
|
|
* @params zOffset the float z-offset at which to create the object
|
|
*
|
|
* @returns the obj_id of the new object
|
|
*/
|
|
obj_id object( string objectName, float xOffset, float zOffset )
|
|
{
|
|
return object( objectName, xOffset, zOffset, -1 );
|
|
}
|
|
|
|
obj_id object( string objectName, float xOffset, float zOffset, int level )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG("create", "create.object called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
|
|
location spawnLoc = new location( getLocation( getSelf() ) );
|
|
spawnLoc.x += xOffset;
|
|
spawnLoc.z += zOffset;
|
|
return create.object( objectName, spawnLoc, level );
|
|
}
|
|
|
|
/**
|
|
* Creates an object, persists it, makes it invulnerable and adds a destroy messageHandler to it
|
|
*
|
|
* @params objectName the template, NPC or creatureName to create
|
|
* @params xOffset
|
|
* @params zOffset the location offset from Self to create the object at
|
|
* @params destoyHandlerName the messageHandler to send a message to Self when this object is destroyed
|
|
* @params destroyMessageDelay the delay for the destroy message to use
|
|
*
|
|
* @returns the obj_id of the new object
|
|
*/
|
|
obj_id themeParkObject( string objectName, float xOffset, float zOffset, string destroyHandlerName, float destroyMessageDelay )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG("create", "create.themeParkObject called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
|
|
obj_id newObject = create.themeParkObject( objectName, xOffset, zOffset );
|
|
addDestroyMessage( newObject, destroyHandlerName, destroyMessageDelay, getSelf() );
|
|
return newObject;
|
|
}
|
|
|
|
obj_id themeParkObject( string objectName, float xOffset, float zOffset )
|
|
{
|
|
if ( objectName == null )
|
|
{
|
|
LOG("create", "create.themeParkObject called with NULL objectName!" );
|
|
return null;
|
|
}
|
|
|
|
location spawnLoc = new location( getLocation( getSelf() ) );
|
|
spawnLoc.x += xOffset;
|
|
spawnLoc.z += zOffset;
|
|
obj_id newObject = create.object( objectName, spawnLoc );
|
|
setInvulnerable( newObject, true );
|
|
ai_lib.setDefaultCalmBehavior( newObject, ai_lib.BEHAVIOR_SENTINEL );
|
|
return newObject;
|
|
}
|
|
|
|
|
|
/* -----------------12/11/2002 3:31PM----------------
|
|
* Used to create an NPC of a specified species or from a specific species-list
|
|
*
|
|
* @params creatureName the name of the creature to spawn
|
|
* @params templateName the name of the creature template to use, or the species datatable name to use
|
|
* @params spawnLocation the location at which to create the thing
|
|
*
|
|
* @returns the obj_id of the created creature
|
|
* --------------------------------------------------*/
|
|
obj_id createNpc( string creatureName, string templateName, location spawnLocation )
|
|
{
|
|
return createNpc( creatureName, templateName, spawnLocation, -1 );
|
|
}
|
|
|
|
obj_id createNpc( string creatureName, string templateName, location spawnLocation, int level )
|
|
{
|
|
if ( creatureName == null )
|
|
{
|
|
LOG("create", "create.createNpc called with NULL creatureName!" );
|
|
return null;
|
|
}
|
|
|
|
if ( templateName == null )
|
|
{
|
|
LOG("create", "create.createNpc called with NULL templateName!" );
|
|
return null;
|
|
}
|
|
|
|
dictionary creatureDict = utils.dataTableGetRow( CREATURE_TABLE, creatureName );
|
|
if ( creatureDict == null )
|
|
return null;
|
|
|
|
if (!templateName.endsWith(".iff" ))
|
|
{
|
|
//if this is an entry in the dress-species table, then use that:
|
|
string tableName = DRESSED_NPC_TABLE_ROOT + templateName.charAt(0) + ".iff";
|
|
if ( dataTableHasColumn( tableName, templateName ) )
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( tableName, templateName );
|
|
if ( templateList.length < 1 || templateList == null )
|
|
{
|
|
LOG( "create", templateName + " column in dressed_species is empty!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ];
|
|
}
|
|
else
|
|
{
|
|
//else use the old species_selection table
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( NPC_CUSTOMIZATION_PREFIX + templateName + ".iff", rand(0,1));
|
|
if ( templateList.length < 1 || templateList == null )
|
|
{
|
|
LOG( "create", templateName + " datatable does not exist!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ];
|
|
}
|
|
}
|
|
return createCreature( creatureName, templateName, spawnLocation, creatureDict, level, true );
|
|
}
|
|
|
|
/**
|
|
* Creates an NPC of a specific type at x/z-offsets from the parent object
|
|
* The parent object is the object to which this script is attached
|
|
*
|
|
* @params creatureName the name of the npc to create
|
|
* @params templateName the species template or species-template list datatable to create
|
|
* @params xOffset the x-offset to create the creature at
|
|
* @params zOffset the z-offset to create the creature at
|
|
*
|
|
* @returns the obj_id of the creature created
|
|
*/
|
|
obj_id createNpc( string creatureName, string templateName, float xOffset, float zOffset )
|
|
{
|
|
return createNpc( creatureName, templateName, xOffset, zOffset, -1 );
|
|
}
|
|
|
|
obj_id createNpc( string creatureName, string templateName, float xOffset, float zOffset, int level )
|
|
{
|
|
|
|
if ( creatureName == null )
|
|
{
|
|
LOG("create", "create.createNpc called with NULL creatureName!" );
|
|
return null;
|
|
}
|
|
|
|
if ( templateName == null )
|
|
{
|
|
LOG("create", "create.createNpc called with NULL templateName!" );
|
|
return null;
|
|
}
|
|
|
|
location spawnLoc = new location( getLocation( getSelf() ) );
|
|
spawnLoc.x += xOffset;
|
|
spawnLoc.z += zOffset;
|
|
return create.createNpc( creatureName, templateName, spawnLoc, level );
|
|
}
|
|
|
|
/**
|
|
* Used to create a creature from the creature datatable at the given location
|
|
*
|
|
* @params creatureName the name/type of creature to spawn
|
|
* @params spawnLocation the location at which to spawn it
|
|
*
|
|
* @returns the obj_id of the creature
|
|
*/
|
|
obj_id createCreature( string creatureName, location spawnLocation, boolean withAi )
|
|
{
|
|
return createCreature( creatureName, spawnLocation, -1, withAi );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, location spawnLocation, int level, boolean withAi )
|
|
{
|
|
return create.createCreature( creatureName, spawnLocation, level, withAi, false );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, location spawnLocation, boolean withAi, boolean isPet )
|
|
{
|
|
return createCreature( creatureName, spawnLocation, -1, withAi, isPet );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, location spawnLocation, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( creatureName == null )
|
|
{
|
|
LOG("create", "create.createCreature called with NULL creatureName!" );
|
|
return null;
|
|
}
|
|
|
|
dictionary creatureDict = utils.dataTableGetRow( CREATURE_TABLE, creatureName );
|
|
if ( creatureDict == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn invalid creatureType: " + creatureName );
|
|
return null;
|
|
}
|
|
|
|
string templateName = creatureDict.getString("template");
|
|
if ( templateName == "" || templateName == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn invalid creatureType: " + creatureName + " - bad templateName?" );
|
|
return null;
|
|
}
|
|
|
|
if (!templateName.endsWith(".iff" ))
|
|
{
|
|
|
|
string tableName = DRESSED_NPC_TABLE_ROOT + templateName.charAt(0) + ".iff";
|
|
if ( dataTableHasColumn( tableName, templateName ) )
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( tableName, templateName );
|
|
if ( templateList.length < 1 || templateList == null )
|
|
{
|
|
LOG( "create", templateName + " column in dressed_species is empty!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ];
|
|
}
|
|
else
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( NPC_CUSTOMIZATION_PREFIX + templateName + ".iff", rand(0,1));
|
|
if ( templateList == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName );
|
|
LOG( "create", templateName + " does not exist!" );
|
|
return null;
|
|
}
|
|
if ( templateList.length == 0)
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName );
|
|
LOG( "create", templateName + " File was Empty!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ] + ".iff";
|
|
}
|
|
}
|
|
return createCreature( creatureName, templateName, spawnLocation, creatureDict, level, withAi, isPet );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, obj_id objContainer, boolean withAi, boolean isPet )
|
|
{
|
|
return createCreature( creatureName, objContainer, -1, withAi, isPet );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, obj_id objContainer, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( creatureName == null )
|
|
{
|
|
LOG("create", "create.createCreature called with NULL creatureName!" );
|
|
return null;
|
|
}
|
|
|
|
dictionary creatureDict = utils.dataTableGetRow( CREATURE_TABLE, creatureName );
|
|
if ( creatureDict == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn invalid creatureType: " + creatureName );
|
|
return null;
|
|
}
|
|
|
|
string templateName = creatureDict.getString("template");
|
|
if ( templateName == "" || templateName == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn invalid creatureType: " + creatureName + " - bad templateName?" );
|
|
return null;
|
|
}
|
|
|
|
if (!templateName.endsWith(".iff" ))
|
|
{
|
|
|
|
string tableName = DRESSED_NPC_TABLE_ROOT + templateName.charAt(0) + ".iff";
|
|
if ( dataTableHasColumn( tableName, templateName ) )
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( tableName, templateName );
|
|
if ( templateList.length < 1 || templateList == null )
|
|
{
|
|
LOG( "create", templateName + " column in dressed_species is empty!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ];
|
|
}
|
|
else
|
|
{
|
|
string[] templateList = dataTableGetStringColumnNoDefaults( NPC_CUSTOMIZATION_PREFIX + templateName + ".iff", rand(0,1));
|
|
if ( templateList == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName );
|
|
LOG( "create", templateName + " does not exist!" );
|
|
return null;
|
|
}
|
|
if ( templateList.length == 0)
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to spawn " + creatureName + " - bad templateName? " + templateName );
|
|
LOG( "create", templateName + " File was Empty!" );
|
|
return null;
|
|
}
|
|
templateName = templateList[ rand(0, templateList.length-1 ) ] + ".iff";
|
|
}
|
|
}
|
|
return createCreature( creatureName, templateName, objContainer, creatureDict, level, withAi, isPet );
|
|
}
|
|
|
|
/**
|
|
* Called by createNpc and createCreature to create a creature and set attribs from the datatable
|
|
*
|
|
* @params creatureName the name of the creature to create
|
|
* @params templateName the name of the template to use
|
|
* @params spawnLocation the location at which to create the creature
|
|
* @params creatureDict a dictionary containing the creature's datatable info
|
|
*
|
|
* @returns the obj_id of the creature created
|
|
*/
|
|
obj_id createCreature( string creatureName, string templateName, location spawnLocation, dictionary creatureDict, boolean withAi )
|
|
{
|
|
return createCreature( creatureName, templateName, spawnLocation, creatureDict, -1, withAi );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, string templateName, location spawnLocation, dictionary creatureDict, int level, boolean withAi )
|
|
{
|
|
return createCreature( creatureName, templateName, spawnLocation, creatureDict, level, withAi, false );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, string templateName, location spawnLocation, dictionary creatureDict, boolean withAi, boolean isPet )
|
|
{
|
|
return createCreature( creatureName, templateName, spawnLocation, creatureDict, -1, withAi, isPet );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, string templateName, location spawnLocation, dictionary creatureDict, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( templateName == "" || templateName == null )
|
|
{
|
|
LOG( "create", creatureName + " could not be made because templateName is null" );
|
|
return null;
|
|
}
|
|
if ( creatureName == "" || creatureName == null )
|
|
{
|
|
LOG( "create", "creatureName is null - can't spawn whatever this was supposed to be" );
|
|
return null;
|
|
}
|
|
|
|
templateName = TEMPLATE_PREFIX + templateName;
|
|
|
|
LOG( "create", getName( getSelf() ) + " creating: " + creatureName + " at " + spawnLocation );
|
|
obj_id creature = createObject( templateName, spawnLocation );
|
|
if (!isIdValid(creature))
|
|
return null;
|
|
else
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
if ( isPet )
|
|
utils.setScriptVar( creature, "petBeingInitialized", true );
|
|
|
|
string finalCreatureName = getStringObjVar( creature, "ai.creatureBaseName" );
|
|
if ( finalCreatureName == null || finalCreatureName == "" )
|
|
{
|
|
finalCreatureName = creatureName;
|
|
}
|
|
|
|
randomlyNameCreature( creature, finalCreatureName );
|
|
|
|
initializeCreature ( creature, creatureName, creatureDict, level );
|
|
|
|
attachCreatureScripts( creature, creatureDict.getString( "scripts" ), withAi );
|
|
|
|
return creature;
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, string templateName, obj_id objContainer, dictionary creatureDict, boolean withAi, boolean isPet )
|
|
{
|
|
return createCreature( creatureName, templateName, objContainer, creatureDict, -1, withAi, isPet );
|
|
}
|
|
|
|
obj_id createCreature( string creatureName, string templateName, obj_id objContainer, dictionary creatureDict, int level, boolean withAi, boolean isPet )
|
|
{
|
|
if ( templateName == "" || templateName == null )
|
|
{
|
|
LOG( "create", creatureName + " could not be made because templateName is null" );
|
|
return null;
|
|
}
|
|
if ( creatureName == "" || creatureName == null )
|
|
{
|
|
LOG( "create", "creatureName is null - can't spawn whatever this was supposed to be" );
|
|
return null;
|
|
}
|
|
|
|
templateName = TEMPLATE_PREFIX + templateName;
|
|
|
|
LOG( "create", getName( getSelf() ) + " creating: " + creatureName + " at " + objContainer );
|
|
obj_id creature = createObject( templateName, objContainer, "" );
|
|
if (!isIdValid(creature))
|
|
return null;
|
|
else
|
|
utils.setScriptVar( creature, "spawnedBy", getSelf() );
|
|
|
|
if ( isPet )
|
|
utils.setScriptVar( creature, "petBeingInitialized", true );
|
|
|
|
string finalCreatureName = getStringObjVar( creature, "ai.creatureBaseName" );
|
|
if ( finalCreatureName == null || finalCreatureName == "" )
|
|
{
|
|
finalCreatureName = creatureName;
|
|
}
|
|
|
|
randomlyNameCreature( creature, finalCreatureName );
|
|
|
|
initializeCreature ( creature, creatureName, creatureDict, level );
|
|
|
|
attachCreatureScripts( creature, creatureDict.getString( "scripts" ), withAi );
|
|
|
|
return creature;
|
|
}
|
|
// returns modified xp value for creature xp
|
|
|
|
/*
|
|
int getCreatureXP(dictionary creatureDict)
|
|
{
|
|
const float PACK_MODIFIER = .05f;
|
|
const float AGGRO_MODIFIER = .10f;
|
|
const float KILLER_MODIIFER = .05f;
|
|
const float STALKER_MODIFIER = .025f;
|
|
|
|
|
|
int intRawXP = creatureDict.getInt( "XP" );
|
|
int intKiller = creatureDict.getInt("killer");
|
|
int intStalker = creatureDict.getInt("stalker");
|
|
int intPack = creatureDict.getInt("pack");
|
|
int intAggro = creatureDict.getInt("aggro");
|
|
|
|
float fltModifier = 1;
|
|
fltModifier = fltModifier + (intKiller * KILLER_MODIIFER);
|
|
fltModifier = fltModifier + (intStalker * STALKER_MODIFIER);
|
|
fltModifier = fltModifier + (intPack * PACK_MODIFIER);
|
|
fltModifier = fltModifier + (intAggro * AGGRO_MODIFIER);
|
|
|
|
intRawXP = (int)(intRawXP * fltModifier);
|
|
return intRawXP;
|
|
}*/
|
|
|
|
/**
|
|
* Called by createCreature to set a creature's attribs from the datatable
|
|
*
|
|
* @params creature the obj_id of the creature to initialize
|
|
* @params creatureName the name of the creature to create
|
|
* @params creatureDict a dictionary containing the creature's datatable info
|
|
*
|
|
* @returns nothing
|
|
*/
|
|
void initializeCreature( obj_id creature, string creatureName, dictionary creatureDict )
|
|
{
|
|
initializeCreature( creature, creatureName, creatureDict, -1);
|
|
}
|
|
|
|
void initializeCreature( obj_id creature, string creatureName, dictionary creatureDict, int level )
|
|
{
|
|
LOGC(aiLoggingEnabled(creature), "debug_ai", ("########## create::initializeCreature() BEGIN ai(" + creature + ") creatureName(" + creatureName + ") ##########"));
|
|
|
|
//record creatureName for AI script to reference
|
|
setCreatureName(creature, creatureName);
|
|
|
|
//set scale:
|
|
float minScale = creatureDict.getFloat( "minScale" );
|
|
float maxScale = creatureDict.getFloat( "maxScale" );
|
|
float baseScale = getScale( creature );
|
|
if ( baseScale != 1.0f )
|
|
utils.setScriptVar( creature, "ai.baseScale", baseScale );
|
|
|
|
// don't rescale storyteller npcs.
|
|
if ( !hasObjVar(creature, "storytellerid") && !hasObjVar(creature, INITIALIZE_CREATURE_DO_NOT_SCALE_OBJVAR) )
|
|
{
|
|
float newScale = baseScale * rand( minScale, maxScale );
|
|
setScale( creature, newScale );
|
|
|
|
setYaw( creature, rand(0.0f, 360.0f ) );
|
|
}
|
|
|
|
setObjVar(creature, "creature_type", creatureName);
|
|
setObjVar(creature, "socialGroup", creatureDict.getString("socialGroup"));
|
|
|
|
//hue creature:
|
|
int huevar = creatureDict.getInt( "hue" );
|
|
if ( huevar != 0 )
|
|
{
|
|
int highhuevar = (huevar*8)-1;
|
|
int lowhuevar = highhuevar-7;
|
|
huevar = rand(lowhuevar, highhuevar);
|
|
ranged_int_custom_var[] c = hue.getPalcolorVars(creature);
|
|
if ( c != null )
|
|
{
|
|
for ( int i = 0; i < c.length; i++)
|
|
c[i].setValue(huevar);
|
|
}
|
|
}
|
|
|
|
// Get level settings
|
|
int baseLevel = creatureDict.getInt("BaseLevel");
|
|
int dmgLevel = baseLevel + creatureDict.getInt("Damagelevelmodifier");
|
|
int statLevel = baseLevel + creatureDict.getInt("StatLevelModifier");
|
|
int toHitLevel = baseLevel + creatureDict.getInt("ToHitLevelModifier");
|
|
int armorLevel = baseLevel + creatureDict.getInt("ArmorLevelModifier");
|
|
|
|
// Cap levels
|
|
if (dmgLevel <= 0) dmgLevel = 1;
|
|
if (statLevel <= 0) statLevel = 1;
|
|
if (toHitLevel <= 0) toHitLevel = 1;
|
|
if (armorLevel <= 0) armorLevel = 1;
|
|
|
|
// Check for level override
|
|
if (level > 0)
|
|
{
|
|
dmgLevel = level;
|
|
statLevel = level;
|
|
toHitLevel = level;
|
|
armorLevel = level;
|
|
}
|
|
else // Compute final level on level settings
|
|
{
|
|
// This weighted average must match up with BE clone level calcs
|
|
level = calcCreatureLevel(statLevel, dmgLevel, toHitLevel, armorLevel);
|
|
}
|
|
|
|
setObjVar(creature, "intCombatDifficulty", level);
|
|
setLevel(creature, level);
|
|
|
|
int stealType = creatureDict.getInt("stealingFlags");
|
|
utils.setScriptVar(creature, stealth.STEAL_TYPE, stealType);
|
|
|
|
int difficultyClass = creatureDict.getInt("difficultyClass");
|
|
if (difficultyClass < 0) difficultyClass = 0;
|
|
|
|
setObjVar(creature, "difficultyClass", difficultyClass);
|
|
|
|
string diffClassName = "";
|
|
if (difficultyClass == 1) diffClassName = "Elite_";
|
|
if (difficultyClass == 2) diffClassName = "Boss_";
|
|
|
|
float damagePerSecond = dataTableGetFloat(STAT_BALANCE_TABLE, dmgLevel - 1, diffClassName+"damagePerSecond");
|
|
|
|
|
|
|
|
int toHitChance = dataTableGetInt(STAT_BALANCE_TABLE, toHitLevel - 1, diffClassName + "ToHit" );
|
|
int defenseValue = dataTableGetInt(STAT_BALANCE_TABLE, toHitLevel - 1, diffClassName + "Def" );
|
|
boolean hasRanged = false;
|
|
obj_id creatureWeapon = getCurrentWeapon( creature );
|
|
float primarySpeed = creatureDict.getFloat("primary_weapon_speed");
|
|
float secondarySpeed = creatureDict.getFloat("secondary_weapon_speed");
|
|
|
|
int priMinDamage = Math.round((damagePerSecond * primarySpeed) * 0.5f);
|
|
int priMaxDamage = Math.round((damagePerSecond * primarySpeed) * 1.5f);
|
|
|
|
int secMinDamage = Math.round((damagePerSecond * secondarySpeed) * 0.5f);
|
|
int secMaxDamage = Math.round((damagePerSecond * secondarySpeed) * 1.5f);
|
|
|
|
|
|
if (isIdValid(creatureWeapon))
|
|
{
|
|
setWeaponAttackSpeed(creatureWeapon, primarySpeed);
|
|
setWeaponMaxDamage(creatureWeapon, priMaxDamage);
|
|
setWeaponMinDamage(creatureWeapon, priMinDamage);
|
|
weapons.setWeaponData(creatureWeapon);
|
|
|
|
// This scriptvar is pre-existing and is already in use in a number of places to check for a weapon that is being used by an ai.
|
|
// The objvar is being added so that if a player somehow gets hold of an ai weapon, we'll be able to know that.
|
|
utils.setScriptVar(creatureWeapon, "isCreatureWeapon", 1);
|
|
setObjVar(creatureWeapon, "isCreatureWeapon", 1);
|
|
}
|
|
|
|
obj_id defaultWeapon = getDefaultWeapon( creature );
|
|
if (isIdValid(defaultWeapon))
|
|
{
|
|
setWeaponAttackSpeed(defaultWeapon, primarySpeed);
|
|
setWeaponMaxDamage(defaultWeapon, secMaxDamage);
|
|
setWeaponMinDamage(defaultWeapon, secMinDamage);
|
|
weapons.setWeaponData(defaultWeapon);
|
|
|
|
// This scriptvar is pre-existing and is already in use in a number of places to check for a weapon that is being used by an ai.
|
|
// The objvar is being added so that if a player somehow gets hold of an ai weapon, we'll be able to know that.
|
|
utils.setScriptVar(defaultWeapon, "isCreatureWeapon", 1);
|
|
setObjVar(defaultWeapon, "isCreatureWeapon", 1);
|
|
}
|
|
|
|
// Setup the primary weapon (there is no real need I can see for this weapon setup to not be in server code)
|
|
{
|
|
if(aiHasPrimaryWeapon(creature))
|
|
{
|
|
const obj_id primaryWeapon = aiGetPrimaryWeapon(creature);
|
|
dictionary primDat = weapons.getWeaponDat(primaryWeapon);
|
|
|
|
if(primDat != null)
|
|
{
|
|
// set values from data table
|
|
weapons.setWeaponAttributes(primaryWeapon, primDat, 1f);
|
|
}
|
|
|
|
// override data table values
|
|
setWeaponAttackSpeed(primaryWeapon, primarySpeed);
|
|
setWeaponMaxDamage(primaryWeapon, priMaxDamage);
|
|
setWeaponMinDamage(primaryWeapon, priMinDamage);
|
|
weapons.setWeaponData(primaryWeapon);
|
|
hasRanged = hasRanged || combat.isRangedWeapon(primaryWeapon);
|
|
if(hasScript(primaryWeapon, BIOLINK_SCRIPT))
|
|
cleanOffBioLink(primaryWeapon);
|
|
|
|
// This scriptvar is pre-existing and is already in use in a number of places to check for a weapon that is being used by an ai.
|
|
// The objvar is being added so that if a player somehow gets hold of an ai weapon, we'll be able to know that.
|
|
utils.setScriptVar(primaryWeapon, "isCreatureWeapon", 1);
|
|
setObjVar(primaryWeapon, "isCreatureWeapon", 1);
|
|
}
|
|
}
|
|
|
|
// Setup the secondary weapon (there is no real need I can see for this weapon setup to not be in server code)
|
|
{
|
|
if(aiHasSecondaryWeapon(creature))
|
|
{
|
|
const obj_id secondaryWeapon = aiGetSecondaryWeapon(creature);
|
|
dictionary secDat = weapons.getWeaponDat(secondaryWeapon);
|
|
|
|
if(secDat != null)
|
|
{
|
|
weapons.setWeaponAttributes(secondaryWeapon, secDat, 1f);
|
|
}
|
|
|
|
// override data table values
|
|
setWeaponAttackSpeed(secondaryWeapon, secondarySpeed);
|
|
setWeaponMaxDamage(secondaryWeapon, secMaxDamage);
|
|
setWeaponMinDamage(secondaryWeapon, secMinDamage);
|
|
weapons.setWeaponData(secondaryWeapon);
|
|
hasRanged = hasRanged || combat.isRangedWeapon(secondaryWeapon);
|
|
if(hasScript(secondaryWeapon, BIOLINK_SCRIPT))
|
|
cleanOffBioLink(secondaryWeapon);
|
|
|
|
// This scriptvar is pre-existing and is already in use in a number of places to check for a weapon that is being used by an ai.
|
|
// The objvar is being added so that if a player somehow gets hold of an ai weapon, we'll be able to know that.
|
|
utils.setScriptVar(secondaryWeapon, "isCreatureWeapon", 1);
|
|
setObjVar(secondaryWeapon, "isCreatureWeapon", 1);
|
|
}
|
|
}
|
|
|
|
// non-ranged opponents get more HP and faster speed
|
|
float speedMod = 1;
|
|
float hpMod = 1;
|
|
if(hasRanged)
|
|
{
|
|
speedMod = MELEE_SPEED_MOD;
|
|
hpMod = MELEE_HP_MOD;
|
|
}
|
|
|
|
//setAttribs
|
|
int avgAttribHealth = dataTableGetInt(STAT_BALANCE_TABLE, statLevel - 1, diffClassName + "HP");
|
|
int minAttribHealth = minAttribHealth = (int)(avgAttribHealth * 0.9f);
|
|
int maxAttribHealth = maxAttribHealth = (int)(avgAttribHealth * 1.1f);
|
|
float newAttribValueHealth = rand( minAttribHealth, maxAttribHealth );
|
|
newAttribValueHealth *= hpMod;
|
|
|
|
setMaxAttrib(creature, HEALTH, (int)newAttribValueHealth );
|
|
setAttrib( creature, HEALTH, (int)newAttribValueHealth );
|
|
|
|
int avgAttribAction = dataTableGetInt(STAT_BALANCE_TABLE, statLevel - 1, diffClassName + "Action");
|
|
int minAttribAction = minAttribAction = (int)(avgAttribAction * 0.9f);
|
|
int maxAttribAction = maxAttribAction = (int)(avgAttribAction * 1.1f);
|
|
int newAttribValueAction = rand( minAttribAction, maxAttribAction);
|
|
setMaxAttrib(creature, ACTION, newAttribValueAction );
|
|
setAttrib( creature, ACTION, newAttribValueAction );
|
|
|
|
setMaxAttrib(creature, MIND, 1000 );
|
|
setAttrib( creature, MIND, 1000 );
|
|
|
|
//setRegenRates
|
|
int healthRegen = dataTableGetInt(STAT_BALANCE_TABLE, statLevel - 1, "HealthRegen" );
|
|
int actionRegen = dataTableGetInt(STAT_BALANCE_TABLE, statLevel - 1, "ActionRegen" );
|
|
int mindRegen = dataTableGetInt(STAT_BALANCE_TABLE, statLevel - 1, "MindRegen" );
|
|
|
|
setRegenRate(creature, CONSTITUTION, healthRegen); // CHANGES MADE 10/31/2005 to work with SJakab's new regen methods.
|
|
//setMaxAttrib(creature, CONSTITUTION, healthRegen );
|
|
setRegenRate(creature, STAMINA, healthRegen);
|
|
//setMaxAttrib(creature, STAMINA, actionRegen );
|
|
setRegenRate(creature, WILLPOWER, healthRegen);
|
|
//setMaxAttrib(creature, WILLPOWER, mindRegen );
|
|
|
|
float normalRegen = dataTableGetFloat(STAT_BALANCE_TABLE, statLevel - 1, diffClassName + "Regen" );
|
|
float combatRegen = dataTableGetFloat(STAT_BALANCE_TABLE, statLevel - 1, diffClassName + "CombatRegen" );
|
|
|
|
int xpValue = xp.getLevelBasedXP(level);
|
|
setObjVar( creature, "combat.intCombatXP", xpValue );
|
|
|
|
{
|
|
float runSpeed = ai_lib.AI_MAX_MOVEMENT_SPEED * aiGetMovementSpeedPercent(creature);
|
|
runSpeed *= speedMod;
|
|
setBaseRunSpeed(creature, runSpeed);
|
|
|
|
// Never let an ai walk at a speed faster than it can run
|
|
{
|
|
if (getBaseWalkSpeed(creature) > runSpeed)
|
|
{
|
|
setBaseWalkSpeed(creature, runSpeed);
|
|
}
|
|
}
|
|
|
|
LOGC(aiLoggingEnabled(creature), "debug_ai", ("create::initializeCreature() ai(" + creature + ") runSpeed(" + runSpeed + ")"));
|
|
}
|
|
|
|
applySkillStatisticModifiers( creature, toHitChance, defenseValue );
|
|
|
|
applyCreatureImmunities(creature, creatureDict);
|
|
|
|
setCreatureObjVars( creature, creatureDict.getString( "objvars" ) );
|
|
|
|
int pvpOnly = creatureDict.getInt("isSpecialForces");
|
|
|
|
string faction = creatureDict.getString( "pvpFaction" );
|
|
if (( faction != null ) && ( faction != "" ))
|
|
factions.setFaction( creature, faction, pvpOnly);
|
|
|
|
int invulnerable = creatureDict.getInt( "invulnerable" );
|
|
if ( invulnerable != 0 )
|
|
setInvulnerable( creature, true );
|
|
|
|
string diction = creatureDict.getString( "diction" );
|
|
if ( !diction.equals("none") )
|
|
setObjVar( creature, "ai.diction", diction );
|
|
|
|
int niche = creatureDict.getInt( "niche" );
|
|
|
|
//Set creatures spawned indoors to default sentinel
|
|
if (getTopMostContainer(creature) != creature)
|
|
ai_lib.setDefaultCalmBehavior(creature, ai_lib.BEHAVIOR_SENTINEL);
|
|
|
|
/*
|
|
if ( niche == NICHE_NPC || niche == NICHE_ANDROID)
|
|
{
|
|
string weaponList = creatureDict.getString( "weaponList" );
|
|
|
|
if (weaponList.equals(null) || weaponList.equals( "" ) )
|
|
dressup.equipNpcWeapon( creature, "pirate_medium" );
|
|
|
|
else if (!weaponList.equals("none") )
|
|
dressup.equipNpcWeapon( creature, weaponList );
|
|
}
|
|
*/
|
|
randomlyNameCreature( creature, creatureName );
|
|
|
|
//setup Attributes and Interests for trigger volume breaches
|
|
setAttributeAttained( creature, attrib.ALL);
|
|
switch ( niche )
|
|
{
|
|
case NICHE_HERBIVORE :
|
|
setAttributeAttained( creature, attrib.HERBIVORE );
|
|
setAttributeInterested( creature, attrib.CARNIVORE );
|
|
break;
|
|
case NICHE_CARNIVORE :
|
|
case NICHE_PREDATOR :
|
|
setAttributeAttained( creature, attrib.CARNIVORE );
|
|
setAttributeInterested( creature, attrib.HERBIVORE );
|
|
setAttributeInterested( creature, attrib.NPC );
|
|
break;
|
|
case NICHE_NPC :
|
|
setAttributeAttained( creature, attrib.NPC );
|
|
setAttributeInterested( creature, attrib.CARNIVORE );
|
|
setUpFactionalEnemyAttribs( creature, faction );
|
|
break;
|
|
case NICHE_ANDROID :
|
|
setAttributeAttained( creature, attrib.NPC );
|
|
setAttributeInterested( creature, attrib.CARNIVORE );
|
|
setUpFactionalEnemyAttribs( creature, faction );
|
|
break;
|
|
}
|
|
|
|
// SETUP OUR LOOT STUFF
|
|
|
|
string strLootTable = creatureDict.getString("lootTable");
|
|
int intRolls = creatureDict.getInt("intLootRolls");
|
|
int intPercentage = creatureDict.getInt("intRollPercent");
|
|
if(strLootTable!="")
|
|
{
|
|
LOG("npe", "table is "+strLootTable);
|
|
int intItems = 0;
|
|
for(int intI =0; intI < intRolls; intI++)
|
|
{
|
|
int intRand = rand(1, 99);
|
|
LOG("npe", "roll is "+intRand+" and +% is "+intPercentage);
|
|
if(intRand<intPercentage)
|
|
{
|
|
|
|
intItems++;
|
|
}
|
|
}
|
|
LOG("npe", "items is "+intItems);
|
|
|
|
setObjVar(creature, "loot.lootTable", strLootTable);
|
|
setObjVar(creature, "loot.numItems", intItems);
|
|
}
|
|
// add natural armor
|
|
if (!initializeArmor(creature, creatureDict, dataTableGetInt(STAT_BALANCE_TABLE, armorLevel - 1, diffClassName + "Armor")))
|
|
LOG( "create", "Unable to initialize armor on " + creatureName + "(" + creature + ")");
|
|
|
|
// Deprecated Armor Abstact Method
|
|
//string armor_abstract = "abstract/armor_statistics/armor/creature/statistics_armor_" + creatureName + ".iff";
|
|
//LOG("LOG_CHANNEL", "abstract ->" + armor_abstract);
|
|
//if (!addArmorLayer(creature, armor_abstract))
|
|
// LOG( "create", "Invalid armor abstract " + armor_abstract + " added to " + creature);
|
|
|
|
|
|
//this is a very hacky way of doing this
|
|
//Check for adding faction scripts
|
|
int stringCheck = creatureName.indexOf("recruiter");
|
|
if (stringCheck > -1)
|
|
{
|
|
string pvpFaction = creatureDict.getString( "pvpFaction" );
|
|
setFactionRecruiter(creature, pvpFaction);
|
|
}
|
|
|
|
//Creatures that can detect hidden
|
|
if(hasObjVar(creature, "detect_hidden"))
|
|
{
|
|
int detectInt = getIntObjVar(creature, "detect_hidden");
|
|
if(detectInt > 0)
|
|
{
|
|
applySkillStatisticModifier(creature, "detect_hidden", detectInt);
|
|
}
|
|
}
|
|
|
|
LOGC(aiLoggingEnabled(creature), "debug_ai", ("########## create::initializeCreature() END ai(" + creature + ") creatureName(" + creatureName + ") ##########"));
|
|
}
|
|
|
|
int calcCreatureLevel(string creatureName)
|
|
{
|
|
dictionary creatureDict = utils.dataTableGetRow( CREATURE_TABLE, creatureName );
|
|
if ( creatureDict == null )
|
|
{
|
|
debugServerConsoleMsg( null, "WARNING: Unable to find invalid creatureType: " + creatureName );
|
|
return -1;
|
|
}
|
|
|
|
return calcCreatureLevel(creatureDict);
|
|
}
|
|
|
|
int calcCreatureLevel(dictionary creatureDict)
|
|
{
|
|
// Get level settings
|
|
int baseLevel = creatureDict.getInt("BaseLevel");
|
|
int dmgLevel = baseLevel + creatureDict.getInt("Damagelevelmodifier");
|
|
int statLevel = baseLevel + creatureDict.getInt("StatLevelModifier");
|
|
int toHitLevel = baseLevel + creatureDict.getInt("ToHitLevelModifier");
|
|
int armorLevel = baseLevel + creatureDict.getInt("ArmorLevelModifier");
|
|
|
|
// Cap levels
|
|
if (statLevel <= 0) statLevel = 1;
|
|
if (dmgLevel <= 0) dmgLevel = 1;
|
|
if (toHitLevel <= 0) toHitLevel = 1;
|
|
if (armorLevel <= 0) armorLevel = 1;
|
|
|
|
return calcCreatureLevel(statLevel, dmgLevel, toHitLevel, armorLevel);
|
|
}
|
|
|
|
int calcCreatureLevel(int statLevel, int dmgLevel, int toHitLevel, int armorLevel)
|
|
{
|
|
int level = 0;
|
|
|
|
// Compute final level on level settings
|
|
{
|
|
// This weighted average must match up with BE clone level calcs
|
|
level = (int)(((statLevel*6) + (dmgLevel*6) + (toHitLevel*2) + (armorLevel*4))/18f);
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
/**
|
|
* Scripts are stored in the format:
|
|
* scriptname,scriptname,scriptname
|
|
*/
|
|
void attachCreatureScripts( obj_id creature, string scriptList, boolean withAi )
|
|
{
|
|
if ( getConfigSetting("GameServer", "disableAI")!=null )
|
|
return;
|
|
|
|
//Everyone gets the AI script:
|
|
if ( withAi )
|
|
{
|
|
//always attach ai.ai first, and cerature combat scripts second:
|
|
attachScript( creature, "ai.ai" );
|
|
attachCombatScripts( creature );
|
|
attachScript(creature, "systems.skills.stealth.player_stealth");
|
|
//grantAllAbilities( creature );
|
|
}
|
|
|
|
if ( scriptList == null || scriptList == "" )
|
|
return;
|
|
|
|
string[] scriptArray = split(scriptList, ',' );
|
|
for ( int i = 0; i < scriptArray.length; i++ )
|
|
{
|
|
attachScript( creature, scriptArray[i] );
|
|
}
|
|
}
|
|
|
|
boolean initializeArmor(obj_id creature, dictionary creatureDict)
|
|
{
|
|
int armorLevel = creatureDict.getInt("BaseLevel") + creatureDict.getInt("ArmorLevelModifier");
|
|
|
|
int difficultyClass = creatureDict.getInt("difficultyClass");
|
|
if (difficultyClass < 0) difficultyClass = 0;
|
|
|
|
string diffClassName = "";
|
|
if (difficultyClass == 1) diffClassName = "Elite_";
|
|
if (difficultyClass == 2) diffClassName = "Boss_";
|
|
|
|
int armorRating = dataTableGetInt(STAT_BALANCE_TABLE, armorLevel - 1, diffClassName + "Armor");
|
|
|
|
return initializeArmor(creature, creatureDict, armorRating);
|
|
}
|
|
|
|
boolean initializeArmor(obj_id creature, dictionary creatureDict, int armorRating)
|
|
{
|
|
int[] armorData = new int[10];
|
|
|
|
armorData[0] = 0;
|
|
armorData[1] = armorRating;
|
|
armorData[2] = creatureDict.getInt("armorKinetic");
|
|
armorData[3] = creatureDict.getInt("armorEnergy");
|
|
armorData[4] = creatureDict.getInt("armorBlast");
|
|
armorData[5] = creatureDict.getInt("armorHeat");
|
|
armorData[6] = creatureDict.getInt("armorCold");
|
|
armorData[7] = creatureDict.getInt("armorElectric");
|
|
armorData[8] = creatureDict.getInt("armorAcid");
|
|
armorData[9] = creatureDict.getInt("armorStun");
|
|
|
|
return initializeArmor(creature, armorData);
|
|
}
|
|
|
|
boolean initializeArmor(obj_id creature, int[] armorData)
|
|
{
|
|
int armorRating = armorData[0];
|
|
int armorEffectiveness = armorData[1];
|
|
int armorKinetic = armorData[2];
|
|
int armorEnergy = armorData[3];
|
|
int armorBlast = armorData[4];
|
|
int armorHeat = armorData[5];
|
|
int armorCold = armorData[6];
|
|
int armorElectric = armorData[7];
|
|
int armorAcid = armorData[8];
|
|
int armorStun = armorData[9];
|
|
|
|
boolean success = true;
|
|
int vulnerability = DAMAGE_RESTRAINT;
|
|
|
|
// Clear out any existing armor data
|
|
armor.removeAllArmorData(creature);
|
|
|
|
// Set base armor info
|
|
string armorCategoryObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_ARMOR_CATEGORY;
|
|
setObjVar(creature, armorCategoryObjVar, AC_battle);
|
|
|
|
string armorLevelObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_ARMOR_LEVEL;
|
|
setObjVar(creature, armorLevelObjVar, AL_standard);
|
|
|
|
string genProtectionObjVar = armor.OBJVAR_ARMOR_BASE + "." + armor.OBJVAR_GENERAL_PROTECTION;
|
|
setObjVar(creature, genProtectionObjVar, (float)armorEffectiveness);
|
|
utils.setScriptVar(creature, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION, armorEffectiveness);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Creature Objvars are stored in the format:
|
|
* int:name=value,string:name=value,float:name=value
|
|
*/
|
|
void setCreatureObjVars( obj_id creature, string objVarList )
|
|
{
|
|
//moved this to utils for other script use as well
|
|
utils.setObjVarsList(creature, objVarList);
|
|
}
|
|
|
|
/**
|
|
* Called to get the STRING difficulty of a lair based on the min and max difficulty
|
|
*
|
|
* @param intMinDifficulty the minimum difficulty of the lair
|
|
* @param intMaxDifficulty the max difficulty of the lair
|
|
* @param intPlayerDifficulty the player's difficulty level
|
|
*
|
|
* @returns the string value of the lair difficulty, "veryEasy", "easy", "medium", "hard" "veryHard"
|
|
*/
|
|
string getLairDifficulty(int intMinDifficulty, int intMaxDifficulty, int intPlayerDifficulty)
|
|
{
|
|
// we need to check where the spawned difficulty falls in terms of the actual ranges.
|
|
// based on that, we'll return a string
|
|
// there are 5 sizes
|
|
// veryEasy, easy, medium, hard, veryHard
|
|
// every 20 % gives us these
|
|
const string[] LAIR_DIFFICULTIES = {"veryEasy", "easy", "medium", "hard", "veryHard"};
|
|
|
|
// normalize the numbers
|
|
int intDifference = intMaxDifficulty - intMinDifficulty;
|
|
intPlayerDifficulty = intPlayerDifficulty - intMinDifficulty;
|
|
|
|
// now we're normalzied to a 0 scale
|
|
if(intDifference==0)
|
|
{
|
|
intDifference = 1;
|
|
}
|
|
float fltPercent = (float)(intPlayerDifficulty / intDifference);
|
|
fltPercent = fltPercent * 100;
|
|
int intIndex = (int)(fltPercent / 20);
|
|
// normalize to 0 through 4)
|
|
intIndex = intIndex - 1;
|
|
if(intIndex<0)
|
|
{
|
|
intIndex = 0;
|
|
}
|
|
if (intIndex >= LAIR_DIFFICULTIES.length)
|
|
{
|
|
intIndex = LAIR_DIFFICULTIES.length - 1;
|
|
}
|
|
|
|
return LAIR_DIFFICULTIES[intIndex];
|
|
}
|
|
|
|
/**
|
|
* Grants npcs all the abilities they need to perform combat skills:
|
|
*
|
|
* @params npc the npc
|
|
*
|
|
* @returns nothing
|
|
*/
|
|
void grantAllAbilities( obj_id npc )
|
|
{
|
|
return;
|
|
/** removing this call because NPCs don't need to be granted abilities:
|
|
|
|
grantCommand(npc, "combatAttitudeEvasive");
|
|
grantCommand(npc, "combatAttitudeNeutral");
|
|
grantCommand(npc, "combatAttitudeAggressive");
|
|
|
|
int niche = ai_lib.aiGetNiche( npc );
|
|
if ( niche != NICHE_NPC )
|
|
return;
|
|
|
|
grantCommand(npc, "aim");
|
|
grantCommand(npc, "rifleAim");
|
|
grantCommand(npc, "pistolAim");
|
|
grantCommand(npc, "carbineAim");
|
|
grantCommand(npc, "unarmedAim");
|
|
grantCommand(npc, "fullAutoSingle");
|
|
grantCommand(npc, "fullAutoArea");
|
|
grantCommand(npc, "headShot");
|
|
grantCommand(npc, "bodyShot");
|
|
grantCommand(npc, "legShot");
|
|
grantCommand(npc, "disarmingShot");
|
|
grantCommand(npc, "suppressionFire");
|
|
grantCommand(npc, "startle");
|
|
grantCommand(npc, "strafe");
|
|
grantCommand(npc, "flurry");
|
|
grantCommand(npc, "diveShot");
|
|
grantCommand(npc, "rollShot");
|
|
grantCommand(npc, "kipUpShot");
|
|
grantCommand(npc, "rescue");
|
|
grantCommand(npc, "wildShot");
|
|
grantCommand(npc, "surpriseShot");
|
|
grantCommand(npc, "charge");
|
|
grantCommand(npc, "berserk");
|
|
grantCommand(npc, "intimidate");
|
|
grantCommand(npc, "warcry");
|
|
grantCommand(npc, "takeCover");
|
|
grantCommand(npc, "tumbleToProne");
|
|
grantCommand(npc, "tumbleToKneeling");
|
|
grantCommand(npc, "tumbleToStanding");
|
|
//grantCommand(npc, "sneak");
|
|
grantCommand(npc, "alert");
|
|
//grantCommand(npc, "block");
|
|
grantCommand(npc, "feignIncapacitation");
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Attaches the appropriate combat script for an npc, based on the npc's skeleton
|
|
*
|
|
* @params npc the npc to attach the scripts to
|
|
*
|
|
* @returns nothing
|
|
*/
|
|
void attachCombatScripts( obj_id npc )
|
|
{
|
|
if ( !hasScript( npc, "ai.creature_combat") )
|
|
attachScript( npc, "ai.creature_combat" );
|
|
|
|
if (!hasScript(npc, "systems.combat.combat_actions"))
|
|
attachScript(npc, "systems.combat.combat_actions");
|
|
|
|
|
|
obj_id primaryWeapon = aiGetPrimaryWeapon(npc);
|
|
obj_id secondaryWeapon = aiGetSecondaryWeapon(npc);
|
|
boolean hasJediWeapon = false;
|
|
|
|
if (isIdValid(primaryWeapon))
|
|
{
|
|
if (jedi.isLightsaber(primaryWeapon))
|
|
hasJediWeapon = true;
|
|
}
|
|
|
|
if (isIdValid(secondaryWeapon))
|
|
{
|
|
if (jedi.isLightsaber(secondaryWeapon))
|
|
hasJediWeapon = true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Applies the toHitPercent to all the creature's skill statistics:
|
|
*
|
|
* @params creature the obj_id of the creature
|
|
* @params toHitChance the float indicating the creatures chance to hit
|
|
*
|
|
* @returns nothing
|
|
*/
|
|
void applySkillStatisticModifiers( obj_id creature, int toHitChance, int defenseValue )
|
|
{
|
|
applySkillStatisticModifier(creature, "rifle_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "toHitChance", toHitChance);
|
|
applySkillStatisticModifier(creature, "carbine_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "pistol_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "heavyweapon_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "onehandmelee_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "twohandmelee_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "unarmed_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "polearm_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "thrown_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "onehandlightsaber_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "twohandlightsaber_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "polearmlightsaber_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "force_accuracy", toHitChance);
|
|
applySkillStatisticModifier(creature, "ranged_defense", defenseValue);
|
|
applySkillStatisticModifier(creature, "melee_defense", defenseValue);
|
|
}
|
|
|
|
void applyCreatureImmunities(obj_id creature, dictionary creatureDict)
|
|
{
|
|
int rootImmune = creatureDict.getInt("rootImmune");
|
|
if (rootImmune > 0)
|
|
{
|
|
applySkillStatisticModifier(creature, "movement_resist_root", rootImmune);
|
|
}
|
|
|
|
int snareImmune = creatureDict.getInt("snareImmune");
|
|
if (snareImmune > 0)
|
|
{
|
|
applySkillStatisticModifier(creature, "movement_resist_snare", snareImmune);
|
|
}
|
|
|
|
int stunImmune = creatureDict.getInt("stunImmune");
|
|
if (stunImmune > 0)
|
|
{
|
|
applySkillStatisticModifier(creature, "movement_resist_stun", stunImmune);
|
|
}
|
|
|
|
int mezImmune = creatureDict.getInt("mezImmune");
|
|
if (mezImmune > 0)
|
|
{
|
|
applySkillStatisticModifier(creature, "movement_resist_mez", mezImmune);
|
|
}
|
|
|
|
int armorBreakImmune = creatureDict.getInt("canNotPunish");
|
|
if (armorBreakImmune > 0)
|
|
{
|
|
utils.setScriptVar(creature, "combat.immune.no_punish", 1);
|
|
}
|
|
int tauntImmune = creatureDict.getInt("tauntImmune");
|
|
if (tauntImmune > 0)
|
|
{
|
|
utils.setScriptVar(creature, "combat.immune.taunt", 1);
|
|
}
|
|
|
|
int ignorePlayer = creatureDict.getInt("ignorePlayer");
|
|
if (ignorePlayer > 0)
|
|
{
|
|
utils.setScriptVar(creature, "combat.immune.taunt", 1);
|
|
factions.setIgnorePlayer(creature);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a destroy message to a creature or object which is sent to
|
|
* the recipient when the creature is destroyed
|
|
*
|
|
* The message is sent by the object.destroy_message script, and is a
|
|
* persisted message if the recipient is a persisted object
|
|
*
|
|
* @params creature the creature that should send the message
|
|
* @params handlerName the messageHandlerName which should be sent
|
|
* @params delay the float delay for delivery of the message
|
|
* @params recipient the obj_id of the object to be messaged.
|
|
*
|
|
* @returns nothing
|
|
*/
|
|
void addDestroyMessage( obj_id creature, string handlerName, float delay, obj_id recipient )
|
|
{
|
|
if ( !isIdValid(creature) )
|
|
return;
|
|
|
|
if ( !hasScript( creature, "object.destroy_message" ))
|
|
attachScript( creature, "object.destroy_message" );
|
|
|
|
resizeable string[] destroyMessageNames = new string[0];
|
|
resizeable float[] destroyMessageDelays = new float[0];
|
|
resizeable obj_id[] destroyMessageRecipients = new obj_id[0];
|
|
|
|
if ( hasObjVar( creature, "destroyMessageList" ))
|
|
{
|
|
destroyMessageNames = getResizeableStringArrayObjVar( creature, "destroyMessageList.handlerNames" );
|
|
destroyMessageDelays = getResizeableFloatArrayObjVar( creature, "destroyMessageList.delays" );
|
|
destroyMessageRecipients = getResizeableObjIdArrayObjVar( creature, "destroyMessageList.recipients" );
|
|
}
|
|
|
|
destroyMessageNames = utils.addElement( destroyMessageNames, handlerName );
|
|
destroyMessageDelays = utils.addElement( destroyMessageDelays, delay );
|
|
destroyMessageRecipients = utils.addElement( destroyMessageRecipients, recipient );
|
|
|
|
if(((destroyMessageNames != null) && (destroyMessageNames.length > 0)) && ((destroyMessageDelays != null) && (destroyMessageDelays.length > 0)) &&
|
|
((destroyMessageRecipients != null) && (destroyMessageRecipients.length > 0))) { // Zero length array check
|
|
|
|
setObjVar(creature, "destroyMessageList.handlerNames", destroyMessageNames );
|
|
setObjVar(creature, "destroyMessageList.delays", destroyMessageDelays );
|
|
setObjVar(creature, "destroyMessageList.recipients", destroyMessageRecipients );
|
|
}
|
|
}
|
|
|
|
void randomlyNameCreature( obj_id npc, string creatureName )
|
|
{
|
|
|
|
string templateName = getTemplateName( npc );
|
|
if ( templateName.indexOf("stormtrooper") != -1 )
|
|
{
|
|
nameStormTrooper( npc );
|
|
// return;
|
|
}
|
|
|
|
if ( templateName.indexOf("swamp_trooper") != -1 )
|
|
{
|
|
nameSwampTrooper( npc );
|
|
// return;
|
|
}
|
|
|
|
if ( templateName.indexOf("scout_trooper") != -1 )
|
|
{
|
|
nameScoutTrooper( npc );
|
|
// return;
|
|
}
|
|
if ( templateName.indexOf("tie_fighter") != -1 )
|
|
{
|
|
nameImperialPilot( npc );
|
|
// return;
|
|
}
|
|
if ( templateName.indexOf("dark_trooper") != -1 )
|
|
{
|
|
nameDarkTrooper( npc );
|
|
// return;
|
|
}
|
|
|
|
string oldName = getAssignedName( npc );
|
|
string_id nameId = new string_id( CREATURE_NAME_FILE, creatureName );
|
|
|
|
string actualName = getString(nameId);
|
|
if (actualName == null || actualName == "")
|
|
{
|
|
setTitle( npc, creatureName );
|
|
return;
|
|
}
|
|
|
|
setName( npc, "" );
|
|
setName( npc, nameId );
|
|
}
|
|
|
|
void setTitle( obj_id npc, string creatureName )
|
|
{
|
|
string_id nameId = new string_id( CREATURE_NAME_FILE, creatureName );
|
|
if ( nameId == null )
|
|
return;
|
|
|
|
string newName = getString( nameId );
|
|
if ( newName == null || newName == "" )
|
|
return;
|
|
|
|
string oldName = getAssignedName( npc );
|
|
string oldNameId = getString( getNameStringId( npc ));
|
|
if ( oldName == oldNameId )
|
|
{
|
|
setName( npc, "" );
|
|
setName( npc, nameId );
|
|
}
|
|
else if ( oldName == null || oldName == "" )
|
|
{
|
|
setName( npc, "" );
|
|
setName( npc, nameId );
|
|
}
|
|
else
|
|
{
|
|
setName( npc, oldName + " (" + getString(nameId) + ")" );
|
|
}
|
|
}
|
|
|
|
void nameStormTrooper( obj_id npc )
|
|
{
|
|
int designation = rand (1,5);
|
|
string StName = "TK-";
|
|
switch (designation)
|
|
{
|
|
case 1:
|
|
StName = "TK-";
|
|
break;
|
|
case 2:
|
|
StName = "GK-";
|
|
break;
|
|
case 3:
|
|
StName = "RK-";
|
|
break;
|
|
case 4:
|
|
StName = "LK-";
|
|
break;
|
|
case 5:
|
|
StName = "VK-";
|
|
break;
|
|
}
|
|
setName (npc, StName + rand(1,820));
|
|
}
|
|
|
|
void nameImperialPilot( obj_id npc )
|
|
{
|
|
setName (npc, "DS-"+ rand(1,820));
|
|
}
|
|
|
|
|
|
void nameScoutTrooper( obj_id npc )
|
|
{
|
|
int designation = rand (1,5);
|
|
string StName = "TK-";
|
|
switch (designation)
|
|
{
|
|
case 1:
|
|
StName = "SX-";
|
|
break;
|
|
case 2:
|
|
StName = "GX-";
|
|
break;
|
|
case 3:
|
|
StName = "VX-";
|
|
break;
|
|
case 4:
|
|
StName = "CX-";
|
|
break;
|
|
case 5:
|
|
StName = "NX-";
|
|
break;
|
|
}
|
|
setName (npc, StName + rand(1,820));
|
|
}
|
|
|
|
void nameDarkTrooper( obj_id npc )
|
|
{
|
|
int designation = rand (1,5);
|
|
string StName = "DLX-";
|
|
switch (designation)
|
|
{
|
|
case 1:
|
|
StName = "JLB-";
|
|
break;
|
|
case 2:
|
|
StName = "RAR-";
|
|
break;
|
|
case 3:
|
|
StName = "KNP-";
|
|
break;
|
|
case 4:
|
|
StName = "BCP-";
|
|
break;
|
|
case 5:
|
|
StName = "RTZ-";
|
|
break;
|
|
}
|
|
setName (npc, StName + rand(10,99));
|
|
}
|
|
|
|
|
|
void nameSwampTrooper( obj_id npc )
|
|
{
|
|
int designation = rand (1,5);
|
|
string StName = "TK-";
|
|
switch (designation)
|
|
{
|
|
case 1:
|
|
StName = "GL-";
|
|
break;
|
|
case 2:
|
|
StName = "TL-";
|
|
break;
|
|
case 3:
|
|
StName = "RL-";
|
|
break;
|
|
case 4:
|
|
StName = "NL-";
|
|
break;
|
|
case 5:
|
|
StName = "CL-";
|
|
break;
|
|
}
|
|
setName (npc, StName + rand(1,820));
|
|
}
|
|
|
|
|
|
boolean setFactionRecruiter(obj_id npc, string faction)
|
|
{
|
|
|
|
// Turns any npc into a faction recruiter of the specified faction.
|
|
// Faction must appear exactly as it does in the faction datatable.
|
|
|
|
if (npc == null || npc == obj_id.NULL_ID)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (faction == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
setObjVar(npc, "faction_recruiter.faction", faction);
|
|
attachScript(npc, "npc.faction_recruiter.faction_recruiter");
|
|
return true;
|
|
|
|
}
|
|
|
|
void setUpFactionalEnemyAttribs( obj_id npc, string pvpFaction )
|
|
{
|
|
if ( pvpFaction == null )
|
|
return;
|
|
|
|
if ( pvpFaction == "" )
|
|
return;
|
|
|
|
//NPCs with factional enemies need to be able to 'see' each other:
|
|
setAttributeAttained( npc, attrib.NPC );
|
|
setAttributeInterested( npc, attrib.NPC );
|
|
}
|
|
|
|
|
|
void cleanOffBioLink(obj_id weapon)
|
|
{
|
|
detachScript(weapon, BIOLINK_SCRIPT);
|
|
removeObjVar(weapon, "biolink");
|
|
return;
|
|
}
|