Merge branch 'master' into put-that-cookie-down

This commit is contained in:
Citinited
2018-04-14 01:21:24 +01:00
committed by GitHub
162 changed files with 1476 additions and 968 deletions
+2 -1
View File
@@ -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.")
+5 -5
View File
@@ -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
+59
View File
@@ -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))
+1 -1
View File
@@ -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
+7
View File
@@ -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"
+3 -3
View File
@@ -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"
+1 -1
View File
@@ -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()
+2 -2
View File
@@ -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()
+1 -1
View File
@@ -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
+2 -3
View File
@@ -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--
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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))
+4
View File
@@ -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
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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()
+3 -2
View File
@@ -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
..()
+1 -1
View File
@@ -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
+1 -5
View File
@@ -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 ..()
+9 -3
View File
@@ -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.\"")
+5 -5
View File
@@ -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
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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.")
+1 -1
View File
@@ -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))
+1 -1
View File
@@ -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