//------------------------------------------------ // space_quest.scriptlib // Jeff Freeman, Brandon Reinhart //------------------------------------------------ //------------------------------------------------ // Includes //------------------------------------------------ include library.money; include library.prose; include library.utils; include library.skill; include library.space_utils; include library.space_transition; include library.group; include library.features; //------------------------------------------------ // Constants //------------------------------------------------ // const string QUEST_OBJECT_TEMPLATE = "object/mission/base_mission_object.iff"; // Quest manager info. const string QUEST_MANAGER = "questManager"; // General quest object objvars. const string QUEST_TYPE = "questType"; const string QUEST_NAME = "questName"; const string QUEST_OWNER = "questOwner"; const string QUEST_ZONE = "questZone"; const string QUEST_DUTY = "duty"; const string QUEST_DELAY = "delay"; const string QUEST_TRIGGER_EVENT = "trigger.triggerEvent"; const string QUEST_TRIGGER_SC = "trigger.triggerSC"; const string QUEST_TRIGGER_ARG = "trigger.triggerArg"; const string QUEST_TRIGGER_ARG2 = "trigger.triggerArg2"; const string QUEST_TRIGGER_ON = "trigger.triggerOn"; const string QUEST_TRIGGER_ENDS = "trigger.triggerEnds"; const string QUEST_TRIGGER_ENDS_NEW = "trigger.triggerEndsMission"; const string QUEST_TRIGGER_DELAY = "trigger.triggerDelay"; // Auto Reward objvars. const string QUEST_AUTO_REWARD = "autoReward"; const string QUEST_AUTO_REWARD_FROM = "autoRewardFrom"; const string QUEST_AUTO_REWARD_SUBJECT = "autoRewardSubject"; const string QUEST_AUTO_REWARD_BODY = "autoRewardBody"; // Quest status objvar. const string QUEST_STATUS = "space_quest"; const string QUEST_STATUS_SERIES = "space_quest.series"; // Quest status flags. const int QUEST_FAILED = 1; const int QUEST_WON = 2; const int QUEST_ABORTED = 4; const int QUEST_RECEIVED_REWARD = 8; const int QUEST_GROUP_QUEST_IN_PROGRESS = 16; // Quest series flags. const int QUEST_SERIES_COMPLETED = 1; // Patrol vars. const int PATROL_NAV_RADIUS = 150; // Escort vars. const int ESCORT_ROAM_TOLERANCE = 1000; // Strings const string_id SID_REQUIRES_JTL = new string_id( "space/quest", "jtl_required" ); const string_id SID_QUEST_RECEIVED = new string_id( "space/quest", "quest_received" ); const string_id SID_QUEST_IN_PROGRESS = new string_id( "space/quest", "quest_in_progress" ); const string_id SID_QUEST_FAILED = new string_id( "space/quest", "quest_failed" ); const string_id SID_QUEST_WON = new string_id( "space/quest", "quest_won" ); const string_id SID_QUEST_ENDED = new string_id( "space/quest", "quest_ended" ); const string_id SID_QUEST_ABORTED = new string_id( "space/quest", "quest_aborted" ); const string_id SID_QUEST_REWARDED = new string_id( "space/quest", "quest_rewarded" ); const string_id SID_QUEST_UPDATE_S = new string_id( "space/quest", "quest_update_s" ); const string_id SID_QUEST_UPDATE_IS = new string_id( "space/quest", "quest_update_is" ); const string_id SID_QUEST_ALERT_S = new string_id( "space/quest", "quest_alert_s" ); const string_id SID_ESCORT_UPDATE = new string_id( "space/quest", "escort_update" ); const string_id SID_DUTY_RECEIVED = new string_id( "space/quest", "duty_received" ); //const string_id SID_DUTY_IN_PROGRESS = new string_id( "space/quest", "duty_in_progress" ); //const string_id SID_DUTY_FAILED = new string_id( "space/quest", "duty_failed" ); //const string_id SID_DUTY_WON = new string_id( "space/quest", "duty_won" ); const string_id SID_DUTY_ABORTED = new string_id( "space/quest", "duty_aborted" ); //const string_id SID_DUTY_REWARDED = new string_id( "space/quest", "duty_rewarded" ); const string_id SID_DUTY_UPDATE_S = new string_id( "space/quest", "duty_update_s" ); const string_id SID_DUTY_UPDATE_IS = new string_id( "space/quest", "duty_update_is" ); const string_id SID_DUTY_ALREADY = new string_id( "space/quest", "duty_already" ); const string_id SID_GROUP_QUEST_RECEIVED = new string_id( "space/quest", "quest_received_group" ); const string_id SID_GROUP_QUEST_WON = new string_id( "space/quest", "quest_won_group" ); const string_id SID_GROUP_QUEST_ENDED = new string_id( "space/quest", "quest_ended_group" ); const string_id SID_GROUP_QUEST_FAILED = new string_id( "space/quest", "quest_failed_group" ); const string_id SID_GROUP_QUEST_ABORTED = new string_id( "space/quest", "quest_aborted_group" ); const string_id SID_GROUP_QUEST_IN_PROGRESS = new string_id( "space/quest", "quest_in_progress_group" ); const string_id SID_QUEST_REWARD = new string_id( "space/quest", "quest_reward" ); const string_id SID_QUEST_REWARD_OBJECT = new string_id( "space/quest", "quest_reward_object" ); const string_id GROUP_QUEST_RECEIVED = new string_id( "space/quest", "group_quest_received" ); const string_id GROUP_QUEST_FAILED = new string_id( "space/quest", "group_quest_failed" ); const string_id GROUP_QUEST_WON = new string_id( "space/quest", "group_quest_won" ); const string_id GROUP_QUEST_ABORTED = new string_id( "space/quest", "group_quest_aborted" ); // Music const string MUSIC_QUEST_RECEIVED = "sound/music_themequest_acc_criminal.snd"; const string MUSIC_QUEST_WON = "sound/music_themequest_victory_rebel.snd"; const string MUSIC_QUEST_FAILED = "sound/music_themequest_fail_criminal.snd"; const string MUSIC_QUEST_ABORTED = "sound/music_themequest_fail_criminal.snd"; const string MUSIC_QUEST_EVENT = "sound/music_event_danger.snd"; const string MUSIC_QUEST_ESCORT_ARRIVAL = "sound/mus_quest_escort_arrival.snd"; const string MUSIC_QUEST_BOSS_COMING = "sound/mus_quest_boss_coming.snd"; const string MUSIC_QUEST_CAPTURE_OP = "sound/mus_quest_theme_docking.snd"; const string MUSIC_QUEST_MISSION_START = "sound/mus_quest_theme_opening.snd"; // Alerts const string ALERT_DROID_WARNING = "clienteffect/space_droid_warning.iff";//"sound/dro_r2_2_warn.snd"; const string ALERT_DROID_DANGER = "clienteffect/space_droid_danger.iff";//"sound/dro_r2_3_danger.snd"; //------------------------------------------------------------------------------------------------ // Quest Logic Methods // // These are public interface functions, to be called from conversation // scripts on quest NPCs. //------------------------------------------------------------------------------------------------ //------------------------------------------------ // hasQuest // Used to see if the player has any quest. // // obj_id player - The player you are checking. // returns boolean - True if the player is on a quest. //------------------------------------------------ boolean hasQuest( obj_id player ) { if ( !isIdValid( player ) ) return false; obj_id datapad = utils.getPlayerDatapad( player ); if ( isIdValid( datapad ) ) { // Search for any quest in the datapad. obj_id[] dpobjs = getContents( datapad ); for ( int i=0; i 0 ) duty = true; // Make sure the file exists. dictionary questInfo = dataTableGetRow( qTable, 0 ); if ( questInfo == null ) { sendSystemMessageTestingOnly( player, "grantQuest: Unable to find quest table " + qTable ); return false; } // Is this on a timer? if ( !duty ) { int delay = dataTableGetInt( qTable, 0, "questDelay" ); if ( !ignoreDelay && (delay > 0) ) { dictionary outparams = new dictionary(); outparams.put( QUEST_TYPE, questType ); outparams.put( QUEST_NAME, questName ); space_utils.notifyObject( player, "registerDelayedQuest", outparams ); return true; } } // Setup the quest. if ( _setupQuest( player, questType, questName, qTable, duty ) ) { // We're all set. // Notify the player. string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp; if ( duty ) pp = prose.getPackage( SID_DUTY_RECEIVED, title ); else pp = prose.getPackage( SID_QUEST_RECEIVED, title ); sendQuestSystemMessage( player, pp ); // GROUP: Notify our group. _groupNotify( player, SID_GROUP_QUEST_RECEIVED, title ); // Play music. if ( !splitQuest ) play2dNonLoopingMusic( player, MUSIC_QUEST_RECEIVED ); // Log it. string groupString = "GROUP:0"; if ( group.isGrouped( player ) ) { obj_id groupId = getGroupObject( player ); int numMembers = getGroupSize( groupId ); if ( numMembers > 0 ) { groupString = "GROUP:"+numMembers; } } CustomerServiceLog( "space_quest", "QUEST_STARTED|V1|"+groupString+"|TIME:"+getGameTime()+"|PLAYER:"+player+"|TYPE:"+questType+"|NAME:"+questName+"|ZONE:"+getCurrentSceneName() ); return true; } else { sendSystemMessageTestingOnly( player, "Failed to grantQuest '" + questType + ":" + questName + "'." ); return false; } } //------------------------------------------------ // grantGroupQuest // Assigns a specific quest to the player, but flags the group for a group reward. //------------------------------------------------ boolean grantGroupQuest( obj_id player, string questType, string questName ) { boolean result = grantQuest( player, questType, questName ); flagGroupQuest( player, questType, questName ); return result; } void flagGroupQuest( obj_id player, string questType, string questName ) { if ( group.isGrouped( player ) ) { obj_id gid = getGroupObject( player ); obj_id[] members = space_utils.getSpaceGroupMemberIds( gid ); if ( members != null && members.length >= 0 ) { for ( int i=0; i= 0 ) { for ( int i=0; i= 0 ) { for ( int i=0; i= minGroupSize ) return true; } return false; } //------------------------------------------------ // hasCompletedQuest // Indicates if the quest is completed (but not whether it was successful or a failure). // // obj_id player - The player to check. // string questType - The quest type. // string questName - The quest name. // returns boolean - True if the player finished the quest. //------------------------------------------------ boolean hasCompletedQuest( obj_id player, string questType, string questName ) { return hasObjVar( player, QUEST_STATUS + "." + questType + "." + questName ); } boolean hasCompletedQuestRecursive( obj_id player, string questType, string questName ) { int recNum = utils.getIntScriptVar( player, "recursiveCheck" ); recNum++; utils.setScriptVar( player, "recursiveCheck", recNum ); if ( recNum > 20 ) { debugSpeakMsg( player, "This quest " + questType + ":" + questName + " is BAD DATA REF'ING ITSELF!" ); utils.removeScriptVar( player, "recursiveCheck" ); return false; } if ( !hasObjVar( player, QUEST_STATUS + "." + questType + "." + questName )) { utils.removeScriptVar( player, "recursiveCheck" ); return false;//You're not done. } // Does this mission have a split? string qTable = "datatables/spacequest/"+questType+"/"+questName+".iff"; dictionary questInfo = dataTableGetRow( qTable, 0 ); if ( questInfo == null ) { utils.removeScriptVar( player, "recursiveCheck" ); return false;//this is fucked, no quest info! } int splittype = questInfo.getInt( "triggerSplitCondition" ); string splitmission = questInfo.getString( "triggerArg" ); if ( (splitmission == null) || splitmission.equals( "" ) || (splittype == 2) ) { utils.removeScriptVar( player, "recursiveCheck" ); return true;//we know you completed this mission, as it has no split, you must be done } else { //check the next one java.util.StringTokenizer st = new java.util.StringTokenizer( splitmission, ":" ); string rtype = st.nextToken(); string rname = st.nextToken(); return hasCompletedQuestRecursive( player, rtype, rname); } } //------------------------------------------------ // Quest Status Queries // ! Return value is invalid if hasCompletedQuest != true // // obj_id player - The player to check. // string questType - The quest type. // string questName - The quest name. // returns boolean - True if the condition is correct. //------------------------------------------------ boolean hasFailedQuest( obj_id player, string questType, string questName ) { return _testQuestFlag( player, questType, questName, QUEST_FAILED ); } boolean hasFailedQuestRecursive( obj_id player, string questType, string questName ) { return _recursiveFlagTest( player, questType, questName, QUEST_FAILED ); } boolean hasWonQuest( obj_id player, string questType, string questName ) { return _testQuestFlag( player, questType, questName, QUEST_WON ); } boolean hasWonQuestRecursive( obj_id player, string questType, string questName ) { return _recursiveFlagTestLast( player, questType, questName, QUEST_WON ); } boolean hasAbortedQuest( obj_id player, string questType, string questName ) { return _testQuestFlag( player, questType, questName, QUEST_ABORTED ); } boolean hasAbortedQuestRecursive( obj_id player, string questType, string questName ) { return _recursiveFlagTest( player, questType, questName, QUEST_ABORTED ); } boolean hasReceivedReward( obj_id player, string questType, string questName ) { return _testQuestFlag( player, questType, questName, QUEST_RECEIVED_REWARD ); } boolean _recursiveFlagTest( obj_id player, string questType, string questName, int flag ) { int recNum = utils.getIntScriptVar( player, "recursiveCheck" ); recNum++; utils.setScriptVar( player, "recursiveCheck", recNum ); if ( recNum > 20 ) { debugSpeakMsg( player, "This quest " + questType + ":" + questName + " is BAD DATA REF'ING ITSELF!" ); utils.removeScriptVar( player, "recursiveCheck" ); return false; } boolean test = _testQuestFlag( player, questType, questName, flag ); if ( test ) { utils.removeScriptVar( player, "recursiveCheck" ); return true; } // Does this mission have a split? string qTable = "datatables/spacequest/"+questType+"/"+questName+".iff"; dictionary questInfo = dataTableGetRow( qTable, 0 ); if ( questInfo == null ) { utils.removeScriptVar( player, "recursiveCheck" ); return false; } int splittype = questInfo.getInt( "triggerSplitCondition" ); string splitmission = questInfo.getString( "triggerArg" ); if ( (splitmission == null) || splitmission.equals( "" ) || (splittype == 2) ) { utils.removeScriptVar( player, "recursiveCheck" ); return _testQuestFlag( player, questType, questName, flag ); } else { java.util.StringTokenizer st = new java.util.StringTokenizer( splitmission, ":" ); string rtype = st.nextToken(); string rname = st.nextToken(); return _recursiveFlagTest( player, rtype, rname, flag ); } } // Finds the last mission in a chain and tests the flag. boolean _recursiveFlagTestLast( obj_id player, string questType, string questName, int flag ) { // Does this mission have a split? string qTable = "datatables/spacequest/"+questType+"/"+questName+".iff"; dictionary questInfo = dataTableGetRow( qTable, 0 ); if ( questInfo == null ) return false; int splittype = questInfo.getInt( "triggerSplitCondition" ); string splitmission = questInfo.getString( "triggerArg" ); if ( (splitmission == null) || splitmission.equals( "" ) || (splittype == 2) ) { return _testQuestFlag( player, questType, questName, flag ); } else { java.util.StringTokenizer st = new java.util.StringTokenizer( splitmission, ":" ); string rtype = st.nextToken(); string rname = st.nextToken(); return _recursiveFlagTest( player, rtype, rname, flag ); } } //------------------------------------------------ // Quest Status Setters // Marks the quest status. // // obj_id player - The player who is on the quest. // obj_id missionObj- Id of the mission in question. // returns boolean - True if the quest was successfully marked. //------------------------------------------------ boolean setQuestFailed( obj_id player, obj_id missionObj ) { return setQuestFailed( player, missionObj, true ); } boolean setQuestFailed( obj_id player, obj_id missionObj, boolean canSplit ) { string questName = getStringObjVar( missionObj, QUEST_NAME ); string questType = getStringObjVar( missionObj, QUEST_TYPE ); dictionary webster = new dictionary(); webster.put ("questName", questName); // Notify the player. string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp = prose.getPackage( SID_QUEST_FAILED, title ); sendQuestSystemMessage( player, pp ); // GROUP: Notify the group. _groupNotify( player, SID_GROUP_QUEST_FAILED, title ); // Play the sad music. play2dNonLoopingMusic( player, MUSIC_QUEST_FAILED ); // Flag the quest. _setQuestFlag( player, questType, questName, QUEST_FAILED ); flagQuestGroup( missionObj, QUEST_FAILED ); // Remove the quest journal entry. _removeQuestJournalEntry( player, questType+"/"+questName ); // Log it. string groupString = "GROUP:0"; if ( group.isGrouped( player ) ) { obj_id groupId = getGroupObject( player ); int numMembers = getGroupSize( groupId ); if ( numMembers > 0 ) { groupString = "GROUP:"+numMembers; } } CustomerServiceLog( "space_quest", "QUEST_FAILED|V1|"+groupString+"|TIME:"+getGameTime()+"|PLAYER:"+player+"|TYPE:"+questType+"|NAME:"+questName+"|ZONE:"+getCurrentSceneName() ); // Check for an end of mission split. if ( (getIntObjVar( missionObj, QUEST_TRIGGER_EVENT ) == 1) && canSplit ) { int triggerD = getIntObjVar( missionObj, QUEST_TRIGGER_DELAY ); int triggerSC = getIntObjVar( missionObj, QUEST_TRIGGER_SC ); if ( (triggerSC == 2) || (triggerSC == 3) ) // Split on a fail or tree case. { dictionary params = new dictionary(); params.put( "quest", missionObj ); params.put( "failure", true ); params.put( "ends", true ); setObjVar( missionObj, "noAbort", 1 ); messageTo( player, "doSpecialEvent", params, triggerD+5, false ); } else { _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceFailed", webster, 1, false ); } } else { _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceFailed", webster, 1, false ); } return true; } boolean setQuestWon( obj_id player, obj_id missionObj ) { return setQuestWon( player, missionObj, true ); } boolean setQuestWon( obj_id player, obj_id missionObj, boolean wsplit ) { string questName = getStringObjVar( missionObj, QUEST_NAME ); string questType = getStringObjVar( missionObj, QUEST_TYPE ); dictionary webster = new dictionary(); webster.put ("questName", questName); // Send mission over messages: // If a duty mission, tell them the mission ended. // otherwise, tell them mission won. string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp; if ( hasObjVar( missionObj, QUEST_DUTY ) ) { // Notify the player. pp = prose.getPackage( SID_QUEST_ENDED, title ); sendQuestSystemMessage( player, pp ); // GROUP: Notify the group. _groupNotify( player, SID_GROUP_QUEST_ENDED, title ); } else { // Notify the player. pp = prose.getPackage( SID_QUEST_WON, title ); sendQuestSystemMessage( player, pp ); // GROUP: Notify the group. _groupNotify( player, SID_GROUP_QUEST_WON, title ); } _setQuestFlag( player, questType, questName, QUEST_WON ); flagQuestGroup( missionObj, QUEST_WON ); // Log it. string groupString = "GROUP:0"; if ( group.isGrouped( player ) ) { obj_id groupId = getGroupObject( player ); int numMembers = getGroupSize( groupId ); if ( numMembers > 0 ) { groupString = "GROUP:"+numMembers; } } CustomerServiceLog( "space_quest", "QUEST_WON|V1|"+groupString+"|TIME:"+getGameTime()+"|PLAYER:"+player+"|TYPE:"+questType+"|NAME:"+questName+"|ZONE:"+getCurrentSceneName() ); // Flag the main quest journal entry as finished. int questid = questGetQuestId( "spacequest/"+questType+"/"+questName ); if ( (questid != 0) && questIsQuestActive( questid, player ) ) questCompleteQuest( questid, player ); // Is there an auto reward to give the player? if ( hasObjVar( missionObj, QUEST_AUTO_REWARD ) ) { int autoReward = getIntObjVar( missionObj, QUEST_AUTO_REWARD ); string_id autoRewardEmailFrom = new string_id ( "spacequest/"+questType+"/"+questName, "autorewardfrom" ); string_id autoRewardEmailSubject = new string_id ( "spacequest/"+questType+"/"+questName, "autorewardsubject" ); string_id autoRewardEmailBody = new string_id ( "spacequest/"+questType+"/"+questName, "autorewardbody" ); // Wire the player the money. money.bankTo( money.ACCT_SPACE_QUEST_REWARD, player, autoReward ); pp = prose.getPackage( SID_QUEST_REWARD, autoReward ); sendQuestSystemMessage( player, pp ); // Send the player the mail. string player_name = getName( player ); string body_oob = chatMakePersistentMessageOutOfBandBody( null, autoRewardEmailBody ); chatSendPersistentMessage( "@"+autoRewardEmailFrom.toString(), player_name, "@" + autoRewardEmailSubject.toString(), null, body_oob ); // Flag us as having received the reward. _setQuestFlag( player, questType, questName, QUEST_RECEIVED_REWARD ); } // Play the happy music. play2dNonLoopingMusic( player, MUSIC_QUEST_WON ); // Check for an end of mission split. if ( wsplit && (getIntObjVar( missionObj, QUEST_TRIGGER_EVENT ) == 1) ) { int triggerOn = getIntObjVar( missionObj, QUEST_TRIGGER_ON ); int triggerSC = getIntObjVar( missionObj, QUEST_TRIGGER_SC ); int triggerD = getIntObjVar( missionObj, QUEST_TRIGGER_DELAY ); boolean split = false; if ( triggerSC == 0 ) split = (triggerOn == -1); // Support for the old style else if ( (triggerSC == 1) || (triggerSC == 3) ) split = true; if ( split ) { dictionary params = new dictionary(); params.put( "quest", missionObj ); webster.put ("quest", missionObj ); params.put( "ends", true ); setObjVar( missionObj, "noAbort", 1 ); messageTo( player, "doSpecialEvent", params, triggerD+5, false ); } else { _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceWinner", webster, 1, false ); } } else { _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceWinner", webster, 1, false ); } return true; } boolean setQuestAborted( obj_id player, obj_id missionObj ) { string questName = getStringObjVar( missionObj, QUEST_NAME ); string questType = getStringObjVar( missionObj, QUEST_TYPE ); dictionary webster = new dictionary (); webster.put ("questName", questName); // Notify the player. string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp; if ( hasObjVar( missionObj, QUEST_DUTY ) ) pp = prose.getPackage( SID_DUTY_ABORTED, title ); else pp = prose.getPackage( SID_QUEST_ABORTED, title ); sendQuestSystemMessage( player, pp ); // GROUP: Notify the group. _groupNotify( player, SID_GROUP_QUEST_ABORTED, title ); _setQuestFlag( player, questType, questName, QUEST_ABORTED ); flagQuestGroup( missionObj, QUEST_ABORTED ); _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceFailed", webster, 1, false ); // Play music. play2dNonLoopingMusic( player, MUSIC_QUEST_ABORTED ); // Remove the quest journal entry. _removeQuestJournalEntry( player, questType+"/"+questName ); // Log it. string groupString = "GROUP:0"; if ( group.isGrouped( player ) ) { obj_id groupId = getGroupObject( player ); int numMembers = getGroupSize( groupId ); if ( numMembers > 0 ) { groupString = "GROUP:"+numMembers; } } CustomerServiceLog( "space_quest", "QUEST_ABORTED|V1|"+groupString+"|TIME:"+getGameTime()+"|PLAYER:"+player+"|TYPE:"+questType+"|NAME:"+questName+"|ZONE:"+getCurrentSceneName() ); return true; } boolean setSilentQuestAborted( obj_id player, obj_id missionObj ) { string questName = getStringObjVar( missionObj, QUEST_NAME ); string questType = getStringObjVar( missionObj, QUEST_TYPE ); dictionary webster = new dictionary (); webster.put ("questName", questName); _setQuestFlag( player, questType, questName, QUEST_ABORTED ); flagQuestGroup( missionObj, QUEST_ABORTED ); _removeQuest( player, questType, questName ); messageTo( player, "groundSpaceFailed", webster, 1, false ); // Remove the quest journal entry. _removeQuestJournalEntry( player, questType+"/"+questName ); // Log it. string groupString = "GROUP:0"; if ( group.isGrouped( player ) ) { obj_id groupId = getGroupObject( player ); int numMembers = getGroupSize( groupId ); if ( numMembers > 0 ) { groupString = "GROUP:"+numMembers; } } CustomerServiceLog( "space_quest", "QUEST_ABORTED|V1|"+groupString+"|TIME:"+getGameTime()+"|PLAYER:"+player+"|TYPE:"+questType+"|NAME:"+questName+"|ZONE:"+getCurrentSceneName() ); return true; } // Should be called after the quest is failed/won/aborted. // Should be called from the quest conversation reward event. boolean setQuestRewarded( obj_id player, string questType, string questName ) { // Notify the player. string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp = prose.getPackage( SID_QUEST_REWARDED, title ); sendQuestSystemMessage( player, pp ); _setQuestFlag( player, questType, questName, QUEST_RECEIVED_REWARD ); // Play music. play2dNonLoopingMusic( player, MUSIC_QUEST_WON ); return true; } // Internal function used to flag a quest as "in progress." // A quest in progress is failed upon death. boolean _setQuestInProgress( obj_id quest ) { return _setQuestInProgress( quest, false ); } boolean _setQuestInProgress( obj_id quest, boolean music ) { if ( hasObjVar( quest, "in_progress" ) ) return false; // Mark as in progress. setObjVar( quest, "in_progress", 1 ); // Notify the player. obj_id player = getObjIdObjVar( quest, QUEST_OWNER ); string questName = getStringObjVar( quest, QUEST_NAME ); string questType = getStringObjVar( quest, QUEST_TYPE ); string_id title = new string_id( "spacequest/"+questType+"/"+questName, "title" ); prose_package pp = prose.getPackage( SID_QUEST_IN_PROGRESS, title ); sendQuestSystemMessage( player, pp ); // Music. if ( music ) play2dNonLoopingMusic( player, space_quest.MUSIC_QUEST_MISSION_START ); // GROUP: Notify the group. _groupNotify( player, SID_GROUP_QUEST_IN_PROGRESS, title ); return true; } boolean isQuestInProgress( obj_id quest ) { if ( hasObjVar( quest, "in_progress" ) ) return true; else return false; } // Message the player with a quest update. void showQuestUpdate( obj_id quest, string_id update_id ) { // Notify the player. obj_id player = getObjIdObjVar( quest, QUEST_OWNER ); string questName = getStringObjVar( quest, QUEST_NAME ); prose_package pp; if ( hasObjVar( quest, QUEST_DUTY ) ) pp = prose.getPackage( SID_DUTY_UPDATE_S, update_id ); else pp = prose.getPackage( SID_QUEST_UPDATE_S, update_id ); sendQuestSystemMessage( player, pp ); _groupNotify( player, pp ); } // Message the player with a quest update. void showQuestUpdate( obj_id quest, string_id update_id, int count ) { // Notify the player. obj_id player = getObjIdObjVar( quest, QUEST_OWNER ); string questName = getStringObjVar( quest, QUEST_NAME ); prose_package pp; if ( hasObjVar( quest, QUEST_DUTY ) ) pp = prose.getPackage( SID_DUTY_UPDATE_IS, update_id, count ); else pp = prose.getPackage( SID_QUEST_UPDATE_IS, update_id, count ); sendQuestSystemMessage( player, pp ); _groupNotify( player, pp ); } // Message the player with a quest update. void showQuestAlert( obj_id player, string_id update_id ) { // Notify the player. prose_package pp = prose.getPackage( SID_QUEST_ALERT_S, update_id ); sendQuestSystemMessage( player, pp ); _groupNotify( player, pp ); } // Message the player with an escort update. void showEscortUpdate( obj_id quest, string_id update_id ) { // Notify the player. obj_id player = getObjIdObjVar( quest, QUEST_OWNER ); string questName = getStringObjVar( quest, QUEST_NAME ); prose_package pp = prose.getPackage( SID_ESCORT_UPDATE, update_id ); sendQuestSystemMessage( player, pp ); _groupNotify( player, pp ); } // Give the player a reward for a quest. void giveReward( obj_id player, string questType, string questName, int credits ) { giveReward( player, questType, questName, credits, null ); } void giveReward( obj_id player, string questType, string questName, int credits, string obj ) { // Flag as rewarded. setQuestRewarded( player, questType, questName ); // Wire the player the money. if ( credits > 0 ) { money.bankTo( money.ACCT_SPACE_QUEST_REWARD, player, credits ); prose_package pp = prose.getPackage( SID_QUEST_REWARD, credits ); sendQuestSystemMessage( player, pp ); } // Give the player an object. if ( obj != null ) { obj_id newItem = createObjectInInventoryAllowOverload( obj, player ); string objectName = utils.packStringId (getNameStringId(newItem)); prose_package pp = prose.getPackage( SID_QUEST_REWARD_OBJECT, objectName ); sendQuestSystemMessage( player, pp ); } } void clearQuestFlags( obj_id player, string questType, string questName ) { removeObjVar( player, QUEST_STATUS + "." + questType + "." + questName ); int questNum = questGetQuestId("spacequest/"+questType+"/"+questName); debugSpeakMsg(player, "Quest ID is" + questNum); questClearQuest(questNum, player); obj_id quest = _getQuest(player, questType, questName); if(isIdValid(quest) && exists(quest)) setQuestAborted(player, quest); } //------------------------------------------------------------------------------------------------ // Internal Quest Management // // These are private functions that shouldn't be called from outside the library or internal quest scripts. //------------------------------------------------------------------------------------------------ //------------------------------------------------ // _getQuestId //------------------------------------------------ int _getQuestId( String questName ) { return dataTableSearchColumnForString( questName, 0, "datatables/player/quests.iff" ); } //------------------------------------------------ // _setupQuest // Sets up the player object quest info. // // obj_id player - The player to give a quest to. // string questType - The quest table (by type) to look in. // dictionary questInfo-The quest datatable row. // returns boolean - True if the quest setup was successful. //------------------------------------------------ boolean _setupQuest( obj_id player, string questType, string questName, string qTable, boolean duty ) { // Don't let them take another duty quest. if ( duty && _hasDutyQuest( player ) ) { sendQuestSystemMessage( player, SID_DUTY_ALREADY ); return false; } // Get the player's datapad. We store mission data in there. obj_id datapad = utils.getPlayerDatapad( player ); if ( !isIdValid( datapad ) ) { sendSystemMessageTestingOnly( player, "_setupQuest: Unable to get datapad for player " + player ); return false; } // Create a quest object for the player. // For now we are using ground mission objects. obj_id missionObj = createObject( QUEST_OBJECT_TEMPLATE, datapad, "" ); if ( !isIdValid( missionObj ) ) { sendSystemMessageTestingOnly( player, "_setupQuest: Unable to create a mission object for quest." ); return false; } // Get the first line of the quest data. "questInfo" dictionary questInfo = dataTableGetRow( qTable, 0 ); // Write the quest info to the mission object. setObjVar( missionObj, QUEST_OWNER, player ); setObjVar( missionObj, QUEST_NAME, questName ); setObjVar( missionObj, QUEST_TYPE, questType ); setObjVar( missionObj, QUEST_ZONE, questInfo.getString( "questZone" ) ); setObjVar( missionObj, "self", missionObj ); if ( duty ) setObjVar( missionObj, QUEST_DUTY, duty ); // Setup some mission object data. setMissionDescription( missionObj, new string_id( "spacequest/"+questType+"/"+questName, "title_d" ) ); setMissionTitle( missionObj, new string_id( "space/quest", questType ) ); if ( questType == "space_battle" || questType == "space_mining_destroy" ) { setMissionType( missionObj, questType ); } else if ( questType == "assassinate" ) { setMissionType( missionObj, "space_assassination" ); } else if ( questType == "inspect" ) { setMissionType( missionObj, "space_inspection" ); } else if ( questType == "destroy_surpriseattack" ) { setMissionType( missionObj, "space_surprise_attack" ); } else if ( questType == "delivery_no_pickup" ) { setMissionType( missionObj, "space_delivery" ); } else { setMissionType( missionObj, "space_"+questType ); } // Auto Rewards: Cash rewards wired to the player with an email. Any quest can have an auto reward. // Narrative quests should probably require you to return to the quest giver or some other person, though. int autoReward = questInfo.getInt( "autoReward" ); if ( autoReward > 0 ) setObjVar( missionObj, QUEST_AUTO_REWARD, autoReward ); // Quest triggers: Special events that happen during the quest. int triggerEvent = questInfo.getInt( "triggerEvent" ); if ( triggerEvent > 0 ) { setObjVar( missionObj, QUEST_TRIGGER_EVENT, triggerEvent ); setObjVar( missionObj, QUEST_TRIGGER_SC, questInfo.getInt( "triggerSplitCondition" ) ); setObjVar( missionObj, QUEST_TRIGGER_ARG, questInfo.getString( "triggerArg" ) ); setObjVar( missionObj, QUEST_TRIGGER_ARG2, questInfo.getString( "triggerArg2" ) ); setObjVar( missionObj, QUEST_TRIGGER_ON, questInfo.getInt( "triggerOn" ) ); if ( questInfo.getInt( "triggerEnds" ) == 1 ) setObjVar( missionObj, QUEST_TRIGGER_ENDS, 1 ); if ( questInfo.getInt( "triggerEndsMission" ) == 1 ) setObjVar( missionObj, QUEST_TRIGGER_ENDS, 1 ); setObjVar( missionObj, QUEST_TRIGGER_DELAY, questInfo.getInt( "triggerDelay" ) ); } // Activate the root quest journal entry. int questid = questGetQuestId( "spacequest/"+questType+"/"+questName ); if ( (questid != 0 ) && (questCanActivateQuest( questid, player ) == 0) ) questActivateQuest( questid, player, null ); // Attach quest scripts. attachScript( missionObj, "space.quest_logic." + questType ); // Attach player quest script. if ( !hasScript( player, "space.quest_logic.player_spacequest" ) ) attachScript( player, "space.quest_logic.player_spacequest" ); // Clear completion flags associated with this quest. _clearQuestFlag( player, questType, questName, QUEST_ABORTED ); _clearQuestFlag( player, questType, questName, QUEST_FAILED ); _clearQuestFlag( player, questType, questName, QUEST_WON ); return true; } //------------------------------------------------ // _removeQuest // Removes the quest object from the player. // // obj_id player - The player to give a quest to. // string questType - The quest table (by type) to look in. // string questName - The quest name. // returns boolean - True if removal was successful. //------------------------------------------------ boolean _removeQuest( obj_id player, string questType, string questName ) { obj_id missionObj = _getQuest( player, questType, questName ); _removeQuest( player, missionObj ); return true; } boolean _removeQuest( obj_id player, obj_id missionObj ) { _removeMissionCriticalShips( player, missionObj ); destroyObject( missionObj ); return true; } //------------------------------------------------ // _hasDutyQuest //------------------------------------------------ boolean _hasDutyQuest( obj_id player ) { obj_id datapad = utils.getPlayerDatapad( player ); if ( isIdValid( datapad ) ) { // Search for any quest in the datapad. obj_id[] dpobjs = getContents( datapad ); for ( int i=0; i= 1, and can assume the 2nd NPC also knows the player * if familiarity is >= 5, and so on. * * --------------------------------------------------*/ const string FAMILIARITY_LIST = "familiarity."; /** setFamiliarity( obj_id player, string label, int value ) * * Sets the familiarity level of player to a specific value for the given label * * player - the player whose level you want to set * label - the label you want to set the level with * value - the value you want to set the level to * * returns nothing */ void setFamiliarity( obj_id player, string label, int value ) { if ( !isPlayer(player) ) return;//only set these on players setObjVar( player, FAMILIARITY_LIST + label, value ); } /** getFamiliarity( obj_id player, string label) * * gets the familiarity level of player with a specific label * * player - the player whose level you want to set * label - the label you want to set the level with * * returns the value of the player's familiarity with this label */ int getFamiliarity( obj_id player, string label ) { return (getIntObjVar( player, FAMILIARITY_LIST + label)); } /** adjustFamiliarity( obj_id player, string label, int value) * * gets the familiarity level of player with a specific label * * player - the player whose level you want to set * label - the label you want to set the level with * value - the +/- value you want to add/subtract from the * player's familiarity level with the given label * * returns the value of the player's familiarity with this label */ int adjustFamiliarity( obj_id player, string label, int value ) { int currentFamiliarity = getFamiliarity( player, label ); currentFamiliarity += value; setFamiliarity( player, label, currentFamiliarity ); return currentFamiliarity; } /** clearFamiliarity( obj_id player, string label ) * * erases the familiarity level of player with a specific label * * player - the player whose level you want to set * label - the label you want to set the level with * * returns nothing */ void clearFamiliarity( obj_id player, string label ) { removeObjVar( player, FAMILIARITY_LIST + label ); } boolean isPlayerQualifiedForSkill(obj_id objPlayer, string strSkill) { boolean boolHasSkills = skill.hasRequiredSkillsForSkillPurchase(objPlayer, strSkill); boolean boolHasXp = skill.hasRequiredXpForSkillPurchase(objPlayer, strSkill); if((boolHasSkills)&&(boolHasXp)) { return true; } return false; } //------------------------------------------------ // getRandomPositionInSphere //------------------------------------------------ transform getRandomPositionInSphere( transform trStartPosition, float fltMinRadius, float fltMaxRadius ) { float fltDistance = rand( fltMinRadius, fltMaxRadius ); vector vctOffset= vector.randomUnit().multiply( fltDistance ); transform transform_w = trStartPosition; transform_w = transform_w.move_p( vctOffset ); return transform_w; } //------------------------------------------------ // cleanArray //------------------------------------------------ void cleanArray( obj_id quest, string objVar, string[] array ) { if ( array == null ) return; // Clean off any dead space at the end. int k = 0; for ( int i=0; i 0) return true; return false; } //------------------------------------------------ // hasCertifiedShip //------------------------------------------------ boolean hasCertifiedShip(obj_id player) { if(!isIdValid(player) || !isPlayer(player)) return false; obj_id[] shipControlDevices = space_transition.findShipControlDevicesForPlayer(player); if(shipControlDevices == null || shipControlDevices.length == 0) return false; for(int i = 0; i < shipControlDevices.length; i++) { obj_id ship = space_transition.getShipFromShipControlDevice(shipControlDevices[i]); if(!isIdValid(ship)) return false; if(hasCertificationsForItem(player, ship)) { string template = getTemplateName(ship); if(template.endsWith("player_sorosuub_space_yacht.iff")) // Don't count the luxury yacht in this check continue; else return true; } } return false; } //------------------------------------------------ // hasCertifiedNonNewbieShip //------------------------------------------------ boolean hasCertifiedNonNewbieShip(obj_id player) { if(!isIdValid(player) || !isPlayer(player)) return false; obj_id[] shipControlDevices = space_transition.findShipControlDevicesForPlayer(player); if(shipControlDevices == null || shipControlDevices.length == 0) return false; for(int i = 0; i < shipControlDevices.length; i++) { obj_id ship = space_transition.getShipFromShipControlDevice(shipControlDevices[i]); if(!isIdValid(ship)) return false; if(hasCertificationsForItem(player, ship)) { string template = getTemplateName(ship); if(template.endsWith("player_basic_tiefighter.iff")) continue; else if(template.endsWith("player_basic_z95.iff")) continue; else if(template.endsWith("player_basic_hutt_light.iff")) continue; else if(template.endsWith("player_sorosuub_space_yacht.iff")) // Don't count the luxury yacht in this check continue; else return true; } } return false; } //------------------------------------------------ // canGrantNewbieShip //------------------------------------------------ boolean canGrantNewbieShip(obj_id player) { if(hasCertifiedShip(player)) return false; if(!space_transition.isPlayerBelowShipLimit(player)) return false; return true; } //------------------------------------------------ // grantNewbieShip // // valid factions: // "imperial", "rebel", "neutral" //------------------------------------------------ void grantNewbieShip( obj_id player, string faction ) { obj_id datapad = utils.getDatapad( player ); if ( !isIdValid(datapad) ) return; string pcd = null; string ship = null; if ( faction.equals( "imperial" ) ) { pcd = "object/intangible/ship/tiefighter_pcd.iff"; ship = "object/ship/player/player_prototype_tiefighter.iff"; } else if ( faction.equals( "rebel" ) ) { pcd = "object/intangible/ship/z95_pcd.iff"; ship = "object/ship/player/player_prototype_z95.iff"; } else if ( faction.equals( "neutral" ) ) { pcd = "object/intangible/ship/hutt_light_s01_pcd.iff"; ship = "object/ship/player/player_prototype_hutt_light.iff"; } obj_id opcd = createObject( pcd, datapad, ""); if ( !isIdValid(opcd) ) sendSystemMessageTestingOnly( player, "Critical failure: Could not create a pcd for the player!" ); else { obj_id oship = createObject( ship, opcd, ""); if ( isIdValid( oship ) ) { setOwner( oship, player ); if ( faction.equals( "imperial" ) ) { setShipComponentMass(oship, ship_chassis_slot_type.SCST_reactor , 1500); //reactor mass setShipReactorEnergyGenerationRate(oship, 8000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_engine, 1500); //engine mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_engine, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_shield_0, 1500); //shield mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_shield_0, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_armor_0, 1500); //armor mass 1 setShipComponentMass(oship, ship_chassis_slot_type.SCST_armor_1, 1500); //armor mass 2 setShipComponentMass(oship, ship_chassis_slot_type.SCST_capacitor, 1500); //capacitor mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_capacitor, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_weapon_0, 1500); //weapon mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_weapon_0, 2000); } else if ( faction.equals( "rebel" ) || faction.equals( "neutral" )) { setShipComponentMass(oship, ship_chassis_slot_type.SCST_reactor , 1500); //reactor mass setShipReactorEnergyGenerationRate(oship, 8000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_engine, 1500); //engine mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_engine, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_shield_0, 1500); //shield mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_shield_0, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_armor_0, 1500); //armor mass 1 setShipComponentMass(oship, ship_chassis_slot_type.SCST_armor_1, 1500); //armor mass 2 setShipComponentMass(oship, ship_chassis_slot_type.SCST_capacitor, 1500); //capacitor mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_capacitor, 2000); setShipComponentMass(oship, ship_chassis_slot_type.SCST_weapon_0, 1500); //weapon mass setShipComponentEnergyMaintenanceRequirement(oship, ship_chassis_slot_type.SCST_weapon_0, 2000); } } else sendSystemMessageTestingOnly( player, "Critical failure: Could not create a ship for the player's pcd!" ); } } //------------------------------------------------ // _groupNotify // // Messages the player's group, but not the player. //------------------------------------------------ void _groupNotify( obj_id player, string_id mid, string_id s1 ) { if ( group.isGrouped( player ) ) { obj_id gid = getGroupObject( player ); obj_id[] members = space_utils.getSpaceGroupMemberIds( gid ); if ( members != null ) { for ( int i=0; i