Files
Aurora.3/code/game/objects/structures/bonfire.dm
Batrachophreno 387cee9243 Object Examine Text Overhaul (#20923)
Extends and reworks how various extended information text (desc_info,
desc_build, desc_upgrades) are handled to make object interactions and
mechanics A.) much more clearly documented in-game and B.) much easier
to support from the back-end.

Almost certainly a candidate for test merge.

Assembly/Disassembly instructions are noticeably sporadic, largely due
to our current lack of a unified framework. That's a future thing I'd
like to attack so that it can be handled programmatically, but for now I
only targeted the biggest culprits as I came across them.

---------

Signed-off-by: Batrachophreno <Batrochophreno@gmail.com>
2025-07-21 15:35:14 +00:00

397 lines
12 KiB
Plaintext

#define MAX_ACTIVE_BONFIRE_LIMIT 15
GLOBAL_LIST_EMPTY(total_active_bonfires)
/obj/structure/bonfire
name = "bonfire"
desc = "A large pile of wood, ready to be burned."
icon = 'icons/obj/bonfire.dmi'
icon_state = "bonfire"
anchored = TRUE
density = FALSE
light_color = LIGHT_COLOR_FIRE
build_amt = 20
pass_flags_self = PASSTABLE | LETPASSTHROW
var/fuel = 2000
var/max_fuel = 2000
var/on_fire = FALSE
var/safe = FALSE
var/list/burnable_materials = list(MATERIAL_WOOD = 200, MATERIAL_WOOD_LOG = 400, MATERIAL_WOOD_BRANCH = 40, MATERIAL_COTTON = 20, MATERIAL_CLOTH = 50, MATERIAL_CARPET = 20, MATERIAL_CARDBOARD = 35)
var/list/burnable_other = list(/obj/item/ore/coal = 750, /obj/item/torch = 20) //For items without material/default material
var/heat_range = 5 //Range in which it will heat other people
var/heating_power
var/last_ambient_message
var/burn_out = TRUE //Whether or not it deletes itself when fuel is depleted
/obj/structure/bonfire/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance > 2)
return
if(on_fire)
switch(fuel)
if(0 to 200)
. += "\The [src] is burning weakly."
if(200 to 600)
. += "\The [src] is gently burning."
if(600 to 900)
. += "\The [src] is burning steadily."
if(900 to 1300)
. += "The flames are dancing wildly!"
if(1300 to 2000)
. += "The fire is roaring!"
/obj/structure/bonfire/Initialize()
. = ..()
fuel = rand(1000, 2000)
create_reagents(120)
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/structure/bonfire/Destroy()
STOP_PROCESSING(SSprocessing, src)
GLOB.total_active_bonfires -= src
. = ..()
/obj/structure/bonfire/update_icon()
if(on_fire)
if(fuel < 200)
icon_state = "[initial(icon_state)]_warm"
else
icon_state = "[initial(icon_state)]_fire"
else
icon_state = initial(icon_state)
/obj/structure/bonfire/AltClick(mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
if(use_check_and_message(H))
return
if(fuel >= max(max_fuel * 0.1, 50) && on_fire)
to_chat(H, SPAN_NOTICE("You grab a burning stick from the fire."))
fuel -= 40
var/obj/item/device/flashlight/flare/torch/stick/torch = new(get_turf(user))
H.put_in_active_hand(torch)
/obj/structure/bonfire/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.isFlameSource() && !on_fire) // needs to go last or else nothing else will work
light(user)
return
if(on_fire && (istype(attacking_item, /obj/item/flame) || istype(attacking_item, /obj/item/device/flashlight/flare/torch) || istype(attacking_item, /obj/item/clothing/mask/smokable))) //light unlit stuff
attacking_item.attackby(src, user)
return
if(fuel < max_fuel)
if(istype(attacking_item, /obj/item/stack/material))
var/obj/item/stack/material/I = attacking_item
if(I.default_type in burnable_materials)
I.use(1)
fuel = min(fuel + burnable_materials[I.default_type], max_fuel)
user.visible_message(SPAN_NOTICE("\The [user] adds some of \the [I] to \the [src]."))
return
if(is_type_in_list(attacking_item, burnable_other))
var/fuel_add = burnable_other[attacking_item]
fuel = min(fuel + fuel_add, max_fuel)
user.visible_message(SPAN_NOTICE("\The [user] tosses \the [attacking_item] into \the [src]."))
user.drop_from_inventory(attacking_item)
qdel(attacking_item)
return
else if(istype(attacking_item, /obj/item/material))
var/obj/item/material/M = attacking_item
if(M.material.name in burnable_materials)
var/fuel_add = burnable_materials[M.material] * (M.w_class / 5) //if you crafted a small item, it's not worth as much fuel
fuel = min(fuel + fuel_add, max_fuel)
user.visible_message(SPAN_NOTICE("\The [user] tosses \the [M] into \the [src]."))
user.drop_from_inventory(attacking_item)
attacking_item.forceMove(get_turf(src))
qdel(attacking_item)
return
else
var/obj/item/reagent_containers/RC = attacking_item
if(RC.is_open_container())
RC.reagents.trans_to(src, RC.amount_per_transfer_from_this)
handle_reagents()
/obj/structure/bonfire/proc/light(mob/user)
if(!fuel)
to_chat(user, SPAN_WARNING("There is not enough fuel to start a fire."))
return
if(GLOB.total_active_bonfires.len >= MAX_ACTIVE_BONFIRE_LIMIT)
to_chat(user, SPAN_WARNING("\The [src] refuses to light, despite all your efforts."))
return
if(!on_fire)
on_fire = TRUE
check_light()
update_icon()
START_PROCESSING(SSprocessing, src)
GLOB.total_active_bonfires += src
/obj/structure/bonfire/proc/check_light()
if(on_fire)
switch(fuel)
if(0 to 200)
set_light(2)
if(200 to 600)
set_light(3)
if(600 to 900)
set_light(4)
if(900 to 1300)
set_light(5)
if(1300 to 2000)
set_light(6)
else
set_light(0)
/obj/structure/bonfire/proc/check_heat_range()
if(on_fire)
switch(fuel)
if(0 to 200)
heat_range = 2
if(200 to 600)
heat_range = 3
if(600 to 900)
heat_range = 4
if(900 to 1300)
heat_range = 5
if(1300 to 2000)
heat_range = 6
else
heat_range = 0
return heat_range
/obj/structure/bonfire/proc/handle_reagents()
var/singleton/reagent/R
var/reagent_level
if(reagents.total_volume > 0 && on_fire)
if(reagents.has_reagent(/singleton/reagent/woodpulp))
R = GET_SINGLETON(/singleton/reagent/woodpulp)
reagent_level = reagents.reagent_volumes[R.type]
fuel = min(max_fuel, fuel + reagent_level * 5)
if(reagents.has_reagent(/singleton/reagent/fuel))
R = GET_SINGLETON(/singleton/reagent/fuel)
reagent_level = reagents.reagent_volumes[R.type]
fuel = min(max_fuel, fuel + reagent_level * 10)
if(reagents.has_reagent(/singleton/reagent/water))
R = GET_SINGLETON(/singleton/reagent/water)
reagent_level = reagents.reagent_volumes[R.type]
fuel = max(0, fuel - reagent_level * 10)
if(reagents.has_reagent(/singleton/reagent/toxin/fertilizer/monoammoniumphosphate))
R = GET_SINGLETON(/singleton/reagent/toxin/fertilizer/monoammoniumphosphate)
reagent_level = reagents.reagent_volumes[R.type]
fuel = max(0, fuel - reagent_level * 20)
if(reagents.has_reagent(/singleton/reagent/toxin/phoron))
R = GET_SINGLETON(/singleton/reagent/toxin/phoron)
reagent_level = reagents.reagent_volumes[R.type]
fuel = min(max_fuel, fuel + (reagent_level * 25))
if(reagents.has_reagent(/singleton/reagent/alcohol/ethanol))
R = GET_SINGLETON(/singleton/reagent/alcohol/ethanol)
var/singleton/reagent/alcohol/ethanol/A = R
reagent_level = reagents.reagent_volumes[A.type]
fuel = min(max_fuel, fuel + (reagent_level * (A.strength/20)))
R.remove_self(reagent_level, reagents)
/obj/structure/bonfire/process()
if(!on_fire)
return
handle_reagents()
fuel -= rand(1, 2)
if(fuel <= 0)
extinguish()
playsound()
update_icon()
if(isturf(loc))
var/turf/T = loc
T.hotspot_expose(700, 5)
if(locate(/mob/living, src.loc))
var/mob/living/M = locate(/mob/living, src.loc)
burn(M)
check_light()
heat()
warm_person()
if(prob(2))
ambient_message()
playsound(get_turf(src), 'sound/effects/fireplace.ogg', 30, 1, -3)
/obj/structure/bonfire/proc/extinguish()
on_fire = FALSE
STOP_PROCESSING(SSprocessing, src)
check_light()
update_icon()
GLOB.total_active_bonfires -= src
if(burn_out)
visible_message(SPAN_NOTICE("\The [src] burns out, turning to a pile of ash and burnt wood."))
new /obj/effect/decal/cleanable/ash(get_turf(src))
qdel(src)
/obj/structure/bonfire/proc/heat()
if(!on_fire)
return
heating_power = 32000 * (fuel / max_fuel) //Less fuel, less heat
var/turf/simulated/L = get_turf(src)
if(istype(L))
var/datum/gas_mixture/env = L.return_air()
if(env && abs(env.temperature - (T20C + 5)) > 0.1)
var/transfer_moles = 0.35 * env.total_moles
var/datum/gas_mixture/removed = env.remove(transfer_moles)
if(removed)
var/heat_transfer = removed.get_thermal_energy_change(T20C + 5)
if(heat_transfer > 0) //heating air
heat_transfer = min(heat_transfer, heating_power)
removed.add_thermal_energy(heat_transfer)
env.merge(removed)
//fire_act stuff inside. Mobs handled in burn()
for(var/obj/O in get_turf(src))
if(O == src)
continue
heat_object(O)
/obj/structure/bonfire/proc/heat_object(obj/O)
if(istype(O, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RC = O
var/current_temperature = RC.reagents.get_temperature()
if(current_temperature <= 310)
var/thermal_energy_limit = RC.reagents.get_thermal_energy_change(current_temperature, 310)
RC.reagents.add_thermal_energy(min(1750, thermal_energy_limit))
RC.reagents.handle_reactions()
if(istype(O, /obj/item/stack/material))
var/obj/item/stack/material/I = O
if(I.default_type in burnable_materials)
if(max_fuel - fuel >= burnable_materials[I.default_type])
I.use(1)
fuel += burnable_materials[I.default_type] * 0.75
O.fire_act()
/obj/structure/bonfire/proc/warm_person()
if(!on_fire)
return
heat_range = check_heat_range()
var/turf/simulated/L = get_turf(src)
if(!istype(L))
return
for(var/mob/living/carbon/human/H in oview(src, heat_range))
var/turf/simulated/T = get_turf(H)
var/datum/gas_mixture/mob_env
if(!istype(T))
continue
mob_env = T.return_air()
var/temp_adj = max(min(T20C, mob_env.temperature) - H.bodytemperature, -8)
if(temp_adj > -1) //only heats, not precise
continue
var/distance = get_dist(src, H)
var/heating_div = distance > 2 ? distance - 1 : 1 //Heat drops off if you're 3 or more tiles away
var/heat_eff = fuel / max_fuel //Less fuel, less heat provided
H.bodytemperature = min(H.bodytemperature + (abs((temp_adj * heat_eff)) / heating_div), 311)
/obj/structure/bonfire/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(on_fire)
burn(arrived, TRUE)
/obj/structure/bonfire/proc/burn(var/mob/living/M, var/entered = FALSE)
if(safe)
return
if(istype(M) && prob((fuel / max_fuel) * 100))
if(entered)
to_chat(M, SPAN_WARNING("You are covered by fire and heat from entering \the [src]!"))
if(isanimal(M))
var/mob/living/simple_animal/H = M
if(H.flying) // Flying mobs will ignore the fire.
return
else
M.bodytemperature = min(M.bodytemperature + 150, 1000)
else
if(prob(50))
M.adjust_fire_stacks(5)
M.IgniteMob()
return
/obj/structure/bonfire/proc/ambient_message()
if(on_fire && world.time >= last_ambient_message + 600)
var/list/message_picks = list("The wood crackles in the heat.", "The flames of the fire dance vibrantly.", "Some wood crackles, sending harmless sparks dancing in the air",
"The light of the fire pulses calmly.", "Dozens of sparks dance upward before burning out.")
if(fuel / max_fuel >= 0.5)
message_picks += list("The fire roars steadily.", "The flames flare up briefly, before relaxing once more.", "Light smoke twirls upward before fading away.")
if(fuel / max_fuel <= 0.25)
message_picks += list("The embers shift as a piece of wood falls into them.", "The glow of the fire pulses weakly.", "Ash dances upward with a few sparks.")
var/message = "<I>[pick(message_picks)]</I>"
visible_message(SPAN_NOTICE(message))
last_ambient_message = world.time
/obj/structure/bonfire/light_up/Initialize()
. = ..()
on_fire = TRUE
check_light()
update_icon()
START_PROCESSING(SSprocessing, src)
GLOB.total_active_bonfires += src
/obj/structure/bonfire/fireplace
name = "fireplace"
desc = "A large stone brick fireplace."
icon = 'icons/obj/fireplace.dmi'
icon_state = "fireplace"
pixel_x = -16
safe = TRUE
density = TRUE
burn_out = FALSE
pass_flags_self = PASSTABLE
/obj/structure/bonfire/fireplace/New(var/newloc, var/material_name)
..()
fuel = 0 //don't start with fuel
if(!material_name)
material_name = MATERIAL_MARBLE
material = SSmaterials.get_material_by_name(material_name)
if(!material)
qdel(src)
return
name = "[material.display_name] fireplace"
/obj/structure/bonfire/fireplace/update_icon()
ClearOverlays()
if(on_fire)
switch(fuel)
if(0 to 250)
AddOverlays("fireplace_fire0")
if(251 to 750)
AddOverlays("fireplace_fire1")
if(751 to 1200)
AddOverlays("fireplace_fire2")
if(1201 to 1700)
AddOverlays("fireplace_fire3")
if(1700 to 2000)
AddOverlays("fireplace_fire4")
AddOverlays("fireplace_glow")
/obj/structure/bonfire/fireplace/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(mover?.movement_type & PHASING)
return TRUE
if(get_dir(loc, target) == NORTH)
return !density
return TRUE
/obj/structure/bonfire/fireplace/CheckExit(atom/movable/O, turf/target)
if(get_dir(loc, target) == NORTH)
return !density
return TRUE
#undef MAX_ACTIVE_BONFIRE_LIMIT