Files
2020-02-19 19:48:29 -08:00

1169 lines
33 KiB
Plaintext

/*
Contents:
Skull of Souls
Tommyize proc
Donald Trumpet
SpyGUI
Chemistry spectrometer prototype
Retractable solar panel prototype
Fibre wire
*/
//////////////////////////////
//The pretty darn mean skull
//That's nice to ghosts
// Yay
////////////////////////////
/obj/item/soulskull
name = "ominous skull"
desc = "This skull gives you the heebie-jeebies."
icon = 'icons/obj/surgery.dmi'
icon_state = "skull_ominous"
var/being_mean = 0
attack_hand(mob/M)
if(!being_mean)
..()
M.show_text("<B><I>It burns...!</I></B>", "red")
if(ishuman(M)) evil_act(M)
/* oops didn't quite think this through
attackby(obj/item/W as obj, mob/user as mob)
if (istype(W, /obj/item/parts/robot_parts/leg))
var/obj/machinery/bot/skullbot/B = new /obj/machinery/bot/skullbot
B.icon = icon('icons/obj/aibots.dmi', "skullbot-ominous")
B.name = "ominous skullbot"
boutput(user, "<span style=\"color:blue\">You add [W] to [src]. That's neat.</span>")
B.set_loc(get_turf(user))
qdel(W)
qdel(src)
return
*/
proc/evil_act(mob/living/carbon/human/H)
var/list/mob/dead/observer/possible_targets = list()
var/list/mob/dead/observer/priority_targets = list()
if(ticker && ticker.mode) //Yes, I'm sure my runtimes will matter if the goddamn TICKER is gone.
for(var/datum/mind/M in (ticker.mode.Agimmicks | ticker.mode.traitors)) //We want an EVIL ghost
if(!M.dnr && M.current && isobserver(M.current) && M.current.client && M.special_role != "vampthrall" && M.special_role != "mindslave")
priority_targets.Add(M.current)
if(!priority_targets.len) //Okay, fine. Any ghost. *sigh
for(var/mob/dead/observer/O in mobs)
if(O && O.client && O.mind && !O.mind.dnr)
possible_targets.Add(O)
if(!priority_targets.len && !possible_targets.len) return //Gotta have a ghostie
being_mean = 1
H.canmove = 0
H.drop_item(src)
src.set_loc(H.loc)
src.layer = EFFECTS_LAYER_4
playsound(src.loc, 'sound/ambience/spooky/Void_Calls.ogg', 40, 1)
SPAWN_DBG(0) animate_levitate(src, -1)
H.emote("scream")
H.changeStatus("weakened", 10 SECONDS)
SPAWN_DBG(7 SECONDS)
if(!H)
being_mean = 0
return
H.emote("faint")
H.changeStatus("paralysis", 150)
H.show_text("<I><font size=5>You feel your mind drifting away from your body!</font></I>", "red")
playsound(src.loc, 'sound/effects/ghost.ogg', 50, 1)
if(!H.mind)
H.ghostize()
else
if(priority_targets.len) //Do we have an evil ghost?
H.mind.swap_with(pick(priority_targets))
H.show_text("<I><B>You hear a sinister voice in your head... \"I have brought you back to do evil once more!\"</B></I>")
else if(possible_targets.len) //Do we have a plain ol' ghost?
H.mind.swap_with(pick(possible_targets))
else //How the fuck did we even get here??
H.ghostize()
SPAWN_DBG(1.5 SECONDS) playsound(src.loc, 'sound/effects/ghostlaugh.ogg', 70, 1)
flick("skull_ominous_explode", src)
sleep(30)
qdel(src)
//////////////////////////////
//Tommyize
////////////////////////////
proc/Create_Tommyname()
return pick("Toh", "Tho", "To") + pick("mmh", "mh", "mm", "m") + pick("i", "eh", "yh", "ee", "u") + " " + pick("Wa", "Wi", "Wu", "Wee", "We") + pick("z", "zh", "se", "seh") + pick("oo", "oh", "eeh", "au", "ay", "uu", "uh")
/mob/proc/tommyize()
src.transforming = 1
src.canmove = 0
src.invisibility = 101
for(var/obj/item/clothing/O in src)
src.u_equip(O)
if (O)
O.set_loc(src.loc)
O.dropped(src)
O.layer = initial(O.layer)
var/mob/living/carbon/human/tommy/T = new(src.loc)
if(src.mind)
src.mind.transfer_to(T)
else
T.key = src.key
SPAWN_DBG(1 SECOND)
qdel(src)
/mob/living/carbon/human/proc/tommyize_reshape()
//Set up the new appearance
var/datum/appearanceHolder/AH = new
AH.gender = "male"
AH.customization_first = "Dreadlocks"
AH.gender = "male"
AH.s_tone = "#FAD7D0"
AH.owner = src
AH.parentHolder = src.bioHolder
src.gender = "male"
src.real_name = Create_Tommyname()
src.sound_list_laugh = list('sound/voice/tommy_hahahah.ogg', 'sound/voice/tommy_hahahaha.ogg')
src.sound_list_scream = list('sound/voice/tommy_you-are-tearing-me-apart-lisauh.ogg', 'sound/voice/tommy_did-not-hit-hehr.ogg')
src.sound_list_flap = list('sound/voice/tommy_weird-chicken-noise.ogg')
for(var/obj/item/clothing/O in src)
src.u_equip(O)
if (O)
if(istype(O, /obj/item/clothing/shoes/black) || istype(O, /obj/item/clothing/under/suit))
O.cant_drop = 1
O.cant_other_remove = 1
O.cant_self_remove = 1
continue
O.set_loc(src.loc)
O.dropped(src)
O.layer = initial(O.layer)
src.equip_if_possible(new /obj/item/clothing/shoes/black {cant_drop = 1; cant_other_remove = 1; cant_self_remove = 1} (src), slot_shoes)
src.equip_if_possible(new /obj/item/clothing/under/suit {cant_drop = 1; cant_other_remove = 1; cant_self_remove = 1} (src), slot_w_uniform)
src.equip_if_possible(new /obj/item/football(src), slot_in_backpack)
src.sound_scream = 'sound/voice/tommy_you-are-tearing-me-apart-lisauh.ogg'
src.sound_fingersnap = 'sound/voice/tommy_did-not-hit-hehr.ogg'
if(src.bioHolder)
src.bioHolder.mobAppearance = AH
src.bioHolder.AddEffect("accent_tommy")
SPAWN_DBG(1 SECOND)
src.bioHolder.mobAppearance.UpdateMob()
//////////////////////////////
//Tommy gun
////////////////////////////
/datum/projectile/tommy
name = "space-tommy disruption"
icon = 'icons/obj/projectiles.dmi'
icon_state = "random_thing"
//How much of a punch this has, tends to be seconds/damage before any resist
power = 10
//How much ammo this costs
cost = 10
//How fast the power goes away
dissipation_rate = 1
//How many tiles till it starts to lose power
dissipation_delay = 10
//Kill/Stun ratio
ks_ratio = 0.0
//name of the projectile setting, used when you change a guns setting
sname = "Tommify"
//file location for the sound you want it to play
shot_sound = 'sound/voice/tommy_hauh.ogg'
//How many projectiles should be fired, each will cost the full cost
shot_number = 1
//What is our damage type
damage_type = 0
//With what % do we hit mobs laying down
hit_ground_chance = 10
//Can we pass windows
window_pass = 0
on_hit(atom/hit)
if(ishuman(hit))
hit:tommyize_reshape()
playsound(hit.loc, 'sound/voice/tommy_hey-everybody.ogg', 50, 1)
else if(ismob(hit))
hit:tommyize()
playsound(hit.loc, 'sound/voice/tommy_hey-everybody.ogg', 50, 1)
///////////////////////////////////////Tommy Gun
/obj/item/gun/energy/tommy_gun
name = "Tommy Gun"
icon = 'icons/obj/gun.dmi'
icon_state = "tommy1"
m_amt = 4000
rechargeable = 1
force = 0.0
desc = "It smells of cheap cologne and..."
New()
cell = new/obj/item/ammo/power_cell/high_power
current_projectile = new/datum/projectile/tommy
projectiles = list(new/datum/projectile/tommy)
..()
update_icon()
return
shoot(var/target,var/start,var/mob/user,var/POX,var/POY)
for(var/mob/O in AIviewers(user, null))
O.show_message("<span style=\"color:red\"><B>[user] fires the [src] at [target]!</B></span>", 1, "<span style=\"color:red\">You hear a loud crackling noise.</span>", 2)
sleep(1)
return ..(target, start, user)
update_icon()
if(cell)
src.icon_state = "tommy[src.cell.charge > 0]"
return
///////////////////////////////////////Analysis datum for the spectrometer
/datum/spectro_analysis
proc/analyze_reagents(var/datum/reagents/R, var/check_recipes = 0)
if(R && R.reagent_list && R.reagent_list.len)
if(check_recipes)
. = analyze_reagent_components(R.reagent_list)
else
. = analyze_reagent_list(R.reagent_list)
//Analyze the recipe for each reagent id. If there's more than one id this will be a fucking mess.
proc/analyze_reagent_components(var/list/reagent_ids)
if(reagent_ids.len)
var/output = list()
for (var/id in reagent_ids)
var/datum/chemical_reaction/recipe = chem_reactions_by_id[id]
if(recipe && recipe.required_reagents && recipe.required_reagents.len)
analyze_reagent_list(recipe.required_reagents, output)
else
for(var/i=0, i<rand(2,7), i++) //If it doesn't have a recipe, just spit out some random data
analyze_single(output, md5(rand(100,100000)))
return output
return null
//Calculates the result for every reagent ID in a list
proc/analyze_reagent_list(var/list/reagent_ids, var/list/output)
if(reagent_ids.len)
if(!output) output = list()
for(var/RID in reagent_ids)
if(reagents_cache[RID])
output = analyze_single(output, RID)
else
logTheThing("debug", null, null, "<B>SpyGuy/spectro:</B> attempted to analyze invalid reagent id: [RID]")
return output
//This is mainly a helper
proc/analyze_single(var/list/base, var/id)
var/hash = md5("AReally[id]ShittySalt")
var/listPos = calc_start_point(hash)
for(var/i=1, i <= lentext(hash), i+=2)
var/block = copytext(hash, i, i+2)
if (isnull(base["[listPos]"]))
base["[listPos]"] = hex2num(block)
else
base["[listPos]"] += hex2num(block)
listPos += (hex2num(copytext(block,1,2)) + hex2num(copytext(block,2)))
return base
//So is this
proc/calc_start_point(var/hash)
for(var/i = 1; i <= lentext(hash); i++)
var/temp = copytext(hash, i, i+1)
temp = hex2num(temp)
. += temp
. = round(. * 1.5)
/////////////////////////////// Trigger
/obj/trigger
name = "trigger"
desc = "warning"
icon = 'icons/mob/screen1.dmi'
icon_state = "x2"
anchored = 1
invisibility = 101
event_handler_flags = USE_HASENTERED
HasEntered(atom/movable/AM)
..()
on_trigger(AM)
proc/on_trigger(var/atom/movable/triggerer)
/obj/trigger/critter //Wakes up all critters in an area
name = "critter trigger"
var/area/assigned_area = null
on_trigger(atom/movable/triggerer)
if(isliving(triggerer) || locate(/mob) in triggerer)
if(!assigned_area) assigned_area = get_area(src)
assigned_area.wake_critters()
/obj/trigger/throw
name = "throw trigger"
var/throw_dir = NORTH
on_trigger(var/atom/movable/triggerer)
if(isobserver(triggerer)) return
var/atom/target = get_edge_target_turf(src, src.throw_dir)
SPAWN_DBG(0)
if (target)
triggerer.throw_at(target, 50, 1)
/obj/trigger/cluwnegib
name = "floor cluwne trigger"
desc = "The name belies the fact that floor cluwnes are not real."
on_trigger(var/atom/movable/triggerer)
if(isobserver(triggerer)) return
var/mob/M = triggerer
if(istype(M))
M.cluwnegib(10)
/obj/trigger/badmantrigger
name = "Death Badman Meeting Zone"
desc = "He's very angry he lost the election."
var/activated = 0
on_trigger(var/atom/movable/triggerer)
//Sanity check, thanks readster.
if(isobserver(triggerer)) return
var/mob/M = triggerer
if(!istype(M))
return
if(istype(M, /mob/living/carbon/human) && !activated)
switch(M.ckey)
if("hydrofloric")
return
if("kyle2143")
return
if("johnwarcrimes")
return
else
activated = 1
sleep(20)
var/startx = 1
var/starty = 1
var/mob/badmantarget = M
boutput(badmantarget, "<span style=\"color:black\"> <B> You hear a voice in your head, 'You're not supposed to be here'. </B>")
playsound(badmantarget, 'sound/misc/american_patriot.ogg', 50, 1, -1)
sleep(100)
startx = badmantarget.x - rand(-11, 11)
starty = badmantarget.y - rand(-11, 11)
var/turf/pickedstart = locate(startx, starty, badmantarget.z)
new /obj/badman(pickedstart, badmantarget)
sleep(150)
activated = 0
/obj/trigger/readsteroffice
name = "that horrible thing in Readster's office trigger"
desc = "There's something terrible here."
var/running = 0
on_trigger(var/atom/movable/triggerer)
//Sanity check
if(isobserver(triggerer) || running) return
var/mob/M = triggerer
if(!istype(M))
return
if(istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
H.unkillable = 0
//Get the area and ensure it's the correct type. Otherwise straight-up gib.
var/area/centcom/offices/readster/readster_office = get_area(src)
if(!istype(readster_office))
M.cluwnegib(10)
return
try
running = 1
var/obj/machinery/door/unpowered/D = locate() in readster_office
//Close the door
close_door(D)
sleep(15)
//Lights out
for(var/obj/machinery/light/L in readster_office)
L.seton(0)
sleep(30)
//Here comes the meanie
M.mind.karma -= 1000 //stay out of my office!!
M.cluwnegib(10)
sleep(20)
//Lights on
for(var/obj/machinery/light/L in readster_office)
L.seton(1)
sleep(10)
D.locked = 0
D.open()
running = 0
catch
//The best error handling
if(M) M.cluwnegib(10)
running = 0
proc/close_door(var/obj/machinery/door/unpowered/D)
if(istype(D))
var/turf/T = get_turf(D)
T.set_density(1)
for(var/mob/living/L in T)
L.set_loc(get_step(D, WEST))
D.close()
D.locked = 1
T.set_density(0)
////////////////////////////// Donald Trumpet
/datum/projectile/energy_bolt_v/trumpet
name = "trumpet bolt"
shot_sound = 'sound/musical_instruments/Bikehorn_2.ogg'
on_hit(atom/hit)
..()
if(ishuman(hit))
var/mob/living/carbon/human/H = hit
if(!istype(H.head, /obj/item/clothing/head/wig))
var/obj/item/clothing/head/wig/W = H.create_wig()
H.bioHolder.mobAppearance.customization_first = "None"
H.cust_one_state = customization_styles["None"]
H.drop_from_slot(H.head)
H.force_equip(W, H.slot_head)
H.set_clothing_icon_dirty()
/obj/item/gun/energy/dtrumpet
name = "Donald Trumpet"
desc = "You can tell this gun has been fired!"
icon = 'icons/obj/instruments.dmi'
icon_state = "trumpet"
New()
cell = new/obj/item/ammo/power_cell/high_power
current_projectile = new/datum/projectile/energy_bolt_v/trumpet
projectiles = list(new/datum/projectile/energy_bolt_v/trumpet)
..()
////////////////////////////// Power machine
/obj/machinery/power/debug_generator
name = "mysterious petrol generator"
desc = "Holds untold powers. Literally. Untold power. Get it? Power. Watts? Ok, fine. This thing spits out unlimited watt-volts!! There. I said it!"
icon_state = "ggenoff"
density = 1
var/generating = 0
New()
..()
//UnsubscribeProcess()
attack_hand(mob/user)
if(!user) return
generating = (input("Select the amount of power this machine should generate (in MW))", "Playing with power") as num) * 1e6
if(generating > 0)
SubscribeToProcess()
powernet = get_direct_powernet()
icon_state = "ggenoff"
else
UnsubscribeProcess()
powernet = null
icon_state = "ggen"
process()
..()
if(!generating) UnsubscribeProcess()
add_avail(generating)
////////////////////////////// Spy GUI (ha ha ha ha)
#define INIT_CHECK if(!src.initialized) src.initialize()
/datum/spyGUI
var/target_file = null
var/desired_file = ""
var/target_window = ""
var/target_params = ""
var/list/mob/subscribed_mobs = new
var/list/mob/connecting = new
var/datum/master = null
var/max_retries = 5
var/time_per_try = 2
var/validate_user = 0
var/initialized = 0
New(var/filename, var/windowname, var/parameters, var/datum/master)
..()
target_window = windowname
target_params = parameters
desired_file = filename
src.master = master
proc/initialize()
target_file = grabResource(desired_file)
initialized = 1
proc/getFile()
if(!target_file)
target_file = grabResource(desired_file)
return target_file
proc/displayInterface(var/mob/target, var/initData)
INIT_CHECK //Initialize the SpyGUI instance on use
if((target in connecting))
return
connecting[target] = initData
var/retries = max_retries
var/extrasleep = 0
target.Browse(getFile(), "window=[target_window];[target_params]")
onclose(target, target_window, src)
do
if(winexists(target, "[target_window].browser")) //Fuck if I know
target << output("\ref[src]", "[target_window].browser:setRef")
sleep(time_per_try + extrasleep++)
while(retries-- > 0 && target in connecting) //Keep trying to send the UI update until it times out or they get it.
if(target in connecting)
connecting -= target
target.Browse(null, target_window)
proc/unsubscribeTarget(var/mob/target, close=1)
INIT_CHECK //Initialize the SpyGUI instance on use
if(close)
target.Browse(null, target_window)
subscribed_mobs -= target
Topic(href, href_list)
INIT_CHECK //Initialize the SpyGUI instance on use
..()
DEBUG_MESSAGE("Received: [href]")
if (href_list["ackref"] )
var/D = connecting[usr]
if(D)
connecting -= usr
subscribed_mobs |= usr
sendData(usr, D, "setUIState")
return
if (href_list["close"])
subscribed_mobs -= usr
if(master != src)
master.Topic(href, href_list) //Pass the href on
proc/validateSubscriber(var/mob/sub)
if(!sub.client) //If the subscriber lacks a client then rip they
return 0
if(!validate_user)
return 1
. = 1
if(sub.stat) . = 0 //Not dead / unconscious
if ( . && istype(master, /atom)) //Range check.
. = (sub in range(1, master))
proc/sendToSubscribers(var/data, var/handler)
INIT_CHECK //Initialize the SpyGUI instance on use
//DEBUG_MESSAGE("Sending: [data] to [handler ? handler : "-nothing-"]")
for(var/mob/M in subscribed_mobs)
if(validateSubscriber(M))
sendData(M, data, handler)
else
unsubscribeTarget(M)
proc/sendData(var/mob/target, var/data, var/handler)
var/list/L = new
L += handler
L += data
var/O = list2params(L)
target << output(O, "[target_window].browser:receiveData")
#undef INIT_CHECK
#define STAT_STANDBY 0
#define STAT_MOVING 1
#define STAT_EXTENDED 2
#define DEFAULT_ANIMATION_TIME 10
////////////////////////////// Solar Panel thingamajig
/obj/solar_control
name = "solar panel servo"
desc = "This machine contains a neatly-folded solar panel, for use when the ship is at little risk of external impacts and low on power."
//invisibility = 100
icon = 'icons/obj/machines/nuclear.dmi'
icon_state = "engineoff"
var/extension_dir = WEST
var/num_panels = 4
var/panel_width = 2
var/panel_length = 5
var/panel_space = 1
var/controller_padding = 1
var/station_padding = 2
var/status = 0
var/list/atom/created_atoms = list()
//TODO:
//Pooling and reuse of components
////Debug verbs
/obj/solar_control/verb/extend()
set src in range(1)
set category = "Local"
extend_panel()
/obj/solar_control/verb/retract()
set src in range(1)
set category = "Local"
retract_panel()
/obj/solar_control/proc/extend_panel()
if(status != STAT_STANDBY) return
icon_state = "engineon"
status = STAT_MOVING
var/paneldir1 = turn(extension_dir, 90)
var/paneldir2 = turn(extension_dir, -90)
var/list/turf/panelturfs = list()
var/turf/walker = get_turf(src)
DEBUG_MESSAGE("Extending panel at [showCoords(src.x, src.y, src.z)]. extension_dir: [extension_dir] ([dir2text(extension_dir)]), paneldir1: [paneldir1] ([dir2text(paneldir1)]), paneldir2: [paneldir2] ([dir2text(paneldir2)])")
var/total_len = station_padding + controller_padding + (panel_space * (num_panels -1)) + num_panels * panel_width
DEBUG_MESSAGE("Determined total length of panel to be [total_len] tiles.")
//Create the initial padding
DEBUG_MESSAGE("Creating stationside padding.")
var/list/catwalk = list(/turf/simulated/floor/airless/plating/catwalk, /obj/grille/catwalk)
for(var/i = 0; i < station_padding;i++)
move_create_obj(catwalk, walker, extension_dir, extension_dir) //Then we walk outwards, creating stuff as we go along
walker = get_step(walker,extension_dir)
/*
if(i == 0)
SPAWN_DBG(0)
move_create_obj(list(new /obj/lattice{icon_state="lattice-dir-b"}), walker, paneldir1, paneldir2 | extension_dir)
move_create_obj(list(new /obj/lattice{icon_state="lattice-dir-b"}), walker, paneldir2, paneldir1 | turn(extension_dir, 180))
*/
DEBUG_MESSAGE("Creating panel segments.")
//Create the panels themselves
for(var/i = 0; i < num_panels; i++)
for(var/j = 0; j < (panel_space + panel_width);j++)
move_create_obj(catwalk, walker, extension_dir, (j >= panel_space) ? paneldir1 : extension_dir)
walker = get_step(walker, extension_dir)
if(j >= panel_space) panelturfs += walker
DEBUG_MESSAGE("Creating controller padding")
for(var/i = 0; i < controller_padding; i++)
move_create_obj(catwalk, walker, extension_dir, extension_dir) //Then we walk outwards, creating stuff as we go along
walker = get_step(walker,extension_dir)
DEBUG_MESSAGE("Creating solar panels")
var/list/solar_list = list(/turf/simulated/floor/airless/solar, /obj/machinery/power/solar)
for(var/turf/T in panelturfs)
SPAWN_DBG(0)
var/turf/w1 = T
var/turf/w2 = T
for(var/i = 0; i < panel_length; i++)
SPAWN_DBG(-1)
move_create_obj(solar_list, w1, paneldir1, paneldir1)
w1 = get_step(w1, paneldir1)
move_create_obj(solar_list, w2, paneldir2, paneldir2)
w2 = get_step(w2, paneldir2)
DEBUG_MESSAGE("Creating solar controller")
move_create_obj(list(/turf/simulated/floor/plating/airless, /obj/machinery/power/tracker), walker, extension_dir)
walker = get_step(walker,extension_dir)
SPAWN_DBG(0) move_create_obj(list(new /obj/lattice{icon_state="lattice-dir-b"}), walker, paneldir1, paneldir2)
move_create_obj(list(new /obj/lattice{icon_state="lattice-dir-b"}), walker, paneldir2, paneldir1)
status = STAT_EXTENDED
icon_state = "engineoff"
/obj/solar_control/proc/retract_panel()
if(status != STAT_EXTENDED) return
status = STAT_MOVING
var/list/atom/panels = get_panels()
for(var/i = panels.len; i > 0; i--)
var/list/atom/L = panels[i]
for(var/atom/A in L)
SPAWN_DBG(0)
move_and_delete_object(A)
sleep(DEFAULT_ANIMATION_TIME)
while(created_atoms.len > 0)
var/atom/A = created_atoms[created_atoms.len]
created_atoms.len--
if(istype(A, /turf))
var/turf/T = A
T.ReplaceWithSpace()
else if(istype(A,/obj))
move_and_delete_object(A)
status = STAT_STANDBY
/obj/solar_control/proc/get_panels()
var/list/atom/out = list()
var/list/atom/temp
out.len = panel_length //A list containing all the solar panels sorted on distance from centreline
for(var/i = created_atoms.len; i > 0; i--)
var/atom/A = created_atoms[i]
if(istype(A, /obj/machinery/power/solar) || istype(A, /turf/simulated/floor/airless/solar))
var/dist = get_dist_from_centreline(A)
temp = out[dist]
if(!temp)
temp = list()
out[dist] = temp
temp += A
return out
/obj/solar_control/proc/move_and_delete_object(var/obj/O, var/animtime = DEFAULT_ANIMATION_TIME)
//calculate new px / py
if(istype(O, /turf))
var/turf/T = O
var/obj/movedummy/MD = unpool(/obj/movedummy)
MD.mimic_turf(T.type, 0)
MD.set_loc(T)
T.ReplaceWithSpace()
O = MD
var/tdir = get_reciprocal(O)
var/npx = 0
if(tdir & (EAST | WEST))
if(tdir & WEST)
npx = -32
else if(tdir & EAST)
npx = 32
var/npy = 0
if(tdir & (NORTH | SOUTH))
if(tdir & NORTH)
npy = 32
else if (tdir & SOUTH)
npy = -32
animate_slide(O, npx, npy, animtime)
sleep(animtime)
if(istype(O, /obj/movedummy))
pool(O)
else
qdel(O)
/obj/solar_control/proc/move_create_obj(var/list/atom/to_create, var/turf/startturf, var/movedir, var/setdir=null, var/animtime = DEFAULT_ANIMATION_TIME)
//calculate initial px / py
var/ipx = 0
if(movedir & (EAST | WEST))
if(movedir & WEST)
ipx = 32
else if(movedir & EAST)
ipx = -32
var/ipy = 0
if(movedir & (NORTH | SOUTH))
if(movedir & NORTH)
ipy = -32
else if (movedir & SOUTH)
ipy = 32
DEBUG_MESSAGE("Initial offsets calculated based on movedir: [movedir] ([dir2text(movedir)]) as ipx: [ipx], ipy: [ipy]")
var/is_turf = 0
var/turf/T = get_step(startturf, movedir)
var/turf_type = null
for(var/t_type in to_create)
if(ispath(t_type, /turf))
turf_type = t_type
break
SPAWN_DBG(0)
for(var/t_type in to_create)
var/obj/O
is_turf = ispath(t_type, /turf) //If it's a turf we need some special handling.
if(istype(t_type, /obj))
O = t_type
else if(ispath(t_type))
if(!is_turf)
O = new t_type(null)
else
var/obj/movedummy/MD = unpool(/obj/movedummy)
MD.mimic_turf(t_type, animtime)
O = MD
else if(!ispath(t_type))
CRASH("move_create_obj not provided with type")
if(!is_turf)
created_atoms += O
O.pixel_x = ipx
O.pixel_y = ipy
if(setdir)
O.dir = setdir
O.set_loc(T)
animate_slide(O, 0, 0, animtime, LINEAR_EASING)
playsound(T, "sound/effects/airbridge_dpl.ogg", 50, 1)
sleep(animtime)
if(turf_type)
DEBUG_MESSAGE("Creating [turf_type] at [showCoords(T.x, T.y, T.z)]")
var/turf/NT = new turf_type(T)
if(setdir) NT.dir = setdir
created_atoms += NT
//Helpers
/obj/solar_control/proc/get_reciprocal(var/atom/A)
var/d = get_dir(A,src)
d &= ~(extension_dir | turn(extension_dir, 180)) //Turn off the bits parallel to the extension_dir
if(!d) d = turn(extension_dir, 180) //If this wound up turning off all the bits, the dir is on the extension line
//Look, I'm Swedish, I don't know your goddamn mathwords
return d
/obj/solar_control/proc/get_dist_from_centreline(var/atom/A) //Finds the distance from the closest point on the extension line
if(extension_dir & (NORTH|SOUTH) )
.= abs(A.x - src.x)
else if ( extension_dir & (EAST|WEST) )
.= abs(A.y - src.y)
DEBUG_MESSAGE("get_dist from [showCoords(A.x, A.y, A.z)] returned: [.]")
//The dummy object that imitates a turf
/obj/movedummy
name = "Dummy object."
invisibility = 101
/obj/movedummy/pooled()
..()
invisibility = 101
/obj/movedummy/proc/mimic_turf(var/turf_type, var/TTL)
ASSERT(ispath(turf_type, /turf))
var/turf/T = turf_type
src.name = initial(T.name)
src.desc = initial(T.desc)
src.icon = initial(T.icon)
src.icon_state = initial(T.icon_state)
src.set_density(initial(T.density))
src.opacity = initial(T.opacity)
src.dir = initial(T.dir)
src.layer = initial(T.layer)
src.invisibility = 0
if(TTL)
SPAWN_DBG(TTL)
pool(src)
#undef STAT_STANDBY
#undef STAT_MOVING
#undef STAT_EXTENDED
#undef DEFAULT_ANIMATION_TIME
//Aw heck
////////////////////////////// Fibre wire
/*
TO DO:
- = Must have
? = Nice to have
* = Done
* Only allow someone to initiate a stranglehold from behind. Eg - someone facing NORTH should only be strangleable from SOUTHWEST, SOUTH and SOUTHEAST
* setTwoHanded does not really cover cases where both hands have contents during the switch, if done while the item is equipped. This needs handling
* There's some weirdness with dropping the garrote and trying to pick it back up calling attack_self() instead of the pick up proc. Test & fix whatever causes it.
* The art needs considerable improvement, good grief
* Stunned targets should not take facing into consideration
* Faster death
* Lower slowdown when opened
? Break free message in case whoever manages to avoid the strangle in the action bar
? Improve grab strangles to jostle the target mob slightly? Make it look more fierce. Make_Jittery() or something similar, perhaps?
*/
/obj/item/garrote
name = "fibre wire"
desc = "A sturdy wire between two handles. Could be used with both hands to really ruin someone's day."
w_class = 1
icon = 'icons/obj/items.dmi'
icon_state = "garrote0"
event_handler_flags = USE_GRAB_CHOKE | USE_FLUID_ENTER
special_grab = /obj/item/grab/garrote_grab
// Are we ready to do something mean here?
var/wire_readied = 0
/obj/item/garrote/proc/toggle_wire_readiness()
set_readiness(!wire_readied)
/obj/item/garrote/proc/set_readiness(new_readiness)
wire_readied = new_readiness
// Try to stretch the wire
if(!src.setTwoHanded(new_readiness))
wire_readied = 0
return
if(wire_readied)
playsound(usr, 'sound/items/garrote_twang.ogg', 25,5)
w_class = 4
else
drop_grab()
w_class = 1
update_state()
/obj/item/garrote/proc/update_state()
if(src.chokehold)
var/obj/item/grab/garrote_grab/GG = src.chokehold
if(!GG.extra_deadly)
icon_state = "garrote2"
//We're choking someone out - apply a hefty slowdown
src.setProperty("movespeed", 6)
else
icon_state = "garrote3"
// We're really putting our back into it now - apply a heftier slowdown
src.setProperty("movespeed", 12)
else
icon_state = "garrote[wire_readied]"
//Slow us down slightly when we have the thing readied to encourage late-readying
src.setProperty("movespeed", 1 * wire_readied)
/obj/item/garrote/proc/is_behind_target(var/mob/living/assailant, var/mob/living/target)
var/assailant_dir = get_dir(target, assailant)
var/target_rear = turn(target.dir, 180)
return (assailant_dir & target_rear) > 0 || target.lying || target.stat
// Try to grab someone
/obj/item/garrote/proc/attempt_grab(var/mob/living/assailant, var/mob/living/target)
// Can't strangle someone who doesn't exist. Or if we're already strangling someone.
// Also no strangling with flaccid wires, that's just weird.
if(!assailant || !target)
return 1
if(!wire_readied)
assailant.show_message("<span class='combat'>You have to have a firm grip of the wire before you can strangle [target]!</span>")
return 1
if(chokehold)
assailant.show_message("<span class='combat'>You're too busy strangling [chokehold.affecting] to strangle someone else!</span>")
return 1
// TODO: check that target has their back turned
if(is_behind_target(assailant, target))
// Try to grab a dude
actions.start(new/datum/action/bar/private/icon/garrote_target(target, src), assailant)
else
assailant.show_message("<span class='combat'>You have to be behind your target or they'll see you coming!</span>")
return 1
// Actually apply the grab (called via action bar)
/obj/item/garrote/try_grab(var/mob/living/target, var/mob/living/assailant)
if(..())
assailant.visible_message("<span class='combat bold'>[assailant] wraps \the [src] around [target]'s neck!</span>")
chokehold.state = GRAB_NECK
chokehold.upgrade_to_kill()
update_state()
// Drop the grab
/obj/item/garrote/drop_grab()
..()
update_state()
// It will crumple when dropped
/obj/item/garrote/dropped()
..()
set_readiness(0)
/obj/item/garrote/disposing()
drop_grab()
..()
// Repeatedly process when in a chokehold, to verify things are as they should be
/obj/item/garrote/process_grab()
..()
if(src.chokehold && src.loc != src.chokehold.assailant)
set_readiness(0)
// Change the size of the garrote or the posture
/obj/item/garrote/attack_self()
if(!chokehold)
..()
toggle_wire_readiness()
else
var/obj/item/grab/garrote_grab/GG = src.chokehold
GG.extra_deadly = !GG.extra_deadly
if(GG.extra_deadly)
GG.assailant.visible_message("<span class='combat bold'>[GG.assailant] tightens their grip on \the [src], it digs into [GG.affecting]'s neck!</span>")
else
GG.assailant.visible_message("<span class='combat bold'>[GG.assailant] releases their hold on [GG.affecting] slightly!</span>")
update_state()
/obj/item/garrote/attack(mob/target, mob/user, def_zone, is_special = 0)
attempt_grab(user, target)
// Need a nice drop button
/obj/item/garrote/Click(location, control, params)
if(src.chokehold)
if(usr == src.loc)
var/list/P = params2list(params)
var/x = text2num(P["icon-x"])
var/y = text2num(P["icon-y"])
DEBUG_MESSAGE("Click on [src] - params x,y: ([x], [y]) - [params]")
//The "drop" icon bounding box
// 16, 8
//CODER LOG: Click on the fibre wire - params x,y: (16, 8) - icon-x=16;icon-y=8;left=1;screen-loc=11:32,1:8
if((x >= 6 && x <= 28 ) && (y >= 4 && y <= 13 ))
drop_grab()
return
..()
/datum/action/bar/private/icon/garrote_target
duration = 10
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED
id = "garrote_target"
icon = 'icons/mob/critter_ui.dmi'
icon_state = "neck_over"
var/mob/living/target
var/obj/item/garrote/the_garrote
New(target, garrote)
src.target = target
the_garrote=garrote
..()
proc/check_conditions()
. = 0
if(get_dist(owner, target) > 1 || !target || !owner || !the_garrote || !the_garrote.wire_readied)
interrupt(INTERRUPT_ALWAYS)
. = 1
onUpdate()
..()
if(check_conditions())
return
onStart()
..()
if(check_conditions())
return
onEnd()
..()
if(check_conditions())
return
the_garrote.try_grab(target, owner)
// Special grab obj that doesn't care if it's in someone's hands
/obj/item/grab/garrote_grab
// No breaking out under own power
break_prob = 0
var/extra_deadly = 0
check()
if(!assailant || !affecting)
qdel(src)
return 1
if(!isturf(assailant.loc) || (!isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) )
qdel(src)
return 1
return 0
// An extra dangerous grab
process_kill(var/mob/living/carbon/human/H, mult = 1)
if(extra_deadly)
affecting.TakeDamage(zone="All", brute=rand(3, 7) * mult)
affecting.losebreath += (1 * mult)
if(prob(25))
// Wire digging into a neck.
take_bleeding_damage(affecting, assailant, rand(0, 20) * mult)
..()
attack_self(user)
/proc/trigger_anti_cheat(var/mob/M, var/message, var/external_alert = 1)
if(M)
message_admins("[key_name(M)] [message].")
logTheThing("admin", M, null, message)
logTheThing("diary", M, null, message, "admin")
if(external_alert)
//IRCbot alert, for fun
var/ircmsg[] = new()
ircmsg["key"] = M.key
ircmsg["name"] = M.real_name
ircmsg["msg"] = "[message] and got themselves got by the anti-cheat cluwne."
ircbot.export("admin", ircmsg)
M.cluwnegib(15, 1)
/proc/list_item_damage_stats()
var/list/L = typesof(/obj/item)
var/result = "<table><tr><th>Name</th><th>Type</th><th>Force</th><th>Stamina damage</th><th>Stamina cost</th><th>Damage Type</th><th>Throw force</th></tr>"
for(var/type in L)
result += {"<tr>
<td>[initial(type:name)]</td>
<td>[type]</td>
<td>[initial(type:force)]</td>
<td>[initial(type:stamina_damage)]</td>
<td>[initial(type:stamina_cost)]</td>
<td>[initial(type:hit_type)]</td>
<td>[initial(type:throwforce)]</td>
</tr>"}
result += "</table>"
usr.Browse(result, "window=item_dam_stats;size=400x400")