mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 10:33:30 +01:00
Merge branch 'master' into put-that-cookie-down
This commit is contained in:
@@ -159,8 +159,8 @@ var/list/admin_verbs_debug = list(
|
||||
/client/proc/restart_controller,
|
||||
/client/proc/enable_debug_verbs,
|
||||
/client/proc/toggledebuglogs,
|
||||
/client/proc/qdel_toggle,
|
||||
/client/proc/cmd_display_del_log,
|
||||
/client/proc/cmd_display_del_log_simple,
|
||||
/client/proc/debugNatureMapGenerator,
|
||||
/client/proc/check_bomb_impacts,
|
||||
/client/proc/test_movable_UI,
|
||||
@@ -976,6 +976,7 @@ var/list/admin_verbs_ticket = list(
|
||||
|
||||
to_chat(T, "<span class='notice'><b><font size=3>Man up and deal with it.</font></b></span>")
|
||||
to_chat(T, "<span class='notice'>Move on.</span>")
|
||||
T << 'sound/voice/ManUp1.ogg'
|
||||
|
||||
log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.")
|
||||
message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.")
|
||||
|
||||
@@ -1576,19 +1576,19 @@
|
||||
log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.")
|
||||
|
||||
else if(href_list["cult_mindspeak"])
|
||||
var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [ticker.mode.cultdat.entity_name]", "Voice of [ticker.mode.cultdat.entity_name]", "")
|
||||
var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [ticker.cultdat.entity_name]", "Voice of [ticker.cultdat.entity_name]", "")
|
||||
if(!input)
|
||||
return
|
||||
|
||||
for(var/datum/mind/H in ticker.mode.cult)
|
||||
if (H.current)
|
||||
to_chat(H.current, "<span class='danger'>[ticker.mode.cultdat.entity_name]</span> murmurs, <span class='cultlarge'>[input]</span></span>")
|
||||
to_chat(H.current, "<span class='danger'>[ticker.cultdat.entity_name]</span> murmurs, <span class='cultlarge'>[input]</span></span>")
|
||||
|
||||
for(var/mob/dead/observer/O in player_list)
|
||||
to_chat(O, "<span class='danger'>[ticker.mode.cultdat.entity_name]</span> murmurs, <span class='cultlarge'>[input]</span></span>")
|
||||
to_chat(O, "<span class='danger'>[ticker.cultdat.entity_name]</span> murmurs, <span class='cultlarge'>[input]</span></span>")
|
||||
|
||||
message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [ticker.mode.cultdat.entity_name].")
|
||||
log_admin("[key_name(usr)] Voice of [ticker.mode.cultdat.entity_name]: [input]")
|
||||
message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [ticker.cultdat.entity_name].")
|
||||
log_admin("[key_name(usr)] Voice of [ticker.cultdat.entity_name]: [input]")
|
||||
|
||||
else if(href_list["adminplayerobservecoodjump"])
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
@@ -748,6 +748,65 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
|
||||
if("Respawnable Mobs")
|
||||
to_chat(usr, jointext(respawnable_list,","))
|
||||
|
||||
/client/proc/cmd_display_del_log()
|
||||
set category = "Debug"
|
||||
set name = "Display del() Log"
|
||||
set desc = "Display del's log of everything that's passed through it."
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/list/dellog = list("<B>List of things that have gone through qdel this round</B><BR><BR><ol>")
|
||||
sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
dellog += "<li><u>[path]</u><ul>"
|
||||
if(I.failures)
|
||||
dellog += "<li>Failures: [I.failures]</li>"
|
||||
dellog += "<li>qdel() Count: [I.qdels]</li>"
|
||||
dellog += "<li>Destroy() Cost: [I.destroy_time]ms</li>"
|
||||
if(I.hard_deletes)
|
||||
dellog += "<li>Total Hard Deletes [I.hard_deletes]</li>"
|
||||
dellog += "<li>Time Spent Hard Deleting: [I.hard_delete_time]ms</li>"
|
||||
if(I.slept_destroy)
|
||||
dellog += "<li>Sleeps: [I.slept_destroy]</li>"
|
||||
if(I.no_respect_force)
|
||||
dellog += "<li>Ignored force: [I.no_respect_force]</li>"
|
||||
if(I.no_hint)
|
||||
dellog += "<li>No hint: [I.no_hint]</li>"
|
||||
dellog += "</ul></li>"
|
||||
|
||||
dellog += "</ol>"
|
||||
|
||||
usr << browse(dellog.Join(), "window=dellog")
|
||||
|
||||
/client/proc/cmd_display_del_log_simple()
|
||||
set category = "Debug"
|
||||
set name = "Display Simple del() Log"
|
||||
set desc = "Display a compacted del's log."
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/dat = "<B>List of things that failed to GC this round</B><BR><BR>"
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
if(I.failures)
|
||||
dat += "[I] - [I.failures] times<BR>"
|
||||
|
||||
dat += "<B>List of paths that did not return a qdel hint in Destroy()</B><BR><BR>"
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
if(I.no_hint)
|
||||
dat += "[I]<BR>"
|
||||
|
||||
dat += "<B>List of paths that slept in Destroy()</B><BR><BR>"
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
if(I.slept_destroy)
|
||||
dat += "[I]<BR>"
|
||||
|
||||
usr << browse(dat, "window=simpledellog")
|
||||
|
||||
/client/proc/cmd_admin_toggle_block(var/mob/M,var/block)
|
||||
if(!check_rights(R_SPAWN))
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
cross = image('icons/obj/storage.dmi',"tome")
|
||||
font_color = "red"
|
||||
prayer_type = "CULTIST PRAYER"
|
||||
deity = ticker.mode.cultdat.entity_name
|
||||
deity = ticker.cultdat.entity_name
|
||||
|
||||
msg = "<span class='notice'>[bicon(cross)]<b><font color=[font_color]>[prayer_type][deity ? " (to [deity])" : ""]:</font>[key_name(src, 1)] (<A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>) (<A HREF='?_src_=holder;adminplayeropts=\ref[src]'>PP</A>) (<A HREF='?_src_=vars;Vars=[UID()]'>VV</A>) (<A HREF='?_src_=holder;subtlemessage=\ref[src]'>SM</A>) ([admin_jump_link(src)]) (<A HREF='?_src_=holder;secretsadmin=check_antagonist'>CA</A>) (<A HREF='?_src_=holder;adminspawncookie=\ref[src]'>SC</a>) (<A HREF='?_src_=holder;Bless=[UID()]'>BLESS</A>) (<A HREF='?_src_=holder;Smite=[UID()]'>SMITE</A>):</b> [msg]</span>"
|
||||
|
||||
|
||||
@@ -136,13 +136,13 @@
|
||||
// Objects loop
|
||||
if(!(flags & DMM_IGNORE_OBJS))
|
||||
for(var/obj/O in model.contents)
|
||||
if(O.dont_save || !isnull(O.gcDestroyed))
|
||||
if(O.dont_save || QDELETED(O))
|
||||
continue
|
||||
obj_template += "[O.type][check_attributes(O,use_json=use_json)],"
|
||||
|
||||
// Mobs Loop
|
||||
for(var/mob/M in model.contents)
|
||||
if(M.dont_save || !isnull(M.gcDestroyed))
|
||||
if(M.dont_save || QDELETED(M))
|
||||
continue
|
||||
if(M.client)
|
||||
if(!(flags & DMM_IGNORE_PLAYERS))
|
||||
|
||||
@@ -17,6 +17,26 @@
|
||||
/mob/living/carbon/human/interactive/away/hotel/guard/doSetup()
|
||||
..("Guard")
|
||||
|
||||
// anti-pinata cheese
|
||||
var/obj/item/weapon/implant/dust/D = new /obj/item/weapon/implant/dust(src)
|
||||
D.implant(src)
|
||||
|
||||
for(var/obj/item/I in get_all_slots())
|
||||
I.flags |= NODROP
|
||||
|
||||
// FIXME(crazylemon) a hack to prevent guards from running around with an
|
||||
// extra security jumpsuit like a goof
|
||||
for(var/obj/item/clothing/under/U in get_all_slots())
|
||||
if(w_uniform != U)
|
||||
qdel(U)
|
||||
|
||||
/mob/living/carbon/human/interactive/away/hotel/guard/KnockOut()
|
||||
// you'll never take me alive (this triggers the implant)
|
||||
emote("deathgasp")
|
||||
if(stat != DEAD)
|
||||
// mission failed. we'll get em next time
|
||||
..()
|
||||
|
||||
/mob/living/carbon/human/interactive/away/hotel/chef
|
||||
default_job = /datum/job/chef
|
||||
away_area = /area/awaymission/spacehotel/kitchen
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
|
||||
var/adminhelped = 0
|
||||
|
||||
var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
#ifdef TESTING
|
||||
var/running_find_references
|
||||
var/last_find_references = 0
|
||||
#endif
|
||||
|
||||
///////////////
|
||||
//SOUND STUFF//
|
||||
///////////////
|
||||
|
||||
@@ -26,19 +26,16 @@
|
||||
display_name = "hi-tech veil"
|
||||
description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races."
|
||||
path = /obj/item/clothing/glasses/tajblind/sci
|
||||
allowed_roles = list("Scientist", "Research Director", "Robotocist")
|
||||
|
||||
/datum/gear/racial/tajeng
|
||||
display_name = "industrial veil"
|
||||
description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races."
|
||||
path = /obj/item/clothing/glasses/tajblind/eng
|
||||
allowed_roles = list("Chief Engineer", "Station Engineer", "Mechanic", "Life Support Specialist")
|
||||
|
||||
/datum/gear/racial/tajcargo
|
||||
display_name = "khaki veil"
|
||||
description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. It is light and comfy!"
|
||||
path = /obj/item/clothing/glasses/tajblind/cargo
|
||||
allowed_roles = list("Quartermaster","Cargo Technician", "Miner")
|
||||
|
||||
/datum/gear/racial/footwraps
|
||||
display_name = "cloth footwraps"
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
resist_time = 150
|
||||
mute = MUTE_MUFFLE
|
||||
flags = DROPDEL
|
||||
species_fit = list("Vox", "Unathi", "Tajaran", "Vulpkanin", "Grey")
|
||||
sprite_sheets = list(
|
||||
"Vox" = 'icons/mob/species/vox/mask.dmi',
|
||||
@@ -109,9 +110,8 @@
|
||||
R.loc = T
|
||||
transfer_fingerprints_to(R)
|
||||
playsound(src,'sound/items/poster_ripped.ogg',40,1)
|
||||
spawn(0) // Because of how dropping is done, if the muzzle gets deleted now, icons won't properly update and the whole unEquip() proc will break stuff.
|
||||
qdel(src) // This makes sure it gets deleted AFTER all that has to be done is done.
|
||||
user.emote("scream")
|
||||
user.emote("scream")
|
||||
. = ..()
|
||||
|
||||
/obj/item/clothing/mask/muzzle/safety
|
||||
name = "safety muzzle"
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
return
|
||||
|
||||
/obj/effect/countdown/process()
|
||||
if(!attached_to || qdeleted(attached_to))
|
||||
if(!attached_to || QDELETED(attached_to))
|
||||
qdel(src)
|
||||
forceMove(get_turf(attached_to))
|
||||
var/new_val = get_value()
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
log_debug("Original brand intelligence machine: [originMachine] [ADMIN_VV(originMachine)] [ADMIN_JMP(originMachine)]")
|
||||
|
||||
/datum/event/brand_intelligence/tick()
|
||||
if(!originMachine || !isnull(originMachine.gcDestroyed) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
for(var/obj/machinery/vending/saved in infectedMachines)
|
||||
saved.shoot_inventory = 0
|
||||
if(originMachine)
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
if(!vendingMachines.len) //if every machine is infected
|
||||
for(var/obj/machinery/vending/upriser in infectedMachines)
|
||||
if(prob(70) && isnull(upriser.gcDestroyed))
|
||||
if(prob(70) && !QDELETED(upriser))
|
||||
var/mob/living/simple_animal/hostile/mimic/copy/M = new(upriser.loc, upriser, null, 1) // it will delete upriser on creation and override any machine checks
|
||||
M.faction = list("profit")
|
||||
M.speak = rampant_speeches.Copy()
|
||||
|
||||
@@ -606,7 +606,7 @@
|
||||
var/list/obj/structure/spacevine/queue_end = list()
|
||||
|
||||
for(var/obj/structure/spacevine/SV in growth_queue)
|
||||
if(qdeleted(SV))
|
||||
if(QDELETED(SV))
|
||||
continue
|
||||
i++
|
||||
queue_end += SV
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
switch(infestation_type)
|
||||
if(1)
|
||||
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/green
|
||||
spawncount = 4
|
||||
spawncount = 5
|
||||
if(2)
|
||||
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/white
|
||||
spawncount = 2
|
||||
@@ -38,8 +38,7 @@
|
||||
while(spawncount >= 1 && vents.len)
|
||||
var/obj/vent = pick(vents)
|
||||
var/obj/structure/spider/spiderling/terror_spiderling/S = new(vent.loc)
|
||||
S.name = "evil-looking spiderling"
|
||||
S.grow_as = spider_type
|
||||
S.amount_grown = 75
|
||||
S.amount_grown = 90
|
||||
vents -= vent
|
||||
spawncount--
|
||||
@@ -264,7 +264,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
|
||||
to_chat(T, "<span class='changeling'><i>Primary [rand(1000,9999)] states:</i> [pick("Hello","Hi","You're my slave now!","Don't try to get rid of me...")]</span>")
|
||||
break
|
||||
sleep(4)
|
||||
if(!qdeleted(borer))
|
||||
if(!QDELETED(borer))
|
||||
qdel(borer)
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -1257,7 +1257,7 @@
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/monkeycube/proc/Expand()
|
||||
if(isnull(gcDestroyed))
|
||||
if(!QDELETED(src))
|
||||
visible_message("<span class='notice'>[src] expands!</span>")
|
||||
new/mob/living/carbon/human(get_turf(src), monkey_type)
|
||||
qdel(src)
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
|
||||
===============================================================================
|
||||
How Garbage Collection Works
|
||||
===============================================================================
|
||||
|
||||
In BYOND, there are exactly two ways anything gets deleted:
|
||||
|
||||
- A "soft delete", which occurs when an object's reference count hits 0, meaning it is not being referenced by anything
|
||||
in the world (more on references further down). When an object is unreferenced, nothing can access it, so it has no
|
||||
reason to exist, and BYOND's garbage collector simply deletes the object. This is fast.
|
||||
|
||||
- A "hard delete", which occurs when the del keyword is used to directly delete an object. This forces BYOND to find
|
||||
every reference to the object being deleted, and null them all out. This is slow.
|
||||
|
||||
A reference is anything that refers to an object, and can thus be used to access it. A variable on another object, a
|
||||
variable in a proc, an argument to a proc, the src of a running proc, the locs of contained objects, another object's
|
||||
contents list (but not the world's automatic contents list), anything. BYOND keeps track of how many references there
|
||||
are to an object - the reference count goes up when you make a new reference to an object, and back down when that
|
||||
reference gets set to null, goes out of scope, or is deleted. When the reference count hits 0, there are no remaining
|
||||
references to the object, and it is deleted by BYOND's garbage collector.
|
||||
|
||||
When you have something that exists in the world, and you want it to stop existing (maybe it got blown up, or eaten by
|
||||
the singularity, or whatever), it needs to be deleted. You can use the del keyword to make sure it's deleted instantly,
|
||||
but del is slow, and you want things to be deleted as quickly as possible, especially if you're deleting a whole lot of
|
||||
them. You want soft deletes.
|
||||
|
||||
That's where the garbage collection system comes in - it prepares things to be soft deleted, and hard deletes anything
|
||||
that can't be. There are two main procs involved in this process:
|
||||
|
||||
/proc/qdel(datumToDelete)
|
||||
This is, effectively, a replacement for del that tells an object to prepare itself to be soft deleted by calling its
|
||||
Destroy() proc. Depending on the qdel hint returned by Destroy(), qdel will queue the object in the garbage collector
|
||||
(to be hard deleted if it isn't soft deleted), directly delete the object, or ignore the object and
|
||||
assume it will handle deleting itself. An object passed into qdel will have its gcDestroyed var set, so
|
||||
isnull(gcDestroyed) will be true if an object is not being destroyed, and false if it is (which means you should get
|
||||
rid of the reference you have to it).
|
||||
|
||||
Note that qdel can only work with datum-based objects, which excludes the world (deleting this shuts down the world),
|
||||
clients (deleting these disconnect the client), lists, and savefiles. If any of these are passed to qdel, they will
|
||||
be directly deleted, just as if they had been passed straight to del, so it should never be unsafe to qdel anything
|
||||
you could del.
|
||||
|
||||
/datum/proc/Destroy()
|
||||
This is, effectively, a replacement for Del() (with some exceptions) which is also responsible for nulling out
|
||||
references to or on the object it is called on. Unlike Del, the Destroy proc will only be called by qdel; generally,
|
||||
this should only happen to datums that are no longer referenced by anything, which shouldn't be an issue.
|
||||
|
||||
The exceptions where Destroy cannot replace Del are for the same non-datum types mentioned under qdel, above. Those
|
||||
should use a Del proc for any necessary cleanup, as a Destroy proc on them will not automatically get called.
|
||||
|
||||
When called by qdel, Destroy is expected to return a qdel hint, which determines whether the object should be directly
|
||||
deleted, queued for deletion, or ignored entirely; which of these are appropriate will vary, though objects
|
||||
should be pooled or queued whenever possible. The full list of qdel hints are in the code for the qdel proc.
|
||||
|
||||
As mentioned above, gcDestroyed can be used to tell whether an object is being destroyed. This is important, because if
|
||||
an object is being destroyed, it WILL still exist, and any code that references it should stop referencing it as soon as
|
||||
possible. In the same places that you check whether a reference still exists, you should also check for something like
|
||||
isnull(myRefVar.gcDestroyed), which will be false if your object is being destroyed, meaning you should throw out the
|
||||
reference immediately.
|
||||
|
||||
The inner workings of the GC itself and the stuff related to testing it probably don't need a detailed description - if
|
||||
you intend to work with them, it would be best to read the code to understand what they do.
|
||||
|
||||
*/
|
||||
@@ -1,185 +0,0 @@
|
||||
// Main garbage collection code
|
||||
|
||||
// For general information about how the GC works and how to use it, see __gc_info.dm
|
||||
|
||||
#define GC_COLLECTIONS_PER_TICK 150 // Was 100.
|
||||
#define GC_COLLECTION_TIMEOUT (30 SECONDS)
|
||||
#define GC_FORCE_DEL_PER_TICK 30
|
||||
//#define GC_DEBUG
|
||||
|
||||
var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
|
||||
|
||||
var/list/noqdelhint = list() // list of all types that do not return a QDEL_HINT
|
||||
|
||||
// The time a datum was destroyed by the GC, or null if it hasn't been
|
||||
/datum/var/gcDestroyed
|
||||
// Whether a datum was hard-deleted by the GC; 0 if not, 1 if it was queued, -1 if directly deleted
|
||||
/datum/var/hard_deleted = 0
|
||||
|
||||
|
||||
|
||||
/datum/controller/process/garbage_collector
|
||||
var/list/queue = new
|
||||
var/del_everything = 0
|
||||
|
||||
// To let them know how hardworking am I :^).
|
||||
var/dels_count = 0
|
||||
var/hard_dels = 0
|
||||
var/soft_dels = 0
|
||||
|
||||
// all types that did not respect qdel(A, force=TRUE) and returned one
|
||||
// of the immortality qdel hints
|
||||
var/list/noforcerespect = list()
|
||||
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/addTrash(var/datum/D)
|
||||
if(!istype(D) || del_everything)
|
||||
del(D)
|
||||
hard_dels++
|
||||
dels_count++
|
||||
return
|
||||
|
||||
queue -= "\ref[D]" // If this is a re-used ref, remove the old ref from the queue
|
||||
queue["\ref[D]"] = world.time
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/processGarbage()
|
||||
var/remainingCollectionPerTick = GC_COLLECTIONS_PER_TICK
|
||||
var/remainingForceDelPerTick = GC_FORCE_DEL_PER_TICK
|
||||
var/collectionTimeScope = world.time - GC_COLLECTION_TIMEOUT
|
||||
while(queue.len && --remainingCollectionPerTick >= 0)
|
||||
var/refID = queue[1]
|
||||
var/destroyedAtTime = queue[refID]
|
||||
|
||||
if(destroyedAtTime > collectionTimeScope)
|
||||
break
|
||||
|
||||
var/datum/D = locate(refID)
|
||||
// If the object still exists, and it's the same object, hard del it
|
||||
if(D && D.gcDestroyed == destroyedAtTime)
|
||||
if(remainingForceDelPerTick <= 0)
|
||||
break
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
gcwarning("GC process force delete [D.type]")
|
||||
#endif
|
||||
|
||||
hardDel(D)
|
||||
queue.Cut(1, 2)
|
||||
|
||||
remainingForceDelPerTick--
|
||||
else // Otherwise, it was GC'd - remove it from the queue
|
||||
queue.Cut(1, 2)
|
||||
soft_dels++
|
||||
dels_count++
|
||||
SCHECK
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
#undef GC_DEBUG
|
||||
#endif
|
||||
|
||||
#undef GC_FORCE_DEL_PER_TICK
|
||||
#undef GC_COLLECTION_TIMEOUT
|
||||
#undef GC_COLLECTIONS_PER_TICK
|
||||
|
||||
/datum/controller/process/garbage_collector/proc/hardDel(var/datum/D)
|
||||
didntgc["[D.type]"]++
|
||||
D.hard_deleted = 1
|
||||
if(!D.gcDestroyed)
|
||||
spawn(-1)
|
||||
D.Destroy()
|
||||
D.gcDestroyed = world.time
|
||||
del(D)
|
||||
hard_dels++
|
||||
dels_count++
|
||||
|
||||
// Effectively replaces del for any datum-based type
|
||||
/proc/qdel(datum/D, force=FALSE)
|
||||
if(isnull(D))
|
||||
return
|
||||
|
||||
if(!istype(D)) // A non-datum was passed into qdel - just delete it outright.
|
||||
// warning("qdel() passed object of type [D.type]. qdel() can only handle /datum/ types.")
|
||||
del(D)
|
||||
return
|
||||
|
||||
if(isnull(garbageCollector))
|
||||
D.Destroy()
|
||||
del(D)
|
||||
return
|
||||
|
||||
if(isnull(D.gcDestroyed))
|
||||
// Let our friend know they're about to get fucked up.
|
||||
var/hint
|
||||
D.gcDestroyed = world.time
|
||||
try
|
||||
hint = D.Destroy(force)
|
||||
catch(var/exception/e)
|
||||
if(istype(e))
|
||||
log_runtime(e, D, "Caught by qdel() destroying [D.type]")
|
||||
else
|
||||
gcwarning("qdel() caught runtime destroying [D.type]: [e]")
|
||||
// Destroy runtimed? Panic! Hard delete!
|
||||
D.hard_deleted = -1
|
||||
del(D)
|
||||
if(garbageCollector)
|
||||
garbageCollector.dels_count++
|
||||
return
|
||||
if(!isnull(D.gcDestroyed) && D.gcDestroyed != world.time)
|
||||
gcwarning("Sleep detected in Destroy() call of [D.type]")
|
||||
sleptDestroy["[D.type]"]++
|
||||
D.gcDestroyed = world.time
|
||||
|
||||
switch(hint)
|
||||
if(QDEL_HINT_QUEUE) //qdel should queue the object for deletion
|
||||
garbageCollector.addTrash(D)
|
||||
if (QDEL_HINT_LETMELIVE, QDEL_HINT_IWILLGC) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
return
|
||||
// Returning LETMELIVE after being told to force destroy
|
||||
// indicates the objects Destroy() does not respect force
|
||||
if(!("[D.type]" in garbageCollector.noforcerespect))
|
||||
garbageCollector.noforcerespect += "[D.type]"
|
||||
gcwarning("WARNING: [D.type] has been force deleted, but is \
|
||||
returning an immortal QDEL_HINT, indicating it does \
|
||||
not respect the force flag for qdel(). It has been \
|
||||
placed in the queue, further instances of this type \
|
||||
will also be queued.")
|
||||
garbageCollector.addTrash(D)
|
||||
if(QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
D.hard_deleted = -1 // -1 means "this hard del skipped the queue", used for profiling
|
||||
del(D)
|
||||
if(garbageCollector)
|
||||
garbageCollector.dels_count++
|
||||
if(QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
|
||||
#ifdef TESTING
|
||||
D.find_references(remove_from_queue = FALSE)
|
||||
#endif
|
||||
garbageCollector.addTrash(D)
|
||||
else
|
||||
if(!noqdelhint["[D.type]"])
|
||||
noqdelhint["[D.type]"] = "[D.type]"
|
||||
gcwarning("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
|
||||
garbageCollector.addTrash(D)
|
||||
|
||||
|
||||
// Returns 1 if the object has been queued for deletion.
|
||||
/proc/qdeleted(var/datum/D)
|
||||
if(!istype(D))
|
||||
return 0
|
||||
if(!isnull(D.gcDestroyed))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
* Like Del(), but for qdel.
|
||||
* Called BEFORE qdel moves shit.
|
||||
*/
|
||||
/datum/proc/Destroy(force=FALSE)
|
||||
tag = null
|
||||
return QDEL_HINT_QUEUE // Garbage Collect everything.
|
||||
|
||||
/proc/gcwarning(msg)
|
||||
log_to_dd("## GC WARNING: [msg]")
|
||||
log_runtime(EXCEPTION(msg))
|
||||
@@ -1,105 +0,0 @@
|
||||
// Garbage collection testing/debugging/profiling code
|
||||
|
||||
/client/proc/qdel_toggle()
|
||||
set name = "(GC) Toggle Queueing"
|
||||
set desc = "Toggle qdel usage between normal and force del()."
|
||||
set category = "Debug"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
garbageCollector.del_everything = !garbageCollector.del_everything
|
||||
// to_chat(world, "<b>GC: qdel turned [garbageCollector.del_everything ? "off" : "on"].</b>")
|
||||
log_admin("[key_name(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].</span>", 1)
|
||||
|
||||
/client/proc/cmd_display_del_log()
|
||||
set category = "Debug"
|
||||
set name = "(GC) Display del() Log"
|
||||
set desc = "Displays a list of things that have failed to GC this round"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/dat = "<B>List of things that failed to GC this round</B><BR><BR>"
|
||||
for(var/path in didntgc)
|
||||
dat += "[path] - [didntgc[path]] times<BR>"
|
||||
|
||||
dat += "<B>List of paths that did not return a qdel hint in Destroy()</B><BR><BR>"
|
||||
for(var/path in noqdelhint)
|
||||
dat += "[path]<BR>"
|
||||
|
||||
dat += "<B>List of paths that slept in Destroy()</B><BR><BR>"
|
||||
for(var/path in sleptDestroy)
|
||||
dat += "[path]<BR>"
|
||||
|
||||
usr << browse(dat, "window=dellog")
|
||||
|
||||
#ifdef TESTING
|
||||
/client/var/running_find_references
|
||||
/datum/var/running_find_references
|
||||
|
||||
/datum/verb/find_references(remove_from_queue = TRUE as num)
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
running_find_references = type
|
||||
if(usr && usr.client)
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
|
||||
running_find_references = null
|
||||
return
|
||||
// Remove this object from the list of things to be auto-deleted.
|
||||
if(remove_from_queue && garbageCollector && ("\ref[src]" in garbageCollector.queue))
|
||||
garbageCollector.queue -= "\ref[src]"
|
||||
if(usr && usr.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
testing("Beginning search for references to a [type].")
|
||||
var/list/things = list()
|
||||
for(var/client/thing)
|
||||
things |= thing
|
||||
for(var/datum/thing)
|
||||
things |= thing
|
||||
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
|
||||
for(var/datum/thing in things)
|
||||
if(usr && usr.client && !usr.client.running_find_references) return
|
||||
for(var/varname in thing.vars)
|
||||
var/variable = thing.vars[varname]
|
||||
if(variable == src)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
|
||||
else if(islist(variable))
|
||||
if(src in variable)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
|
||||
testing("Completed search for references to a [type].")
|
||||
if(usr && usr.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
/client/verb/purge_all_destroyed_objects()
|
||||
set category = "Debug"
|
||||
if(garbageCollector)
|
||||
while(garbageCollector.queue.len)
|
||||
var/datum/o = locate(garbageCollector.queue[1])
|
||||
if(istype(o) && !isnull(o.gcDestroyed))
|
||||
del(o)
|
||||
garbageCollector.dels_count++
|
||||
garbageCollector.queue.Cut(1, 2)
|
||||
|
||||
/datum/verb/qdel_then_find_references()
|
||||
set category = "Debug"
|
||||
set name = "qdel() then Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
qdel(src)
|
||||
if(!running_find_references)
|
||||
find_references(remove_from_queue = FALSE)
|
||||
#endif
|
||||
@@ -131,7 +131,7 @@
|
||||
awakening = 1
|
||||
|
||||
spawn(30)
|
||||
if(!qdeleted(src))
|
||||
if(!QDELETED(src))
|
||||
var/mob/living/simple_animal/hostile/killertomato/K = new /mob/living/simple_animal/hostile/killertomato(get_turf(loc))
|
||||
K.maxHealth += round(seed.endurance / 3)
|
||||
K.melee_damage_lower += round(seed.potency / 10)
|
||||
|
||||
@@ -427,7 +427,7 @@
|
||||
|
||||
/obj/structure/closet/stasis/process()
|
||||
if(holder_animal)
|
||||
if(holder_animal.stat == DEAD && !qdeleted(holder_animal))
|
||||
if(holder_animal.stat == DEAD && !QDELETED(holder_animal))
|
||||
dump_contents()
|
||||
holder_animal.gib()
|
||||
return
|
||||
@@ -455,7 +455,7 @@
|
||||
L.disabilities &= ~MUTE
|
||||
L.status_flags &= ~GODMODE
|
||||
L.notransform = 0
|
||||
if(holder_animal && !qdeleted(holder_animal))
|
||||
if(holder_animal && !QDELETED(holder_animal))
|
||||
holder_animal.mind.transfer_to(L)
|
||||
L.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/exit_possession)
|
||||
if(kill || !isanimal(loc))
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
var/datum/middleClickOverride/middleClickOverride = null
|
||||
|
||||
/mob/living/carbon/Destroy()
|
||||
// This clause is here due to items falling off from limb deletion
|
||||
for(var/obj/item in get_all_slots())
|
||||
unEquip(item)
|
||||
qdel(item)
|
||||
QDEL_LIST(internal_organs)
|
||||
QDEL_LIST(stomach_contents)
|
||||
var/mob/living/simple_animal/borer/B = has_brain_worms()
|
||||
|
||||
@@ -237,10 +237,15 @@
|
||||
/mob/living/carbon/human/interactive/proc/shitcurity(obj)
|
||||
var/list/allContents = getAllContents()
|
||||
|
||||
if(retal && TARGET && Adjacent(TARGET))
|
||||
var/obj/item/weapon/restraints/R = locate() in allContents
|
||||
if(R)
|
||||
R.attack(TARGET, src) // go go bluespace restraint launcher!
|
||||
if(retal && TARGET)
|
||||
if(Adjacent(TARGET))
|
||||
var/obj/item/weapon/restraints/R = locate() in allContents
|
||||
if(R)
|
||||
R.attack(TARGET, src) // go go bluespace restraint launcher!
|
||||
else if(TARGET in oview(7, src))
|
||||
var/obj/item/weapon/gun/G = locate() in allContents
|
||||
if(G)
|
||||
G.afterattack(TARGET, src)
|
||||
|
||||
/mob/living/carbon/human/interactive/proc/clowning(obj)
|
||||
if(shouldModulePass())
|
||||
|
||||
@@ -556,8 +556,12 @@
|
||||
var/obj/item/I = get_item_by_slot(pick(slots))
|
||||
var/obj/item/weapon/storage/BP = get_item_by_slot(slot_back)
|
||||
if(back && BP && I)
|
||||
// hack to allow SNPCs to "sticky grab" items without losing their inventorying
|
||||
var/oldnodrop = I.flags | NODROP
|
||||
I.flags &= ~NODROP
|
||||
if(BP.can_be_inserted(I))
|
||||
BP.handle_item_insertion(I)
|
||||
I.flags |= oldnodrop
|
||||
else
|
||||
unEquip(I,TRUE)
|
||||
update_hands = 1
|
||||
|
||||
@@ -993,7 +993,7 @@
|
||||
return 1
|
||||
|
||||
/mob/living/proc/harvest(mob/living/user)
|
||||
if(qdeleted(src))
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(butcher_results)
|
||||
for(var/path in butcher_results)
|
||||
|
||||
@@ -1236,7 +1236,7 @@ var/list/ai_verbs_default = list(
|
||||
malfhacking = 0
|
||||
clear_alert("hackingapc")
|
||||
|
||||
if(!istype(apc) || qdeleted(apc) || apc.stat & BROKEN)
|
||||
if(!istype(apc) || QDELETED(apc) || apc.stat & BROKEN)
|
||||
to_chat(src, "<span class='danger'>Hack aborted. The designated APC no longer exists on the power network.</span>")
|
||||
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1)
|
||||
else if(apc.aidisabled)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
death()
|
||||
return 0
|
||||
|
||||
if(!eyeobj || qdeleted(eyeobj) || !eyeobj.loc)
|
||||
if(!eyeobj || QDELETED(eyeobj) || !eyeobj.loc)
|
||||
view_core()
|
||||
|
||||
if(machine)
|
||||
|
||||
@@ -118,6 +118,14 @@
|
||||
C.toff = 1
|
||||
..()
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/silicon/pai/movement_delay()
|
||||
. = ..()
|
||||
. += slowdown
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
req_access = list(access_engine, access_robotics)
|
||||
ventcrawler = 2
|
||||
magpulse = 1
|
||||
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
default_language = "Drone"
|
||||
|
||||
// We need to keep track of a few module items so we don't need to do list operations
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
|
||||
/mob/living/silicon/robot/unEquip(obj/item/I)
|
||||
if(I == module_active)
|
||||
deselect_module(get_selected_module())
|
||||
uneq_active(I)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/proc/update_module_icon()
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
var/obj/item/stack/S = O
|
||||
|
||||
if(!S)
|
||||
modules -= null
|
||||
S = new T(src)
|
||||
modules += S
|
||||
S.amount = 1
|
||||
@@ -124,10 +123,10 @@
|
||||
module_type = "Medical"
|
||||
subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor)
|
||||
stacktypes = list(
|
||||
/obj/item/stack/medical/bruise_pack/advanced = 5,
|
||||
/obj/item/stack/medical/ointment/advanced = 5,
|
||||
/obj/item/stack/medical/splint = 5,
|
||||
/obj/item/stack/nanopaste = 5
|
||||
/obj/item/stack/medical/bruise_pack/advanced = 6,
|
||||
/obj/item/stack/medical/ointment/advanced = 6,
|
||||
/obj/item/stack/medical/splint = 6,
|
||||
/obj/item/stack/nanopaste = 6
|
||||
)
|
||||
|
||||
/obj/item/weapon/robot_module/medical/New()
|
||||
|
||||
@@ -224,10 +224,11 @@
|
||||
/mob/living/silicon/proc/remove_med_sec_hud()
|
||||
var/datum/atom_hud/secsensor = huds[sec_hud]
|
||||
var/datum/atom_hud/medsensor = huds[med_hud]
|
||||
var/datum/atom_hud/diagsensor = huds[d_hud]
|
||||
for(var/datum/atom_hud/data/diagnostic/diagsensor in huds)
|
||||
diagsensor.remove_hud_from(src)
|
||||
secsensor.remove_hud_from(src)
|
||||
medsensor.remove_hud_from(src)
|
||||
diagsensor.remove_hud_from(src)
|
||||
|
||||
|
||||
/mob/living/silicon/proc/add_sec_hud()
|
||||
var/datum/atom_hud/secsensor = huds[sec_hud]
|
||||
|
||||
@@ -35,15 +35,15 @@
|
||||
icon_living = const_type
|
||||
icon_state = const_type
|
||||
else
|
||||
name = "[ticker.mode.cultdat.get_name(const_type)] ([rand(1, 1000)])"
|
||||
real_name = ticker.mode.cultdat.get_name(const_type)
|
||||
icon_living = ticker.mode.cultdat.get_icon(const_type)
|
||||
icon_state = ticker.mode.cultdat.get_icon(const_type)
|
||||
name = "[ticker.cultdat.get_name(const_type)] ([rand(1, 1000)])"
|
||||
real_name = ticker.cultdat.get_name(const_type)
|
||||
icon_living = ticker.cultdat.get_icon(const_type)
|
||||
icon_state = ticker.cultdat.get_icon(const_type)
|
||||
|
||||
for(var/spell in construct_spells)
|
||||
AddSpell(new spell(null))
|
||||
|
||||
if(ticker.mode.cultdat.theme == "blood")
|
||||
if(ticker.cultdat.theme == "blood")
|
||||
updateglow()
|
||||
|
||||
/mob/living/simple_animal/hostile/construct/examine(mob/user)
|
||||
|
||||
@@ -218,7 +218,7 @@ Difficulty: Medium
|
||||
fire_rain()
|
||||
|
||||
icon_state = "dragon"
|
||||
if(swoop_target && !qdeleted(swoop_target) && swoop_target.z == src.z)
|
||||
if(swoop_target && !QDELETED(swoop_target) && swoop_target.z == src.z)
|
||||
tturf = get_turf(swoop_target)
|
||||
else
|
||||
tturf = get_turf(src)
|
||||
@@ -233,7 +233,7 @@ Difficulty: Medium
|
||||
L.gib()
|
||||
else
|
||||
L.adjustBruteLoss(75)
|
||||
if(L && !qdeleted(L)) // Some mobs are deleted on death
|
||||
if(L && !QDELETED(L)) // Some mobs are deleted on death
|
||||
var/throw_dir = get_dir(src, L)
|
||||
if(L.loc == loc)
|
||||
throw_dir = pick(alldirs)
|
||||
|
||||
@@ -446,7 +446,7 @@ Difficulty: Hard
|
||||
if(!currently_seeking)
|
||||
currently_seeking = TRUE
|
||||
targetturf = get_turf(target)
|
||||
while(target && src && !qdeleted(src) && currently_seeking && x && y && targetturf) //can this target actually be sook out
|
||||
while(target && src && !QDELETED(src) && currently_seeking && x && y && targetturf) //can this target actually be sook out
|
||||
if(!moving) //we're out of tiles to move, find more and where the target is!
|
||||
more_previouser_moving_dir = previous_moving_dir
|
||||
previous_moving_dir = moving_dir
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define TS_DESC_WHITE "White - Infect"
|
||||
#define TS_DESC_BLACK "Black - Poison"
|
||||
#define TS_DESC_PURPLE "Purple - Guard"
|
||||
#define TS_DESC_BROWN "Brown - Breacher"
|
||||
#define TS_DESC_PRINCE "Prince - WAR"
|
||||
#define TS_DESC_MOTHER "Mother - HORROR"
|
||||
#define TS_DESC_QUEEN "Queen - LEADER"
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
if(thick)
|
||||
W.opacity = 1
|
||||
W.name = "thick terror web"
|
||||
W.health = W.health * 2
|
||||
if(web_infects)
|
||||
W.infectious = 1
|
||||
W.name = "sharp terror web"
|
||||
@@ -251,17 +252,32 @@
|
||||
stop_automated_movement = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/DoVentSmash()
|
||||
var/valid_target = FALSE
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/P in range(1, get_turf(src)))
|
||||
if(P.welded)
|
||||
valid_target = TRUE
|
||||
for(var/obj/machinery/atmospherics/unary/vent_scrubber/C in range(1, get_turf(src)))
|
||||
if(C.welded)
|
||||
valid_target = TRUE
|
||||
if(!valid_target)
|
||||
to_chat(src, "<span class='warning'>No welded vent or scrubber nearby!</span>")
|
||||
return
|
||||
playsound(get_turf(src), 'sound/machines/airlock_alien_prying.ogg', 50, 0)
|
||||
if(do_after(src, 40, target = loc))
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/P in view(1, src))
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/P in range(1, get_turf(src)))
|
||||
if(P.welded)
|
||||
P.welded = 0
|
||||
P.update_icon()
|
||||
P.update_pipe_image()
|
||||
forceMove(P.loc)
|
||||
P.visible_message("<span class='danger'>[src] smashes the welded cover off [P]!</span>")
|
||||
return
|
||||
for(var/obj/machinery/atmospherics/unary/vent_scrubber/C in view(1, src))
|
||||
for(var/obj/machinery/atmospherics/unary/vent_scrubber/C in range(1, get_turf(src)))
|
||||
if(C.welded)
|
||||
C.welded = 0
|
||||
C.update_icon()
|
||||
C.update_pipe_image()
|
||||
forceMove(C.loc)
|
||||
C.visible_message("<span class='danger'>[src] smashes the welded cover off [C]!</span>")
|
||||
return
|
||||
to_chat(src, "<span class='danger'>There is no welded vent or scrubber close enough to do this.</span>")
|
||||
@@ -39,9 +39,8 @@
|
||||
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
|
||||
L.attack_animal(src)
|
||||
if(!ckey && (!(target in enemies) || L.reagents.has_reagent("terror_black_toxin", 60)))
|
||||
step_away(src,L)
|
||||
step_away(src,L)
|
||||
step_away(src, L)
|
||||
step_away(src, L)
|
||||
LoseTarget()
|
||||
for(var/i in 0 to 3)
|
||||
step_away(src, L)
|
||||
visible_message("<span class='notice'>[src] warily eyes [L] from a distance.</span>")
|
||||
step_away(src, L)
|
||||
visible_message("<span class='notice'>[src] jumps away from [L]!</span>")
|
||||
@@ -0,0 +1,34 @@
|
||||
// --------------------------------------------------------------------------------
|
||||
// ----------------- TERROR SPIDERS: T2 BROWN TERROR ------------------------------
|
||||
// --------------------------------------------------------------------------------
|
||||
// -------------: ROLE: breaking vents
|
||||
// -------------: AI: ventcrawls a lot, breaks open vents
|
||||
// -------------: SPECIAL: ventsmash
|
||||
// -------------: TO FIGHT IT: blast it before it can get away!
|
||||
// -------------: SPRITES FROM: IK3I
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/brown
|
||||
name = "Brown Terror spider"
|
||||
desc = "An ominous-looking spider, colored brown like the dirt it crawled out of. Its forearms have sharp digging claws."
|
||||
spider_role_summary = "Vent-breaking spider that breaches into new areas."
|
||||
ai_target_method = TS_DAMAGE_BRUTE
|
||||
icon_state = "terror_brown"
|
||||
icon_living = "terror_brown"
|
||||
icon_dead = "terror_brown_dead"
|
||||
maxHealth = 120 // Low
|
||||
health = 120
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 30
|
||||
move_to_delay = 20 // Slow.
|
||||
spider_opens_doors = 2 // Breach specialist.
|
||||
environment_smash = ENVIRONMENT_SMASH_RWALLS // Breaks anything.
|
||||
spider_tier = TS_TIER_2
|
||||
ai_ventbreaker = 1
|
||||
freq_ventcrawl_combat = 600 // Ventcrawls very frequently, breaking open vents as it goes.
|
||||
freq_ventcrawl_idle = 1800
|
||||
var/datum/action/innate/terrorspider/ventsmash/ventsmash_action
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/brown/New()
|
||||
..()
|
||||
ventsmash_action = new()
|
||||
ventsmash_action.Grant(src)
|
||||
@@ -31,7 +31,7 @@
|
||||
return
|
||||
if(error_on_humanize == "")
|
||||
var/spider_ask = alert(humanize_prompt, "Join as Terror Spider?", "Yes", "No")
|
||||
if(spider_ask == "No" || !src || qdeleted(src))
|
||||
if(spider_ask == "No" || !src || QDELETED(src))
|
||||
return
|
||||
else
|
||||
to_chat(user, "Cannot inhabit spider: [error_on_humanize]")
|
||||
|
||||
@@ -29,19 +29,16 @@
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/gray/spider_specialattack(mob/living/carbon/human/L, poisonable)
|
||||
if(!poisonable)
|
||||
..()
|
||||
return
|
||||
if(L.silent >= 10)
|
||||
L.attack_animal(src)
|
||||
var/obj/structure/spider/terrorweb/W = locate() in get_turf(L)
|
||||
if(W)
|
||||
melee_damage_lower = initial(melee_damage_lower) * 2
|
||||
melee_damage_upper = initial(melee_damage_upper) * 2
|
||||
visible_message("<span class='danger'>[src] savagely mauls [target] while they are stuck in the web!</span>")
|
||||
else
|
||||
var/inject_target = pick("chest","head")
|
||||
if(L.stunned || L.can_inject(null,0,inject_target,0))
|
||||
L.Silence(20) // instead of having a venom that only lasts seconds, we just add the silence directly.
|
||||
visible_message("<span class='danger'>[src] buries grey fangs deep into the [inject_target] of [target]!</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
|
||||
L.attack_animal(src)
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
visible_message("<span class='danger'>[src] bites [target]!</span>")
|
||||
L.attack_animal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/gray/adjustBruteLoss(damage)
|
||||
..(damage)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
ventcrawler = 0
|
||||
ai_ventcrawls = 0
|
||||
environment_smash = ENVIRONMENT_SMASH_RWALLS
|
||||
loot = list(/obj/item/clothing/accessory/medal)
|
||||
idle_ventcrawl_chance = 0
|
||||
spider_tier = TS_TIER_3
|
||||
spider_opens_doors = 2
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
health = 200
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 25
|
||||
move_to_delay = 6
|
||||
move_to_delay = 4
|
||||
spider_tier = TS_TIER_2
|
||||
spider_opens_doors = 2
|
||||
ventcrawler = 0
|
||||
@@ -30,7 +30,12 @@
|
||||
var/dcheck_counter = 0
|
||||
var/queen_visible = 1
|
||||
var/cycles_noqueen = 0
|
||||
var/datum/action/innate/terrorspider/thickweb/thickweb_action
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/purple/New()
|
||||
..()
|
||||
thickweb_action = new()
|
||||
thickweb_action.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/purple/death(gibbed)
|
||||
if(spider_myqueen)
|
||||
@@ -43,14 +48,6 @@
|
||||
visible_message("<span class='notice'>[src] chitters in the direction of [Q]!</span>")
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/purple/spider_specialattack(mob/living/carbon/human/L, poisonable)
|
||||
if(cycles_noqueen < 6 && prob(10))
|
||||
visible_message("<span class='danger'>[src] rams into [L], knocking them to the floor!</span>")
|
||||
L.Weaken(5)
|
||||
L.Stun(5)
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/purple/Life()
|
||||
. = ..()
|
||||
if(.) // if mob is NOT dead
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// -------------: AI: builds a nest, lays many eggs, attempts to take over the station
|
||||
// -------------: SPECIAL: spins webs, breaks lights, breaks cameras, webs objects, lays eggs, commands other spiders...
|
||||
// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs and/or decloner guns are a very good idea.
|
||||
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
|
||||
// -------------: SPRITES FROM: IK3I
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen
|
||||
name = "Queen of Terror spider"
|
||||
@@ -23,17 +23,6 @@
|
||||
melee_damage_upper = 20
|
||||
move_to_delay = 15 // yeah, this is very slow, but
|
||||
ventcrawler = 1
|
||||
var/spider_spawnfrequency = 1200 // 120 seconds
|
||||
var/spider_spawnfrequency_stable = 1200 // 120 seconds. Spawnfrequency is set to this on ai spiders once nest setup is complete.
|
||||
var/spider_lastspawn = 0
|
||||
var/nestfrequency = 150 // 15 seconds
|
||||
var/lastnestsetup = 0
|
||||
var/neststep = 0
|
||||
var/hasnested = 0
|
||||
var/spider_max_per_nest = 25 // above this, AI queens become stable
|
||||
var/canlay = 4 // main counter for egg-laying ability! # = num uses, incremented at intervals
|
||||
var/eggslaid = 0
|
||||
var/spider_can_fakelings = 3 // spawns defective spiderlings that don't grow up, used to freak out crew, atmosphere
|
||||
idle_ventcrawl_chance = 0
|
||||
force_threshold = 18 // outright immune to anything of force under 18, this means welders can't hurt it, only guns can
|
||||
ranged = 1
|
||||
@@ -43,7 +32,18 @@
|
||||
projectiletype = /obj/item/projectile/terrorqueenspit
|
||||
spider_tier = TS_TIER_4
|
||||
spider_opens_doors = 2
|
||||
loot = list(/obj/item/clothing/accessory/medal)
|
||||
var/spider_spawnfrequency = 1200 // 120 seconds
|
||||
var/spider_spawnfrequency_stable = 1200 // 120 seconds. Spawnfrequency is set to this on ai spiders once nest setup is complete.
|
||||
var/spider_lastspawn = 0
|
||||
var/nestfrequency = 300 // 30 seconds
|
||||
var/lastnestsetup = 0
|
||||
var/neststep = 0
|
||||
var/hasnested = 0
|
||||
var/spider_max_per_nest = 25 // above this, AI queens become stable
|
||||
var/canlay = 4 // main counter for egg-laying ability! # = num uses, incremented at intervals
|
||||
var/eggslaid = 0
|
||||
var/spider_can_fakelings = 3 // spawns defective spiderlings that don't grow up, used to freak out crew, atmosphere
|
||||
var/list/spider_types_standard = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green, /mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
var/datum/action/innate/terrorspider/queen/queennest/queennest_action
|
||||
var/datum/action/innate/terrorspider/queen/queensense/queensense_action
|
||||
var/datum/action/innate/terrorspider/queen/queeneggs/queeneggs_action
|
||||
@@ -98,12 +98,18 @@
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist)
|
||||
T.enemies |= enemies
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full()
|
||||
var/numspiders = CountSpiders()
|
||||
if(numspiders >= spider_max_per_nest)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/handle_automated_action()
|
||||
..()
|
||||
if(!stat && !ckey && AIStatus != AI_OFF && !target && !path_to_vent)
|
||||
switch(neststep)
|
||||
if(0)
|
||||
// we have no nest :(
|
||||
// No nest. If current location is eligible for nesting, advance to step 1.
|
||||
var/ok_to_nest = 1
|
||||
var/area/new_area = get_area(loc)
|
||||
if(new_area)
|
||||
@@ -140,81 +146,60 @@
|
||||
qdel(src)
|
||||
new /obj/effect/portal(get_turf(loc))
|
||||
if(1)
|
||||
// No nest, and we should create one. Start NestMode(), then advance to step 2.
|
||||
if(world.time > (lastnestsetup + nestfrequency))
|
||||
lastnestsetup = world.time
|
||||
neststep = 2
|
||||
NestMode()
|
||||
if(2)
|
||||
// Create initial pair of purple nest guards.
|
||||
if(world.time > (lastnestsetup + nestfrequency))
|
||||
lastnestsetup = world.time
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2, 0)
|
||||
neststep = 3
|
||||
if(3)
|
||||
// Create spiders (random T1 types) until nest is full.
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
if(prob(20))
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/N = locate() in get_turf(src)
|
||||
if(!N)
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, 2, 0)
|
||||
if(ai_nest_is_full())
|
||||
if(spider_awaymission)
|
||||
spider_spawnfrequency = spider_spawnfrequency_stable
|
||||
neststep = 4
|
||||
else
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/N = locate() in get_turf(src)
|
||||
if(!N)
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(pick(spider_types_standard), 2, 0)
|
||||
if(4)
|
||||
// Nest should be full. If so, pulse attack command. Otherwise, start replenishing nest (stage 5).
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
if(prob(20))
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/N = locate() in get_turf(src)
|
||||
if(!N)
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 2, 1)
|
||||
if(ai_nest_is_full())
|
||||
SetHiveCommand(0, 15) // AI=0 (attack everyone), ventcrawl=15%/tick
|
||||
else
|
||||
neststep = 5
|
||||
if(5)
|
||||
// If already replenished, go idle (stage 4). Otherwise, replenish nest.
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
if(prob(20))
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/N = locate() in get_turf(src)
|
||||
if(!N)
|
||||
if(!spider_awaymission)
|
||||
QueenFakeLings()
|
||||
spider_lastspawn = world.time
|
||||
if(prob(33))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, 2, 1)
|
||||
else if(prob(50))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 2, 1)
|
||||
else
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, 2, 1)
|
||||
var/spidercount = CountSpiders()
|
||||
if(spidercount >= spider_max_per_nest) // station overwhelmed!
|
||||
neststep = 6
|
||||
if(6)
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
spider_lastspawn = world.time
|
||||
// go hostile, EXTERMINATE MODE.
|
||||
SetHiveCommand(0, 15) // AI=0 (attack everyone), ventcrawl=15%/tick
|
||||
var/numspiders = CountSpiders()
|
||||
if(numspiders < spider_max_per_nest)
|
||||
if(prob(33))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, 2, 1)
|
||||
else if(prob(50))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 2, 0)
|
||||
if(ai_nest_is_full())
|
||||
neststep = 4
|
||||
else
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, 2, 1)
|
||||
else if(spider_awaymission)
|
||||
neststep = 7
|
||||
spider_spawnfrequency = spider_spawnfrequency_stable
|
||||
// if we're an away mission queen... don't keep spawning spiders at high rates. Away team should have a chance.
|
||||
if(7)
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
spider_lastspawn = world.time
|
||||
var/numspiders = CountSpiders()
|
||||
if(numspiders < spider_max_per_nest)
|
||||
// someone is killing my children...
|
||||
if(prob(25))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, 2, 1)
|
||||
else if(prob(33))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 2, 0)
|
||||
else if(prob(50))
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, 2, 1)
|
||||
else
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2, 0)
|
||||
neststep = 6
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/N = locate() in get_turf(src)
|
||||
if(!N)
|
||||
spider_lastspawn = world.time
|
||||
var/num_purple = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple)
|
||||
var/num_white = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/white)
|
||||
var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown)
|
||||
if(num_purple < 4)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2, 0)
|
||||
else if(num_white < 2)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2, 0)
|
||||
else if(num_brown < 2)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 2, 0)
|
||||
else
|
||||
DoLayTerrorEggs(pick(spider_types_standard), 2, 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestPrompt()
|
||||
var/confirm = alert(src, "Are you sure you want to nest? You will be able to lay eggs, and smash walls, but not ventcrawl.","Nest?","Yes","No")
|
||||
@@ -264,13 +249,15 @@
|
||||
to_chat(src, "<span class='danger'>Too soon to attempt that again. Wait just a few more seconds...</span>")
|
||||
return
|
||||
var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE)
|
||||
if(canlay >= 4)
|
||||
eggtypes |= TS_DESC_BROWN
|
||||
if(canlay >= 12)
|
||||
eggtypes |= TS_DESC_MOTHER
|
||||
eggtypes |= TS_DESC_PRINCE
|
||||
var num_purples = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple)
|
||||
var/num_purples = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple)
|
||||
if(num_purples >= 2)
|
||||
eggtypes -= TS_DESC_PURPLE
|
||||
var num_blacks = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
var/num_blacks = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
if(num_blacks >= 2)
|
||||
eggtypes -= TS_DESC_BLACK
|
||||
var/eggtype = input("What kind of eggs?") as null|anything in eggtypes
|
||||
@@ -288,6 +275,13 @@
|
||||
canlay -= 12
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, 1, 0)
|
||||
return
|
||||
else if(eggtype == TS_DESC_BROWN)
|
||||
if(canlay < 4)
|
||||
to_chat(src, "<span class='danger'>Insufficient strength. It takes as much effort to lay one of those as it does to lay 4 normal eggs.</span>")
|
||||
else
|
||||
canlay -= 4
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 1, 0)
|
||||
return
|
||||
var/numlings = 1
|
||||
if(eggtype != TS_DESC_PURPLE)
|
||||
if(canlay >= 5)
|
||||
@@ -333,9 +327,7 @@
|
||||
for(var/i in 1 to numlings)
|
||||
var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src))
|
||||
S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red
|
||||
S.stillborn = TRUE
|
||||
S.name = "Evil-Looking Spiderling"
|
||||
S.desc = "It moves very quickly, hisses loudly for its size... and has disproportionately large fangs. Hopefully it does not grow up..."
|
||||
S.stillborn = 1
|
||||
if(!spider_can_fakelings)
|
||||
queenfakelings_action.Remove(src)
|
||||
else
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
last_ventcrawl_time = world.time
|
||||
var/vdistance = 99
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(10, src))
|
||||
if(!v.welded)
|
||||
if(!v.welded || ai_ventbreaker)
|
||||
if(get_dist(src,v) < vdistance)
|
||||
entry_vent = v
|
||||
vdistance = get_dist(src,v)
|
||||
@@ -217,7 +217,8 @@
|
||||
var/tgt_dir = get_dir(src,mygoal)
|
||||
for(var/obj/machinery/door/airlock/A in view(1, src))
|
||||
if(A.density)
|
||||
try_open_airlock(A)
|
||||
spawn(0)
|
||||
try_open_airlock(A)
|
||||
for(var/obj/machinery/door/firedoor/F in view(1, src))
|
||||
if(tgt_dir == get_dir(src,F) && F.density && !F.welded)
|
||||
visible_message("<span class='danger'>[src] pries open the firedoor!</span>")
|
||||
@@ -234,9 +235,13 @@
|
||||
if(is_type_in_list(obstacle, valid_obstacles))
|
||||
obstacle.attack_animal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/TSVentCrawlRandom(/var/entry_vent)
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/TSVentCrawlRandom()
|
||||
if(entry_vent)
|
||||
if(get_dist(src, entry_vent) <= 2)
|
||||
if(ai_ventbreaker && entry_vent.welded)
|
||||
entry_vent.welded = 0
|
||||
entry_vent.update_icon()
|
||||
entry_vent.visible_message("<span class='danger'>[src] smashes the welded cover off [entry_vent]!</span>")
|
||||
var/list/vents = list()
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.parent.other_atmosmch)
|
||||
vents.Add(temp_vent)
|
||||
@@ -250,17 +255,23 @@
|
||||
forceMove(exit_vent)
|
||||
var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
|
||||
spawn(travel_time)
|
||||
if(!exit_vent || exit_vent.welded)
|
||||
if(!exit_vent || (exit_vent.welded && !ai_ventbreaker))
|
||||
forceMove(original_location)
|
||||
entry_vent = null
|
||||
return
|
||||
if(prob(50))
|
||||
audible_message("<span class='notice'>You hear something squeezing through the ventilation ducts.</span>")
|
||||
spawn(travel_time)
|
||||
if(!exit_vent || exit_vent.welded)
|
||||
if(!exit_vent || (exit_vent.welded && !ai_ventbreaker))
|
||||
forceMove(original_location)
|
||||
entry_vent = null
|
||||
return
|
||||
if(ai_ventbreaker && exit_vent.welded)
|
||||
exit_vent.welded = 0
|
||||
exit_vent.update_icon()
|
||||
exit_vent.update_pipe_image()
|
||||
exit_vent.visible_message("<span class='danger'>[src] smashes the welded cover off [exit_vent]!</span>")
|
||||
playsound(exit_vent.loc, 'sound/machines/airlock_alien_prying.ogg', 50, 0)
|
||||
forceMove(exit_vent.loc)
|
||||
entry_vent = null
|
||||
var/area/new_area = get_area(loc)
|
||||
|
||||
@@ -49,6 +49,7 @@ var/global/list/ts_spiderling_list = list()
|
||||
var/freq_ventcrawl_combat = 1800 // 3 minutes
|
||||
var/freq_ventcrawl_idle = 9000 // 15 minutes
|
||||
var/last_ventcrawl_time = -9000 // Last time the spider crawled. Used to prevent excessive crawling. Setting to freq*-1 ensures they can crawl once on spawn.
|
||||
var/ai_ventbreaker = 0
|
||||
|
||||
// AI movement tracking
|
||||
var/spider_steps_taken = 0 // leave at 0, its a counter for ai steps taken.
|
||||
@@ -271,6 +272,8 @@ var/global/list/ts_spiderling_list = list()
|
||||
// after 30 seconds, assuming nobody took control of it yet, offer it to ghosts.
|
||||
addtimer(src, "CheckFaction", 150)
|
||||
addtimer(src, "announcetoghosts", 300)
|
||||
var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
U.add_hud_to(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts()
|
||||
if(spider_awaymission)
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
melee_damage_upper = 15
|
||||
move_to_delay = 4
|
||||
spider_tier = TS_TIER_2
|
||||
loot = list(/obj/item/clothing/accessory/medal)
|
||||
web_infects = 1
|
||||
|
||||
|
||||
|
||||
@@ -93,13 +93,13 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles,
|
||||
item_state = "tribble1"
|
||||
w_class = 10
|
||||
var/gestation = 0
|
||||
flags = DROPDEL
|
||||
|
||||
/obj/item/toy/tribble/attack_self(mob/user as mob) //hug that tribble (and play a sound if we add one)
|
||||
..()
|
||||
to_chat(user, "<span class='notice'>You nuzzle the tribble and it trills softly.</span>")
|
||||
|
||||
/obj/item/toy/tribble/dropped(mob/user as mob) //now you can't item form them to get rid of them all so easily
|
||||
..()
|
||||
new /mob/living/simple_animal/tribble(user.loc)
|
||||
for(var/mob/living/simple_animal/tribble/T in user.loc)
|
||||
T.icon_state = src.icon_state
|
||||
@@ -108,7 +108,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles,
|
||||
T.gestation = src.gestation
|
||||
|
||||
to_chat(user, "<span class='notice'>The tribble gets up and wanders around.</span>")
|
||||
qdel(src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/toy/tribble/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) //neutering and un-neutering
|
||||
..()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
AA.viewers -= src
|
||||
viewing_alternate_appearances = null
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
return QDEL_HINT_HARDDEL
|
||||
|
||||
/mob/New()
|
||||
mob_list += src
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
/obj/item/weapon/grab
|
||||
name = "grab"
|
||||
flags = NOBLUDGEON | ABSTRACT
|
||||
flags = NOBLUDGEON | ABSTRACT | DROPDEL
|
||||
var/obj/screen/grab/hud = null
|
||||
var/mob/living/affecting = null
|
||||
var/mob/living/assailant = null
|
||||
@@ -433,10 +433,6 @@
|
||||
|
||||
return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it
|
||||
|
||||
/obj/item/weapon/grab/dropped()
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/item/weapon/grab/Destroy()
|
||||
if(affecting)
|
||||
affecting.pixel_x = 0
|
||||
|
||||
@@ -79,7 +79,9 @@
|
||||
"current_positions" = job.current_positions,
|
||||
"total_positions" = job.total_positions,
|
||||
"can_open" = can_open_job(job),
|
||||
"can_close" = can_close_job(job))))
|
||||
"can_close" = can_close_job(job),
|
||||
"can_prioritize" = can_prioritize_job(job)
|
||||
)))
|
||||
|
||||
return formatted
|
||||
|
||||
@@ -121,6 +123,19 @@
|
||||
return -1
|
||||
return 0
|
||||
|
||||
/datum/computer_file/program/card_mod/proc/can_prioritize_job(datum/job/job)
|
||||
if(job)
|
||||
if(!job_blacklisted(job))
|
||||
if(job in job_master.prioritized_jobs)
|
||||
return 2
|
||||
else
|
||||
if(job_master.prioritized_jobs.len >= 3)
|
||||
return 0
|
||||
if(job.total_positions <= job.current_positions)
|
||||
return 0
|
||||
return 1
|
||||
return -1
|
||||
|
||||
/datum/computer_file/program/card_mod/proc/format_jobs(list/jobs)
|
||||
var/obj/item/weapon/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
|
||||
if(!card_slot || !card_slot.stored_card)
|
||||
@@ -210,6 +225,7 @@
|
||||
//let custom jobs function as an impromptu alt title, mainly for sechuds
|
||||
if(temp_t && modify)
|
||||
modify.assignment = temp_t
|
||||
log_game("[key_name(usr)] has given \"[modify.registered_name]\" the custom job title \"[temp_t]\".")
|
||||
else
|
||||
var/list/access = list()
|
||||
if(is_centcom)
|
||||
@@ -227,6 +243,11 @@
|
||||
|
||||
access = jobdatum.get_access()
|
||||
|
||||
var/jobnamedata = modify.getRankAndAssignment()
|
||||
log_game("[key_name(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".")
|
||||
if(t1 == "Civilian")
|
||||
message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".")
|
||||
|
||||
modify.access = access
|
||||
modify.assignment = t1
|
||||
modify.rank = t1
|
||||
@@ -287,9 +308,11 @@
|
||||
|
||||
if("PRG_terminate")
|
||||
if(is_authenticated(usr))
|
||||
var/jobnamedata = modify.getRankAndAssignment()
|
||||
log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
|
||||
message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
|
||||
modify.assignment = "Terminated"
|
||||
modify.access = list()
|
||||
|
||||
callHook("terminate_employee", list(modify))
|
||||
|
||||
if("PRG_make_job_available")
|
||||
@@ -323,6 +346,26 @@
|
||||
opened_positions[edit_job_target]--
|
||||
log_game("[key_name(usr)] has closed a job slot for job \"[j]\".")
|
||||
|
||||
|
||||
if("PRG_prioritize_job")
|
||||
// TOGGLE WHETHER JOB APPEARS AS PRIORITIZED IN THE LOBBY
|
||||
if(is_authenticated(usr))
|
||||
var/priority_target = href_list["job"]
|
||||
var/datum/job/j = job_master.GetJob(priority_target)
|
||||
if(!j)
|
||||
return 0
|
||||
// Unlike the proper ID computer, this does not check job_in_department
|
||||
var/priority = TRUE
|
||||
if(j in job_master.prioritized_jobs)
|
||||
job_master.prioritized_jobs -= j
|
||||
priority = FALSE
|
||||
else if(job_master.prioritized_jobs.len < 3)
|
||||
job_master.prioritized_jobs += j
|
||||
else
|
||||
return 0
|
||||
log_game("[key_name(usr)] [priority ? "prioritized" : "unprioritized"] the job \"[j.title]\".")
|
||||
playsound(computer.loc, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
if(modify)
|
||||
modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])")
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
|
||||
if(F in stored_files)
|
||||
stored_files -= F
|
||||
qdel(F)
|
||||
recalculate_size()
|
||||
return 1
|
||||
else
|
||||
@@ -119,7 +118,6 @@
|
||||
return null
|
||||
|
||||
/obj/item/weapon/computer_hardware/hard_drive/Destroy()
|
||||
QDEL_LIST(stored_files)
|
||||
stored_files = null
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -30,9 +30,12 @@ var/list/alldepartments = list()
|
||||
/obj/machinery/photocopier/faxmachine/New()
|
||||
..()
|
||||
allfaxes += src
|
||||
update_network()
|
||||
|
||||
if( !(("[department]" in alldepartments) || ("[department]" in admin_departments)) )
|
||||
alldepartments |= department
|
||||
/obj/machinery/photocopier/faxmachine/proc/update_network()
|
||||
if(department != "Unknown")
|
||||
if(!(("[department]" in alldepartments) || ("[department]" in admin_departments) || ("[department]" in hidden_admin_departments)))
|
||||
alldepartments |= department
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/longrange
|
||||
name = "long range fax machine"
|
||||
@@ -224,7 +227,7 @@ var/list/alldepartments = list()
|
||||
|
||||
var/success = 0
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in allfaxes)
|
||||
if( F.department == destination )
|
||||
if(F.department == destination)
|
||||
success = F.receivefax(copyitem)
|
||||
|
||||
if(success)
|
||||
@@ -304,6 +307,9 @@ var/list/alldepartments = list()
|
||||
message_admins(sender, "CENTCOM FAX", destination, rcvdcopy, "#006100")
|
||||
if("Syndicate")
|
||||
message_admins(sender, "SYNDICATE FAX", destination, rcvdcopy, "#DC143C")
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in allfaxes)
|
||||
if(F.department == destination)
|
||||
F.receivefax(copyitem)
|
||||
sendcooldown = cooldown_time
|
||||
spawn(50)
|
||||
visible_message("[src] beeps, \"Message transmitted successfully.\"")
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
/obj/singularity/narsie/large/New()
|
||||
..()
|
||||
icon_state = ticker.mode.cultdat.entity_icon_state
|
||||
name = ticker.mode.cultdat.entity_name
|
||||
icon_state = ticker.cultdat.entity_icon_state
|
||||
name = ticker.cultdat.entity_name
|
||||
to_chat(world, "<font size='15' color='red'><b> [name] HAS RISEN</b></font>")
|
||||
world << pick(sound('sound/hallucinations/im_here1.ogg'), sound('sound/hallucinations/im_here2.ogg'))
|
||||
|
||||
@@ -126,12 +126,12 @@
|
||||
/obj/singularity/narsie/proc/acquire(var/mob/food)
|
||||
if(food == target)
|
||||
return
|
||||
to_chat(target, "<span class='cultlarge'>[uppertext(ticker.mode.cultdat.entity_name)] HAS LOST INTEREST IN YOU</span>")
|
||||
to_chat(target, "<span class='cultlarge'>[uppertext(ticker.cultdat.entity_name)] HAS LOST INTEREST IN YOU</span>")
|
||||
target = food
|
||||
if(ishuman(target))
|
||||
to_chat(target, "<span class ='cultlarge'>[uppertext(ticker.mode.cultdat.entity_name)] HUNGERS FOR YOUR SOUL</span>")
|
||||
to_chat(target, "<span class ='cultlarge'>[uppertext(ticker.cultdat.entity_name)] HUNGERS FOR YOUR SOUL</span>")
|
||||
else
|
||||
to_chat(target, "<span class ='cultlarge'>[uppertext(ticker.mode.cultdat.entity_name)] HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL</span>")
|
||||
to_chat(target, "<span class ='cultlarge'>[uppertext(ticker.cultdat.entity_name)] HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL</span>")
|
||||
|
||||
//Wizard narsie
|
||||
/obj/singularity/narsie/wizard
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
/obj/machinery/power/solar/ex_act(severity, target)
|
||||
..()
|
||||
if(isnull(gcDestroyed))
|
||||
if(!QDELETED(src))
|
||||
switch(severity)
|
||||
if(2)
|
||||
if(prob(50) && broken())
|
||||
@@ -516,7 +516,7 @@
|
||||
|
||||
/obj/machinery/power/solar_control/ex_act(severity, target)
|
||||
..()
|
||||
if(isnull(gcDestroyed))
|
||||
if(!QDELETED(src))
|
||||
switch(severity)
|
||||
if(2)
|
||||
if(prob(50))
|
||||
|
||||
@@ -853,16 +853,17 @@
|
||||
description = "A strong anesthetic and sedative."
|
||||
reagent_state = LIQUID
|
||||
color = "#96DEDE"
|
||||
metabolization_rate = 0.1
|
||||
|
||||
/datum/reagent/medicine/ether/on_mob_life(mob/living/M)
|
||||
M.AdjustJitter(-25)
|
||||
switch(current_cycle)
|
||||
if(1 to 15)
|
||||
if(1 to 30)
|
||||
if(prob(7))
|
||||
M.emote("yawn")
|
||||
if(16 to 35)
|
||||
if(31 to 40)
|
||||
M.Drowsy(20)
|
||||
if(36 to INFINITY)
|
||||
if(41 to INFINITY)
|
||||
M.Paralyse(15)
|
||||
M.Drowsy(20)
|
||||
..()
|
||||
@@ -1042,6 +1043,7 @@
|
||||
description = "Highly advanced nanites equipped with calcium payloads designed to repair bones. Nanomachines son."
|
||||
color = "#9b3401"
|
||||
metabolization_rate = 0.5
|
||||
can_synth = FALSE
|
||||
|
||||
/datum/reagent/medicine/nanocalcium/on_mob_life(mob/living/carbon/human/M)
|
||||
switch(current_cycle)
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/item/projectile/P)
|
||||
..()
|
||||
if(!qdeleted(src)) //wasn't deleted by the projectile's effects.
|
||||
if(!QDELETED(src)) //wasn't deleted by the projectile's effects.
|
||||
if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE)))
|
||||
message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion.")
|
||||
log_game("[key_name(P.firer)] triggered a fueltank explosion.")
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
var/target_area
|
||||
target_area = input("Area to land", "Select a Landing Zone", target_area) in teleportlocs
|
||||
var/area/picked_area = teleportlocs[target_area]
|
||||
if(!src || qdeleted(src))
|
||||
if(!src || QDELETED(src))
|
||||
return
|
||||
|
||||
var/turf/T = safepick(get_area_turfs(picked_area))
|
||||
|
||||
@@ -869,7 +869,7 @@
|
||||
desc = "Used to call and send the SIT shuttle."
|
||||
req_access = list(access_syndicate)
|
||||
shuttleId = "sit"
|
||||
possible_destinations = "sit_arrivals;sit_scimaint;sit_engshuttle;sit_away"
|
||||
possible_destinations = "sit_arrivals;sit_engshuttle;sit_away"
|
||||
|
||||
|
||||
var/global/trade_dock_timelimit = 0
|
||||
|
||||
Reference in New Issue
Block a user