diff --git a/baystation12.dme b/baystation12.dme
index 24052903c4..0af861983b 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -157,6 +157,11 @@
#include "code\datums\spells\trigger.dm"
#include "code\datums\spells\turf_teleport.dm"
#include "code\datums\spells\wizard.dm"
+#include "code\datums\visibility_networks\chunk.dm"
+#include "code\datums\visibility_networks\dictionary.dm"
+#include "code\datums\visibility_networks\update_triggers.dm"
+#include "code\datums\visibility_networks\visibility_interface.dm"
+#include "code\datums\visibility_networks\visibility_network.dm"
#include "code\defines\obj.dm"
#include "code\defines\obj\hydro.dm"
#include "code\defines\obj\weapon.dm"
@@ -970,6 +975,7 @@
#include "code\modules\mob\living\carbon\monkey\monkey.dm"
#include "code\modules\mob\living\carbon\monkey\powers.dm"
#include "code\modules\mob\living\carbon\monkey\update_icons.dm"
+#include "code\modules\mob\living\silicon\alarm.dm"
#include "code\modules\mob\living\silicon\death.dm"
#include "code\modules\mob\living\silicon\login.dm"
#include "code\modules\mob\living\silicon\say.dm"
@@ -987,6 +993,7 @@
#include "code\modules\mob\living\silicon\ai\freelook\eye.dm"
#include "code\modules\mob\living\silicon\ai\freelook\read_me.dm"
#include "code\modules\mob\living\silicon\ai\freelook\update_triggers.dm"
+#include "code\modules\mob\living\silicon\ai\freelook\visibility_interface.dm"
#include "code\modules\mob\living\silicon\decoy\death.dm"
#include "code\modules\mob\living\silicon\decoy\decoy.dm"
#include "code\modules\mob\living\silicon\decoy\life.dm"
@@ -1016,6 +1023,7 @@
#include "code\modules\mob\living\silicon\robot\drone\drone.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm"
+#include "code\modules\mob\living\silicon\robot\drone\drone_damage.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm"
#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm"
#include "code\modules\mob\living\simple_animal\bees.dm"
@@ -1025,7 +1033,6 @@
#include "code\modules\mob\living\simple_animal\parrot.dm"
#include "code\modules\mob\living\simple_animal\shade.dm"
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
-#include "code\modules\mob\living\simple_animal\vox.dm"
#include "code\modules\mob\living\simple_animal\worm.dm"
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
@@ -1060,6 +1067,12 @@
#include "code\modules\mob\new_player\preferences_setup.dm"
#include "code\modules\mob\new_player\skill.dm"
#include "code\modules\mob\new_player\sprite_accessories.dm"
+#include "code\modules\mob\spirit\cultnet.dm"
+#include "code\modules\mob\spirit\movement.dm"
+#include "code\modules\mob\spirit\spirit.dm"
+#include "code\modules\mob\spirit\viewpoint.dm"
+#include "code\modules\mob\spirit\mask\mask.dm"
+#include "code\modules\mob\spirit\mask\respawn.dm"
#include "code\modules\nano\_JSON.dm"
#include "code\modules\nano\JSON Reader.dm"
#include "code\modules\nano\JSON Writer.dm"
diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm
index c2d8a2fc6c..c152159b7a 100644
--- a/code/ZAS/Variable Settings.dm
+++ b/code/ZAS/Variable Settings.dm
@@ -192,7 +192,7 @@ var/global/vs_control/vsc = new
/vs_control/proc/SetDefault(var/mob/user)
var/list/setting_choices = list("Phoron - Standard", "Phoron - Low Hazard", "Phoron - High Hazard", "Phoron - Oh Shit!",\
- "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish")
+ "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish", "ZAS/Phoron - Initial")
var/def = input(user, "Which of these presets should be used?") as null|anything in setting_choices
if(!def)
return
@@ -290,6 +290,36 @@ var/global/vs_control/vsc = new
airflow_mob_slowdown = 3
connection_insulation = 0
+ if("ZAS/Phoron - Initial")
+ fire_consuption_rate = initial(fire_consuption_rate)
+ fire_firelevel_multiplier = initial(fire_firelevel_multiplier)
+ fire_fuel_energy_release = initial(fire_fuel_energy_release)
+ IgnitionLevel = initial(IgnitionLevel)
+ airflow_lightest_pressure = initial(airflow_lightest_pressure)
+ airflow_light_pressure = initial(airflow_light_pressure)
+ airflow_medium_pressure = initial(airflow_medium_pressure)
+ airflow_heavy_pressure = initial(airflow_heavy_pressure)
+ airflow_dense_pressure = initial(airflow_dense_pressure)
+ airflow_stun_pressure = initial(airflow_stun_pressure)
+ airflow_stun_cooldown = initial(airflow_stun_cooldown)
+ airflow_stun = initial(airflow_stun)
+ airflow_damage = initial(airflow_damage)
+ airflow_speed_decay = initial(airflow_speed_decay)
+ airflow_delay = initial(airflow_delay)
+ airflow_mob_slowdown = initial(airflow_mob_slowdown)
+ connection_insulation = initial(connection_insulation)
+ connection_temperature_delta = initial(connection_temperature_delta)
+
+ plc.PHORON_DMG = initial(plc.PHORON_DMG)
+ plc.CLOTH_CONTAMINATION = initial(plc.CLOTH_CONTAMINATION)
+ plc.PHORONGUARD_ONLY = initial(plc.PHORONGUARD_ONLY)
+ plc.GENETIC_CORRUPTION = initial(plc.GENETIC_CORRUPTION)
+ plc.SKIN_BURNS = initial(plc.SKIN_BURNS)
+ plc.EYE_BURNS = initial(plc.EYE_BURNS)
+ plc.CONTAMINATION_LOSS = initial(plc.CONTAMINATION_LOSS)
+ plc.PHORON_HALLUCINATION = initial(plc.PHORON_HALLUCINATION)
+ plc.N2O_HALLUCINATION = initial(plc.N2O_HALLUCINATION)
+
world << "\blue [key_name(user)] changed the global phoron/ZAS settings to \"[def]\""
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 76d15e1a6a..9db6e92075 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -7,6 +7,7 @@ var/list/directory = list() //list of all ckeys with associated client
var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player
var/global/list/mob_list = list() //List of all mobs, including clientless
+var/global/list/spirits = list() //List of all the spirits, including Masks
var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player
var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player
@@ -22,6 +23,7 @@ var/global/list/joblist = list() //list of all jobstypes, minus borg and AI
//Languages/species/whitelist.
var/global/list/all_species[0]
var/global/list/all_languages[0]
+var/global/list/language_keys[0] //table of say codes for all languages
var/global/list/whitelisted_species = list("Human")
// Posters
@@ -99,10 +101,17 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al
for(var/T in paths)
var/datum/language/L = new T
all_languages[L.name] = L
+
+ for (var/language_name in all_languages)
+ var/datum/language/L = all_languages[language_name]
+ language_keys[":[lowertext(L.key)]"] = L
+ var/rkey = 0
paths = typesof(/datum/species)-/datum/species
for(var/T in paths)
+ rkey++
var/datum/species/S = new T
+ S.race_key = rkey //Used in mob icon caching.
all_species[S.name] = S
if(S.flags & IS_WHITELISTED)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index f89488e9c4..bdb017e5ee 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -443,36 +443,29 @@ Turf and target are seperate in case you want to teleport some distance from a t
return creatures
+var/list/sortMobsOrder = list( "/mob/living/silicon/ai",
+ "/mob/living/silicon/pai",
+ "/mob/living/silicon/robot",
+ "/mob/living/carbon/human",
+ "/mob/spirit/mask",
+ "/mob/living/carbon/brain",
+ "/mob/living/carbon/alien",
+ "/mob/dead/observer",
+ "/mob/new_player",
+ "/mob/living/carbon/monkey",
+ "/mob/living/carbon/slime",
+ "/mob/living/simple_animal",
+ "/mob/living/silicon/hivebot",
+ "/mob/living/silicon/hive_mainframe" )
+
//Orders mobs by type then by name
/proc/sortmobs()
var/list/moblist = list()
var/list/sortmob = sortAtom(mob_list)
- for(var/mob/living/silicon/ai/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/silicon/pai/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/silicon/robot/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/carbon/human/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/carbon/brain/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/carbon/alien/M in sortmob)
- moblist.Add(M)
- for(var/mob/dead/observer/M in sortmob)
- moblist.Add(M)
- for(var/mob/new_player/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/carbon/monkey/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/carbon/slime/M in sortmob)
- moblist.Add(M)
- for(var/mob/living/simple_animal/M in sortmob)
- moblist.Add(M)
-// for(var/mob/living/silicon/hivebot/M in world)
-// mob_list.Add(M)
-// for(var/mob/living/silicon/hive_mainframe/M in world)
-// mob_list.Add(M)
+ for (var/path in sortMobsOrder)
+ for (var/mob/sorting in sortmob)
+ if (istype(sorting,text2path(path)))
+ moblist.Add(sorting)
return moblist
//E = MC^2
@@ -538,7 +531,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
else if(M.name)
name = M.name
- if(is_special_character(M))
+ if(include_link && is_special_character(M))
. += "/([name])" //Orange
else
. += "/([name])"
@@ -727,14 +720,14 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl
return 0
var/delayfraction = round(delay/numticks)
- var/turf/T = get_turf(user)
+ var/original_loc = user.loc
var/holding = user.get_active_hand()
for(var/i = 0, i 1)
+ return 0
+
+ return 1
+
/*
Adjacency (to anything else):
* Must be on a turf
@@ -108,7 +123,12 @@
if( O.flags&ON_BORDER) // windows have throwpass but are on border, check them first
if( O.dir & target_dir || O.dir&(O.dir-1) ) // full tile windows are just diagonals mechanically
- return 0
+ var/obj/structure/window/W = target_atom
+ if(istype(W))
+ if(!W.is_fulltile()) //exception for breaking full tile windows on top of single pane windows
+ return 0
+ else
+ return 0
else if( !border_only ) // dense, not on border, cannot pass over
return 0
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 02406ed507..093899c5c4 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -118,7 +118,8 @@
return
// Allows you to click on a box's contents, if that box is on the ground, but no deeper than that
- if(isturf(A) || isturf(A.loc) || (A.loc && isturf(A.loc.loc)))
+ sdepth = A.storage_depth_turf()
+ if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
next_move = world.time + 10
if(A.Adjacent(src)) // see adjacent.dm
@@ -246,7 +247,7 @@
/atom/proc/AltClick(var/mob/user)
var/turf/T = get_turf(src)
- if(T && T.Adjacent(user))
+ if(T && T.AdjacentQuick(user))
if(user.listed_turf == T)
user.listed_turf = null
else
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index b6e157c59b..b513a6cd06 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -268,6 +268,7 @@ client
body += ""
body += ""
body += ""
+ body += ""
body += ""
body += ""
body += ""
@@ -729,7 +730,18 @@ client
usr << "Mob doesn't exist anymore"
return
holder.Topic(href, list("makeai"=href_list["makeai"]))
-
+
+
+ else if(href_list["makemask"])
+ if(!check_rights(R_SPAWN)) return
+ var/mob/currentMob = locate(href_list["makemask"])
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return
+ if(!currentMob)
+ usr << "Mob doesn't exist anymore"
+ return
+ holder.Topic(href, list("makemask"=href_list["makemask"]))
+
+
else if(href_list["setmutantrace"])
if(!check_rights(R_SPAWN)) return
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 733c3e4e93..32ebbed57e 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -642,20 +642,11 @@ datum/mind
switch(href_list["cult"])
if("clear")
if(src in ticker.mode.cult)
- ticker.mode.cult -= src
- ticker.mode.update_cult_icons_removed(src)
- special_role = null
- var/datum/game_mode/cult/cult = ticker.mode
- if (istype(cult))
- if(!config.objectives_disabled)
- cult.memoize_cult_objectives(src)
- current << "\red You have been brainwashed! You are no longer a cultist!"
- memory = ""
+ ticker.mode.remove_cultist(src)
log_admin("[key_name_admin(usr)] has de-cult'ed [current].")
if("cultist")
if(!(src in ticker.mode.cult))
- ticker.mode.cult += src
- ticker.mode.update_cult_icons_added(src)
+ ticker.mode.add_cultist(src)
special_role = "Cultist"
current << "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie."
current << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
diff --git a/code/datums/visibility_networks/chunk.dm b/code/datums/visibility_networks/chunk.dm
new file mode 100644
index 0000000000..4abc1340b0
--- /dev/null
+++ b/code/datums/visibility_networks/chunk.dm
@@ -0,0 +1,179 @@
+#define UPDATE_BUFFER 25 // 2.5 seconds
+
+// CAMERA CHUNK
+//
+// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
+// Allows the mob using this chunk to stream these chunks and know what it can and cannot see.
+
+/datum/visibility_chunk
+ var/obscured_image = 'icons/effects/cameravis.dmi'
+ var/obscured_sub = "black"
+ var/list/obscuredTurfs = list()
+ var/list/visibleTurfs = list()
+ var/list/obscured = list()
+ var/list/viewpoints = list()
+ var/list/turfs = list()
+ var/list/seenby = list()
+ var/visible = 0
+ var/changed = 0
+ var/updating = 0
+ var/x = 0
+ var/y = 0
+ var/z = 0
+
+/datum/visibility_chunk/proc/add(mob/new_mob)
+
+ // if this thing doesn't use one of these visibility systems, kick it out
+ if (!new_mob.visibility_interface)
+ return
+
+ // if the mob being added isn't a valid form of that mob, kick it out
+ if (!new_mob.visibility_interface:canBeAddedToChunk(src))
+ return
+
+ // add this chunk to the list of visible chunks
+ new_mob.visibility_interface:addChunk(src)
+
+ visible++
+ seenby += new_mob
+ if(changed && !updating)
+ update()
+
+/datum/visibility_chunk/proc/remove(mob/new_mob)
+ // if this thing doesn't use one of these visibility systems, kick it out
+ if (!new_mob.visibility_interface)
+ return
+
+ // if the mob being added isn't a valid form of that mob, kick it out
+ if (!new_mob.visibility_interface:canBeAddedToChunk(src))
+ return
+
+ // remove the chunk
+ new_mob.visibility_interface:removeChunk(src)
+
+ // remove the mob from out lists
+ seenby -= new_mob
+ if(visible > 0)
+ visible--
+
+/datum/visibility_chunk/proc/visibilityChanged(turf/loc)
+ if(!visibleTurfs[loc])
+ return
+ hasChanged()
+
+/datum/visibility_chunk/proc/hasChanged(var/update_now = 0)
+ if(visible || update_now)
+ if(!updating)
+ updating = 1
+ spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once
+ update()
+ updating = 0
+ else
+ changed = 1
+
+
+/*
+This function needs to be overwritten to return True if the viewpoint object is valid, and false if it is not.
+*/
+/datum/visibility_chunk/proc/validViewpoint(var/viewpoint)
+ return FALSE
+
+/*
+This function needs to be overwritten to return a list of visible turfs for that viewpoint
+*/
+/datum/visibility_chunk/proc/getVisibleTurfsForViewpoint(var/viewpoint)
+ return list()
+
+// returns a list of turfs which can be seen in by the chunks viewpoints
+/datum/visibility_chunk/proc/getVisibleTurfs()
+ var/list/newVisibleTurfs = list()
+ for(var/viewpoint in viewpoints)
+ if (validViewpoint(viewpoint))
+ for (var/turf/t in getVisibleTurfsForViewpoint(viewpoint))
+ newVisibleTurfs[t]=t
+ return newVisibleTurfs
+
+/*
+This function needs to be overwritten to find nearby viewpoint objects to the chunk center.
+*/
+/datum/visibility_chunk/proc/findNearbyViewpoints()
+ return FALSE
+
+/*
+This function can be overwritten to change or randomize the obscuring images
+*/
+/datum/visibility_chunk/proc/setObscuredImage(var/turf/target_turf)
+ if(!target_turf.obscured)
+ target_turf.obscured = image(obscured_image, target_turf, obscured_sub, 15)
+
+/datum/visibility_chunk/proc/update()
+
+ set background = 1
+
+ // get a list of all the turfs that our viewpoints can see
+ var/list/newVisibleTurfs = getVisibleTurfs()
+
+ // Removes turf that isn't in turfs.
+ newVisibleTurfs &= turfs
+
+ var/list/visAdded = newVisibleTurfs - visibleTurfs
+ var/list/visRemoved = visibleTurfs - newVisibleTurfs
+
+ visibleTurfs = newVisibleTurfs
+ obscuredTurfs = turfs - newVisibleTurfs
+
+ // update the visibility overlays
+ for(var/turf in visAdded)
+ var/turf/t = turf
+ if(t.obscured)
+ obscured -= t.obscured
+ for(var/mob/current_mob in seenby)
+ if (current_mob.visibility_interface)
+ current_mob.visibility_interface:removeObscuredTurf(t)
+
+ for(var/turf in visRemoved)
+ var/turf/t = turf
+ if(obscuredTurfs[t])
+ setObscuredImage(t)
+ obscured += t.obscured
+ for(var/mob/current_mob in seenby)
+ if (current_mob.visibility_interface)
+ current_mob.visibility_interface:addObscuredTurf(t)
+ else
+ seenby -= current_mob
+
+
+// Create a new chunk, since the chunks are made as they are needed.
+/datum/visibility_chunk/New(loc, x, y, z)
+
+ // 0xf = 15
+ x &= ~0xf
+ y &= ~0xf
+
+ src.x = x
+ src.y = y
+ src.z = z
+
+ for(var/turf/t in range(10, locate(x + 8, y + 8, z)))
+ if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16)
+ turfs[t] = t
+
+ // locate all nearby viewpoints
+ findNearbyViewpoints()
+
+ // get the turfs that are visible to those viewpoints
+ visibleTurfs = getVisibleTurfs()
+
+ // Removes turf that isn't in turfs.
+ visibleTurfs &= turfs
+
+ // create the list of turfs we can't see
+ obscuredTurfs = turfs - visibleTurfs
+
+ // create the list of obscuring images to add to viewing clients
+ for(var/turf in obscuredTurfs)
+ var/turf/t = turf
+ setObscuredImage(t)
+ obscured += t.obscured
+
+#undef UPDATE_BUFFER
\ No newline at end of file
diff --git a/code/datums/visibility_networks/dictionary.dm b/code/datums/visibility_networks/dictionary.dm
new file mode 100644
index 0000000000..5f57ddd7a1
--- /dev/null
+++ b/code/datums/visibility_networks/dictionary.dm
@@ -0,0 +1,11 @@
+var/datum/visibility_network/cameras/cameranet = new()
+var/datum/visibility_network/cult/cultNetwork = new()
+var/datum/visibility_network/list/visibility_networks = list("ALL_CAMERAS"=cameranet, "CULT" = cultNetwork)
+
+
+// used by turfs and objects to update all visibility networks
+/proc/updateVisibilityNetworks(atom/A, var/opacity_check = 1)
+ var/datum/visibility_network/currentNetwork
+ for (var/networkName in visibility_networks)
+ currentNetwork = visibility_networks[networkName]
+ currentNetwork.updateVisibility(A, opacity_check)
\ No newline at end of file
diff --git a/code/datums/visibility_networks/update_triggers.dm b/code/datums/visibility_networks/update_triggers.dm
new file mode 100644
index 0000000000..9a366e7d64
--- /dev/null
+++ b/code/datums/visibility_networks/update_triggers.dm
@@ -0,0 +1,94 @@
+//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update.
+
+// TURFS
+
+/turf
+ var/image/obscured
+
+/turf/proc/visibilityChanged()
+ if(ticker)
+ updateVisibilityNetworks(src)
+
+/turf/simulated/Del()
+ visibilityChanged()
+ ..()
+
+/turf/simulated/New()
+ ..()
+ visibilityChanged()
+
+
+
+// STRUCTURES
+
+/obj/structure/Del()
+ if(ticker)
+ updateVisibilityNetworks(src)
+ ..()
+
+/obj/structure/New()
+ ..()
+ if(ticker)
+ updateVisibilityNetworks(src)
+
+// EFFECTS
+
+/obj/effect/Del()
+ if(ticker)
+ updateVisibilityNetworks(src)
+ ..()
+
+/obj/effect/New()
+ ..()
+ if(ticker)
+ updateVisibilityNetworks(src)
+
+
+// DOORS
+
+// Simply updates the visibility of the area when it opens/closes/destroyed.
+/obj/machinery/door/proc/update_nearby_tiles(need_rebuild)
+
+ if(!glass)
+ updateVisibilityNetworks(src,0)
+
+ if(!air_master)
+ return 0
+
+ for(var/turf/simulated/turf in locs)
+ update_heat_protection(turf)
+ air_master.mark_for_update(turf)
+
+ return 1
+
+
+
+#define UPDATE_VISIBILITY_NETWORK_BUFFER 30
+
+/mob
+ var/datum/visibility_network/list/visibilityNetworks=list()
+ var/updatingVisibilityNetworks=FALSE
+
+/mob/Move(n,direct)
+ var/oldLoc = src.loc
+ . = ..()
+ if(.)
+ if(src.visibilityNetworks.len)
+ if(!src.updatingVisibilityNetworks)
+ src.updatingVisibilityNetworks = 1
+ spawn(UPDATE_VISIBILITY_NETWORK_BUFFER)
+ if(oldLoc != src.loc)
+ for (var/datum/visibility_network/currentNetwork in src.visibilityNetworks)
+ currentNetwork.updateMob(src)
+ src.updatingVisibilityNetworks = 0
+ return ..(n,direct)
+
+/mob/proc/addToVisibilityNetwork(var/datum/visibility_network/network)
+ if(network)
+ src.visibilityNetworks+=network
+
+/mob/proc/removeFromVisibilityNetwork(var/datum/visibility_network/network)
+ if(network)
+ src.visibilityNetworks|=network
+
+#undef UPDATE_VISIBILITY_NETWORK_BUFFER
\ No newline at end of file
diff --git a/code/datums/visibility_networks/visibility_interface.dm b/code/datums/visibility_networks/visibility_interface.dm
new file mode 100644
index 0000000000..7d8efba41d
--- /dev/null
+++ b/code/datums/visibility_networks/visibility_interface.dm
@@ -0,0 +1,46 @@
+/datum/visibility_interface
+ var/chunk_type = null
+ var/mob/controller = null
+ var/list/visible_chunks = list()
+
+
+/datum/visibility_interface/New(var/mob/controller)
+ src.controller = controller
+
+
+/datum/visibility_interface/proc/validMob()
+ return getClient()
+
+/datum/visibility_interface/proc/getClient()
+ return controller.client
+
+/datum/visibility_interface/proc/canBeAddedToChunk(var/datum/visibility_chunk/test_chunk)
+ return istype(test_chunk,chunk_type)
+
+
+/datum/visibility_interface/proc/addChunk(var/datum/visibility_chunk/test_chunk)
+ visible_chunks+=test_chunk
+ var/client/currentClient = getClient()
+ if(currentClient)
+ currentClient.images += test_chunk.obscured
+
+
+/datum/visibility_interface/proc/removeChunk(var/datum/visibility_chunk/test_chunk)
+ visible_chunks-=test_chunk
+ var/client/currentClient = getClient()
+ if(currentClient)
+ currentClient.images -= test_chunk.obscured
+
+
+/datum/visibility_interface/proc/removeObscuredTurf(var/turf/target_turf)
+ if(validMob())
+ var/client/currentClient = getClient()
+ if(currentClient)
+ currentClient.images -= target_turf.obscured
+
+
+/datum/visibility_interface/proc/addObscuredTurf(var/turf/target_turf)
+ if(validMob())
+ var/client/currentClient = getClient()
+ if(currentClient)
+ currentClient.images -= target_turf.obscured
\ No newline at end of file
diff --git a/code/datums/visibility_networks/visibility_network.dm b/code/datums/visibility_networks/visibility_network.dm
new file mode 100644
index 0000000000..225b6e3def
--- /dev/null
+++ b/code/datums/visibility_networks/visibility_network.dm
@@ -0,0 +1,141 @@
+/datum/visibility_network
+ var/list/viewpoints = list()
+
+ // the type of chunk used by this network
+ var/datum/visibility_chunk/ChunkType = /datum/visibility_chunk
+
+ // The chunks of the map, mapping the areas that the viewpoints can see.
+ var/list/chunks = list()
+
+ var/ready = 0
+
+
+// Creates a chunk key string from x,y,z coordinates
+/datum/visibility_network/proc/createChunkKey(x,y,z)
+ x &= ~0xf
+ y &= ~0xf
+ return "[x],[y],[z]"
+
+
+// Checks if a chunk has been Generated in x, y, z.
+/datum/visibility_network/proc/chunkGenerated(x, y, z)
+ return (chunks[createChunkKey(x, y, z)])
+
+
+// Returns the chunk in the x, y, z.
+// If there is no chunk, it creates a new chunk and returns that.
+/datum/visibility_network/proc/getChunk(x, y, z)
+ var/key = createChunkKey(x, y, z)
+ if(!chunks[key])
+ chunks[key] = new ChunkType(null, x, y, z)
+ return chunks[key]
+
+
+/datum/visibility_network/proc/visibility(var/mob/targetMob)
+
+ // if we've got not visibility interface on the mob, we canot do this
+ if (!targetMob.visibility_interface)
+ return
+
+ // 0xf = 15
+ var/x1 = max(0, targetMob.x - 16) & ~0xf
+ var/y1 = max(0, targetMob.y - 16) & ~0xf
+ var/x2 = min(world.maxx, targetMob.x + 16) & ~0xf
+ var/y2 = min(world.maxy, targetMob.y + 16) & ~0xf
+
+ var/list/visibleChunks = list()
+
+ for(var/x = x1; x <= x2; x += 16)
+ for(var/y = y1; y <= y2; y += 16)
+ visibleChunks += getChunk(x, y, targetMob.z)
+
+ var/list/remove = targetMob.visibility_interface:visible_chunks - visibleChunks
+ var/list/add = visibleChunks - targetMob.visibility_interface:visible_chunks
+
+ for(var/datum/visibility_chunk/chunk in remove)
+ chunk.remove(targetMob)
+
+ for(var/datum/visibility_chunk/chunk in add)
+ chunk.add(targetMob)
+
+
+// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
+/datum/visibility_network/proc/updateVisibility(atom/A, var/opacity_check = 1)
+ if(!ticker || (opacity_check && !A.opacity))
+ return
+ majorChunkChange(A, 2)
+
+
+/datum/visibility_network/proc/updateChunk(x, y, z)
+ if(!chunkGenerated(x, y, z))
+ return
+ var/datum/visibility_chunk/chunk = getChunk(x, y, z)
+ chunk.hasChanged()
+
+
+/datum/visibility_network/proc/validViewpoint(var/viewpoint)
+ return FALSE
+
+
+/datum/visibility_network/proc/addViewpoint(var/viewpoint)
+ if(validViewpoint(viewpoint))
+ majorChunkChange(viewpoint, 1)
+
+
+/datum/visibility_network/proc/removeViewpoint(var/viewpoint)
+ if(validViewpoint(viewpoint))
+ majorChunkChange(viewpoint, 0)
+
+/datum/visibility_network/proc/getViewpointFromMob(var/mob/currentMob)
+ return FALSE
+
+/datum/visibility_network/proc/updateMob(var/mob/currentMob)
+ var/viewpoint = getViewpointFromMob(currentMob)
+ if(viewpoint)
+ updateViewpoint(viewpoint)
+
+
+/datum/visibility_network/proc/updateViewpoint(var/viewpoint)
+ if(validViewpoint(viewpoint))
+ majorChunkChange(viewpoint, 1)
+
+
+// Never access this proc directly!!!!
+// This will update the chunk and all the surrounding chunks.
+// It will also add the atom to the cameras list if you set the choice to 1.
+// Setting the choice to 0 will remove the viewpoint from the chunks.
+// If you want to update the chunks around an object, without adding/removing a viewpoint, use choice 2.
+/datum/visibility_network/proc/majorChunkChange(atom/c, var/choice)
+ // 0xf = 15
+ if(!c)
+ return
+
+ var/turf/T = get_turf(c)
+ if(T)
+ var/x1 = max(0, T.x - 8) & ~0xf
+ var/y1 = max(0, T.y - 8) & ~0xf
+ var/x2 = min(world.maxx, T.x + 8) & ~0xf
+ var/y2 = min(world.maxy, T.y + 8) & ~0xf
+
+ for(var/x = x1; x <= x2; x += 16)
+ for(var/y = y1; y <= y2; y += 16)
+ if(chunkGenerated(x, y, T.z))
+ var/datum/visibility_chunk/chunk = getChunk(x, y, T.z)
+ if(choice == 0)
+ // Remove the viewpoint.
+ chunk.viewpoints -= c
+ else if(choice == 1)
+ // You can't have the same viewpoint in the list twice.
+ chunk.viewpoints |= c
+ chunk.hasChanged()
+
+// checks if the network can see a particular atom
+/datum/visibility_network/proc/checkCanSee(var/atom/target)
+ var/turf/position = get_turf(target)
+ var/datum/visibility_chunk/chunk = getChunk(position.x, position.y, position.z)
+ if(chunk)
+ if(chunk.changed)
+ chunk.hasChanged(1) // Update now, no matter if it's visible or not.
+ if(chunk.visibleTurfs[position])
+ return 1
+ return 0
\ No newline at end of file
diff --git a/code/defines/obj/hydro.dm b/code/defines/obj/hydro.dm
index 443ba95b09..3e1124d35a 100644
--- a/code/defines/obj/hydro.dm
+++ b/code/defines/obj/hydro.dm
@@ -115,6 +115,24 @@
plant_type = 0
growthstages = 2
+/obj/item/seeds/peanutseed
+ name = "pack of peanut seeds"
+ desc = "These seeds grow into peanut vines."
+ icon_state = "seed-peanut"
+ mypath = "/obj/item/seeds/peanutseed"
+ species = "peanut"
+ plantname = "Peanuts"
+ productname = "/obj/item/weapon/reagent_containers/food/snacks/grown/peanut"
+ lifespan = 55
+ endurance = 50
+ maturation = 6
+ production = 6
+ yield = 6
+ potency = 10
+ plant_type = 0
+ growthstages = 6
+
+
/obj/item/seeds/cabbageseed
name = "pack of cabbage seeds"
desc = "These seeds grow into cabbages."
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index e11a950eee..f16d2810b0 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -431,7 +431,7 @@
/obj/item/weapon/camera_bug/attack_self(mob/usr as mob)
var/list/cameras = new/list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
+ for (var/obj/machinery/camera/C in cameranet.viewpoints)
if (C.bugged && C.status)
cameras.Add(C)
if (length(cameras) == 0)
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 45452a95e1..b1e3442d34 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -8,6 +8,7 @@
/proc/iscultist(mob/living/M as mob)
return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.cult)
+
/proc/is_convertable_to_cult(datum/mind/mind)
if(!istype(mind)) return 0
if(istype(mind.current, /mob/living/carbon/human) && (mind.assigned_role in list("Captain", "Chaplain"))) return 0
@@ -137,7 +138,8 @@
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
mob.mutations.Remove(CLUMSY)
-
+ add_cult_viewpoint(mob) // give them a viewpoint
+
var/obj/item/weapon/paper/talisman/supply/T = new(mob)
var/list/slots = list (
"backpack" = slot_in_backpack,
@@ -173,11 +175,20 @@
cult_mob.mind.store_memory("You remember that [wordexp]", 0, 0)
+/datum/game_mode/proc/add_cult_viewpoint(var/mob/target)
+ for(var/obj/cult_viewpoint/viewpoint in target)
+ return
+ var/obj/cult_viewpoint/viewpoint = new(target)
+ viewpoint.loc = target
+ return viewpoint
+
+
/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE
if (!istype(cult_mind))
return 0
if(!(cult_mind in cult) && is_convertable_to_cult(cult_mind))
cult += cult_mind
+ add_cult_viewpoint(cult_mind.current)
update_cult_icons_added(cult_mind)
return 1
@@ -194,57 +205,133 @@
cult -= cult_mind
cult_mind.current << "\red An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it."
cult_mind.memory = ""
+
+ // remove the cult viewpoint object
+ var/obj/viewpoint = getCultViewpoint(cult_mind.current)
+ del(viewpoint)
+
update_cult_icons_removed(cult_mind)
if(show_message)
for(var/mob/M in viewers(cult_mind.current))
M << "[cult_mind.current] looks like they just reverted to their old faith!"
+
/datum/game_mode/proc/update_all_cult_icons()
spawn(0)
+ // reset the cult
+ for(var/datum/mind/cultist in cult)
+ reset_cult_icons_for_cultist(cultist)
+ // reset the spirits
+ for(var/mob/spirit/currentSpirit in spirits)
+ reset_cult_icons_for_spirit(currentSpirit)
+
+
+/datum/game_mode/proc/reset_cult_icons_for_cultist(var/datum/mind/target)
+ if(target.current)
+ if(target.current.client)
+ remove_all_cult_icons(target)
+ for(var/datum/mind/cultist in cult)
+ if(cultist.current)
+ add_cult_icon(target.current.client,cultist.current)
+
+
+/datum/game_mode/proc/reset_cult_icons_for_spirit(mob/spirit/target)
+ if (target.client)
+ remove_all_cult_icons(target)
for(var/datum/mind/cultist in cult)
if(cultist.current)
- if(cultist.current.client)
- for(var/image/I in cultist.current.client.images)
- if(I.icon_state == "cult")
- del(I)
+ add_cult_icon(target.client,cultist.current)
+
+
+/datum/game_mode/proc/add_cult_icon(client/target_client,mob/target_mob)
+ var/I = image('icons/mob/mob.dmi', loc = target_mob, icon_state = "cult")
+ target_client.images += I
- for(var/datum/mind/cultist in cult)
- if(cultist.current)
- if(cultist.current.client)
- for(var/datum/mind/cultist_1 in cult)
- if(cultist_1.current)
- var/I = image('icons/mob/mob.dmi', loc = cultist_1.current, icon_state = "cult")
- cultist.current.client.images += I
+
+/datum/game_mode/proc/remove_cult_icon(client/target_client,mob/target_mob)
+ for(var/image/I in target_client.images)
+ if(I.icon_state == "cult" && I.loc == target_mob)
+ del(I)
+
+
+/datum/game_mode/proc/remove_all_cult_icons_from_client(client/target)
+ for(var/image/I in target.images)
+ if(I.icon_state == "cult")
+ del(I)
+
+
+/datum/game_mode/proc/remove_all_cult_icons(target)
+ var/datum/mind/cultist = target
+ if(istype(cultist))
+ if(cultist.current)
+ if(cultist.current.client)
+ remove_all_cult_icons_from_client(cultist.current.client)
+ return TRUE
+ var/mob/spirit/currentSpirit = target
+ if(istype(currentSpirit))
+ if (currentSpirit.client)
+ remove_all_cult_icons_from_client(currentSpirit.client)
+ return TRUE
+ return FALSE
+
+
+/datum/game_mode/proc/add_cult_icon_to_spirit(mob/spirit/currentSpirit,datum/mind/cultist)
+ if(!istype(currentSpirit) || !istype(cultist))
+ return FALSE
+ if (currentSpirit.client)
+ if (cultist.current)
+ add_cult_icon(currentSpirit.client,cultist.current)
+
+
+/datum/game_mode/proc/add_cult_icon_to_cultist(datum/mind/first_cultist,datum/mind/second_cultist)
+ if(first_cultist.current && second_cultist.current)
+ if(first_cultist.current.client)
+ add_cult_icon(first_cultist.current.client, second_cultist.current)
+
+
+/datum/game_mode/proc/remove_cult_icon_from_cultist(datum/mind/first_cultist,datum/mind/second_cultist)
+ if(first_cultist.current && second_cultist.current)
+ if(first_cultist.current.client)
+ remove_cult_icon(first_cultist.current.client,second_cultist.current)
+
+/datum/game_mode/proc/remove_cult_icon_from_spirit(mob/spirit/currentSpirit,datum/mind/cultist)
+ if(!istype(currentSpirit) || !istype(cultist))
+ return FALSE
+ if (currentSpirit.client)
+ if (cultist.current)
+ remove_cult_icon(currentSpirit.client,cultist.current)
+
+
+/datum/game_mode/proc/cult_icon_pair_link(datum/mind/first_cultist,datum/mind/second_cultist)
+ if (!istype(first_cultist) || !istype(second_cultist))
+ return 0
+ add_cult_icon_to_cultist(first_cultist,second_cultist)
+ add_cult_icon_to_cultist(second_cultist,first_cultist)
+
+/datum/game_mode/proc/cult_icon_pair_unlink(datum/mind/first_cultist,datum/mind/second_cultist)
+ if (!istype(first_cultist) || !istype(second_cultist))
+ return 0
+ remove_cult_icon(first_cultist,second_cultist)
+ remove_cult_icon(second_cultist,first_cultist)
+
+
/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind)
spawn(0)
for(var/datum/mind/cultist in cult)
- if(cultist.current)
- if(cultist.current.client)
- var/I = image('icons/mob/mob.dmi', loc = cult_mind.current, icon_state = "cult")
- cultist.current.client.images += I
- if(cult_mind.current)
- if(cult_mind.current.client)
- var/image/J = image('icons/mob/mob.dmi', loc = cultist.current, icon_state = "cult")
- cult_mind.current.client.images += J
-
-
+ cult_icon_pair_link(cultist,cult_mind)
+ for(var/mob/spirit/currentSpirit in spirits)
+ add_cult_icon_to_spirit(currentSpirit,cult_mind)
+
+
/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind)
spawn(0)
for(var/datum/mind/cultist in cult)
- if(cultist.current)
- if(cultist.current.client)
- for(var/image/I in cultist.current.client.images)
- if(I.icon_state == "cult" && I.loc == cult_mind.current)
- del(I)
-
- if(cult_mind.current)
- if(cult_mind.current.client)
- for(var/image/I in cult_mind.current.client.images)
- if(I.icon_state == "cult")
- del(I)
-
+ cult_icon_pair_unlink(cultist,cult_mind)
+ for(var/mob/spirit/currentSpirit in spirits)
+ remove_cult_icon_from_spirit(currentSpirit,cult_mind)
+
/datum/game_mode/cult/proc/get_unconvertables()
var/list/ucs = list()
@@ -280,6 +367,10 @@
return 1
+/atom/proc/cult_log(var/message)
+ investigate_log(message, "cult")
+
+
/datum/game_mode/cult/declare_completion()
if(config.objectives_disabled)
return 1
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index ac89f04647..33a7b905d8 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -1,6 +1,7 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
var/cultwords = list()
+var/rune_to_english = list()
var/runedec = 0
var/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide")
@@ -16,19 +17,22 @@ var/engwords = list("travel", "blood", "join", "hell", "destroy", "technology",
/proc/runerandom() //randomizes word meaning
var/list/runewords=list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") ///"orkan" and "allaq" removed.
for (var/word in engwords)
- cultwords[word] = pick(runewords)
+ var/runeword = pick(runewords)
+ cultwords[word] = runeword
+ rune_to_english[runeword] = word
runewords-=cultwords[word]
+
/obj/effect/rune
desc = ""
anchored = 1
icon = 'icons/obj/rune.dmi'
icon_state = "1"
var/visibility = 0
+ var/view_range = 7
unacidable = 1
layer = TURF_LAYER
-
var/word1
var/word2
var/word3
@@ -61,122 +65,112 @@ var/engwords = list("travel", "blood", "join", "hell", "destroy", "technology",
// self other technology - Communication rune //was other hear blood
// join hide technology - stun rune. Rune color: bright pink.
- New()
- ..()
- var/image/blood = image(loc = src)
- blood.override = 1
- for(var/mob/living/silicon/ai/AI in player_list)
- AI.client.images += blood
- examine()
- set src in view(2)
+/obj/effect/rune/New()
+ ..()
+ var/image/blood = image(loc = src)
+ blood.override = 1
+ for(var/mob/living/silicon/ai/AI in player_list)
+ AI.client.images += blood
+ cultNetwork.viewpoints+=src
+ cultNetwork.addViewpoint(src)
- if(!iscultist(usr))
- usr << "A strange collection of symbols drawn in blood."
- return
- /* Explosions... really?
- if(desc && !usr.stat)
- usr << "It reads: [desc]."
- sleep(30)
- explosion(src.loc, 0, 2, 5, 5)
- if(src)
- del(src)
- */
- if(!desc)
- usr << "A spell circle drawn in blood. It reads: [word1] [word2] [word3]."
- else
- usr << "Explosive Runes inscription in blood. It reads: [desc]."
+/obj/effect/rune/Del()
+ ..()
+ cultNetwork.viewpoints-=src
+ cultNetwork.removeViewpoint(src)
+/obj/effect/rune/examine()
+ set src in view(2)
+
+ if(!iscultist(usr) && !isSpirit(usr))
+ usr << "A strange collection of symbols drawn in blood."
return
+ if(!desc)
+ usr << "A spell circle drawn in blood. It reads: [word1] [word2] [word3]."
+ else
+ usr << "Explosive Runes inscription in blood. It reads: [desc]."
+
+ return
- attackby(I as obj, user as mob)
- if(istype(I, /obj/item/weapon/tome) && iscultist(user))
- user << "You retrace your steps, carefully undoing the lines of the rune."
- del(src)
- return
- else if(istype(I, /obj/item/weapon/nullrod))
- user << "\blue You disrupt the vile magic with the deadening field of the null rod!"
- del(src)
- return
+/obj/effect/rune/attackby(I as obj, user as mob)
+ if(istype(I, /obj/item/weapon/tome) && iscultist(user))
+ user << "You retrace your steps, carefully undoing the lines of the rune."
+ del(src)
return
+ else if(istype(I, /obj/item/weapon/nullrod))
+ user << "\blue You disrupt the vile magic with the deadening field of the null rod!"
+ del(src)
+ return
+ return
- attack_hand(mob/living/user as mob)
- if(!iscultist(user))
- user << "You can't mouth the arcane scratchings without fumbling over them."
- return
- if(istype(user.wear_mask, /obj/item/clothing/mask/muzzle))
- user << "You are unable to speak the words of the rune."
- return
- if(!word1 || !word2 || !word3 || prob(user.getBrainLoss()))
- return fizzle()
-// if(!src.visibility)
-// src.visibility=1
- if(word1 == cultwords["travel"] && word2 == cultwords["self"])
- return teleport(src.word3)
- if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"])
- return tomesummon()
- if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"])
- return armor()
- if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"])
- return convert()
- if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"])
- return tearreality()
- if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"])
- return emp(src.loc,3)
- if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"])
- return drain()
- if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"])
- return seer()
- if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"])
- return raise()
- if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"])
- return obscure(4)
- if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"])
- return ajourney()
- if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"])
- return manifest()
- if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"])
- return talisman()
- if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"])
- return sacrifice()
- if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"])
- return revealrunes(src)
- if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"])
- return wall()
- if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"])
- return freedom()
- if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"])
- return cultsummon()
- if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"])
- return deafen()
- if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"])
- return blind()
- if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"])
- return bloodboil()
- if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"])
- return communicate()
- if(word1 == cultwords["travel"] && word2 == cultwords["other"])
- return itemport(src.word3)
- if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"])
- return runestun()
- else
- return fizzle()
+/obj/effect/rune/proc/get_word_string()
+ if (word1 == cultwords["travel"])
+ if (word2 == cultwords["self"])
+ return "teleport"
+ if (word2 == cultwords["other"])
+ return "itemport"
+ return "[rune_to_english[word1]]_[rune_to_english[word2]]_[rune_to_english[word3]]"
+
+
+/obj/effect/rune
+ var/list/effect_dictionary = list( "teleport"=/obj/effect/rune/proc/teleportRune,
+ "itemport"=/obj/effect/rune/proc/itemportRune,
+ "see_blood_hell"=/obj/effect/rune/proc/tomesummon,
+ "hell_destroy_other"=/obj/effect/rune/proc/armor,
+ "join_blood_self"=/obj/effect/rune/proc/convert,
+ "hell_join_self"=/obj/effect/rune/proc/tearreality,
+ "destroy_see_technology"=/obj/effect/rune/proc/empRune,
+ "travel_blood_self"=/obj/effect/rune/proc/drain,
+ "see_hell_join"=/obj/effect/rune/proc/seer,
+ "blood_join_hell"=/obj/effect/rune/proc/raise,
+ "hide_see_blood"=/obj/effect/rune/proc/obscureRune,
+ "hell_travel_self"=/obj/effect/rune/proc/ajourney,
+ "blood_see_travel"=/obj/effect/rune/proc/manifest,
+ "hell_technology_join"=/obj/effect/rune/proc/talisman,
+ "hell_blood_join"=/obj/effect/rune/proc/sacrifice,
+ "blood_see_hide"=/obj/effect/rune/proc/revealrunesrune,
+ "destroy_travel_self"=/obj/effect/rune/proc/wall,
+ "travel_technology_other"=/obj/effect/rune/proc/freedom,
+ "join_other_self"=/obj/effect/rune/proc/cultsummon,
+ "hide_other_see"=/obj/effect/rune/proc/deafen,
+ "destroy_see_other"=/obj/effect/rune/proc/blind,
+ "destroy_see_blood"=/obj/effect/rune/proc/bloodboil,
+ "self_other_technology"=/obj/effect/rune/proc/communicate,
+ "join_hide_technology"=/obj/effect/rune/proc/runestun )
+
+/obj/effect/rune/attack_hand(mob/living/user as mob)
+ if(!iscultist(user))
+ user << "You can't mouth the arcane scratchings without fumbling over them."
+ return
+ if(istype(user.wear_mask, /obj/item/clothing/mask/muzzle))
+ user << "You are unable to speak the words of the rune."
+ return
+ if(user.silent) // checking if we've been muted somehow
+ user << "You are unable to speak at all! You cannot say the words of the rune."
+ if(!word1 || !word2 || !word3 || prob(user.getBrainLoss()))
+ return fizzle()
+
+ var/word_string = get_word_string()
+ if (word_string in effect_dictionary)
+ cult_log("of type [effect_dictionary[word_string]] activated by [key_name_admin(user)].")
+ return call(src,effect_dictionary[word_string])()
+ return fizzle()
- proc
- fizzle()
- if(istype(src,/obj/effect/rune))
- usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix."))
- else
- usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix."))
- for (var/mob/V in viewers(src))
- V.show_message("\red The markings pulse with a small burst of light, then fall dark.", 3, "\red You hear a faint fizzle.", 2)
- return
+/obj/effect/rune/proc/fizzle()
+ if(istype(src,/obj/effect/rune))
+ usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix."))
+ else
+ usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix."))
+ for (var/mob/V in viewers(src))
+ V.show_message("\red The markings pulse with a small burst of light, then fall dark.", 3, "\red You hear a faint fizzle.", 2)
+ return
- check_icon()
- icon = get_uristrune_cult(word1, word2, word3)
+/obj/effect/rune/proc/check_icon()
+ icon = get_uristrune_cult(word1, word2, word3)
/obj/item/weapon/tome
name = "arcane tome"
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index a9217cd00f..d78c3333ea 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -1,197 +1,205 @@
var/list/sacrificed = list()
-/obj/effect/rune
+
/////////////////////////////////////////FIRST RUNE
- proc
- teleport(var/key)
- var/mob/living/user = usr
- var/allrunesloc[]
- allrunesloc = new/list()
- var/index = 0
- // var/tempnum = 0
- for(var/obj/effect/rune/R in world)
- if(R == src)
- continue
- if(R.word1 == cultwords["travel"] && R.word2 == cultwords["self"] && R.word3 == key && R.z != 2)
- index++
- allrunesloc.len = index
- allrunesloc[index] = R.loc
- if(index >= 5)
- user << "\red You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric"
- if (istype(user, /mob/living))
- user.take_overall_damage(5, 0)
- del(src)
- if(allrunesloc && index != 0)
- if(istype(src,/obj/effect/rune))
- user.say("Sas[pick("'","`")]so c'arta forbici!")//Only you can stop auto-muting
- else
- user.whisper("Sas[pick("'","`")]so c'arta forbici!")
- user.visible_message("\red [user] disappears in a flash of red light!", \
- "\red You feel as your body gets dragged through the dimension of Nar-Sie!", \
- "\red You hear a sickening crunch and sloshing of viscera.")
- user.loc = allrunesloc[rand(1,index)]
- return
- if(istype(src,/obj/effect/rune))
- return fizzle() //Use friggin manuals, Dorf, your list was of zero length.
- else
- call(/obj/effect/rune/proc/fizzle)()
- return
+/obj/effect/rune/proc/teleportRune()
+ return teleport(src.word3)
+
+/obj/effect/rune/proc/teleport(var/key)
+ var/mob/living/user = usr
+ var/allrunesloc[]
+ allrunesloc = new/list()
+ var/index = 0
+// var/tempnum = 0
+ for(var/obj/effect/rune/R in world)
+ if(R == src)
+ continue
+ if(R.word1 == cultwords["travel"] && R.word2 == cultwords["self"] && R.word3 == key && R.z != 2)
+ index++
+ allrunesloc.len = index
+ allrunesloc[index] = R.loc
+ if(index >= 5)
+ user << "\red You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric"
+ if (istype(user, /mob/living))
+ user.take_overall_damage(5, 0)
+ del(src)
+ if(allrunesloc && index != 0)
+ if(istype(src,/obj/effect/rune))
+ user.say("Sas[pick("'","`")]so c'arta forbici!")//Only you can stop auto-muting
+ else
+ user.whisper("Sas[pick("'","`")]so c'arta forbici!")
+ user.visible_message("\red [user] disappears in a flash of red light!", \
+ "\red You feel as your body gets dragged through the dimension of Nar-Sie!", \
+ "\red You hear a sickening crunch and sloshing of viscera.")
+ user.loc = allrunesloc[rand(1,index)]
+ return
+ if(istype(src,/obj/effect/rune))
+ return fizzle() //Use friggin manuals, Dorf, your list was of zero length.
+ else
+ call(/obj/effect/rune/proc/fizzle)()
+ return
-
- itemport(var/key)
+/obj/effect/rune/proc/itemportRune()
+ return itemport(src.word3)
+/obj/effect/rune/proc/itemport(var/key)
// var/allrunesloc[]
// allrunesloc = new/list()
// var/index = 0
- // var/tempnum = 0
- var/culcount = 0
- var/runecount = 0
- var/obj/effect/rune/IP = null
- var/mob/living/user = usr
- for(var/obj/effect/rune/R in world)
- if(R == src)
- continue
- if(R.word1 == cultwords["travel"] && R.word2 == cultwords["other"] && R.word3 == key)
- IP = R
- runecount++
- if(runecount >= 2)
- user << "\red You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric"
- if (istype(user, /mob/living))
- user.take_overall_damage(5, 0)
- del(src)
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- culcount++
- if(culcount>=3)
- user.say("Sas[pick("'","`")]so c'arta forbici tarem!")
- user.visible_message("\red You feel air moving from the rune - like as it was swapped with somewhere else.", \
- "\red You feel air moving from the rune - like as it was swapped with somewhere else.", \
- "\red You smell ozone.")
- for(var/obj/O in src.loc)
- if(!O.anchored)
- O.loc = IP.loc
- for(var/mob/M in src.loc)
- M.loc = IP.loc
- return
+// var/tempnum = 0
+ var/culcount = 0
+ var/runecount = 0
+ var/obj/effect/rune/IP = null
+ var/mob/living/user = usr
+ for(var/obj/effect/rune/R in world)
+ if(R == src)
+ continue
+ if(R.word1 == cultwords["travel"] && R.word2 == cultwords["other"] && R.word3 == key)
+ IP = R
+ runecount++
+ if(runecount >= 2)
+ user << "\red You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric"
+ if (istype(user, /mob/living))
+ user.take_overall_damage(5, 0)
+ del(src)
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ culcount++
+ if(culcount>=3)
+ user.say("Sas[pick("'","`")]so c'arta forbici tarem!")
+ user.visible_message("\red You feel air moving from the rune - like as it was swapped with somewhere else.", \
+ "\red You feel air moving from the rune - like as it was swapped with somewhere else.", \
+ "\red You smell ozone.")
+ for(var/obj/O in src.loc)
+ if(!O.anchored)
+ O.loc = IP.loc
+ for(var/mob/M in src.loc)
+ M.loc = IP.loc
+ return
- return fizzle()
+ return fizzle()
/////////////////////////////////////////SECOND RUNE
- tomesummon()
- if(istype(src,/obj/effect/rune))
- usr.say("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!")
- else
- usr.whisper("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!")
- usr.visible_message("\red Rune disappears with a flash of red light, and in its place now a book lies.", \
- "\red You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a book.", \
- "\red You hear a pop and smell ozone.")
- if(istype(src,/obj/effect/rune))
- new /obj/item/weapon/tome(src.loc)
- else
- new /obj/item/weapon/tome(usr.loc)
- del(src)
- return
+/obj/effect/rune/proc/tomesummon()
+ if(istype(src,/obj/effect/rune))
+ usr.say("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!")
+ else
+ usr.whisper("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!")
+ usr.visible_message("\red Rune disappears with a flash of red light, and in its place now a book lies.", \
+ "\red You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a book.", \
+ "\red You hear a pop and smell ozone.")
+ if(istype(src,/obj/effect/rune))
+ new /obj/item/weapon/tome(src.loc)
+ else
+ new /obj/item/weapon/tome(usr.loc)
+ del(src)
+ return
/////////////////////////////////////////THIRD RUNE
- convert()
- for(var/mob/living/carbon/M in src.loc)
- if(iscultist(M))
- continue
- if(M.stat==2)
- continue
- usr.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!")
- M.visible_message("\red [M] writhes in pain as the markings below him glow a bloody red.", \
- "\red AAAAAAHHHH!.", \
- "\red You hear an anguished scream.")
- if(is_convertable_to_cult(M.mind) && !jobban_isbanned(M, "cultist"))//putting jobban check here because is_convertable uses mind as argument
- ticker.mode.add_cultist(M.mind)
- M.mind.special_role = "Cultist"
- M << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
- M << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
- return 1
- else
- M << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
- M << "And you were able to force it out of your mind. You now know the truth, there's something horrible out there, stop it and its minions at all costs."
- return 0
+/obj/effect/rune/proc/convert()
+ for(var/mob/living/carbon/M in src.loc)
+ if(iscultist(M))
+ continue
+ if(M.stat==2)
+ continue
+ usr.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!")
+ M.visible_message("\red [M] writhes in pain as the markings below him glow a bloody red.", \
+ "\red AAAAAAHHHH!.", \
+ "\red You hear an anguished scream.")
+ cult_log("[key_name_admin(usr)] tried to convert [key_name_admin(M)]")
+ if(is_convertable_to_cult(M.mind) && !jobban_isbanned(M, "cultist"))//putting jobban check here because is_convertable uses mind as argument
+ ticker.mode.add_cultist(M.mind)
+ M.mind.special_role = "Cultist"
+ M << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
+ M << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
+ return 1
+ else
+ M << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
+ M << "And you were able to force it out of your mind. You now know the truth, there's something horrible out there, stop it and its minions at all costs."
+ return 0
- return fizzle()
+ return fizzle()
/////////////////////////////////////////FOURTH RUNE
- tearreality()
- var/cultist_count = 0
- for(var/mob/M in range(1,src))
- if(iscultist(M) && !M.stat)
- M.say("Tok-lyr rqa'nap g[pick("'","`")]lt-ulotf!")
- cultist_count += 1
- if(cultist_count >= 9)
- new /obj/machinery/singularity/narsie/large(src.loc)
- if(ticker.mode.name == "cult")
- ticker.mode:eldergod = 0
- return
- else
- return fizzle()
+/obj/effect/rune/proc/tearreality()
+ var/cultist_count = 0
+ for(var/mob/M in range(1,src))
+ if(iscultist(M) && !M.stat)
+ M.say("Tok-lyr rqa'nap g[pick("'","`")]lt-ulotf!")
+ cultist_count += 1
+ if(cultist_count >= 9)
+ cult_log("THE CULT HAS SUMMONED NAR'SIE. GGNORE")
+ new /obj/machinery/singularity/narsie/large(src.loc)
+ if(ticker.mode.name == "cult")
+ ticker.mode:eldergod = 0
+ return
+ else
+ return fizzle()
/////////////////////////////////////////FIFTH RUNE
-
- emp(var/U,var/range_red) //range_red - var which determines by which number to reduce the default emp range, U is the source loc, needed because of talisman emps which are held in hand at the moment of using and that apparently messes things up -- Urist
- if(istype(src,/obj/effect/rune))
- usr.say("Ta'gh fara[pick("'","`")]qha fel d'amar det!")
- else
- usr.whisper("Ta'gh fara[pick("'","`")]qha fel d'amar det!")
- playsound(U, 'sound/items/Welder2.ogg', 25, 1)
- var/turf/T = get_turf(U)
- if(T)
- T.hotspot_expose(700,125)
- var/rune = src // detaching the proc - in theory
- empulse(U, (range_red - 2), range_red)
- del(rune)
- return
+/obj/effect/rune/proc/empRune()
+ emp(src.loc,3)
+
+/obj/effect/rune/proc/emp(var/U,var/range_red) //range_red - var which determines by which number to reduce the default emp range, U is the source loc, needed because of talisman emps which are held in hand at the moment of using and that apparently messes things up -- Urist
+ if(istype(src,/obj/effect/rune))
+ usr.say("Ta'gh fara[pick("'","`")]qha fel d'amar det!")
+ else
+ usr.whisper("Ta'gh fara[pick("'","`")]qha fel d'amar det!")
+ playsound(U, 'sound/items/Welder2.ogg', 25, 1)
+ var/turf/T = get_turf(U)
+ if(T)
+ T.hotspot_expose(700,125)
+ var/rune = src // detaching the proc - in theory
+ empulse(U, (range_red - 2), range_red)
+ del(rune)
+ return
/////////////////////////////////////////SIXTH RUNE
- drain()
- var/drain = 0
- for(var/obj/effect/rune/R in world)
- if(R.word1==cultwords["travel"] && R.word2==cultwords["blood"] && R.word3==cultwords["self"])
- for(var/mob/living/carbon/D in R.loc)
- if(D.stat!=2)
- var/bdrain = rand(1,25)
- D << "\red You feel weakened."
- D.take_overall_damage(bdrain, 0)
- drain += bdrain
- if(!drain)
- return fizzle()
- usr.say ("Yu[pick("'","`")]gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!")
- usr.visible_message("\red Blood flows from the rune into [usr]!", \
- "\red The blood starts flowing from the rune and into your frail mortal body. You feel... empowered.", \
- "\red You hear a liquid flowing.")
- var/mob/living/user = usr
- if(user.bhunger)
- user.bhunger = max(user.bhunger-2*drain,0)
- if(drain>=50)
- user.visible_message("\red [user]'s eyes give off eerie red glow!", \
- "\red ...but it wasn't nearly enough. You crave, crave for more. The hunger consumes you from within.", \
- "\red You hear a heartbeat.")
- user.bhunger += drain
- src = user
- spawn()
- for (,user.bhunger>0,user.bhunger--)
- sleep(50)
- user.take_overall_damage(3, 0)
- return
- user.heal_organ_damage(drain%5, 0)
- drain-=drain%5
- for (,drain>0,drain-=5)
- sleep(2)
- user.heal_organ_damage(5, 0)
- return
+/obj/effect/rune/proc/drain()
+ var/drain = 0
+ for(var/obj/effect/rune/R in world)
+ if(R.word1==cultwords["travel"] && R.word2==cultwords["blood"] && R.word3==cultwords["self"])
+ for(var/mob/living/carbon/D in R.loc)
+ if(D.stat!=2)
+ cult_log("[key_name_admin(usr)] has drained blood from [key_name_admin(D)]")
+ var/bdrain = rand(1,25)
+ D << "\red You feel weakened."
+ D.take_overall_damage(bdrain, 0)
+ drain += bdrain
+ if(!drain)
+ return fizzle()
+ usr.say ("Yu[pick("'","`")]gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!")
+ usr.visible_message("\red Blood flows from the rune into [usr]!", \
+ "\red The blood starts flowing from the rune and into your frail mortal body. You feel... empowered.", \
+ "\red You hear a liquid flowing.")
+ var/mob/living/user = usr
+ if(user.bhunger)
+ user.bhunger = max(user.bhunger-2*drain,0)
+ if(drain>=50)
+ user.visible_message("\red [user]'s eyes give off eerie red glow!", \
+ "\red ...but it wasn't nearly enough. You crave, crave for more. The hunger consumes you from within.", \
+ "\red You hear a heartbeat.")
+ user.bhunger += drain
+ src = user
+ spawn()
+ for (,user.bhunger>0,user.bhunger--)
+ sleep(50)
+ user.take_overall_damage(3, 0)
+ return
+ user.heal_organ_damage(drain%5, 0)
+ drain-=drain%5
+ for (,drain>0,drain-=5)
+ sleep(2)
+ user.heal_organ_damage(5, 0)
+ return
@@ -200,98 +208,98 @@ var/list/sacrificed = list()
/////////////////////////////////////////SEVENTH RUNE
- seer()
- if(usr.loc==src.loc)
- if(usr.seer==1)
- usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.")
- usr << "\red The world beyond fades from your vision."
- usr.see_invisible = SEE_INVISIBLE_LIVING
- usr.seer = 0
- else if(usr.see_invisible!=SEE_INVISIBLE_LIVING)
- usr << "\red The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision."
- usr.see_invisible = SEE_INVISIBLE_OBSERVER
- usr.seer = 0
- else
- usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!")
- usr << "\red The world beyond opens to your eyes."
- usr.see_invisible = SEE_INVISIBLE_OBSERVER
- usr.seer = 1
- return
- return fizzle()
+/obj/effect/rune/proc/seer()
+ if(usr.loc==src.loc)
+ if(usr.seer==1)
+ usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.")
+ usr << "\red The world beyond fades from your vision."
+ usr.see_invisible = SEE_INVISIBLE_LIVING
+ usr.seer = 0
+ else if(usr.see_invisible!=SEE_INVISIBLE_LIVING)
+ usr << "\red The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision."
+ usr.see_invisible = SEE_INVISIBLE_OBSERVER
+ usr.seer = 0
+ else
+ usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!")
+ usr << "\red The world beyond opens to your eyes."
+ usr.see_invisible = SEE_INVISIBLE_OBSERVER
+ usr.seer = 1
+ return
+ return fizzle()
/////////////////////////////////////////EIGHTH RUNE
- raise()
- var/mob/living/carbon/human/corpse_to_raise
- var/mob/living/carbon/human/body_to_sacrifice
+/obj/effect/rune/proc/raise()
+ var/mob/living/carbon/human/corpse_to_raise
+ var/mob/living/carbon/human/body_to_sacrifice
- var/is_sacrifice_target = 0
- for(var/mob/living/carbon/human/M in src.loc)
- if(M.stat == DEAD)
- if(ticker.mode.name == "cult" && M.mind == ticker.mode:sacrifice_target)
+ var/is_sacrifice_target = 0
+ for(var/mob/living/carbon/human/M in src.loc)
+ if(M.stat == DEAD)
+ if(ticker.mode.name == "cult" && M.mind == ticker.mode:sacrifice_target)
+ is_sacrifice_target = 1
+ else
+ corpse_to_raise = M
+ if(M.key)
+ M.ghostize(1) //kick them out of their body
+ break
+ if(!corpse_to_raise)
+ if(is_sacrifice_target)
+ usr << "\red The Geometer of blood wants this mortal for himself."
+ return fizzle()
+
+
+ is_sacrifice_target = 0
+ find_sacrifice:
+ for(var/obj/effect/rune/R in world)
+ if(R.word1==cultwords["blood"] && R.word2==cultwords["join"] && R.word3==cultwords["hell"])
+ for(var/mob/living/carbon/human/N in R.loc)
+ if(ticker.mode.name == "cult" && N.mind && N.mind == ticker.mode:sacrifice_target)
is_sacrifice_target = 1
else
- corpse_to_raise = M
- if(M.key)
- M.ghostize(1) //kick them out of their body
- break
- if(!corpse_to_raise)
- if(is_sacrifice_target)
- usr << "\red The Geometer of blood wants this mortal for himself."
- return fizzle()
+ if(N.stat!= DEAD)
+ body_to_sacrifice = N
+ break find_sacrifice
+ if(!body_to_sacrifice)
+ if (is_sacrifice_target)
+ usr << "\red The Geometer of blood wants that corpse for himself."
+ else
+ usr << "\red The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used."
+ return fizzle()
- is_sacrifice_target = 0
- find_sacrifice:
- for(var/obj/effect/rune/R in world)
- if(R.word1==cultwords["blood"] && R.word2==cultwords["join"] && R.word3==cultwords["hell"])
- for(var/mob/living/carbon/human/N in R.loc)
- if(ticker.mode.name == "cult" && N.mind && N.mind == ticker.mode:sacrifice_target)
- is_sacrifice_target = 1
- else
- if(N.stat!= DEAD)
- body_to_sacrifice = N
- break find_sacrifice
+ var/mob/dead/observer/ghost
+ for(var/mob/dead/observer/O in loc)
+ if(!O.client) continue
+ if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
+ ghost = O
+ break
- if(!body_to_sacrifice)
- if (is_sacrifice_target)
- usr << "\red The Geometer of blood wants that corpse for himself."
- else
- usr << "\red The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used."
- return fizzle()
+ if(!ghost)
+ usr << "\red You require a restless spirit which clings to this world. Beckon their prescence with the sacred chants of Nar-Sie."
+ return fizzle()
- var/mob/dead/observer/ghost
- for(var/mob/dead/observer/O in loc)
- if(!O.client) continue
- if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
- ghost = O
- break
+ corpse_to_raise.revive()
- if(!ghost)
- usr << "\red You require a restless spirit which clings to this world. Beckon their prescence with the sacred chants of Nar-Sie."
- return fizzle()
-
- corpse_to_raise.revive()
-
- corpse_to_raise.key = ghost.key //the corpse will keep its old mind! but a new player takes ownership of it (they are essentially possessed)
- //This means, should that player leave the body, the original may re-enter
- usr.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
- corpse_to_raise.visible_message("\red [corpse_to_raise]'s eyes glow with a faint red as he stands up, slowly starting to breathe again.", \
- "\red Life... I'm alive again...", \
- "\red You hear a faint, slightly familiar whisper.")
- body_to_sacrifice.visible_message("\red [body_to_sacrifice] is torn apart, a black smoke swiftly dissipating from his remains!", \
- "\red You feel as your blood boils, tearing you apart.", \
- "\red You hear a thousand voices, all crying in pain.")
- body_to_sacrifice.gib()
+ corpse_to_raise.key = ghost.key //the corpse will keep its old mind! but a new player takes ownership of it (they are essentially possessed)
+ //This means, should that player leave the body, the original may re-enter
+ usr.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
+ corpse_to_raise.visible_message("\red [corpse_to_raise]'s eyes glow with a faint red as he stands up, slowly starting to breathe again.", \
+ "\red Life... I'm alive again...", \
+ "\red You hear a faint, slightly familiar whisper.")
+ body_to_sacrifice.visible_message("\red [body_to_sacrifice] is torn apart, a black smoke swiftly dissipating from his remains!", \
+ "\red You feel as your blood boils, tearing you apart.", \
+ "\red You hear a thousand voices, all crying in pain.")
+ body_to_sacrifice.gib()
// if(ticker.mode.name == "cult")
// ticker.mode:add_cultist(corpse_to_raise.mind)
// else
// ticker.mode.cult |= corpse_to_raise.mind
- corpse_to_raise << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
- corpse_to_raise << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
- return
+ corpse_to_raise << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
+ corpse_to_raise << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
+ return
@@ -299,116 +307,118 @@ var/list/sacrificed = list()
/////////////////////////////////////////NINETH RUNE
- obscure(var/rad)
- var/S=0
- for(var/obj/effect/rune/R in orange(rad,src))
- if(R!=src)
- R.invisibility=INVISIBILITY_OBSERVER
- S=1
- if(S)
- if(istype(src,/obj/effect/rune))
- usr.say("Kla[pick("'","`")]atu barada nikt'o!")
- for (var/mob/V in viewers(src))
- V.show_message("\red The rune turns into gray dust, veiling the surrounding runes.", 3)
- del(src)
- else
- usr.whisper("Kla[pick("'","`")]atu barada nikt'o!")
- usr << "\red Your talisman turns into gray dust, veiling the surrounding runes."
- for (var/mob/V in orange(1,src))
- if(V!=usr)
- V.show_message("\red Dust emanates from [usr]'s hands for a moment.", 3)
+/obj/effect/rune/proc/obscureRune()
+ return obscure(4)
+/obj/effect/rune/proc/obscure(var/rad)
+ var/S=0
+ for(var/obj/effect/rune/R in orange(rad,src))
+ if(R!=src)
+ R.invisibility=INVISIBILITY_OBSERVER
+ S=1
+ if(S)
+ if(istype(src,/obj/effect/rune))
+ usr.say("Kla[pick("'","`")]atu barada nikt'o!")
+ for (var/mob/V in viewers(src))
+ V.show_message("\red The rune turns into gray dust, veiling the surrounding runes.", 3)
+ del(src)
+ else
+ usr.whisper("Kla[pick("'","`")]atu barada nikt'o!")
+ usr << "\red Your talisman turns into gray dust, veiling the surrounding runes."
+ for (var/mob/V in orange(1,src))
+ if(V!=usr)
+ V.show_message("\red Dust emanates from [usr]'s hands for a moment.", 3)
- return
- if(istype(src,/obj/effect/rune))
- return fizzle()
- else
- call(/obj/effect/rune/proc/fizzle)()
- return
+ return
+ if(istype(src,/obj/effect/rune))
+ return fizzle()
+ else
+ call(/obj/effect/rune/proc/fizzle)()
+ return
/////////////////////////////////////////TENTH RUNE
- ajourney() //some bits copypastaed from admin tools - Urist
- if(usr.loc==src.loc)
- var/mob/living/carbon/human/L = usr
- usr.say("Fwe[pick("'","`")]sh mah erl nyag r'ya!")
- usr.visible_message("\red [usr]'s eyes glow blue as \he freezes in place, absolutely motionless.", \
- "\red The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry...", \
- "\red You hear only complete silence for a moment.")
- usr.ghostize(1)
- L.ajourn = 1
- while(L)
- if(L.key)
- L.ajourn=0
- return
- else
- L.take_organ_damage(10, 0)
- sleep(100)
- return fizzle()
+/obj/effect/rune/proc/ajourney() //some bits copypastaed from admin tools - Urist
+ if(usr.loc==src.loc)
+ var/mob/living/carbon/human/L = usr
+ usr.say("Fwe[pick("'","`")]sh mah erl nyag r'ya!")
+ usr.visible_message("\red [usr]'s eyes glow blue as \he freezes in place, absolutely motionless.", \
+ "\red The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry...", \
+ "\red You hear only complete silence for a moment.")
+ usr.ghostize(1)
+ L.ajourn = 1
+ while(L)
+ if(L.key)
+ L.ajourn=0
+ return
+ else
+ L.take_organ_damage(10, 0)
+ sleep(100)
+ return fizzle()
/////////////////////////////////////////ELEVENTH RUNE
- manifest()
- var/obj/effect/rune/this_rune = src
- src = null
- if(usr.loc!=this_rune.loc)
- return this_rune.fizzle()
- var/mob/dead/observer/ghost
- for(var/mob/dead/observer/O in this_rune.loc)
- if(!O.client) continue
- if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
- ghost = O
- break
- if(!ghost)
- return this_rune.fizzle()
- if(jobban_isbanned(ghost, "cultist"))
- return this_rune.fizzle()
+/obj/effect/rune/proc/manifest()
+ var/obj/effect/rune/this_rune = src
+ src = null
+ if(usr.loc!=this_rune.loc)
+ return this_rune.fizzle()
+ var/mob/dead/observer/ghost
+ for(var/mob/dead/observer/O in this_rune.loc)
+ if(!O.client) continue
+ if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
+ ghost = O
+ break
+ if(!ghost)
+ return this_rune.fizzle()
+ if(jobban_isbanned(ghost, "cultist"))
+ return this_rune.fizzle()
- usr.say("Gal'h'rfikk harfrandid mud[pick("'","`")]gib!")
- var/mob/living/carbon/human/dummy/D = new(this_rune.loc)
- usr.visible_message("\red A shape forms in the center of the rune. A shape of... a man.", \
- "\red A shape forms in the center of the rune. A shape of... a man.", \
- "\red You hear liquid flowing.")
- D.real_name = "Unknown"
- var/chose_name = 0
- for(var/obj/item/weapon/paper/P in this_rune.loc)
- if(P.info)
- D.real_name = copytext(P.info, findtext(P.info,">")+1, findtext(P.info,"<",2) )
- chose_name = 1
- break
- if(!chose_name)
- D.real_name = "[pick(first_names_male)] [pick(last_names)]"
- D.universal_speak = 1
- D.status_flags &= ~GODMODE
- D.s_tone = 35
- D.b_eyes = 200
- D.r_eyes = 200
- D.g_eyes = 200
- D.underwear = 0
+ usr.say("Gal'h'rfikk harfrandid mud[pick("'","`")]gib!")
+ var/mob/living/carbon/human/dummy/D = new(this_rune.loc)
+ usr.visible_message("\red A shape forms in the center of the rune. A shape of... a man.", \
+ "\red A shape forms in the center of the rune. A shape of... a man.", \
+ "\red You hear liquid flowing.")
+ D.real_name = "Unknown"
+ var/chose_name = 0
+ for(var/obj/item/weapon/paper/P in this_rune.loc)
+ if(P.info)
+ D.real_name = copytext(P.info, findtext(P.info,">")+1, findtext(P.info,"<",2) )
+ chose_name = 1
+ break
+ if(!chose_name)
+ D.real_name = "[pick(first_names_male)] [pick(last_names)]"
+ D.universal_speak = 1
+ D.status_flags &= ~GODMODE
+ D.s_tone = 35
+ D.b_eyes = 200
+ D.r_eyes = 200
+ D.g_eyes = 200
+ D.underwear = 0
- D.key = ghost.key
+ D.key = ghost.key
- if(ticker.mode.name == "cult")
- ticker.mode:add_cultist(D.mind)
- else
- ticker.mode.cult+=D.mind
+ if(ticker.mode.name == "cult")
+ ticker.mode:add_cultist(D.mind)
+ else
+ ticker.mode.cult+=D.mind
- D.mind.special_role = "Cultist"
- D << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
- D << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
+ D.mind.special_role = "Cultist"
+ D << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root."
+ D << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back."
- var/mob/living/user = usr
- while(this_rune && user && user.stat==CONSCIOUS && user.client && user.loc==this_rune.loc)
- user.take_organ_damage(1, 0)
- sleep(30)
- if(D)
- D.visible_message("\red [D] slowly dissipates into dust and bones.", \
- "\red You feel pain, as bonds formed between your soul and this homunculus break.", \
- "\red You hear faint rustle.")
- D.dust()
- return
+ var/mob/living/user = usr
+ while(this_rune && user && user.stat==CONSCIOUS && user.client && user.loc==this_rune.loc)
+ user.take_organ_damage(1, 0)
+ sleep(30)
+ if(D)
+ D.visible_message("\red [D] slowly dissipates into dust and bones.", \
+ "\red You feel pain, as bonds formed between your soul and this homunculus break.", \
+ "\red You hear faint rustle.")
+ D.dust()
+ return
@@ -416,625 +426,649 @@ var/list/sacrificed = list()
/////////////////////////////////////////TWELFTH RUNE
- talisman()//only hide, emp, teleport, deafen, blind and tome runes can be imbued atm
- var/obj/item/weapon/paper/newtalisman
- var/unsuitable_newtalisman = 0
- for(var/obj/item/weapon/paper/P in src.loc)
- if(!P.info)
- newtalisman = P
- break
- else
- unsuitable_newtalisman = 1
- if (!newtalisman)
- if (unsuitable_newtalisman)
- usr << "\red The blank is tainted. It is unsuitable."
- return fizzle()
+/obj/effect/rune/proc/talisman()//only hide, emp, teleport, deafen, blind and tome runes can be imbued atm
+ var/obj/item/weapon/paper/newtalisman
+ var/unsuitable_newtalisman = 0
+ for(var/obj/item/weapon/paper/P in src.loc)
+ if(!P.info)
+ newtalisman = P
+ break
+ else
+ unsuitable_newtalisman = 1
+ if (!newtalisman)
+ if (unsuitable_newtalisman)
+ usr << "\red The blank is tainted. It is unsuitable."
+ return fizzle()
- var/obj/effect/rune/imbued_from
- var/obj/item/weapon/paper/talisman/T
- for(var/obj/effect/rune/R in orange(1,src))
- if(R==src)
- continue
- if(R.word1==cultwords["travel"] && R.word2==cultwords["self"]) //teleport
- T = new(src.loc)
- T.imbue = "[R.word3]"
- T.info = "[R.word3]"
- imbued_from = R
- break
- if(R.word1==cultwords["see"] && R.word2==cultwords["blood"] && R.word3==cultwords["hell"]) //tome
- T = new(src.loc)
- T.imbue = "newtome"
- imbued_from = R
- break
- if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["technology"]) //emp
- T = new(src.loc)
- T.imbue = "emp"
- imbued_from = R
- break
- if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["destroy"]) //conceal
- T = new(src.loc)
- T.imbue = "conceal"
- imbued_from = R
- break
- if(R.word1==cultwords["hell"] && R.word2==cultwords["destroy"] && R.word3==cultwords["other"]) //armor
- T = new(src.loc)
- T.imbue = "armor"
- imbued_from = R
- break
- if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["hide"]) //reveal
- T = new(src.loc)
- T.imbue = "revealrunes"
- imbued_from = R
- break
- if(R.word1==cultwords["hide"] && R.word2==cultwords["other"] && R.word3==cultwords["see"]) //deafen
- T = new(src.loc)
- T.imbue = "deafen"
- imbued_from = R
- break
- if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["other"]) //blind
- T = new(src.loc)
- T.imbue = "blind"
- imbued_from = R
- break
- if(R.word1==cultwords["self"] && R.word2==cultwords["other"] && R.word3==cultwords["technology"]) //communicat
- T = new(src.loc)
- T.imbue = "communicate"
- imbued_from = R
- break
- if(R.word1==cultwords["join"] && R.word2==cultwords["hide"] && R.word3==cultwords["technology"]) //communicat
- T = new(src.loc)
- T.imbue = "runestun"
- imbued_from = R
- break
- if (imbued_from)
- for (var/mob/V in viewers(src))
- V.show_message("\red The runes turn into dust, which then forms into an arcane image on the paper.", 3)
- usr.say("H'drak v[pick("'","`")]loso, mir'kanas verbot!")
- del(imbued_from)
- del(newtalisman)
- else
- return fizzle()
+ var/obj/effect/rune/imbued_from
+ var/obj/item/weapon/paper/talisman/T
+ for(var/obj/effect/rune/R in orange(1,src))
+ if(R==src)
+ continue
+ if(R.word1==cultwords["travel"] && R.word2==cultwords["self"]) //teleport
+ T = new(src.loc)
+ T.imbue = "[R.word3]"
+ T.info = "[R.word3]"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["see"] && R.word2==cultwords["blood"] && R.word3==cultwords["hell"]) //tome
+ T = new(src.loc)
+ T.imbue = "newtome"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["technology"]) //emp
+ T = new(src.loc)
+ T.imbue = "emp"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["destroy"]) //conceal
+ T = new(src.loc)
+ T.imbue = "conceal"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["hell"] && R.word2==cultwords["destroy"] && R.word3==cultwords["other"]) //armor
+ T = new(src.loc)
+ T.imbue = "armor"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["hide"]) //reveal
+ T = new(src.loc)
+ T.imbue = "revealrunes"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["hide"] && R.word2==cultwords["other"] && R.word3==cultwords["see"]) //deafen
+ T = new(src.loc)
+ T.imbue = "deafen"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["other"]) //blind
+ T = new(src.loc)
+ T.imbue = "blind"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["self"] && R.word2==cultwords["other"] && R.word3==cultwords["technology"]) //communicat
+ T = new(src.loc)
+ T.imbue = "communicate"
+ imbued_from = R
+ break
+ if(R.word1==cultwords["join"] && R.word2==cultwords["hide"] && R.word3==cultwords["technology"]) //communicat
+ T = new(src.loc)
+ T.imbue = "runestun"
+ imbued_from = R
+ break
+ if (imbued_from)
+ for (var/mob/V in viewers(src))
+ V.show_message("\red The runes turn into dust, which then forms into an arcane image on the paper.", 3)
+ usr.say("H'drak v[pick("'","`")]loso, mir'kanas verbot!")
+ del(imbued_from)
+ del(newtalisman)
+ else
+ return fizzle()
/////////////////////////////////////////THIRTEENTH RUNE
- mend()
- var/mob/living/user = usr
- src = null
- user.say("Uhrast ka'hfa heldsagen ver[pick("'","`")]lot!")
- user.take_overall_damage(200, 0)
- runedec+=10
- user.visible_message("\red [user] keels over dead, his blood glowing blue as it escapes his body and dissipates into thin air.", \
- "\red In the last moment of your humble life, you feel an immense pain as fabric of reality mends... with your blood.", \
- "\red You hear faint rustle.")
- for(,user.stat==2)
- sleep(600)
- if (!user)
- return
- runedec-=10
+/obj/effect/rune/proc/mend()
+ var/mob/living/user = usr
+ src = null
+ user.say("Uhrast ka'hfa heldsagen ver[pick("'","`")]lot!")
+ user.take_overall_damage(200, 0)
+ runedec+=10
+ user.visible_message("\red [user] keels over dead, his blood glowing blue as it escapes his body and dissipates into thin air.", \
+ "\red In the last moment of your humble life, you feel an immense pain as fabric of reality mends... with your blood.", \
+ "\red You hear faint rustle.")
+ for(,user.stat==2)
+ sleep(600)
+ if (!user)
return
+ runedec-=10
+ return
/////////////////////////////////////////FOURTEETH RUNE
- // returns 0 if the rune is not used. returns 1 if the rune is used.
- communicate()
- . = 1 // Default output is 1. If the rune is deleted it will return 1
- var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")
- if(!input)
- if (istype(src))
- fizzle()
- return 0
- else
- return 0
- if(istype(src,/obj/effect/rune))
- usr.say("O bidai nabora se[pick("'","`")]sma!")
- else
- usr.whisper("O bidai nabora se[pick("'","`")]sma!")
+// returns 0 if the rune is not used. returns 1 if the rune is used.
+/obj/effect/rune/proc/communicate()
+ . = 1 // Default output is 1. If the rune is deleted it will return 1
+ var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")
+ if(!input)
+ if (istype(src))
+ fizzle()
+ return 0
+ else
+ return 0
+
+ // record this
+ cult_log("[key_name(usr,0)] says : [input]")
- if(istype(src,/obj/effect/rune))
- usr.say("[input]")
- else
- usr.whisper("[input]")
- for(var/datum/mind/H in ticker.mode.cult)
- if (H.current)
- H.current << "\red \b [input]"
- del(src)
- return 1
+ var/obj/cult_viewpoint/vp = getCultViewpoint(usr)
+ if (!vp)
+ return 0
+
+ var/displayName = vp.get_display_name()
+ var/cultName = vp.get_cult_name()
+
+ if(istype(src,/obj/effect/rune))
+ usr.say("O bidai nabora se[pick("'","`")]sma!")
+ else
+ usr.whisper("O bidai nabora se[pick("'","`")]sma!")
+
+ if(istype(src,/obj/effect/rune))
+ usr.say("[input]")
+ else
+ usr.whisper("[input]")
+
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (H.current)
+ H.current << "[cultName]: [input]"
+
+ for(var/mob/spirit/spirit in spirits)
+ spirit << "[displayName]: [input]"
+
+ del(src)
+ return 1
/////////////////////////////////////////FIFTEENTH RUNE
- sacrifice()
- var/list/mob/living/carbon/human/cultsinrange = list()
- var/list/mob/living/carbon/human/victims = list()
- for(var/mob/living/carbon/human/V in src.loc)//Checks for non-cultist humans to sacrifice
- if(ishuman(V))
- if(!(iscultist(V)))
- victims += V//Checks for cult status and mob type
- for(var/obj/item/I in src.loc)//Checks for MMIs/brains/Intellicards
- if(istype(I,/obj/item/brain))
- var/obj/item/brain/B = I
- victims += B.brainmob
- else if(istype(I,/obj/item/device/mmi))
- var/obj/item/device/mmi/B = I
- victims += B.brainmob
- else if(istype(I,/obj/item/device/aicard))
- for(var/mob/living/silicon/ai/A in I)
- victims += A
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- cultsinrange += C
- C.say("Barhah hra zar[pick("'","`")]garis!")
- for(var/mob/H in victims)
- if (ticker.mode.name == "cult")
- if(H.mind == ticker.mode:sacrifice_target)
- if(cultsinrange.len >= 3)
- sacrificed += H.mind
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- usr << "\red The Geometer of Blood accepts this sacrifice, your objective is now complete."
- else
- usr << "\red Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual."
+/obj/effect/rune/proc/sacrifice()
+ var/list/mob/living/carbon/human/cultsinrange = list()
+ var/list/mob/living/carbon/human/victims = list()
+ for(var/mob/living/carbon/human/V in src.loc)//Checks for non-cultist humans to sacrifice
+ if(ishuman(V))
+ if(!(iscultist(V)))
+ victims += V//Checks for cult status and mob type
+ for(var/obj/item/I in src.loc)//Checks for MMIs/brains/Intellicards
+ if(istype(I,/obj/item/brain))
+ var/obj/item/brain/B = I
+ victims += B.brainmob
+ else if(istype(I,/obj/item/device/mmi))
+ var/obj/item/device/mmi/B = I
+ victims += B.brainmob
+ else if(istype(I,/obj/item/device/aicard))
+ for(var/mob/living/silicon/ai/A in I)
+ victims += A
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ cultsinrange += C
+ C.say("Barhah hra zar[pick("'","`")]garis!")
+ for(var/mob/H in victims)
+ if (ticker.mode.name == "cult")
+ if(H.mind == ticker.mode:sacrifice_target)
+ if(cultsinrange.len >= 3)
+ sacrificed += H.mind
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
else
- if(cultsinrange.len >= 3)
- if(H.stat !=2)
- if(prob(80))
- usr << "\red The Geometer of Blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, this soul was not enough to gain His favor."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- else
- if(prob(40))
- usr << "\red The Geometer of blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, a mere dead body is not enough to satisfy Him."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- else
- if(H.stat !=2)
- usr << "\red The victim is still alive, you will need more cultists chanting for the sacrifice to succeed."
- else
- if(prob(40))
- usr << "\red The Geometer of blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, a mere dead body is not enough to satisfy Him."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
+ H.gib()
+ usr << "\red The Geometer of Blood accepts this sacrifice, your objective is now complete."
else
- if(cultsinrange.len >= 3)
- if(H.stat !=2)
- if(prob(80))
- usr << "\red The Geometer of Blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, this soul was not enough to gain His favor."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- else
- if(prob(40))
- usr << "\red The Geometer of blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, a mere dead body is not enough to satisfy Him."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- else
- if(H.stat !=2)
- usr << "\red The victim is still alive, you will need more cultists chanting for the sacrifice to succeed."
- else
- if(prob(40))
- usr << "\red The Geometer of blood accepts this sacrifice."
- ticker.mode:grant_runeword(usr)
- else
- usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, a mere dead body is not enough to satisfy Him."
- if(isrobot(H))
- H.dust()//To prevent the MMI from remaining
- else
- H.gib()
- for(var/mob/living/carbon/monkey/M in src.loc)
- if (ticker.mode.name == "cult")
- if(M.mind == ticker.mode:sacrifice_target)
- if(cultsinrange.len >= 3)
- sacrificed += M.mind
- usr << "\red The Geometer of Blood accepts this sacrifice, your objective is now complete."
- else
- usr << "\red Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual."
- continue
- else
- if(prob(20))
- usr << "\red The Geometer of Blood accepts your meager sacrifice."
+ usr << "\red Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual."
+ else
+ if(cultsinrange.len >= 3)
+ if(H.stat !=2)
+ if(prob(80))
+ usr << "\red The Geometer of Blood accepts this sacrifice."
ticker.mode:grant_runeword(usr)
else
usr << "\red The Geometer of blood accepts this sacrifice."
- usr << "\red However, a mere monkey is not enough to satisfy Him."
+ usr << "\red However, this soul was not enough to gain His favor."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
+ else
+ if(prob(40))
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, a mere dead body is not enough to satisfy Him."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
else
- usr << "\red The Geometer of Blood accepts your meager sacrifice."
- if(prob(20))
- ticker.mode.grant_runeword(usr)
- M.gib()
-/* for(var/mob/living/carbon/alien/A)
- for(var/mob/K in cultsinrange)
- K.say("Barhah hra zar'garis!")
- A.dust() /// A.gib() doesnt work for some reason, and dust() leaves that skull and bones thingy which we dont really need.
- if (ticker.mode.name == "cult")
- if(prob(75))
- usr << "\red The Geometer of Blood accepts your exotic sacrifice."
+ if(H.stat !=2)
+ usr << "\red The victim is still alive, you will need more cultists chanting for the sacrifice to succeed."
+ else
+ if(prob(40))
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, a mere dead body is not enough to satisfy Him."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
+ else
+ if(cultsinrange.len >= 3)
+ if(H.stat !=2)
+ if(prob(80))
+ usr << "\red The Geometer of Blood accepts this sacrifice."
ticker.mode:grant_runeword(usr)
else
- usr << "\red The Geometer of Blood accepts your exotic sacrifice."
- usr << "\red However, this alien is not enough to gain His favor."
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, this soul was not enough to gain His favor."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
else
- usr << "\red The Geometer of Blood accepts your exotic sacrifice."
- return
- return fizzle() */
+ if(prob(40))
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, a mere dead body is not enough to satisfy Him."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
+ else
+ if(H.stat !=2)
+ usr << "\red The victim is still alive, you will need more cultists chanting for the sacrifice to succeed."
+ else
+ if(prob(40))
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, a mere dead body is not enough to satisfy Him."
+ if(isrobot(H))
+ H.dust()//To prevent the MMI from remaining
+ else
+ H.gib()
+ for(var/mob/living/carbon/monkey/M in src.loc)
+ if (ticker.mode.name == "cult")
+ if(M.mind == ticker.mode:sacrifice_target)
+ if(cultsinrange.len >= 3)
+ sacrificed += M.mind
+ usr << "\red The Geometer of Blood accepts this sacrifice, your objective is now complete."
+ else
+ usr << "\red Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual."
+ continue
+ else
+ if(prob(20))
+ usr << "\red The Geometer of Blood accepts your meager sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of blood accepts this sacrifice."
+ usr << "\red However, a mere monkey is not enough to satisfy Him."
+ else
+ usr << "\red The Geometer of Blood accepts your meager sacrifice."
+ if(prob(20))
+ ticker.mode.grant_runeword(usr)
+ M.gib()
+/* for(var/mob/living/carbon/alien/A)
+ for(var/mob/K in cultsinrange)
+ K.say("Barhah hra zar'garis!")
+ A.dust() /// A.gib() doesnt work for some reason, and dust() leaves that skull and bones thingy which we dont really need.
+ if (ticker.mode.name == "cult")
+ if(prob(75))
+ usr << "\red The Geometer of Blood accepts your exotic sacrifice."
+ ticker.mode:grant_runeword(usr)
+ else
+ usr << "\red The Geometer of Blood accepts your exotic sacrifice."
+ usr << "\red However, this alien is not enough to gain His favor."
+ else
+ usr << "\red The Geometer of Blood accepts your exotic sacrifice."
+ return
+ return fizzle() */
/////////////////////////////////////////SIXTEENTH RUNE
- revealrunes(var/obj/W as obj)
- var/go=0
- var/rad
- var/S=0
- if(istype(W,/obj/effect/rune))
- rad = 6
- go = 1
- if (istype(W,/obj/item/weapon/paper/talisman))
- rad = 4
- go = 1
- if (istype(W,/obj/item/weapon/nullrod))
- rad = 1
- go = 1
- if(go)
- for(var/obj/effect/rune/R in orange(rad,src))
- if(R!=src)
- R:visibility=15
- S=1
- if(S)
- if(istype(W,/obj/item/weapon/nullrod))
- usr << "\red Arcane markings suddenly glow from underneath a thin layer of dust!"
- return
- if(istype(W,/obj/effect/rune))
- usr.say("Nikt[pick("'","`")]o barada kla'atu!")
- for (var/mob/V in viewers(src))
- V.show_message("\red The rune turns into red dust, reveaing the surrounding runes.", 3)
- del(src)
- return
- if(istype(W,/obj/item/weapon/paper/talisman))
- usr.whisper("Nikt[pick("'","`")]o barada kla'atu!")
- usr << "\red Your talisman turns into red dust, revealing the surrounding runes."
- for (var/mob/V in orange(1,usr.loc))
- if(V!=usr)
- V.show_message("\red Red dust emanates from [usr]'s hands for a moment.", 3)
- return
- return
- if(istype(W,/obj/effect/rune))
- return fizzle()
- if(istype(W,/obj/item/weapon/paper/talisman))
- call(/obj/effect/rune/proc/fizzle)()
- return
+/obj/effect/rune/proc/revealrunesrune()
+ revealrunes(src)
+
+/obj/effect/rune/proc/revealrunes(var/obj/W as obj)
+ var/go=0
+ var/rad
+ var/S=0
+ if(istype(W,/obj/effect/rune))
+ rad = 6
+ go = 1
+ if (istype(W,/obj/item/weapon/paper/talisman))
+ rad = 4
+ go = 1
+ if (istype(W,/obj/item/weapon/nullrod))
+ rad = 1
+ go = 1
+ if(go)
+ for(var/obj/effect/rune/R in orange(rad,src))
+ if(R!=src)
+ R:visibility=15
+ S=1
+ if(S)
+ if(istype(W,/obj/item/weapon/nullrod))
+ usr << "\red Arcane markings suddenly glow from underneath a thin layer of dust!"
+ return
+ if(istype(W,/obj/effect/rune))
+ usr.say("Nikt[pick("'","`")]o barada kla'atu!")
+ for (var/mob/V in viewers(src))
+ V.show_message("\red The rune turns into red dust, reveaing the surrounding runes.", 3)
+ del(src)
+ return
+ if(istype(W,/obj/item/weapon/paper/talisman))
+ usr.whisper("Nikt[pick("'","`")]o barada kla'atu!")
+ usr << "\red Your talisman turns into red dust, revealing the surrounding runes."
+ for (var/mob/V in orange(1,usr.loc))
+ if(V!=usr)
+ V.show_message("\red Red dust emanates from [usr]'s hands for a moment.", 3)
+ return
+ return
+ if(istype(W,/obj/effect/rune))
+ return fizzle()
+ if(istype(W,/obj/item/weapon/paper/talisman))
+ call(/obj/effect/rune/proc/fizzle)()
+ return
/////////////////////////////////////////SEVENTEENTH RUNE
- wall()
- usr.say("Khari[pick("'","`")]d! Eske'te tannin!")
- src.density = !src.density
- var/mob/living/user = usr
- user.take_organ_damage(2, 0)
- if(src.density)
- usr << "\red Your blood flows into the rune, and you feel that the very space over the rune thickens."
- else
- usr << "\red Your blood flows into the rune, and you feel as the rune releases its grasp on space."
- return
+/obj/effect/rune/proc/wall()
+ usr.say("Khari[pick("'","`")]d! Eske'te tannin!")
+ src.density = !src.density
+ var/mob/living/user = usr
+ user.take_organ_damage(2, 0)
+ if(src.density)
+ usr << "\red Your blood flows into the rune, and you feel that the very space over the rune thickens."
+ else
+ usr << "\red Your blood flows into the rune, and you feel as the rune releases its grasp on space."
+ return
/////////////////////////////////////////EIGHTTEENTH RUNE
- freedom()
- var/mob/living/user = usr
- var/list/mob/living/carbon/cultists = new
- for(var/datum/mind/H in ticker.mode.cult)
- if (istype(H.current,/mob/living/carbon))
- cultists+=H.current
- var/list/mob/living/carbon/users = new
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- users+=C
- if(users.len>=3)
- var/mob/living/carbon/cultist = input("Choose the one who you want to free", "Followers of Geometer") as null|anything in (cultists - users)
- if(!cultist)
- return fizzle()
- if (cultist == user) //just to be sure.
- return
- if(!(cultist.buckled || \
- cultist.handcuffed || \
- istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle) || \
- (istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) || \
- (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \
- (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \
- ))
- user << "\red The [cultist] is already free."
- return
- cultist.buckled = null
- if (cultist.handcuffed)
- cultist.drop_from_inventory(cultist.handcuffed)
- if (cultist.legcuffed)
- cultist.drop_from_inventory(cultist.legcuffed)
- if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
- cultist.u_equip(cultist.wear_mask)
- if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
- cultist.loc:welded = 0
- if(istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked)
- cultist.loc:locked = 0
- if(istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked)
- cultist.loc:locked = 0
- for(var/mob/living/carbon/C in users)
- user.take_overall_damage(15, 0)
- C.say("Khari[pick("'","`")]d! Gual'te nikka!")
- del(src)
+/obj/effect/rune/proc/freedom()
+ var/mob/living/user = usr
+ var/list/mob/living/carbon/cultists = new
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (istype(H.current,/mob/living/carbon))
+ cultists+=H.current
+ var/list/mob/living/carbon/users = new
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ users+=C
+ if(users.len>=3)
+ var/mob/living/carbon/cultist = input("Choose the one who you want to free", "Followers of Geometer") as null|anything in (cultists - users)
+ if(!cultist)
return fizzle()
+ if (cultist == user) //just to be sure.
+ return
+ if(!(cultist.buckled || \
+ cultist.handcuffed || \
+ istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle) || \
+ (istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) || \
+ (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \
+ (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \
+ ))
+ user << "\red The [cultist] is already free."
+ return
+ cultist.buckled = null
+ if (cultist.handcuffed)
+ cultist.handcuffed.loc = cultist.loc
+ cultist.handcuffed = null
+ cultist.update_inv_handcuffed()
+ if (cultist.legcuffed)
+ cultist.legcuffed.loc = cultist.loc
+ cultist.legcuffed = null
+ cultist.update_inv_legcuffed()
+ if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle))
+ cultist.u_equip(cultist.wear_mask)
+ if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded)
+ cultist.loc:welded = 0
+ if(istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked)
+ cultist.loc:locked = 0
+ if(istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked)
+ cultist.loc:locked = 0
+ for(var/mob/living/carbon/C in users)
+ user.take_overall_damage(15, 0)
+ C.say("Khari[pick("'","`")]d! Gual'te nikka!")
+ del(src)
+ return fizzle()
/////////////////////////////////////////NINETEENTH RUNE
- cultsummon()
- var/mob/living/user = usr
- var/list/mob/living/carbon/cultists = new
- for(var/datum/mind/H in ticker.mode.cult)
- if (istype(H.current,/mob/living/carbon))
- cultists+=H.current
- var/list/mob/living/carbon/users = new
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- users+=C
- if(users.len>=3)
- var/mob/living/carbon/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - user)
- if(!cultist)
- return fizzle()
- if (cultist == user) //just to be sure.
- return
- if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet)))
- user << "\red You cannot summon \the [cultist], for his shackles of blood are strong."
- return fizzle()
- cultist.loc = src.loc
- cultist.lying = 1
- cultist.regenerate_icons()
- for(var/mob/living/carbon/human/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- C.say("N'ath reth sh'yro eth d[pick("'","`")]rekkathnor!")
- C.take_overall_damage(25, 0)
- user.visible_message("\red Rune disappears with a flash of red light, and in its place now a body lies.", \
- "\red You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a body.", \
- "\red You hear a pop and smell ozone.")
- del(src)
+/obj/effect/rune/proc/cultsummon()
+ var/mob/living/user = usr
+ var/list/mob/living/carbon/cultists = new
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (istype(H.current,/mob/living/carbon))
+ cultists+=H.current
+ var/list/mob/living/carbon/users = new
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ users+=C
+ if(users.len>=3)
+ var/mob/living/carbon/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - user)
+ if(!cultist)
return fizzle()
+ if (cultist == user) //just to be sure.
+ return
+ if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet)))
+ user << "\red You cannot summon \the [cultist], for his shackles of blood are strong."
+ return fizzle()
+ cultist.loc = src.loc
+ cultist.lying = 1
+ cultist.regenerate_icons()
+ for(var/mob/living/carbon/human/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ C.say("N'ath reth sh'yro eth d[pick("'","`")]rekkathnor!")
+ C.take_overall_damage(25, 0)
+ user.visible_message("\red Rune disappears with a flash of red light, and in its place now a body lies.", \
+ "\red You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a body.", \
+ "\red You hear a pop and smell ozone.")
+ del(src)
+ return fizzle()
/////////////////////////////////////////TWENTIETH RUNES
- deafen()
- if(istype(src,/obj/effect/rune))
- var/affected = 0
- for(var/mob/living/carbon/C in range(7,src))
- if (iscultist(C))
- continue
- var/obj/item/weapon/nullrod/N = locate() in C
- if(N)
- continue
- C.ear_deaf += 50
- C.show_message("\red The world around you suddenly becomes quiet.", 3)
- affected++
- if(prob(1))
- C.sdisabilities |= DEAF
- if(affected)
- usr.say("Sti[pick("'","`")] kaliedir!")
- usr << "\red The world becomes quiet as the deafening rune dissipates into fine dust."
- del(src)
- else
- return fizzle()
- else
- var/affected = 0
- for(var/mob/living/carbon/C in range(7,usr))
- if (iscultist(C))
- continue
- var/obj/item/weapon/nullrod/N = locate() in C
- if(N)
- continue
- C.ear_deaf += 30
- //talismans is weaker.
- C.show_message("\red The world around you suddenly becomes quiet.", 3)
- affected++
- if(affected)
- usr.whisper("Sti[pick("'","`")] kaliedir!")
- usr << "\red Your talisman turns into gray dust, deafening everyone around."
- for (var/mob/V in orange(1,src))
- if(!(iscultist(V)))
- V.show_message("\red Dust flows from [usr]'s hands for a moment, and the world suddenly becomes quiet..", 3)
- return
+/obj/effect/rune/proc/deafen()
+ if(istype(src,/obj/effect/rune))
+ var/affected = 0
+ for(var/mob/living/carbon/C in range(7,src))
+ if (iscultist(C))
+ continue
+ var/obj/item/weapon/nullrod/N = locate() in C
+ if(N)
+ continue
+ C.ear_deaf += 50
+ C.show_message("\red The world around you suddenly becomes quiet.", 3)
+ affected++
+ if(prob(1))
+ C.sdisabilities |= DEAF
+ if(affected)
+ usr.say("Sti[pick("'","`")] kaliedir!")
+ usr << "\red The world becomes quiet as the deafening rune dissipates into fine dust."
+ del(src)
+ else
+ return fizzle()
+ else
+ var/affected = 0
+ for(var/mob/living/carbon/C in range(7,usr))
+ if (iscultist(C))
+ continue
+ var/obj/item/weapon/nullrod/N = locate() in C
+ if(N)
+ continue
+ C.ear_deaf += 30
+ //talismans is weaker.
+ C.show_message("\red The world around you suddenly becomes quiet.", 3)
+ affected++
+ if(affected)
+ usr.whisper("Sti[pick("'","`")] kaliedir!")
+ usr << "\red Your talisman turns into gray dust, deafening everyone around."
+ for (var/mob/V in orange(1,src))
+ if(!(iscultist(V)))
+ V.show_message("\red Dust flows from [usr]'s hands for a moment, and the world suddenly becomes quiet..", 3)
+ return
- blind()
- if(istype(src,/obj/effect/rune))
- var/affected = 0
- for(var/mob/living/carbon/C in viewers(src))
- if (iscultist(C))
- continue
- var/obj/item/weapon/nullrod/N = locate() in C
- if(N)
- continue
- C.eye_blurry += 50
- C.eye_blind += 20
- if(prob(5))
- C.disabilities |= NEARSIGHTED
- if(prob(10))
- C.sdisabilities |= BLIND
- C.show_message("\red Suddenly you see red flash that blinds you.", 3)
- affected++
- if(affected)
- usr.say("Sti[pick("'","`")] kaliesin!")
- usr << "\red The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust."
- del(src)
- else
- return fizzle()
- else
- var/affected = 0
- for(var/mob/living/carbon/C in view(2,usr))
- if (iscultist(C))
- continue
- var/obj/item/weapon/nullrod/N = locate() in C
- if(N)
- continue
- C.eye_blurry += 30
- C.eye_blind += 10
- //talismans is weaker.
- affected++
- C.show_message("\red You feel a sharp pain in your eyes, and the world disappears into darkness..", 3)
- if(affected)
- usr.whisper("Sti[pick("'","`")] kaliesin!")
- usr << "\red Your talisman turns into gray dust, blinding those who not follow the Nar-Sie."
- return
+/obj/effect/rune/proc/blind()
+ if(istype(src,/obj/effect/rune))
+ var/affected = 0
+ for(var/mob/living/carbon/C in viewers(src))
+ if (iscultist(C))
+ continue
+ var/obj/item/weapon/nullrod/N = locate() in C
+ if(N)
+ continue
+ C.eye_blurry += 50
+ C.eye_blind += 20
+ if(prob(5))
+ C.disabilities |= NEARSIGHTED
+ if(prob(10))
+ C.sdisabilities |= BLIND
+ C.show_message("\red Suddenly you see red flash that blinds you.", 3)
+ affected++
+ if(affected)
+ usr.say("Sti[pick("'","`")] kaliesin!")
+ usr << "\red The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust."
+ del(src)
+ else
+ return fizzle()
+ else
+ var/affected = 0
+ for(var/mob/living/carbon/C in view(2,usr))
+ if (iscultist(C))
+ continue
+ var/obj/item/weapon/nullrod/N = locate() in C
+ if(N)
+ continue
+ C.eye_blurry += 30
+ C.eye_blind += 10
+ //talismans is weaker.
+ affected++
+ C.show_message("\red You feel a sharp pain in your eyes, and the world disappears into darkness..", 3)
+ if(affected)
+ usr.whisper("Sti[pick("'","`")] kaliesin!")
+ usr << "\red Your talisman turns into gray dust, blinding those who not follow the Nar-Sie."
+ return
- bloodboil() //cultists need at least one DANGEROUS rune. Even if they're all stealthy.
+/obj/effect/rune/proc/bloodboil() //cultists need at least one DANGEROUS rune. Even if they're all stealthy.
/*
- var/list/mob/living/carbon/cultists = new
- for(var/datum/mind/H in ticker.mode.cult)
- if (istype(H.current,/mob/living/carbon))
- cultists+=H.current
+ var/list/mob/living/carbon/cultists = new
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (istype(H.current,/mob/living/carbon))
+ cultists+=H.current
*/
- var/culcount = 0 //also, wording for it is old wording for obscure rune, which is now hide-see-blood.
+ var/culcount = 0 //also, wording for it is old wording for obscure rune, which is now hide-see-blood.
// var/list/cultboil = list(cultists-usr) //and for this words are destroy-see-blood.
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- culcount++
- if(culcount>=3)
- for(var/mob/living/carbon/M in viewers(usr))
- if(iscultist(M))
- continue
- var/obj/item/weapon/nullrod/N = locate() in M
- if(N)
- continue
- M.take_overall_damage(51,51)
- M << "\red Your blood boils!"
- if(prob(5))
- spawn(5)
- M.gib()
- for(var/obj/effect/rune/R in view(src))
- if(prob(10))
- explosion(R.loc, -1, 0, 1, 5)
- for(var/mob/living/carbon/human/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- C.say("Dedo ol[pick("'","`")]btoh!")
- C.take_overall_damage(15, 0)
- del(src)
- else
- return fizzle()
- return
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ culcount++
+ if(culcount>=3)
+ for(var/mob/living/carbon/M in viewers(usr))
+ if(iscultist(M))
+ continue
+ var/obj/item/weapon/nullrod/N = locate() in M
+ if(N)
+ continue
+ cult_log(": Blood Boil damaged [key_name_admin(M)].")
+ M.take_overall_damage(51,51)
+ M << "\red Your blood boils!"
+ if(prob(5))
+ spawn(5)
+ M.gib()
+ for(var/obj/effect/rune/R in view(src))
+ if(prob(10))
+ explosion(R.loc, -1, 0, 1, 5)
+ for(var/mob/living/carbon/human/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ C.say("Dedo ol[pick("'","`")]btoh!")
+ C.take_overall_damage(15, 0)
+ del(src)
+ else
+ return fizzle()
+ return
// WIP rune, I'll wait for Rastaf0 to add limited blood.
- burningblood()
- var/culcount = 0
- for(var/mob/living/carbon/C in orange(1,src))
- if(iscultist(C) && !C.stat)
- culcount++
- if(culcount >= 5)
- for(var/obj/effect/rune/R in world)
- if(R.blood_DNA == src.blood_DNA)
- for(var/mob/living/M in orange(2,R))
- M.take_overall_damage(0,15)
- if (R.invisibility>M.see_invisible)
- M << "\red Aargh it burns!"
- else
- M << "\red Rune suddenly ignites, burning you!"
- var/turf/T = get_turf(R)
- T.hotspot_expose(700,125)
- for(var/obj/effect/decal/cleanable/blood/B in world)
- if(B.blood_DNA == src.blood_DNA)
- for(var/mob/living/M in orange(1,B))
- M.take_overall_damage(0,5)
- M << "\red Blood suddenly ignites, burning you!"
- var/turf/T = get_turf(B)
- T.hotspot_expose(700,125)
- del(B)
- del(src)
+/obj/effect/rune/proc/burningblood()
+ var/culcount = 0
+ for(var/mob/living/carbon/C in orange(1,src))
+ if(iscultist(C) && !C.stat)
+ culcount++
+ if(culcount >= 5)
+ for(var/obj/effect/rune/R in world)
+ if(R.blood_DNA == src.blood_DNA)
+ for(var/mob/living/M in orange(2,R))
+ M.take_overall_damage(0,15)
+ if (R.invisibility>M.see_invisible)
+ M << "\red Aargh it burns!"
+ else
+ M << "\red Rune suddenly ignites, burning you!"
+ var/turf/T = get_turf(R)
+ T.hotspot_expose(700,125)
+ for(var/obj/effect/decal/cleanable/blood/B in world)
+ if(B.blood_DNA == src.blood_DNA)
+ for(var/mob/living/M in orange(1,B))
+ M.take_overall_damage(0,5)
+ M << "\red Blood suddenly ignites, burning you!"
+ var/turf/T = get_turf(B)
+ T.hotspot_expose(700,125)
+ del(B)
+ del(src)
////////// Rune 24 (counting burningblood, which kinda doesnt work yet.)
- runestun(var/mob/living/T as mob)
- if(istype(src,/obj/effect/rune)) ///When invoked as rune, flash and stun everyone around.
- usr.say("Fuu ma[pick("'","`")]jin!")
- for(var/mob/living/L in viewers(src))
+/obj/effect/rune/proc/runestun(var/mob/living/T as mob)
+ if(istype(src,/obj/effect/rune)) ///When invoked as rune, flash and stun everyone around.
+ usr.say("Fuu ma[pick("'","`")]jin!")
+ for(var/mob/living/L in viewers(src))
- if(iscarbon(L))
- var/mob/living/carbon/C = L
- flick("e_flash", C.flash)
- if(C.stuttering < 1 && (!(HULK in C.mutations)))
- C.stuttering = 1
- C.Weaken(1)
- C.Stun(1)
- C.show_message("\red The rune explodes in a bright flash.", 3)
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ flick("e_flash", C.flash)
+ if(C.stuttering < 1 && (!(HULK in C.mutations)))
+ C.stuttering = 1
+ C.Weaken(1)
+ C.Stun(1)
+ C.show_message("\red The rune explodes in a bright flash.", 3)
- else if(issilicon(L))
- var/mob/living/silicon/S = L
- S.Weaken(5)
- S.show_message("\red BZZZT... The rune has exploded in a bright flash.", 3)
- del(src)
- else ///When invoked as talisman, stun and mute the target mob.
- usr.say("Dream sign ''Evil sealing talisman'[pick("'","`")]!")
- var/obj/item/weapon/nullrod/N = locate() in T
- if(N)
- for(var/mob/O in viewers(T, null))
- O.show_message(text("\red [] invokes a talisman at [], but they are unaffected!", usr, T), 1)
- else
- for(var/mob/O in viewers(T, null))
- O.show_message(text("\red [] invokes a talisman at []", usr, T), 1)
+ else if(issilicon(L))
+ var/mob/living/silicon/S = L
+ S.Weaken(5)
+ S.show_message("\red BZZZT... The rune has exploded in a bright flash.", 3)
+ del(src)
+ else ///When invoked as talisman, stun and mute the target mob.
+ usr.say("Dream sign ''Evil sealing talisman'[pick("'","`")]!")
+ var/obj/item/weapon/nullrod/N = locate() in T
+ if(N)
+ for(var/mob/O in viewers(T, null))
+ O.show_message(text("\red [] invokes a talisman at [], but they are unaffected!", usr, T), 1)
+ else
+ for(var/mob/O in viewers(T, null))
+ O.show_message(text("\red [] invokes a talisman at []", usr, T), 1)
- if(issilicon(T))
- T.Weaken(15)
+ if(issilicon(T))
+ T.Weaken(15)
- else if(iscarbon(T))
- var/mob/living/carbon/C = T
- flick("e_flash", C.flash)
- if (!(HULK in C.mutations))
- C.silent += 15
- C.Weaken(25)
- C.Stun(25)
- return
+ else if(iscarbon(T))
+ var/mob/living/carbon/C = T
+ flick("e_flash", C.flash)
+ if (!(HULK in C.mutations))
+ C.silent += 15
+ C.Weaken(25)
+ C.Stun(25)
+ return
/////////////////////////////////////////TWENTY-FIFTH RUNE
- armor()
- var/mob/living/carbon/human/user = usr
- if(istype(src,/obj/effect/rune))
- usr.say("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!")
- else
- usr.whisper("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!")
- usr.visible_message("\red The rune disappears with a flash of red light, and a set of armor appears on [usr]...", \
- "\red You are blinded by the flash of red light! After you're able to see again, you see that you are now wearing a set of armor.")
+/obj/effect/rune/proc/armor()
+ var/mob/living/carbon/human/user = usr
+ if(istype(src,/obj/effect/rune))
+ usr.say("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!")
+ else
+ usr.whisper("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!")
+ usr.visible_message("\red The rune disappears with a flash of red light, and a set of armor appears on [usr]...", \
+ "\red You are blinded by the flash of red light! After you're able to see again, you see that you are now wearing a set of armor.")
- user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head)
- user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit)
- user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes)
- user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back)
- //the above update their overlay icons cache but do not call update_icons()
- //the below calls update_icons() at the end, which will update overlay icons by using the (now updated) cache
- user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) //put in hands or on floor
+ user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head)
+ user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit)
+ user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes)
+ user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back)
+ //the above update their overlay icons cache but do not call update_icons()
+ //the below calls update_icons() at the end, which will update overlay icons by using the (now updated) cache
+ user.put_in_hands(new /obj/item/weapon/melee/cultblade(user)) //put in hands or on floor
- del(src)
- return
+ del(src)
+ return
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index 104fbf1cdb..81559bcfe3 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -122,7 +122,7 @@ rcd light flash thingy on matter drain
mod_pick_name = "recam"
uses = 10
-/client/proc/reactivate_camera(obj/machinery/camera/C as obj in cameranet.cameras)
+/client/proc/reactivate_camera(obj/machinery/camera/C as obj in cameranet.viewpoints)
set name = "Reactivate Camera"
set category = "Malfunction"
if (istype (C, /obj/machinery/camera))
@@ -143,7 +143,7 @@ rcd light flash thingy on matter drain
mod_pick_name = "upgradecam"
uses = 10
-/client/proc/upgrade_camera(obj/machinery/camera/C as obj in cameranet.cameras)
+/client/proc/upgrade_camera(obj/machinery/camera/C as obj in cameranet.viewpoints)
set name = "Upgrade Camera"
set category = "Malfunction"
if(istype(C))
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index daea3219d6..e9257eacc0 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -39,7 +39,7 @@
assembly = new(src)
assembly.state = 4
/* // Use this to look for cameras that have the same c_tag.
- for(var/obj/machinery/camera/C in cameranet.cameras)
+ for(var/obj/machinery/camera/C in cameranet.viewpoints)
var/list/tempnetwork = C.network&src.network
if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]"
@@ -215,7 +215,7 @@
/obj/machinery/camera/proc/cancelCameraAlarm()
alarm_on = 0
for(var/mob/living/silicon/S in mob_list)
- S.cancelAlarm("Camera", get_area(src), list(src), src)
+ S.cancelAlarm("Camera", get_area(src), src)
/obj/machinery/camera/proc/can_use()
if(!status)
diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm
index 588ab5005c..19f2620630 100644
--- a/code/game/machinery/camera/motion.dm
+++ b/code/game/machinery/camera/motion.dm
@@ -42,14 +42,14 @@
/obj/machinery/camera/proc/cancelAlarm()
if (detectTime == -1)
for (var/mob/living/silicon/aiPlayer in player_list)
- if (status) aiPlayer.cancelAlarm("Motion", src.loc.loc)
+ if (status) aiPlayer.cancelAlarm("Motion", get_area(src), src)
detectTime = 0
return 1
/obj/machinery/camera/proc/triggerAlarm()
if (!detectTime) return 0
for (var/mob/living/silicon/aiPlayer in player_list)
- if (status) aiPlayer.triggerAlarm("Motion", src.loc.loc, src)
+ if (status) aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
detectTime = -1
return 1
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index b019c4ee86..631594d28e 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -4,7 +4,7 @@
return
var/list/L = list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
+ for (var/obj/machinery/camera/C in cameranet.viewpoints)
L.Add(C)
camera_sort(L)
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index 61784e98c3..0f72177ca1 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -109,6 +109,10 @@
name = "Circuit board (Robotics Control)"
build_path = "/obj/machinery/computer/robotics"
origin_tech = "programming=3"
+/obj/item/weapon/circuitboard/drone_control
+ name = "Circuit board (Drone Control)"
+ build_path = "/obj/machinery/computer/drone_control"
+ origin_tech = "programming=3"
/obj/item/weapon/circuitboard/cloning
name = "Circuit board (Cloning)"
build_path = "/obj/machinery/computer/cloning"
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index d2bf8a1661..9eaf5c6da7 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -36,7 +36,7 @@
user.set_machine(src)
var/list/L = list()
- for (var/obj/machinery/camera/C in cameranet.cameras)
+ for (var/obj/machinery/camera/C in cameranet.viewpoints)
L.Add(C)
camera_sort(L)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 437c7f60c4..30c5a3cd9b 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -42,6 +42,8 @@
dat += "2. Emergency Full Destruct "
if(screen == 1)
for(var/mob/living/silicon/robot/R in mob_list)
+ if(istype(R, /mob/living/silicon/robot/drone))
+ continue //There's a specific console for drones.
if(istype(user, /mob/living/silicon/ai))
if (R.connected_ai != user)
continue
@@ -220,8 +222,7 @@
while(src.timeleft)
for(var/mob/living/silicon/robot/R in mob_list)
- if(!R.scrambledcodes)
+ if(!R.scrambledcodes && !istype(R, /mob/living/silicon/robot/drone))
R.self_destruct()
- return
-
+ return
\ No newline at end of file
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 950871d983..fb4c43daee 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -262,15 +262,6 @@
/obj/machinery/door/proc/requiresID()
return 1
-/obj/machinery/door/proc/update_nearby_tiles(need_rebuild)
- if(!air_master)
- return 0
-
- for(var/turf/simulated/turf in locs)
- update_heat_protection(turf)
- air_master.mark_for_update(turf)
-
- return 1
/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source)
if(istype(source))
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 1c130d252b..6c702cbcb2 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -806,7 +806,7 @@
products = list(/obj/item/seeds/bananaseed = 3,/obj/item/seeds/berryseed = 3,/obj/item/seeds/carrotseed = 3,/obj/item/seeds/chantermycelium = 3,/obj/item/seeds/chiliseed = 3,
/obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/replicapod = 3,/obj/item/seeds/soyaseed = 3,
/obj/item/seeds/sunflowerseed = 3,/obj/item/seeds/tomatoseed = 3,/obj/item/seeds/towermycelium = 3,/obj/item/seeds/wheatseed = 3,/obj/item/seeds/appleseed = 3,
- /obj/item/seeds/poppyseed = 3,/obj/item/seeds/sugarcaneseed = 3,/obj/item/seeds/ambrosiavulgarisseed = 3,/obj/item/seeds/whitebeetseed = 3,/obj/item/seeds/watermelonseed = 3,/obj/item/seeds/limeseed = 3,
+ /obj/item/seeds/poppyseed = 3,/obj/item/seeds/sugarcaneseed = 3,/obj/item/seeds/ambrosiavulgarisseed = 3,/obj/item/seeds/peanutseed = 3,/obj/item/seeds/whitebeetseed = 3,/obj/item/seeds/watermelonseed = 3,/obj/item/seeds/limeseed = 3,
/obj/item/seeds/lemonseed = 3,/obj/item/seeds/orangeseed = 3,/obj/item/seeds/grassseed = 3,/obj/item/seeds/cocoapodseed = 3,/obj/item/seeds/plumpmycelium = 2,
/obj/item/seeds/cabbageseed = 3,/obj/item/seeds/grapeseed = 3,/obj/item/seeds/pumpkinseed = 3,/obj/item/seeds/cherryseed = 3,/obj/item/seeds/plastiseed = 3,/obj/item/seeds/riceseed = 3)
contraband = list(/obj/item/seeds/amanitamycelium = 2,/obj/item/seeds/glowshroom = 2,/obj/item/seeds/libertymycelium = 2,/obj/item/seeds/mtearseed = 2,
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index bb8132744f..3d9cb6031a 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -194,4 +194,26 @@
user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!")
src.force = on_damage
src.damtype = "fire"
- processing_objects += src
\ No newline at end of file
+ processing_objects += src
+
+/obj/item/device/flashlight/slime
+ gender = PLURAL
+ name = "glowing slime extract"
+ desc = "A glowing ball of what appears to be amber."
+ icon = 'icons/obj/lighting.dmi'
+ icon_state = "floor1" //not a slime extract sprite but... something close enough!
+ item_state = "slime"
+ w_class = 1
+ m_amt = 0
+ g_amt = 0
+ brightness_on = 6
+ on = 1 //Bio-luminesence has one setting, on.
+
+/obj/item/device/flashlight/slime/New()
+ SetLuminosity(brightness_on)
+ spawn(1) //Might be sloppy, but seems to be necessary to prevent further runtimes and make these work as intended... don't judge me!
+ update_brightness()
+ icon_state = initial(icon_state)
+
+/obj/item/device/flashlight/slime/attack_self(mob/user)
+ return //Bio-luminescence does not toggle.
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 77aa58a1a6..32d704cdc5 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -148,7 +148,7 @@
src = null //dont kill proc after del()
usr.before_take_item(oldsrc)
del(oldsrc)
- if (istype(O,/obj/item))
+ if (istype(O,/obj/item) && istype(usr,/mob/living/carbon))
usr.put_in_hands(O)
O.add_fingerprint(usr)
//BubbleWrap - so newly formed boxes are empty
diff --git a/code/game/objects/items/weapons/implants/implanter.dm b/code/game/objects/items/weapons/implants/implanter.dm
index 3706ef6bfd..f1f5d8521e 100644
--- a/code/game/objects/items/weapons/implants/implanter.dm
+++ b/code/game/objects/items/weapons/implants/implanter.dm
@@ -51,7 +51,7 @@
src.imp = null
update()
-
+
return
@@ -120,6 +120,12 @@
if (c.scanned)
user << "\red Something is already scanned inside the implant!"
return
- imp:scanned = A
+ c.scanned = A
+ if(istype(A.loc,/mob/living/carbon/human))
+ var/mob/living/carbon/human/H = A.loc
+ H.u_equip(A)
+ else if(istype(A.loc,/obj/item/weapon/storage))
+ var/obj/item/weapon/storage/S = A.loc
+ S.remove_from_storage(A)
A.loc.contents.Remove(A)
update()
diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm
index 11058f0dc5..67205c7619 100644
--- a/code/game/objects/items/weapons/storage/internal.dm
+++ b/code/game/objects/items/weapons/storage/internal.dm
@@ -80,8 +80,6 @@
if (M.s_active == src)
src.close(M)
return 1
-
-
-
-
-
\ No newline at end of file
+
+/obj/item/weapon/storage/internal/Adjacent(var/atom/neighbor)
+ return master_item.Adjacent(neighbor)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index a4c5644598..6864e6aad3 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -456,12 +456,30 @@
O.hear_talk(M, text)
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
-//Returns -1 if the atom was not found on user.
-/atom/proc/storage_depth(mob/user)
+//Returns -1 if the atom was not found on container.
+/atom/proc/storage_depth(atom/container)
var/depth = 0
var/atom/cur_atom = src
- while (cur_atom && !(cur_atom in user.contents))
+ while (cur_atom && !(cur_atom in container.contents))
+ if (isarea(cur_atom))
+ return -1
+ if (istype(cur_atom.loc, /obj/item/weapon/storage))
+ depth++
+ cur_atom = cur_atom.loc
+
+ if (!cur_atom)
+ return -1 //inside something with a null loc.
+
+ return depth
+
+//Like storage depth, but returns the depth to the nearest turf
+//Returns -1 if no top level turf (a loc was null somewhere, or a non-turf atom's loc was an area somehow).
+/atom/proc/storage_depth_turf()
+ var/depth = 0
+ var/atom/cur_atom = src
+
+ while (cur_atom && !isturf(cur_atom))
if (isarea(cur_atom))
return -1
if (istype(cur_atom.loc, /obj/item/weapon/storage))
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 54b4c2c2fb..64728d2d9a 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -108,6 +108,7 @@ var/global/floorIsLava = 0
body += "Is an AI "
else if(ishuman(M))
body += {"Make AI |
+ Make Mask |
Make Robot |
Make Alien |
Make slime
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index c8067a0958..208ac86f92 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -549,9 +549,14 @@ var/list/admin_verbs_mentor = list(
set category = "Fun"
set name = "Give Spell"
set desc = "Gives a spell to a mob."
- var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spells
+ var/list/spell_names = list()
+ for(var/v in spells)
+ // "/obj/effect/proc_holder/spell/" 30 symbols ~Intercross21
+ spell_names.Add(copytext("[v]", 31, 0))
+ var/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_names
if(!S) return
- T.spell_list += new S
+ var/path = text2path("/obj/effect/proc_holder/spell/[S]")
+ T.spell_list += new path
feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
message_admins("\blue [key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1)
@@ -560,9 +565,14 @@ var/list/admin_verbs_mentor = list(
set category = "Fun"
set name = "Give Disease"
set desc = "Gives a Disease to a mob."
- var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in diseases
+ var/list/disease_names = list()
+ for(var/v in diseases)
+ // "/datum/disease/" 15 symbols ~Intercross
+ disease_names.Add(copytext("[v]", 16, 0))
+ var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in disease_names
if(!D) return
- T.contract_disease(new D, 1)
+ var/path = text2path("/datum/disease/[D]")
+ T.contract_disease(new path, 1)
feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].")
message_admins("\blue [key_name_admin(usr)] gave [key_name(T)] the disease [D].", 1)
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 21c1b6344c..9eb85cf34d 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -265,6 +265,9 @@
else if(isobserver(M))
M_job = "Ghost"
+
+ else if(isSpirit(M))
+ M_job = (ismask(M)) ? "Mask" : "Spirit"
M_job = replacetext(M_job, "'", "")
M_job = replacetext(M_job, "\"", "")
@@ -348,6 +351,10 @@
dat += "
Monkey
"
else if(isalien(M))
dat += "
Alien
"
+ else if(ismask(M))
+ dat += "
Mask
"
+ else if(isSpirit(M))
+ dat += "
Spirit
"
else
dat += "
Unknown
"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 5129bba4a0..2834b4d404 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1200,6 +1200,15 @@
message_admins("\red Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1)
log_admin("[key_name(usr)] AIized [key_name(H)]")
H.AIize()
+
+
+ else if(href_list["makemask"])
+ if(!check_rights(R_SPAWN)) return
+ var/mob/currentMob = locate(href_list["makemask"])
+ message_admins("\red Admin [key_name_admin(usr)] made [key_name_admin(currentMob)] into a Mask of Nar'Sie!", 1)
+ log_admin("[key_name(usr)] made [key_name(currentMob)] into a Mask of Nar'Sie!")
+ currentMob.make_into_mask(0,0)
+
else if(href_list["makealien"])
if(!check_rights(R_SPAWN)) return
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 0a7c1fe345..d0c552fe15 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -58,7 +58,7 @@ var/intercom_range_display_status = 0
del(C)
if(camera_range_display_status)
- for(var/obj/machinery/camera/C in cameranet.cameras)
+ for(var/obj/machinery/camera/C in cameranet.viewpoints)
new/obj/effect/debugging/camera_range(C.loc)
feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -74,7 +74,7 @@ var/intercom_range_display_status = 0
var/list/obj/machinery/camera/CL = list()
- for(var/obj/machinery/camera/C in cameranet.cameras)
+ for(var/obj/machinery/camera/C in cameranet.viewpoints)
CL += C
var/output = {"CAMERA ANNOMALITIES REPORT
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 6d65e39dd8..9ec56f1fc9 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -248,7 +248,6 @@ BLIND // can't see anything
var/rolled_down = 0
var/basecolor
-
/obj/item/clothing/under/attackby(obj/item/I, mob/user)
if(hastie)
hastie.attackby(I, user)
@@ -257,7 +256,7 @@ BLIND // can't see anything
if(!hastie && istype(I, /obj/item/clothing/tie))
user.drop_item()
hastie = I
- hastie.attach_to(src, user)
+ hastie.on_attached(src, user)
if(istype(loc, /mob/living/carbon/human))
var/mob/living/carbon/human/H = loc
@@ -274,7 +273,7 @@ BLIND // can't see anything
return
..()
-//This is to allow people to take off suits when there is an attached accessory
+//This is to ensure people can take off suits when there is an attached accessory
/obj/item/clothing/under/MouseDrop(obj/over_object as obj)
if (ishuman(usr) || ismonkey(usr))
//makes sure that the clothing is equipped so that we can't drag it into our hand from miles away.
@@ -352,6 +351,17 @@ BLIND // can't see anything
else
usr << "You cannot roll down the uniform!"
+/obj/item/clothing/under/proc/remove_accessory(mob/user as mob)
+ if(!hastie)
+ return
+
+ hastie.on_removed(user)
+ hastie = null
+
+ if(istype(loc, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = loc
+ H.update_inv_w_uniform()
+
/obj/item/clothing/under/verb/removetie()
set name = "Remove Accessory"
set category = "Object"
@@ -359,13 +369,7 @@ BLIND // can't see anything
if(!istype(usr, /mob/living)) return
if(usr.stat) return
- if(hastie)
- hastie.remove(usr)
- hastie = null
-
- if(istype(loc, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = loc
- H.update_inv_w_uniform()
+ src.remove_accessory(usr)
/obj/item/clothing/under/rank/New()
sensor_mode = pick(0,1,2,3)
diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm
index ffbe5c0a8b..f14101fcd9 100644
--- a/code/modules/clothing/masks/breath.dm
+++ b/code/modules/clothing/masks/breath.dm
@@ -43,18 +43,11 @@
icon_state = "voxmask"
item_state = "voxmask"
permeability_coefficient = 0.01
+ species_restricted = ("Vox")
toggle()
set category = "Object"
set name = "Adjust mask"
set src in usr
- usr << "You can't really adjust this mask - it's moulded to your beak!"
-
-/obj/item/clothing/mask/breath/vox/mob_can_equip(M as mob, slot)
- var/mob/living/carbon/human/V = M
- if(V.species.name != "Vox")
- V << "This clearly isn't designed for your species!"
- return 0
-
- return ..()
+ usr << "You can't really adjust this mask - it's moulded to your beak!"
\ No newline at end of file
diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm
index 8016a99e72..4e931595fa 100644
--- a/code/modules/clothing/spacesuits/alien.dm
+++ b/code/modules/clothing/spacesuits/alien.dm
@@ -130,12 +130,14 @@
icon_state = "vox-carapace"
item_state = "vox-carapace"
desc = "A glowing visor, perhaps stolen from a depressed Cylon."
+ species_restricted = list("Vox","Vox Armalis")
/obj/item/clothing/suit/space/vox/carapace
name = "alien carapace armour"
icon_state = "vox-carapace"
item_state = "vox-carapace"
desc = "An armoured, segmented carapace with glowing purple lights. It looks pretty run-down."
+ species_restricted = list("Vox","Vox Armalis")
/obj/item/clothing/head/helmet/space/vox/stealth
name = "alien stealth helmet"
@@ -187,7 +189,7 @@
siemens_coefficient = 0
permeability_coefficient = 0.05
item_color="gloves-vox"
- species_restricted = list("Vox")
+ species_restricted = list("Vox","Vox Armalis")
/obj/item/clothing/shoes/magboots/vox
@@ -195,7 +197,7 @@
name = "vox magclaws"
item_state = "boots-vox"
icon_state = "boots-vox"
- species_restricted = list("Vox")
+ species_restricted = list("Vox","Vox Armalis")
action_button_name = "Toggle the magclaws"
/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user)
@@ -212,8 +214,8 @@
if (H.shoes != src)
user << "You will have to put on the [src] before you can do that."
return
-
-
+
+
flags |= NOSLIP
magpulse = 1
canremove = 0 //kinda hard to take off magclaws when you are gripping them tightly.
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index 9f45d7a53d..a88cd5033c 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -9,23 +9,42 @@
slot_flags = 0
w_class = 2.0
var/obj/item/clothing/under/has_suit = null //the suit the tie may be attached to
+ var/image/inv_overlay = null //overlay used when attached to clothing.
+
+/obj/item/clothing/tie/New()
+ ..()
+ inv_overlay = image("icon" = 'icons/obj/clothing/ties_overlay.dmi', "icon_state" = "[item_color? "[item_color]" : "[icon_state]"]")
//when user attached an accessory to S
-/obj/item/clothing/tie/proc/attach_to(obj/item/clothing/under/S, mob/user as mob)
+/obj/item/clothing/tie/proc/on_attached(obj/item/clothing/under/S, mob/user as mob)
if(!istype(S))
return
has_suit = S
loc = has_suit
+ has_suit.overlays += inv_overlay
+
user << "You attach [src] to [has_suit]."
src.add_fingerprint(user)
-/obj/item/clothing/tie/proc/remove(mob/user as mob)
+/obj/item/clothing/tie/proc/on_removed(mob/user as mob)
if(!has_suit)
return
+ has_suit.overlays -= inv_overlay
has_suit = null
usr.put_in_hands(src)
src.add_fingerprint(user)
+//default attackby behaviour
+/obj/item/clothing/tie/attackby(obj/item/I, mob/user)
+ ..()
+
+//default attack_hand behaviour
+/obj/item/clothing/tie/attack_hand(mob/user as mob)
+ if(has_suit)
+ has_suit.remove_accessory(user)
+ return //we aren't an object on the ground so don't call parent
+ ..()
+
/obj/item/clothing/tie/blue
name = "blue tie"
icon_state = "bluetie"
@@ -244,11 +263,11 @@
else
usr << "It is empty."
-/obj/item/clothing/tie/holster/attach_to(obj/item/clothing/under/S, mob/user as mob)
+/obj/item/clothing/tie/holster/on_attached(obj/item/clothing/under/S, mob/user as mob)
..()
has_suit.verbs += /obj/item/clothing/tie/holster/verb/holster_verb
-/obj/item/clothing/tie/holster/remove(mob/user as mob)
+/obj/item/clothing/tie/holster/on_removed(mob/user as mob)
has_suit.verbs -= /obj/item/clothing/tie/holster/verb/holster_verb
..()
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index b9a27f007f..0e40136276 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -104,6 +104,14 @@
icon = 'icons/obj/custom_items.dmi'
icon_state = "matthewriebhardt"
+/////////////////////// Serveris: Officer's Notebook ////////////////////
+
+/obj/item/weapon/folder/blue/fluff/officer_notebook
+ name = "Officer's Notebook"
+ desc = "A simple, spiral bound notebook. A holographic crescent moon is printed on the cover, as well as 'S. Seto' beneath it. Numerous paper flags divide the pages, titled everything from incident reports to personal notes."
+ icon = 'icons/obj/custom_items.dmi'
+ icon_state = "syrus_notebook"
+
/obj/item/weapon/pen/fluff/multi //spaceman96: Trenna Seber
name = "multicolor pen"
desc = "It's a cool looking pen. Lots of colors!"
@@ -584,6 +592,15 @@
icon_state = "officerberet"
flags = FPRINT | TABLEPASS
+////////////////////////////// Serithi - Adapted Security Helmet //////////////////////////////
+
+/obj/item/clothing/head/helmet/fluff/adapted
+ name = "Adapted helmet"
+ desc = "Standard Security gear. Protects the head from impacts. This helmet is specially made for horned Unathi."
+ item_state = "adapted_h"
+ icon_state = "adapted_h"
+ icon = 'icons/obj/custom_items.dmi'
+
//////////// Suits ////////////
/obj/item/clothing/suit/storage/labcoat/fluff/aeneas_rinil //Robotics Labcoat - Aeneas Rinil [APPR]
@@ -621,6 +638,15 @@
item_state = "mantle-unathi"
body_parts_covered = UPPER_TORSO
+/////////////////////////////// 50_n00b - R.A.N.G.E.'s blue dress //////////////////////////
+
+/obj/item/clothing/suit/fluff/b_dress
+ name = "Blue dress"
+ desc = "A blue dress, worn with a white blouse. It is small, and looks like it won't fit a full grown human."
+ icon = 'icons/obj/custom_items.dmi'
+ icon_state = "br_dress"
+ item_state = "br_dress"
+
//////////// Uniforms ////////////
/obj/item/clothing/under/fluff/milo_hachert //Field Dress Uniform - Milo Hachert - Commissar_Drew
@@ -711,6 +737,14 @@
item_state = "solara_dress"
item_color = "solara_dress"
+/obj/item/clothing/under/rank/nursesuit/fluff/sasha
+ name = "RN Uniform"
+ desc = "A nurse's uniform that is dark blue and gold. It looks like it's been tailored for a short person."
+ icon = 'icons/obj/custom_items.dmi'
+ icon_state = "sasha_s"
+ item_state = "sasha_s"
+ item_color = "sasha_s"
+
/////// NT-SID Suit //Zuhayr: Jane Doe
/obj/item/clothing/under/fluff/jane_sidsuit
@@ -786,6 +820,15 @@
flags = FPRINT|TABLEPASS
w_class = 2
+/////////////// Oen'g Issek Medical Mask //////////////////////////
+
+/obj/item/clothing/mask/surgical/fluff/primitive
+ name = "primitive mask"
+ desc = "A decorated and creepy gas mask with the filters removed."
+ icon = 'icons/obj/custom_items.dmi'
+ item_state = "head_m"
+ icon_state = "head_m"
+
////// Small locket - Altair An-Nasaqan - Serithi
/obj/item/clothing/tie/fluff/altair_locket
@@ -1022,3 +1065,11 @@
/obj/item/weapon/fluff/farwadoll/attack_self(mob/user as mob)
user.visible_message("[user] hugs [src]! How cute! ", \
"You hug [src]. Dawwww... ")
+
+////////////////////////////// Meyar - Cane /////////////////////////////////////////////////
+
+/obj/item/weapon/cane/fluff/ryals
+ name = "cane"
+ desc = "This cane seems to have 'Ryals' engraved on its handle."
+ icon_state = "cane"
+ item_state = "stick"
\ No newline at end of file
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 1853826f34..1e5056db49 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -17,7 +17,6 @@
message = stars(message)
if(language)
- verb = language.speech_verb
style = language.colour
var/speaker_name = speaker.name
@@ -42,7 +41,7 @@
if(speaker == src)
src << "You cannot hear yourself speak!"
else
- src << "[speaker_name][alt_name] talks but you cannot hear them."
+ src << "[speaker_name][alt_name] talks but you cannot hear \him."
else
src << "[speaker_name][alt_name] [track][verb], \"[message]\""
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index c94e85658c..27d332b9c2 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -20,13 +20,21 @@
flags = WHITELISTED
/datum/language/tajaran
- name = "Siik'tajr"
- desc = "An expressive language that combines yowls and chirps with posture, tail and ears. Native to the Tajaran."
+ name = "Siik'maas"
+ desc = "The traditionally employed tongue of Ahdomai, composed of expressive yowls and chirps. Native to the Tajaran."
speech_verb = "mrowls"
colour = "tajaran"
key = "j"
flags = WHITELISTED
+/datum/language/tajaran_sign
+ name = "Siik'tajr"
+ desc = "An expressive language that combines yowls and chirps with posture, tail and ears. Spoken by many Tajaran."
+ speech_verb = "mrowls"
+ colour = "tajaran"
+ key = "y" //only "dfpqyz" left.
+ flags = WHITELISTED | NONVERBAL
+
/datum/language/skrell
name = "Skrellian"
desc = "A melodic and complex language spoken by the Skrell of Qerrbalak. Some of the notes are inaudible to humans."
@@ -90,6 +98,11 @@
return 0
+// Can we speak this language, as opposed to just understanding it?
+/mob/proc/can_speak(datum/language/speaking)
+
+ return (universal_speak || speaking in src.languages)
+
//TBD
/mob/verb/check_languages()
set name = "Check Known Languages"
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index bebd5680ef..292f74dbc5 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -71,9 +71,10 @@
var/datum/organ/external/head = get_organ("head")
var/mob/living/simple_animal/borer/B
- for(var/I in head.implants)
- if(istype(I,/mob/living/simple_animal/borer))
- B = I
+ if(istype(head))
+ for(var/I in head.implants)
+ if(istype(I,/mob/living/simple_animal/borer))
+ B = I
if(B)
if(!B.ckey && ckey && B.controlling)
B.ckey = ckey
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 3dffd15774..9f5289c43f 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -28,6 +28,10 @@
h_style = "Short Vox Quills"
..(new_loc, "Vox")
+/mob/living/carbon/human/voxarmalis/New(var/new_loc)
+ h_style = "Bald"
+ ..(new_loc, "Vox Armalis")
+
/mob/living/carbon/human/diona/New(var/new_loc)
..(new_loc, "Diona")
@@ -95,6 +99,13 @@
now_pushing = 0
return
+ //Leaping mobs just land on the tile, no pushing, no anything.
+ if(status_flags & LEAPING)
+ loc = tmob.loc
+ status_flags &= ~LEAPING
+ now_pushing = 0
+ return
+
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
if((tmob.a_intent == "help" || tmob.restrained()) && (a_intent == "help" || src.restrained()) && tmob.canmove && canmove) // mutual brohugs all around!
var/turf/oldloc = loc
@@ -1308,4 +1319,106 @@
. = 0
if(!. && error_msg && user)
// Might need re-wording.
- user << "There is no exposed flesh or thin material [target_zone == "head" ? "on their head" : "on their body"] to inject into."
+ user << "There is no exposed flesh or thin material [target_zone == "head" ? "on their head" : "on their body"] to inject into."
+
+
+//Putting a couple of procs here that I don't know where else to dump.
+//Mostly going to be used for Vox and Vox Armalis, but other human mobs might like them (for adminbuse).
+
+/mob/living/carbon/human/proc/leap()
+ set category = "IC"
+ set name = "Leap"
+ set desc = "Leap at a target and grab them aggressively."
+
+ if(last_special > world.time)
+ return
+
+ if(stat || paralysis || stunned || weakened || lying)
+ src << "You cannot leap in your current state."
+ return
+
+ var/list/choices = list()
+ for(var/mob/living/M in view(6,src))
+ if(!istype(M,/mob/living/silicon))
+ choices += M
+ choices -= src
+
+ var/mob/living/T = input(src,"Who do you wish to leap at?") in null|choices
+
+ if(!T || !src || src.stat) return
+
+ if(get_dist(get_turf(T), get_turf(src)) > 6) return
+
+ last_special = world.time + 100
+ status_flags |= LEAPING
+
+ src.visible_message("\The [src] leaps at [T]!")
+ src.throw_at(get_step(get_turf(T),get_turf(src)), 5, 1)
+ playsound(src.loc, 'sound/voice/shriek1.ogg', 50, 1)
+
+ sleep(5)
+
+ if(status_flags & LEAPING) status_flags &= ~LEAPING
+
+ if(!src.Adjacent(T))
+ src << "\red You miss!"
+ return
+
+ T.Weaken(5)
+
+ var/use_hand = "left"
+ if(l_hand)
+ if(r_hand)
+ src << "\red You need to have one hand free to grab someone."
+ return
+ else
+ use_hand = "right"
+
+ src.visible_message("\The [src] seizes [T] aggressively!")
+
+ var/obj/item/weapon/grab/G = new(src,T)
+ if(use_hand == "left")
+ l_hand = G
+ else
+ r_hand = G
+
+ G.state = GRAB_AGGRESSIVE
+ G.icon_state = "grabbed1"
+ G.synch()
+
+/mob/living/carbon/human/proc/gut()
+ set category = "IC"
+ set name = "Gut"
+ set desc = "While grabbing someone aggressively, rip their guts out or tear them apart."
+
+ if(last_special > world.time)
+ return
+
+ if(stat || paralysis || stunned || weakened || lying)
+ src << "\red You cannot do that in your current state."
+ return
+
+ var/obj/item/weapon/grab/G = locate() in src
+ if(!G || !istype(G))
+ src << "\red You are not grabbing anyone."
+ return
+
+ if(G.state < GRAB_AGGRESSIVE)
+ src << "\red You must have an aggressive grab to gut your prey!"
+ return
+
+ last_special = world.time + 50
+
+ visible_message("\The [src] rips viciously at \the [G.affecting]'s body with its claws!")
+
+ if(istype(G.affecting,/mob/living/carbon/human))
+ var/mob/living/carbon/human/H = G.affecting
+ H.apply_damage(50,BRUTE)
+ if(H.stat == 2)
+ H.gib()
+ else
+ var/mob/living/M = G.affecting
+ if(!istype(M)) return //wut
+ M.apply_damage(50,BRUTE)
+ if(M.stat == 2)
+ M.gib()
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index ef73710df1..036a84b8b1 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -436,111 +436,162 @@
return 0
- var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
- //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now)
- var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
- var/safe_phoron_max = 0.005
+ var/safe_pressure_min = 16 // Minimum safe partial pressure of breathable gas in kPa
+ //var/safe_pressure_max = 140 // Maximum safe partial pressure of breathable gas in kPa (Not used for now)
+ var/safe_exhaled_max = 10 // Yes it's an arbitrary value who cares?
+ var/safe_toxins_max = 0.005
var/SA_para_min = 1
var/SA_sleep_min = 5
- var/oxygen_used = 0
- var/nitrogen_used = 0
+ var/inhaled_gas_used = 0
+
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
- var/vox_oxygen_max = 1 // For vox.
- //Partial pressure of the O2 in our breath
- var/O2_pp = (breath.oxygen/breath.total_moles())*breath_pressure
- // Same, but for the phoron
- var/Toxins_pp = (breath.phoron/breath.total_moles())*breath_pressure
- // And CO2, lets say a PP of more than 10 will be bad (It's a little less really, but eh, being passed out all round aint no fun)
- var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*breath_pressure // Tweaking to fit the hacky bullshit I've done with atmo -- TLE
- //var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*0.5 // The default pressure value
- // Nitrogen, for Vox.
- var/Nitrogen_pp = (breath.nitrogen/breath.total_moles())*breath_pressure
+ var/inhaling
+ var/exhaling
+ var/poison
+ var/no_exhale
- if(O2_pp < safe_oxygen_min && species.name != "Vox") // Too little oxygen
- if(prob(20))
- spawn(0) emote("gasp")
- if(O2_pp > 0)
- var/ratio = safe_oxygen_min/O2_pp
- adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!)
- failed_last_breath = 1
- oxygen_used = breath.oxygen*ratio/6
+ switch(species.breath_type)
+ if("nitrogen")
+ inhaling = breath.nitrogen
+ if("phoron")
+ inhaling = breath.phoron
+ if("C02")
+ inhaling = breath.carbon_dioxide
else
- adjustOxyLoss(HUMAN_MAX_OXYLOSS)
- failed_last_breath = 1
- oxygen_alert = max(oxygen_alert, 1)
- /*else if (O2_pp > safe_oxygen_max) // Too much oxygen (commented this out for now, I'll deal with pressure damage elsewhere I suppose)
- spawn(0) emote("cough")
- var/ratio = O2_pp/safe_oxygen_max
- oxyloss += 5*ratio
- oxygen_used = breath.oxygen*ratio/6
- oxygen_alert = max(oxygen_alert, 1)*/
- else if(Nitrogen_pp < safe_oxygen_min && species.name == "Vox") //Vox breathe nitrogen, not oxygen.
+ inhaling = breath.oxygen
+ switch(species.poison_type)
+ if("oxygen")
+ poison = breath.oxygen
+ if("nitrogen")
+ poison = breath.nitrogen
+ if("C02")
+ poison = breath.carbon_dioxide
+ else
+ poison = breath.phoron
+
+ switch(species.exhale_type)
+ if("C02")
+ exhaling = breath.carbon_dioxide
+ if("oxygen")
+ exhaling = breath.oxygen
+ if("nitrogen")
+ exhaling = breath.nitrogen
+ if("phoron")
+ exhaling = breath.phoron
+ else
+ no_exhale = 1
+
+ var/inhale_pp = (inhaling/breath.total_moles())*breath_pressure
+ var/toxins_pp = (poison/breath.total_moles())*breath_pressure
+ var/exhaled_pp = (exhaling/breath.total_moles())*breath_pressure
+
+ if(inhale_pp < safe_pressure_min)
if(prob(20))
spawn(0) emote("gasp")
- if(Nitrogen_pp > 0)
- var/ratio = safe_oxygen_min/Nitrogen_pp
+ if(inhale_pp > 0)
+ var/ratio = safe_pressure_min/inhale_pp
+
+ // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!)
adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS))
failed_last_breath = 1
- nitrogen_used = breath.nitrogen*ratio/6
+ inhaled_gas_used = inhaling*ratio/6
+
else
+
adjustOxyLoss(HUMAN_MAX_OXYLOSS)
failed_last_breath = 1
+
oxygen_alert = max(oxygen_alert, 1)
- else // We're in safe limits
+ else
+ // We're in safe limits
failed_last_breath = 0
adjustOxyLoss(-5)
- oxygen_used = breath.oxygen/6
+ inhaled_gas_used = inhaling/6
oxygen_alert = 0
- breath.oxygen -= oxygen_used
- breath.nitrogen -= nitrogen_used
- breath.carbon_dioxide += oxygen_used
+ switch(species.breath_type)
+ if("nitrogen")
+ breath.nitrogen -= inhaled_gas_used
+ else
+ breath.oxygen -= inhaled_gas_used
- //CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick.
- if(CO2_pp > safe_co2_max)
- if(!co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
+ if(!no_exhale)
+ switch(species.exhale_type)
+ if("oxygen")
+ breath.oxygen += inhaled_gas_used
+ if("nitrogen")
+ breath.nitrogen += inhaled_gas_used
+ if("phoron")
+ breath.phoron += inhaled_gas_used
+ if("C02")
+ breath.carbon_dioxide += inhaled_gas_used
+
+ // CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2,
+ // this will hurt you, but only once per 4 ticks, instead of once per tick.
+
+ if(exhaled_pp > safe_exhaled_max)
+
+ // If it's the first breath with too much CO2 in it, lets start a counter,
+ // then have them pass out after 12s or so.
+ if(!co2overloadtime)
co2overloadtime = world.time
+
else if(world.time - co2overloadtime > 120)
+
+ // Lets hurt em a little, let them know we mean business
Paralyse(3)
- adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business
- if(world.time - co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
+ adjustOxyLoss(3)
+
+ // They've been in here 30s now, lets start to kill them for their own good!
+ if(world.time - co2overloadtime > 300)
adjustOxyLoss(8)
- if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
+
+ // Lets give them some chance to know somethings not right though I guess.
+ if(prob(20))
spawn(0) emote("cough")
else
co2overloadtime = 0
- if(Toxins_pp > safe_phoron_max) // Too much phoron
- var/ratio = (breath.phoron/safe_phoron_max) * 10
- //adjustToxLoss(Clamp(ratio, MIN_PHORON_DAMAGE, MAX_PHORON_DAMAGE)) //Limit amount of damage toxin exposure can do per second
+ // Too much poison in the air.
+ if(toxins_pp > safe_toxins_max)
+ var/ratio = (poison/safe_toxins_max) * 10
if(reagents)
+ //TODO: Fix Ravensdale's shit, make toxins toxins again instead of phoron.
reagents.add_reagent("phoron", Clamp(ratio, MIN_PHORON_DAMAGE, MAX_PHORON_DAMAGE))
phoron_alert = max(phoron_alert, 1)
- else if(O2_pp > vox_oxygen_max && species.name == "Vox") //Oxygen is toxic to vox.
- var/ratio = (breath.oxygen/vox_oxygen_max) * 1000
- adjustToxLoss(Clamp(ratio, MIN_PHORON_DAMAGE, MAX_PHORON_DAMAGE))
- phoron_alert = max(phoron_alert, 1)
else
phoron_alert = 0
- if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here.
+ // If there's some other shit in the air lets deal with it here.
+ if(breath.trace_gases.len)
for(var/datum/gas/sleeping_agent/SA in breath.trace_gases)
var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure
- if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit
- Paralyse(3) // 3 gives them one second to wake up and run away a bit!
- if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
+
+ // Enough to make us paralysed for a bit
+ if(SA_pp > SA_para_min)
+
+ // 3 gives them one second to wake up and run away a bit!
+ Paralyse(3)
+
+ // Enough to make us sleep as well
+ if(SA_pp > SA_sleep_min)
sleeping = min(sleeping+2, 10)
- else if(SA_pp > 0.15) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
+
+ // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
+ else if(SA_pp > 0.15)
if(prob(20))
spawn(0) emote(pick("giggle", "laugh"))
SA.moles = 0
if( (abs(310.15 - breath.temperature) > 50) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :(
- if(status_flags & GODMODE) return 1 //godmode
+
+ if(status_flags & GODMODE)
+ return 1
+
if(breath.temperature < species.cold_level_1)
if(prob(20))
src << "\red You feel your face freezing and an icicle forming in your lungs!"
@@ -575,6 +626,11 @@
proc/handle_environment(datum/gas_mixture/environment)
if(!environment)
return
+
+ //Moved pressure calculations here for use in skip-processing check.
+ var/pressure = environment.return_pressure()
+ var/adjusted_pressure = calculate_affecting_pressure(pressure)
+
if(!istype(get_turf(src), /turf/space)) //space is not meant to change your body temperature.
var/loc_temp = T0C
if(istype(loc, /obj/mecha))
@@ -586,7 +642,7 @@
else
loc_temp = environment.temperature
- if(abs(loc_temp - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.phoron < MOLES_PHORON_VISIBLE)
+ if(adjusted_pressure < species.warning_low_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.phoron < MOLES_PHORON_VISIBLE)
return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp
//Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit.
@@ -638,9 +694,6 @@
// Account for massive pressure differences. Done by Polymorph
// Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense!
-
- var/pressure = environment.return_pressure()
- var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob.
if(status_flags & GODMODE) return 1 //godmode
if(adjusted_pressure >= species.hazard_high_pressure)
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 32a64a145b..9f448d57e6 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -8,8 +8,8 @@
if(client.prefs.muted & MUTE_IC)
src << "\red You cannot speak in IC (Muted)."
return
-
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+
+ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if(stat == 2)
return say_dead(message)
@@ -23,33 +23,21 @@
if(name != GetVoice())
alt_name = "(as [get_id_name("Unknown")])"
- var/message_mode = null
- var/datum/language/speaking = null
+ //parse the radio code and consume it
+ var/message_mode = parse_message_mode(message, "headset")
+ if (message_mode)
+ if (message_mode == "headset")
+ message = copytext(message,2) //it would be really nice if the parse procs could do this for us.
+ else
+ message = copytext(message,3)
- if(copytext(message,1,2) == ";")
- message_mode = "headset"
- message = copytext(message,2)
-
- if(length(message) >= 2)
- var/channel_prefix = copytext(message, 1 ,3)
- var/check_language_and_radio = copytext(message,3,5)
- if(languages.len)
- for(var/datum/language/L in languages)
- if(lowertext(channel_prefix) == ":[L.key]" || lowertext(check_language_and_radio) == ":[L.key]")
- verb = L.speech_verb
- speaking = L
- break
- if(!message_mode)
- message_mode = department_radio_keys[channel_prefix]
-
- if(speaking || copytext(message,1,2) == ":")
- var/positioncut = 3
- if(speaking && (message_mode && copytext(message,3,4)==":"))
- positioncut += 2
- message = trim(copytext(message,positioncut))
+ //parse the language code and consume it
+ var/datum/language/speaking = parse_language(message)
+ if (speaking)
+ verb = speaking.speech_verb
+ message = copytext(message,3)
-
- message = capitalize(trim_left(message))
+ message = capitalize(trim(message))
if(speech_problem_flag)
var/list/handle_r = handle_speech_problems(message)
@@ -60,11 +48,12 @@
if(!message || stat)
return
- var/ending = copytext(message, length(message))
- if(ending=="!")
- verb=pick("exclaims","shouts","yells")
- if(ending=="?")
- verb="asks"
+ if (!speaking)
+ var/ending = copytext(message, length(message))
+ if(ending=="!")
+ verb=pick("exclaims","shouts","yells")
+ if(ending=="?")
+ verb="asks"
var/list/obj/item/used_radios = new
@@ -100,7 +89,7 @@
R = l_ear
has_radio = 1
if(l_hand && istype(l_hand,/obj/item/device/radio))
- R = l_hand
+ R = l_hand
has_radio = 1
if(has_radio)
R.talk_into(src,message,null,verb,speaking)
@@ -111,7 +100,7 @@
I.talk_into(src, message, verb, speaking)
used_radios += I
if("whisper")
- whisper(message)
+ whisper_say(message, speaking, alt_name)
return
if("binary")
if(robot_talk_understand || binarycheck())
@@ -131,7 +120,7 @@
used_radios += l_ear
else if(r_ear && istype(r_ear,/obj/item/device/radio))
r_ear.talk_into(src,message, message_mode, verb, speaking)
- used_radios += r_ear
+ used_radios += r_ear
if(used_radios.len)
@@ -145,26 +134,33 @@
italics = 1
message_range =1
+ if((species.name == "Vox" || species.name == "Vox Armalis") && prob(20))
+ playsound(src.loc, 'sound/voice/shriek1.ogg', 50, 1)
+
..(message, speaking, verb, alt_name, italics, message_range, used_radios)
/mob/living/carbon/human/say_understands(var/mob/other,var/datum/language/speaking = null)
if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak.
return 1
- if (istype(other, /mob/living/carbon/monkey/diona) && !speaking)
- if(other.languages.len >= 2) //They've sucked down some blood and can speak common now.
- return 1
- if (istype(other, /mob/living/silicon))
- return 1
- if (istype(other, /mob/living/carbon/brain))
- return 1
- if (istype(other, /mob/living/carbon/slime))
- return 1
- if (istype(other, /mob/living/simple_animal))
- if(other.universal_speak || src.universal_speak || src.universal_understand)
+ //These only pertain to common. Languages are handled by mob/say_understands()
+ if (!speaking)
+ if (istype(other, /mob/living/carbon/monkey/diona))
+ if(other.languages.len >= 2) //They've sucked down some blood and can speak common now.
+ return 1
+ if (istype(other, /mob/living/silicon))
return 1
- return 0
+ if (istype(other, /mob/living/carbon/brain))
+ return 1
+ if (istype(other, /mob/living/carbon/slime))
+ return 1
+
+ //This is already covered by mob/say_understands()
+ //if (istype(other, /mob/living/simple_animal))
+ // if((other.universal_speak && !speaking) || src.universal_speak || src.universal_understand)
+ // return 1
+ // return 0
return ..()
@@ -234,5 +230,5 @@
returns[1] = message
returns[2] = verb
returns[3] = handled
-
+
return returns
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 2c4a381d6e..d2bcbfd5a6 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -1,3 +1,11 @@
+/*
+ Global associative list for caching humanoid icons.
+ Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0.
+ TODO: Proper documentation
+ icon_key is [species.race_key][g][husk][fat][hulk][skeleton][s_tone]
+*/
+var/global/list/human_icon_cache = list()
+
///////////////////////
//UPDATE_ICONS SYSTEM//
///////////////////////
@@ -195,7 +203,6 @@ proc/get_damage_icon_part(damage_state, body_part)
var/image/standing_image = new /image("icon" = standing)
-
// blend the individual damage states with our icons
for(var/datum/organ/external/O in organs)
if(!(O.status & ORGAN_DESTROYED))
@@ -206,44 +213,75 @@ proc/get_damage_icon_part(damage_state, body_part)
standing_image.overlays += DI
-
overlays_standing[DAMAGE_LAYER] = standing_image
if(update_icons) update_icons()
//BASE MOB SPRITE
/mob/living/carbon/human/proc/update_body(var/update_icons=1)
- if(stand_icon) del(stand_icon)
var/husk_color_mod = rgb(96,88,80)
var/hulk_color_mod = rgb(48,224,40)
var/necrosis_color_mod = rgb(10,50,0)
- var/husk = (HUSK in src.mutations) //100% unnecessary -Agouri //nope, do you really want to iterate through src.mutations repeatedly? -Pete
+ var/husk = (HUSK in src.mutations)
var/fat = (FAT in src.mutations)
var/hulk = (HULK in src.mutations)
var/skeleton = (SKELETON in src.mutations)
- var/g = "m"
- if(gender == FEMALE) g = "f"
-
- var/datum/organ/external/chest = get_organ("chest")
- stand_icon = chest.get_icon(g)
- if(!skeleton)
- if(husk)
- stand_icon.ColorTone(husk_color_mod)
- else if(hulk)
- var/list/TONE = ReadRGB(hulk_color_mod)
- stand_icon.MapColors(rgb(TONE[1],0,0),rgb(0,TONE[2],0),rgb(0,0,TONE[3]))
-
- var/datum/organ/external/head = get_organ("head")
+ var/g = (gender == FEMALE ? "f" : "m")
var/has_head = 0
- if(head && !(head.status & ORGAN_DESTROYED))
- has_head = 1
+ //CACHING: Generate an index key from visible bodyparts.
+ //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic.
+
+ //Create a new, blank icon for our mob to use.
+ if(stand_icon)
+ del(stand_icon)
+
+ stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi',"blank")
+
+ var/icon_key = "[species.race_key][g][s_tone]"
for(var/datum/organ/external/part in organs)
- if(!istype(part, /datum/organ/external/chest) && !(part.status & ORGAN_DESTROYED))
- var/icon/temp
+
+ if(istype(part,/datum/organ/external/head) && !(part.status & ORGAN_DESTROYED))
+ has_head = 1
+
+ if(part.status & ORGAN_DESTROYED)
+ icon_key = "[icon_key]0"
+ else if(part.status & ORGAN_ROBOT)
+ icon_key = "[icon_key]2"
+ else if(part.status & ORGAN_DEAD) //Do we even have necrosis in our current code? ~Z
+ icon_key = "[icon_key]3"
+ else
+ icon_key = "[icon_key]1"
+
+ icon_key = "[icon_key][husk ? 1 : 0][fat ? 1 : 0][hulk ? 1 : 0][skeleton ? 1 : 0][s_tone]"
+
+ var/icon/base_icon
+ if(human_icon_cache[icon_key])
+ //Icon is cached, use existing icon.
+ base_icon = human_icon_cache[icon_key]
+
+ //log_debug("Retrieved cached mob icon ([icon_key] \icon[human_icon_cache[icon_key]]) for [src].")
+
+ else
+
+ //BEGIN CACHED ICON GENERATION.
+
+ //Icon is not cached, generate and store it.
+ //Robotic limbs are handled in get_icon() so all we worry about are missing or dead limbs.
+ //No icon stored, so we need to start with a basic one.
+ var/datum/organ/external/chest = get_organ("chest")
+ base_icon = chest.get_icon(g)
+
+ for(var/datum/organ/external/part in organs)
+
+ var/icon/temp //Hold the bodypart icon for processing.
+
+ if(part.status & ORGAN_DESTROYED)
+ continue
+
if (istype(part, /datum/organ/external/groin) || istype(part, /datum/organ/external/head))
temp = part.get_icon(g)
else
@@ -253,51 +291,71 @@ proc/get_damage_icon_part(damage_state, body_part)
temp.ColorTone(necrosis_color_mod)
temp.SetIntensity(0.7)
- else if(!skeleton)
- if(husk)
- temp.ColorTone(husk_color_mod)
- else if(hulk)
- var/list/TONE = ReadRGB(hulk_color_mod)
- temp.MapColors(rgb(TONE[1],0,0),rgb(0,TONE[2],0),rgb(0,0,TONE[3]))
-
//That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST
//And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part)
if(part.icon_position&(LEFT|RIGHT))
+
var/icon/temp2 = new('icons/mob/human.dmi',"blank")
+
temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH)
temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH)
+
if(!(part.icon_position & LEFT))
temp2.Insert(new/icon(temp,dir=EAST),dir=EAST)
+
if(!(part.icon_position & RIGHT))
temp2.Insert(new/icon(temp,dir=WEST),dir=WEST)
- stand_icon.Blend(temp2, ICON_OVERLAY)
- temp2 = new('icons/mob/human.dmi',"blank")
+
+ base_icon.Blend(temp2, ICON_OVERLAY)
+
if(part.icon_position & LEFT)
temp2.Insert(new/icon(temp,dir=EAST),dir=EAST)
+
if(part.icon_position & RIGHT)
temp2.Insert(new/icon(temp,dir=WEST),dir=WEST)
- stand_icon.Blend(temp2, ICON_UNDERLAY)
+
+ base_icon.Blend(temp2, ICON_UNDERLAY)
+
else
- stand_icon.Blend(temp, ICON_OVERLAY)
- //Skin tone
- if(!skeleton && !husk && !hulk && (species.flags & HAS_SKIN_TONE))
- if(s_tone >= 0)
- stand_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD)
- else
- stand_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
+ base_icon.Blend(temp, ICON_OVERLAY)
- //Skin color
- if(!skeleton && !husk && !hulk && (species.flags & HAS_SKIN_COLOR))
+ if(!skeleton)
+ if(husk)
+ base_icon.ColorTone(husk_color_mod)
+ else if(hulk)
+ var/list/tone = ReadRGB(hulk_color_mod)
+ base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3]))
+
+ //Handle husk overlay.
+ if(husk)
+ var/icon/mask = new(base_icon)
+ var/icon/husk_over = new(race_icon,"overlay_husk")
+ mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0)
+ husk_over.Blend(mask, ICON_ADD)
+ base_icon.Blend(husk_over, ICON_OVERLAY)
+
+
+ //Skin tone.
+ if(!husk && !hulk)
+ if(species.flags & HAS_SKIN_TONE)
+ if(s_tone >= 0)
+ base_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD)
+ else
+ base_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
+
+ human_icon_cache[icon_key] = base_icon
+
+ //log_debug("Generated new cached mob icon ([icon_key] \icon[human_icon_cache[icon_key]]) for [src]. [human_icon_cache.len] cached mob icons.")
+
+ //END CACHED ICON GENERATION.
+
+ stand_icon.Blend(base_icon,ICON_OVERLAY)
+
+ //Skin colour. Not in cache because highly variable (and relatively benign).
+ if (species.flags & HAS_SKIN_COLOR)
stand_icon.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD)
- if(husk)
- var/icon/mask = new(stand_icon)
- var/icon/husk_over = new(race_icon,"overlay_husk")
- mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0)
- husk_over.Blend(mask, ICON_ADD)
- stand_icon.Blend(husk_over, ICON_OVERLAY)
-
if(has_head)
//Eyes
if(!skeleton)
@@ -324,6 +382,8 @@ proc/get_damage_icon_part(damage_state, body_part)
update_tail_showing(0)
+
+
//HAIR OVERLAY
/mob/living/carbon/human/proc/update_hair(var/update_icons=1)
//Reset our hair
@@ -486,7 +546,7 @@ proc/get_damage_icon_part(damage_state, body_part)
if(!t_color) t_color = icon_state
var/image/standing = image("icon_state" = "[t_color]_s")
- standing.icon = ((w_uniform.icon_override) ? w_uniform.icon_override : 'icons/mob/uniform.dmi')
+ standing.icon = ((w_uniform.icon_override) ? w_uniform.icon_override : (species.sprite_sheets["uniform"] ? species.sprite_sheets["uniform"] : 'icons/mob/uniform.dmi'))
if(w_uniform.blood_DNA)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "uniformblood")
@@ -501,6 +561,7 @@ proc/get_damage_icon_part(damage_state, body_part)
overlays_standing[UNIFORM_LAYER] = standing
else
overlays_standing[UNIFORM_LAYER] = null
+ // This really, really seems like it should not be mixed in the middle of display code...
// Automatically drop anything in store / id / belt if you're not wearing a uniform. //CHECK IF NECESARRY
for( var/obj/item/thing in list(r_store, l_store, wear_id, belt) ) //
if(thing) //
@@ -533,7 +594,7 @@ proc/get_damage_icon_part(damage_state, body_part)
if(gloves)
var/t_state = gloves.item_state
if(!t_state) t_state = gloves.icon_state
- var/image/standing = image("icon" = ((gloves.icon_override) ? gloves.icon_override : 'icons/mob/hands.dmi'), "icon_state" = "[t_state]")
+ var/image/standing = image("icon" = ((gloves.icon_override) ? gloves.icon_override : (species.sprite_sheets["gloves"] ? species.sprite_sheets["gloves"] : 'icons/mob/hands.dmi')), "icon_state" = "[t_state]")
if(gloves.blood_DNA)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "bloodyhands")
bloodsies.color = gloves.blood_color
@@ -552,7 +613,7 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/update_inv_glasses(var/update_icons=1)
if(glasses)
- overlays_standing[GLASSES_LAYER] = image("icon" = ((glasses.icon_override) ? glasses.icon_override : 'icons/mob/eyes.dmi'), "icon_state" = "[glasses.icon_state]")
+ overlays_standing[GLASSES_LAYER] = image("icon" = ((glasses.icon_override) ? glasses.icon_override : (species.sprite_sheets["eyes"] ? species.sprite_sheets["eyes"] : 'icons/mob/eyes.dmi')), "icon_state" = "[glasses.icon_state]")
else
overlays_standing[GLASSES_LAYER] = null
if(update_icons) update_icons()
@@ -560,16 +621,20 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/update_inv_ears(var/update_icons=1)
if(l_ear || r_ear)
if(l_ear)
- overlays_standing[EARS_LAYER] = image("icon" = ((l_ear.icon_override) ? l_ear.icon_override : 'icons/mob/ears.dmi'), "icon_state" = "[l_ear.icon_state]")
+ var/t_type = l_ear.icon_state
+ if(l_ear.icon_override || species.sprite_sheets["ears"]) t_type = "[t_type]_l"
+ overlays_standing[EARS_LAYER] = image("icon" = ((l_ear.icon_override) ? l_ear.icon_override : (species.sprite_sheets["ears"] ? species.sprite_sheets["ears"] : 'icons/mob/ears.dmi')), "icon_state" = "[t_type]")
if(r_ear)
- overlays_standing[EARS_LAYER] = image("icon" = ((r_ear.icon_override) ? r_ear.icon_override : 'icons/mob/ears.dmi'), "icon_state" = "[r_ear.icon_state]")
+ var/t_type = r_ear.icon_state
+ if(r_ear.icon_override || species.sprite_sheets["ears"]) t_type = "[t_type]_r"
+ overlays_standing[EARS_LAYER] = image("icon" = ((r_ear.icon_override) ? r_ear.icon_override : (species.sprite_sheets["ears"] ? species.sprite_sheets["ears"] : 'icons/mob/ears.dmi')), "icon_state" = "t_type]")
else
overlays_standing[EARS_LAYER] = null
if(update_icons) update_icons()
/mob/living/carbon/human/update_inv_shoes(var/update_icons=1)
if(shoes)
- var/image/standing = image("icon" = ((shoes.icon_override) ? shoes.icon_override : 'icons/mob/feet.dmi'), "icon_state" = "[shoes.icon_state]")
+ var/image/standing = image("icon" = ((shoes.icon_override) ? shoes.icon_override : (species.sprite_sheets["feet"] ? species.sprite_sheets["feet"] : 'icons/mob/feet.dmi')), "icon_state" = "[shoes.icon_state]")
if(shoes.blood_DNA)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "shoeblood")
bloodsies.color = shoes.blood_color
@@ -602,7 +667,7 @@ proc/get_damage_icon_part(damage_state, body_part)
if(istype(head,/obj/item/clothing/head/kitty))
standing = image("icon" = head:mob)
else
- standing = image("icon" = ((head.icon_override) ? head.icon_override : 'icons/mob/head.dmi'), "icon_state" = "[head.icon_state]")
+ standing = image("icon" = ((head.icon_override) ? head.icon_override : (species.sprite_sheets["head"] ? species.sprite_sheets["head"] : 'icons/mob/head.dmi')), "icon_state" = "[head.icon_state]")
if(head.blood_DNA)
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "helmetblood")
bloodsies.color = head.blood_color
@@ -617,7 +682,7 @@ proc/get_damage_icon_part(damage_state, body_part)
belt.screen_loc = ui_belt //TODO
var/t_state = belt.item_state
if(!t_state) t_state = belt.icon_state
- overlays_standing[BELT_LAYER] = image("icon" = ((belt.icon_override) ? belt.icon_override : 'icons/mob/belt.dmi'), "icon_state" = "[t_state]")
+ overlays_standing[BELT_LAYER] = image("icon" = ((belt.icon_override) ? belt.icon_override : (species.sprite_sheets["belt"] ? species.sprite_sheets["belt"] : 'icons/mob/belt.dmi')), "icon_state" = "[t_state]")
else
overlays_standing[BELT_LAYER] = null
if(update_icons) update_icons()
@@ -626,7 +691,7 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/update_inv_wear_suit(var/update_icons=1)
if( wear_suit && istype(wear_suit, /obj/item/clothing/suit) ) //TODO check this
wear_suit.screen_loc = ui_oclothing //TODO
- var/image/standing = image("icon" = ((wear_suit.icon_override) ? wear_suit.icon_override : 'icons/mob/suit.dmi'), "icon_state" = "[wear_suit.icon_state]")
+ var/image/standing = image("icon" = ((wear_suit.icon_override) ? wear_suit.icon_override : (species.sprite_sheets["suit"] ? species.sprite_sheets["suit"] : 'icons/mob/suit.dmi')), "icon_state" = "[wear_suit.icon_state]")
if( istype(wear_suit, /obj/item/clothing/suit/straight_jacket) )
drop_from_inventory(handcuffed)
@@ -661,7 +726,7 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/update_inv_wear_mask(var/update_icons=1)
if( wear_mask && ( istype(wear_mask, /obj/item/clothing/mask) || istype(wear_mask, /obj/item/clothing/tie) ) )
wear_mask.screen_loc = ui_mask //TODO
- var/image/standing = image("icon" = ((wear_mask.icon_override) ? wear_mask.icon_override : 'icons/mob/mask.dmi'), "icon_state" = "[wear_mask.icon_state]")
+ var/image/standing = image("icon" = ((wear_mask.icon_override) ? wear_mask.icon_override : (species.sprite_sheets["mask"] ? species.sprite_sheets["mask"] : 'icons/mob/mask.dmi')), "icon_state" = "[wear_mask.icon_state]")
if( !istype(wear_mask, /obj/item/clothing/mask/cigarette) && wear_mask.blood_DNA )
var/image/bloodsies = image("icon" = 'icons/effects/blood.dmi', "icon_state" = "maskblood")
bloodsies.color = wear_mask.blood_color
@@ -675,7 +740,7 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/update_inv_back(var/update_icons=1)
if(back)
back.screen_loc = ui_back //TODO
- overlays_standing[BACK_LAYER] = image("icon" = ((back.icon_override) ? back.icon_override : 'icons/mob/back.dmi'), "icon_state" = "[back.icon_state]")
+ overlays_standing[BACK_LAYER] = image("icon" = ((back.icon_override) ? back.icon_override : (species.sprite_sheets["back"] ? species.sprite_sheets["back"] : 'icons/mob/back.dmi')), "icon_state" = "[back.icon_state]")
else
overlays_standing[BACK_LAYER] = null
if(update_icons) update_icons()
@@ -716,7 +781,8 @@ proc/get_damage_icon_part(damage_state, body_part)
r_hand.screen_loc = ui_rhand //TODO
var/t_state = r_hand.item_state
if(!t_state) t_state = r_hand.icon_state
- overlays_standing[R_HAND_LAYER] = image("icon" = 'icons/mob/items_righthand.dmi', "icon_state" = "[t_state]")
+ if(r_hand.icon_override || species.sprite_sheets["held"]) t_state = "[t_state]_r"
+ overlays_standing[R_HAND_LAYER] = image("icon" = ((r_hand.icon_override) ? r_hand.icon_override : (species.sprite_sheets["held"] ? species.sprite_sheets["held"] : 'icons/mob/items_righthand.dmi')), "icon_state" = "[t_state]")
if (handcuffed) drop_r_hand()
else
overlays_standing[R_HAND_LAYER] = null
@@ -728,7 +794,8 @@ proc/get_damage_icon_part(damage_state, body_part)
l_hand.screen_loc = ui_lhand //TODO
var/t_state = l_hand.item_state
if(!t_state) t_state = l_hand.icon_state
- overlays_standing[L_HAND_LAYER] = image("icon" = 'icons/mob/items_lefthand.dmi', "icon_state" = "[t_state]")
+ if(l_hand.icon_override || species.sprite_sheets["held"]) t_state = "[t_state]_l"
+ overlays_standing[L_HAND_LAYER] = image("icon" = ((l_hand.icon_override) ? l_hand.icon_override : (species.sprite_sheets["held"] ? species.sprite_sheets["held"] : 'icons/mob/items_lefthand.dmi')), "icon_state" = "[t_state]")
if (handcuffed) drop_l_hand()
else
overlays_standing[L_HAND_LAYER] = null
@@ -737,12 +804,12 @@ proc/get_damage_icon_part(damage_state, body_part)
/mob/living/carbon/human/proc/update_tail_showing(var/update_icons=1)
overlays_standing[TAIL_LAYER] = null
- if(species.tail && species.flags & HAS_TAIL)
+ if(species.tail && species.flags & HAS_TAIL)
if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space))
var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[species.tail]_s")
tail_s.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD)
- overlays_standing[TAIL_LAYER] = image(tail_s)
+ overlays_standing[TAIL_LAYER] = image(tail_s)
if(update_icons)
update_icons()
diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm
index 7f7f977b70..b365fb3922 100644
--- a/code/modules/mob/living/carbon/human/whisper.dm
+++ b/code/modules/mob/living/carbon/human/whisper.dm
@@ -1,15 +1,11 @@
//Lallander was here
/mob/living/carbon/human/whisper(message as text)
+ var/alt_name = ""
if(say_disabled) //This is here to try to identify lag problems
usr << "\red Speech is currently admin-disabled."
return
-
- message = trim(copytext(strip_html_simple(message), 1, MAX_MESSAGE_LEN))
-
- if (!message || silent || miming)
- return
-
+
log_whisper("[src.name]/[src.key] : [message]")
if (src.client)
@@ -20,27 +16,51 @@
if (src.client.handle_spam_prevention(message,MUTE_IC))
return
-
if (src.stat == 2)
return src.say_dead(message)
if (src.stat)
return
+
+ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) //made consistent with say
- var/alt_name = ""
- if (istype(src, /mob/living/carbon/human) && src.name != GetVoice())
- var/mob/living/carbon/human/H = src
- alt_name = " (as [H.get_id_name("Unknown")])"
+ if(name != GetVoice())
+ alt_name = "(as [get_id_name("Unknown")])"
+
+ //parse the language code and consume it
+ var/datum/language/speaking = parse_language(message)
+ if (speaking)
+ message = copytext(message,3)
+
+ whisper_say(message, speaking, alt_name)
+
+
+//This is used by both the whisper verb and human/say() to handle whispering
+/mob/living/carbon/human/proc/whisper_say(var/message, var/datum/language/speaking = null, var/alt_name="", var/verb="whispers")
+ var/message_range = 1
+ var/eavesdropping_range = 2
+ var/watching_range = 5
+ var/italics = 1
+
+ if (speaking)
+ verb = speaking.speech_verb + pick(" quietly", " softly")
+
+ message = capitalize(trim(message))
+
+ //TODO: handle_speech_problems for silent
+ if (!message || silent || miming)
+ return
+
// Mute disability
+ //TODO: handle_speech_problems
if (src.sdisabilities & MUTE)
return
+ //TODO: handle_speech_problems
if (istype(src.wear_mask, /obj/item/clothing/mask/muzzle))
return
- var/italics = 1
- var/message_range = 1
-
+ //looks like this only appears in whisper. Should it be elsewhere as well? Maybe handle_speech_problems?
if(istype(src.wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)&&src.wear_mask:voice=="Unknown")
if(copytext(message, 1, 2) != "*")
var/list/temp_message = text2list(message, " ")
@@ -60,90 +80,57 @@
message = replacetext(message, "u", "µ")
message = replacetext(message, "b", "ß")
+ //TODO: handle_speech_problems
if (src.stuttering)
message = stutter(message)
- for (var/obj/O in view(message_range, src))
- spawn (0)
- if (O)
- O.hear_talk(src, message)
-
var/list/listening = hearers(message_range, src)
listening |= src
-
+
+ //ghosts
+ for (var/mob/M in dead_mob_list) //does this include players who joined as observers as well?
+ if (!(M.client))
+ continue
+ if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS))
+ listening |= M
+
//Pass whispers on to anything inside the immediate listeners.
for(var/mob/L in listening)
for(var/mob/C in L.contents)
if(istype(C,/mob/living))
listening += C
+
+ //pass on the message to objects that can hear us.
+ for (var/obj/O in view(message_range, src))
+ spawn (0)
+ if (O)
+ O.hear_talk(src, message) //O.hear_talk(src, message, verb, speaking)
- var/list/eavesdropping = hearers(2, src)
+ var/list/eavesdropping = hearers(eavesdropping_range, src)
eavesdropping -= src
eavesdropping -= listening
- var/list/watching = hearers(5, src)
+ var/list/watching = hearers(watching_range, src)
watching -= src
watching -= listening
watching -= eavesdropping
- var/list/heard_a = list() // understood us
- var/list/heard_b = list() // didn't understand us
-
- for (var/mob/M in listening)
- if (M.say_understands(src))
- heard_a += M
- else
- heard_b += M
-
- var/rendered = null
-
- for (var/mob/M in watching)
- if (M.say_understands(src))
- rendered = "[src.name] whispers something."
- else
- rendered = "[src.voice_name] whispers something."
- M.show_message(rendered, 2)
-
- if (length(heard_a))
- var/message_a = message
-
- if (italics)
- message_a = "[message_a]"
- //This appears copied from carbon/living say.dm so the istype check for mob is probably not needed. Appending for src is also not needed as the game will check that automatically.
- rendered = "[GetVoice()][alt_name] whispers, \"[message_a]\""
-
- for (var/mob/M in heard_a)
- M.show_message(rendered, 2)
-
- if (length(heard_b))
- var/message_b
-
- message_b = stars(message)
-
- if (italics)
- message_b = "[message_b]"
-
- rendered = "[src.voice_name] whispers, \"[message_b]\""
-
- for (var/mob/M in heard_b)
- M.show_message(rendered, 2)
-
- for (var/mob/M in eavesdropping)
- if (M.say_understands(src))
- var/message_c
- message_c = stars(message)
- rendered = "[GetVoice()][alt_name] whispers, \"[message_c]\""
- M.show_message(rendered, 2)
- else
- rendered = "[src.voice_name] whispers something."
- M.show_message(rendered, 2)
-
- if (italics)
- message = "[message]"
- rendered = "[GetVoice()][alt_name] whispers, \"[message]\""
-
- for (var/mob/M in dead_mob_list)
- if (!(M.client))
- continue
- if (M.stat > 1 && !(M in heard_a) && (M.client.prefs.toggles & CHAT_GHOSTEARS))
+ //now mobs
+ var/speech_bubble_test = say_test(message)
+ var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]")
+ spawn(30) del(speech_bubble)
+
+ for(var/mob/M in listening)
+ M << speech_bubble
+ M.hear_say(message, verb, speaking, alt_name, italics, src)
+
+ if (eavesdropping.len)
+ var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less.
+ for(var/mob/M in eavesdropping)
+ M << speech_bubble
+ M.hear_say(new_message, verb, speaking, alt_name, italics, src)
+
+ if (watching.len)
+ var/rendered = "[src.name] whispers something."
+ for (var/mob/M in watching)
M.show_message(rendered, 2)
diff --git a/code/modules/mob/living/carbon/monkey/emote.dm b/code/modules/mob/living/carbon/monkey/emote.dm
index 615fb631d0..d5328183f3 100644
--- a/code/modules/mob/living/carbon/monkey/emote.dm
+++ b/code/modules/mob/living/carbon/monkey/emote.dm
@@ -31,6 +31,11 @@
if ("custom")
return custom_emote(m_type, message)
+ if ("chirp")
+ if(istype(src,/mob/living/carbon/monkey/diona))
+ message = "The [src.name] chirps!"
+ playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0)
+ m_type = 2
if("sign")
if (!src.restrained())
message = text("The monkey signs[].", (text2num(param) ? text(" the number []", text2num(param)) : null))
@@ -114,7 +119,11 @@
message = "The [src.name] lets out a faint chimper as it collapses and stops moving..."
m_type = 1
if("help")
- src << "choke, collapse, dance, deathgasp, drool, gasp, shiver, gnarl, jump, paw, moan, nod, roar, roll, scratch,\nscretch, shake, sign-#, sit, sulk, sway, tail, twitch, whimper"
+ var/text = "choke, "
+ if(istype(src,/mob/living/carbon/monkey/diona))
+ text += "chirp, "
+ text += "collapse, dance, deathgasp, drool, gasp, shiver, gnarl, jump, paw, moan, nod, roar, roll, scratch,\nscretch, shake, sign-#, sit, sulk, sway, tail, twitch, whimper"
+ src << text
else
src << text("Invalid Emote: []", act)
if ((message && src.stat == 0))
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index 5ea5eb25a2..6afcc88764 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -22,7 +22,7 @@
if(loc)
environment = loc.return_air()
- if (stat != DEAD)
+ if (stat != DEAD)
if(!istype(src,/mob/living/carbon/monkey/diona)) //still breathing
//First, resolve location and get a breath
if(air_master.current_cycle%4==2)
@@ -79,7 +79,7 @@
if(prob(1))
emote(pick("scratch","jump","roll","tail"))
updatehealth()
-
+
/mob/living/carbon/monkey/calculate_affecting_pressure(var/pressure)
..()
@@ -174,9 +174,9 @@
for (var/ID in virus2)
var/datum/disease2/disease/V = virus2[ID]
V.cure(src)
-
+
for(var/obj/effect/decal/cleanable/O in view(1,src))
- if(istype(O,/obj/effect/decal/cleanable/blood))
+ if(istype(O,/obj/effect/decal/cleanable/blood))
var/obj/effect/decal/cleanable/blood/B = O
if(B.virus2.len)
for (var/ID in B.virus2)
@@ -397,9 +397,18 @@
if(!environment)
return
- if(abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.phoron < MOLES_PHORON_VISIBLE)
- return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp
-
+ //Moved these vars here for use in the fuck-it-skip-processing check.
+ var/pressure = environment.return_pressure()
+ var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob.
+
+ if(adjusted_pressure < WARNING_HIGH_PRESSURE && adjusted_pressure > WARNING_LOW_PRESSURE && abs(environment.temperature - 293.15) < 20 && abs(bodytemperature - 310.14) < 0.5 && environment.phoron < MOLES_PHORON_VISIBLE)
+
+ //Hopefully should fix the walk-inside-still-pressure-warning issue.
+ if(pressure_alert)
+ pressure_alert = 0
+
+ return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp
+
var/environment_heat_capacity = environment.heat_capacity()
if(istype(get_turf(src), /turf/space))
var/turf/heat_turf = get_turf(src)
@@ -414,9 +423,6 @@
bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000)
//Account for massive pressure differences
-
- var/pressure = environment.return_pressure()
- var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob.
switch(adjusted_pressure)
if(HAZARD_HIGH_PRESSURE to INFINITY)
adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) )
@@ -468,7 +474,7 @@
adjustToxLoss(-1)
adjustOxyLoss(-1)
- if(reagents && reagents.reagent_list.len)
+ if(reagents && reagents.reagent_list.len)
reagents.metabolize(src,alien)
if (drowsyness)
diff --git a/code/modules/mob/living/carbon/species.dm b/code/modules/mob/living/carbon/species.dm
index 2c65f88090..743e57d4e9 100644
--- a/code/modules/mob/living/carbon/species.dm
+++ b/code/modules/mob/living/carbon/species.dm
@@ -17,6 +17,8 @@
var/mutantrace // Safeguard due to old code.
var/breath_type = "oxygen" // Non-oxygen gas breathed, if any.
+ var/poison_type = "phoron" // Poisonous air.
+ var/exhale_type = "C02" // Exhaled gas type.
var/cold_level_1 = 260 // Cold damage level 1 below this point.
var/cold_level_2 = 200 // Cold damage level 2 below this point.
@@ -42,6 +44,30 @@
var/blood_color = "#A10808" //Red.
var/flesh_color = "#FFC896" //Pink.
+ //Used in icon caching.
+ var/race_key = 0
+ var/icon/icon_template
+
+ /* Species-specific sprites, concept stolen from Paradise//vg/.
+ ex:
+ sprite_sheets = list(
+ "held" = 'icons/mob/path',
+ "uniform" = 'icons/mob/path',
+ "suit" = 'icons/mob/path',
+ "belt" = 'icons/mob/path'
+ "head" = 'icons/mob/path',
+ "back" = 'icons/mob/path',
+ "mask" = 'icons/mob/path',
+ "ears" = 'icons/mob/path',
+ "eyes" = 'icons/mob/path',
+ "feet" = 'icons/mob/path',
+ "gloves" = 'icons/mob/path'
+ )
+ If index term exists and icon_override is not set, this sprite sheet will be used.
+ */
+
+ var/list/sprite_sheets = list()
+
/datum/species/proc/create_organs(var/mob/living/carbon/human/H) //Handles creation of mob organs.
//This is a basic humanoid limb setup.
H.organs = list()
@@ -78,14 +104,12 @@
for(var/datum/organ/internal/I in H.internal_organs)
I.mechanize()
- return
-
/datum/species/proc/handle_post_spawn(var/mob/living/carbon/human/H) //Handles anything not already covered by basic species assignment.
return
/datum/species/proc/handle_death(var/mob/living/carbon/human/H) //Handles any species-specific death events (such as dionaea nymph spawns).
if(flags & IS_SYNTHETIC)
- //H.make_jittery(200) //S-s-s-s-sytem f-f-ai-i-i-i-i-lure-ure-ure-ure
+ //H.make_jittery(200) //S-s-s-s-sytem f-f-ai-i-i-i-i-lure-ure-ure-ure
H.h_style = ""
spawn(100)
//H.is_jittery = 0
@@ -130,7 +154,7 @@
name = "Tajaran"
icobase = 'icons/mob/human_races/r_tajaran.dmi'
deform = 'icons/mob/human_races/r_def_tajaran.dmi'
- language = "Siik'tajr"
+ language = "Siik'maas"
tail = "tajtail"
attack_verb = "scratch"
punch_damage = 5
@@ -175,14 +199,79 @@
cold_level_3 = 0
eyes = "vox_eyes_s"
+
breath_type = "nitrogen"
+ poison_type = "oxygen"
flags = NO_SCAN | NO_BLOOD
blood_color = "#2299FC"
flesh_color = "#808D11"
-/datum/species/vox/handle_post_spawn(var/mob/living/carbon/human/H)
+ sprite_sheets = list(
+ "suit" = 'icons/mob/species/vox/suit.dmi',
+ "head" = 'icons/mob/species/vox/head.dmi',
+ "mask" = 'icons/mob/species/vox/mask.dmi',
+ "feet" = 'icons/mob/species/vox/feet.dmi',
+ "gloves" = 'icons/mob/species/vox/gloves.dmi'
+ )
+
+/datum/species/vox/handle_post_spawn()
+
+ verbs += /mob/living/carbon/human/proc/leap
+ ..()
+
+/datum/species/vox/armalis/handle_post_spawn()
+
+ verbs += /mob/living/carbon/human/proc/gut
+ ..()
+
+/datum/species/vox/armalis
+ name = "Vox Armalis"
+ icobase = 'icons/mob/human_races/r_armalis.dmi'
+ deform = 'icons/mob/human_races/r_armalis.dmi'
+ language = "Vox-pidgin"
+ attack_verb = "slash"
+
+ warning_low_pressure = 50
+ hazard_low_pressure = 0
+
+ cold_level_1 = 80
+ cold_level_2 = 50
+ cold_level_3 = 0
+
+ heat_level_1 = 2000
+ heat_level_2 = 3000
+ heat_level_3 = 4000
+
+ brute_mod = 0.2
+ burn_mod = 0.2
+
+ eyes = "blank_eyes"
+ breath_type = "nitrogen"
+ poison_type = "oxygen"
+
+ flags = NO_SCAN | NO_BLOOD | HAS_TAIL | NO_PAIN | IS_WHITELISTED
+
+ blood_color = "#2299FC"
+ flesh_color = "#808D11"
+
+ tail = "armalis_tail"
+ icon_template = 'icons/mob/human_races/r_armalis.dmi'
+
+ sprite_sheets = list(
+ "suit" = 'icons/mob/species/armalis/suit.dmi',
+ "gloves" = 'icons/mob/species/armalis/gloves.dmi',
+ "feet" = 'icons/mob/species/armalis/feet.dmi',
+ "head" = 'icons/mob/species/armalis/head.dmi',
+ "held" = 'icons/mob/species/armalis/held.dmi'
+ )
+
+/datum/species/vox/create_organs(var/mob/living/carbon/human/H)
+
+ ..() //create organs first.
+
+ //Now apply cortical stack.
var/datum/organ/external/affected = H.get_organ("head")
//To avoid duplicates.
@@ -201,8 +290,6 @@
M.cortical_stacks += I
M.raiders[H.mind] = I
- return ..()
-
/datum/species/diona
name = "Diona"
icobase = 'icons/mob/human_races/r_diona.dmi'
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 47bbe015ee..46e505affc 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -592,6 +592,7 @@
sleep(10)
SC.broken = 1
SC.locked = 0
+ SC.update_icon()
usr << "\red You successfully break out!"
for(var/mob/O in viewers(L.loc))
O.show_message("\red \the [usr] successfully broke out of \the [SC]!", 1)
@@ -601,6 +602,7 @@
SC.open()
else
C.welded = 0
+ C.update_icon()
usr << "\red You successfully break out!"
for(var/mob/O in viewers(L.loc))
O.show_message("\red \the [usr] successfully broke out of \the [C]!", 1)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 89d6302597..46da4b8469 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -113,7 +113,7 @@ var/list/department_radio_keys = list(
for(var/obj/O in objects)
spawn(0)
- O.hear_talk(src, message, verb, speaking)
+ O.hear_talk(src, message, verb, speaking)
var/speech_bubble_test = say_test(message)
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]")
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index dfda7bae5c..c9c4278f5a 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -24,7 +24,6 @@ var/list/ai_list = list()
var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
//var/list/laws = list()
- var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
var/viewalerts = 0
var/lawcheck[1]
var/ioncheck[1]
@@ -95,7 +94,16 @@ var/list/ai_list = list()
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/toggle_camera_light)
-
+ //Languages
+ add_language("Sol Common", 0)
+ add_language("Sinta'unathi", 0)
+ add_language("Siik'maas", 0)
+ add_language("Siik'tajr", 0)
+ add_language("Skrellian", 0)
+ add_language("Rootspeak", 0)
+ add_language("Tradeband", 1)
+ add_language("Gutter", 0)
+
if(!safety)//Only used by AIize() to successfully spawn an AI.
if (!B)//If there is no player/brain inside.
new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
@@ -237,26 +245,20 @@ var/list/ai_list = list()
dat += "Close
"
for (var/cat in alarms)
dat += text("[] \n", cat)
- var/list/L = alarms[cat]
- if (L.len)
- for (var/alarm in L)
- var/list/alm = L[alarm]
- var/area/A = alm[1]
- var/C = alm[2]
- var/list/sources = alm[3]
+ var/list/alarmlist = alarms[cat]
+ if (alarmlist.len)
+ for (var/area_name in alarmlist)
+ var/datum/alarm/alarm = alarmlist[area_name]
dat += ""
- if (C && istype(C, /list))
- var/dat2 = ""
- for (var/obj/machinery/camera/I in C)
- dat2 += text("[][]", (dat2=="") ? "" : " | ", src, I, I.c_tag)
- dat += text("-- [] ([])", A.name, (dat2!="") ? dat2 : "No Camera")
- else if (C && istype(C, /obj/machinery/camera))
- var/obj/machinery/camera/Ctmp = C
- dat += text("-- [] ([])", A.name, src, C, Ctmp.c_tag)
- else
- dat += text("-- [] (No Camera)", A.name)
- if (sources.len > 1)
- dat += text("- [] sources", sources.len)
+
+ var/cameratext = ""
+ if (alarm.cameras)
+ for (var/obj/machinery/camera/I in alarm.cameras)
+ cameratext += text("[][]", (cameratext=="") ? "" : " | ", src, I, I.c_tag)
+ dat += text("-- [] ([])", alarm.area.name, (cameratext)? cameratext : "No Camera")
+
+ if (alarm.sources.len > 1)
+ dat += text(" - [] sources", alarm.sources.len)
dat += " \n"
else
dat += "-- All Systems Nominal \n"
@@ -365,7 +367,7 @@ var/list/ai_list = list()
unset_machine()
src << browse(null, t1)
if (href_list["switchcamera"])
- switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
+ switchCamera(locate(href_list["switchcamera"])) in cameranet.viewpoints
if (href_list["showalerts"])
ai_alerts()
//Carn: holopad requests
@@ -534,59 +536,28 @@ var/list/ai_list = list()
return 1
-/mob/living/silicon/ai/triggerAlarm(var/class, area/A, var/O, var/alarmsource)
+/mob/living/silicon/ai/triggerAlarm(var/class, area/A, list/cameralist, var/source)
if (stat == 2)
return 1
- var/list/L = alarms[class]
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/sources = alarm[3]
- if (!(alarmsource in sources))
- sources += alarmsource
- return 1
- var/obj/machinery/camera/C = null
- var/list/CL = null
- if (O && istype(O, /list))
- CL = O
- if (CL.len == 1)
- C = CL[1]
- else if (O && istype(O, /obj/machinery/camera))
- C = O
- L[A.name] = list(A, (C) ? C : O, list(alarmsource))
- if (O)
- if (C && C.can_use())
- queueAlarm("--- [class] alarm detected in [A.name]! ([C.c_tag])", class)
- else if (CL && CL.len)
- var/foo = 0
- var/dat2 = ""
- for (var/obj/machinery/camera/I in CL)
- dat2 += text("[][]", (!foo) ? "" : " | ", src, I, I.c_tag) //I'm not fixing this shit...
- foo = 1
- queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
- else
- queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
- else
- queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
+
+ ..()
+
+ var/cameratext = ""
+ for (var/obj/machinery/camera/C in cameralist)
+ cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]"
+
+ queueAlarm("--- [class] alarm detected in [A.name]! ([(cameratext)? cameratext : "No Camera"])", class)
+
if (viewalerts) ai_alerts()
- return 1
-/mob/living/silicon/ai/cancelAlarm(var/class, area/A as area, obj/origin)
- var/list/L = alarms[class]
- var/cleared = 0
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/srcs = alarm[3]
- if (origin in srcs)
- srcs -= origin
- if (srcs.len == 0)
- cleared = 1
- L -= I
- if (cleared)
+/mob/living/silicon/ai/cancelAlarm(var/class, area/A as area, var/source)
+ var/has_alarm = ..()
+
+ if (!has_alarm)
queueAlarm(text("--- [] alarm in [] has been cleared.", class, A.name), class, 0)
if (viewalerts) ai_alerts()
- return !cleared
+
+ return has_alarm
/mob/living/silicon/ai/cancel_camera()
set category = "AI Commands"
@@ -612,7 +583,7 @@ var/list/ai_list = list()
var/mob/living/silicon/ai/U = usr
- for (var/obj/machinery/camera/C in cameranet.cameras)
+ for (var/obj/machinery/camera/C in cameranet.viewpoints)
if(!C.can_use())
continue
@@ -630,7 +601,7 @@ var/list/ai_list = list()
if(isnull(network))
network = old_network // If nothing is selected
else
- for(var/obj/machinery/camera/C in cameranet.cameras)
+ for(var/obj/machinery/camera/C in cameranet.viewpoints)
if(!C.can_use())
continue
if(network in C.network)
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
index 7d52fbd7a2..e34dee5b24 100644
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
@@ -1,148 +1,25 @@
-// CAMERA NET
-//
-// The datum containing all the chunks.
+/datum/visibility_network/cameras
+ ChunkType = /datum/visibility_chunk/camera
-var/datum/cameranet/cameranet = new()
-
-/datum/cameranet
- // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Del().
- var/list/cameras = list()
- // The chunks of the map, mapping the areas that the cameras can see.
- var/list/chunks = list()
- var/ready = 0
-
-// Checks if a chunk has been Generated in x, y, z.
-/datum/cameranet/proc/chunkGenerated(x, y, z)
- x &= ~0xf
- y &= ~0xf
- var/key = "[x],[y],[z]"
- return (chunks[key])
-
-// Returns the chunk in the x, y, z.
-// If there is no chunk, it creates a new chunk and returns that.
-/datum/cameranet/proc/getCameraChunk(x, y, z)
- x &= ~0xf
- y &= ~0xf
- var/key = "[x],[y],[z]"
- if(!chunks[key])
- chunks[key] = new /datum/camerachunk(null, x, y, z)
-
- return chunks[key]
-
-// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
-
-/datum/cameranet/proc/visibility(mob/aiEye/ai)
- // 0xf = 15
- var/x1 = max(0, ai.x - 16) & ~0xf
- var/y1 = max(0, ai.y - 16) & ~0xf
- var/x2 = min(world.maxx, ai.x + 16) & ~0xf
- var/y2 = min(world.maxy, ai.y + 16) & ~0xf
-
- var/list/visibleChunks = list()
-
- for(var/x = x1; x <= x2; x += 16)
- for(var/y = y1; y <= y2; y += 16)
- visibleChunks += getCameraChunk(x, y, ai.z)
-
- var/list/remove = ai.visibleCameraChunks - visibleChunks
- var/list/add = visibleChunks - ai.visibleCameraChunks
-
- for(var/chunk in remove)
- var/datum/camerachunk/c = chunk
- c.remove(ai)
-
- for(var/chunk in add)
- var/datum/camerachunk/c = chunk
- c.add(ai)
-
-// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
-
-/datum/cameranet/proc/updateVisibility(atom/A, var/opacity_check = 1)
-
- if(!ticker || (opacity_check && !A.opacity))
- return
- majorChunkChange(A, 2)
-
-/datum/cameranet/proc/updateChunk(x, y, z)
- // 0xf = 15
- if(!chunkGenerated(x, y, z))
- return
- var/datum/camerachunk/chunk = getCameraChunk(x, y, z)
- chunk.hasChanged()
-
-// Removes a camera from a chunk.
-
-/datum/cameranet/proc/removeCamera(obj/machinery/camera/c)
- if(c.can_use())
- majorChunkChange(c, 0)
-
-// Add a camera to a chunk.
-
-/datum/cameranet/proc/addCamera(obj/machinery/camera/c)
- if(c.can_use())
- majorChunkChange(c, 1)
-
-// Used for Cyborg cameras. Since portable cameras can be in ANY chunk.
-
-/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/c)
- if(c.can_use())
- majorChunkChange(c, 1)
- //else
- // majorChunkChange(c, 0)
-
-// Never access this proc directly!!!!
-// This will update the chunk and all the surrounding chunks.
-// It will also add the atom to the cameras list if you set the choice to 1.
-// Setting the choice to 0 will remove the camera from the chunks.
-// If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
-
-/datum/cameranet/proc/majorChunkChange(atom/c, var/choice)
- // 0xf = 15
- if(!c)
- return
-
- var/turf/T = get_turf(c)
- if(T)
- var/x1 = max(0, T.x - 8) & ~0xf
- var/y1 = max(0, T.y - 8) & ~0xf
- var/x2 = min(world.maxx, T.x + 8) & ~0xf
- var/y2 = min(world.maxy, T.y + 8) & ~0xf
-
- //world << "X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]"
-
- for(var/x = x1; x <= x2; x += 16)
- for(var/y = y1; y <= y2; y += 16)
- if(chunkGenerated(x, y, T.z))
- var/datum/camerachunk/chunk = getCameraChunk(x, y, T.z)
- if(choice == 0)
- // Remove the camera.
- chunk.cameras -= c
- else if(choice == 1)
- // You can't have the same camera in the list twice.
- chunk.cameras |= c
- chunk.hasChanged()
-
-// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0.
-
-/datum/cameranet/proc/checkCameraVis(mob/living/target as mob)
-
- // 0xf = 15
- var/turf/position = get_turf(target)
- var/datum/camerachunk/chunk = getCameraChunk(position.x, position.y, position.z)
- if(chunk)
- if(chunk.changed)
- chunk.hasChanged(1) // Update now, no matter if it's visible or not.
- if(chunk.visibleTurfs[position])
- return 1
- return 0
-
-
-// Debug verb for VVing the chunk that the turf is in.
-/*
-/turf/verb/view_chunk()
- set src in world
-
- if(cameranet.chunkGenerated(x, y, z))
- var/datum/camerachunk/chunk = cameranet.getCameraChunk(x, y, z)
- usr.client.debug_variables(chunk)
-*/
\ No newline at end of file
+/datum/visibility_network/cameras/getViewpointFromMob(var/mob/currentMob)
+ var/mob/living/silicon/robot/currentRobot=currentMob
+ if(currentRobot)
+ return currentRobot.camera
+ return FALSE
+
+/datum/visibility_network/cameras/validViewpoint(var/viewpoint)
+ var/obj/machinery/camera/c = viewpoint
+ if (!c)
+ return FALSE
+ return c.can_use()
+
+
+// adding some indirection so that I don't have to edit a ton of files
+/datum/visibility_network/cameras/proc/addCamera(var/camera)
+ return addViewpoint(camera)
+
+/datum/visibility_network/cameras/proc/removeCamera(var/camera)
+ return removeViewpoint(camera)
+
+/datum/visibility_network/cameras/proc/checkCameraVis(var/atom/target)
+ return checkCanSee(target)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
index 33dbfd0966..89c88c1aaf 100644
--- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
@@ -1,168 +1,23 @@
-#define UPDATE_BUFFER 25 // 2.5 seconds
-
-// CAMERA CHUNK
-//
-// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
-// Allows the AI Eye to stream these chunks and know what it can and cannot see.
-
-/datum/camerachunk
- var/list/obscuredTurfs = list()
- var/list/visibleTurfs = list()
- var/list/obscured = list()
- var/list/cameras = list()
- var/list/turfs = list()
- var/list/seenby = list()
- var/visible = 0
- var/changed = 0
- var/updating = 0
- var/x = 0
- var/y = 0
- var/z = 0
-
-// Add an AI eye to the chunk, then update if changed.
-
-/datum/camerachunk/proc/add(mob/aiEye/ai)
- if(!ai.ai)
- return
- ai.visibleCameraChunks += src
- if(ai.ai.client)
- ai.ai.client.images += obscured
- visible++
- seenby += ai
- if(changed && !updating)
- update()
-
-// Remove an AI eye from the chunk, then update if changed.
-
-/datum/camerachunk/proc/remove(mob/aiEye/ai)
- if(!ai.ai)
- return
- ai.visibleCameraChunks -= src
- if(ai.ai.client)
- ai.ai.client.images -= obscured
- seenby -= ai
- if(visible > 0)
- visible--
-
-// Called when a chunk has changed. I.E: A wall was deleted.
-
-/datum/camerachunk/proc/visibilityChanged(turf/loc)
- if(!visibleTurfs[loc])
- return
- hasChanged()
-
-// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
-// instead be flagged to update the next time an AI Eye moves near it.
-
-/datum/camerachunk/proc/hasChanged(var/update_now = 0)
- if(visible || update_now)
- if(!updating)
- updating = 1
- spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once
- update()
- updating = 0
- else
- changed = 1
-
-// The actual updating. It gathers the visible turfs from cameras and puts them into the appropiate lists.
-
-/datum/camerachunk/proc/update()
-
- set background = 1
-
- var/list/newVisibleTurfs = list()
-
- for(var/camera in cameras)
- var/obj/machinery/camera/c = camera
-
- if(!c)
- continue
-
- if(!c.can_use())
- continue
-
- var/turf/point = locate(src.x + 8, src.y + 8, src.z)
- if(get_dist(point, c) > 24)
- continue
-
- for(var/turf/t in c.can_see())
- newVisibleTurfs[t] = t
-
- // Removes turf that isn't in turfs.
- newVisibleTurfs &= turfs
-
- var/list/visAdded = newVisibleTurfs - visibleTurfs
- var/list/visRemoved = visibleTurfs - newVisibleTurfs
-
- visibleTurfs = newVisibleTurfs
- obscuredTurfs = turfs - newVisibleTurfs
-
- for(var/turf in visAdded)
- var/turf/t = turf
- if(t.obscured)
- obscured -= t.obscured
- for(var/eye in seenby)
- var/mob/aiEye/m = eye
- if(!m || !m.ai)
- continue
- if(m.ai.client)
- m.ai.client.images -= t.obscured
-
- for(var/turf in visRemoved)
- var/turf/t = turf
- if(obscuredTurfs[t])
- if(!t.obscured)
- t.obscured = image('icons/effects/cameravis.dmi', t, "black", 15)
-
- obscured += t.obscured
- for(var/eye in seenby)
- var/mob/aiEye/m = eye
- if(!m || !m.ai)
- seenby -= m
- continue
- if(m.ai.client)
- m.ai.client.images += t.obscured
-
-// Create a new camera chunk, since the chunks are made as they are needed.
-
-/datum/camerachunk/New(loc, x, y, z)
-
- // 0xf = 15
- x &= ~0xf
- y &= ~0xf
-
- src.x = x
- src.y = y
- src.z = z
+/datum/visibility_chunk/camera
+/datum/visibility_chunk/camera/validViewpoint(var/viewpoint)
+ var/obj/machinery/camera/c = viewpoint
+ if(!c)
+ return FALSE
+ if(!c.can_use())
+ return FALSE
+ var/turf/point = locate(src.x + 8, src.y + 8, src.z)
+ if(get_dist(point, c) > 24)
+ return FALSE
+ return TRUE
+
+
+/datum/visibility_chunk/camera/getVisibleTurfsForViewpoint(var/viewpoint)
+ var/obj/machinery/camera/c = viewpoint
+ return c.can_see()
+
+
+/datum/visibility_chunk/camera/findNearbyViewpoints()
for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z)))
if(c.can_use())
- cameras += c
-
- for(var/turf/t in range(10, locate(x + 8, y + 8, z)))
- if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16)
- turfs[t] = t
-
- for(var/camera in cameras)
- var/obj/machinery/camera/c = camera
- if(!c)
- continue
-
- if(!c.can_use())
- continue
-
- for(var/turf/t in c.can_see())
- visibleTurfs[t] = t
-
- // Removes turf that isn't in turfs.
- visibleTurfs &= turfs
-
- obscuredTurfs = turfs - visibleTurfs
-
- for(var/turf in obscuredTurfs)
- var/turf/t = turf
- if(!t.obscured)
- t.obscured = image('icons/effects/cameravis.dmi', t, "black", 15)
- obscured += t.obscured
-
-#undef UPDATE_BUFFER
\ No newline at end of file
+ viewpoints += c
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index c82fbb7c76..351b9b60fa 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -6,13 +6,16 @@
/mob/aiEye
name = "Inactive AI Eye"
icon = 'icons/obj/status_display.dmi' // For AI friend secret shh :o
- var/list/visibleCameraChunks = list()
var/mob/living/silicon/ai/ai = null
density = 0
status_flags = GODMODE // You can't damage it.
mouse_opacity = 0
see_in_dark = 7
+/mob/aiEye/New()
+ ..()
+ visibility_interface = new /datum/visibility_interface/ai_eye(src)
+
// Movement code. Returns 0 to stop air movement from moving it.
/mob/aiEye/Move()
return 0
@@ -37,7 +40,6 @@
// It will also stream the chunk that the new loc is in.
/mob/aiEye/proc/setLoc(var/T)
-
if(ai)
if(!isturf(ai.loc))
return
@@ -136,7 +138,8 @@
if(client && client.eye)
client.eye = src
- for(var/datum/camerachunk/c in eyeobj.visibleCameraChunks)
+
+ for(var/datum/visibility_chunk/camera/c in eyeobj.visibility_interface.visible_chunks)
c.remove(eyeobj)
/mob/living/silicon/ai/verb/toggle_acceleration()
diff --git a/code/modules/mob/living/silicon/ai/freelook/read_me.dm b/code/modules/mob/living/silicon/ai/freelook/read_me.dm
index 53e68ff137..aa3d99bdba 100644
--- a/code/modules/mob/living/silicon/ai/freelook/read_me.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/read_me.dm
@@ -43,7 +43,7 @@
WHERE IS EVERYTHING?
- cameranet.dm = Everything about the cameranet datum.
+ cameraNetwork.dm = Everything about the cameraNetwork datum.
chunk.dm = Everything about the chunk datum.
eye.dm = Everything about the AI and the AIEye.
updating.dm = Everything about triggers that will update chunks.
diff --git a/code/modules/mob/living/silicon/ai/freelook/update_triggers.dm b/code/modules/mob/living/silicon/ai/freelook/update_triggers.dm
index c1f6331372..5a1f9c6e9b 100644
--- a/code/modules/mob/living/silicon/ai/freelook/update_triggers.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/update_triggers.dm
@@ -1,80 +1,5 @@
#define BORG_CAMERA_BUFFER 30
-//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update.
-
-// TURFS
-
-/turf
- var/image/obscured
-
-/turf/proc/visibilityChanged()
- if(ticker)
- cameranet.updateVisibility(src)
-
-/turf/simulated/Del()
- visibilityChanged()
- ..()
-
-/turf/simulated/New()
- ..()
- visibilityChanged()
-
-
-
-// STRUCTURES
-
-/obj/structure/Del()
- if(ticker)
- cameranet.updateVisibility(src)
- ..()
-
-/obj/structure/New()
- ..()
- if(ticker)
- cameranet.updateVisibility(src)
-
-// EFFECTS
-
-/obj/effect/Del()
- if(ticker)
- cameranet.updateVisibility(src)
- ..()
-
-/obj/effect/New()
- ..()
- if(ticker)
- cameranet.updateVisibility(src)
-
-
-// DOORS
-
-// Simply updates the visibility of the area when it opens/closes/destroyed.
-/obj/machinery/door/update_nearby_tiles(need_rebuild)
- . = ..(need_rebuild)
- // Glass door glass = 1
- // don't check then?
- if(!glass && cameranet)
- cameranet.updateVisibility(src, 0)
-
-
-// ROBOT MOVEMENT
-
-// Update the portable camera everytime the Robot moves.
-// This might be laggy, comment it out if there are problems.
-/mob/living/silicon/robot/var/updating = 0
-
-/mob/living/silicon/robot/Move()
- var/oldLoc = src.loc
- . = ..()
- if(.)
- if(src.camera && src.camera.network.len)
- if(!updating)
- updating = 1
- spawn(BORG_CAMERA_BUFFER)
- if(oldLoc != src.loc)
- cameranet.updatePortableCamera(src.camera)
- updating = 0
-
// CAMERA
// An addition to deactivate which removes/adds the camera from the chunk list based on if it works or not.
@@ -82,23 +7,23 @@
/obj/machinery/camera/deactivate(user as mob, var/choice = 1)
..(user, choice)
if(src.can_use())
- cameranet.addCamera(src)
+ cameranet.addViewpoint(src)
else
src.SetLuminosity(0)
- cameranet.removeCamera(src)
+ cameranet.removeViewpoint(src)
/obj/machinery/camera/New()
..()
- cameranet.cameras += src //Camera must be added to global list of all cameras no matter what...
+ cameranet.viewpoints += src //Camera must be added to global list of all cameras no matter what...
var/list/open_networks = difflist(network,RESTRICTED_CAMERA_NETWORKS) //...but if all of camera's networks are restricted, it only works for specific camera consoles.
if(open_networks.len) //If there is at least one open network, chunk is available for AI usage.
- cameranet.addCamera(src)
+ cameranet.addViewpoint(src)
/obj/machinery/camera/Del()
- cameranet.cameras -= src
+ cameranet.viewpoints -= src
var/list/open_networks = difflist(network,RESTRICTED_CAMERA_NETWORKS)
if(open_networks.len)
- cameranet.removeCamera(src)
+ cameranet.removeViewpoint(src)
..()
#undef BORG_CAMERA_BUFFER
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/ai/freelook/visibility_interface.dm b/code/modules/mob/living/silicon/ai/freelook/visibility_interface.dm
new file mode 100644
index 0000000000..b55598c609
--- /dev/null
+++ b/code/modules/mob/living/silicon/ai/freelook/visibility_interface.dm
@@ -0,0 +1,10 @@
+/datum/visibility_interface/ai_eye
+ chunk_type = /datum/visibility_chunk/camera
+
+/datum/visibility_interface/ai_eye/getClient()
+ var/mob/aiEye/eye = controller
+ if (!eye)
+ return FALSE
+ if (!eye.ai)
+ return FALSE
+ return eye.ai.client
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index 7705e5aedc..65118d811c 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -5,17 +5,6 @@
//If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
..(message)
-/mob/living/silicon/say_understands(var/other)
- if (istype(other, /mob/living/carbon/human))
- return 1
- if (istype(other, /mob/living/silicon))
- return 1
- if (istype(other, /mob/living/carbon/brain))
- return 1
- return ..()
-
-
-
// These Verbs are commented out since we've disabled the AI vocal (VOX) announcements.
// If you re-enable them there is 3 lines in ai.dm Topic() that you need to uncomment as well.
// just search for VOX in there.
diff --git a/code/modules/mob/living/silicon/alarm.dm b/code/modules/mob/living/silicon/alarm.dm
new file mode 100644
index 0000000000..ee505fae5c
--- /dev/null
+++ b/code/modules/mob/living/silicon/alarm.dm
@@ -0,0 +1,111 @@
+/datum/alarm
+ var/area/area //the area associated with the alarm. Used to identify the alarm
+ var/list/sources //list of things triggering the alarm. Used to determine when the alarm should be cleared.
+ var/list/cameras //list of cameras that can be switched to, if the player has that capability.
+
+/datum/alarm/New(area/A, list/sourcelist=list(), list/cameralist=list())
+ area = A
+ sources = sourcelist
+ cameras = cameralist
+
+/mob/living/silicon
+ var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) //each sublist stores alarms keyed by the area name
+ var/list/alarms_to_show = list()
+ var/list/alarms_to_clear = list()
+ var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
+ var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
+
+/mob/living/silicon/proc/triggerAlarm(var/class, area/A, list/cameralist, var/source)
+ var/list/alarmlist = alarms[class]
+
+ //see if there is already an alarm of this class for this area
+ if (A.name in alarmlist)
+ var/datum/alarm/existing = alarmlist[A.name]
+ existing.sources += source
+ existing.cameras |= cameralist
+ else
+ alarmlist[A.name] = new /datum/alarm(A, list(source), cameralist)
+
+/mob/living/silicon/proc/cancelAlarm(var/class, area/A as area, var/source)
+ var/cleared = 0
+ var/list/alarmlist = alarms[class]
+
+ if (A.name in alarmlist)
+ var/datum/alarm/alarm = alarmlist[A.name]
+ alarm.sources -= source
+
+ if (!(alarm.sources.len))
+ cleared = 1
+ alarmlist -= A.name
+
+ return !cleared
+
+/mob/living/silicon/proc/queueAlarm(var/message, var/type, var/incoming = 1)
+ var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
+ if(incoming)
+ alarms_to_show += message
+ alarm_types_show[type] += 1
+ else
+ alarms_to_clear += message
+ alarm_types_clear[type] += 1
+
+ if(!in_cooldown)
+ spawn(10 * 10) // 10 seconds
+
+ if(alarms_to_show.len < 5)
+ for(var/msg in alarms_to_show)
+ src << msg
+ else if(alarms_to_show.len)
+
+ var/msg = "--- "
+
+ if(alarm_types_show["Motion"])
+ msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
+
+ if(alarm_types_show["Fire"])
+ msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
+
+ if(alarm_types_show["Atmosphere"])
+ msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
+
+ if(alarm_types_show["Power"])
+ msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
+
+ if(alarm_types_show["Camera"])
+ msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
+
+ msg += "\[Show Alerts\]"
+ src << msg
+
+ if(alarms_to_clear.len < 3)
+ for(var/msg in alarms_to_clear)
+ src << msg
+
+ else if(alarms_to_clear.len)
+ var/msg = "--- "
+
+ if(alarm_types_clear["Motion"])
+ msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
+
+ if(alarm_types_clear["Fire"])
+ msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
+
+ if(alarm_types_clear["Atmosphere"])
+ msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
+
+ if(alarm_types_clear["Power"])
+ msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
+
+ if(alarm_types_show["Camera"])
+ msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
+
+ msg += "\[Show Alerts\]"
+ src << msg
+
+
+ alarms_to_show = list()
+ alarms_to_clear = list()
+ for(var/i = 1; i < alarm_types_show.len; i++)
+ alarm_types_show[i] = 0
+ for(var/i = 1; i < alarm_types_clear.len; i++)
+ alarm_types_clear[i] = 0
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 2e366adbf8..8f65ee384e 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -63,6 +63,11 @@
card.radio = new /obj/item/device/radio(src.card)
radio = card.radio
+ //Default languages without universal translator software
+ add_language("Sol Common", 1)
+ add_language("Tradeband", 1)
+ add_language("Gutter", 1)
+
//PDA
pda = new(src)
spawn(5)
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index dce96a8685..757b04b1ea 100644
--- a/code/modules/mob/living/silicon/pai/software.dm
+++ b/code/modules/mob/living/silicon/pai/software.dm
@@ -497,7 +497,7 @@
// Universal Translator
/mob/living/silicon/pai/proc/softwareTranslator()
var/dat = {"
Universal Translator
- When enabled, this device will automatically convert all spoken and written language into a format that any known recipient can understand.
+ When enabled, this device will automatically convert all spoken and written languages into a format that any known recipient can understand.
The device is currently [ (src.universal_speak) ? "en" : "dis" ]abled. Toggle Device
"}
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index f2cc8d0e17..4e9c806850 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -105,6 +105,19 @@
src << "\red Your binary communications component isn't functional."
return
robot_talk(trim(copytext(message,3)))
+ else
+
+ var/list/listeners = hearers(5,src)
+ listeners |= src
+
+ for(var/mob/living/silicon/robot/drone/D in listeners)
+ if(D.client) D << "[src] transmits, \"[message]\""
+
+ for (var/mob/M in player_list)
+ if (istype(M, /mob/new_player))
+ continue
+ else if(M.stat == 2 && M.client.prefs.toggles & CHAT_GHOSTEARS)
+ if(M.client) M << "[src] transmits, \"[message]\""
//Sick of trying to get this to display properly without redefining it.
/mob/living/silicon/robot/drone/show_system_integrity()
@@ -212,6 +225,8 @@
/mob/living/silicon/robot/drone/handle_regular_status_updates()
if(health <= -35 && src.stat != 2)
+ timeofdeath = world.time
+ death() //Possibly redundant, having trouble making death() cooperate.
gib()
return
..()
@@ -257,7 +272,7 @@
/mob/living/silicon/robot/drone/proc/request_player()
for(var/mob/dead/observer/O in player_list)
- if(jobban_isbanned(O, "Maintenance Drone"))
+ if(jobban_isbanned(O, "Cyborg"))
continue
if(O.client)
if(O.client.prefs.be_special & BE_PAI)
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm
index 1df70fc709..a01b7cb6e5 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm
@@ -37,7 +37,7 @@
//Item holder.
/obj/item/weapon/holder/drone
- name = "maintenance bot"
+ name = "maintenance drone"
desc = "It's a small maintenance robot."
icon = 'icons/obj/objects.dmi'
icon_state = "drone"
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm
index 121928bdfc..801dce4c2e 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/computer.dmi'
icon_state = "power"
req_access = list(access_engine_equip)
- circuit = "/obj/item/weapon/circuitboard/robotics"
+ circuit = "/obj/item/weapon/circuitboard/drone_control"
//Used when pinging drones.
var/drone_call_area = "Engineering"
@@ -119,4 +119,6 @@
return
dronefab.produce_drones = !dronefab.produce_drones
- usr << "\blue You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator."
\ No newline at end of file
+ usr << "\blue You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator."
+
+ src.updateUsrDialog()
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_damage.dm b/code/modules/mob/living/silicon/robot/drone/drone_damage.dm
new file mode 100644
index 0000000000..57ffc89887
--- /dev/null
+++ b/code/modules/mob/living/silicon/robot/drone/drone_damage.dm
@@ -0,0 +1,24 @@
+//Redefining some robot procs, since drones can't be repaired and really shouldn't take component damage.
+/mob/living/silicon/robot/drone/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = 0, var/used_weapon = null)
+ bruteloss += brute
+ fireloss += burn
+
+/mob/living/silicon/robot/drone/heal_overall_damage(var/brute, var/burn)
+
+ bruteloss -= brute
+ fireloss -= burn
+
+ if(bruteloss<0) bruteloss = 0
+ if(fireloss<0) fireloss = 0
+
+/mob/living/silicon/robot/drone/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = 0)
+ take_overall_damage(brute,burn)
+
+/mob/living/silicon/robot/drone/heal_organ_damage(var/brute, var/burn)
+ heal_overall_damage(brute,burn)
+
+/mob/living/silicon/robot/drone/getFireLoss()
+ return fireloss
+
+/mob/living/silicon/robot/drone/getBruteLoss()
+ return bruteloss
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index 924dac229d..a121991977 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -21,6 +21,8 @@
/obj/item/weapon/table_parts,
/obj/item/weapon/rack_parts,
/obj/item/weapon/camera_assembly,
+ /obj/item/weapon/tank,
+ /obj/item/weapon/circuitboard
)
//Item currently being held.
@@ -104,6 +106,23 @@
else
user << "\red Your gripper cannot hold \the [target]."
+ else if(istype(target,/obj/machinery/power/apc))
+ var/obj/machinery/power/apc/A = target
+ if(A.opened)
+ if(A.cell)
+
+ wrapped = A.cell
+
+ A.cell.add_fingerprint(user)
+ A.cell.updateicon()
+ A.cell.loc = src
+ A.cell = null
+
+ A.charging = 0
+ A.update_icon()
+
+ user.visible_message("\red [user] removes the power cell from [A]!", "You remove the power cell.")
+
//TODO: Matter decompiler.
/obj/item/weapon/matter_decompiler
@@ -130,22 +149,93 @@
//Used to give the right message.
var/grabbed_something = 0
- for(var/obj/item/W in T)
+ for(var/mob/M in T)
+ if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse))
+ src.loc.visible_message("\red [src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","\red It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.")
+ new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
+ del(M)
+ stored_comms["wood"]++
+ stored_comms["wood"]++
+ stored_comms["plastic"]++
+ stored_comms["plastic"]++
+ return
+
+ else if(istype(M,/mob/living/silicon/robot/drone) && M.stat == 2 && !M.client)
+
+ var/mob/living/silicon/robot/drone/D = src.loc
+
+ if(!istype(D))
+ return
+
+ D << "\red You begin decompiling the other drone."
+
+ if(!do_after(D,50))
+ D << "\red You need to remain still while decompiling such a large object."
+ return
+
+ if(!M || !D) return
+
+ D << "\red You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself."
+
+ del(M)
+ new/obj/effect/decal/cleanable/blood/oil(get_turf(src))
+
+ stored_comms["metal"] += 15
+ stored_comms["glass"] += 15
+ stored_comms["wood"] += 5
+ stored_comms["plastic"] += 5
+
+ return
+ else
+ continue
+
+ for(var/obj/W in T)
//Different classes of items give different commodities.
- if(istype(W,/obj/item/trash))
+ if (istype(W,/obj/item/weapon/cigbutt))
+ stored_comms["plastic"]++
+ else if(istype(W,/obj/effect/spider/spiderling))
+ stored_comms["wood"]++
+ stored_comms["wood"]++
+ stored_comms["plastic"]++
+ stored_comms["plastic"]++
+ else if(istype(W,/obj/item/weapon/light))
+ var/obj/item/weapon/light/L = W
+ if(L.status >= 2) //In before someone changes the inexplicably local defines. ~ Z
+ stored_comms["metal"]++
+ stored_comms["glass"]++
+ else
+ continue
+ else if(istype(W,/obj/effect/decal/remains/robot))
stored_comms["metal"]++
+ stored_comms["metal"]++
+ stored_comms["plastic"]++
+ stored_comms["plastic"]++
+ stored_comms["glass"]++
+ else if(istype(W,/obj/item/trash))
+ stored_comms["metal"]++
+ stored_comms["plastic"]++
+ stored_comms["plastic"]++
stored_comms["plastic"]++
else if(istype(W,/obj/effect/decal/cleanable/blood/gibs/robot))
stored_comms["metal"]++
+ stored_comms["metal"]++
+ stored_comms["glass"]++
stored_comms["glass"]++
else if(istype(W,/obj/item/ammo_casing))
stored_comms["metal"]++
else if(istype(W,/obj/item/weapon/shard/shrapnel))
stored_comms["metal"]++
+ stored_comms["metal"]++
+ stored_comms["metal"]++
else if(istype(W,/obj/item/weapon/shard))
stored_comms["glass"]++
+ stored_comms["glass"]++
+ stored_comms["glass"]++
else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/grown))
stored_comms["wood"]++
+ stored_comms["wood"]++
+ stored_comms["wood"]++
+ stored_comms["wood"]++
else
continue
@@ -203,7 +293,7 @@
dat += tools
if (emagged)
- if (module.emag)
+ if (!module.emag)
dat += text("Resource depleted ")
else if(activated(module.emag))
dat += text("[module.emag]: Activated ")
@@ -228,22 +318,22 @@
switch(type)
if("metal")
if(!stack_metal)
- stack_metal = new(src.module)
+ stack_metal = new /obj/item/stack/sheet/metal/cyborg(src.module)
stack_metal.amount = 1
stack = stack_metal
if("glass")
if(!stack_glass)
- stack_glass = new(src.module)
+ stack_glass = new /obj/item/stack/sheet/glass/cyborg(src.module)
stack_glass.amount = 1
stack = stack_glass
if("wood")
if(!stack_wood)
- stack_wood = new(src.module)
+ stack_wood = new /obj/item/stack/sheet/wood/cyborg(src.module)
stack_wood.amount = 1
stack = stack_wood
if("plastic")
if(!stack_plastic)
- stack_plastic = new(src.module)
+ stack_plastic = new /obj/item/stack/sheet/mineral/plastic/cyborg(src.module)
stack_plastic.amount = 1
stack = stack_plastic
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
index 5788bfa121..cdf9561a2f 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
@@ -17,7 +17,6 @@
/obj/machinery/drone_fabricator/New()
..()
- produce_drones = config.allow_drone_spawn
/obj/machinery/drone_fabricator/power_change()
if (powered())
@@ -28,7 +27,10 @@
/obj/machinery/drone_fabricator/process()
- if(stat & NOPOWER || !produce_drones)
+ if(ticker.current_state < GAME_STATE_PLAYING)
+ return
+
+ if((stat & NOPOWER || !produce_drones) && icon_state != "drone_fab_nopower")
icon_state = "drone_fab_nopower"
return
@@ -83,6 +85,11 @@
set name = "Join As Drone"
set desc = "If there is a powered, enabled fabricator in the game world with a prepared chassis, join as a maintenance drone."
+
+ if(ticker.current_state < GAME_STATE_PLAYING)
+ src << "\red The game hasn't started yet!"
+ return
+
if(!(config.allow_drone_spawn))
src << "\red That verb is not currently permitted."
return
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index db0ccea0a7..c72cb82030 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -5,8 +5,7 @@
icon_state = "robot"
maxHealth = 200
health = 200
- universal_speak = 1
-
+
var/sight_mode = 0
var/custom_name = ""
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
@@ -46,7 +45,6 @@
var/list/req_access = list(access_robotics)
var/ident = 0
//var/list/laws = list()
- var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
var/viewalerts = 0
var/modtype = "Default"
var/lower_mod = 0
@@ -241,6 +239,9 @@
module_sprites["Combat Android"] = "droid-combat"
module.channels = list("Security" = 1)
+ //languages
+ module.add_languages(src)
+
//Custom_sprite check and entry
if (custom_sprite == 1)
module_sprites["Custom"] = "[src.ckey]-[modtype]"
@@ -328,16 +329,14 @@
dat += "Close
"
for (var/cat in alarms)
dat += text("[cat] \n")
- var/list/L = alarms[cat]
- if (L.len)
- for (var/alarm in L)
- var/list/alm = L[alarm]
- var/area/A = alm[1]
- var/list/sources = alm[3]
+ var/list/alarmlist = alarms[cat]
+ if (alarmlist.len)
+ for (var/area_name in alarmlist)
+ var/datum/alarm/alarm = alarmlist[area_name]
dat += ""
- dat += text("-- [A.name]")
- if (sources.len > 1)
- dat += text("- [sources.len] sources")
+ dat += text("-- [area_name]")
+ if (alarm.sources.len > 1)
+ dat += text("- [alarm.sources.len] sources")
dat += " \n"
else
dat += "-- All Systems Nominal \n"
@@ -529,47 +528,22 @@
return
-/mob/living/silicon/robot/triggerAlarm(var/class, area/A, var/O, var/alarmsource)
+/mob/living/silicon/robot/triggerAlarm(var/class, area/A, list/cameralist, var/source)
if (stat == 2)
return 1
- var/list/L = alarms[class]
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/sources = alarm[3]
- if (!(alarmsource in sources))
- sources += alarmsource
- return 1
- var/obj/machinery/camera/C = null
- var/list/CL = null
- if (O && istype(O, /list))
- CL = O
- if (CL.len == 1)
- C = CL[1]
- else if (O && istype(O, /obj/machinery/camera))
- C = O
- L[A.name] = list(A, (C) ? C : O, list(alarmsource))
+
+ ..()
+
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
-// if (viewalerts) robot_alerts()
- return 1
/mob/living/silicon/robot/cancelAlarm(var/class, area/A as area, obj/origin)
- var/list/L = alarms[class]
- var/cleared = 0
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/srcs = alarm[3]
- if (origin in srcs)
- srcs -= origin
- if (srcs.len == 0)
- cleared = 1
- L -= I
- if (cleared)
+ var/has_alarm = ..()
+
+ if (!has_alarm)
queueAlarm(text("--- [class] alarm in [A.name] has been cleared."), class, 0)
// if (viewalerts) robot_alerts()
- return !cleared
+ return has_alarm
/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob)
@@ -613,7 +587,7 @@
user << "Need more welding fuel!"
return
- else if(istype(W, /obj/item/weapon/cable_coil) && wiresexposed)
+ else if(istype(W, /obj/item/weapon/cable_coil) && (wiresexposed || istype(src,/mob/living/silicon/robot/drone)))
if (!getFireLoss())
user << "Nothing to fix here!"
return
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 7ca79523df..f6ca16f6e4 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -10,7 +10,6 @@
var/obj/item/emag = null
var/obj/item/borg/upgrade/jetpack = null
-
emp_act(severity)
if(modules)
for(var/obj/O in modules)
@@ -41,6 +40,9 @@
if(O)
modules += O
+/obj/item/weapon/robot_module/proc/add_languages(var/mob/living/silicon/robot/R)
+ R.add_language("Tradeband", 0)
+
/obj/item/weapon/robot_module/standard
name = "standard robot module"
@@ -224,6 +226,17 @@
R.add_reagent("beer2", 50)
src.emag.name = "Mickey Finn's Special Brew"
return
+
+ add_languages(var/mob/living/silicon/robot/R)
+ //full set of languages
+ R.add_language("Sol Common", 1)
+ R.add_language("Sinta'unathi", 1)
+ R.add_language("Siik'maas", 1)
+ R.add_language("Siik'tajr", 0)
+ R.add_language("Skrellian", 1)
+ R.add_language("Rootspeak", 1)
+ R.add_language("Tradeband", 1)
+ R.add_language("Gutter", 1)
/obj/item/weapon/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R)
var/obj/item/weapon/reagent_containers/food/condiment/enzyme/E = locate() in src.modules
@@ -270,7 +283,17 @@
/obj/item/weapon/robot_module/drone
name = "drone module"
-
+ var/list/stacktypes = list(
+ /obj/item/stack/sheet/wood/cyborg = 1,
+ /obj/item/stack/sheet/mineral/plastic/cyborg = 1,
+ /obj/item/stack/sheet/rglass/cyborg = 5,
+ /obj/item/stack/tile/wood = 5,
+ /obj/item/stack/rods = 15,
+ /obj/item/stack/tile/plasteel = 15,
+ /obj/item/stack/sheet/metal/cyborg = 20,
+ /obj/item/stack/sheet/glass/cyborg = 20,
+ /obj/item/weapon/cable_coil = 30
+ )
New()
//TODO: Replace with shittier flashlight and work out why we can't remove the flash. ~Z
@@ -289,37 +312,21 @@
src.emag = new /obj/item/weapon/card/emag(src)
src.emag.name = "Cryptographic Sequencer"
-
- var/list/stacktypes = list(
- /obj/item/stack/rods = 10,
- /obj/item/stack/tile/plasteel = 10,
- /obj/item/stack/sheet/metal/cyborg = 10,
- /obj/item/stack/sheet/wood/cyborg = 1,
- /obj/item/weapon/cable_coil = 30,
- /obj/item/stack/sheet/glass/cyborg = 10,
- /obj/item/stack/sheet/mineral/plastic/cyborg = 1
- )
-
for(var/T in stacktypes)
var/obj/item/stack/sheet/W = new T(src)
W.amount = stacktypes[T]
src.modules += W
return
+
+ add_languages(var/mob/living/silicon/robot/R)
+ return //not much ROM to spare in that tiny microprocessor!
/obj/item/weapon/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R)
var/obj/item/weapon/reagent_containers/spray/cleaner/C = locate() in src.modules
C.reagents.add_reagent("cleaner", 10)
- var/list/stacks = list (
- /obj/item/stack/sheet/metal,
- /obj/item/weapon/cable_coil,
- /obj/item/stack/sheet/glass/cyborg,
- /obj/item/stack/rods,
- /obj/item/stack/tile/plasteel
- )
-
- for(var/T in stacks)
+ for(var/T in stacktypes)
var/O = locate(T) in src.modules
var/obj/item/stack/sheet/S = O
@@ -327,9 +334,9 @@
src.modules -= null
S = new T(src)
src.modules += S
- S.amount = 0
+ S.amount = 1
- if(istype(S) && S.amount < 15)
+ if(S && S.amount < stacktypes[T])
S.amount++
var/obj/item/device/lightreplacer/LR = locate() in src.modules
diff --git a/code/modules/mob/living/silicon/robot/robot_upgrades.dm b/code/modules/mob/living/silicon/robot/robot_upgrades.dm
index 45269fba43..71f22a3029 100644
--- a/code/modules/mob/living/silicon/robot/robot_upgrades.dm
+++ b/code/modules/mob/living/silicon/robot/robot_upgrades.dm
@@ -34,6 +34,8 @@
R.base_icon = "robot"
R.icon_state = "robot"
R.updateicon()
+ R.languages = list()
+ R.speech_synthesizer_langs = list()
return 1
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 8a3da816dc..295100772a 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -12,6 +12,17 @@
#define IS_ROBOT 2
#define IS_PAI 3
+/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null)
+ //These only pertain to common. Languages are handled by mob/say_understands()
+ if (!speaking)
+ if (istype(other, /mob/living/carbon/human))
+ return 1
+ if (istype(other, /mob/living/silicon))
+ return 1
+ if (istype(other, /mob/living/carbon/brain))
+ return 1
+ return ..()
+
/mob/living/silicon/say(var/message)
if (!message)
return
@@ -49,27 +60,27 @@
return
var/verb = say_quote(message)
- var/message_mode = null
-
-
- if(copytext(message,1,2) == ";")
- message_mode = "general" // I don't know why but regular radio = fuck you we ain't broadcasting, pAI mode was what was in old say code.
- message = trim(copytext(message,2))
-
- else if(length(message) >= 2)
- var/channel_prefix = copytext(message, 1 ,3)
- if(!message_mode)
- message_mode = department_radio_keys[channel_prefix]
-
- if(message_mode && bot_type == IS_ROBOT)
- if(message_mode != "binary" && !R.is_component_functioning("radio"))
- src << "\red Your radio isn't functional at this time."
- return
-
-
- if(message_mode && message_mode != "general")
- message = trim(copytext(message,3))
-
+
+ //parse radio key and consume it
+ var/message_mode = parse_message_mode(message, "general")
+ if (message_mode)
+ if (message_mode == "general")
+ message = trim(copytext(message,2))
+ else
+ message = trim(copytext(message,3))
+
+ if(message_mode && bot_type == IS_ROBOT && message_mode != "binary" && !R.is_component_functioning("radio"))
+ src << "\red Your radio isn't functional at this time."
+ return
+
+
+ //parse language key and consume it
+ var/datum/language/speaking = parse_language(message)
+ if (speaking)
+ verb = speaking.speech_verb
+ message = copytext(message,3)
+
+
switch(message_mode)
if("department")
switch(bot_type)
@@ -77,10 +88,10 @@
AI.holopad_talk(message)
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
- R.radio.talk_into(src,message,message_mode,verb)
+ R.radio.talk_into(src,message,message_mode,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
- P.radio.talk_into(src,message,message_mode,verb)
+ P.radio.talk_into(src,message,message_mode,verb,speaking)
return
if("binary")
@@ -101,10 +112,10 @@
src << "Yeah, not yet, sorry"
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
- R.radio.talk_into(src,message,null,verb)
+ R.radio.talk_into(src,message,null,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
- P.radio.talk_into(src,message,null,verb)
+ P.radio.talk_into(src,message,null,verb,speaking)
return
else
@@ -115,14 +126,14 @@
return
if(IS_ROBOT)
log_say("[key_name(src)] : [message]")
- R.radio.talk_into(src,message,message_mode,verb)
+ R.radio.talk_into(src,message,message_mode,verb,speaking)
if(IS_PAI)
log_say("[key_name(src)] : [message]")
- P.radio.talk_into(src,message,message_mode,verb)
+ P.radio.talk_into(src,message,message_mode,verb,speaking)
return
- return ..(message,null,verb)
+ return ..(message,speaking,verb)
//For holopads only. Usable by AI.
/mob/living/silicon/ai/proc/holopad_talk(var/message)
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 944063d9e0..19ed4be3c2 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -4,92 +4,13 @@
voice_name = "synthesized voice"
var/syndicate = 0
var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS
- var/list/alarms_to_show = list()
- var/list/alarms_to_clear = list()
immune_to_ssd = 1
var/list/hud_list[9]
- var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
- var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
-
-/mob/living/silicon/proc/cancelAlarm()
- return
-
-/mob/living/silicon/proc/triggerAlarm()
- return
+ var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
/mob/living/silicon/proc/show_laws()
return
-/mob/living/silicon/proc/queueAlarm(var/message, var/type, var/incoming = 1)
- var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
- if(incoming)
- alarms_to_show += message
- alarm_types_show[type] += 1
- else
- alarms_to_clear += message
- alarm_types_clear[type] += 1
-
- if(!in_cooldown)
- spawn(10 * 10) // 10 seconds
-
- if(alarms_to_show.len < 5)
- for(var/msg in alarms_to_show)
- src << msg
- else if(alarms_to_show.len)
-
- var/msg = "--- "
-
- if(alarm_types_show["Motion"])
- msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
-
- if(alarm_types_show["Fire"])
- msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
-
- if(alarm_types_show["Atmosphere"])
- msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
-
- if(alarm_types_show["Power"])
- msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
-
- if(alarm_types_show["Camera"])
- msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
-
- msg += "\[Show Alerts\]"
- src << msg
-
- if(alarms_to_clear.len < 3)
- for(var/msg in alarms_to_clear)
- src << msg
-
- else if(alarms_to_clear.len)
- var/msg = "--- "
-
- if(alarm_types_clear["Motion"])
- msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
-
- if(alarm_types_clear["Fire"])
- msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
-
- if(alarm_types_clear["Atmosphere"])
- msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
-
- if(alarm_types_clear["Power"])
- msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
-
- if(alarm_types_show["Camera"])
- msg += "CAMERA: [alarm_types_show["Power"]] alarms detected. - "
-
- msg += "\[Show Alerts\]"
- src << msg
-
-
- alarms_to_show = list()
- alarms_to_clear = list()
- for(var/i = 1; i < alarm_types_show.len; i++)
- alarm_types_show[i] = 0
- for(var/i = 1; i < alarm_types_clear.len; i++)
- alarm_types_clear[i] = 0
-
/mob/living/silicon/drop_item()
return
@@ -207,3 +128,33 @@
if(error_msg)
user << "The armoured plating is too tough."
return 0
+
+
+//Silicon mob language procs
+
+/mob/living/silicon/can_speak(datum/language/speaking)
+ return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language
+
+/mob/living/silicon/add_language(var/language, var/can_speak=1)
+ if (..(language) && can_speak)
+ speech_synthesizer_langs.Add(all_languages[language])
+
+/mob/living/silicon/remove_language(var/rem_language)
+ ..(rem_language)
+
+ for (var/datum/language/L in speech_synthesizer_langs)
+ if (L.name == rem_language)
+ speech_synthesizer_langs -= L
+
+/mob/living/silicon/check_languages()
+ set name = "Check Known Languages"
+ set category = "IC"
+ set src = usr
+
+ var/dat = "Known Languages
"
+
+ for(var/datum/language/L in languages)
+ dat += "[L.name] (:[L.key]) Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"] [L.desc]
"
+
+ src << browse(dat, "window=checklanguage")
+ return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 388cab4e10..e20633e627 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -333,6 +333,9 @@ var/list/slot_equipment_priority = list( \
return
*/
+/mob
+ var/newPlayerType = /mob/new_player
+
/mob/verb/abandon_mob()
set name = "Respawn"
set category = "OOC"
@@ -382,14 +385,12 @@ var/list/slot_equipment_priority = list( \
log_game("[usr.key] AM failed due to disconnect.")
return
- var/mob/new_player/M = new /mob/new_player()
+ var/mob/newPlayer = new newPlayerType()
if(!client)
log_game("[usr.key] AM failed due to disconnect.")
- del(M)
+ del(newPlayer)
return
-
- M.key = key
-// M.Login() //wat
+ newPlayer.key = key
return
/client/verb/changes()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 777f684dcb..803b2e7c00 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -205,6 +205,7 @@
var/obj/control_object //Used by admins to possess objects. All mobs should have this var
+ var/datum/visibility_interface/visibility_interface = null // used by the visibility system to provide an interface for the visibility networks
//Whether or not mobs can understand other mobtypes. These stay in /mob so that ghosts can hear everything.
var/universal_speak = 0 // Set to 1 to enable the mob to speak to everyone -- TLE
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 314a4026ab..bef1dd940d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -114,7 +114,17 @@ proc/isobserver(A)
if(istype(A, /mob/dead/observer))
return 1
return 0
+
+/proc/isSpirit(A)
+ if(istype(A, /mob/spirit))
+ return 1
+ return 0
+/proc/ismask(A)
+ if(istype(A, /mob/spirit/mask))
+ return 1
+ return 0
+
proc/isorgan(A)
if(istype(A, /datum/organ/external))
return 1
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 59721a24ab..0e1514177a 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -176,8 +176,15 @@
return
if(mob.stat==2) return
-
- if(isAI(mob)) return AIMove(n,direct,mob)
+
+// handle possible spirit movement
+ if(istype(mob,/mob/spirit))
+ var/mob/spirit/currentSpirit = mob
+ return currentSpirit.Spirit_Move(direct)
+
+ // handle possible AI movement
+ if(isAI(mob))
+ return AIMove(n,direct,mob)
if(mob.monkeyizing) return//This is sota the goto stop mobs from moving var
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 63ea3485bb..15a63ad79c 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -68,32 +68,34 @@
/mob/proc/say_understands(var/mob/other,var/datum/language/speaking = null)
- if(!other)
+ if (src.stat == 2) //Dead
return 1
+
//Universal speak makes everything understandable, for obvious reasons.
- else if(other.universal_speak || src.universal_speak || src.universal_understand)
+ else if(src.universal_speak || src.universal_understand)
return 1
- else if (src.stat == 2)
- return 1
- else if (speaking) //Language check.
-
- var/understood
- for(var/datum/language/L in src.languages)
- if(speaking.name == L.name)
- understood = 1
- break
-
- if(understood || universal_speak)
+
+ //Languages are handled after.
+ if (!speaking)
+ if(!other)
return 1
- else
- return 0
-
- else if(other.universal_speak || src.universal_speak || src.universal_understand)
- return 1
- else if(isAI(src) && ispAI(other))
- return 1
- else if (istype(other, src.type) || istype(src, other.type))
- return 1
+ if(other.universal_speak)
+ return 1
+ if(isAI(src) && ispAI(other))
+ return 1
+ if (istype(other, src.type) || istype(src, other.type))
+ return 1
+ return 0
+
+ //Language check.
+ for(var/datum/language/L in src.languages)
+ if(speaking.name == L.name)
+ if (L.flags & NONVERBAL)
+ if ((src.sdisabilities & BLIND || src.blinded || src.stat) || !(other in view(src)))
+ return 0
+
+ return 1
+
return 0
/mob/proc/say_quote(var/text,var/datum/language/speaking)
@@ -145,3 +147,28 @@
else if (ending == "!")
return "2"
return "0"
+
+//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say.
+//returns the message mode string or null for no message mode.
+//standard mode is the mode returned for the special ';' radio code.
+/mob/proc/parse_message_mode(var/message, var/standard_mode="headset")
+ if(length(message) >= 1 && copytext(message,1,2) == ";")
+ return standard_mode
+
+ if(length(message) >= 2)
+ var/channel_prefix = copytext(message, 1 ,3)
+ return department_radio_keys[channel_prefix]
+
+ return null
+
+//parses the language code (e.g. :j) from text, such as that supplied to say.
+//returns the language object only if the code corresponds to a language that src can speak, otherwise null.
+/mob/proc/parse_language(var/message)
+ if(length(message) >= 2)
+ var/language_prefix = lowertext(copytext(message, 1 ,3))
+ var/datum/language/L = language_keys[language_prefix]
+ if (can_speak(L))
+ return L
+
+ return null
+
diff --git a/code/modules/mob/spirit/cultnet.dm b/code/modules/mob/spirit/cultnet.dm
new file mode 100644
index 0000000000..df0cd57ff5
--- /dev/null
+++ b/code/modules/mob/spirit/cultnet.dm
@@ -0,0 +1,80 @@
+/*
+
+This file contains the code necessary to do the display code for cult spirits.
+
+It reuses a lot of code from the AIEye cameraNetwork. In order to work properly, some of those files needed to be modified as well.
+
+*/
+
+
+/proc/isCultRune(var/viewpoint)
+ var/obj/effect/rune/test_rune = viewpoint
+ if (test_rune)
+ return TRUE
+ return FALSE
+
+
+/proc/isCultViewpoint(var/viewpoint)
+ var/obj/cult_viewpoint/vp = viewpoint
+ if (vp)
+ return TRUE
+ return FALSE
+
+
+/datum/visibility_chunk/cult/validViewpoint(var/atom/viewpoint)
+ var/turf/point = locate(src.x + 8, src.y + 8, src.z)
+ if(get_dist(point, viewpoint) > 24)
+ return FALSE
+
+ if (isCultRune(viewpoint) || isCultViewpoint(viewpoint))
+ return viewpoint:can_use()
+ return FALSE
+
+
+/datum/visibility_chunk/cult/getVisibleTurfsForViewpoint(var/viewpoint)
+ var/obj/effect/rune/rune = viewpoint
+ if (rune)
+ return rune.can_see()
+ var/obj/cult_viewpoint/cvp = viewpoint
+ if (cvp)
+ return cvp.can_see()
+ return null
+
+
+/datum/visibility_chunk/cult/findNearbyViewpoints()
+ for(var/obj/cult_viewpoint/vp in range(16, locate(x + 8, y + 8, z)))
+ if(vp.can_use())
+ viewpoints += vp
+ for(var/obj/effect/rune/rune in range(16, locate(x + 8, y + 8, z)))
+ viewpoints += rune
+
+
+/datum/visibility_network/cult
+ ChunkType = /datum/visibility_chunk/cult
+
+
+/datum/visibility_network/cult/validViewpoint(var/viewpoint)
+ if (isCultRune(viewpoint) || isCultViewpoint(viewpoint))
+ return viewpoint:can_use()
+ return FALSE
+
+/datum/visibility_network/cult/getViewpointFromMob(var/mob/currentMob)
+ for(var/obj/cult_viewpoint/currentView in currentMob)
+ return currentView
+ return FALSE
+
+
+/datum/visibility_interface/cult
+ chunk_type = /datum/visibility_chunk/cult
+
+
+/*
+RUNE JUNK
+*/
+
+/obj/effect/rune/proc/can_use()
+ return TRUE
+
+/obj/effect/rune/proc/can_see()
+ return hear(view_range, get_turf(src))
+
diff --git a/code/modules/mob/spirit/mask/mask.dm b/code/modules/mob/spirit/mask/mask.dm
new file mode 100644
index 0000000000..6b58f201cf
--- /dev/null
+++ b/code/modules/mob/spirit/mask/mask.dm
@@ -0,0 +1,227 @@
+/mob/spirit/mask
+ icon = 'icons/mob/spirits/mask.dmi'
+ icon_state = "depressurized"
+
+/mob/spirit/mask/New()
+ ..()
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman(src)
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/blood_speech(src)
+ spell_list += new /obj/effect/proc_holder/spell/aoe_turf/shatter_lights(src)
+
+
+/mob/spirit/mask/verb/go_to_follower()
+ set category = "Mask"
+ set name = "Go to follower"
+ set desc = "Select who you would like to go too."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ follow_cultist(cultist.owner)
+ cult_log("[key_name_admin(src)] started following [key_name_admin(cultist)].")
+ src << "You start following [cultist.get_display_name()]."
+
+
+/mob/spirit/mask/verb/urge_cultist()
+ set category = "Mask"
+ set name = "Urge cultist"
+ set desc = "Push your cultists to do something."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ if (cultist.owner)
+ var/newUrge = stripped_input(usr, "", "Set Urge", "")
+ cultist.set_urge(newUrge)
+ src << "You urge [cultist.owner.name] to [newUrge]."
+ cult_log("controlled by [key_name_admin(src)] has urged [key_name_admin(cultist.owner)] to [newUrge].")
+
+/mob/spirit/mask/verb/set_cult_name()
+ set category = "Mask"
+ set name = "Set Cult Name"
+ set desc = "Grant a cultist a name."
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ var/newName = stripped_input(usr, "", "Set Cult Name", "")
+ if (!newName)
+ return
+ cultist.set_cult_name(newName)
+ src << "You grant [cultist.owner.name] the secret name of [newName]."
+ if (cultist.owner)
+ cult_log("[key_name_admin(src)] has set [key_name_admin(cultist.owner)] to \'[newName]\'")
+
+
+/mob/spirit/mask/verb/urge_cult()
+ set category = "Mask"
+ set name = "Urge Cult"
+ set desc = "Set urge on the entire cult."
+
+ var/newUrge = stripped_input(usr, "Please choose an urge.", "Set Urge", "")
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ viewpoint.set_urge(newUrge)
+ src << "You urge the entire cult to [newUrge]."
+ cult_log("[key_name_admin(src)] has urged the entire cult to [newUrge]")
+
+
+/mob/spirit/mask/verb/set_favor_for_cultist()
+ set category = "Mask"
+ set name = "Show your favor"
+ set desc = "Set the favor for a cultist"
+
+ var/obj/cult_viewpoint/cultist = pick_cultist()
+ if (cultist)
+ if (cultist.owner)
+ var/list/favor = list("Pleased", "Displeased", "Indifference")
+ var/emotion = input("Pick your emotion", "Mask", null, null) in favor
+ switch(emotion)
+ if("Pleased")
+ cultist.set_favor(1)
+ cult_log("[key_name_admin(src)] is pleased with [key_name_admin(cultist.owner)]")
+ if("Displeased")
+ cultist.set_favor(-1)
+ cult_log("[key_name_admin(src)] is displeased with [key_name_admin(cultist.owner)]")
+ if("Indifference")
+ cultist.set_favor(0)
+ cult_log("[key_name_admin(src)] is indifferent too [key_name_admin(cultist.owner)]")
+
+
+/mob/spirit/mask/proc/set_name()
+ spawn(0)
+ var/newName = stripped_input(src, "Please pick a name.", "Pick Name for Mask", "")
+ name = newName ? newName : "Mask of Nar'sie"
+ src << "You have set your name to [name]."
+
+
+/mob/spirit/mask/proc/pick_cultist()
+ var/list/cultists = list()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ cultists[viewpoint.get_display_name()]=viewpoint
+ var/input = input("Please, select a cultist!", "Cult", null, null) as null|anything in cultists
+ var/obj/cult_viewpoint/result = cultists[input]
+ return result
+
+
+// this proc makes the mask visible very briefly
+/mob/spirit/mask/proc/flicker()
+ spawn(0)
+ alpha = 127
+ invisibility=0
+ sleep(5)
+ invisibility=initial(invisibility)
+ alpha = 255
+
+/proc/flicker_mask(mob/spirit/mask/target)
+ if(istype(target))
+ target.flicker()
+
+// SPELLS
+/obj/effect/proc_holder/spell/aoe_turf/blood_speech
+ name = "Speak to your Acolytes"
+ desc = "This spell allows you to speak to your flock."
+ school = "unknown evil"
+ charge_type = "recharge"
+ charge_max = 2000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+
+/obj/effect/proc_holder/spell/aoe_turf/blood_speech/cast(list/targets)
+ var/input = stripped_input(usr, "Please choose a message to tell your acolytes.", "Voice of Blood", "")
+ if(!input)
+ revert_cast(usr)
+ cult_log("[key_name_admin(usr)]says : [input]")
+ flicker_mask(usr)
+ for(var/datum/mind/H in ticker.mode.cult)
+ if (H.current)
+ H.current << "[usr.name]: [input]"
+ for(var/mob/spirit/spirit in spirits)
+ spirit << "[usr.name]: [input]"
+
+
+/obj/effect/proc_holder/spell/aoe_turf/shatter_lights
+ name = "Spread Shadows"
+ desc = "This spell breaks lights near the mask."
+ school = "unknown evil"
+ charge_type = "recharge"
+ charge_max = 1000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+
+/obj/effect/proc_holder/spell/aoe_turf/shatter_lights/cast(list/targets)
+ cult_log("[key_name_admin(usr)] used Spread Shadows.")
+ flicker_mask(usr)
+ spawn(0)
+ for(var/area/A in range(3,get_turf(usr)))
+ for(var/obj/machinery/light/L in A)
+ L.on = 1
+ L.broken()
+ sleep(1)
+ for(var/obj/item/device/flashlight/F in A)
+ F.on = 0
+
+
+/obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman
+ name = "Create Talisman"
+ desc = "This spell conjures a talisman"
+
+ school = "conjuration"
+ charge_type = "recharge"
+ charge_max = 3000
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+ range = 0
+ summon_type = list(/obj/item/weapon/paper/talisman)
+
+ var/list/talismans = list( "Armor"="armor",
+ "Blind"="blind",
+ "Conceal"="conceal",
+ "Communicate"="communicate",
+ "Deafen"="deafen",
+ "EMP"="emp",
+ "Teleport"="teleport",
+ "Tome"="newtome",
+ "Reveal Runes",
+ "Stun"="runestun",
+ "Soul Stone"="soulstone",
+ "Construct"="construct")
+
+
+/obj/effect/proc_holder/spell/aoe_turf/conjure/create_talisman/cast(list/targets)
+
+ var/talisman = input("Pick a talisman type", "Talisman", null, null) as null|anything in talismans
+ var/imbue_value = talismans[talisman]
+ if (!talisman)
+ usr << "You choose not to create a talisman."
+ revert_cast(usr)
+ return
+
+ cult_log("[key_name_admin(usr,0)] created a talisman of type [talisman].")
+ flicker_mask(usr)
+
+ switch(talisman)
+
+ if ("Teleport")
+ var/target_rune = input("Pick a teleport target", "Teleport Rune", null, null) as null|anything in engwords
+ if (!target_rune)
+ usr << "You choose not to create a talisman."
+ revert_cast(usr)
+ return
+ summon_type = list(/obj/item/weapon/paper/talisman)
+ newVars = list("imbue" = "[target_rune]", "info" = "[target_rune]")
+
+ if ("Soul Stone")
+ summon_type = list(/obj/item/device/soulstone)
+ newVars = list()
+
+ if ("Construct")
+ summon_type = list(/obj/structure/constructshell)
+ newVars = list()
+
+ else
+ summon_type = list(/obj/item/weapon/paper/talisman)
+ newVars = list("imbue" = "[imbue_value]")
+
+ ..()
\ No newline at end of file
diff --git a/code/modules/mob/spirit/mask/respawn.dm b/code/modules/mob/spirit/mask/respawn.dm
new file mode 100644
index 0000000000..acfb69073c
--- /dev/null
+++ b/code/modules/mob/spirit/mask/respawn.dm
@@ -0,0 +1,20 @@
+/proc/there_can_be_only_one_mask(var/mob/spirit/mask/target)
+ if(!istype(target))
+ return
+ for(var/mob/spirit/mask/currentSpirit in spirits)
+ if(currentSpirit)
+ if(currentSpirit!=target)
+ // create the ghost
+ var/mob/dead/observer/ghost = currentSpirit.ghostize(TRUE)
+ // let the deposed mask respawn immediately, the poor dear
+ ghost.timeofdeath = world.time - 20000
+ ghost.newPlayerType = /mob/new_player/cultist
+ // remove old mask body
+ del(currentSpirit)
+
+
+/mob/new_player/cultist/AttemptLateSpawn(rank)
+ var/mob/newCharacter = ..(rank)
+ if(ticker.mode)
+ if(is_convertable_to_cult(newCharacter.mind))
+ ticker.mode.add_cultist(newCharacter.mind)
\ No newline at end of file
diff --git a/code/modules/mob/spirit/movement.dm b/code/modules/mob/spirit/movement.dm
new file mode 100644
index 0000000000..d8847cb480
--- /dev/null
+++ b/code/modules/mob/spirit/movement.dm
@@ -0,0 +1,62 @@
+// spirits are not moved by airflow
+mob/spirit/Move()
+ return 0
+
+// this is the main move proc for spirits, it uses their 'setLoc' function to handle all the visibility shenanigans
+// this, like most movement code for these guys, is cribbed from the AIEye movement code
+mob/spirit/proc/Spirit_Move(direct)
+
+ var/initial = initial(sprint)
+ var/max_sprint = 50
+
+ // if we haven't moved in a while, we stop sprinting
+ if(cooldown && cooldown < world.timeofday) // 3 seconds
+ sprint = initial
+
+ for(var/i = 0; i < max(sprint, initial); i += 20)
+ var/turf/step = get_turf(get_step(src, direct))
+ if(step)
+ setLoc(step)
+
+ dir = direct // update our sprite
+
+ cooldown = world.timeofday + 5
+ if(acceleration)
+ sprint = min(sprint + 0.5, max_sprint)
+ else
+ sprint = initial
+
+ // if we're trying to move, we want to stop following our target
+ follow_target = null
+
+
+/mob/spirit/proc/follow_cultist(mob/living/target as mob)
+ if(!istype(target)) return
+ var/obj/cult_viewpoint/currentView = getCultViewpoint(target)
+ var/mob/spirit/U = usr
+
+ if (!currentView)
+ U << "As a spirit, you may only track cultists."
+
+ U.follow_target = target
+ U << "Now following [currentView.get_cult_name()]."
+
+ spawn (0)
+ while (U.follow_target == target)
+ if (U.follow_target == null)
+ return
+ U.setLoc(get_turf(target))
+ sleep(10)
+
+
+mob/spirit/proc/setLoc(var/T)
+ T = get_turf(T)
+ loc = T
+ cultNetwork.visibility(src)
+
+mob/spirit/verb/toggle_acceleration()
+ set category = "Spirit"
+ set name = "Toggle Acceleration"
+
+ acceleration = !acceleration
+ usr << "Acceleration has been toggled [acceleration ? "on" : "off"]."
\ No newline at end of file
diff --git a/code/modules/mob/spirit/spirit.dm b/code/modules/mob/spirit/spirit.dm
new file mode 100644
index 0000000000..ce7080b744
--- /dev/null
+++ b/code/modules/mob/spirit/spirit.dm
@@ -0,0 +1,73 @@
+/*
+This mob type is used for entities that exist within the Cult's spirit world. They share the same visibility network and are intangible.
+*/
+
+mob/spirit
+ name = "spirit"
+ desc = "A spirit"
+ icon = 'icons/mob/mob.dmi'
+ icon_state = "ghost"
+ layer = 4
+ stat = CONSCIOUS
+ status_flags = GODMODE // spirits cannot be killed
+ density = 0
+ canmove = 0
+ blinded = 0
+ anchored = 1
+ mouse_opacity = 0
+ invisibility = INVISIBILITY_SPIRIT
+ universal_speak = 1
+
+ // pseudo-movement values
+ var/sprint = 10
+ var/cooldown = 0
+ var/acceleration = 1
+ var/follow_target = null
+
+
+mob/spirit/is_active()
+ if (client && client.inactivity <= 10 * 60 * 10)
+ return TRUE
+ return FALSE
+
+
+mob/spirit/New()
+ sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
+ see_invisible = SEE_SPIRITS
+ see_in_dark = 100
+
+ loc = pick(latejoin)
+
+ // hook them to the cult visibility network
+ visibility_interface = new /datum/visibility_interface/cult(src)
+
+ // no nameless spirits
+ if (!name)
+ name = "Boogyman"
+
+ spirits+=src
+
+ ..()
+
+mob/spirit/Del()
+ spirits-=src
+ ..()
+
+
+mob/spirit/Topic(href, href_list)
+
+ if(usr != src)
+ return
+ ..()
+
+ usr << "Spirit Href = [href]"
+ for (var/tempref in href_list)
+ usr << "Spirit href list [tempref] = [href_list[tempref]]"
+
+ if (href_list["track"])
+ usr << "Got to tracking."
+ var/mob/target = locate(href_list["track"]) in mob_list
+ var/mob/spirit/A = locate(href_list["track2"]) in spirits
+ if(A && target)
+ A.follow_cultist(target)
+ return
\ No newline at end of file
diff --git a/code/modules/mob/spirit/viewpoint.dm b/code/modules/mob/spirit/viewpoint.dm
new file mode 100644
index 0000000000..5e06356129
--- /dev/null
+++ b/code/modules/mob/spirit/viewpoint.dm
@@ -0,0 +1,202 @@
+#define FAVOR_PLEASED 1
+#define FAVOR_INDIFFERENT 0
+#define FAVOR_DISPLEASED -1
+
+
+var/obj/cult_viewpoint/list/cult_viewpoints = list()
+
+
+/obj/cult_viewpoint
+ var/view_range = 7
+ var/updating = 0
+ var/mob/owner = null
+ var/urge = ""
+ var/favor = FAVOR_INDIFFERENT
+ var/cult_name = null
+
+
+/obj/cult_viewpoint/New(var/mob/target)
+ owner = target
+ //src.loc = owner
+ owner.addToVisibilityNetwork(cultNetwork)
+ cultNetwork.viewpoints+=src
+ cultNetwork.addViewpoint(src)
+ cult_viewpoints+=src
+ //handle_missing_mask()
+ ..()
+
+
+/obj/cult_viewpoint/Del()
+ processing_objects.Remove(src)
+ cultNetwork.viewpoints-=src
+ cultNetwork.removeViewpoint(src)
+ cult_viewpoints-=src
+ owner.removeFromVisibilityNetwork(cultNetwork)
+ ..()
+ return
+
+
+// VERBS
+/obj/cult_viewpoint/verb/check_urge()
+ set category = "Cult"
+ set desc = "Discover what your god commands of you."
+ set name = "Check Urge"
+ set src in usr
+ if (src.urge)
+ owner << "\red \b You feel the urge to [src.urge]"
+ else
+ owner << "\b You feel no supernatural compulsions."
+
+
+/obj/cult_viewpoint/verb/reach_out()
+ set category = "Cult"
+ set desc = "Reach out for your gods presence."
+ set name = "Reach Out"
+ set src in usr
+
+ for(var/mob/spirit/mask/currentMask in spirits)
+ if (currentMask.is_active())
+ owner << "\red \b You feel the reassuring presence of your god."
+ currentMask << "[get_display_name()] has reached out to you."
+ return
+ owner << "\b You feel a chilling absence."
+ handle_missing_mask()
+
+
+/obj/cult_viewpoint/verb/check_favor()
+ set category = "Cult"
+ set desc = "Check your favor with your god."
+ set name = "Check Favor"
+ set src in usr
+ switch(favor)
+ if(FAVOR_PLEASED)
+ owner << "\red \b You bask in your gods favor."
+ if(FAVOR_INDIFFERENT)
+ owner << "\red \b You feel nothing."
+ if(FAVOR_DISPLEASED)
+ owner << "\red \b You cringe at your gods displeasure."
+
+
+/obj/cult_viewpoint/verb/pray_to_mask()
+ set category = "Cult"
+ set desc = "Pray to your god"
+ set name = "Pray to Nar'Sie"
+ set src in usr
+
+ var/input = stripped_input(usr, "Please choose a message to say to your god.", "Pray to Nar'Sie", "")
+ if(!input)
+ return
+
+ cult_log("[key_name(usr,0)](Pray):[input]")
+ owner << "You pray to Nar'Sie: [input]"
+
+ for(var/mob/spirit/spirit in spirits)
+ spirit << "[get_display_name()] prays : [input]"
+
+// PROCS
+/obj/cult_viewpoint/proc/set_favor(var/newFavor)
+ favor = newFavor
+ check_favor()
+
+
+/obj/cult_viewpoint/proc/set_urge(var/newUrge)
+ if (!newUrge)
+ src.urge = null
+ src.urge = copytext(newUrge, 1, MAX_MESSAGE_LEN)
+ check_urge()
+
+
+/obj/cult_viewpoint/proc/can_use()
+ if (owner.stat != DEAD)
+ return TRUE
+ return FALSE
+
+
+/obj/cult_viewpoint/proc/can_see()
+ return hear(view_range, get_turf(owner))
+
+
+/obj/cult_viewpoint/proc/get_cult_name()
+ if (cult_name)
+ return cult_name
+ return "An Unknown Servent"
+
+
+/obj/cult_viewpoint/proc/set_cult_name(var/newName)
+ if (!owner)
+ return FALSE
+ if (newName)
+ cult_name = newName
+ owner << "\red \b You have been blessed with the secret name of '[newName]'."
+ else
+ cult_name = null
+ owner << "\red \b Your god has taken your secret name."
+
+
+/obj/cult_viewpoint/proc/get_display_name()
+ if (!owner)
+ return
+ if (cult_name)
+ return cult_name
+ return owner.name
+
+
+/obj/cult_viewpoint/proc/become_mask()
+ set category = "Cult"
+ set name = "Become Mask"
+ set desc = "Sacrifice your life and become a Mask of Nar'sie."
+ set src in usr
+
+ cult_log("[key_name(usr,0)] has tried to become a Mask of Nar'sie.")
+
+ if (!active_mask())
+ var/transformation_type = alert(owner.client, "You are about to become a Mask. Do you want it to be subtle or violent?", "Mask", "Subtle", "Violent")
+ if(!active_mask())
+ cult_log("[key_name(usr,0)] has become a Mask of Nar'sie.")
+ if (transformation_type=="Subtle")
+ log_admin("[key_name_admin(owner)] has subtly become a Mask of Nar'sie")
+ owner.make_into_mask(0,0)
+ else
+ log_admin("[key_name_admin(owner)] has violently become a Mask of Nar'sie")
+ owner.make_into_mask(1,1)
+ else
+ owner << "\b You cannot become a mask of Nar'Sie because a Mask already exists."
+ mask_has_been_found()
+ return
+
+
+/obj/cult_viewpoint/proc/active_mask()
+ for(var/mob/spirit/mask/currentMask in spirits)
+ if (currentMask.is_active())
+ return TRUE
+ return FALSE
+
+
+/obj/cult_viewpoint/proc/handle_missing_mask()
+ if (active_mask())
+ mask_has_been_found()
+ else
+ mask_is_missing()
+
+
+/obj/cult_viewpoint/proc/mask_has_been_found()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ if (viewpoint.verbs.Find(/obj/cult_viewpoint/proc/become_mask))
+ viewpoint.verbs-=/obj/cult_viewpoint/proc/become_mask
+
+
+/obj/cult_viewpoint/proc/mask_is_missing()
+ for(var/obj/cult_viewpoint/viewpoint in cult_viewpoints)
+ if (!viewpoint.verbs.Find(/obj/cult_viewpoint/proc/become_mask))
+ viewpoint.verbs+=/obj/cult_viewpoint/proc/become_mask
+
+
+/proc/getCultViewpoint(var/mob/currentMob)
+ for(var/obj/cult_viewpoint/currentView in currentMob)
+ return currentView
+ return FALSE
+
+
+#undef FAVOR_PLEASED
+#undef FAVOR_INDIFFERENT
+#undef FAVOR_DISPLEASED
\ No newline at end of file
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 6efec2f7ec..d9f0a403b3 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -134,7 +134,59 @@
. = O
del(src)
+
+/mob/living/carbon/human/make_into_mask(var/should_gib = 0)
+ for(var/t in organs)
+ del(t)
+ return ..(should_gib)
+
+/mob/proc/make_into_mask(var/should_gib = 0, var/should_remove_items = 0)
+
+ if(!should_gib)
+ icon = null
+ invisibility = 101
+
+ if(!should_remove_items)
+ for(var/obj/item/W in src)
+ drop_from_inventory(W)
+
+ var/mob/spirit/mask/new_spirit = new()
+
+ if(mind)
+ new_spirit.mind = mind
+ new_spirit.mind.assigned_role = "Mask"
+ new_spirit.mind.original = new_spirit
+
+ new_spirit.key = key
+ new_spirit.loc=loc
+
+ if (should_gib)
+ spawn(0)
+ src.gib() // gib the body
+ else
+ spawn(0)//To prevent the proc from returning null.
+ src.visible_message( \
+ "[src] disappears into the shadows, never to be seen again.", \
+ "You disappear into the shadows, never to be seen again.", \
+ "You hear strange noise, you can't quite place it.")
+ del(src)
+
+ new_spirit << "You are a Mask of Nar'sie now. You are a tiny fragment of the unknowable entity that is the god."
+ new_spirit << "Your job is to help your acolytes complete their goals. Be spooky. Do evil."
+
+ new_spirit.set_name()
+
+ // let spirits identify cultists
+ if(ticker.mode)
+ ticker.mode.reset_cult_icons_for_spirit(new_spirit)
+
+ // highlander test
+ there_can_be_only_one_mask(new_spirit)
+
+ return new_spirit
+
+
//human -> robot
/mob/living/carbon/human/proc/Robotize()
if (monkeyizing)
diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm
index d6c0058ff8..8ede4c9ed1 100644
--- a/code/modules/paperwork/paper_bundle.dm
+++ b/code/modules/paperwork/paper_bundle.dm
@@ -81,7 +81,10 @@
dat+= ""
if(istype(src[page], /obj/item/weapon/paper))
var/obj/item/weapon/paper/P = W
- dat+= "[P.name][P.info][P.stamps]"
+ if(!(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/dead/observer) || istype(usr, /mob/living/silicon)))
+ dat+= "[P.name][stars(P.info)][P.stamps]"
+ else
+ dat+= "[P.name][P.info][P.stamps]"
human_user << browse(dat, "window=[name]")
P.add_fingerprint(usr)
else if(istype(src[page], /obj/item/weapon/photo))
@@ -94,6 +97,7 @@
+ "