Objectives: Difference between revisions
added target_complete/target_failed spawnargs |
|||
(20 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
{{Original_Reference|Ishtvan|4121}} ''additions by greebo'' | {{Original_Reference|Ishtvan|4121}} ''additions by greebo'' | ||
This article covers details of the objectives system. Note that mappers can use | |||
DarkRadiant'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. | 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 = | = Mission = | ||
Objectives are stored by entities named | Objectives are stored by entities named ''atdm_target_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: | 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: | ||
Line 40: | Line 47: | ||
*;<tt>objN_state</tt> (int) Defines the state of the objective at map start. | *;<tt>objN_state</tt> (int) Defines the state of the objective at map start. | ||
:'''Possible Objective States:''' | :'''Possible Objective States:''' | ||
:* 0 = | :* 0 = STATE_INCOMPLETE | ||
:* 1 = | :* 1 = STATE_COMPLETE | ||
:* 2 = | :* 2 = STATE_INVALID | ||
:* 3 = | :* 3 = STATE_FAILED | ||
:Code defaults to 0 = incomplete. | :Code defaults to 0 = incomplete. | ||
Line 71: | Line 78: | ||
*;<tt>objN_target_complete</tt> (string) | *;<tt>objN_target_complete</tt> (string) | ||
*;<tt>objN_target_failed</tt> (string) | *;<tt>objN_target_failed</tt> (string) | ||
:Name of a target entity to | :Name of a target entity to [[Triggers|trigger]] 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 [[Triggers#trigger_relay|trigger_relay]] entity. | ||
*;<tt>objN_logic_success</tt> (string) | *;<tt>objN_logic_success</tt> (string) | ||
Line 204: | Line 211: | ||
;*<tt>location</tt> Object in location brush (using info_tdm_objective_location brush) | ;*<tt>location</tt> Object in location brush (using info_tdm_objective_location brush) | ||
:A particular item must be in a particular location, defined by an | :A particular item must be in a particular location, defined by an info_tdm_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 AND them together in the objective logic. | ||
:'''SDK Name''': COMP_LOCATION | :'''SDK Name''': COMP_LOCATION | ||
:'''Number of Specificatons''': 2 (object entity to check, location entity to check) | :'''Number of Specificatons''': 2 (object entity to check, location entity to check) | ||
Line 211: | Line 218: | ||
;*<tt>info_location</tt> Object in location area (using info_location entities) | ;*<tt>info_location</tt> 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 | :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 AND them together in the objective logic. | ||
:This check is done periodically. Set spawnarg <tt>clock_interval</tt> to the time in seconds between periodic checks. | :This check is done periodically. Set spawnarg <tt>clock_interval</tt> to the time in seconds between periodic checks. | ||
:'''SDK Name''': COMP_INFO_LOCATION | :'''SDK Name''': COMP_INFO_LOCATION | ||
Line 239: | Line 246: | ||
:'''Number of Specificatons''': none (see arguments below) | :'''Number of Specificatons''': none (see arguments below) | ||
:'''Applicable Specifiers''': none | :'''Applicable Specifiers''': none | ||
:'''Expected Arguments''': string (name of first entity), string (name of second entity), int (maximum distance in doom units) | :'''Expected Arguments''': string (name of first entity), string (name of second entity), int (maximum distance in doom units). If one of the entities is the player, use the string 'player1'. | ||
;*<tt>readable_opened</tt> | |||
:''available 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 | |||
;*<tt>readable_closed</tt> | |||
:''available 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 | |||
;*<tt>readable_page_reached</tt> | |||
:''available 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 == | == Specifiers == | ||
Line 288: | Line 319: | ||
:'''SDK Name''': SPEC_AI_INNOCENCE | :'''SDK Name''': SPEC_AI_INNOCENCE | ||
:'''Specifier Argument Type Expected''': int (1 = innocent, 0 = not) | :'''Specifier Argument Type Expected''': int (1 = innocent, 0 = not) | ||
= Objective Conditions = | |||
With the campaign code added to TDM 1.06 a new set of spawnargs can be used to let objectives depend on those in a previous mission. These spawnargs are starting with the <tt>obj_condition</tt> prefix. | |||
Each condition defines the "source" mission and the source objective by number, plus the state of the source objective. It also needs to specify the "target" objective (which is always in the current mission) by number and which action to take. Right now, three different actions are supported: | |||
* Change Objective State (complete, failed, etc.) | |||
* Change the Objective's Visibility (hide or show) | |||
* Change the Objective's Mandatory Flag | |||
== Syntax == | |||
*;<tt>obj_condition_N_src_mission</tt> (integer) | |||
:Defines the source mission the condition depends on. The integer is 0-based, i.e. the first mission has the number 0. The placeholder N is the number of the condition. | |||
*;<tt>obj_condition_N_src_obj</tt> (integer) | |||
:Defines the number of the source objective in the source mission. The integer is 0-based, i.e. the first objective has the number 0. The placeholder N is the number of the condition. | |||
*;<tt>obj_condition_N_src_state</tt> (integer) | |||
:Defines the state the source objective needs to be in for this condition to apply. The value can be in the same range as the objN_state spawnarg above: | |||
:* 0 = STATE_INCOMPLETE | |||
:* 1 = STATE_COMPLETE | |||
:* 2 = STATE_INVALID | |||
:* 3 = STATE_FAILED | |||
*;<tt>obj_condition_N_target_obj</tt> (integer) | |||
:Defines the number of the target objective in the current mission. The integer is 0-based, i.e. the first objective has the number 0. The placeholder N is the number of the condition. | |||
*;<tt>obj_condition_N_type</tt> (string) | |||
:Defines the action to take if the condition applies. There are three possible action types available: | |||
:* "changestate" Change the state of the target objective. | |||
:* "changevisibility" Change the visibility of the target objective. | |||
:* "changemandatory" Change the mandatory flag of the target objective. | |||
:The combination of this "type" spawnarg and the "value" spawnarg below defines exactly what action is performed on the target objective. | |||
*;<tt>obj_condition_N_value</tt> (integer) | |||
:The meaning and possible values of this spawnarg depend on the "type" spawnarg above. It is used as "parameter" or "argument" for the above action, e.g. the "changevisibility" action needs to know whether to show or hide the target objective. The possible values are listed below: | |||
:* For type "changestate" | |||
:** 0: set to incomplete | |||
:** 1: set to complete | |||
:** 2: set to invalid | |||
:** 3: set to failed | |||
:* For type "changevisibility" | |||
:** 0: set to invisible | |||
:** 1: set to visible | |||
:* For type "changemandatory" | |||
:** 0: clear mandatory flag | |||
:** 1: set mandatory flag | |||
= Conclusion = | = 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. | 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. | ||
= Using Objectives Creatively = | |||
==A method for using objectives to change AI patrols when alert.== | |||
This works beautifully. The AI are placed at the seats just like in the card player prefab. Each player have "sitting 1" so they sit down at map start. AI's have no targets. | |||
Then I have one unseen objective per player: | |||
Objective component is only "AI is alerted", with the single entity card playing AI name. I can set the minimum alert level required for the AI to react. I set that to 3, so if the AI search actively, he won't be playing cards anymore, but goes patrolling instead. | |||
Basically the objective component says: "Alert entity cardgamer1 1 times to a minimum alert level of 3." | |||
And when that gets satisfied, the objective has completion target gopatrol_start, which is an ordinary trigger_once. This in turn targets a func_static which gives all the cardgamers new targets to their patrol routes. | |||
This is done as follows: Choose any kind of func_static. Open it in the Stim&Response editor. Add response trigger. Add an effect to this response. Choose "Add Target." Put in the entity box the name of the AI. Put in the target box the name of the path_node where the AI should go. | |||
When I make one objective for each player, alerting any single one of them will make everyone of them to go for patrol. And once the trigger_once is gone, further alerting of the AI has no effect. | |||
==A way to replace AI when others are knocked out or killed.== | |||
Have spare AIs isolated in an inaccessible place either of your map, or room off in the void, targeting a path_waitfortrigger which then carries on their path. Have the AI subject to substitution on a unique team in your map (or identify another way), as well as the spare AIs. An objective is set up like the usual fail if you knock out or kill an AI, but not visible, set to the appropriate team. I found I needed multiple Objective Components, the first no knock/no kill set to an amount of one. Then two and three, etc. Take note of what objective number the objective is and plan on not altering that. Set the failure target to a trigger relay, which targets as many trigger_counts as you have replacement AI. That's it for setting up the objective part. | |||
Add the aforementioned trigger relay, trigger_counts, and a target_setobjective_state. Setup the target_setobjective_state with "Obj_id0" #, where # is the objective number noted earlier, and set "obj_state" to "0". Add a matching number of atdm:teleport (in target/) as trigger_counts, each teleport targeting one of the stored AI. The first trigger_count is set to 1, and targets the first teleport and AI (to release it from the path_waitfortrigger). The second trigger_count is set to 2, and targets the next teleport and AI, etc. The trigger_relay targets all the trigger_counts and the target_setobjective_state. | |||
So when the initial AI is knocked out or killed, the objective failure triggers the relay. It in turns increments the counts and resets the objective to work again. The first count activates the teleport and triggers the AI to move off it's path_waitfortrigger on to the next path node. Test and voilà! | |||
==Having the player place multiple interchangeable items in varied places.== | |||
In "{{TDM-FM|83|Inn Business}}", the player has to place a number of markers in any of several places. However it was key to not permit multiple markers being placed in the same place, and not all places are required, between half and two-thirds so there's flexibility for various players and their styles. | |||
An "item is in location" with "group identifier" component was used, so each spot (info_tdm_objective_location) had the same spawnarg "objective_group", "drops". Also "interval" was set to "0.3", with no noticeable performance issue while being responsive enough for player feedback. These locations were separate enough, but limited in size, to meet the restrictions below. | |||
So a number of objectives matching the number of inventory items were made, with several components. | |||
* let the target entity ''marker1'' be at location 0 of ''drops'' | |||
* Do NOT let the entity ''marker1'' and ''marker2'' get within 150 units (check interval 0.3) | |||
* Do NOT let the entity ''marker1'' and ''marker3'' get within 150 units (check interval 0.3) | |||
* Do NOT let the entity ''marker1'' and ''marker4'' get within 150 units (check interval 0.3) | |||
* etc., depending how many marker entities there are | |||
The other objectives were set up the same way but for their own ''marker#''. (IE, don't let 2 and 1 get within..., don't let 2 and 3, etc.) In this manner, the objective checks if the marker is within the area, but unchecks if the maker rolls out or is removed by the player, and rechecks when they put it back in. | |||
Finally, another objective was created to complete when all had been placed which is not visible but irreversible. It has Enabling Objectives of the former objectives AND together. IE, "9 AND 10 AND 11 AND 12" if there are four. It has a completion target of a [[Triggers#trigger_relay|trigger relay]] entity which targeted several "atdm:target_setobjective_visibility" to turn off all the marker objectives and enable other mission specific things. | |||
The components match the markers, again using the group identifier: | |||
* let the target entity ''marker1'' be at location 0 of ''drops'' | |||
* let the target entity ''marker2'' be at location 0 of ''drops'' | |||
* let the target entity ''marker#etc...'' be at location 0 of ''drops'' | |||
So if all the marker objectives are complete (all items dropped in separate spots), then this completes and fires the trigger, which removes all the marker objectives from display (which the player had for feedback of progress) and moves on with objectives, perhaps displaying a dummy completed objective "All markers placed." | |||
Note: It's important for the marker objective numbers in the objective editor not to change, or update the non visible completion objective AND logic accordingly. | |||
== Don't hurt AI script == | |||
To have a mission fail if an AI is simply hurt (not just knocked out/killed), this script may be used: | |||
void checkHarm() | |||
{ | |||
float JackHealth = $Jack.getHealth(); | |||
float JillHealth = $Jill.getHealth();<br> | |||
while(1) | |||
{<br> | |||
if(JackHealth - $Jack.getHealth() > 1) | |||
{ | |||
$FailHarmObjective.activate($player1); | |||
}<br> | |||
if(JillHealth - $Jill.getHealth() > 1) | |||
{ | |||
$FailHarmObjective.activate($player1); | |||
}<br> | |||
sys.wait(1); | |||
} | |||
} | |||
You would just need to add one variable/if statement for each AI to be affected and run it from the start of the level (i.e. in the void main just have a line that says "thread checkHarm()"). The "FailHarmObjective" is an actual entity in the map which would then fail the relevant objective. | |||
==Notes on using INVALID status== | |||
The INVALID state on an objective: | |||
* prevents any ''timed'' components from being checked (e.g. check every 0.3 seconds whether item A is in place B ) | |||
* prevents the objective itself from being checked for completion | |||
* allows objectives that are dependent on the invalid objective to complete | |||
* allows the mission to complete | |||
* is used as the status for missing or malformed objectives | |||
* is used for non-applicable objectives, e.g. the loot goal for a different difficulty setting | |||
You can use INVALID for objectives that don't apply at map start, but then activate them later by setting them to status INCOMPLETE. | |||
Note that: | |||
* The mission and any dependent objectives can still be completed, whether or not the invalid objective has been completed. | |||
* Players can still complete individual components of your invalid objective. This can cause unexpected behaviour. If the player completes all of the components of an invalid objective and then you set the objective to "incomplete" status, the objective will be marked completed, but not right away: it'll be marked complete the next time any ''other'' objective gets an update. That's because any objective getting an update causes all objectives to be re-tested for completeness. | |||
If you need to avoid the second scenario, you can use a target_setobjective_component_state to uncheck the components when you re-enable the objective. | |||
[[Category:Editing]] | [[Category:Editing]] | ||
[[Category:Tutorial]] | [[Category:Tutorial]] |
Latest revision as of 21:38, 25 April 2024
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 DarkRadiant'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 atdm_target_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 trigger 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_tdm_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 AND 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 AND 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). If one of the entities is the player, use the string 'player1'.
- readable_opened
- available 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
- available 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
- available 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)
Objective Conditions
With the campaign code added to TDM 1.06 a new set of spawnargs can be used to let objectives depend on those in a previous mission. These spawnargs are starting with the obj_condition prefix.
Each condition defines the "source" mission and the source objective by number, plus the state of the source objective. It also needs to specify the "target" objective (which is always in the current mission) by number and which action to take. Right now, three different actions are supported:
- Change Objective State (complete, failed, etc.)
- Change the Objective's Visibility (hide or show)
- Change the Objective's Mandatory Flag
Syntax
- obj_condition_N_src_mission (integer)
- Defines the source mission the condition depends on. The integer is 0-based, i.e. the first mission has the number 0. The placeholder N is the number of the condition.
- obj_condition_N_src_obj (integer)
- Defines the number of the source objective in the source mission. The integer is 0-based, i.e. the first objective has the number 0. The placeholder N is the number of the condition.
- obj_condition_N_src_state (integer)
- Defines the state the source objective needs to be in for this condition to apply. The value can be in the same range as the objN_state spawnarg above:
- 0 = STATE_INCOMPLETE
- 1 = STATE_COMPLETE
- 2 = STATE_INVALID
- 3 = STATE_FAILED
- obj_condition_N_target_obj (integer)
- Defines the number of the target objective in the current mission. The integer is 0-based, i.e. the first objective has the number 0. The placeholder N is the number of the condition.
- obj_condition_N_type (string)
- Defines the action to take if the condition applies. There are three possible action types available:
- "changestate" Change the state of the target objective.
- "changevisibility" Change the visibility of the target objective.
- "changemandatory" Change the mandatory flag of the target objective.
- The combination of this "type" spawnarg and the "value" spawnarg below defines exactly what action is performed on the target objective.
- obj_condition_N_value (integer)
- The meaning and possible values of this spawnarg depend on the "type" spawnarg above. It is used as "parameter" or "argument" for the above action, e.g. the "changevisibility" action needs to know whether to show or hide the target objective. The possible values are listed below:
- For type "changestate"
- 0: set to incomplete
- 1: set to complete
- 2: set to invalid
- 3: set to failed
- For type "changevisibility"
- 0: set to invisible
- 1: set to visible
- For type "changemandatory"
- 0: clear mandatory flag
- 1: set mandatory flag
- For type "changestate"
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.
Using Objectives Creatively
A method for using objectives to change AI patrols when alert.
This works beautifully. The AI are placed at the seats just like in the card player prefab. Each player have "sitting 1" so they sit down at map start. AI's have no targets.
Then I have one unseen objective per player: Objective component is only "AI is alerted", with the single entity card playing AI name. I can set the minimum alert level required for the AI to react. I set that to 3, so if the AI search actively, he won't be playing cards anymore, but goes patrolling instead. Basically the objective component says: "Alert entity cardgamer1 1 times to a minimum alert level of 3."
And when that gets satisfied, the objective has completion target gopatrol_start, which is an ordinary trigger_once. This in turn targets a func_static which gives all the cardgamers new targets to their patrol routes.
This is done as follows: Choose any kind of func_static. Open it in the Stim&Response editor. Add response trigger. Add an effect to this response. Choose "Add Target." Put in the entity box the name of the AI. Put in the target box the name of the path_node where the AI should go.
When I make one objective for each player, alerting any single one of them will make everyone of them to go for patrol. And once the trigger_once is gone, further alerting of the AI has no effect.
A way to replace AI when others are knocked out or killed.
Have spare AIs isolated in an inaccessible place either of your map, or room off in the void, targeting a path_waitfortrigger which then carries on their path. Have the AI subject to substitution on a unique team in your map (or identify another way), as well as the spare AIs. An objective is set up like the usual fail if you knock out or kill an AI, but not visible, set to the appropriate team. I found I needed multiple Objective Components, the first no knock/no kill set to an amount of one. Then two and three, etc. Take note of what objective number the objective is and plan on not altering that. Set the failure target to a trigger relay, which targets as many trigger_counts as you have replacement AI. That's it for setting up the objective part.
Add the aforementioned trigger relay, trigger_counts, and a target_setobjective_state. Setup the target_setobjective_state with "Obj_id0" #, where # is the objective number noted earlier, and set "obj_state" to "0". Add a matching number of atdm:teleport (in target/) as trigger_counts, each teleport targeting one of the stored AI. The first trigger_count is set to 1, and targets the first teleport and AI (to release it from the path_waitfortrigger). The second trigger_count is set to 2, and targets the next teleport and AI, etc. The trigger_relay targets all the trigger_counts and the target_setobjective_state.
So when the initial AI is knocked out or killed, the objective failure triggers the relay. It in turns increments the counts and resets the objective to work again. The first count activates the teleport and triggers the AI to move off it's path_waitfortrigger on to the next path node. Test and voilà!
Having the player place multiple interchangeable items in varied places.
In "Inn Business", the player has to place a number of markers in any of several places. However it was key to not permit multiple markers being placed in the same place, and not all places are required, between half and two-thirds so there's flexibility for various players and their styles.
An "item is in location" with "group identifier" component was used, so each spot (info_tdm_objective_location) had the same spawnarg "objective_group", "drops". Also "interval" was set to "0.3", with no noticeable performance issue while being responsive enough for player feedback. These locations were separate enough, but limited in size, to meet the restrictions below.
So a number of objectives matching the number of inventory items were made, with several components.
- let the target entity marker1 be at location 0 of drops
- Do NOT let the entity marker1 and marker2 get within 150 units (check interval 0.3)
- Do NOT let the entity marker1 and marker3 get within 150 units (check interval 0.3)
- Do NOT let the entity marker1 and marker4 get within 150 units (check interval 0.3)
- etc., depending how many marker entities there are
The other objectives were set up the same way but for their own marker#. (IE, don't let 2 and 1 get within..., don't let 2 and 3, etc.) In this manner, the objective checks if the marker is within the area, but unchecks if the maker rolls out or is removed by the player, and rechecks when they put it back in.
Finally, another objective was created to complete when all had been placed which is not visible but irreversible. It has Enabling Objectives of the former objectives AND together. IE, "9 AND 10 AND 11 AND 12" if there are four. It has a completion target of a trigger relay entity which targeted several "atdm:target_setobjective_visibility" to turn off all the marker objectives and enable other mission specific things.
The components match the markers, again using the group identifier:
- let the target entity marker1 be at location 0 of drops
- let the target entity marker2 be at location 0 of drops
- let the target entity marker#etc... be at location 0 of drops
So if all the marker objectives are complete (all items dropped in separate spots), then this completes and fires the trigger, which removes all the marker objectives from display (which the player had for feedback of progress) and moves on with objectives, perhaps displaying a dummy completed objective "All markers placed."
Note: It's important for the marker objective numbers in the objective editor not to change, or update the non visible completion objective AND logic accordingly.
Don't hurt AI script
To have a mission fail if an AI is simply hurt (not just knocked out/killed), this script may be used:
void checkHarm() { float JackHealth = $Jack.getHealth(); float JillHealth = $Jill.getHealth();
while(1) {
if(JackHealth - $Jack.getHealth() > 1) { $FailHarmObjective.activate($player1); }
if(JillHealth - $Jill.getHealth() > 1) { $FailHarmObjective.activate($player1); }
sys.wait(1); } }
You would just need to add one variable/if statement for each AI to be affected and run it from the start of the level (i.e. in the void main just have a line that says "thread checkHarm()"). The "FailHarmObjective" is an actual entity in the map which would then fail the relevant objective.
Notes on using INVALID status
The INVALID state on an objective:
- prevents any timed components from being checked (e.g. check every 0.3 seconds whether item A is in place B )
- prevents the objective itself from being checked for completion
- allows objectives that are dependent on the invalid objective to complete
- allows the mission to complete
- is used as the status for missing or malformed objectives
- is used for non-applicable objectives, e.g. the loot goal for a different difficulty setting
You can use INVALID for objectives that don't apply at map start, but then activate them later by setting them to status INCOMPLETE. Note that:
- The mission and any dependent objectives can still be completed, whether or not the invalid objective has been completed.
- Players can still complete individual components of your invalid objective. This can cause unexpected behaviour. If the player completes all of the components of an invalid objective and then you set the objective to "incomplete" status, the objective will be marked completed, but not right away: it'll be marked complete the next time any other objective gets an update. That's because any objective getting an update causes all objectives to be re-tested for completeness.
If you need to avoid the second scenario, you can use a target_setobjective_component_state to uncheck the components when you re-enable the objective.