From 98519f0f45c9caf76e3ef0f2286a2f3c1b26f5d7 Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:08:55 -0700 Subject: [PATCH] [MIRROR] Flamethrower TGUI + Tweaks (#10479) Co-authored-by: Cameron Lennox Co-authored-by: Willburd <7099514+Willburd@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- .../objects/items/weapons/flamethrower.dm | 219 ++++++++++-------- .../objects/items/weapons/tanks/tank_types.dm | 8 +- .../items/weapons/tools/weldingtool.dm | 21 +- .../tgui/interfaces/Flamethrower/index.tsx | 70 ++++++ .../tgui/interfaces/Flamethrower/types.ts | 11 + 5 files changed, 211 insertions(+), 118 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/Flamethrower/index.tsx create mode 100644 tgui/packages/tgui/interfaces/Flamethrower/types.ts diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index c181b8bb1c..0f53b08d26 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -1,3 +1,6 @@ +#define THROWER_MIN 50 +#define THROWER_MAX 1000 + /obj/item/flamethrower name = "flamethrower" desc = "You are a firestarter!" @@ -15,15 +18,27 @@ w_class = ITEMSIZE_NORMAL origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1) matter = list(MAT_STEEL = 500) - var/status = 0 - var/throw_amount = 100 - var/lit = 0 //on or off - var/operating = 0//cooldown + var/status = FALSE + var/throw_amount = THROWER_MIN + var/lit = FALSE //on or off + var/operating = FALSE //cooldown var/turf/previousturf = null var/obj/item/weldingtool/weldtool = null var/obj/item/assembly/igniter/igniter = null var/obj/item/tank/phoron/ptank = null + var/volume_per_max_burn = 20 // gets divided by the intended burn ratio +/obj/item/flamethrower/Initialize(mapload) + weldtool = new /obj/item/weldingtool(src) + weldtool.status = 0 // for disassembly + update_icon() + . = ..() + +/obj/item/flamethrower/full/Initialize(mapload) + igniter = new /obj/item/assembly/igniter(src) + igniter.secured = 0 // for disassembly + . = ..() + status = TRUE /obj/item/flamethrower/Destroy() QDEL_NULL(weldtool) @@ -44,7 +59,6 @@ location.hotspot_expose(700, 2) return - /obj/item/flamethrower/update_icon() cut_overlays() if(igniter) @@ -59,26 +73,55 @@ return /obj/item/flamethrower/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - // Make sure our user is still holding us + if(!lit || operating) + return if(user && user.get_active_hand() == src) - var/turf/target_turf = get_turf(target) - if(target_turf) - var/turflist = getline(user, target_turf) - flame_turf(turflist) + if(user.a_intent == I_HELP && user.client?.prefs?.read_preference(/datum/preference/toggle/safefiring)) + to_chat(user, span_warning("You refrain from firing \the [src] as your intent is set to help.")) + return + if(check_fuel()) + // spawn projectile + var/obj/item/projectile/P = new /obj/item/projectile/bullet/dragon/flamethrower(get_turf(src)) + P.submunition_spread_max = 90 + round(80*thrower_spew_percent()) + P.submunition_spread_min = 5 + round(50*thrower_spew_percent()) + P.submunitions = list(/obj/item/projectile/bullet/incendiary/dragonflame/flamethrower = 1 + round(thrower_spew_percent()*2)) + P.launch_projectile( target, BP_TORSO, user) + + // suck out fuel and burn it + var/datum/gas_mixture/used_gas = ptank.air_contents.remove_ratio(volume_per_max_burn * thrower_spew_percent() / ptank.air_contents.volume) + qdel(used_gas) + if(!check_fuel()) + lit = FALSE + update_icon() + else + to_chat(user, span_notice("There is not enough pressure in [src]'s tank!")) + lit = FALSE + update_icon() + // prevent spam + operating = TRUE + addtimer(VARSET_CALLBACK(src, operating, FALSE), 15) + return + +/obj/item/flamethrower/proc/thrower_spew_percent() + return throw_amount / THROWER_MAX + +/obj/item/flamethrower/proc/check_fuel() + return ptank != null && ptank.air_contents.total_moles > 5 // minimum fuel usage is five moles, for EXTREMELY hot mix or super low pressure /obj/item/flamethrower/attackby(obj/item/W as obj, mob/user as mob) - if(user.stat || user.restrained() || user.lying) return + if(user.stat || user.restrained() || user.lying) + return + if(W.has_tool_quality(TOOL_WRENCH) && !status)//Taking this apart var/turf/T = get_turf(src) if(weldtool) - weldtool.loc = T + weldtool.forceMove(T) weldtool = null if(igniter) - igniter.loc = T + igniter.forceMove(T) igniter = null if(ptank) - ptank.loc = T + ptank.forceMove(T) ptank = null new /obj/item/stack/rods(T) qdel(src) @@ -95,7 +138,7 @@ if(I.secured) return if(igniter) return user.drop_item() - I.loc = src + I.forceMove(src) igniter = I update_icon() return @@ -106,99 +149,79 @@ return user.drop_item() ptank = W - W.loc = src + W.forceMove(src) update_icon() return ..() - return - /obj/item/flamethrower/attack_self(mob/user as mob) - if(user.stat || user.restrained() || user.lying) return - user.set_machine(src) - if(!ptank) - to_chat(user, span_notice("Attach a phoron tank first!")) + if(user.stat || user.restrained() || user.lying) return - var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
\n Tank Pressure: [ptank.air_contents.return_pressure()]
\nAmount to throw: - - - [throw_amount] + + +
\nRemove phorontank - Close
") - user << browse(dat, "window=flamethrower;size=600x300") - onclose(user, "flamethrower") - return + tgui_interact(user) +/obj/item/flamethrower/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Flamethrower", name) + ui.open() -/obj/item/flamethrower/Topic(href,href_list[]) - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=flamethrower") - return - if(usr.stat || usr.restrained() || usr.lying) return - usr.set_machine(src) - if(href_list["light"]) - if(!ptank) return - if(ptank.air_contents.gas[GAS_PHORON] < 1) return - if(!status) return - lit = !lit - if(lit) - START_PROCESSING(SSobj, src) - if(href_list["amount"]) - throw_amount = throw_amount + text2num(href_list["amount"]) - throw_amount = max(50, min(5000, throw_amount)) - if(href_list["remove"]) - if(!ptank) return - usr.put_in_hands(ptank) - ptank = null - lit = 0 - usr.unset_machine() - usr << browse(null, "window=flamethrower") - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - update_icon() - return +/obj/item/flamethrower/tgui_data(mob/user) + var/list/dat = list() + dat["lit"] = lit + dat["constructed"] = status + dat["throw_amount"] = throw_amount + // Tank + dat["has_tank"] = !isnull(ptank) + dat["throw_min"] = THROWER_MIN + dat["throw_max"] = THROWER_MAX + dat["fuel_kpa"] = check_fuel() ? ptank.air_contents.return_pressure() : 0 + return dat +/obj/item/flamethrower/tgui_act(action, params, datum/tgui/ui) + if(usr.stat || usr.restrained() || usr.lying) + return FALSE + if(..()) + return FALSE -//Called from turf.dm turf/dblclick -/obj/item/flamethrower/proc/flame_turf(turflist) - if(!lit || operating) return - operating = 1 - for(var/turf/T in turflist) - if(T.density || istype(T, /turf/space)) - break - if(!previousturf && length(turflist)>1) - previousturf = get_turf(src) - continue //so we don't burn the tile we be standin on - if(previousturf && LinkBlocked(previousturf, T)) - break - ignite_turf(T) - sleep(1) - previousturf = null - operating = 0 - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - return + switch(action) + if("light") + if(!check_fuel() || ptank.air_contents.gas[GAS_PHORON] < 1 || !status) + return FALSE + lit = !lit + if(lit) + START_PROCESSING(SSobj, src) + playsound(src, 'sound/items/welderactivate.ogg', 50, 1) + else + playsound(src, 'sound/items/welderdeactivate.ogg', 50, 1) + update_icon() + return TRUE + if("amount") + throw_amount = text2num(params["amount"]) + throw_amount = clamp(throw_amount,THROWER_MIN,THROWER_MAX) + return TRUE + if("remove") + if(!ptank) + return FALSE + usr.put_in_hands(ptank) + ptank = null + lit = 0 + update_icon() + return TRUE +// Projectile +/obj/item/projectile/bullet/dragon/flamethrower + name = "flames" + icon_state = "fireball2" + submunitions = list(/obj/item/projectile/bullet/incendiary/dragonflame/flamethrower = 2) + damage = 0 + hitsound_wall = null -/obj/item/flamethrower/proc/ignite_turf(turf/target) - //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... - //Transfer 5% of current tank air contents to turf - var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) - //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is mischievious and I could probably make it work without fucking it up like this, but there you have it. -- TLE - new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas[GAS_PHORON],get_dir(loc,target)) - air_transfer.gas[GAS_PHORON] = 0 - target.assume_air(air_transfer) - //Burn it based on transfered gas - //target.hotspot_expose(part4.air_contents.temperature*2,300) - target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE - //location.hotspot_expose(1000,500,1) - return +/obj/item/projectile/bullet/incendiary/dragonflame/flamethrower + name = "flames" + icon_state = "fireball2" + damage = 2 + hitsound_wall = null -/obj/item/flamethrower/full/New(var/loc) - ..() - weldtool = new /obj/item/weldingtool(src) - weldtool.status = 0 - igniter = new /obj/item/assembly/igniter(src) - igniter.secured = 0 - status = 1 - update_icon() - return +#undef THROWER_MIN +#undef THROWER_MAX diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index a89a050913..c5a9d315e5 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -85,11 +85,13 @@ if (istype(W, /obj/item/flamethrower)) var/obj/item/flamethrower/F = W - if ((!F.status)||(F.ptank)) return - src.master = F + if ((!F.status)||(F.ptank)) + return + master = F F.ptank = src user.remove_from_mob(src) - src.loc = F + forceMove(F) + F.update_icon() return /obj/item/tank/vox //Can't be a child of phoron or the gas amount gets screwey. diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index c29a03bbef..efd7125385 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -111,32 +111,19 @@ to_chat(user, span_notice("You secure the welder.")) else to_chat(user, span_notice("The welder can now be attached and modified.")) - src.add_fingerprint(user) + add_fingerprint(user) return if((!status) && (istype(W,/obj/item/stack/rods))) var/obj/item/stack/rods/R = W R.use(1) - var/obj/item/flamethrower/F = new/obj/item/flamethrower(user.loc) - src.loc = F + var/obj/item/flamethrower/F = new/obj/item/flamethrower(get_turf(user)) + user.drop_from_inventory(src,F) F.weldtool = src - if (user.client) - user.client.screen -= src - if (user.r_hand == src) - user.remove_from_mob(src) - else - user.remove_from_mob(src) - src.master = F - src.layer = initial(src.layer) - user.remove_from_mob(src) - if (user.client) - user.client.screen -= src - src.loc = F - src.add_fingerprint(user) + add_fingerprint(user) return ..() - return /obj/item/weldingtool/process() if(welding) diff --git a/tgui/packages/tgui/interfaces/Flamethrower/index.tsx b/tgui/packages/tgui/interfaces/Flamethrower/index.tsx new file mode 100644 index 0000000000..ec5ea28dbd --- /dev/null +++ b/tgui/packages/tgui/interfaces/Flamethrower/index.tsx @@ -0,0 +1,70 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Button, Knob, LabeledList } from 'tgui-core/components'; + +import type { Data } from './types'; + +export const Flamethrower = (props) => { + const { act, data } = useBackend(); + const { + lit, + fuel_kpa, + throw_amount, + throw_min, + throw_max, + has_tank, + constructed, + } = data; + + return ( + + + + act('remove')} + > + Remove Tank + + } + > + {!has_tank ? 'No Tank' : fuel_kpa <= 0 ? 'Empty' : fuel_kpa + 'Kpa'} + + act('light')} + style={{ textAlign: 'center' }} + > + {lit && has_tank && fuel_kpa + ? 'Lit' + : constructed + ? 'UnLit' + : 'Insecure'} + + } + > + {!!constructed && ( + act('amount', { amount: val })} + /> + )} + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Flamethrower/types.ts b/tgui/packages/tgui/interfaces/Flamethrower/types.ts new file mode 100644 index 0000000000..1beb7a82c3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Flamethrower/types.ts @@ -0,0 +1,11 @@ +import type { BooleanLike } from 'tgui-core/react'; + +export type Data = { + constructed: BooleanLike; + lit: BooleanLike; + has_tank: BooleanLike; + throw_amount: number; + throw_min: number; + throw_max: number; + fuel_kpa: number; +};