From d38fc8f74b2df984eceded38591f910eb12f1034 Mon Sep 17 00:00:00 2001 From: TiviPlus <572233640+TiviPlus@users.noreply.com> Date: Sun, 3 Nov 2024 14:40:41 +0100 Subject: [PATCH 1/7] Adds new marine race: Prototype Supersoldier --- .../dcs/signals/signals_keybindings.dm | 1 + code/__DEFINES/is_helpers.dm | 2 +- code/__DEFINES/mobs.dm | 29 +- code/__DEFINES/movespeed_modification.dm | 2 + code/__DEFINES/species.dm | 11 + code/__DEFINES/traits.dm | 1 + code/_globalvars/bitfields.dm | 1 - .../prototype_supersoldier_action.dm | 413 ++++++++++++++++++ code/datums/components/stamina_behavior.dm | 6 +- code/datums/keybinding/human.dm | 8 + code/datums/namepool.dm | 12 +- code/datums/status_effects/gun_skill.dm | 5 + code/game/objects/effects/decals/rune.dm | 6 +- code/game/objects/effects/misc.dm | 3 - .../objects/items/tools/cleaning_tools.dm | 4 +- code/modules/admin/panels/player_panel.dm | 1 + code/modules/admin/topic.dm | 2 + code/modules/client/preferences.dm | 3 + code/modules/client/preferences_savefile.dm | 11 + .../mob/living/carbon/human/_species.dm | 2 + .../mob/living/carbon/human/human_helpers.dm | 4 +- .../mob/living/carbon/human/human_species.dm | 3 + .../living/carbon/human/human_status_procs.dm | 2 + .../carbon/human/species_types/humans.dm | 3 +- .../species_types/prototype_supersoldier.dm | 15 + .../carbon/human/species_types/synthetics.dm | 1 + .../carbon/human/species_types/vatborns.dm | 2 + .../mob/living/carbon/human/update_icons.dm | 2 +- .../mob/new_player/sprite_accessories.dm | 2 +- icons/mob/actions.dmi | Bin 21083 -> 19814 bytes .../{female_synth.tx => female_synth.txt} | 0 strings/names/prototype_supersoldier.txt | 92 ++++ tgmc.dme | 3 + .../tgui/interfaces/SupersoldierStims.tsx | 240 ++++++++++ 34 files changed, 859 insertions(+), 33 deletions(-) create mode 100644 code/__DEFINES/species.dm create mode 100644 code/datums/actions/species_actions/prototype_supersoldier_action.dm create mode 100644 code/modules/mob/living/carbon/human/species_types/prototype_supersoldier.dm rename strings/names/{female_synth.tx => female_synth.txt} (100%) create mode 100644 strings/names/prototype_supersoldier.txt create mode 100644 tgui/packages/tgui/interfaces/SupersoldierStims.tsx diff --git a/code/__DEFINES/dcs/signals/signals_keybindings.dm b/code/__DEFINES/dcs/signals/signals_keybindings.dm index 2b09eba697abc..5d84abe7e0fe1 100644 --- a/code/__DEFINES/dcs/signals/signals_keybindings.dm +++ b/code/__DEFINES/dcs/signals/signals_keybindings.dm @@ -77,6 +77,7 @@ #define COMSIG_KB_GIVE "keybind_give" #define COMSIG_KB_HELMETMODULE "keybinding_helmetmodule" #define COMSIG_KB_ROBOT_AUTOREPAIR "keybinding_robot_autorepair" +#define COMSIG_KB_STIMS "keybinding_stims_menu" #define COMSIG_KB_SUITLIGHT "keybinding_suitlight" #define COMSIG_KB_MOVEORDER "keybind_moveorder" #define COMSIG_KB_HOLDORDER "keybind_holdorder" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 2353c3efec764..abd4d0490ca0f 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -246,7 +246,7 @@ GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur"))) #define isAPC(A) (istype(A, /obj/machinery/power/apc)) -#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable +#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/decal/cleanable/rune)) //if something is cleanable #define isvehicle(A) (istype(A, /obj/vehicle)) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 0cd074464345b..e5f594d1c1f17 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -376,21 +376,20 @@ GLOBAL_LIST_INIT(xenoupgradetiers, list(XENO_UPGRADE_BASETYPE, XENO_UPGRADE_INVA #define NO_OVERDOSE (1<<4) #define NO_POISON (1<<5) #define NO_CHEM_METABOLIZATION (1<<6) -#define HAS_SKIN_TONE (1<<7) -#define HAS_SKIN_COLOR (1<<8) -#define HAS_LIPS (1<<9) -#define HAS_UNDERWEAR (1<<10) -#define HAS_NO_HAIR (1<<11) -#define IS_SYNTHETIC (1<<12) -#define NO_STAMINA (1<<13) -#define DETACHABLE_HEAD (1<<14) -#define USES_ALIEN_WEAPONS (1<<15) -#define NO_DAMAGE_OVERLAY (1<<16) -#define HEALTH_HUD_ALWAYS_DEAD (1<<17) -#define PARALYSE_RESISTANT (1<<18) -#define ROBOTIC_LIMBS (1<<19) -#define GREYSCALE_BLOOD (1<<20) -#define IS_INSULATED (1<<21) +#define HAS_SKIN_COLOR (1<<7) +#define HAS_LIPS (1<<8) +#define HAS_UNDERWEAR (1<<9) +#define HAS_NO_HAIR (1<<10) +#define IS_SYNTHETIC (1<<11) +#define NO_STAMINA (1<<12) +#define DETACHABLE_HEAD (1<<13) +#define USES_ALIEN_WEAPONS (1<<14) +#define NO_DAMAGE_OVERLAY (1<<15) +#define HEALTH_HUD_ALWAYS_DEAD (1<<16) +#define PARALYSE_RESISTANT (1<<17) +#define ROBOTIC_LIMBS (1<<18) +#define GREYSCALE_BLOOD (1<<19) +#define IS_INSULATED (1<<20) //================================================= diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index e62b7e8be31af..282ef2bc01671 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -37,6 +37,8 @@ #define MOVESPEED_ID_SPECIES "SPECIES_SPEED_MOD" +#define MOVESPEED_ID_STIM_INCREASE "ELF_SPELL_INCREASE" + #define MOVESPEED_ID_BULKY_DRAGGING "BULKY_DRAG" #define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY" #define MOVESPEED_ID_COLD "COLD" diff --git a/code/__DEFINES/species.dm b/code/__DEFINES/species.dm new file mode 100644 index 0000000000000..334e0e2f66878 --- /dev/null +++ b/code/__DEFINES/species.dm @@ -0,0 +1,11 @@ +#define SPECIES_LIMB_GENERIC 0 +#define SPECIES_LIMB_HUMAN 1 +#define SPECIES_LIMB_CLONE 2 + + +#define STIM_DURATION_DEFAULT 10 MINUTES +#define MAX_ACTIVE_STIMS 5 +#define MAX_STIM_SEQUENCES 10 + +#define STIM_ALLOW_DUPE (1<<0) +#define STIM_ABSTRACT (1<<1) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 856b0b64d65fc..70b14b8285c08 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -208,6 +208,7 @@ #define TRAIT_STAGGERIMMUNE "stagger_immunity" //Immunity to stagger #define TRAIT_STAGGER_RESISTANT "stagger_resistant" //Resistance to certain sources of stagger #define TRAIT_SLOWDOWNIMMUNE "slowdown_immunity" //Immunity to slowdown +#define TRAIT_EARDAMAGE_IMMUNE "eardmgimmune" //immune to getting deaf through ear dmg #define TRAIT_SEE_IN_DARK "see_in_dark" //Able to see in dark #define TRAIT_MUTED "muted" //target is mute and can't speak #define TRAIT_TURRET_HIDDEN "turret_hidden" //target gets passed over by turrets choosing a victim diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index a4386432b0da5..9ae282ed79a5e 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -407,7 +407,6 @@ GLOBAL_LIST_INIT(bitfields, list( "NO_OVERDOSE" = NO_OVERDOSE, "NO_POISON" = NO_POISON, "NO_CHEM_METABOLIZATION" = NO_CHEM_METABOLIZATION, - "HAS_SKIN_TONE" = HAS_SKIN_TONE, "HAS_SKIN_COLOR" = HAS_SKIN_COLOR, "HAS_LIPS" = HAS_LIPS, "HAS_UNDERWEAR" = HAS_UNDERWEAR, diff --git a/code/datums/actions/species_actions/prototype_supersoldier_action.dm b/code/datums/actions/species_actions/prototype_supersoldier_action.dm new file mode 100644 index 0000000000000..5f5188171691f --- /dev/null +++ b/code/datums/actions/species_actions/prototype_supersoldier_action.dm @@ -0,0 +1,413 @@ +/atom/movable/stim_say_holder + tts_silicon_voice_effect = TRUE + speech_span = SPAN_ROBOT + name = "stimulant implant" + +/datum/action/supersolder_stims + name = "Inject Stimulants" + action_icon_state = "stim_menu" + interaction_flags = INTERACT_OBJ_UI + keybinding_signals = list( + KEYBINDING_NORMAL = COMSIG_KB_STIMS, + ) + ///list of currently running stims for this owner + var/list/datum/stim/active_stims = list() + ///say holder we use to say things for fluff + var/atom/movable/stim_say_holder/say_holder + +/datum/action/supersolder_stims/New(Target) + . = ..() + say_holder = new + +/datum/action/supersolder_stims/Destroy() + QDEL_NULL(say_holder) + return ..() + +/datum/action/supersolder_stims/give_action(mob/M) + . = ..() + say_holder.forceMove(M) + +/datum/action/supersolder_stims/remove_action(mob/M) + say_holder.moveToNullspace() + return ..() + + +/datum/action/supersolder_stims/can_use_action() + . = ..() + if(!.) + return + return !owner.incapacitated() + +/datum/action/supersolder_stims/action_activate() + . = ..() + if(!.) + return + interact(owner) + +/datum/action/supersolder_stims/ui_state() + return GLOB.not_incapacitated_state + +/datum/action/supersolder_stims/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "SupersoldierStims") + ui.open() + +/datum/action/supersolder_stims/ui_static_data(mob/user) + var/static/list/data = list() + if(!length(data)) // list not built yet + data["stims"] = list() + for(var/datum/stim/typepath AS in subtypesof(/datum/stim)) + if(typepath::stim_flags & STIM_ABSTRACT) + continue + data["stims"] += list(list( + "name" = typepath::name, + "desc" = typepath::desc, + "uid" = typepath::stim_uid, + "duration" = typepath::stim_duration, + "allow_dupe" = (typepath::stim_flags & STIM_ALLOW_DUPE), + )) + data["max_stims"] = MAX_ACTIVE_STIMS + return data + +GLOBAL_LIST_INIT(stim_type_lookup, init_stims()) +/proc/init_stims() + . = list() + for(var/datum/stim/typepath AS in subtypesof(/datum/stim)) + .[typepath::stim_uid] = typepath + +/datum/action/supersolder_stims/ui_data(mob/user) + var/list/data = list() + data["active_stims"] = list() + for(var/datum/stim/active_stim AS in active_stims) + data["active_stims"] += list(list( + "active_uid" = active_stim.stim_uid, + "decisecondsleft" = ((active_stim.castedtime + active_stim.stim_duration) - world.time) + )) + data["active_stimsets"] = user.client?.prefs.stim_sequences + return data + +/datum/action/supersolder_stims/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + var/sequence_name = params["sequence"] + var/list/stim_sequence + if(sequence_name) + if(!length(sequence_name)) + return FALSE + if(!(sequence_name in owner.client?.prefs.stim_sequences)) + return FALSE + stim_sequence = owner.client?.prefs.stim_sequences[sequence_name] + + var/stim_uid = params["stim"] + if(stim_uid && !(stim_uid in GLOB.stim_type_lookup)) + return FALSE + + switch(action) + if("cast_sequence") + ui.close() + cast_stim_sequence(stim_sequence) + return FALSE + + if("change_pos") + var/old_pos = text2num(params["old_pos"]) + var/new_pos = text2num(params["new_pos"]) + stim_sequence.Swap(old_pos, new_pos) + owner.client?.prefs.save_preferences() + return TRUE + + if("remove_from_sequence") + var/old_pos = text2num(params["old_pos"]) + stim_sequence.Cut(old_pos, old_pos + 1) + owner.client?.prefs.save_preferences() + return TRUE + + if("add_to_sequence") + if(length(stim_sequence) >= MAX_ACTIVE_STIMS) + to_chat(owner, span_info("Cannot have more than [MAX_ACTIVE_STIMS] stims.")) + return FALSE + var/datum/stim/stim_type = GLOB.stim_type_lookup[stim_uid] + if((stim_uid in stim_sequence) && !(stim_type::stim_flags & STIM_ALLOW_DUPE)) + to_chat(owner, span_info("Cannot duplicate this stim")) + return FALSE + stim_sequence += stim_uid + owner.client?.prefs.save_preferences() + return TRUE + + if("new_sequence") + var/new_name = params["new_name"] + if(!istext(new_name) || !length(new_name)) + return FALSE + if(new_name in owner.client?.prefs.stim_sequences) + to_chat(owner, "That sequence already exists!") + return FALSE + owner.client?.prefs.stim_sequences[new_name] = list() + owner.client?.prefs.save_preferences() + return TRUE + + if("delete_sequence") + owner.client?.prefs.stim_sequences -= sequence_name + owner.client?.prefs.save_preferences() + return TRUE + +///takes a list of stim types or stim UIDs to cast them in an order with all the fanfare associated it like yelling it out and sounds +/datum/action/supersolder_stims/proc/cast_stim_sequence(list/stim_types) + var/list/datum/stim/stims = list() + for(var/typepath in stim_types) + if(!ispath(typepath)) + typepath = GLOB.stim_type_lookup[typepath] + if(!ispath(typepath)) + stack_trace("bad stim passed, must be UID or type: [typepath]") + continue + stims += new typepath + for(var/datum/stim/stim AS in stims) + if(!stim.can_cast(owner, src)) + return + playsound(owner.loc, 'sound/machines/hiss.ogg', 30, TRUE) + say_holder.say(stim.cast_say, forced=TRUE) + if(!do_after(owner, stim.cast_delay, extra_checks=CALLBACK(stim, TYPE_PROC_REF(/datum/stim, can_cast), owner, src))) + return + stim.finish_cast(owner) + stim.castedtime = world.time + active_stims += stim + addtimer(CALLBACK(src, PROC_REF(clean_stim), stim), stim.stim_duration) + +///commands a stim to clean itself up and cleans iwhats needed from its end +/datum/action/supersolder_stims/proc/clean_stim(datum/stim/stim) + if(!(stim in active_stims)) // cleaned up already by something else, mostly for timer/qdel skew + return + active_stims -= stim + stim.end_effects(owner) + qdel(stim) + +/datum/stim + /// displayed in UI + var/name = "DEFAULT STIM" + /// displayed as desc in the UI + var/desc = "Example simple desc" + ///UIDS BETWEEN STIMS MUST BE UNIQUE, THIS IS USED FOR PREF (SO DONT CHANGE IT) AND IDENTIFICATION IN THE TGUI + var/stim_uid = "DO NOT SAVE" + ///what we say when casting the stim + var/cast_say = "GAYT" + /// on init, typepath to make particles, once finish cast executes then the actual effects + var/obj/effect/abstract/particle_holder/particles + + /// stim flags see species.dm defines + var/stim_flags = NONE + ///how long we last this stim, cannot recast if its not duplicatable during this time + var/stim_duration = STIM_DURATION_DEFAULT + ///delay while casting until the next stim + var/cast_delay = 2.5 SECONDS + + ///last world.time that this stim was cast at, for tracking in UI + var/castedtime + +///bool return as to whether we are currently alllowed to cats this stim +/datum/stim/proc/can_cast(mob/living/owner, datum/action/supersolder_stims/action, silent=FALSE) + SHOULD_CALL_PARENT(TRUE) + for(var/datum/action/supersolder_stims/stim AS in action.active_stims) + if((type == stim.type) && !(stim_flags & STIM_ALLOW_DUPE)) + if(!silent) + owner.balloon_alert(owner, "cannot duplicate stim") + return FALSE + if(length(action.active_stims) >= MAX_ACTIVE_STIMS) + if(!silent) + owner.balloon_alert(owner, "too many stims") + return FALSE + return TRUE + +/// all effects to apply/play when applying this stim +/datum/stim/proc/finish_cast(mob/living/owner) + if(particles) + particles = new(owner, particles) + +/// any extra effects to play/cleanup when the stim ends +/datum/stim/proc/end_effects(mob/living/owner) + if(particles) + QDEL_NULL(particles) + +/datum/stim/status_effect + stim_flags = STIM_ABSTRACT + ///typepath for status effect we are going to be applying + var/effect_type = /datum/status_effect + +/datum/stim/status_effect/finish_cast(mob/living/owner) + owner.set_timed_status_effect(stim_duration, effect_type, only_if_higher = TRUE) + +/datum/stim/status_effect/scatter + name = "Scatter" + cast_say = "Releasing muscarinic agonists..." + desc = "Decreases the amount of scatter from shooting guns by activating arm nerves beyond normal levels." + stim_uid = "scatterreduction" + stim_flags = NONE + effect_type = /datum/status_effect/gun_skill/scatter/buff + +/datum/stim/status_effect/accuracy + name = "Accuracy" + cast_say = "Releasing hypotropine..." + desc = "Increases accuracy at range from shooting guns by deepening perception by adjusting the pupils." + stim_uid = "accuracyimprovement" + stim_flags = NONE + effect_type = /datum/status_effect/gun_skill/accuracy/buff + +/datum/stim/trait + stim_flags = STIM_ABSTRACT + /// trait we will be applying + var/trait_type = null + +/datum/stim/trait/finish_cast(mob/living/owner) + ADD_TRAIT(owner, TRAIT_EARDAMAGE_IMMUNE, name) + return ..() + +/datum/stim/trait/end_effects(mob/living/owner) + REMOVE_TRAIT(owner, TRAIT_EARDAMAGE_IMMUNE, name) + return ..() + +/datum/stim/trait/no_ear_damage + name = "Ear Resistance" + desc = "Reinforces your ears, making you immune to ear damage while it is active." + cast_say = "Reinforcing auricle..." + stim_uid = "deafnessimmunity" + stim_flags = NONE + trait_type = TRAIT_EARDAMAGE_IMMUNE + +/datum/stim/trait/no_flashbang + name = "Flash Resistance" + desc = "Reinforces your eyes, making you immune to bright flashes while it is active. Does not make you immune to eye damage!" + cast_say = "Reinforcing oculus..." + stim_uid = "noflashbang" + stim_flags = NONE + trait_type = TRAIT_FLASHBANGIMMUNE + +/datum/stim/trait/no_footsteps + name = "Silent footsteps" + desc = "Remolds your feet temporarily, making your footsteps completely silent." + cast_say = "Adjusting pedisurface..." + stim_uid = "nofootsteps" + stim_flags = NONE + trait_type = TRAIT_SILENT_FOOTSTEPS + +/datum/stim/portal_toggle + name = "Portal Vulnerability" + desc = "Inverts your dimensionalal alignment through a mix of targetted isotopes, making you immune to portals if you weren't already, and makes you able to go through them if you were." + cast_say = "Inverting polarity..." + stim_uid = "portallchange" + stim_flags = NONE + +/datum/stim/portal_toggle/finish_cast(mob/living/owner) + owner.resistance_flags ^= PORTAL_IMMUNE + return ..() + +/datum/stim/portal_toggle/end_effects(mob/living/owner) + owner.resistance_flags ^= PORTAL_IMMUNE + return ..() + +/datum/stim/speed_increase + name = "Speed Increase" + desc = "Increases your speed of movement, making you walk and move passively faster." + cast_say = "Adminstering adrenaline..." + stim_uid = "speedincrease" + particles = /particles/stims/speed + stim_flags = NONE + +/datum/stim/speed_increase/finish_cast(mob/living/owner) + owner.add_movespeed_modifier(MOVESPEED_ID_STIM_INCREASE, TRUE, 0, NONE, TRUE, 1) + return ..() + +/datum/stim/speed_increase/end_effects(mob/living/owner) + owner.remove_movespeed_modifier(MOVESPEED_ID_STIM_INCREASE) + return ..() + +/datum/stim/stam_usage_decrease + name = "Stamina Efficiency" + desc = "Increases your ease of movement, making you use up stamina slower." + cast_say = "Administering synephrine..." + stim_uid = "stamusedecrease" + stim_flags = STIM_ALLOW_DUPE + ///cached amount that we edited + var/amount_edited + +/datum/stim/stam_usage_decrease/finish_cast(mob/living/owner) + var/datum/component/stamina_behavior/stam = owner.GetComponent(/datum/component/stamina_behavior) + amount_edited = stam.stim_drain_modifier * 0.3 + stam.stim_drain_modifier -= amount_edited + return ..() + +/datum/stim/stam_usage_decrease/end_effects(mob/living/owner) + var/datum/component/stamina_behavior/stam = owner.GetComponent(/datum/component/stamina_behavior) + stam.stim_drain_modifier += amount_edited + return ..() + + +/datum/stim/stamina_regen + name = "Stamina Recovery" + desc = "Increases your resistance to tiredness, making you use stamina more slowly." + cast_say = "Administering amphetamines..." + stim_uid = "stamregenincrease" + stim_flags = STIM_ALLOW_DUPE + +/datum/stim/stamina_regen/finish_cast(mob/living/owner) + owner.add_stamina_regen_modifier(name, 3*STAMINA_SKILL_REGEN_MOD) //equates 3 levels of stam skill + return ..() + +/datum/stim/stamina_regen/end_effects(mob/living/owner) + owner.remove_stamina_regen_modifier(name) + return ..() + +/datum/stim/skills + stim_flags = STIM_ABSTRACT + ///assoc list list(SKILL = AMT_TOCHANGE) for when we want to let themm use it + var/list/skills + ///assoc list list(SKILL = MAXIMUM_INT) for when we dont want to let them cast this + var/list/max_skills + +/datum/stim/skills/can_cast(mob/living/owner, datum/action/supersolder_stims/action, silent) + . = ..() + for(var/skill in max_skills) + if(owner.skills.getRating(skill) >= max_skills[skill]) + if(!silent) + owner.balloon_alert(owner, "skill already too high") + return FALSE + +/datum/stim/skills/finish_cast(mob/living/owner) + . = ..() + owner.set_skills(owner.skills.modifyRating(arglist(skills))) + +/datum/stim/skills/end_effects(mob/living/owner) + . = ..() + var/list/negativeskills = skills.Copy() + for(var/skill in negativeskills) + negativeskills[skill] = -negativeskills[skill] + owner.set_skills(owner.skills.modifyRating(arglist(negativeskills))) + +/datum/stim/skills/melee + name = "Melee Skill" + desc = "Enhances your strikes with magic, increasing your skill in melee." + cast_say = "Neural reaction module loading..." + stim_uid = "meleeskillbuff" + stim_flags = STIM_ALLOW_DUPE + skills = list(SKILL_MELEE_WEAPONS = 1) + max_skills = list(SKILL_MELEE_WEAPONS = SKILL_MELEE_SUPER) + +/datum/stim/skills/powerloader + name = "Powerloader Skill" + desc = "Increase your skill at using power loaders." + cast_say = "Neural powerloader module loading..." + stim_uid = "powerloaderskillbuff" + stim_flags = STIM_ALLOW_DUPE + skills = list(SKILL_POWERLOADER = 1) + max_skills = list(SKILL_POWERLOADER = SKILL_POWERLOADER_PRO) // ensures RO and such are still better + +/particles/stims + count = 10 + spawning = 1 + gravity = list(0, -0.03) + icon = 'icons/effects/particles/generic_particles.dmi' + lifespan = 10 + fade = 8 + color = 1 + color_change = 0.05 + position = generator(GEN_SPHERE, 0, 14, NORMAL_RAND) + +/particles/stims/speed + icon_state = "square" + gradient = list(1, "#001eff", 2, "#00ffc3", "loop") diff --git a/code/datums/components/stamina_behavior.dm b/code/datums/components/stamina_behavior.dm index 85327937f2c52..1ade85d6fb1ca 100644 --- a/code/datums/components/stamina_behavior.dm +++ b/code/datums/components/stamina_behavior.dm @@ -1,7 +1,9 @@ /datum/component/stamina_behavior var/stamina_state = STAMINA_STATE_IDLE - ///multiplier on stamina cost + ///multiplier on stamina cost due to gravity var/drain_modifier = 1 + ///extra modifier for handling elfs + var/stim_drain_modifier = 1 /datum/component/stamina_behavior/Initialize() @@ -46,7 +48,7 @@ var/mob/living/stamina_holder = parent if(oldloc == stamina_holder.loc) return - stamina_holder.adjustStaminaLoss(1 * drain_modifier) + stamina_holder.adjustStaminaLoss(1 * drain_modifier * stim_drain_modifier) if(stamina_holder.staminaloss >= 0) stamina_holder.toggle_move_intent(MOVE_INTENT_WALK) diff --git a/code/datums/keybinding/human.dm b/code/datums/keybinding/human.dm index 5700bf94c6fe4..89ff08ff86cc5 100644 --- a/code/datums/keybinding/human.dm +++ b/code/datums/keybinding/human.dm @@ -132,6 +132,14 @@ description = "Activate combat robot's autorepair" keybind_signal = COMSIG_KB_ROBOT_AUTOREPAIR +/datum/keybinding/human/stims_menu + hotkey_keys = list("g") + name = "stims menu" + full_name = "Supersoldier Stims" + description = "Manage injecting stims as a prototype supersoldier." + keybind_signal = COMSIG_KB_STIMS + + /datum/keybinding/human/move_order name = "move_order" full_name = "Issue Move Order" diff --git a/code/datums/namepool.dm b/code/datums/namepool.dm index 20f422009efc7..9a1ed1ca77728 100644 --- a/code/datums/namepool.dm +++ b/code/datums/namepool.dm @@ -47,7 +47,7 @@ GLOBAL_LIST_EMPTY_TYPED(operation_namepool, /datum/operation_namepool) /datum/namepool/sectoid/get_random_name() return "Sectoid [rand(1,9)]X[ascii2text(rand(65, 87))]" //65 to 87 is (uppercase) A to W -/datum/namepool/vatborn/ +/datum/namepool/vatborn firstname_male_pool = "names/first_male" firstname_female_pool = "names/first_female" @@ -87,3 +87,13 @@ GLOBAL_LIST_EMPTY_TYPED(operation_namepool, /datum/operation_namepool) operation_name += " [pick(SSstrings.get_list_from_file(operation_prefixes))]" operation_name += "-[pick(SSstrings.get_list_from_file(operation_postfixes))]" return uppertext(operation_name) + +/datum/namepool/prototype_supersoldier + firstname_female_pool = "names/prototype_supersoldier" + +/datum/namepool/prototype_supersoldier/get_random_name(gender = MALE) + . = "[ascii2text(rand(65, 90))]" //65 to 87 is (uppercase) A to Z + . += "[rand(1,9)]" + . += "[ascii2text(rand(65, 90))] " //65 to 87 is (uppercase) A to Z + . += pick(SSstrings.get_list_from_file(firstname_female_pool)) + //result is [A-Z][num][A-Z] - [Combat Role] e.g A2V Saboteur diff --git a/code/datums/status_effects/gun_skill.dm b/code/datums/status_effects/gun_skill.dm index 6182689caa754..2dfed7c5b7546 100644 --- a/code/datums/status_effects/gun_skill.dm +++ b/code/datums/status_effects/gun_skill.dm @@ -1,5 +1,6 @@ /datum/status_effect/gun_skill id = "gun_skill" + alert_type = null ///reference to particle effect holder is present for this stack, initially a reference to the type to use var/obj/effect/abstract/particle_holder/particles = /particles/gun_skill @@ -90,6 +91,8 @@ /particles/gun_skill/accuracy/buff gradient = list(1, "#00ff00", 2, "#ff0", "loop") + count = 10 + spawning = 1 /particles/gun_skill/accuracy/debuff gradient = list(1, "#f00", 2, "#ff0", "loop") @@ -99,6 +102,8 @@ /particles/gun_skill/scatter/buff icon_state = "up_arrow" gradient = list(1, "#00ff00", 2, "#ff0", "loop") + count = 10 + spawning = 1 /particles/gun_skill/scatter/debuff icon_state = "down_arrow" diff --git a/code/game/objects/effects/decals/rune.dm b/code/game/objects/effects/decals/rune.dm index 5849500e1b808..a6771c617b798 100644 --- a/code/game/objects/effects/decals/rune.dm +++ b/code/game/objects/effects/decals/rune.dm @@ -9,18 +9,18 @@ /obj/effect/decal/cleanable/rune/Initialize(mapload) . = ..() - icon_state = "rune[pick("1","2","3","4","5","6")]" + icon_state = "rune[rand(1, 6)]" /obj/effect/decal/cleanable/rune/blank icon_state = "main1" /obj/effect/decal/cleanable/rune/blank/Initialize(mapload) . = ..() - icon_state = "main[pick("1","2","3","4","5","6")]" + icon_state = "main[rand(1, 6)]" /obj/effect/decal/cleanable/rune/alt icon_state = "shade" /obj/effect/decal/cleanable/rune/alt/Initialize(mapload) . = ..() - icon_state = "shade[pick("1","2","3","4","5","6")]" + icon_state = "shade[rand(1, 6)]" diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 0d4b94135df87..cfc8602a086dd 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -37,9 +37,6 @@ desc = "This looks like a projection of something." anchored = TRUE -/obj/effect/rune/attunement - luminosity = 5 - /obj/effect/soundplayer anchored = TRUE opacity = FALSE diff --git a/code/game/objects/items/tools/cleaning_tools.dm b/code/game/objects/items/tools/cleaning_tools.dm index 444c761703244..26c815a631ac6 100644 --- a/code/game/objects/items/tools/cleaning_tools.dm +++ b/code/game/objects/items/tools/cleaning_tools.dm @@ -25,7 +25,7 @@ if(source.reagents.has_reagent(/datum/reagent/water, 1)) wash() for(var/obj/effect/O in src) - if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay)) + if(istype(O, /obj/effect/decal/cleanable/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay)) qdel(O) source.reagents.reaction(src, TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. source.reagents.remove_any(1) //reaction() doesn't use up the reagents @@ -33,7 +33,7 @@ /obj/item/tool/mop/afterattack(atom/A, mob/user, proximity) if(!proximity) return - if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) + if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/decal/cleanable/rune)) if(reagents.total_volume < 1) balloon_alert(user, "Mop is dry") return diff --git a/code/modules/admin/panels/player_panel.dm b/code/modules/admin/panels/player_panel.dm index 79cb872a323c8..c53eefb355adf 100644 --- a/code/modules/admin/panels/player_panel.dm +++ b/code/modules/admin/panels/player_panel.dm @@ -464,6 +464,7 @@ Early_Synth | Vatborn | Vatgrown | + Prototype Supersoldier | Combat_Robot | Monkey | Moth | diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d5937fd56f42d..b4183904f8422 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -468,6 +468,8 @@ Status: [status ? status : "Unknown"] | Damage: [health ? health : "None"] newmob = M.change_mob_type(/mob/living/carbon/human/species/skeleton, location, null, delmob) if("monkey") newmob = M.change_mob_type(/mob/living/carbon/human/species/monkey, location, null, delmob, "Monkey") //todo doublecheck this + if("prototype_supersoldier") + newmob = M.change_mob_type(/mob/living/carbon/human/species/prototype_supersoldier, location, null, delmob) //todo doublecheck this if("moth") newmob = M.change_mob_type(/mob/living/carbon/human/species/moth, location, null, delmob, "Moth") if("zombie") diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8af5da8e8fb8a..9638419f4042e 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -192,6 +192,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) ///What outfit typepaths we've favorited in the SelectEquipment menu var/list/favorite_outfits = list() + /// list("sequencename" = list(uid, uid, uid)) + var/list/stim_sequences = list() + ///List of slot_draw_order var/list/slot_draw_order_pref = list() diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 0df2eff1352e1..cb90d26234d5b 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -184,6 +184,7 @@ READ_FILE(S["mute_xeno_health_alert_messages"], mute_xeno_health_alert_messages) READ_FILE(S["show_xeno_rank"], show_xeno_rank) + READ_FILE(S["stim_sequences"], stim_sequences) // Runechat options READ_FILE(S["chat_on_map"], chat_on_map) READ_FILE(S["max_chat_length"], max_chat_length) @@ -247,6 +248,10 @@ mute_xeno_health_alert_messages = sanitize_integer(mute_xeno_health_alert_messages, FALSE, TRUE, initial(mute_xeno_health_alert_messages)) show_xeno_rank = sanitize_integer(show_xeno_rank, FALSE, TRUE, initial(show_xeno_rank)) + stim_sequences = sanitize_islist(stim_sequences, list()) + for(var/spell_sequence in stim_sequences) + stim_sequences[spell_sequence] = sanitize_islist(stim_sequences[spell_sequence], list(), null, TRUE, GLOB.stim_type_lookup) + chat_on_map = sanitize_integer(chat_on_map, FALSE, TRUE, initial(chat_on_map)) max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length)) see_chat_non_mob = sanitize_integer(see_chat_non_mob, FALSE, TRUE, initial(see_chat_non_mob)) @@ -317,6 +322,10 @@ slot_draw_order_pref = sanitize_islist(slot_draw_order_pref, SLOT_DRAW_ORDER, length(SLOT_DRAW_ORDER), TRUE, SLOT_DRAW_ORDER) status_toggle_flags = sanitize_integer(status_toggle_flags, NONE, MAX_BITFLAG, initial(status_toggle_flags)) + stim_sequences = sanitize_islist(stim_sequences, list()) + for(var/spell_sequence in stim_sequences) + stim_sequences[spell_sequence] = sanitize_islist(stim_sequences[spell_sequence], list(), null, TRUE, GLOB.stim_type_lookup) + // Runechat chat_on_map = sanitize_integer(chat_on_map, FALSE, TRUE, initial(chat_on_map)) max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length)) @@ -372,6 +381,8 @@ WRITE_FILE(S["mute_xeno_health_alert_messages"], mute_xeno_health_alert_messages) WRITE_FILE(S["show_xeno_rank"], show_xeno_rank) + WRITE_FILE(S["stim_sequences"], stim_sequences) + // Runechat options WRITE_FILE(S["chat_on_map"], chat_on_map) WRITE_FILE(S["max_chat_length"], max_chat_length) diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 421269cc22770..643518fa7a084 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -11,6 +11,8 @@ var/species_type = SPECIES_HUMAN ///Special effects that are inherent to our species var/species_flags = NONE + ///used in limb code to find which bodytype files to pull from, yes this code can defenitely be improved + var/limb_type = SPECIES_LIMB_GENERIC //----Icon stuff here ///Normal icon file diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 3dfa57e51e280..f2e610d69a8dc 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -5,7 +5,7 @@ return g /proc/get_limb_icon_name(datum/species/S, gender, limb_name, ethnicity) - if(S.name == "Human" || S.name == "Synthetic") + if(S.limb_type == SPECIES_LIMB_HUMAN) // todo this section is fucking stupid and can be way more generic easily switch(limb_name) if ("torso", "chest") return "[ethnicity]_torso_[get_gender_name(gender)]" @@ -42,7 +42,7 @@ else return null - else if(S.name == "Vatborn" || S.name == "Early Vat-Grown Human" || S.name == "Vat-Grown Human") + else if(S.limb_type == SPECIES_LIMB_CLONE) switch(limb_name) if ("torso", "chest") return "torso_[get_gender_name(gender)]" diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index a94c6d1f1c950..9125bdb93b89b 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -67,3 +67,6 @@ /mob/living/carbon/human/species/robot/bravada race = "Sterling Combat Robot" + +/mob/living/carbon/human/species/prototype_supersoldier + race = "Prototype Supersoldier" diff --git a/code/modules/mob/living/carbon/human/human_status_procs.dm b/code/modules/mob/living/carbon/human/human_status_procs.dm index ac8f9e795bed3..1085855d0b87d 100644 --- a/code/modules/mob/living/carbon/human/human_status_procs.dm +++ b/code/modules/mob/living/carbon/human/human_status_procs.dm @@ -65,6 +65,8 @@ /mob/living/carbon/human/adjust_ear_damage(damage = 0, deaf = 0) + if(HAS_TRAIT(src, TRAIT_EARDAMAGE_IMMUNE)) + return if(isspeciessynthetic(src)) return return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index f94073b510a78..506aae860f764 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -1,8 +1,9 @@ /datum/species/human name = "Human" unarmed_type = /datum/unarmed_attack/punch - species_flags = HAS_SKIN_TONE|HAS_LIPS|HAS_UNDERWEAR + species_flags = HAS_LIPS|HAS_UNDERWEAR count_human = TRUE + limb_type = SPECIES_LIMB_HUMAN screams = list(MALE = SFX_MALE_SCREAM, FEMALE = SFX_FEMALE_SCREAM) paincries = list(MALE = SFX_MALE_PAIN, FEMALE = SFX_FEMALE_PAIN) diff --git a/code/modules/mob/living/carbon/human/species_types/prototype_supersoldier.dm b/code/modules/mob/living/carbon/human/species_types/prototype_supersoldier.dm new file mode 100644 index 0000000000000..152a4591ae630 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/prototype_supersoldier.dm @@ -0,0 +1,15 @@ +/datum/species/human/prototype_supersoldier + name = "Prototype Supersoldier" + max_stamina = 30 + slowdown = 1 + inherent_traits = list(TRAIT_TOO_TALL) + inherent_actions = list(/datum/action/supersolder_stims) + namepool = /datum/namepool/prototype_supersoldier + +/datum/species/human/prototype_supersoldier/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) + . = ..() + H.health_threshold_crit = -25 + +/datum/species/human/prototype_supersoldier/post_species_loss(mob/living/carbon/human/H) + . = ..() + H.health_threshold_crit = initial(H.health_threshold_crit) diff --git a/code/modules/mob/living/carbon/human/species_types/synthetics.dm b/code/modules/mob/living/carbon/human/species_types/synthetics.dm index be1b701a723c7..4e8d297e80a40 100644 --- a/code/modules/mob/living/carbon/human/species_types/synthetics.dm +++ b/code/modules/mob/living/carbon/human/species_types/synthetics.dm @@ -2,6 +2,7 @@ name = "Synthetic" default_language_holder = /datum/language_holder/synthetic unarmed_type = /datum/unarmed_attack/punch + limb_type = SPECIES_LIMB_HUMAN total_health = 125 //more health than regular humans diff --git a/code/modules/mob/living/carbon/human/species_types/vatborns.dm b/code/modules/mob/living/carbon/human/species_types/vatborns.dm index 3997b3e1caf6e..a85823b88114c 100644 --- a/code/modules/mob/living/carbon/human/species_types/vatborns.dm +++ b/code/modules/mob/living/carbon/human/species_types/vatborns.dm @@ -2,6 +2,7 @@ name = "Vatborn" icobase = 'icons/mob/human_races/r_vatborn.dmi' namepool = /datum/namepool/vatborn + limb_type = SPECIES_LIMB_CLONE /datum/species/human/vatborn/prefs_name(datum/preferences/prefs) return prefs.real_name @@ -13,6 +14,7 @@ burn_mod = 1.05 slowdown = 0.05 joinable_roundstart = FALSE + limb_type = SPECIES_LIMB_CLONE /datum/species/human/vatgrown/random_name(gender) return "CS-[gender == FEMALE ? "F": "M"]-[rand(111,999)]" diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index bdbb36e524fee..8a8174aba292b 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -559,7 +559,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) ///snowflake replacement handling since we don't have tg's proper handling of individual body parts as overlays /mob/living/carbon/human/proc/update_bodyparts() remove_overlay(BODYPARTS_LAYER) - overlays_standing[BODYPARTS_LAYER] = image(icon=stand_icon, layer=-BODYPARTS_LAYER) + overlays_standing[BODYPARTS_LAYER] = image(icon=stand_icon, icon_state="blank", layer=-BODYPARTS_LAYER) apply_overlay(BODYPARTS_LAYER) /mob/living/carbon/human/update_inv_w_uniform() diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 89607bdcfdff2..7e2723282218e 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -58,7 +58,7 @@ var/gender = NEUTER // Determines if the accessory will be skipped or included in random hair generations var/locked = FALSE //Is this part locked from roundstart selection? Used for parts that apply effects - var/list/species_allowed = list("Human","Human Hero", "Synthetic", "Early Synthetics", "Vat-Grown", "Vatborn") // Restrict some styles to specific species + var/list/species_allowed = list("Human","Human Hero", "Synthetic", "Early Synthetics", "Vat-Grown", "Vatborn", "Prototype Supersoldier") // Restrict some styles to specific species var/do_colouration = TRUE // Whether or not the accessory can be affected by colouration var/datum/greyscale_config/greyscale_config diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index 10d08e9339c357298a55b751bf9839f4f266f2a6..eb2202004d15ef618f9926514bc138da15fb654b 100644 GIT binary patch literal 19814 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 LNkvXXu0mjf(g!Zu diff --git a/strings/names/female_synth.tx b/strings/names/female_synth.txt similarity index 100% rename from strings/names/female_synth.tx rename to strings/names/female_synth.txt diff --git a/strings/names/prototype_supersoldier.txt b/strings/names/prototype_supersoldier.txt new file mode 100644 index 0000000000000..2bcb3f644680d --- /dev/null +++ b/strings/names/prototype_supersoldier.txt @@ -0,0 +1,92 @@ +Recon +Assault +Sniper +Heavy +Scout +Breacher +Supporter +Spotter +Guard +Communicator +Lander +Sapper +Rifleman +Spy +Snarer +Tanker +Cover +Pilot +Tech +Drone +Pointman +Seer +Sieger +Stormer +Vigil +Pathfinder +Shielder +Leader +Sentry +Front +Scout +Flarer +Gunner +Infantry +Grenadier +Ranger +Diver +Cavalry +Operator +Technician +Demolisher +Sharpshooter +Marksman +Observer +Artillerist +Navigator +Scoutmaster +Flanker +Decoy +Machinegunner +Dragoon +Enforcer +Vanguard +Tracker +Controller +Courier +Grunt +Fusilier +Patroller +Exfiltrator +Logistician +Reservist +Backliner +Striker +Sentinel +Watcher +Harrier +Bastion +Marine +Parachutist +Veteran +Shocktrooper +Stormtrooper +Policer +Commissar +Saboteur +Overseer +Outrider +Rescuer +Defender +Breachman +Pathseer +Juggernaut +Pointguard +Lookout +Charger +Signalman +Trainer +Firewatch +Ambusher +Pathleader +Airguard diff --git a/tgmc.dme b/tgmc.dme index 51ac661d0895c..2bd28b917df25 100644 --- a/tgmc.dme +++ b/tgmc.dme @@ -124,6 +124,7 @@ F// DM Environment file for baystation12.dme. #include "code\__DEFINES\spaceman_dmm.dm" #include "code\__DEFINES\span.dm" #include "code\__DEFINES\spatial_gridmap.dm" +#include "code\__DEFINES\species.dm" #include "code\__DEFINES\speech_channels.dm" #include "code\__DEFINES\speech_controller.dm" #include "code\__DEFINES\stat_tracking.dm" @@ -418,6 +419,7 @@ F// DM Environment file for baystation12.dme. #include "code\datums\actions\skill.dm" #include "code\datums\actions\weapon_actions.dm" #include "code\datums\actions\xeno_action.dm" +#include "code\datums\actions\species_actions\prototype_supersoldier_action.dm" #include "code\datums\actions\species_actions\sectoid_action.dm" #include "code\datums\changelog\changelog.dm" #include "code\datums\components\_component.dm" @@ -1766,6 +1768,7 @@ F// DM Environment file for baystation12.dme. #include "code\modules\mob\living\carbon\human\species_types\humans.dm" #include "code\modules\mob\living\carbon\human\species_types\monkeys.dm" #include "code\modules\mob\living\carbon\human\species_types\mothmen.dm" +#include "code\modules\mob\living\carbon\human\species_types\prototype_supersoldier.dm" #include "code\modules\mob\living\carbon\human\species_types\sectoids.dm" #include "code\modules\mob\living\carbon\human\species_types\skeletons.dm" #include "code\modules\mob\living\carbon\human\species_types\synthetics.dm" diff --git a/tgui/packages/tgui/interfaces/SupersoldierStims.tsx b/tgui/packages/tgui/interfaces/SupersoldierStims.tsx new file mode 100644 index 0000000000000..7ee1ce5a84e3c --- /dev/null +++ b/tgui/packages/tgui/interfaces/SupersoldierStims.tsx @@ -0,0 +1,240 @@ +import { Fragment, useState } from 'react'; + +import { useBackend } from '../backend'; +import { Button, Input, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +type Stim = { + name: string; + desc: string; + uid: string; + duration: number; + allow_dupe: boolean; +}; + +type ActiveBuffs = { + active_uid: string; + decisecondsleft: number; +}; + +type SupersoldierStimsData = { + stims: Stim[]; + active_stims: ActiveBuffs[]; + active_stimsets: { [key: string]: string[] }; + max_stims: number; +}; + +export const SupersoldierStims = (_props) => { + const { act, data } = useBackend(); + const [activeStimSet, setActiveStimsSet] = useState( + Object.keys(data.active_stimsets)[0] || '', + ); + const selected_stimset = data.active_stimsets[activeStimSet] || []; + // Function to find a stim ref by its UID in data.stims + const findStimByUID = (stim_uid) => { + return data.stims.find((stim) => stim.uid === stim_uid); + }; + + return ( + + +