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; } /* ---------------------------------- * 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; } /* --------------------------------- * 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; float walkSpeed = 2.0f; setBaseRunSpeed(creature, runSpeed); setBaseWalkSpeed(creature, walkSpeed); // 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" + 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; }