mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-21 14:34:49 +00:00
About The Pull Request
This PR removes intents and replaces them with a combat mode. An explanation of what this means can be found below
Major changes:
Disarm and Grab intents have been removed.
Harm/Help is now combat mode, toggled by F or 4 by default
The context/verb/popup menu now only works when you do shift+right-click
Right click is now disarm, both in and out of combat mode.
Grabbing is now on ctrl-click.
If you're in combat mode, and are currently grabbing/pulling someone, and ctrl-click somewhere else, it will not release the grab (To prevent misclicks)
Minor interaction changes:
Right click to dissasemble tables, racks, filing cabinets (When holding the right tool to do so)
Left click to stunbaton, right click to harmbaton
Right click to tip cows
Right click to malpractice surgery
Right click to hold people at gunpoint (if youre holding a gun)
Why It's Good For The Game
Intents heavily cripple both the code and the UI design of interactions. While I understand that a lot of people will dislike this PR as they are used to intents, they are one of our weakest links in terms of explaining to players how to do specific things, and require a lot more keypresses to do compared to this.
As an example, martial arts can now be done without having to juggle 1 2 3 and 4 to switch intents quickly.
As some of you who saw the first combat mode PR, the context menu used to be disabled in combat mode. In this version it is instead on shift-right click ensuring that you can always use it in the same way.
In this version, combat mode also no longer prevents you from attacking with items when you would so before, as this was something that was commonly complained about.
The full intention of this shift in control scheme is that right click will become "secondary interaction" for items, which prevents some of the awkward juggling we have now with item modes etcetera.
Changelog
cl Qustinnus
add: Intents have been replaced with a combat mode. For more info find the PR here: #56601
/cl
690 lines
31 KiB
Plaintext
690 lines
31 KiB
Plaintext
//this is designed to replace the destructive analyzer
|
|
|
|
//NEEDS MAJOR CODE CLEANUP
|
|
|
|
#define SCANTYPE_POKE 1
|
|
#define SCANTYPE_IRRADIATE 2
|
|
#define SCANTYPE_GAS 3
|
|
#define SCANTYPE_HEAT 4
|
|
#define SCANTYPE_COLD 5
|
|
#define SCANTYPE_OBLITERATE 6
|
|
#define SCANTYPE_DISCOVER 7
|
|
|
|
#define EFFECT_PROB_VERYLOW 20
|
|
#define EFFECT_PROB_LOW 35
|
|
#define EFFECT_PROB_MEDIUM 50
|
|
#define EFFECT_PROB_HIGH 75
|
|
#define EFFECT_PROB_VERYHIGH 95
|
|
|
|
#define FAIL 8
|
|
/obj/machinery/rnd/experimentor
|
|
name = "\improper E.X.P.E.R.I-MENTOR"
|
|
desc = "A \"replacement\" for the destructive analyzer with a slight tendency to catastrophically fail."
|
|
icon = 'icons/obj/machines/heavy_lathe.dmi'
|
|
icon_state = "h_lathe"
|
|
density = TRUE
|
|
use_power = IDLE_POWER_USE
|
|
circuit = /obj/item/circuitboard/machine/experimentor
|
|
var/recentlyExperimented = 0
|
|
var/mob/trackedIan
|
|
var/mob/trackedRuntime
|
|
var/badThingCoeff = 0
|
|
var/resetTime = 15
|
|
var/cloneMode = FALSE
|
|
var/list/item_reactions = list()
|
|
var/list/valid_items = list() //valid items for special reactions like transforming
|
|
var/list/critical_items_typecache //items that can cause critical reactions
|
|
var/banned_typecache // items that won't be produced
|
|
|
|
/obj/machinery/rnd/experimentor/proc/ConvertReqString2List(list/source_list)
|
|
var/list/temp_list = params2list(source_list)
|
|
for(var/O in temp_list)
|
|
temp_list[O] = text2num(temp_list[O])
|
|
return temp_list
|
|
|
|
|
|
/obj/machinery/rnd/experimentor/proc/SetTypeReactions()
|
|
// Don't need to keep this typecache around, only used in this proc once.
|
|
var/list/banned_typecache = typecacheof(list(
|
|
/obj/item/stock_parts/cell/infinite,
|
|
/obj/item/grenade/chem_grenade/tuberculosis
|
|
))
|
|
|
|
for(var/I in typesof(/obj/item))
|
|
if(ispath(I, /obj/item/relic))
|
|
item_reactions["[I]"] = SCANTYPE_DISCOVER
|
|
else
|
|
item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
|
|
|
|
if(is_type_in_typecache(I, banned_typecache))
|
|
continue
|
|
|
|
if(ispath(I, /obj/item/stock_parts) || ispath(I, /obj/item/grenade/chem_grenade) || ispath(I, /obj/item/kitchen))
|
|
var/obj/item/tempCheck = I
|
|
if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
|
|
valid_items["[I]"] += 15
|
|
|
|
if(ispath(I, /obj/item/reagent_containers/food))
|
|
var/obj/item/tempCheck = I
|
|
if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
|
|
valid_items["[I]"] += rand(1,4)
|
|
|
|
/obj/machinery/rnd/experimentor/Initialize()
|
|
. = ..()
|
|
|
|
trackedIan = locate(/mob/living/simple_animal/pet/dog/corgi/ian) in GLOB.mob_living_list
|
|
trackedRuntime = locate(/mob/living/simple_animal/pet/cat/runtime) in GLOB.mob_living_list
|
|
SetTypeReactions()
|
|
|
|
critical_items_typecache = typecacheof(list(
|
|
/obj/item/construction/rcd,
|
|
/obj/item/grenade,
|
|
/obj/item/aicard,
|
|
/obj/item/storage/backpack/holding,
|
|
/obj/item/slime_extract,
|
|
/obj/item/onetankbomb,
|
|
/obj/item/transfer_valve))
|
|
|
|
/obj/machinery/rnd/experimentor/RefreshParts()
|
|
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
|
if(resetTime > 0 && (resetTime - M.rating) >= 1)
|
|
resetTime -= M.rating
|
|
for(var/obj/item/stock_parts/scanning_module/M in component_parts)
|
|
badThingCoeff += M.rating*2
|
|
for(var/obj/item/stock_parts/micro_laser/M in component_parts)
|
|
badThingCoeff += M.rating
|
|
|
|
/obj/machinery/rnd/experimentor/examine(mob/user)
|
|
. = ..()
|
|
if(in_range(user, src) || isobserver(user))
|
|
. += "<span class='notice'>The status display reads: Malfunction probability reduced by <b>[badThingCoeff]%</b>.<br>Cooldown interval between experiments at <b>[resetTime*0.1]</b> seconds.</span>"
|
|
|
|
/obj/machinery/rnd/experimentor/proc/checkCircumstances(obj/item/O)
|
|
//snowflake check to only take "made" bombs
|
|
if(istype(O, /obj/item/transfer_valve))
|
|
var/obj/item/transfer_valve/T = O
|
|
if(!T.tank_one || !T.tank_two || !T.attached_device)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/rnd/experimentor/Insert_Item(obj/item/O, mob/living/user)
|
|
if(!user.combat_mode)
|
|
. = 1
|
|
if(!is_insertion_ready(user))
|
|
return
|
|
if(!user.transferItemToLoc(O, src))
|
|
return
|
|
loaded_item = O
|
|
to_chat(user, "<span class='notice'>You add [O] to the machine.</span>")
|
|
flick("h_lathe_load", src)
|
|
|
|
/obj/machinery/rnd/experimentor/default_deconstruction_crowbar(obj/item/O)
|
|
ejectItem()
|
|
. = ..(O)
|
|
|
|
/obj/machinery/rnd/experimentor/ui_interact(mob/user)
|
|
var/list/dat = list("<center>")
|
|
if(loaded_item)
|
|
dat += "<b>Loaded Item:</b> [loaded_item]"
|
|
|
|
dat += "<div>Available tests:"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_POKE]'>Poke</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_IRRADIATE];'>Irradiate</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_GAS]'>Gas</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_HEAT]'>Burn</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_COLD]'>Freeze</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_OBLITERATE]'>Destroy</A></b></div>"
|
|
if(istype(loaded_item,/obj/item/relic))
|
|
dat += "<b><a href='byond://?src=[REF(src)];item=[REF(loaded_item)];function=[SCANTYPE_DISCOVER]'>Discover</A></b>"
|
|
dat += "<b><a href='byond://?src=[REF(src)];function=eject'>Eject</A>"
|
|
var/list/listin = techweb_item_boost_check(src)
|
|
if(listin)
|
|
var/list/output = list("<b><font color='purple'>Research Boost Data:</font></b>")
|
|
var/list/res = list("<b><font color='blue'>Already researched:</font></b>")
|
|
var/list/boosted = list("<b><font color='red'>Already boosted:</font></b>")
|
|
for(var/node_id in listin)
|
|
var/datum/techweb_node/N = SSresearch.techweb_node_by_id(node_id)
|
|
var/str = "<b>[N.display_name]</b>: [listin[N]] points.</b>"
|
|
if(SSresearch.science_tech.researched_nodes[N.id])
|
|
res += str
|
|
else if(SSresearch.science_tech.boosted_nodes[N.id])
|
|
boosted += str
|
|
if(SSresearch.science_tech.visible_nodes[N.id]) //JOY OF DISCOVERY!
|
|
output += str
|
|
output += boosted + res
|
|
dat += output
|
|
else
|
|
dat += "<b>Nothing loaded.</b>"
|
|
dat += "<a href='byond://?src=[REF(src)];function=refresh'>Refresh</A>"
|
|
dat += "<a href='byond://?src=[REF(src)];close=1'>Close</A></center>"
|
|
var/datum/browser/popup = new(user, "experimentor","Experimentor", 700, 400, src)
|
|
popup.set_content(dat.Join("<br>"))
|
|
popup.open()
|
|
onclose(user, "experimentor")
|
|
|
|
/obj/machinery/rnd/experimentor/Topic(href, href_list)
|
|
if(..())
|
|
return
|
|
usr.set_machine(src)
|
|
|
|
var/scantype = href_list["function"]
|
|
var/obj/item/process = locate(href_list["item"]) in src
|
|
|
|
if(href_list["close"])
|
|
usr << browse(null, "window=experimentor")
|
|
return
|
|
else if(scantype == "eject")
|
|
ejectItem()
|
|
else if(scantype == "refresh")
|
|
updateUsrDialog()
|
|
else
|
|
if(recentlyExperimented)
|
|
to_chat(usr, "<span class='warning'>[src] has been used too recently!</span>")
|
|
else if(!loaded_item)
|
|
to_chat(usr, "<span class='warning'>[src] is not currently loaded!</span>")
|
|
else if(!process || process != loaded_item) //Interface exploit protection (such as hrefs or swapping items with interface set to old item)
|
|
to_chat(usr, "<span class='danger'>Interface failure detected in [src]. Please try again.</span>")
|
|
else
|
|
var/dotype
|
|
if(text2num(scantype) == SCANTYPE_DISCOVER)
|
|
dotype = SCANTYPE_DISCOVER
|
|
else
|
|
dotype = matchReaction(process,scantype)
|
|
experiment(dotype,process)
|
|
use_power(750)
|
|
if(dotype != FAIL)
|
|
var/list/nodes = techweb_item_boost_check(process)
|
|
var/picked = pickweight(nodes) //This should work.
|
|
stored_research.boost_with_path(SSresearch.techweb_node_by_id(picked), process.type)
|
|
updateUsrDialog()
|
|
|
|
/obj/machinery/rnd/experimentor/proc/matchReaction(matching,reaction)
|
|
var/obj/item/D = matching
|
|
if(D)
|
|
if(item_reactions.Find("[D.type]"))
|
|
var/tor = item_reactions["[D.type]"]
|
|
if(tor == text2num(reaction))
|
|
return tor
|
|
else
|
|
return FAIL
|
|
else
|
|
return FAIL
|
|
else
|
|
return FAIL
|
|
|
|
/obj/machinery/rnd/experimentor/proc/ejectItem(delete=FALSE)
|
|
if(loaded_item)
|
|
if(cloneMode)
|
|
visible_message("<span class='notice'>A duplicate [loaded_item] pops out!</span>")
|
|
var/type_to_make = loaded_item.type
|
|
new type_to_make(get_turf(pick(oview(1,src))))
|
|
cloneMode = FALSE
|
|
return
|
|
var/turf/dropturf = get_turf(pick(view(1,src)))
|
|
if(!dropturf) //Failsafe to prevent the object being lost in the void forever.
|
|
dropturf = drop_location()
|
|
loaded_item.forceMove(dropturf)
|
|
if(delete)
|
|
qdel(loaded_item)
|
|
loaded_item = null
|
|
|
|
/obj/machinery/rnd/experimentor/proc/throwSmoke(turf/where)
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(0, where)
|
|
smoke.start()
|
|
|
|
|
|
/obj/machinery/rnd/experimentor/proc/experiment(exp,obj/item/exp_on)
|
|
recentlyExperimented = 1
|
|
icon_state = "h_lathe_wloop"
|
|
var/chosenchem
|
|
var/criticalReaction = is_type_in_typecache(exp_on, critical_items_typecache)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_POKE)
|
|
visible_message("<span class='notice'>[src] prods at [exp_on] with mechanical arms.</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='notice'>[exp_on] is gripped in just the right way, enhancing its focus.</span>")
|
|
badThingCoeff++
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] malfunctions and destroys [exp_on], lashing its arms out at nearby people!</span>")
|
|
for(var/mob/living/m in oview(1, src))
|
|
m.apply_damage(15, BRUTE, pick(BODY_ZONE_HEAD,BODY_ZONE_CHEST,BODY_ZONE_PRECISE_GROIN))
|
|
investigate_log("Experimentor dealt minor brute to [m].", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions!</span>")
|
|
exp = SCANTYPE_OBLITERATE
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] malfunctions, throwing the [exp_on]!</span>")
|
|
var/mob/living/target = locate(/mob/living) in oview(7,src)
|
|
if(target)
|
|
var/obj/item/throwing = loaded_item
|
|
investigate_log("Experimentor has thrown [loaded_item] at [key_name(target)]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
if(throwing)
|
|
throwing.throw_at(target, 10, 1)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_IRRADIATE)
|
|
visible_message("<span class='danger'>[src] reflects radioactive rays at [exp_on]!</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='notice'>[exp_on] has activated an unknown subroutine!</span>")
|
|
cloneMode = TRUE
|
|
investigate_log("Experimentor has made a clone of [exp_on]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] malfunctions, melting [exp_on] and leaking radiation!</span>")
|
|
radiation_pulse(src, 500)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, spewing toxic waste!</span>")
|
|
for(var/turf/T in oview(1, src))
|
|
if(!T.density)
|
|
if(prob(EFFECT_PROB_VERYHIGH) && !(locate(/obj/effect/decal/cleanable/greenglow) in T))
|
|
var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
|
|
reagentdecal.reagents.add_reagent(/datum/reagent/uranium/radium, 7)
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
var/savedName = "[exp_on]"
|
|
ejectItem(TRUE)
|
|
var/newPath = text2path(pickweight(valid_items))
|
|
loaded_item = new newPath(src)
|
|
visible_message("<span class='warning'>[src] malfunctions, transforming [savedName] into [loaded_item]!</span>")
|
|
investigate_log("Experimentor has transformed [savedName] into [loaded_item]", INVESTIGATE_EXPERIMENTOR)
|
|
if(istype(loaded_item, /obj/item/grenade/chem_grenade))
|
|
var/obj/item/grenade/chem_grenade/CG = loaded_item
|
|
CG.detonate()
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_GAS)
|
|
visible_message("<span class='warning'>[src] fills its chamber with gas, [exp_on] included.</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='notice'>[exp_on] achieves the perfect mix!</span>")
|
|
new /obj/item/stack/sheet/mineral/plasma(get_turf(pick(oview(1,src))))
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] destroys [exp_on], leaking dangerous gas!</span>")
|
|
chosenchem = pick(/datum/reagent/carbon,/datum/reagent/uranium/radium,/datum/reagent/toxin,/datum/reagent/consumable/condensedcapsaicin,/datum/reagent/drug/mushroomhallucinogen,/datum/reagent/drug/space_drugs,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/ethanol/beepsky_smash)
|
|
var/datum/reagents/R = new/datum/reagents(50)
|
|
R.my_atom = src
|
|
R.add_reagent(chosenchem , 50)
|
|
investigate_log("Experimentor has released [chosenchem] smoke.", INVESTIGATE_EXPERIMENTOR)
|
|
var/datum/effect_system/smoke_spread/chem/smoke = new
|
|
smoke.set_up(R, 0, src, silent = TRUE)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
smoke.start()
|
|
qdel(R)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src]'s chemical chamber has sprung a leak!</span>")
|
|
chosenchem = pick(/datum/reagent/mutationtoxin/classic,/datum/reagent/nanomachines,/datum/reagent/toxin/acid)
|
|
var/datum/reagents/R = new/datum/reagents(50)
|
|
R.my_atom = src
|
|
R.add_reagent(chosenchem , 50)
|
|
var/datum/effect_system/smoke_spread/chem/smoke = new
|
|
smoke.set_up(R, 0, src, silent = TRUE)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
smoke.start()
|
|
qdel(R)
|
|
ejectItem(TRUE)
|
|
warn_admins(usr, "[chosenchem] smoke")
|
|
investigate_log("Experimentor has released <font color='red'>[chosenchem]</font> smoke!", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, spewing harmless gas.</span>")
|
|
throwSmoke(loc)
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] melts [exp_on], ionizing the air around it!</span>")
|
|
empulse(loc, 4, 6)
|
|
investigate_log("Experimentor has generated an Electromagnetic Pulse.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_HEAT)
|
|
visible_message("<span class='notice'>[src] raises [exp_on]'s temperature.</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='warning'>[src]'s emergency coolant system gives off a small ding!</span>")
|
|
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
|
|
var/obj/item/reagent_containers/food/drinks/coffee/C = new /obj/item/reagent_containers/food/drinks/coffee(get_turf(pick(oview(1,src))))
|
|
chosenchem = pick(/datum/reagent/toxin/plasma,/datum/reagent/consumable/capsaicin,/datum/reagent/consumable/ethanol)
|
|
C.reagents.remove_any(25)
|
|
C.reagents.add_reagent(chosenchem , 50)
|
|
C.name = "Cup of Suspicious Liquid"
|
|
C.desc = "It has a large hazard symbol printed on the side in fading ink."
|
|
investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
var/turf/start = get_turf(src)
|
|
var/mob/M = locate(/mob/living) in view(src, 3)
|
|
var/turf/MT = get_turf(M)
|
|
if(MT)
|
|
visible_message("<span class='danger'>[src] dangerously overheats, launching a flaming fuel orb!</span>")
|
|
investigate_log("Experimentor has launched a <font color='red'>fireball</font> at [M]!", INVESTIGATE_EXPERIMENTOR)
|
|
var/obj/projectile/magic/aoe/fireball/FB = new /obj/projectile/magic/aoe/fireball(start)
|
|
FB.preparePixelProjectile(MT, start)
|
|
FB.fire()
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] malfunctions, melting [exp_on] and releasing a burst of flame!</span>")
|
|
explosion(loc, -1, 0, 0, 0, 0, flame_range = 2)
|
|
investigate_log("Experimentor started a fire.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, melting [exp_on] and leaking hot air!</span>")
|
|
var/datum/gas_mixture/env = loc.return_air()
|
|
var/transfer_moles = 0.25 * env.total_moles()
|
|
var/datum/gas_mixture/removed = env.remove(transfer_moles)
|
|
if(removed)
|
|
var/heat_capacity = removed.heat_capacity()
|
|
if(heat_capacity == 0 || heat_capacity == null)
|
|
heat_capacity = 1
|
|
removed.temperature = min((removed.temperature*heat_capacity + 100000)/heat_capacity, 1000)
|
|
env.merge(removed)
|
|
air_update_turf(FALSE, FALSE)
|
|
investigate_log("Experimentor has released hot air.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, activating its emergency coolant systems!</span>")
|
|
throwSmoke(loc)
|
|
for(var/mob/living/m in oview(1, src))
|
|
m.apply_damage(5, BURN, pick(BODY_ZONE_HEAD,BODY_ZONE_CHEST,BODY_ZONE_PRECISE_GROIN))
|
|
investigate_log("Experimentor has dealt minor burn damage to [key_name(m)]", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_COLD)
|
|
visible_message("<span class='notice'>[src] lowers [exp_on]'s temperature.</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='warning'>[src]'s emergency coolant system gives off a small ding!</span>")
|
|
var/obj/item/reagent_containers/food/drinks/coffee/C = new /obj/item/reagent_containers/food/drinks/coffee(get_turf(pick(oview(1,src))))
|
|
playsound(src, 'sound/machines/ding.ogg', 50, TRUE) //Ding! Your death coffee is ready!
|
|
chosenchem = pick(/datum/reagent/uranium,/datum/reagent/consumable/frostoil,/datum/reagent/medicine/ephedrine)
|
|
C.reagents.remove_any(25)
|
|
C.reagents.add_reagent(chosenchem , 50)
|
|
C.name = "Cup of Suspicious Liquid"
|
|
C.desc = "It has a large hazard symbol printed on the side in fading ink."
|
|
investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR)
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src] malfunctions, shattering [exp_on] and releasing a dangerous cloud of coolant!</span>")
|
|
var/datum/reagents/R = new/datum/reagents(50)
|
|
R.my_atom = src
|
|
R.add_reagent(/datum/reagent/consumable/frostoil , 50)
|
|
investigate_log("Experimentor has released frostoil gas.", INVESTIGATE_EXPERIMENTOR)
|
|
var/datum/effect_system/smoke_spread/chem/smoke = new
|
|
smoke.set_up(R, 0, src, silent = TRUE)
|
|
playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3)
|
|
smoke.start()
|
|
qdel(R)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, shattering [exp_on] and leaking cold air!</span>")
|
|
var/datum/gas_mixture/env = loc.return_air()
|
|
var/transfer_moles = 0.25 * env.total_moles()
|
|
var/datum/gas_mixture/removed = env.remove(transfer_moles)
|
|
if(removed)
|
|
var/heat_capacity = removed.heat_capacity()
|
|
if(heat_capacity == 0 || heat_capacity == null)
|
|
heat_capacity = 1
|
|
removed.temperature = (removed.temperature*heat_capacity - 75000)/heat_capacity
|
|
env.merge(removed)
|
|
air_update_turf(FALSE, FALSE)
|
|
investigate_log("Experimentor has released cold air.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
|
visible_message("<span class='warning'>[src] malfunctions, releasing a flurry of chilly air as [exp_on] pops out!</span>")
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(0, loc)
|
|
smoke.start()
|
|
ejectItem()
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == SCANTYPE_OBLITERATE)
|
|
visible_message("<span class='warning'>[exp_on] activates the crushing mechanism, [exp_on] is destroyed!</span>")
|
|
if(prob(EFFECT_PROB_LOW) && criticalReaction)
|
|
visible_message("<span class='warning'>[src]'s crushing mechanism slowly and smoothly descends, flattening the [exp_on]!</span>")
|
|
new /obj/item/stack/sheet/plasteel(get_turf(pick(oview(1,src))))
|
|
else if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src]'s crusher goes way too many levels too high, crushing right through space-time!</span>")
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE, -3)
|
|
investigate_log("Experimentor has triggered the 'throw things' reaction.", INVESTIGATE_EXPERIMENTOR)
|
|
for(var/atom/movable/AM in oview(7,src))
|
|
if(!AM.anchored)
|
|
AM.throw_at(src,10,1)
|
|
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
|
|
visible_message("<span class='danger'>[src]'s crusher goes one level too high, crushing right into space-time!</span>")
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE, -3)
|
|
investigate_log("Experimentor has triggered the 'minor throw things' reaction.", INVESTIGATE_EXPERIMENTOR)
|
|
var/list/throwAt = list()
|
|
for(var/atom/movable/AM in oview(7,src))
|
|
if(!AM.anchored)
|
|
throwAt.Add(AM)
|
|
for(var/counter = 1, counter < throwAt.len, ++counter)
|
|
var/atom/movable/cast = throwAt[counter]
|
|
cast.throw_at(pick(throwAt),10,1)
|
|
ejectItem(TRUE)
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
if(exp == FAIL)
|
|
var/a = pick("rumbles","shakes","vibrates","shudders","honks")
|
|
var/b = pick("crushes","spins","viscerates","smashes","insults")
|
|
visible_message("<span class='warning'>[exp_on] [a], and [b], the experiment was a failure.</span>")
|
|
|
|
if(exp == SCANTYPE_DISCOVER)
|
|
visible_message("<span class='notice'>[src] scans the [exp_on], revealing its true nature!</span>")
|
|
playsound(src, 'sound/effects/supermatter.ogg', 50, 3, -1)
|
|
var/obj/item/relic/R = loaded_item
|
|
if (!R.revealed)
|
|
var/points = rand(3500,3750) // discovery reward
|
|
new /obj/item/research_notes(drop_location(src), points, "experimentation")
|
|
visible_message("<span class='notice'> This discovery netted [points] points for research.</span>")
|
|
R.reveal()
|
|
investigate_log("Experimentor has revealed a relic with <span class='danger'>[R.realProc]</span> effect.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem()
|
|
|
|
//Global reactions
|
|
if(prob(EFFECT_PROB_VERYLOW-badThingCoeff) && loaded_item)
|
|
var/globalMalf = rand(1,100)
|
|
if(globalMalf < 15)
|
|
visible_message("<span class='warning'>[src]'s onboard detection system has malfunctioned!</span>")
|
|
item_reactions["[exp_on.type]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
|
|
ejectItem()
|
|
if(globalMalf > 16 && globalMalf < 35)
|
|
visible_message("<span class='warning'>[src] melts [exp_on], ian-izing the air around it!</span>")
|
|
throwSmoke(loc)
|
|
if(trackedIan)
|
|
throwSmoke(trackedIan.loc)
|
|
trackedIan.forceMove(loc)
|
|
investigate_log("Experimentor has stolen Ian!", INVESTIGATE_EXPERIMENTOR) //...if anyone ever fixes it...
|
|
else
|
|
new /mob/living/simple_animal/pet/dog/corgi(loc)
|
|
investigate_log("Experimentor has spawned a new corgi.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
if(globalMalf > 36 && globalMalf < 50)
|
|
visible_message("<span class='warning'>Experimentor draws the life essence of those nearby!</span>")
|
|
for(var/mob/living/m in view(4,src))
|
|
to_chat(m, "<span class='danger'>You feel your flesh being torn from you, mists of blood drifting to [src]!</span>")
|
|
m.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
|
|
investigate_log("Experimentor has taken 50 brute a blood sacrifice from [m]", INVESTIGATE_EXPERIMENTOR)
|
|
if(globalMalf > 51 && globalMalf < 75)
|
|
visible_message("<span class='warning'>[src] encounters a run-time error!</span>")
|
|
throwSmoke(loc)
|
|
if(trackedRuntime)
|
|
throwSmoke(trackedRuntime.loc)
|
|
trackedRuntime.forceMove(drop_location())
|
|
investigate_log("Experimentor has stolen Runtime!", INVESTIGATE_EXPERIMENTOR)
|
|
else
|
|
new /mob/living/simple_animal/pet/cat(loc)
|
|
investigate_log("Experimentor failed to steal runtime, and instead spawned a new cat.", INVESTIGATE_EXPERIMENTOR)
|
|
ejectItem(TRUE)
|
|
if(globalMalf > 76 && globalMalf < 98)
|
|
visible_message("<span class='warning'>[src] begins to smoke and hiss, shaking violently!</span>")
|
|
use_power(500000)
|
|
investigate_log("Experimentor has drained power from its APC", INVESTIGATE_EXPERIMENTOR)
|
|
if(globalMalf == 99)
|
|
visible_message("<span class='warning'>[src] begins to glow and vibrate. It's going to blow!</span>")
|
|
addtimer(CALLBACK(src, .proc/boom), 50)
|
|
if(globalMalf == 100)
|
|
visible_message("<span class='warning'>[src] begins to glow and vibrate. It's going to blow!</span>")
|
|
addtimer(CALLBACK(src, .proc/honk), 50)
|
|
|
|
addtimer(CALLBACK(src, .proc/reset_exp), resetTime)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/boom()
|
|
explosion(src, 1, 5, 10, 5, 1)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/honk()
|
|
playsound(src, 'sound/items/bikehorn.ogg', 500)
|
|
new /obj/item/grown/bananapeel(loc)
|
|
|
|
/obj/machinery/rnd/experimentor/proc/reset_exp()
|
|
update_icon()
|
|
recentlyExperimented = FALSE
|
|
|
|
/obj/machinery/rnd/experimentor/update_icon_state()
|
|
icon_state = "h_lathe"
|
|
|
|
/obj/machinery/rnd/experimentor/proc/warn_admins(user, ReactionName)
|
|
var/turf/T = get_turf(user)
|
|
message_admins("Experimentor reaction: [ReactionName] generated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(T)]")
|
|
log_game("Experimentor reaction: [ReactionName] generated by [key_name(user)] in [AREACOORD(T)]")
|
|
|
|
#undef SCANTYPE_POKE
|
|
#undef SCANTYPE_IRRADIATE
|
|
#undef SCANTYPE_GAS
|
|
#undef SCANTYPE_HEAT
|
|
#undef SCANTYPE_COLD
|
|
#undef SCANTYPE_OBLITERATE
|
|
#undef SCANTYPE_DISCOVER
|
|
|
|
#undef EFFECT_PROB_VERYLOW
|
|
#undef EFFECT_PROB_LOW
|
|
#undef EFFECT_PROB_MEDIUM
|
|
#undef EFFECT_PROB_HIGH
|
|
#undef EFFECT_PROB_VERYHIGH
|
|
|
|
#undef FAIL
|
|
|
|
|
|
//////////////////////////////////SPECIAL ITEMS////////////////////////////////////////
|
|
|
|
/obj/item/relic
|
|
name = "strange object"
|
|
desc = "What mysteries could this hold? Maybe Research & Development could find out."
|
|
icon = 'icons/obj/assemblies.dmi'
|
|
var/realName = "defined object"
|
|
var/revealed = FALSE
|
|
var/realProc
|
|
var/cooldownMax = 60
|
|
var/cooldown
|
|
|
|
/obj/item/relic/Initialize()
|
|
. = ..()
|
|
icon_state = pick("shock_kit","armor-igniter-analyzer","infra-igniter0","infra-igniter1","radio-multitool","prox-radio1","radio-radio","timer-multitool0","radio-igniter-tank")
|
|
realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","illegal tech","weapon")]"
|
|
|
|
|
|
/obj/item/relic/proc/reveal()
|
|
if(revealed) //Re-rolling your relics seems a bit overpowered, yes?
|
|
return
|
|
revealed = TRUE
|
|
name = realName
|
|
cooldownMax = rand(60,300)
|
|
realProc = pick(.proc/teleport,.proc/explode,.proc/rapidDupe,.proc/petSpray,.proc/flash,.proc/clean,.proc/corgicannon)
|
|
|
|
/obj/item/relic/attack_self(mob/user)
|
|
if(revealed)
|
|
if(cooldown)
|
|
to_chat(user, "<span class='warning'>[src] does not react!</span>")
|
|
return
|
|
else if(loc == user)
|
|
cooldown = TRUE
|
|
call(src,realProc)(user)
|
|
if(!QDELETED(src))
|
|
addtimer(CALLBACK(src, .proc/cd), cooldownMax)
|
|
else
|
|
to_chat(user, "<span class='notice'>You aren't quite sure what this is. Maybe R&D knows what to do with it?</span>")
|
|
|
|
/obj/item/relic/proc/cd()
|
|
cooldown = FALSE
|
|
|
|
//////////////// RELIC PROCS /////////////////////////////
|
|
|
|
/obj/item/relic/proc/throwSmoke(turf/where)
|
|
var/datum/effect_system/smoke_spread/smoke = new
|
|
smoke.set_up(0, get_turf(where))
|
|
smoke.start()
|
|
|
|
/obj/item/relic/proc/corgicannon(mob/user)
|
|
playsound(src, "sparks", rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/mob/living/simple_animal/pet/dog/corgi/C = new/mob/living/simple_animal/pet/dog/corgi(get_turf(user))
|
|
C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .proc/throwSmoke, C))
|
|
warn_admins(user, "Corgi Cannon", 0)
|
|
|
|
/obj/item/relic/proc/clean(mob/user)
|
|
playsound(src, "sparks", rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/obj/item/grenade/chem_grenade/cleaner/CL = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user))
|
|
CL.detonate()
|
|
warn_admins(user, "Smoke", 0)
|
|
|
|
/obj/item/relic/proc/flash(mob/user)
|
|
playsound(src, "sparks", rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
|
|
var/obj/item/grenade/flashbang/CB = new/obj/item/grenade/flashbang(user.loc)
|
|
CB.detonate()
|
|
warn_admins(user, "Flash")
|
|
|
|
/obj/item/relic/proc/petSpray(mob/user)
|
|
var/message = "<span class='danger'>[src] begins to shake, and in the distance the sound of rampaging animals arises!</span>"
|
|
visible_message(message)
|
|
to_chat(user, message)
|
|
var/animals = rand(1,25)
|
|
var/counter
|
|
var/list/valid_animals = list(/mob/living/simple_animal/parrot, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/crab, /mob/living/simple_animal/pet/fox, /mob/living/simple_animal/hostile/lizard, /mob/living/simple_animal/mouse, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/hostile/bear, /mob/living/simple_animal/hostile/poison/bees, /mob/living/simple_animal/hostile/carp)
|
|
for(counter = 1; counter < animals; counter++)
|
|
var/mobType = pick(valid_animals)
|
|
new mobType(get_turf(src))
|
|
warn_admins(user, "Mass Mob Spawn")
|
|
if(prob(60))
|
|
to_chat(user, "<span class='warning'>[src] falls apart!</span>")
|
|
qdel(src)
|
|
|
|
/obj/item/relic/proc/rapidDupe(mob/user)
|
|
audible_message("[src] emits a loud pop!")
|
|
var/list/dupes = list()
|
|
var/counter
|
|
var/max = rand(5,10)
|
|
for(counter = 1; counter < max; counter++)
|
|
var/obj/item/relic/R = new type(get_turf(src))
|
|
R.name = name
|
|
R.desc = desc
|
|
R.realName = realName
|
|
R.realProc = realProc
|
|
R.revealed = TRUE
|
|
dupes |= R
|
|
R.throw_at(pick(oview(7,get_turf(src))),10,1)
|
|
counter = 0
|
|
QDEL_LIST_IN(dupes, rand(10, 100))
|
|
warn_admins(user, "Rapid duplicator", 0)
|
|
|
|
/obj/item/relic/proc/explode(mob/user)
|
|
to_chat(user, "<span class='danger'>[src] begins to heat up!</span>")
|
|
addtimer(CALLBACK(src, .proc/do_explode, user), rand(35, 100))
|
|
|
|
/obj/item/relic/proc/do_explode(mob/user)
|
|
if(loc == user)
|
|
visible_message("<span class='notice'>\The [src]'s top opens, releasing a powerful blast!</span>")
|
|
explosion(user.loc, 0, rand(1,5), rand(1,5), rand(1,5), rand(1,5), flame_range = 2)
|
|
warn_admins(user, "Explosion")
|
|
qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
|
|
|
|
/obj/item/relic/proc/teleport(mob/user)
|
|
to_chat(user, "<span class='notice'>[src] begins to vibrate!</span>")
|
|
addtimer(CALLBACK(src, .proc/do_the_teleport, user), rand(10, 30))
|
|
|
|
/obj/item/relic/proc/do_the_teleport(mob/user)
|
|
var/turf/userturf = get_turf(user)
|
|
if(loc == user && !is_centcom_level(userturf.z)) //Because Nuke Ops bringing this back on their shuttle, then looting the ERT area is 2fun4you!
|
|
visible_message("<span class='notice'>[src] twists and bends, relocating itself!</span>")
|
|
throwSmoke(userturf)
|
|
do_teleport(user, userturf, 8, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
|
|
throwSmoke(get_turf(user))
|
|
warn_admins(user, "Teleport", 0)
|
|
|
|
//Admin Warning proc for relics
|
|
/obj/item/relic/proc/warn_admins(mob/user, RelicType, priority = 1)
|
|
var/turf/T = get_turf(src)
|
|
var/log_msg = "[RelicType] relic used by [key_name(user)] in [AREACOORD(T)]"
|
|
if(priority) //For truly dangerous relics that may need an admin's attention. BWOINK!
|
|
message_admins("[RelicType] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(T)]")
|
|
log_game(log_msg)
|
|
investigate_log(log_msg, "experimentor")
|