Objectives
Originally written by Ishtvan on http://forums.thedarkmod.com/topic/4121 additions by greebo
This article covers details of the objectives system. Note that mappers can use Dark Radiant's Objectives Editor to make it easier to create objectives.
The objectives system exists to determine when the player has completed the mission. At the highest level, a mission consists of a list of objectives to be completed and boolean logic to determine when the mission has succeeded or failed based on which of the objectives are complete. At the objective level, an objective consists of individual components and boolean logic to determine when the objective is completed or failed. At the component level, each component has a type, a specifier, and arguments. These will be explained fully in the components section.
Mission
Objectives are stored by entities named target_tdm_addobjectives. Your mission can have several of these but typically you will only want one for all your objectives.
By default these entities self-trigger (activates themselves) at mission start. If you need to delay this and activate it later (via any trigger entity that can have a target spawnarg) then add the spawnarg wait_for_trigger with a value of 1.
When triggered, these entities add objectives to the mission based on their spawnargs.
Spawnargs governing mission level behavior may be entered on any target_addobjectives. Non-empty spawnargs will be overwritten by the most recently triggered target_addobjectives (FM authors may want to do this if they add an objective later on and want to change the mission success logic to include that objective, for example). The following is a list of the mission-level spawnargs and what they mean:
Success/Failure Logic
By default, all mandatory objectives need to be completed for mission success. However, it's possible to define a custom success and failure logic string on an objective entity. Be sure to have this addobjectives entity active at map start so that the logic is parsed right at map load.
Default Logic
- mission_logic_success (string)
- A string of boolean logic applied to individual objective states to determine if the mission succeeds. AND, OR, NOT and parenthesis are available. The FM author can construct strings using these and the integer index for an objective. For example: "1 OR (NOT(2) AND 3)" would complete the mission if either objective 1 is true, or if 2 is false and 3 is true. By default, a mission succeeds when all mandatory objectives are completed.
- mission_logic_failure (string)
- Boolean logic applied to individual objective component states to determine if the mission has failed. By default, if any mandatory objective fails, the mission fails.
Difficulty Dependent Logic
The above spawnargs apply to all difficulty levels. It's of course possible to override this default for a given difficulty level, by setting these spawnargs. The index N is referring to the difficulty level number (starting from 0 = Easy):
- mission_logic_success_diff_N (string)
- Defines the success logic for the difficulty level N (override the default logic), e.g. "1 AND 2 AND 3".
- mission_logic_failure_diff_N (string)
- Defines the failure logic for the difficulty level N (override the default logic). When this condition is fulfilled, the mission has failed.
If specific logic is not specified for a certain difficulty level, the default spawnargs (without _diff_*) will be used.
Objectives
Objectives added by the target_addobjectives each have a unique integer index, starting with objective 1, the first objective. The spawnargs associated with an objective are prefixed in the following manner:
obj<objective index>_<spawnarg name>
Example: obj3_desc
The spawnargs (minus prefix) and meanings for objective attributes are listed below. Each objective also includes a list of components and their attributes, which will be covered in the next section.
- objN_desc (string)
- The description of this objective. This is used to display the objective in the GUI - the user will never see the objective number, only this string. The default value is an empty string.
- objN_state (int) Defines the state of the objective at map start.
- Possible Objective States:
- 0 = STATE_INCOMPLETE
- 1 = STATE_COMPLETE
- 2 = STATE_INVALID
- 3 = STATE_FAILED
- Code defaults to 0 = incomplete.
- objN_mandatory (1/0)
- Set to 0 for optional objective that doesn't fail the mission if you fail it. Objective must be completed to complete the mission if 1. Code defaults to 1.
- objN_irreversible (1/0)
- Irreversible objectives lock their state once they're changed to COMPLETE or FAILED. Irreversible components will only change state once and then stay in that state. (Note: There are scriptfunctions to reset irreversible objectives and components if the FM author wants to let them change again for some reason). Code defaults to 0.
- objN_ongoing (1/0)
- If true, this objective is not marked as complete and the player is not informed that it's complete until the end of the mission. Typically used for "do not kill" type objectives, because technically they would be complete right from the beginning, but we don't want to mark them off or tell the player they're complete right at the beginning. Could also be used for "have some item with you when you complete the mission." Code defaults to 0.
- objN_visible (1/0)
- Whether the objective appears in the player's Objectives GUI. Use invalidated and hidden objectives to queue up objectives for later, or hidden but valid objectives as another way of scripting events when the player does a certain thing, since objectives can launch an arbitrary script after they're completed. Code defaults to 1.
- objN_difficulty (integer)
- Space delimited list of difficulty levels that should include this objective. For example, "2 3" would enable this objective at difficulty levels 2 and 3, but not for other difficulty levels. Default value is empty - the objective applies to all difficulty levels.
- Note: When the objective is not active for a given difficutly, it is invalidated and hidden, but it still exists, so the numbering of the next objective should still increase by 1. E.g., objective 5 only appears on hard difficulty, but you still have to call the next objective objective 6, you can't call it objective 5 only appearing on easy or medium difficulty.
- objN_enabling_objs (space-delimited integers)
- This is a list of objectives which must be completed before this objective can be completed. For example, we shouldn't mark "objective complete" for exiting the mission until all the other mission goals have been completed. An example value for this spawnarg would be "4 6" to let this objective depend on objectives #4 and #6.
- objN_script_complete (string)
- objN_script_failed (string)
- Name of a script to call when this objective is completed or failed. For example, this could be used in campaigns to set up optional objectives in one mission that effect other missions. This feature could also be used as a general scripting aide if the author wants to set up the conditions for something happening using a hidden objective.
- objN_target_complete (string)
- objN_target_failed (string)
- Name of a target entity to call when this objective is completed or failed, similar to the completion/failure script. Only one entity name is allowed here. If you need to trigger multiple entities on objective state change, use a trigger_relay entity.
- objN_logic_success (string)
- Boolean logic applied to individual objective component states to determine if the objective is complete. The same boolean logic options are available as in the mission-level logic described above. By default, all of the components must be true to complete the objective. In other words, all components are ANDed together (1 AND 2 AND 3...).
- objN_logic_failure (string)
- Boolean logic applied to individual objective component states to determine if the objective is failed. By default, if an objective is ongoing, and any opponent becomes false, that objective will fail.
Objective Components
Each objective has an associated list of components and spawnargs defining the attributes of those components. These component spawnargs associated with a given objective are prefixed in the following manner:
obj<objective index>_<component index>_<spawnarg name>
Example: obj1_2_state
In the following, the index N defines the objective number and M refers to the component number. Programmers beware that both numbers start from the value 1.
- objN_M_state (1/0)
- Defines the state of the component at map start, which can only be "1" (complete) or "0" (incomplete). Code defaults to 0.
- objN_M_player_responsible (1/0)
- When true, the player must be responsible for the events that satisfy this component. Events the player is not responsible for are not counted. (e.g., the player kills an AI vs. another AI killing an AI). Code defaults to 1.
- objN_M_not (1/0)
- Code defaults to 0. If true, this component is logically NOTed, so when the conditions described by the type and speifiers are not met, the component state is true, and when they are met, it is false.
- objN_M_irreversible (1/0)
- When the component state changes from its initial state, it will stay in its new state regardless of what happens later. (There is a script event to "unlatch" a latched objective component if needed). Code defaults to 0.
- objN_M_type (string)
- Defines the type of this objective component. See the section Component Types below for detailed information.
- Possible 'Objective Component' Types:
- kill (also includes non-living things being destroyed)
- ko
- ai_find_item (not implemented)
- ai_find_body (not implemented)
- alert
- destroy (inanimate item destroyed ("killed"))
- item (picked up or dropped an inventory item)
- pickpocket (inventory item acquired from conscious AI)
- location (Item A at location B, where "A" is any entity that must have the key "objective_ent" set to "1" on it, and location B is a special entity info_objective_location. Later on we may support using the D3 location system as well)
- custom (custom component that is set true/false by a map script)
- These components are triggered every N milliseconds by a system clock:
- custom_clocked (runs a custom map script that should check something and set the objective)
- info_location (Item A at info_location area B) - TODO: more information needed
- distance (distance between the origin of entity X and that of entity Y)
Any component can have up to two specifiers, each of which can carry a value. Here, K refers to the number of the specifier, e.g. ("obj2_1_spec4" "name").
- objN_M_specK (string)
- Specification Methods:
- Specification methods determine what the objective component is looking for. Does it look at how many AI of a specific team you've KO'd, how many humans, how many overall, or whether you've KO'd an AI with a certain name? Each objective component can have up to two of the following specifiers (Not all components can take two specifiers, and not all specifiers make sense for all component types, future documentation will cover that). See the section Specifiers below for a detailed description.
- The following apply to both AIs and items:
- none
- name (name of entity)
- overall (overall count of something: kills, loot, kos, etc)
- group (inventory group)
- classname (soft/scripting classname)
- spawnclass (hard / SDK classname, e.g. "idPlayer")
- The following specification methods apply only to AI:
- ai_type (human, beast, undead, bot, etc)
- ai_team (mapper defined team of the AI)
- ai_innocence (For distinguishing noncombatants)
- objN_M_spec_valK
- Value that the Kth specifier should match in order to count towards this component. (E.g., an entity name if we specify by name.)
- objN_M_args (space-delimited string)
- Integer and String Arguments:
- A component can have an arbitrary number of int and string arguments that are fed in space delimited lists. The type of component and specifier determines which args it will use and what it will do with them. For example, the first integer arg is often used to determine how many times something can happen, e.g., the player is only allowed 4 KOs, or must get 500 loot. Not all events are counted up though. The only things that are counted up are things in the mission statistics like loot and kills, so everything else is pretty much a one shot deal where it happens once. Future documentation will detail which objective events are counted up and which are one shots.
- objN_M_clock_interval (float/seconds)
- For clocked components like "custom_clocked" this determines how often they are updated. The value is to be set in seconds.
Component Types
AI-Related Components
The following specifiers are termed "Standard AI Specifiers": All specifiers minus the group specifier.
- kill
- An AI must be killed to satisfy this component.
- SDK Name: COMP_KILL
- Number of Specificatons: 1 (object that must be killed)
- Applicable Specifiers: Standard AI Specifiers
- Expected Arguments: int (number of kills required)
- ko (Knockout)
- An AI must be knocked out by any means (gas, blackjack, other).
- SDK Name: COMP_KO
- Number of Specificatons: 1 (AI that must be KO'd)
- Applicable Specifiers: Standard AI Specifiers
- Expected Arguments: int (number of KOs required)
- ai_find_item
- not yet implemented
- SDK Name: COMP_AI_FIND_ITEM
- Number of Specificatons: --
- Applicable Specifiers: --
- Expected Arguments: --
- ai_find_body
- not yet implemented An AI must find a body. Currently, the specifiers apply to the bodies found, and any AI finding the bodies triggers this component. Future revisions might add another specifier for the AI that finds the body.
- SDK Name: COMP_AI_FIND_BODY
- Number of Specificatons: 1 (Type of body that must be found)
- Applicable Specifiers: Standard AI Specifiers
- Expected Arguments: int (number of bodies that must be found)
- alert
- True if an AI or type of AI is alerted. Use the player_responsible flag to determine if this should count alerts not caused by the player's actions (e.g., enemy AI being sighted)
- SDK Name: COMP_ALERT
- Number of Specificatons: 1 (AI that must be alerted)
- Applicable Specifiers: Standard AI Specifiers
- Expected Arguments: int (number of alerts required), int (minimum alert level that counts: 1-5 with 5 being the combat state)
Non-AI Components
- destroy
- An inanimate item must be destroyed (damage it until its health is lower than zero).
- SDK Name: COMP_DESTROY
- Number of Specificatons: 1 (item to be destroyed)
- Applicable Specifiers: all
- Expected Arguments: int (number of items to be destroyed)
- item
- Completed when the player has this inventory item or loot.
- SDK Name: COMP_ITEM
- Number of Specificatons: 1 (item the player must have)
- Applicable Specifiers: All except AI type, team and innocence.
- Expected Arguments: int (number of items to have, 1 by default)
- pickpocket
- An inventory item must be taken from a conscious AI. Note that it doesn't matter what happens to the item after it is pickpocketed. It could be destroyed or dropped, but as long as it was originally pickpocketed, it counts.
- SDK Name: COMP_PICKPOCKET
- Number of Specificatons: 1 (Item the player must pickpocket)
- Applicable Specifiers: All except AI type, team and innocence
- Expected Arguments: int (number of items to pickpocket, 1 by default)
- location Object in location brush (using info_tdm_objective_location brush)
- A particular item must be in a particular location, defined by an info_objective_location brush. For optimization reasons, the entity or entities to be checked must also have this spawnarg: "objective_ent" set to "1". NOTE: Multiple objects in a single location are not counted up for this component, this is a single-shot objective. If you want the player to put more than one object in a location, you must currently create several of these components, specifying by name, and OR them together in the objective logic.
- SDK Name: COMP_LOCATION
- Number of Specificatons: 2 (object entity to check, location entity to check)
- Applicable Specifiers: For the object to check, all specifiers apply, but multiple objects are not counted up. Typically the objects will be specified by name. For the location to check, it will either be specified by name or by group.
- Expected Arguments: None (if both specifiers match, the objective is completed).
- info_location Object in location area (using info_location entities)
- A particular item must be in a particular location, defined by an info_location area (see vanilla D3 documentation for info_location). Multiple objects in a single location are not counted up for this component, this is a single-shot objective. If you want the player to put more than one object in a location, you must currently create several of these components, specifying by name, and OR them together in the objective logic.
- This check is done periodically. Set spawnarg clock_interval to the time in seconds between periodic checks.
- SDK Name: COMP_INFO_LOCATION
- Number of Specificatons: 2 (object entity to check, location entity to check)
- Applicable Specifiers: For the object to check, all specifiers apply, but multiple objects are not counted up. Typically the objects will be specified by name. For the location to check, it will either be specified by name or by group.
- Expected Arguments: None (if both specifiers match, the objective is completed).
- custom
- This custom objective component is only changed when an external script changes it. Otherwise it does nothing.
- SDK Name: COMP_CUSTOM_ASYNC
- Number of Specificatons: none
- Applicable Specifiers: none
- Expected Arguments: none
- custom_clocked
- This component calls a custom script periodically to check some condition. When run, the script is expected to this component true or false based on the custom check. This objective component could also be used as a convenient method to periodically call a script that has nothing to do with objectives.
- Set spawnarg clock_interval to the time in seconds between periodic calls of the script.
- SDK Name: COMP_CUSTOM_CLOCKED
- Number of Specificatons: none
- Applicable Specifiers: none
- Expected Arguments: string (name of the script to call periodically)
- distance
- Two entities must be sufficiently close to eachother to satisfy this component. The distance check is done between the origin of the first object and that of the second object. If this distance is below the input maximum distance, the component is set to true.
- This check is done periodically. Set spawnarg "clock_interval" to the time in seconds between periodic checks.
- SDK Name: COMP_DISTANCE
- Number of Specificatons: none (see arguments below)
- Applicable Specifiers: none
- Expected Arguments: string (name of first entity), string (name of second entity), int (maximum distance in doom units)
- readable_opened (since TDM 1.02)
- The player must open a named readable to satisfy this component. This doesn't refer to a specific page, just opening the readable is enough.
- SDK Name: COMP_READABLE_OPENED
- Number of Specificatons: 1 (The name of the readable)
- Applicable Specifiers: name
- Expected Arguments: none
- readable_closed (since TDM 1.02)
- The player must close a named readable to satisfy this component. This doesn't check whether the player has finished reading all pages, just closing the previously opened readable is enough.
- SDK Name: COMP_READABLE_CLOSED
- Number of Specificatons: 1 (The name of the readable)
- Applicable Specifiers: name
- Expected Arguments: none
- readable_page_reached (since TDM 1.02)
- The player must open/read a specific page of a named readable to fulfil this component. Scrolling to the page is enough to trigger this event.
- SDK Name: COMP_READABLE_PAGE_REACHED
- Number of Specificatons: 1 (The name of the readable)
- Applicable Specifiers: name
- Expected Arguments: int (the page number which should be reached, the number 1 refers to the first page)
Specifiers
- none
- No specifier. Component will be completed as soon as any event of that component type happens. (E.g., if you set a KO component type, with specifier none, it would be set to true as soon as soon as the player KO's anyone). This is the default specifier if none is entered. Not very useful, except for blanket "don't do this" objectives. Even then, it is better to use "overall" described below.
- SDK Name: SPEC_NONE
- Specifier Argument Type Expected: No spec argument needed since we don't have to match anything.
- name
- Name of the entity
- SDK Name: SPEC_NAME
- Specifier Argument Type Expected: string
- overall
- In the context of inventory item objectives, this refers to overall loot. In the context of AI objectives, this refers to all AI, regardless of team, type, etc.
- SDK Name: SPEC_OVERALL
- Specifier Argument Type Expected: No spec argument needed since we don't have to match anything.
- Group
- For inventory items, the item's inv_name spawnarg value will be used here. For example, if you want the player to get 5 flashbombs, you would use this with group equal to the inv_name of flashbombs. As opposed to getting a specific flashbomb in the map, where you would use the name specifier instead. If an inventory item is loot, the loot group is used here instead (e.g., loot_gold).
- Group is also used for other component types as a convenient way to group things, such as info_objective_location or info_location entities checked by COMP_LOCATION and COMP_INFO_LOCATION, respectively.
- SDK Name: SPEC_GROUP
- Specifier Argument Type Expected: string
- Loot group names: loot_gold, loot_goods, loot_jewels and loot_total
- Classname
- The entityDef classname of the entity. For example: atdm:ai_builder_guard
- SDK Name: SPEC_CLASSNAME
- Specifier Argument Type Expected: string
- Spawnclass
- The SDK classname of the entity. For example: idAI
- SDK Name: SPEC_SPAWNCLASS
- Specifier Argument Type Expected: string
- ai_type
- Type of AI: human, beast, undead, steambot, etc. (reads m_AIType in the SDK, which is set by the "type" spawnarg on idActors. Not sure if this is implemented yet).
- SDK Name: SPEC_AI_TYPE
- Specifier Argument Type Expected: int
- ai_team
- The AI's team integer. This can be set up by the mapper to put AI on arbitrary teams. Mapper can also team up AI for objective purposes, and use this team specifier as a fast way of saying "KO any of these 5 AI." Alternatively, one could use the name specifier and put in multiple components for each of those 5 AI, then OR all the components in the objective success logic. Making a new team may save time over that method.
- SDK Name: SPEC_AI_TEAM
- Specifier Argument Type Expected: int
- ai_innocence
- Differentiates between combatants and non-combatants. A value of 1 = non-combatant, a value of 0 => non-combatant, or otherwise not deemed "innocent" by the FM author.
- SDK Name: SPEC_AI_INNOCENCE
- Specifier Argument Type Expected: int (1 = innocent, 0 = not)
Conclusion
Using components that combine component types with up to 2 entity arguments and 2 specifier types, plus objectives that are a boolean combination of those components should hopefully allow for a variety of different objectives.