Replaces Traitor Greentext Objectives with Assassination/Escape Objectives (#71349)

## About The Pull Request
- Traitors now have the old two kill + escape objectives from before
progressive traitors. These objectives are for greentext/victory
conditions only, and are known as "Primary Objectives".
- The Progressive Traitor system remains in place for unlocking
dangerous gear.
- Completing the Final Objective from the progressive list is an
alternate route to victory.
- Traitor Uplinks now have a new tab that lists Primary Objectives,
which will also indicate when the Traitor has a secured victory by Final
Objective.
- Also cleaned up the round end report, see below.
## Why It's Good For The Game

See https://hackmd.io/WVjTLp6xTJiTFEkhSqqWIQ?view for my complete
thoughts.

TL;DR:
- Getting Rep is too much of a time commitment for Greentext. 
- Most of ProgTraitor is quiet sabotage that no one cares about, leading
to less conflict with the crew.
- Losing the Uplink creates an unwinnable state.

This PR fixes the above, by bringing back the 2 kill + escape system.
Traitors are now required to actually negatively affect at least some
crewmembers, and actually survive the round, to gain a victory state.
Alternatively, if they reach the Final Objective, they are given a free
pass, as an alternate route if they cannot find their Primary Objective
targets. Final Objectives will count as a victory even if the traitor
dies.

The Progressive Traitor system is still used for unlocking gear, and no
values for items, or restrictions based on role, have been changed.
## Changelog
🆑
balance: Traitors now have two kill and an escape objective for
Greentext purposes, though completing a Final Objective will Greentext
too.
/🆑
New Primary Objective Tab:

![tot_objs](https://user-images.githubusercontent.com/37497534/202838143-e3f8f10f-8b13-4e0a-80dc-efc9271e8089.png)
If the Final Objective is done, the traitor's victory condition is
secured, and the Primary Objectives no longer matter.

![tot_objs_complete](https://user-images.githubusercontent.com/37497534/202838147-bac663b0-8f30-4e84-bf2a-23f014992a75.png)
Round-end report, showing one traitor that greentexts through
traditional means, and one that doesn't get their kill target or escape,
but greentexts via Final Objective anyway.

![round_end](https://user-images.githubusercontent.com/37497534/202838156-0b1cc7dd-3048-40f6-a640-cef7a613d4ba.png)
This commit is contained in:
zxaber
2022-12-11 00:01:06 +00:00
committed by GitHub
parent b5b4d69dcf
commit 54cfbcce2e
12 changed files with 357 additions and 231 deletions
+14
View File
@@ -166,20 +166,34 @@
data["maximum_potential_objectives"] = uplink_handler.maximum_potential_objectives
if(uplink_handler.has_objectives)
var/list/primary_objectives = list()
for(var/datum/objective/task as anything in uplink_handler.primary_objectives)
var/list/task_data = list()
if(length(primary_objectives) > length(GLOB.phonetic_alphabet))
task_data["task_name"] = "DIRECTIVE [length(primary_objectives) + 1]" //The english alphabet is WEAK
else
task_data["task_name"] = "DIRECTIVE [uppertext(GLOB.phonetic_alphabet[length(primary_objectives) + 1])]"
task_data["task_text"] = task.explanation_text
primary_objectives += list(task_data)
var/list/potential_objectives = list()
for(var/index in 1 to uplink_handler.potential_objectives.len)
var/datum/traitor_objective/objective = uplink_handler.potential_objectives[index]
var/list/objective_data = objective.uplink_ui_data(user)
objective_data["id"] = index
potential_objectives += list(objective_data)
var/list/active_objectives = list()
for(var/index in 1 to uplink_handler.active_objectives.len)
var/datum/traitor_objective/objective = uplink_handler.active_objectives[index]
var/list/objective_data = objective.uplink_ui_data(user)
objective_data["id"] = index
active_objectives += list(objective_data)
data["primary_objectives"] = primary_objectives
data["potential_objectives"] = potential_objectives
data["active_objectives"] = active_objectives
data["completed_final_objective"] = uplink_handler.final_objective
var/list/stock_list = uplink_handler.item_stock.Copy()
var/list/extra_purchasable_stock = list()
+7 -6
View File
@@ -110,7 +110,7 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list
return target
//dupe_search_range is a list of antag datums / minds / teams
/datum/objective/proc/find_target(dupe_search_range, blacklist)
/datum/objective/proc/find_target(dupe_search_range, list/blacklist)
var/list/datum/mind/owners = get_owners()
if(!dupe_search_range)
dupe_search_range = get_owners()
@@ -462,7 +462,7 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list
var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
var/target_missing_id
/datum/objective/escape/escape_with_identity/find_target(dupe_search_range)
/datum/objective/escape/escape_with_identity/find_target(dupe_search_range, list/blacklist)
target = ..()
update_explanation_text()
@@ -576,7 +576,7 @@ GLOBAL_LIST_EMPTY(possible_items)
for(var/I in subtypesof(/datum/objective_item/steal))
new I
/datum/objective/steal/find_target(dupe_search_range)
/datum/objective/steal/find_target(dupe_search_range, list/blacklist)
var/list/datum/mind/owners = get_owners()
if(!dupe_search_range)
dupe_search_range = get_owners()
@@ -659,7 +659,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
for(var/I in subtypesof(/datum/objective_item/special) + subtypesof(/datum/objective_item/stack))
new I
/datum/objective/steal/special/find_target(dupe_search_range)
/datum/objective/steal/special/find_target(dupe_search_range, list/blacklist)
return set_target(pick(GLOB.possible_items_special))
/datum/objective/capture
@@ -821,8 +821,9 @@ GLOBAL_LIST_EMPTY(possible_items_special)
name = "destroy AI"
martyr_compatible = TRUE
/datum/objective/destroy/find_target(dupe_search_range)
var/list/possible_targets = active_ais(1)
/datum/objective/destroy/find_target(dupe_search_range, list/blacklist)
var/list/possible_targets = active_ais(TRUE)
possible_targets -= blacklist
var/mob/living/silicon/ai/target_ai = pick(possible_targets)
target = target_ai.mind
update_explanation_text()
+1 -1
View File
@@ -347,7 +347,7 @@
UnregisterSignal(target.current, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_MIND_TRANSFERRED_INTO))
target = null
/datum/objective/sacrifice/find_target(dupe_search_range)
/datum/objective/sacrifice/find_target(dupe_search_range, list/blacklist)
clear_sacrifice()
if(!istype(team, /datum/team/cult))
return
@@ -47,6 +47,7 @@
uplink.uplink_handler = uplink_handler
else
uplink_handler = uplink.uplink_handler
uplink_handler.primary_objectives = objectives
uplink_handler.has_progression = TRUE
SStraitor.register_uplink_handler(uplink_handler)
@@ -147,71 +148,33 @@
employer = pick(possible_employers)
traitor_flavor = strings(TRAITOR_FLAVOR_FILE, employer)
/datum/objective/traitor_progression
name = "traitor progression"
explanation_text = "Become a living legend by getting a total of %REPUTATION% reputation points"
var/possible_range = list(40 MINUTES, 90 MINUTES)
var/required_total_progression_points
/datum/objective/traitor_progression/New(text)
. = ..()
required_total_progression_points = round(rand(possible_range[1], possible_range[2]))
explanation_text = replacetext(explanation_text, "%REPUTATION%", DISPLAY_PROGRESSION(required_total_progression_points))
/datum/objective/traitor_progression/check_completion()
if(!owner)
return FALSE
var/datum/antagonist/traitor/traitor = owner.has_antag_datum(/datum/antagonist/traitor)
if(!traitor)
return FALSE
if(!traitor.uplink_handler)
return FALSE
if(traitor.uplink_handler.progression_points < required_total_progression_points)
return FALSE
return TRUE
/datum/objective/traitor_objectives
name = "traitor objective"
explanation_text = "Complete objectives colletively worth more than %REPUTATION% reputation points"
var/possible_range = list(20 MINUTES, 30 MINUTES)
var/required_progression_in_objectives
/datum/objective/traitor_objectives/New(text)
. = ..()
required_progression_in_objectives = round(rand(possible_range[1], possible_range[2]))
explanation_text = replacetext(explanation_text, "%REPUTATION%", DISPLAY_PROGRESSION(required_progression_in_objectives))
/datum/objective/traitor_objectives/check_completion()
if(!owner)
return FALSE
var/datum/antagonist/traitor/traitor = owner.has_antag_datum(/datum/antagonist/traitor)
if(!traitor)
return FALSE
if(!traitor.uplink_handler)
return FALSE
var/total_points = 0
for(var/datum/traitor_objective/objective as anything in traitor.uplink_handler.completed_objectives)
if(objective.objective_state != OBJECTIVE_STATE_COMPLETED)
continue
total_points += objective.progression_reward
if(total_points < required_progression_in_objectives)
return FALSE
return TRUE
/// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack.
/datum/antagonist/traitor/proc/forge_traitor_objectives()
objectives.Cut()
var/datum/objective/traitor_progression/final_objective = new /datum/objective/traitor_progression()
final_objective.owner = owner
objectives += final_objective
var/list/kill_targets = list() //for blacklisting already-set targets
var/objective_limit = CONFIG_GET(number/traitor_objectives_amount)
for(var/i in 1 to objective_limit)
var/list/ai_targets = active_ais(z = 2) //For multiZ stations, this proc will include AIs on all station levels if the provided arg is 2.
ai_targets -= kill_targets
if(ai_targets.len)
var/diceroll = rand(1, (living_player_count() - 1)) //AI kill and crew kill objectives are different, but I want a rough equal chance for AIs to be targets. Maybe I'll refractor this someday
if(diceroll <= ai_targets.len)
var/datum/objective/task = new /datum/objective/destroy()
task.owner = owner
task.find_target(blacklist = kill_targets)
kill_targets += task.target
objectives += task
continue
var/datum/objective/traitor_objectives/objective_completion = new /datum/objective/traitor_objectives()
objective_completion.owner = owner
objectives += objective_completion
var/datum/objective/task = new /datum/objective/assassinate()
task.owner = owner
task.find_target(blacklist = kill_targets)
kill_targets += task.target
objectives += task
var/datum/objective/escape/bye = new /datum/objective/escape()
objectives += bye
bye.owner = owner
/datum/antagonist/traitor/apply_innate_effects(mob/living/mob_override)
. = ..()
@@ -279,6 +242,9 @@
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
traitor_won = FALSE
count++
if(uplink_handler.final_objective)
objectives_text += "<br>[span_greentext("[traitor_won ? "Additionally" : "However"], the final objective \"[uplink_handler.final_objective]\" was completed!")]"
traitor_won = TRUE
result += "<br>[owner.name] <B>[traitor_flavor["roundend_report"]]</B>"
@@ -292,11 +258,7 @@
result += objectives_text
if(uplink_handler)
var/completed_objectives_text = "Completed Uplink Objectives: "
for(var/datum/traitor_objective/objective as anything in uplink_handler.completed_objectives)
if(objective.objective_state == OBJECTIVE_STATE_COMPLETED)
completed_objectives_text += "<br><B>[objective.name]</B> - ([objective.telecrystal_reward] TC, [DISPLAY_PROGRESSION(objective.progression_reward)] Reputation)"
result += completed_objectives_text
result += "<br>The traitor had a total of [uplink_handler.progression_points] Reputation and [uplink_handler.telecrystals] Unused Telecrystals."
var/special_role_text = lowertext(name)
@@ -171,6 +171,12 @@
/datum/traitor_objective/assassinate/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
if(!istype(task.target, /datum/mind))
continue
already_targeting += task.target //Removing primary objective kill targets from the list
var/parent_type = type2parent(type)
//don't roll head of staff types if you haven't completed the normal version
if(heads_of_staff && !handler.get_completion_count(parent_type))
@@ -182,6 +188,8 @@
if(generating_for.late_joiner)
try_target_late_joiners = TRUE
for(var/datum/mind/possible_target as anything in get_crewmember_minds())
if(possible_target in already_targeting)
continue
var/target_area = get_area(possible_target.current)
if(possible_target == generating_for)
continue
@@ -50,6 +50,13 @@
heads_of_staff = TRUE
/datum/traitor_objective/eyesnatching/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
if(!istype(task.target, /datum/mind))
continue
already_targeting += task.target //Removing primary objective kill targets from the list
var/list/possible_targets = list()
var/try_target_late_joiners = FALSE
if(generating_for.late_joiner)
@@ -59,6 +66,9 @@
if(possible_target == generating_for)
continue
if(possible_target in already_targeting)
continue
if(!ishuman(possible_target.current))
continue
@@ -30,6 +30,7 @@
for(var/datum/traitor_objective/objective as anything in handler.potential_objectives)
objective.fail_objective()
user.playsound_local(get_turf(user), 'sound/traitor/final_objective.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
handler.final_objective = name
/datum/traitor_objective/ultimate/uplink_ui_data(mob/user)
. = ..()
@@ -136,11 +136,21 @@
alive_bonus = 2
/datum/traitor_objective/kidnapping/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
if(!istype(task.target, /datum/mind))
continue
already_targeting += task.target //Removing primary objective kill targets from the list
var/list/possible_targets = list()
for(var/datum/mind/possible_target as anything in get_crewmember_minds())
if(possible_target == generating_for)
continue
if(possible_target in already_targeting)
continue
if(!ishuman(possible_target.current))
continue
@@ -36,6 +36,10 @@
var/list/completed_objectives = list()
/// All objectives assigned by type to handle any duplicates
var/list/potential_duplicate_objectives = list()
/// Text of the final objective, once assigned. Used for uplink data and traitor greentext. Empty string means not yet reached.
var/final_objective = ""
/// Objectives that must be completed for traitor greentext. Set by the traitor datum.
var/list/primary_objectives
/// The role that this uplink handler is associated to.
var/assigned_role
/// The species this uplink handler is associated to.
@@ -291,6 +291,7 @@ const ObjectiveFunction = (
progressionReward={objective.progression_reward}
objectiveState={objective.objective_state}
originalProgression={objective.original_progression}
hideTcRep={objective.final_objective}
finalObjective={objective.final_objective}
canAbort={
!!handleAbort &&
@@ -338,18 +339,19 @@ type ObjectiveElementProps = {
telecrystalReward: number;
progressionReward: number;
uiButtons?: JSX.Element;
objectiveState: ObjectiveState;
objectiveState?: ObjectiveState;
originalProgression: number;
telecrystalPenalty: number;
grow: boolean;
hideTcRep: BooleanLike;
finalObjective: BooleanLike;
canAbort: BooleanLike;
handleCompletion: (event: MouseEvent) => void;
handleAbort: (event: MouseEvent) => void;
handleCompletion?: (event: MouseEvent) => void;
handleAbort?: (event: MouseEvent) => void;
};
const ObjectiveElement = (props: ObjectiveElementProps, context) => {
export const ObjectiveElement = (props: ObjectiveElementProps, context) => {
const {
name,
reputation,
@@ -364,6 +366,7 @@ const ObjectiveElement = (props: ObjectiveElementProps, context) => {
canAbort,
originalProgression,
grow,
hideTcRep,
finalObjective,
...rest
} = props;
@@ -419,9 +422,17 @@ const ObjectiveElement = (props: ObjectiveElementProps, context) => {
</Box>
</Flex.Item>
<Flex.Item grow={grow} basis="content">
<Box className="UplinkObjective__Content" height="100%">
<Box
style={{
'border-bottom': hideTcRep
? '2px solid rgba(0, 0, 0, 0.5)'
: undefined,
}}
className="UplinkObjective__Content"
height="100%"
mb={hideTcRep ? 2 : 0}>
<Box>{description}</Box>
{!finalObjective && (
{!hideTcRep && (
<Box mt={1}>
Failing this objective will deduct {telecrystalPenalty} TC.
</Box>
@@ -436,109 +447,112 @@ const ObjectiveElement = (props: ObjectiveElementProps, context) => {
</Box>
</Flex.Item>
<Flex.Item>
<Box className="UplinkObjective__Footer">
<Stack vertical>
<Stack.Item>
<Stack align="center" justify="center">
<Box
style={{
'border': '2px solid rgba(0, 0, 0, 0.5)',
'border-left': 'none',
'border-right': 'none',
'border-bottom': objectiveFinished ? 'none' : undefined,
}}
className={reputation.gradient}
py={0.5}
width="100%"
textAlign="center">
{telecrystalReward} TC,
<Box ml={1} as="span">
{calculateProgression(progressionReward)} Reputation
{Math.abs(progressionDiff) > 10 && (
<Tooltip
content={
<Box>
You will get
<Box
mr={1}
ml={1}
color={
progressionDiff > 0
? progressionDiff > 25
? 'red'
: 'orange'
: 'green'
}
as="span">
{Math.abs(progressionDiff)}%
</Box>
{progressionDiff > 0 ? 'less' : 'more'} reputation
from this objective. This is because your reputation
is {progressionDiff > 0 ? 'ahead ' : 'behind '}
where it normally should be at.
</Box>
}>
<Box
ml={1}
color={
progressionDiff > 0
? progressionDiff > 35
? 'red'
: 'orange'
: 'green'
}
as="span">
({progressionDiff > 0 ? '-' : '+'}
{Math.abs(progressionDiff)}%)
</Box>
</Tooltip>
)}
</Box>
</Box>
</Stack>
{objectiveFinished ? (
<Box
inline
className={reputation.gradient}
style={{
'border-radius': '0',
'border': '2px solid rgba(0, 0, 0, 0.5)',
'border-left': 'none',
'border-right': 'none',
}}
position="relative"
width="100%"
textAlign="center"
bold>
{!hideTcRep && (
<Box className="UplinkObjective__Footer">
<Stack vertical>
<Stack.Item>
<Stack align="center" justify="center">
<Box
width="100%"
height="100%"
backgroundColor={
objectiveFailed
? 'rgba(255, 0, 0, 0.1)'
: 'rgba(0, 255, 0, 0.1)'
}
position="absolute"
left={0}
top={0}
/>
<Button
onClick={handleCompletion}
color={objectiveFailed ? 'bad' : 'good'}
style={{
'border': '1px solid rgba(0, 0, 0, 0.65)',
'border': '2px solid rgba(0, 0, 0, 0.5)',
'border-left': 'none',
'border-right': 'none',
'border-bottom': objectiveFinished ? 'none' : undefined,
}}
my={1}>
TURN IN
</Button>
</Box>
) : null}
</Stack.Item>
{!!uiButtons && !objectiveFinished && (
<Stack.Item>{uiButtons}</Stack.Item>
)}
</Stack>
</Box>
className={reputation.gradient}
py={0.5}
width="100%"
textAlign="center">
{telecrystalReward} TC,
<Box ml={1} as="span">
{calculateProgression(progressionReward)} Reputation
{Math.abs(progressionDiff) > 10 && (
<Tooltip
content={
<Box>
You will get
<Box
mr={1}
ml={1}
color={
progressionDiff > 0
? progressionDiff > 25
? 'red'
: 'orange'
: 'green'
}
as="span">
{Math.abs(progressionDiff)}%
</Box>
{progressionDiff > 0 ? 'less' : 'more'} reputation
from this objective. This is because your
reputation is{' '}
{progressionDiff > 0 ? 'ahead ' : 'behind '}
where it normally should be at.
</Box>
}>
<Box
ml={1}
color={
progressionDiff > 0
? progressionDiff > 35
? 'red'
: 'orange'
: 'green'
}
as="span">
({progressionDiff > 0 ? '-' : '+'}
{Math.abs(progressionDiff)}%)
</Box>
</Tooltip>
)}
</Box>
</Box>
</Stack>
{objectiveFinished ? (
<Box
inline
className={reputation.gradient}
style={{
'border-radius': '0',
'border': '2px solid rgba(0, 0, 0, 0.5)',
'border-left': 'none',
'border-right': 'none',
}}
position="relative"
width="100%"
textAlign="center"
bold>
<Box
width="100%"
height="100%"
backgroundColor={
objectiveFailed
? 'rgba(255, 0, 0, 0.1)'
: 'rgba(0, 255, 0, 0.1)'
}
position="absolute"
left={0}
top={0}
/>
<Button
onClick={handleCompletion}
color={objectiveFailed ? 'bad' : 'good'}
style={{
'border': '1px solid rgba(0, 0, 0, 0.65)',
}}
my={1}>
TURN IN
</Button>
</Box>
) : null}
</Stack.Item>
{!!uiButtons && !objectiveFinished && (
<Stack.Item>{uiButtons}</Stack.Item>
)}
</Stack>
</Box>
)}
</Flex.Item>
</Flex>
);
@@ -0,0 +1,82 @@
import { Box, Dimmer, Section, Stack } from '../../components';
import { ObjectiveElement } from './ObjectiveMenu';
type PrimaryObjectiveMenuProps = {
primary_objectives;
final_objective;
};
export const PrimaryObjectiveMenu = (
props: PrimaryObjectiveMenuProps,
context
) => {
const { primary_objectives, final_objective } = props;
return (
<Section fill>
<Section>
<Box mt={3} mb={3} bold fontSize={1.2} align="center" color="white">
{
'Agent, your Primary Objectives are as follows. Complete these at all costs.'
}
</Box>
<Box mt={3} mb={5} bold fontSize={1.2} align="center" color="white">
{
'Completing Secondary Objectives may allow you to aquire additional equipment.'
}
</Box>
</Section>
{final_objective && (
<Dimmer>
<Box
color="red"
fontFamily={'Bahnschrift'}
fontSize={3}
align={'top'}
as="span">
PRIORITY MESSAGE
<br />
SOURCE: xxx.xxx.xxx.224:41394
<br />
<br />
\\Debrief in progress.
<br />
\\Final Objective confirmed complete. <br />
\\Your work is done here, agent.
<br />
<br />
CONNECTION CLOSED_
</Box>
</Dimmer>
)}
<Section>
<Stack vertical fill scrollable>
{primary_objectives.map((prim_obj, index) => (
<Stack.Item key={index}>
<ObjectiveElement
key={prim_obj.id}
name={prim_obj['task_name']}
description={prim_obj['task_text']}
reputation={{
minutesLessThan: 0,
title: 'none',
gradient:
index === primary_objectives.length - 1
? 'reputation-good'
: 'reputation-very-good',
}}
telecrystalReward={0}
telecrystalPenalty={0}
progressionReward={0}
originalProgression={0}
hideTcRep={1}
canAbort={false}
grow={false}
finalObjective={false}
/>
</Stack.Item>
))}
</Stack>
</Section>
</Section>
);
};
+73 -53
View File
@@ -1,11 +1,12 @@
import { useBackend } from '../../backend';
import { Window } from '../../layouts';
import { GenericUplink, Item } from './GenericUplink';
import { Component } from 'inferno';
import { Component, Fragment } from 'inferno';
import { fetchRetry } from '../../http';
import { resolveAsset } from '../../assets';
import { BooleanLike } from 'common/react';
import { Box, Tabs, Button, Stack, Section, Tooltip } from '../../components';
import { PrimaryObjectiveMenu } from './PrimaryObjectiveMenu';
import { Objective, ObjectiveMenu } from './ObjectiveMenu';
import { calculateProgression, calculateReputationLevel, reputationDefault, reputationLevelsTooltip } from './calculateReputationLevel';
@@ -46,6 +47,10 @@ type UplinkData = {
has_objectives: BooleanLike;
has_progression: BooleanLike;
primary_objectives: {
[key: number]: string;
};
completed_final_objective: string;
potential_objectives: Objective[];
active_objectives: Objective[];
maximum_active_objectives: number;
@@ -150,6 +155,8 @@ export class Uplink extends Component<{}, UplinkState> {
const {
telecrystals,
progression_points,
primary_objectives,
completed_final_objective,
active_objectives,
potential_objectives,
has_objectives,
@@ -313,15 +320,22 @@ export class Uplink extends Component<{}, UplinkState> {
<Stack.Item grow={1}>
<Tabs fluid textAlign="center">
{!!has_objectives && (
<Tabs.Tab
selected={currentTab === 0}
onClick={() => this.setState({ currentTab: 0 })}>
Objectives
</Tabs.Tab>
<Fragment>
<Tabs.Tab
selected={currentTab === 0}
onClick={() => this.setState({ currentTab: 0 })}>
Primary Objectives
</Tabs.Tab>
<Tabs.Tab
selected={currentTab === 1}
onClick={() => this.setState({ currentTab: 1 })}>
Secondary Objectives
</Tabs.Tab>
</Fragment>
)}
<Tabs.Tab
selected={currentTab === 1 || !has_objectives}
onClick={() => this.setState({ currentTab: 1 })}>
selected={currentTab === 2 || !has_objectives}
onClick={() => this.setState({ currentTab: 2 })}>
Market
</Tabs.Tab>
</Tabs>
@@ -341,53 +355,59 @@ export class Uplink extends Component<{}, UplinkState> {
</Stack.Item>
<Stack.Item grow>
{(currentTab === 0 && has_objectives && (
<ObjectiveMenu
activeObjectives={active_objectives}
potentialObjectives={potential_objectives}
maximumActiveObjectives={maximum_active_objectives}
maximumPotentialObjectives={maximum_potential_objectives}
handleObjectiveAction={(objective, action) =>
act('objective_act', {
check: objective.original_progression,
objective_action: action,
index: objective.id,
})
}
handleStartObjective={(objective) =>
act('start_objective', {
check: objective.original_progression,
index: objective.id,
})
}
handleObjectiveAbort={(objective) =>
act('objective_abort', {
check: objective.original_progression,
index: objective.id,
})
}
handleObjectiveCompleted={(objective) =>
act('finish_objective', {
check: objective.original_progression,
index: objective.id,
})
}
handleRequestObjectives={() => act('regenerate_objectives')}
<PrimaryObjectiveMenu
primary_objectives={primary_objectives}
final_objective={completed_final_objective}
/>
)) || (
<GenericUplink
currency=""
categories={allCategories}
items={items}
handleBuy={(item) => {
const extraDataItem = item as Item<ItemExtraData>;
if (!extraDataItem.extraData?.ref) {
act('buy', { path: item.id });
} else {
act('buy', { ref: extraDataItem.extraData.ref });
)) ||
(currentTab === 1 && has_objectives && (
<ObjectiveMenu
activeObjectives={active_objectives}
potentialObjectives={potential_objectives}
maximumActiveObjectives={maximum_active_objectives}
maximumPotentialObjectives={maximum_potential_objectives}
handleObjectiveAction={(objective, action) =>
act('objective_act', {
check: objective.original_progression,
objective_action: action,
index: objective.id,
})
}
}}
/>
)}
handleStartObjective={(objective) =>
act('start_objective', {
check: objective.original_progression,
index: objective.id,
})
}
handleObjectiveAbort={(objective) =>
act('objective_abort', {
check: objective.original_progression,
index: objective.id,
})
}
handleObjectiveCompleted={(objective) =>
act('finish_objective', {
check: objective.original_progression,
index: objective.id,
})
}
handleRequestObjectives={() => act('regenerate_objectives')}
/>
)) || (
<GenericUplink
currency=""
categories={allCategories}
items={items}
handleBuy={(item) => {
const extraDataItem = item as Item<ItemExtraData>;
if (!extraDataItem.extraData?.ref) {
act('buy', { path: item.id });
} else {
act('buy', { ref: extraDataItem.extraData.ref });
}
}}
/>
)}
</Stack.Item>
</Stack>
</Window.Content>