Merge pull request #14887 from Runa-Dacino/TraitTutorial

Adds new human ability: "Explain Custom Traits"
This commit is contained in:
Heroman3003
2023-05-28 20:18:36 +10:00
committed by GitHub
7 changed files with 228 additions and 1 deletions
@@ -85,6 +85,16 @@
/datum/trait/neutral/bloodsucker
name = "Bloodsucker, Obligate"
desc = "Makes you unable to gain nutrition from anything but blood. To compenstate, you get fangs that can be used to drain blood from prey."
tutorial = "This trait forces you to only consume blood - you cannot have normal food anymore. Vore is, of course, an exception! <br> \
You can satisfy this by clicking bloodbags in your hand on harm intent, drinking from glasses, blood tomatoes \
or finding a (un)willing donor for your appropriate appendage! <br><br> \
Controls for taking blood from your victim can be changed at will by trying to drink from yourself. <br>\
Intent-based control scheme: <br> \
HELP - Loud, No Bleeding <br> \
DISARM - Subtle, Causes bleeding <br> \
GRAB - Subtle, No Bleeding <br> \
HARM - Loud, Causes Bleeding"
cost = 0
custom_only = FALSE
var_changes = list("organic_food_coeff" = 0, "bloodsucker" = TRUE)
@@ -97,6 +107,15 @@
/datum/trait/neutral/bloodsucker_freeform
name = "Bloodsucker"
desc = "You get fangs that can be used to drain blood from prey."
tutorial = "This trait allows you to consume blood on top of normal food! <br> \
You can do this by clicking bloodbags in your hand on harm intent, drinking from glasses, blood tomatoes \
or finding a (un)willing donor for your appropriate appendage! <br><br> \
Controls for taking blood from your victim can be changed at will by trying to drink from yourself. <br>\
Intent-based control scheme: <br> \
HELP - Loud, No Bleeding <br> \
DISARM - Subtle, Causes bleeding <br> \
GRAB - Subtle, No Bleeding <br> \
HARM - Loud, Causes Bleeding"
cost = 0
custom_only = FALSE
var_changes = list("bloodsucker" = TRUE)
@@ -1,6 +1,7 @@
/datum/trait
var/name
var/desc = "Contact a developer if you see this trait."
var/tutorial = "This trait has no detailed tutorial yet. Suggest one at #Dev-Suggestions on the discord!" //Use <br> for newlines, NOT \n
var/cost = 0
var/sort = TRAIT_SORT_NORMAL // Sort order, 1 before 2 before 3 etc. Alphabetical is used for same-group traits.
@@ -32,6 +33,7 @@
S.vars[trait] = trait_prefs[trait]
if(TRAIT_VAREDIT_TARGET_MOB)
H.vars[trait] = trait_prefs[trait]
H.verbs |= /mob/living/carbon/human/proc/trait_tutorial
return
//Applying trait to preferences rather than just us.
@@ -0,0 +1,76 @@
/*
A new Ability given to those human mobs that have a non-zero number of traits.
Verb is added by /datum/trait/proc/apply() in code\modules\mob\living\carbon\human\species\station\traits_vr\trait.dm file.
This proc gets called whenever the user is spawned, resleeved, autoresleeved and so forth.
If there are special cases that do NOT call this var that result in a completely new body -
I will fix it once I am informed of their existence.
Been tested - does NOT lead to multiples of the verb thanks to the |=.
Given the apply() proc is only called if they have verbs - this should avoid it erronously popping up for traitless people.
This ability intends to retrieve all positive, neutral and negative traits chosen in the character set-up
then retrieve their relevant vars by assuming the character's species has the full list. This should always work. Should
The ability is intended to be developed both as a to_chat() and a tgui window.
The user is given the ability to choose which they would like whenever they press the ability to better suit whatever scenario they find themselves
thirsty for knowledge.
When adding new tutorials for trait subtypes, use <br> rather than \n newlines
TGUI backend path: code\modules\tgui\modules\trait_tutorial_tgui.dm
TGUI frontend path: tgui\packages\tgui\interfaces\TraitTutorial.tsx
*/
/mob/living/carbon/human/proc/trait_tutorial()
set name = "Explain Custom Traits"
set desc = "Click this verb to obtain a detailed tutorial on your selected traits. "
set category = "Abilities"
var/datum/tgui_module/trait_tutorial_tgui/fancy_UI
if(!fancy_UI)
fancy_UI = new /datum/tgui_module/trait_tutorial_tgui/ //Preventing a bunch of instances being spawned all over the place. Hopefully
var/list/list_of_traits = species.traits
if(!LAZYLEN(list_of_traits)) //Although we shouldn't show up if no traits, leaving this in case someone loses theirs after (re)spawning.
to_chat(usr, SPAN_NOTICE("You do not have any custom traits!"))
return //Dont want an empty TGUI panel and list by accident after all.
var/UI_choice = tgui_alert(src, "Would you like the tutorial text to be printed to chat?", "Choose preferred tutorial interface", list("TGUI","To Chat", "Cancel"))
if(UI_choice == "Cancel")
return
//Initializing associative lists
var/trait_names = list() //List of keys
var/trait_category = list() // name:category
var/trait_desc = list() // name:desc
var/trait_tutorial = list() //name:tutorial
for(var/trait in list_of_traits)
var/datum/trait/T = all_traits[trait]
trait_names += T.name
trait_desc[T.name] = T.desc
trait_tutorial[T.name] = T.tutorial
switch(T.category)
if(TRAIT_TYPE_NEGATIVE)
trait_category[T.name] = "Negative Trait"
if(TRAIT_TYPE_NEUTRAL)
trait_category[T.name] = "Neutral Trait"
if(TRAIT_TYPE_POSITIVE)
trait_category[T.name] = "Positive Trait"
if(UI_choice == "To Chat")
var/to_chat_choice = tgui_input_list(usr, "Please choose the trait to be explained", "Print to Chat", trait_names, null)
if(to_chat_choice)
to_chat(usr,SPAN_NOTICE("<b>Name:</b> [to_chat_choice] \n <b>Category:</b> [trait_category[to_chat_choice]] \n <b>Description:</b> [trait_desc[to_chat_choice]] \n \
<b>Guide:</b> \n [trait_tutorial[to_chat_choice]]"))
else if(UI_choice == "TGUI")
fancy_UI.set_vars(trait_names, trait_category, trait_desc, trait_tutorial)
fancy_UI.tgui_interact(usr)
@@ -0,0 +1,48 @@
/*
Verb/Main definition path: code\modules\mob\living\carbon\human\species\station\traits_vr\traits_tutorial.dm
Frontend path: tgui\packages\tgui\interfaces\TraitTutorial.tsx
*/
/datum/tgui_module/trait_tutorial_tgui
name = "Explain Custom Traits"
tgui_id = "TraitTutorial"
var/trait_names = list()
var/trait_category = list() // name:category
var/trait_desc = list() // name:desc
var/trait_tutorial = list() //name:tutorial
var/trait_selected = ""
/datum/tgui_module/trait_tutorial_tgui/proc/set_vars(list/names, list/categories, list/descriptions, list/tutorials)
trait_names = names
trait_category = categories
trait_desc = descriptions
trait_tutorial = tutorials
/datum/tgui_module/trait_tutorial_tgui/tgui_interact(mob/living/carbon/human/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, tgui_id, name)
ui.open()
/datum/tgui_module/trait_tutorial_tgui/tgui_data(mob/user)
var/list/data = list()
data["names"] = trait_names //passes a list of strings
data["descriptions"] = trait_desc //passes an assoc list as obj
data["categories"] = trait_category //passes an assoc list as obj
data["tutorials"] = trait_tutorial //passes an assoc list as obj
data["selection"] = trait_selected //passes a string
return data
/datum/tgui_module/trait_tutorial_tgui/tgui_act(action, params)
. = ..()
if(.)
return
if(action == "select_trait")
var/selection = params["name"]
trait_selected = selection
. = TRUE
/datum/tgui_module/trait_tutorial_tgui/tgui_state(mob/user)
return GLOB.tgui_always_state
@@ -0,0 +1,80 @@
/* eslint-disable react/no-danger */
import { useBackend } from '../backend';
import { Flex, Tabs, Section, Box, Divider } from '../components';
import { Window } from '../layouts';
type data = {
namae: string;
names: string[];
descriptions: { key: string; val: string }[];
categories: { key: string; val: string }[];
tutorials: { key: string; val: string }[];
selection: string;
};
export const TraitTutorial = (props, context) => {
const { act, data } = useBackend<data>(context);
return (
<Window width={804} height={426} scrollable>
<Window.Content scrollable>
<Section title="Guide to Custom Traits" scrollable>
<TraitSelection />
</Section>
</Window.Content>
</Window>
);
};
export const TraitSelection = (props, context) => {
const { act, data } = useBackend<data>(context);
const { names, selection } = data;
return (
<Flex scrollable>
<Flex.Item shrink scrollable>
<Section title="Trait Selection" scrollable>
<Tabs vertical scrollable>
{names.map((name) => (
<Tabs.Tab key={name} selected={name === selection} onClick={() => act('select_trait', { name })}>
<Box inline>{name}</Box>
</Tabs.Tab>
))}
</Tabs>
</Section>
</Flex.Item>
<Flex.Item grow={2}>
<Divider vertical />
</Flex.Item>
<Flex.Item grow={8} scrollable>
{selection && (
<Section title={selection} scrollable>
<TraitDescription name={selection} />
</Section>
)}
</Flex.Item>
</Flex>
);
};
export const TraitDescription = (props, context) => {
const { act, data } = useBackend<data>(context);
const { name } = props;
const { descriptions, categories, tutorials } = data;
return (
<Section scrollable flexWrap>
<b>Name:</b> {name}
<br />
<b>Category:</b> {categories[name]}
<br />
<b>Description:</b> {descriptions[name]}
<br />
<b>Details & How to Use:</b>
<br />
<br />
<div dangerouslySetInnerHTML={{ __html: tutorials[name] as unknown as string }} />
</Section>
);
};
File diff suppressed because one or more lines are too long
+2
View File
@@ -2928,6 +2928,7 @@
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\neutral.dm"
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\positive.dm"
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\trait.dm"
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\traits_tutorial.dm"
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\weaver_objs.dm"
#include "code\modules\mob\living\carbon\human\species\station\traits_vr\weaver_recipies.dm"
#include "code\modules\mob\living\carbon\human\species\virtual_reality\avatar.dm"
@@ -3980,6 +3981,7 @@
#include "code\modules\tgui\modules\shutoff_monitor.dm"
#include "code\modules\tgui\modules\supermatter_monitor.dm"
#include "code\modules\tgui\modules\teleporter.dm"
#include "code\modules\tgui\modules\trait_tutorial_tgui.dm"
#include "code\modules\tgui\modules\admin\player_notes.dm"
#include "code\modules\tgui\modules\ntos-only\cardmod.dm"
#include "code\modules\tgui\modules\ntos-only\configurator.dm"