include library.badge; include library.beast_lib; include library.buff; include library.chat; include library.combat_consts; include library.dot; include library.factions; include library.locations; include library.movement; include library.pet_lib; include library.skill; include library.utils; const float PLAYER_RUN_SPEED = 8f; const float AI_MAX_MOVEMENT_SPEED = PLAYER_RUN_SPEED; const string ALERT_VOLUME_NAME = "alertTriggerVolume"; const string AGGRO_VOLUME_NAME = "aggroTriggerVolume"; const string MOOD_CALM = "calm"; const string MOOD_NERVOUS = "nervous"; const string MOOD_THREATEN = "threaten"; const int BEHAVIOR_WANDER = 0;//zero is always default behavior const int BEHAVIOR_SENTINEL = 1; const int BEHAVIOR_LOITER = 2; const int BEHAVIOR_STOP = 3; const string DIF_VERY_EASY = "veryEasy"; const string DIF_EASY = "easy"; const string DIF_MEDIUM = "medium"; const string DIF_HARD = "hard"; const string DIF_VERY_HARD = "veryHard"; const string ALLY_LIST = "allyList"; const string CREATURE_TABLE = "datatables/mob/creatures.iff"; const string CREATURE_NAME_FILE = "mob/creature_names"; const string ACTION_ALERT = "alert"; const string ACTION_THREATEN = "threaten"; const string_id SMUGGLER_SCAN_SUCCESS = new string_id("base_player","smuggler_scan_success"); const string_id SMUGGLER_SCAN_FAIL = new string_id("base_player","smuggler_scan_fail"); const float DEFAULT_FOLLOW_MIN = 4f; const float DEFAULT_FOLLOW_MAX = 8f; const string OBJVAR_ATTACKABLE_OVERRIDE = "ai_combat.attackable"; const string SCRIPTVAR_CACHED_PATROL_PATH = "ai.patrolPath"; const string SCRIPTVAR_CACHED_PATROL_NAMED_PATH = "ai.patrolNamedPath"; const string SCRIPTVAR_CACHED_PATROL_TYPE = "ai.patrolType"; const int PATROL_FLAG_RANDOM = 0x0001; const int PATROL_FLAG_FLIP = 0x0002; const int PATROL_FLAG_REPEAT = 0x0004; const int FORMATION_COLUMN = 0; const int FORMATION_WEDGE = 1; const int FORMATION_LINE = 2; const int FORMATION_BOX = 3; const int DIFFICULTY_NORMAL = 0; const int DIFFICULTY_ELITE = 1; const int DIFFICULTY_BOSS = 2; const string SHARED_HEALTH_LIST = "shared_health_list"; boolean isWithinLeash(obj_id ai) { const float distanceToHome = getDistance(getLocation(ai), aiGetLeashAnchorLocation(ai)); const float leashRadius = aiGetLeashRadius(); return (distanceToHome <= leashRadius); } void resetAi() { const obj_id self = getSelf(); LOGC(aiLoggingEnabled(self), "debug_ai", ("ai_lib::resetAi() --- BEGIN RESET AI --- self(" + self + ") getName(" + getName(self) + ")")); setMovementWalk(self); movement.refresh(self); resetCombatTriggerVolumes(); // The delay in this messageTo is to allow the object to complete creation messageTo(self, "resumeDefaultCalmBehavior", null, 1, false); LOGC(aiLoggingEnabled(self), "debug_ai", ("ai_lib::resetAi() --- END RESET AI --- self(" + self + ") getName(" + getName(self) + ")")); } void resetCombatTriggerVolumes() { const obj_id self = getSelf(); LOGC(aiLoggingEnabled(self), "debug_ai", ("ai_lib::resetCombatTriggerVolumes() self(" + self + ") getName(" + getName(self) + ")")); // Invulnerable AI or non-aggressive animals never need combat trigger volumes boolean needsCombatTriggerVolumes = true; if (isInvulnerable(self)) { needsCombatTriggerVolumes = false; } else if (isAnimal(self) && !aiIsAggressive(self)) { needsCombatTriggerVolumes = false; } else if ( isVehicleMine(self) ) { needsCombatTriggerVolumes = false; } else if (beast_lib.isBeast(self)) { needsCombatTriggerVolumes = false; } if (needsCombatTriggerVolumes) { // Create ALERT trigger volume { const boolean promiscuous = true; const float alertRadius = 64.0f; createTriggerVolume(ai_lib.ALERT_VOLUME_NAME, alertRadius, promiscuous); } // Create AGGRO trigger volume, AI needs an aggro trigger volume because they could be factionally aggressive towards something { const boolean promiscuous = false; const float aggroRadius = aiGetAggroRadius(self); createTriggerVolume(ai_lib.AGGRO_VOLUME_NAME, aggroRadius, promiscuous); } } } void clearCombatData() { const obj_id self = getSelf(); clearHateList(self); queueClear(self); pclib.clearCombatData(self); movement.removeAllModifiers(self); buff.removeAllBuffs(self); dot.removeAllDots(self); removeTriggerVolume(ai_lib.ALERT_VOLUME_NAME); removeTriggerVolume(ai_lib.AGGRO_VOLUME_NAME); } boolean isInCombat(obj_id ai) { return (getState(ai, STATE_COMBAT) == 1); } void setAttackable( obj_id npc, boolean val) { if(val) setObjVar(npc, OBJVAR_ATTACKABLE_OVERRIDE, 1); else if(hasObjVar(npc, OBJVAR_ATTACKABLE_OVERRIDE)) removeObjVar(npc, OBJVAR_ATTACKABLE_OVERRIDE); } boolean isAttackable( obj_id npc ) { return hasObjVar(npc, OBJVAR_ATTACKABLE_OVERRIDE); } //obj_id getBehaviorTarget( obj_id npc, int behavior ) //{ // obj_id[] allTargets = getBehaviorTargets( npc, behavior ); // obj_id bestTarget = null; // // if ( allTargets == null || allTargets.length == 0 ) // return null; // // for ( int i = 0; i < allTargets.length; i++ ) // { // if (isInWorld( allTargets[i] ) && !ai_lib.isAiDead( allTargets[i] ) && getDistance( npc, allTargets[i] ) < combat_engine.getMaxCombatRange() ) // return allTargets[i]; // } // //debugSpeakMsg( npc, "all my targets were incapacitated" ); // return bestTarget; //} void setMood( obj_id npc, string mood ) { //debugSpeakMsg( npc, "SETTING MOOD : " + mood ); obj_id thisNpc = getSelf(); if ( thisNpc != npc ) { //debugSpeakMsg( npc, "sending a callback" ); dictionary parms = new dictionary(); parms.put( "animMood", mood ); messageTo( npc, "handleAnimationAction", parms, 1, false ); return; } if ( ai_lib.isAiDead( npc ) ) { //debugSpeakMsg( npc, "i am dead" ); return; } //debugSpeakMsg( npc, "mood: " + mood ); if ( mood == MOOD_CALM ) { //debugSpeakMsg( npc, "calm" ); removeObjVar( npc, "ai.mood" ); } else { //debugSpeakMsg( npc, "mood " + mood ); setObjVar( npc, "ai.mood", mood ); } if ( mood == MOOD_THREATEN ) { //debugSpeakMsg( npc, "threaten!" ); chat.setAngryMood( npc ); ai_lib.barkString( npc, "threat" ); } else if ( mood == MOOD_NERVOUS ) { //debugSpeakMsg( npc, "alert!" ); chat.setBadMood( npc ); ai_lib.barkString( npc, "alert" ); } if ( mood == MOOD_CALM && hasObjVar( npc, "ai.defaultCalmMood" ) ) { //debugSpeakMsg( npc, "calm and has objvar" ); mood = getStringObjVar( npc, "ai.defaultCalmMood" ); //debugSpeakMsg( npc, "animood is " + mood); } else { //debugSpeakMsg( npc, "no objvar or not calm so setting mood " + mood ); } setAnimationMood( npc, mood ); } void doAction( obj_id npc, string anim ) { obj_id thisNpc = getSelf(); if ( thisNpc != npc ) { dictionary parms = new dictionary(); parms.put( "anim", anim ); messageTo( npc, "handleAnimationAction", parms, 1, false ); return; } if ( ai_lib.isAiDead( npc ) ) return; //debugSpeakMsg( npc, "doing anim " + anim ); int npcPosture = getPosture( npc ); if ( npcPosture != POSTURE_UPRIGHT ) { if ( npcPosture != POSTURE_KNOCKED_DOWN ) posture.stand(npc); //debugSpeakMsg( npc, "stopping to stand so I can animate" ); removeObjVar( npc, "ai.combat.moveMode" ); } else { doAnimationAction( npc, anim ); } } void setPatrolPath( obj_id npc, string[] waypoints ) { setPatrolPath(npc, waypoints, 0); } void setPatrolPath( obj_id npc, string[] waypoints, int startPoint ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolPath(npc, patrolLocs, startPoint); } } void setPatrolOncePath( obj_id npc, string[] waypoints ) { setPatrolOncePath(npc, waypoints, 0); } void setPatrolOncePath( obj_id npc, string[] waypoints, int startPoint ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolOncePath(npc, patrolLocs, startPoint); } } void setPatrolRandomPath( obj_id npc, string[] waypoints ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolRandomPath(npc, patrolLocs); } } void setPatrolRandomOncePath( obj_id npc, string[] waypoints ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolRandomOncePath(npc, patrolLocs); } } void setPatrolFlipPath( obj_id npc, string[] waypoints ) { setPatrolFlipPath(npc, waypoints, 0); } void setPatrolFlipPath( obj_id npc, string[] waypoints, int startPoint ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolFlipPath(npc, patrolLocs, startPoint); } } void setPatrolFlipOncePath( obj_id npc, string[] waypoints ) { setPatrolFlipOncePath(npc, waypoints, 0); } void setPatrolFlipOncePath( obj_id npc, string[] waypoints, int startPoint ) { if ( isIdValid(npc) && waypoints != null ) { location[] patrolLocs = parseWaypoints( npc, waypoints ); if ( patrolLocs != null ) setPatrolFlipOncePath(npc, patrolLocs, startPoint); } } void setPatrolPath( obj_id npc, location[] patrolLoc ) { setPatrolPath(npc, patrolLoc, 0); } void setPatrolPath( obj_id npc, location[] patrolLoc, int startPoint ) { LOGC(aiLoggingEnabled(npc), "debug_ai", ("ai_lib::setPatrolPath() self(" + npc + ") getName(" + getName(npc) + ") patrolLength(" + patrolLoc + ")")); if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_REPEAT); patrol(npc, patrolLoc, startPoint); } } void setPatrolOncePath(obj_id npc, location[] patrolLoc) { setPatrolOncePath(npc, patrolLoc, 0); } void setPatrolOncePath(obj_id npc, location[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, 0); patrolOnce(npc, patrolLoc, startPoint); } } void setPatrolRandomPath(obj_id npc, location[] patrolLoc) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_RANDOM | PATROL_FLAG_REPEAT); patrolRandom(npc, patrolLoc); } } void setPatrolRandomOncePath(obj_id npc, location[] patrolLoc) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_RANDOM); patrolRandomOnce(npc, patrolLoc); } } void setPatrolFlipPath(obj_id npc, location[] patrolLoc) { setPatrolFlipPath(npc, patrolLoc, 0); } void setPatrolFlipPath(obj_id npc, location[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_FLIP | PATROL_FLAG_REPEAT); patrolFlip(npc, patrolLoc, startPoint); } } void setPatrolFlipOncePath(obj_id npc, location[] patrolLoc) { setPatrolFlipOncePath(npc, patrolLoc, 0); } void setPatrolFlipOncePath(obj_id npc, location[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_FLIP); patrolFlipOnce(npc, patrolLoc, startPoint); } } void setPatrolNamedPath( obj_id npc, string[] patrolLoc ) { setPatrolNamedPath(npc, patrolLoc, 0); } void setPatrolNamedPath( obj_id npc, string[] patrolLoc, int startPoint ) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_REPEAT); patrol(npc, patrolLoc, startPoint); } } void setPatrolOnceNamedPath(obj_id npc, string[] patrolLoc) { setPatrolOnceNamedPath(npc, patrolLoc, 0); } void setPatrolOnceNamedPath(obj_id npc, string[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, 0); patrolOnce(npc, patrolLoc, startPoint); } } void setPatrolRandomNamedPath(obj_id npc, string[] patrolLoc) { if ( isIdValid(npc) && patrolLoc != null ) { if ( !exists(npc) ) { LOG("debug_ai", "WARNING: ai_lib.setPatrolRandomNamedPath passed npc id " + npc + " that doesn't exist"); return; } else if ( !isGameObjectTypeOf(npc, GOT_creature) ) { LOG("debug_ai", "WARNING: ai_lib.setPatrolRandomNamedPath passed npc id " + npc + " that is not a creature. "+ "Object template = " + getTemplateName(npc)); } utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_RANDOM | PATROL_FLAG_REPEAT); patrolRandom(npc, patrolLoc); } } void setPatrolRandomOnceNamedPath(obj_id npc, string[] patrolLoc) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_RANDOM); patrolRandomOnce(npc, patrolLoc); } } void setPatrolFlipNamedPath(obj_id npc, string[] patrolLoc) { setPatrolFlipNamedPath(npc, patrolLoc, 0); } void setPatrolFlipNamedPath(obj_id npc, string[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_FLIP | PATROL_FLAG_REPEAT); patrolFlip(npc, patrolLoc, startPoint); } } void setPatrolFlipOnceNamedPath(obj_id npc, string[] patrolLoc) { setPatrolFlipOnceNamedPath(npc, patrolLoc, 0); } void setPatrolFlipOnceNamedPath(obj_id npc, string[] patrolLoc, int startPoint) { if ( isIdValid(npc) && patrolLoc != null ) { utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH, patrolLoc); utils.setScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE, PATROL_FLAG_FLIP); patrolFlipOnce(npc, patrolLoc, startPoint); } } void clearPatrolPath(obj_id npc) { if ( isIdValid(npc) ) { utils.removeScriptVar(npc, SCRIPTVAR_CACHED_PATROL_PATH); utils.removeScriptVar(npc, SCRIPTVAR_CACHED_PATROL_NAMED_PATH); utils.removeScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE); } } void resumePatrol( obj_id npc ) { LOGC(aiLoggingEnabled(npc), "debug_ai", ("ai_lib::resumePatrol() self(" + npc + ") getName(" + getName(npc) + ")")); if ( isIdValid(npc) && aiGetMovementState(npc) != MOVEMENT_PATROL && hasSuspendedMovement(npc) ) { resumeMovement(npc); } } boolean canPatrol( obj_id npc ) { if ( isIdValid(npc) ) return utils.hasScriptVar(npc, SCRIPTVAR_CACHED_PATROL_TYPE); return false; } location[] parseWaypoints(obj_id npc, string[] waypoints) { if ( !isIdValid(npc) || waypoints != null ) return null; location baseLoc = aiGetHomeLocation( npc ); if ( (baseLoc.x == 0.0f) && (baseLoc.z == 0.0f) ) { baseLoc = getLocation( npc ); if ( (baseLoc.x == 0.0f ) && (baseLoc.z == 0.0f) ) { debugServerConsoleMsg( npc, "* ERROR: TRYING TO GET LOCATION ON AN NPC NOT INSTANTIATED! *" ); return null; } } location[] patrolLocs = new location[waypoints.length]; location patrolLoc = new location( baseLoc ); for ( int i = 0; i < waypoints.length; i++ ) { float[] coords = parseWaypoints( waypoints[i] ); if ( coords != null && coords.length == 2 ) { patrolLoc.x = baseLoc.x + coords[0]; patrolLoc.z = baseLoc.z + coords[1]; } else debugServerConsoleMsg( npc, "* ERROR: COULD NOT PARSE WAYPOINT STRING <" + waypoints[i] + "> *" ); patrolLocs[i] = patrolLoc; } return patrolLocs; } float[] parseWaypoints(string text) { int intIndex = 0; int intI = 0; intIndex = text.indexOf(","); string strText1 = ""; string strText2 = ""; if(intIndex<0) { return null; } else { while(intI < text.length() ) { if (intI < intIndex ) strText1 = strText1+text.charAt(intI); else if (intI > intIndex ) strText2 = strText2+text.charAt(intI); intI = intI+1; } } float[] returnCoords = new float[2]; returnCoords[0] = Float.parseFloat(strText1); returnCoords[1] = Float.parseFloat(strText2); return returnCoords; } void setIgnoreCombat( obj_id npc ) { setWantSawAttackTriggers( npc, false ); } /* -------------------------------------------------- * ai.diction - This objvar specifies which npc reaction stringfile * the npc should use to bark spam. * * If the objvar isn't set then the NPC won't speak. * --------------------------------------------------*/ void barkString( obj_id npc, string text ) { if ( !text.equals("death") ) { if ( ai_lib.isAiDead( npc ) ) return; } if (!hasObjVar( npc, "ai.diction") ) return; if ( utils.hasScriptVar( npc, "ai.recentlyBarked") ) { if ( getGameTime() < utils.getIntScriptVar( npc, "ai.recentlyBarked")) return; } if ( !ai_lib.isNpc( npc ) && !pet_lib.isDroidPet( npc ) ) { removeObjVar( npc, "ai.diction" ); return; } //hmm. We should convert diction barks to be directed To a specific player, // use prose packages to say the player's name, and do language checks // for wookiees talking to players. That would be great. if ( getSpecies (npc)==SPECIES_WOOKIEE ) { removeObjVar( npc, "ai.diction" ); return; } utils.setScriptVar( npc, "ai.recentlyBarked", getGameTime()+rand(20,45) ); location loc = getLocation( npc ); if ( locations.isInCity( loc ) ) { if ( rand(1,100) != 1 ) return;//1/100 chance to spam in town } else { if ( rand( 1,10) != 1 ) return;//1/10 chance to spam out of town } string diction = getStringObjVar( npc, "ai.diction" ); string stringFile = "npc_reaction/" + diction; string textString = text + "_" + rand( 1, 16 ); string_id speakString = new string_id( stringFile, textString ); if ( getString(speakString) == null ) { debugServerConsoleMsg( npc, "WARNING: Npc_reaction " + speakString + " - does it exist?" ); return; } //debugSpeakMsg( npc, stringFile + ":" + textString ); chat.chat( npc, speakString ); } boolean isAnimal( obj_id npc ) { return isMonster(npc); } boolean isMonster( obj_id npc ) { int myNiche = aiGetNiche( npc ); if ( myNiche == NICHE_MONSTER || myNiche == NICHE_HERBIVORE || myNiche == NICHE_CARNIVORE || myNiche == NICHE_PREDATOR ) return true; else return false; } boolean isDroid (obj_id npc) { int niche = aiGetNiche(npc); if ( niche == NICHE_DROID) return true; else return false; }//end isDroid boolean isPredator (obj_id npc) { int niche = aiGetNiche(npc); if (niche == NICHE_PREDATOR) return true; else return false; }//end isPredator boolean isCarnivore (obj_id npc) { int niche = aiGetNiche(npc); if (niche == NICHE_CARNIVORE) return true; else return false; }//end isCarnivore boolean isHerbivore (obj_id npc) { int niche = aiGetNiche(npc); if (niche == NICHE_HERBIVORE) return true; else return false; }//end isHerbivore boolean isVehicle (obj_id npc) { int niche = aiGetNiche(npc); if (niche == NICHE_VEHICLE) return true; else return false; }//end isVehicle boolean isAndroid (obj_id npc) { int niche = aiGetNiche(npc); if (niche == NICHE_ANDROID) return true; else return false; }//end isAndroid boolean isHumanoid(obj_id target) { if (isPlayer(target)) return true; else { int niche = aiGetNiche(target); if (niche == NICHE_NPC || niche == NICHE_ANDROID) return true; } return false; } boolean isNpc( obj_id npc ) { int myNiche = aiGetNiche( npc ); return ( myNiche == NICHE_NPC ); } boolean isTurret (obj_id npc) { int got = getGameObjectType(npc); if ( isGameObjectTypeOf(got, GOT_installation_turret) ) return true; else return false; }//end isTurret void setIgnoreCombat(obj_id npc, boolean b) { setWantSawAttackTriggers( npc, b); } void setDefaultCalmBehavior( obj_id npc, int behavior ) { LOGC(aiLoggingEnabled(npc), "debug_ai", "ai_lib::setDefaultCalmBehavior() self(" + npc + getName(npc) + ") behavior(" + behavior + ")"); if ( !isIdValid(npc) ) return; if (behavior == BEHAVIOR_WANDER) { behavior = BEHAVIOR_LOITER; } setObjVar(npc, "ai.defaultCalmBehavior", behavior); messageTo(npc, "handleSetDefaultBehavior", null, 1, false); } void greet(obj_id ai, obj_id player) { LOGC(aiLoggingEnabled(ai), "debug_ai", "ai_lib::greet() ai(" + ai + ":" + getName(ai) + ") player(" + player + ":" + getName(player) + ")"); if (isInCombat(ai)) return; const int reaction = factions.getFactionReaction(ai, player); if (reaction < factions.REACTION_NEUTRAL) { chat.setAngryMood(ai ); ai_lib.barkString(ai, "hi_mean"); } else if (reaction > factions.REACTION_NEUTRAL) { chat.setGoodMood(ai); ai_lib.barkString(ai, "hi_nice"); } else { chat.setNeutralMood(ai); ai_lib.barkString(ai, "hi_mid"); } } void dismiss(obj_id ai, obj_id player) { LOGC(aiLoggingEnabled(ai), "debug_ai", "ai_lib::dismiss() ai(" + ai + ":" + getName(ai) + ") player(" + player + ":" + getName(player) + ")"); if (isInCombat(ai)) return; const int reaction = factions.getFactionReaction(ai, player); if (reaction < factions.REACTION_NEUTRAL) { chat.setBadMood(ai); ai_lib.barkString(ai, "bye_mean"); } else if (reaction > factions.REACTION_NEUTRAL) { chat.setGoodMood(ai); ai_lib.barkString(ai, "bye_nice"); } else { chat.setNeutralMood(ai); ai_lib.barkString(ai, "bye_mid"); } } //dupe of function call below because all the others start with aiIs, not isAi boolean aiIsDead( obj_id npc ) { return isAiDead( npc ); } boolean isAiDead( obj_id npc ) { if ( !isMob(npc) ) return (isDisabled(npc)); if ( isIncapacitated( npc )) { //debugSpeakMsg( npc, "i am incapacitated" ); return true; } if (isDead(npc)) { //debugSpeakMsg( npc, "so I am dead" ); return true; } //debugSpeakMsg( npc, "so i am not dead" ); return false; } void aiFollow( obj_id npc, obj_id target ) { aiFollow(npc, target, DEFAULT_FOLLOW_MIN, DEFAULT_FOLLOW_MAX); } void aiFollow(obj_id npc, obj_id target, float min_dist, float max_dist) { if(target == npc) { debugServerConsoleMsg( npc, npc + " was told to follow itself"); return; } if(getPosture( npc ) != POSTURE_UPRIGHT) { posture.stand(npc); } setObjVar(npc, "ai.persistantFollowing.target", target); if(min_dist < 1f) { min_dist = 1f; } if(max_dist < 1f) { max_dist = 1f; } if(max_dist <= min_dist) { max_dist = min_dist + 1f; } setObjVar(npc, "ai.persistantFollowing.min_dist", min_dist); setObjVar(npc, "ai.persistantFollowing.max_dist", max_dist); follow(npc, target, min_dist, max_dist); } boolean isFollowing(obj_id npc) { if(!isIdValid(npc)) { return false; } return hasObjVar(npc, "ai.persistantFollowing"); } obj_id getFollowTarget(obj_id npc) { if(!isIdValid(npc)) { return null; } return getObjIdObjVar(npc, "ai.persistantFollowing.target"); } void resumeFollow(obj_id npc) { if(!isIdValid(npc)) return; obj_id target = getObjIdObjVar(npc, "ai.persistantFollowing.target"); if(isIdValid(target)) { float min_dist = DEFAULT_FOLLOW_MIN; if(hasObjVar(npc, "ai.persistantFollowing.min_dist")) { min_dist = getFloatObjVar(npc, "ai.persistantFollowing.min_dist"); } float max_dist = DEFAULT_FOLLOW_MAX; if(hasObjVar(npc, "ai.persistantFollowing.max_dist")) { max_dist = getFloatObjVar(npc, "ai.persistantFollowing.max_dist"); } aiFollow(npc, target, min_dist, max_dist); return; } aiStopFollowing(npc); } void aiStopFollowing(obj_id npc) { stop(npc); removeObjVar(npc, "ai.persistantFollowing"); if(getBehavior(npc) <= BEHAVIOR_CALM) { messageTo(npc, "resumeDefaultCalmBehavior", null, 5, false); } } string getMood( obj_id npc ) { if ( !hasObjVar( npc, "ai.mood" ) ) return ai_lib.MOOD_CALM; else return getStringObjVar( npc, "ai.mood" ); } void pathNear( obj_id npc, location dest, float range ) { location startLoc = getLocation( npc ); if ( dest.x < startLoc.x ) dest.x += range; else dest.x -= range; if ( dest.z < startLoc.z ) dest.z += range; else dest.z -= range; pathTo( npc, dest ); } void followInFormation( obj_id npc, obj_id leader, int formationType, int position ) { //debugSpeakMsg( npc, "following " + leader ); if ( npc == leader ) { debugServerConsoleMsg( npc, npc + " was told to followInFormation itself" ); return; } setObjVar( npc, "ai.inFormation", leader ); setObjVar( npc, "ai.formationType", formationType ); setObjVar( npc, "ai.formationPosition", position ); if ( !isInCombat( npc ) ) { stop( npc ); resumeFormationFollowing( npc ); } } void resumeFormationFollowing( obj_id npc ) { //debugSpeakMsg( npc, "resuming following" ); if ( isInCombat( npc )) return; obj_id leader = getObjIdObjVar( npc, "ai.inFormation" ); if ( !exists( leader ) || !isInWorld( leader ) || isAiDead( leader ) ) { removeObjVar( npc, "ai.inFormation" ); return; } int formationType = getIntObjVar( npc, "ai.formationType" ); int position = getIntObjVar( npc, "ai.formationPosition" ); switch ( formationType ) { case FORMATION_WEDGE : followInWedgeFormation( npc, leader, position ); break; case FORMATION_LINE : followInLineFormation( npc, leader, position ); break; case FORMATION_BOX : followInBoxFormation( npc, leader, position ); break; default: followInColumnFormation( npc, leader, position ); break; } } void followInWedgeFormation( obj_id npc, obj_id leader, int position ) { if ( npc == leader ) { debugServerConsoleMsg( npc, npc + " was told to followInWedgeFormation itself" ); return; } // L = leader // // ^ // L // 2 1 // 4 3 // 6 5 // ... ... // Clamp the position to positive integers position = Math.max(1, position); location offset = new location(); const float spacing = getObjectCollisionRadius(leader) * 2.25f; const float x = (spacing * (float)Math.ceil(position / 2.0f)); offset.x = (position % 2 == 0) ? -x : x; offset.z = -x; follow(npc, leader, offset); } void followInBoxFormation( obj_id npc, obj_id leader, int position ) { if ( npc == leader ) { debugServerConsoleMsg( npc, npc + " was told to followInLineFormation itself" ); return; } /* POSITIONS: 0 1 2 3 4 5 6 7 */ location offset = new location(0,0,0,""); //set the x: if ( position < 3 ) { offset.x += ( position * 3 ); } else if ( position == 4 ) { offset.x = 6; } else if ( position !=3 ) { offset.x += ((position-5)*3); } //set the z: if ( position == 3 || position == 4 ) { offset.z += 3; } else if ( position > 4 ) { offset.z += 6; } if ( position < 1 || position > 7 ) follow( npc, leader, rand(15,20), rand(10,18)); else follow( npc, leader, offset ); } void followInLineFormation( obj_id npc, obj_id leader, int position ) { if ( npc == leader ) { debugServerConsoleMsg( npc, npc + " was told to followInLineFormation itself" ); return; } // L = leader // // ^ // ... 6 4 2 L 1 3 5 ... // Clamp the position to positive integers position = Math.max(1, position); location offset = new location(); const float spacing = getObjectCollisionRadius(leader) * 2.25f; const float x = (spacing * (float)Math.ceil(position / 2.0f)); offset.x = (position % 2 == 0) ? -x : x; follow(npc, leader, offset); } void followInColumnFormation( obj_id npc, obj_id leader, int position ) { if ( npc == leader ) { debugServerConsoleMsg( npc, npc + " was told to followInColumnFormation itself" ); return; } // L = leader // // ^ // L 1 // 2 3 // 4 5 // 6 7 // 8 9 // . . // . . // . . // Clamp the position to positive integers position = Math.max(1, position); location offset = new location(); const float spacing = getObjectCollisionRadius(leader) * 2.25f; offset.x = (position % 2 == 0) ? 0.0f : spacing; offset.z = -(spacing * (float)Math.ceil((position - 1) / 2.0f)); follow(npc, leader, offset); } void pathAwayFrom( obj_id npc, obj_id target ) { if ( !isIdValid( npc ) || !isIdValid( target ) ) { return; } if ( npc == target ) { return; } if ( hasObjVar( npc, "ai.pathingAwayFrom")) { obj_id oldTarget = getObjIdObjVar( npc, "ai.pathingAwayFrom" ); if ( oldTarget == target ) return; if ( getDistance( npc, target ) > getDistance( npc, oldTarget ) ) return; } location myLoc = new location( getLocation( npc ) ); location targetLoc = new location( getLocation( target ) ); if ( myLoc.x < targetLoc.x ) myLoc.x -= rand( 20f, 40f ); else myLoc.x += rand( 20f, 40f ); if ( myLoc.z < targetLoc.z ) myLoc.z -= rand( 20f, 40f ); else myLoc.z += rand( 20f, 40f ); setObjVar( npc, "ai.pathingAwayFrom", target ); pathTo( npc, myLoc ); messageTo( npc, "handleClearPathingFlag", null, 30f, isObjectPersisted( npc ) ); } void setLoiterRanges( obj_id npc, float minRange, float maxRange ) { setObjVar( npc, "ai.loiterMinRange", minRange ); setObjVar( npc, "ai.loiterMaxRange", maxRange ); } int aiGetSpecies( obj_id npc ) { return getSpecies( npc ); } void setCustomIdleAnimation( obj_id npc, string animationString ) { setDefaultCalmMood( npc, animationString ); } void setDefaultCalmMood( obj_id npc, string mood ) { //debugSpeakMsg( npc, "setting the default calm mood objvar" ); setObjVar( npc, "ai.defaultCalmMood", mood ); if ( getBehavior(npc) <= BEHAVIOR_CALM ) { //debugSpeakMsg( npc, "calm, so setting mood" ); setMood( npc, MOOD_CALM ); } } int getDefaultCalmBehavior(obj_id ai) { int behavior = BEHAVIOR_LOITER; if (hasObjVar(ai, "ai.defaultCalmBehavior")) { behavior = getIntObjVar(ai, "ai.defaultCalmBehavior"); } return behavior; } void aiPathTo( obj_id npc, location pathLoc ) { if ( (pathLoc.x == 0.0f) && (pathLoc.z == 0.0f) ) { //nope. return; } setObjVar( npc, "ai.persistentPathing", pathLoc ); stop( npc ); pathTo( npc, pathLoc ); } /* int aiGetNiche( obj_id npc ) { if ( isPlayer( npc ) ) return getNiche( npc ); string creatureName = getCreatureName( npc ); if ( creatureName != null ) { int niche = dataTableGetInt( CREATURE_TABLE, creatureName, "niche" ); return niche; } int niche = getNiche( npc ); return niche; } */ int aiGetNiche( obj_id npc ) { if ( isPlayer( npc ) ) return NICHE_PC; string creatureName = getCreatureName( npc ); if ( creatureName != null ) return ( dataTableGetInt( CREATURE_TABLE, creatureName, "niche" ) ); else return NICHE_NONE; } int aiGetNiche( string creatureName ) { if ( creatureName == null || creatureName.equals("") ) return NICHE_NONE; return dataTableGetInt( CREATURE_TABLE, creatureName, "niche" ); } string getSocialGroup( obj_id npc ) { string creatureName = getCreatureName(npc); if(creatureName != null || !beast_lib.isBeast(npc)) { string socialGroup = dataTableGetString( CREATURE_TABLE, creatureName, "socialGroup" ); return socialGroup; } return null; } boolean isSameSocialGroup( obj_id npc, obj_id target ) { boolean result = false; if(!pet_lib.isPet(npc) && !pet_lib.isPet(target) && !beast_lib.isBeast(npc) && !beast_lib.isBeast(target)) { const string npcCreatureName = getCreatureName(npc); const string targetCreatureName = getCreatureName(target); if (npcCreatureName != null && targetCreatureName != null) { string mySocialGroup = getSocialGroup(npc); string yourSocialGroup = getSocialGroup(target); if ( (mySocialGroup != null) && (yourSocialGroup != null) && mySocialGroup.equals(yourSocialGroup)) { result = true; } } else if ((factions.getFaction(npc) != null) && (factions.getFaction(target) != null)) { string mySocialGroup = factions.getFaction(npc); string yourSocialGroup = factions.getFaction(target); if ( (mySocialGroup != null) && (yourSocialGroup != null) && mySocialGroup.equals(yourSocialGroup)) { result = true; } } } return result; } boolean isHerdingCreature( obj_id npc ) { // Do data checks on server load if ( isNpc( npc ) || isPlayer( npc ) || isAndroid(npc)) return false; string creatureName = getCreatureName( npc ); if ( creatureName != null ) { boolean isHerding = (dataTableGetInt( CREATURE_TABLE, creatureName, "herd" )==1); return isHerding; } return false; } boolean isStalkingCreature(obj_id ai) { // Do data checks on server load if (isPlayer(ai) || (aiGetNiche(ai) == NICHE_NPC) || isAndroid(ai)) return false; if (hasScript(ai, "ai.pet_advance")) return false; return aiIsStalker(ai); } boolean isAggroToward( obj_id npc, obj_id threat ) { if ( hasScript( npc, "ai.pet_advance" ) ) return false; // If the threat has the npc on their PEF list and // also does PVP faction checks if ( pvpIsEnemy( npc, threat ) ) { //debugSpeakMsg( npc, "we are enemies"); return true; } //creatures in the same social group aren't aggro: if ( ai_lib.isSameSocialGroup( npc, threat )) return false; // In creatures_table.xml, remove references to NICHE_MONSTER and NICHE_PREDATOR //players are considered to be aggro to creatures, not to npcs //if ( isPlayer( npc ) ) //{ // //debugSpeakMsg( npc, "you are a player and I am a monster"); // return ( ai_lib.isMonster( threat ) ); //} // Non-imperial/rebel faction int reaction = factions.getFactionReaction( npc, threat ); if ( reaction == factions.REACTION_NEGATIVE ) { //debugSpeakMsg( npc, "reaction is negative"); return true; } if (!ai_lib.isMonster( npc ) ) { if ( ai_lib.isAggro( npc ) ) { //I will attack you unless I like you very much: if ( reaction <= factions.REACTION_NEUTRAL ) { //debugSpeakMsg( npc, "reaction is worse than neutral"); return true; } } return false;// all checks below are for CREATURES only } // NON-MONSTERS //aggro creatures hate players: if ( isPlayer( threat ) && ai_lib.isAggro( npc ) ) { //debugSpeakMsg( npc, "you are a player and I am aggroo"); return true; } int npcNiche = ai_lib.aiGetNiche( npc ); int threatNiche = ai_lib.aiGetNiche( threat ); //Carnivores and Predators eat herbivores if ( npcNiche == NICHE_CARNIVORE /* || npcNiche == NICHE_PREDATOR */) { if ( threatNiche == NICHE_HERBIVORE ) return true; } return false; } boolean isAggro(obj_id ai) { if (isPlayer(ai)) return false; if ( hasScript( ai, "ai.pet_advance" ) ) return false; if ( hasScript( ai, "systems.missions.dynamic.mission_bounty_target")) return false; return aiIsAggressive(ai); } boolean isHealingNpc( obj_id npc ) { if ( isPlayer( npc ) ) return false; int niche = aiGetNiche( npc ); if ( niche != NICHE_NPC || niche != NICHE_ANDROID) return false; string creatureName = getCreatureName( npc ); if ( creatureName != null ) { boolean isHealer = (dataTableGetInt( CREATURE_TABLE, creatureName, "healer" )!=0); return isHealer; } return false; } boolean incapacitateMob(obj_id target) { if ( target == null || !isMob(target) ) { return false; } boolean result = true; result &= setHealth(target, -50); result &= setAction(target, -50); result &= setMind(target, -50); return result; } boolean isHumanSkeleton( obj_id npc ) { if ( isPlayer(npc) ) return true; int speciesNum = ai_lib.aiGetSpecies( npc ); if ( speciesNum == -1 ) { return false; } string skeleton = dataTableGetString("datatables/ai/species.iff", speciesNum, "Skeleton"); if ( skeleton == "human" ) { return true; } return false; } string getSkeleton(int speciesNum) { if ( speciesNum == -1 ) return null; string skeleton = dataTableGetString("datatables/ai/species.iff", speciesNum, "Skeleton"); return skeleton; } string getSkeleton(obj_id npc) { int speciesNum = ai_lib.aiGetSpecies( npc ); LOG("mount", "speciesnum is "+speciesNum); if ( speciesNum == -1 ) { return null; } string skeleton = dataTableGetString("datatables/ai/species.iff", speciesNum, "Skeleton"); LOG("mount", "skeleton is "+skeleton); return skeleton; } void doThreatenAnimation( obj_id npc ) { if (ai_lib.aiGetNiche( npc )!=NICHE_NPC || !isAndroid(npc)) { doAction( npc, ACTION_THREATEN ); return; } string anim = ai_lib.ACTION_THREATEN; switch ( rand(0,13)) { case 0 : anim = ai_lib.ACTION_THREATEN; break; case 1 : anim = "alert"; break; case 2 : anim = "angry"; break; case 3 : anim = "gesticulate_wildly"; break; case 4 : anim = "greet"; break; case 5 : anim = "look_casual"; break; case 6 : anim = "look_left"; break; case 7 : anim = "look_right"; break; case 8 : anim = "point_accusingly"; break; case 9 : anim = "pound_fist_chest"; break; case 10 : anim = "pound_fist_palm"; break; case 11 : anim = "taunt1"; break; case 12 : anim = "taunt2"; break; case 13 : anim = "taunt3"; break; } ai_lib.doAction( npc, anim ); } void doVictoryDance( obj_id npc ) { if (ai_lib.isMonster( npc )) { if ( rand(1,500)==1 ) { if ( rand(1,2)==1 ) doAnimationAction( npc, "trick_1"); else doAnimationAction( npc, "trick_2"); } else doAction( npc, "vocalize" ); return; } string anim = "celebrate"; switch ( rand(0,23)) { case 0 : anim = "celebrate"; break; case 1 : anim = "applause_excited"; break; case 2 : anim = "belly_laugh"; break; case 3 : anim = "celebrate1"; break; case 4 : anim = "coup_de_grace"; break; case 5 : anim = "dismiss"; break; case 6 : anim = "flex_biceps"; break; case 7 : anim = "laugh_pointing"; break; case 8 : anim = "point_accusingly"; break; case 9 : anim = "pound_fist_chest"; break; case 10 : anim = "pound_fist_palm"; break; case 11 : anim = "salute1"; break; case 12 : anim = "salute2"; break; case 13 : anim = "shrug_hands"; break; case 14 : anim = "shrug_shoulders"; break; case 15 : anim = "smack_self"; break; case 16 : anim = "shake_head_disgust"; break; case 17 : anim = "snap_finger1"; break; case 18 : anim = "snap_finger2"; break; case 19 : anim = "tap_head"; break; case 20 : anim = "taunt1"; break; case 21 : anim = "taunt2"; break; case 22 : anim = "taunt3"; break; case 23 : anim = "yawn"; break; case 24 : anim = "check_wrist_device"; break; } doAction( npc, anim ); } obj_id getLair( obj_id npc ) { if ( isPlayer(npc) ) return null; //DO I know whether I have a lair or not? if ( utils.hasScriptVar(npc, "ai.hasLair" ) ) return utils.getObjIdScriptVar(npc, "ai.hasLair" ); obj_id myPOI = getObjIdObjVar( npc, "poi.baseObject" ); if ( myPOI == null ) { utils.setScriptVar( npc, "ai.noLiar", true ); return null; } string buildingType = getStringObjVar( myPOI, "spawning.buildingType" ); if ( (buildingType == null || buildingType.equals( "none" )) && !hasObjVar( myPOI, "spawning.target" )) { utils.setScriptVar( npc, "ai.noLair", true ); return null; } utils.setScriptVar( npc, "ai.hasLair", myPOI ); return myPOI; } int aiGetApproachTriggerRange( obj_id npc ) { return (int)aiGetAggroRadius(npc); } void aiSetPosture( obj_id npc, int newPosture ) { if ( ( getState( npc, STATE_COMBAT ) != 1 ) ) { setPostureClientImmediate(npc, newPosture); } else { attacker_results cbtAnimationResults = new attacker_results(); cbtAnimationResults.endPosture = newPosture; cbtAnimationResults.id = npc; doCombatResults("change_posture", cbtAnimationResults, null); } } boolean canSit( obj_id npc ) { int speciesNum = ai_lib.aiGetSpecies( npc ); if ( speciesNum == -1 ) { return false; } boolean hasSitAnims = ( dataTableGetInt("datatables/ai/species.iff", speciesNum, "CanSit") == 1 ); return hasSitAnims; } boolean canLieDown( obj_id npc ) { int speciesNum = ai_lib.aiGetSpecies( npc ); if ( speciesNum == -1 ) { return false; } boolean hasLayAnims = ( dataTableGetInt("datatables/ai/species.iff", speciesNum, "CanLieDown") == 1 ); return hasLayAnims; } boolean isInSameBuilding(obj_id npc, obj_id target) { if(!isIdValid(npc)) return false; if(!isIdValid(target)) return false; obj_id npcCell = getLocation( npc ).cell; obj_id targetCell = getLocation( target ).cell; if ( isIdValid( npcCell ) == false && isIdValid( targetCell ) == false ) return true;//we're both outside else if ( isIdValid( npcCell ) == false || isIdValid( targetCell ) == false ) return false;//one of us is outside obj_id npcBldg = getTopMostContainer( npc ); obj_id targetBldg = getTopMostContainer( target ); if ( npcBldg == targetBldg ) return (canSee( npc, target ));//we're in the same building (and can see each other) else return false;//not in the same bldg } string getAttackString( int hash ) { string outstring = "@combat_effects:"; if ( hash == 0 ) return "@combat_effects:none"; int[] attacks = dataTableGetIntColumn( "datatables/ai/special_attack.iff", "ATTACK_HASH" ); string[] attackstrings = dataTableGetStringColumn( "datatables/ai/special_attack.iff", "ATTACK_STRING" ); for ( int i=0; i evadeScan) { //smuggler is busted sendSystemMessage(player, SMUGGLER_SCAN_FAIL); return false; } } //smuggler avoids being caught sendSystemMessage(player, SMUGGLER_SCAN_SUCCESS); return true; }//end checkForSmuggler //jedi check for contraband searches boolean checkForJedi( obj_id player ) { if ( !utils.isProfession(player, utils.FORCE_SENSITIVE) || factions.getFactionFlag(player) == factions.FACTION_FLAG_NEUTRAL ) return false; int mindTrick = 0; if ( isGod(player) ) mindTrick += 101; else if ( badge.hasBadge (player, "bdg_jedi_elder") || getState(player, STATE_GLOWING_JEDI) != 0 ) mindTrick += 40; else if ( badge.hasBadge (player, "new_prof_jedi_master") ) mindTrick += 30; else mindTrick += 10; int roll = rand( 1, 100 ); if ( roll > mindTrick ) { return false; } return true; } boolean setupNpc(obj_id npc, boolean conversable, boolean invulnerable) { return setupNpc(npc, null, conversable, invulnerable); } boolean setupNpc(obj_id npc, string name, boolean conversable, boolean invulnerable) { if (name != null && !name.equals("")) setName(npc, name); if (conversable) setCondition(npc, CONDITION_CONVERSABLE); setInvulnerable (npc, invulnerable); return true; } void establishAgroLink(obj_id subject, float range) { string socialGroup = getSocialGroup(subject); obj_id[] creatures = getCreaturesInRange(subject, range); if (creatures == null || creatures.length == 0) return; resizeable obj_id[] linkedCreatures = new obj_id[0]; for (int i=0;i= 100) { showFlyTextPrivate(target, player, new string_id("cbt_spam", "immune"), 1.5f, colors.WHITE); return false; } if (rand(1, 100) > mt_resist) { playClientEffectObj(target, "appearance/pt_mind_trick.prt", target, "root"); removeHateTarget(target, player); return true; } else { showFlyTextPrivate(target, player, new string_id("cbt_spam", "resist"), 1.5f, colors.WHITE); return false; } } return false; } boolean isTauntable(obj_id creature) { return !utils.hasScriptVar(creature, "combat.immune.taunt"); } void establishSharedHealth(resizeable obj_id[] allies) { for (int i=0;i