mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 19:51:59 +00:00
## About The Pull Request Melee attack chain now has a list passed along with it, `attack_modifiers`, which you can stick force modifiers to change the resulting attack This is basically a soft implementation of damage packets until a more definitive pr, but one that only applies to item attack chain, and not unarmed attacks. This change was done to facilitate a baton refactor - batons no longer hack together their own attack chain, and are now integrated straight into the real attack chain. This refactor itself was done because batons don't send any attack signals, which has been annoying in the past (for swing combat). ## Changelog 🆑 Melbert refactor: Batons have been refactored again. Baton stuns now properly count as an attack, when before it was a nothing. Report any oddities, particularly in regards to harmbatonning vs normal batonning. refactor: The method of adjusting item damage mid-attack has been refactored - some affected items include the Nullblade and knives. Report any strange happenings with damage numbers. refactor: A few objects have been moved to the new interaction chain - records consoles, mawed crucible, alien weeds and space vines, hedges, restaurant portals, and some mobs - to name a few. fix: Spears only deal bonus damage against secure lockers, not all closet types (including crates) /🆑
362 lines
13 KiB
Plaintext
362 lines
13 KiB
Plaintext
#define MAX_RADIUS_REQUIRED 20 //maxcap
|
|
#define MIN_RADIUS_REQUIRED 4 //1, 2, 4
|
|
/// How long the compression test can last before the machine just gives up and ejects the items.
|
|
#define COMPRESSION_TEST_TIME (SSOBJ_DT SECONDS * 5)
|
|
|
|
/**
|
|
* # Explosive compressor machines
|
|
*
|
|
* The explosive compressor machine used in anomaly core production.
|
|
*
|
|
* Uses the standard ordnance/tank explosion scaling to compress raw anomaly cores into completed ones. The required explosion radius increases as more cores of that type are created.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery
|
|
name = "anomaly refinery"
|
|
desc = "An advanced machine capable of implosion-compressing raw anomaly cores into finished artifacts. Also equipped with state of the art bomb prediction software."
|
|
circuit = /obj/item/circuitboard/machine/anomaly_refinery
|
|
icon = 'icons/obj/machines/research.dmi'
|
|
base_icon_state = "explosive_compressor"
|
|
icon_state = "explosive_compressor"
|
|
density = TRUE
|
|
|
|
/// The raw core inserted in the machine.
|
|
var/obj/item/raw_anomaly_core/inserted_core
|
|
/// The TTV inserted in the machine.
|
|
var/obj/item/transfer_valve/inserted_bomb
|
|
/// The timer that lets us timeout the test.
|
|
var/datum/timedevent/timeout_timer
|
|
/// Whether we are currently active a bomb and core.
|
|
var/active = FALSE
|
|
/// The message produced by the explosive compressor at the end of the compression test.
|
|
var/test_status = null
|
|
/// Determines which tank will be the merge_gases target (destroyed upon testing).
|
|
var/obj/item/tank/tank_to_target
|
|
|
|
// These vars are used for the explosion simulation and doesn't affect the core detonation.
|
|
/// Combined result of the first two tanks. Exists only in our machine.
|
|
var/datum/gas_mixture/combined_gasmix
|
|
/// Here for the UI, tracks the amounts of reaction that has occured. 1 means valve opened but not reacted.
|
|
var/reaction_increment = 0
|
|
|
|
/obj/machinery/research/anomaly_refinery/Initialize(mapload)
|
|
. = ..()
|
|
RegisterSignal(src, COMSIG_ATOM_INTERNAL_EXPLOSION, PROC_REF(check_test))
|
|
|
|
/obj/machinery/research/anomaly_refinery/examine_more(mob/user)
|
|
. = ..()
|
|
if (obj_flags & EMAGGED)
|
|
. += span_notice("A small panel on [p_their()] side is dislaying a notice. Something about firmware?")
|
|
|
|
|
|
/obj/machinery/research/anomaly_refinery/assume_air(datum/gas_mixture/giver)
|
|
return null // Required to make the TTV not vent directly into the air.
|
|
|
|
/**
|
|
* Determines how much explosive power (last value, so light impact theoretical radius) is required to make a certain anomaly type.
|
|
*
|
|
* Returns null if the max amount has already been reached.
|
|
*
|
|
* Arguments:
|
|
* * anomaly_type - anomaly type define
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/get_required_radius(anomaly_type)
|
|
if(!SSresearch.is_core_available(anomaly_type))
|
|
return //return null
|
|
|
|
var/already_made = SSresearch.created_anomaly_types[anomaly_type]
|
|
var/hard_limit = SSresearch.anomaly_hard_limit_by_type[anomaly_type]
|
|
|
|
// my crappy autoscale formula
|
|
// linear scaling.
|
|
var/radius_span = MAX_RADIUS_REQUIRED - MIN_RADIUS_REQUIRED
|
|
var/radius_increase_per_core = radius_span / hard_limit
|
|
var/radius = clamp(round(MIN_RADIUS_REQUIRED + radius_increase_per_core * already_made, 1), MIN_RADIUS_REQUIRED, MAX_RADIUS_REQUIRED)
|
|
return radius
|
|
|
|
/obj/machinery/research/anomaly_refinery/attackby(obj/item/tool, mob/living/user, list/modifiers, list/attack_modifiers)
|
|
if(active)
|
|
to_chat(user, span_warning("You can't insert [tool] into [src] while [p_theyre()] currently active."))
|
|
return
|
|
if(istype(tool, /obj/item/raw_anomaly_core))
|
|
if(inserted_core)
|
|
to_chat(user, span_warning("There is already a core in [src]."))
|
|
return
|
|
if(!user.transferItemToLoc(tool, src))
|
|
to_chat(user, span_warning("[tool] is stuck to your hand."))
|
|
return
|
|
var/obj/item/raw_anomaly_core/raw_core = tool
|
|
if(!get_required_radius(raw_core.anomaly_type))
|
|
say("Unfortunately, due to diminishing supplies of condensed anomalous matter, [raw_core] and any cores of its type are no longer of a sufficient quality level to be compressed into a working core.")
|
|
return
|
|
inserted_core = raw_core
|
|
to_chat(user, span_notice("You insert [raw_core] into [src]."))
|
|
return
|
|
if(istype(tool, /obj/item/transfer_valve))
|
|
if(inserted_bomb)
|
|
to_chat(user, span_warning("There is already a bomb in [src]."))
|
|
return
|
|
var/obj/item/transfer_valve/valve = tool
|
|
if(!valve.ready())
|
|
to_chat(user, span_warning("[valve] is incomplete."))
|
|
return
|
|
if(!user.transferItemToLoc(tool, src))
|
|
to_chat(user, span_warning("[tool] is stuck to your hand."))
|
|
return
|
|
inserted_bomb = tool
|
|
tank_to_target = inserted_bomb.tank_two
|
|
to_chat(user, span_notice("You insert [tool] into [src]"))
|
|
return
|
|
update_appearance()
|
|
return ..()
|
|
|
|
/obj/machinery/research/anomaly_refinery/wrench_act(mob/living/user, obj/item/tool)
|
|
. = ..()
|
|
default_unfasten_wrench(user, tool)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/machinery/research/anomaly_refinery/screwdriver_act(mob/living/user, obj/item/tool)
|
|
if(!default_deconstruction_screwdriver(user, "[base_icon_state]-off", "[base_icon_state]", tool))
|
|
return FALSE
|
|
update_appearance()
|
|
return TRUE
|
|
|
|
/obj/machinery/research/anomaly_refinery/crowbar_act(mob/living/user, obj/item/tool)
|
|
if(!default_deconstruction_crowbar(tool))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/research/anomaly_refinery/emag_act(mob/user, obj/item/card/emag/emag_card)
|
|
. = ..()
|
|
if (obj_flags & EMAGGED)
|
|
balloon_alert(user, "already hacked!")
|
|
return
|
|
|
|
obj_flags |= EMAGGED
|
|
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, vary = FALSE)
|
|
say("ERROR: Unauthorized firmware access.")
|
|
return TRUE
|
|
|
|
/**
|
|
* Starts a compression test.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/start_test()
|
|
if (active)
|
|
say("ERROR: Already running a compression test.")
|
|
return
|
|
|
|
if(!istype(inserted_core) || !istype(inserted_bomb))
|
|
end_test("ERROR: Missing equpment. Items ejected.")
|
|
return
|
|
|
|
if(!inserted_bomb?.tank_one || !inserted_bomb?.tank_two || !(tank_to_target == inserted_bomb?.tank_one || tank_to_target == inserted_bomb?.tank_two))
|
|
end_test("ERROR: Transfer valve malfunctioning. Items ejected.")
|
|
return
|
|
|
|
say("Beginning compression test. Opening transfer valve.")
|
|
active = TRUE
|
|
test_status = null
|
|
|
|
if (obj_flags & EMAGGED)
|
|
say("ERROR: An firmware issue was detected while starting a process. Running autopatcher.")
|
|
playsound(src, 'sound/machines/ding.ogg', 50, vary = TRUE)
|
|
addtimer(CALLBACK(src, PROC_REF(error_test)), 2 SECONDS, TIMER_STOPPABLE | TIMER_UNIQUE | TIMER_NO_HASH_WAIT) // Synced with the sound.
|
|
return
|
|
|
|
inserted_bomb.toggle_valve(tank_to_target)
|
|
timeout_timer = addtimer(CALLBACK(src, PROC_REF(timeout_test)), COMPRESSION_TEST_TIME, TIMER_STOPPABLE | TIMER_UNIQUE | TIMER_NO_HASH_WAIT)
|
|
return
|
|
|
|
/**
|
|
* Ejects a live TTV.
|
|
* Triggered by attempting to operate an emagged anomaly refinery.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/error_test()
|
|
message_admins("[src] was emagged and ejected a TTV.")
|
|
investigate_log("was emagged and ejected a TTV.", INVESTIGATE_RESEARCH)
|
|
obj_flags &= ~EMAGGED
|
|
|
|
say("Issue resolved. Have a nice day!")
|
|
inserted_bomb.toggle_valve(tank_to_target)
|
|
eject_bomb(force = TRUE)
|
|
timeout_timer = addtimer(CALLBACK(src, PROC_REF(timeout_test)), COMPRESSION_TEST_TIME, TIMER_STOPPABLE | TIMER_UNIQUE | TIMER_NO_HASH_WAIT) // Actually start the test so they can't just put the bomb back in.
|
|
|
|
/**
|
|
* Ends a compression test.
|
|
*
|
|
* Arguments:
|
|
* - message: A message for the compressor to say when the test ends.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/end_test(message)
|
|
active = FALSE
|
|
tank_to_target = null
|
|
test_status = null
|
|
if(inserted_core)
|
|
eject_core()
|
|
if(inserted_bomb)
|
|
eject_bomb()
|
|
if(timeout_timer)
|
|
QDEL_NULL(timeout_timer)
|
|
if(message)
|
|
say(message)
|
|
return
|
|
|
|
/**
|
|
* Checks whether an internal explosion was sufficient to compress the core.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/check_test(atom/source, list/arguments)
|
|
SIGNAL_HANDLER
|
|
if(!inserted_core)
|
|
test_status = "ERROR: No core present during detonation."
|
|
return COMSIG_CANCEL_EXPLOSION
|
|
|
|
var/heavy = arguments[EXARG_KEY_DEV_RANGE]
|
|
var/medium = arguments[EXARG_KEY_HEAVY_RANGE]
|
|
var/light = arguments[EXARG_KEY_LIGHT_RANGE]
|
|
var/explosion_range = max(heavy, medium, light, 0)
|
|
var/required_range = get_required_radius(inserted_core.anomaly_type)
|
|
var/turf/location = get_turf(src)
|
|
|
|
var/cap_multiplier = SSmapping.level_trait(location.z, ZTRAIT_BOMBCAP_MULTIPLIER)
|
|
if(isnull(cap_multiplier))
|
|
cap_multiplier = 1
|
|
var/capped_heavy = min(GLOB.MAX_EX_DEVESTATION_RANGE * cap_multiplier, heavy)
|
|
var/capped_medium = min(GLOB.MAX_EX_HEAVY_RANGE * cap_multiplier, medium)
|
|
SSexplosions.shake_the_room(location, explosion_range, (capped_heavy * 15) + (capped_medium * 20), capped_heavy, capped_medium)
|
|
|
|
if(explosion_range < required_range)
|
|
test_status = "Resultant detonation failed to produce enough implosive power to compress [inserted_core]. Items ejected."
|
|
return COMSIG_CANCEL_EXPLOSION
|
|
|
|
if(test_status)
|
|
return COMSIG_CANCEL_EXPLOSION
|
|
inserted_core = inserted_core.create_core(src, TRUE, TRUE)
|
|
test_status = "Success. Resultant detonation has theoretical range of [explosion_range]. Required radius was [required_range]. Core production complete."
|
|
return COMSIG_CANCEL_EXPLOSION
|
|
|
|
/**
|
|
* Handles timing out the test after a while.
|
|
*/
|
|
/obj/machinery/research/anomaly_refinery/proc/timeout_test()
|
|
timeout_timer = null
|
|
if(!test_status)
|
|
test_status = "Transfer valve resulted in negligible explosive power. Items ejected."
|
|
end_test(test_status)
|
|
|
|
/// This is not the real valve opening process. This is the simulated one used for displaying reactions.
|
|
/obj/machinery/research/anomaly_refinery/proc/simulate_valve()
|
|
if(!inserted_bomb?.tank_one || !inserted_bomb?.tank_two)
|
|
eject_bomb()
|
|
return FALSE
|
|
|
|
if(reaction_increment == 0)
|
|
var/datum/gas_mixture/first_gasmix = inserted_bomb.tank_one.return_air()
|
|
var/datum/gas_mixture/second_gasmix = inserted_bomb.tank_two.return_air()
|
|
|
|
combined_gasmix = new(70)
|
|
combined_gasmix.volume = first_gasmix.volume + second_gasmix.volume
|
|
combined_gasmix.merge(first_gasmix.copy())
|
|
combined_gasmix.merge(second_gasmix.copy())
|
|
else
|
|
combined_gasmix.react()
|
|
|
|
reaction_increment += 1
|
|
|
|
/// We dont allow incomplete valves to go in but do code in checks for incomplete valves. Just in case.
|
|
/obj/machinery/research/anomaly_refinery/proc/eject_bomb(mob/user, force = FALSE)
|
|
if(!inserted_bomb || (active && !force))
|
|
return
|
|
if(user)
|
|
user.put_in_hands(inserted_bomb)
|
|
to_chat(user, span_notice("You remove [inserted_bomb] from [src]."))
|
|
else
|
|
inserted_bomb.forceMove(drop_location())
|
|
combined_gasmix = null
|
|
reaction_increment = 0
|
|
|
|
/obj/machinery/research/anomaly_refinery/proc/eject_core(mob/user)
|
|
if(!inserted_core || active)
|
|
return
|
|
if(user)
|
|
user.put_in_hands(inserted_core)
|
|
to_chat(user, span_notice("You remove [inserted_core] from [src]."))
|
|
else
|
|
inserted_core.forceMove(drop_location())
|
|
|
|
/// We rely on exited to clear references.
|
|
/obj/machinery/research/anomaly_refinery/Exited(atom/movable/gone, direction)
|
|
if(gone == inserted_bomb)
|
|
inserted_bomb = null
|
|
tank_to_target = null
|
|
if(gone == inserted_core)
|
|
inserted_core = null
|
|
. = ..()
|
|
|
|
/obj/machinery/research/anomaly_refinery/proc/swap_target()
|
|
if(!inserted_bomb?.tank_one || !inserted_bomb?.tank_two)
|
|
eject_bomb()
|
|
return FALSE
|
|
tank_to_target = (tank_to_target == inserted_bomb.tank_one) ? inserted_bomb.tank_two : inserted_bomb.tank_one
|
|
|
|
/obj/machinery/research/anomaly_refinery/on_deconstruction(disassembled)
|
|
eject_bomb()
|
|
eject_core()
|
|
return ..()
|
|
|
|
/obj/machinery/research/anomaly_refinery/Destroy()
|
|
inserted_bomb = null
|
|
inserted_core = null
|
|
combined_gasmix = null
|
|
return ..()
|
|
|
|
/obj/machinery/research/anomaly_refinery/ui_interact(mob/user, datum/tgui/ui)
|
|
. = ..()
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "AnomalyRefinery")
|
|
ui.open()
|
|
|
|
/obj/machinery/research/anomaly_refinery/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if (.)
|
|
return
|
|
switch(action)
|
|
if("react")
|
|
simulate_valve()
|
|
if("eject_bomb")
|
|
eject_bomb(usr)
|
|
if("eject_core")
|
|
eject_core(usr)
|
|
if("start_implosion")
|
|
start_test()
|
|
if("swap")
|
|
swap_target()
|
|
|
|
/obj/machinery/research/anomaly_refinery/ui_data(mob/user)
|
|
var/list/data = list()
|
|
var/list/parsed_gasmixes = list()
|
|
var/obj/item/tank/other_tank
|
|
|
|
if(inserted_bomb?.tank_one && inserted_bomb?.tank_two)
|
|
other_tank = inserted_bomb.tank_one == tank_to_target ? inserted_bomb.tank_two : inserted_bomb.tank_one
|
|
|
|
parsed_gasmixes += list(gas_mixture_parser(tank_to_target?.return_air(), tank_to_target?.name))
|
|
parsed_gasmixes += list(gas_mixture_parser(other_tank?.return_air(), other_tank?.name))
|
|
parsed_gasmixes += list(gas_mixture_parser(combined_gasmix, "Combined Gasmix"))
|
|
|
|
data["gasList"] = parsed_gasmixes
|
|
|
|
data["valvePresent"] = inserted_bomb ? TRUE : FALSE
|
|
data["valveReady"] = (inserted_bomb?.tank_one && inserted_bomb?.tank_two) ? TRUE : FALSE
|
|
data["reactionIncrement"] = reaction_increment
|
|
|
|
data["core"] = inserted_core ? inserted_core.name : FALSE
|
|
data["requiredRadius"] = inserted_core ? get_required_radius(inserted_core.anomaly_type) : null
|
|
|
|
data["active"] = active
|
|
|
|
return data
|
|
|
|
#undef MAX_RADIUS_REQUIRED
|
|
#undef MIN_RADIUS_REQUIRED
|
|
#undef COMPRESSION_TEST_TIME
|