diff --git a/_maps/map_files/RandomZLevels/spacehotel.dmm b/_maps/map_files/RandomZLevels/spacehotel.dmm
index 4d431302130..f438e48dfc2 100644
--- a/_maps/map_files/RandomZLevels/spacehotel.dmm
+++ b/_maps/map_files/RandomZLevels/spacehotel.dmm
@@ -3125,7 +3125,6 @@
},
/area/awaymission/spacehotel/kitchen)
"gE" = (
-/mob/living/carbon/human/interactive/away/hotel/chef,
/turf/unsimulated/floor{
icon_state = "cafeteria"
},
@@ -3278,7 +3277,6 @@
},
/area/awaymission/spacehotel)
"ha" = (
-/mob/living/carbon/human/interactive/away/hotel/bartender,
/turf/unsimulated/floor{
icon_state = "whiteyellowfull"
},
@@ -4135,9 +4133,6 @@
/turf/unsimulated/floor/carpet,
/area/awaymission/spacehotel)
"jm" = (
-/obj/effect/spawner/snpc_squad{
- squad_type = /mob/living/carbon/human/interactive/away/hotel/guard
- },
/turf/unsimulated/floor/carpet,
/area/awaymission/spacehotel)
"jn" = (
@@ -4489,7 +4484,6 @@
/obj/structure/chair{
dir = 8
},
-/mob/living/carbon/human/interactive/away/hotel/concierge,
/turf/unsimulated/floor/carpet,
/area/awaymission/spacehotel/reception)
"kk" = (
diff --git a/_maps/map_files/RandomZLevels/stationCollision.dmm b/_maps/map_files/RandomZLevels/stationCollision.dmm
index a8e3a533033..cc1767248cf 100644
--- a/_maps/map_files/RandomZLevels/stationCollision.dmm
+++ b/_maps/map_files/RandomZLevels/stationCollision.dmm
@@ -4123,7 +4123,7 @@
/turf/simulated/floor/plasteel,
/area/awaymission/arrivalblock)
"kG" = (
-/obj/item/clothing/shoes/syndigaloshes,
+/obj/item/clothing/shoes/chameleon/noslip,
/obj/item/clothing/under/syndicate,
/obj/effect/decal/remains/human{
desc = "This guy seemed to have died in terrible way! Half his remains are dust.";
diff --git a/_maps/map_files/RandomZLevels/terrorspiders.dmm b/_maps/map_files/RandomZLevels/terrorspiders.dmm
index 8cb46a5966d..583ae8c74c9 100644
--- a/_maps/map_files/RandomZLevels/terrorspiders.dmm
+++ b/_maps/map_files/RandomZLevels/terrorspiders.dmm
@@ -13398,7 +13398,7 @@
/area/awaymission/UO71/loot)
"zt" = (
/obj/structure/chair/wood/normal,
-/obj/item/clothing/shoes/syndigaloshes,
+/obj/item/clothing/shoes/chameleon/noslip,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/loot)
"zu" = (
diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm
index 190f9ef72ee..dda71eb7e09 100644
--- a/_maps/map_files/cyberiad/z2.dmm
+++ b/_maps/map_files/cyberiad/z2.dmm
@@ -8544,7 +8544,7 @@
pixel_y = 3
},
/obj/item/clothing/mask/gas/cyborg,
-/obj/item/clothing/mask/gas/voice,
+/obj/item/clothing/mask/chameleon,
/obj/item/clothing/mask/balaclava{
pixel_x = 3;
pixel_y = -3
@@ -8730,7 +8730,7 @@
/area/admin)
"uY" = (
/obj/structure/rack,
-/obj/item/clothing/under/chameleon/all{
+/obj/item/clothing/mask/chameleon{
pixel_x = -3;
pixel_y = 3
},
@@ -9037,7 +9037,7 @@
pixel_y = 3
},
/obj/item/clothing/shoes/centcom,
-/obj/item/clothing/shoes/syndigaloshes{
+/obj/item/clothing/shoes/chameleon/noslip{
pixel_x = 3;
pixel_y = -3
},
@@ -12276,7 +12276,7 @@
"Hv" = (
/obj/structure/rack,
/obj/item/katana/energy,
-/obj/item/clothing/mask/gas/voice/space_ninja,
+/obj/item/clothing/mask/gas/space_ninja,
/turf/unsimulated/floor{
icon_state = "engine"
},
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index ba4bf0090c7..a8c6b4bd5e8 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -52,12 +52,6 @@
#define GRAB_UPGRADING 4
#define GRAB_KILL 5
-
-//Hostile Mob AI Status
-#define AI_ON 1
-#define AI_IDLE 2
-#define AI_OFF 3
-
//Attack types for checking shields/hit reactions
#define MELEE_ATTACK 1
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 375ce811af8..6e9e8a9d102 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -426,4 +426,10 @@
#define GHOST_ORBIT_PENTAGON "pentagon"
//Explosive wall groups
-#define EXPLOSIVE_WALL_GROUP_SYNDICATE_BASE "syndicate_base"
\ No newline at end of file
+#define EXPLOSIVE_WALL_GROUP_SYNDICATE_BASE "syndicate_base"
+
+//suit sensors: sensor_mode defines
+#define SENSOR_OFF 0
+#define SENSOR_LIVING 1
+#define SENSOR_VITALS 2
+#define SENSOR_COORDS 3
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 0f05c08b2f7..284b0e4d745 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -94,6 +94,13 @@
#define APPEARANCE_ALL_BODY APPEARANCE_ALL_HAIR|APPEARANCE_HEAD_ACCESSORY|APPEARANCE_MARKINGS|APPEARANCE_BODY_ACCESSORY|APPEARANCE_ALT_HEAD
#define APPEARANCE_ALL 32767
+//If you add a new status, be sure to add a list for it to the simple_animals global in _globalvars/lists/mobs.dm
+//Hostile Mob AI Status
+#define AI_ON 1
+#define AI_IDLE 2
+#define AI_OFF 3
+#define AI_Z_OFF 4
+
// Intents
#define INTENT_HELP "help"
#define INTENT_DISARM "disarm"
diff --git a/code/__DEFINES/snpc.dm b/code/__DEFINES/snpc.dm
deleted file mode 100644
index 8d4768005cd..00000000000
--- a/code/__DEFINES/snpc.dm
+++ /dev/null
@@ -1,23 +0,0 @@
-#define SNPC_MAX_RANGE_FIND 32
-#define SNPC_MIN_RANGE_FIND 16
-#define SNPC_FUZZY_CHANCE_HIGH 85
-#define SNPC_FUZZY_CHANCE_LOW 50
-#define SNPC_CHANCE_TALK 15
-
-#define SNPC_BRUTE 1
-#define SNPC_STEALTH 2
-#define SNPC_MARTYR 3
-#define SNPC_PSYCHO 4
-
-#define SNPC_INTERACTING 2
-#define SNPC_TRAVEL 4
-#define SNPC_FIGHTING 8
-#define SNPC_SPECIAL 16
-
-#define TRAIT_ROBUST 2
-#define TRAIT_UNROBUST 4
-#define TRAIT_SMART 8
-#define TRAIT_DUMB 16
-#define TRAIT_MEAN 32
-#define TRAIT_FRIENDLY 64
-#define TRAIT_THIEVING 128
\ No newline at end of file
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index f1e6f38a2d4..6b5807deaef 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -124,9 +124,13 @@
. += A
//Like typesof() or subtypesof(), but returns a typecache instead of a list
-/proc/typecacheof(path, ignore_root_path)
+/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
if(ispath(path))
- var/list/types = ignore_root_path ? subtypesof(path) : typesof(path)
+ var/list/types = list()
+ if(only_root_path)
+ types = list(path)
+ else
+ types = ignore_root_path ? subtypesof(path) : typesof(path)
var/list/L = list()
for(var/T in types)
L[T] = TRUE
@@ -140,8 +144,11 @@
L[T] = TRUE
else
for(var/P in pathlist)
- for(var/T in typesof(P))
- L[T] = TRUE
+ if(only_root_path)
+ L[P] = TRUE
+ else
+ for(var/T in typesof(P))
+ L[T] = TRUE
return L
//Removes any null entries from the list
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 551b729028e..b6fcae25061 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -22,8 +22,7 @@ GLOBAL_LIST_INIT(living_mob_list, list()) //List of all alive mobs, including
GLOBAL_LIST_INIT(dead_mob_list, list()) //List of all dead mobs, including clientless. Excludes /mob/new_player
GLOBAL_LIST_INIT(respawnable_list, list()) //List of all mobs, dead or in mindless creatures that still be respawned.
GLOBAL_LIST_INIT(non_respawnable_keys, list()) //List of ckeys that are excluded from respawning for remainder of round.
-GLOBAL_LIST_INIT(simple_animal_list, list()) //List of all simple animals, including clientless
-GLOBAL_LIST_INIT(snpc_list, list()) //List of all snpc's, including clientless
+GLOBAL_LIST_INIT(simple_animals, list(list(), list(), list(), list())) //One for each AI_* status define, List of all simple animals, including clientless
GLOBAL_LIST_INIT(bots_list, list()) //List of all bots(beepsky, medibots,etc)
GLOBAL_LIST_INIT(med_hud_users, list())
diff --git a/code/controllers/Processes/npcai.dm b/code/controllers/Processes/npcai.dm
deleted file mode 100644
index 127429efd34..00000000000
--- a/code/controllers/Processes/npcai.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-var/global/datum/controller/process/npcai/npcai_master
-
-/datum/controller/process/npcai
- var/current_cycle
-
-/datum/controller/process/npcai/setup()
- name = "npc ai"
- schedule_interval = 20 // every 2 seconds
- start_delay = 16
- log_startup_progress("NPC ticker starting up.")
-
-/datum/controller/process/npcai/started()
- ..()
- if(!GLOB.simple_animal_list)
- GLOB.simple_animal_list = list()
- if(!GLOB.snpc_list)
- GLOB.snpc_list = list()
-
-/datum/controller/process/npcai/statProcess()
- ..()
- stat(null, "[GLOB.simple_animal_list.len] simple animals")
- stat(null, "[GLOB.snpc_list.len] SNPC's")
-
-/datum/controller/process/npcai/doWork()
- for(last_object in GLOB.simple_animal_list)
- var/mob/living/simple_animal/M = last_object
- if(istype(M) && !QDELETED(M))
- if(!M.client && M.stat == CONSCIOUS)
- try
- M.process_ai()
- catch(var/exception/e)
- catchException(e, M)
- SCHECK
- else
- catchBadType(M)
- GLOB.simple_animal_list -= M
-
- current_cycle++
-
-
-DECLARE_GLOBAL_CONTROLLER(npcai, npcai_master)
diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm
new file mode 100644
index 00000000000..2dad0abca0f
--- /dev/null
+++ b/code/controllers/subsystem/idlenpcpool.dm
@@ -0,0 +1,41 @@
+SUBSYSTEM_DEF(idlenpcpool)
+ name = "Idling NPC Pool"
+ flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
+ priority = FIRE_PRIORITY_IDLE_NPC
+ wait = 60
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/list/currentrun = list()
+ var/static/list/idle_mobs_by_zlevel[][]
+
+/datum/controller/subsystem/idlenpcpool/stat_entry()
+ var/list/idlelist = GLOB.simple_animals[AI_IDLE]
+ var/list/zlist = GLOB.simple_animals[AI_Z_OFF]
+ ..("IdleNPCS:[idlelist.len]|Z:[zlist.len]")
+
+/datum/controller/subsystem/idlenpcpool/Initialize(start_timeofday)
+ idle_mobs_by_zlevel = new /list(world.maxz,0)
+ return ..()
+
+/datum/controller/subsystem/idlenpcpool/fire(resumed = FALSE)
+ if(!resumed)
+ var/list/idlelist = GLOB.simple_animals[AI_IDLE]
+ src.currentrun = idlelist.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(currentrun.len)
+ var/mob/living/simple_animal/SA = currentrun[currentrun.len]
+ --currentrun.len
+ if(!SA)
+ GLOB.simple_animals[AI_IDLE] -= SA
+ continue
+
+ if(!SA.ckey)
+ if(SA.stat != DEAD)
+ SA.handle_automated_movement()
+ if(SA.stat != DEAD)
+ SA.consider_wakeup()
+ if(MC_TICK_CHECK)
+ return
\ No newline at end of file
diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm
index d19396af767..20061d7489b 100644
--- a/code/controllers/subsystem/mobs.dm
+++ b/code/controllers/subsystem/mobs.dm
@@ -1,14 +1,19 @@
SUBSYSTEM_DEF(mobs)
name = "Mobs"
priority = FIRE_PRIORITY_MOBS
- flags = SS_KEEP_TIMING | SS_NO_INIT
+ flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
+ var/static/list/clients_by_zlevel[][]
/datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_list.len]")
+/datum/controller/subsystem/mobs/Initialize(start_timeofday)
+ clients_by_zlevel = new /list(world.maxz,0)
+ return ..()
+
/datum/controller/subsystem/mobs/fire(resumed = 0)
var/seconds = wait * 0.1
if(!resumed)
diff --git a/code/controllers/subsystem/npcai.dm b/code/controllers/subsystem/npcai.dm
deleted file mode 100644
index f8fa3a5aa0f..00000000000
--- a/code/controllers/subsystem/npcai.dm
+++ /dev/null
@@ -1,24 +0,0 @@
-SUBSYSTEM_DEF(npcai)
- name = "NPC AI" // Simple AI controller, isolated from the SNPC one (NPCPool).
- flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND
- priority = FIRE_PRIORITY_NPC
- runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
- var/list/simple_animal_list = list()
-
-/datum/controller/subsystem/npcai/stat_entry()
- ..("SimAnimals:[simple_animal_list.len]")
-
-/datum/controller/subsystem/npcai/fire(resumed = FALSE)
- if(!resumed)
- src.simple_animal_list = simple_animal_list.Copy()
- for(var/mob/living/simple_animal/M in simple_animal_list)
- if(istype(M) && !QDELETED(M))
- if(!M.client && M.stat == CONSCIOUS)
- M.process_ai()
- if(MC_TICK_CHECK)
- return
- else
- simple_animal_list -= M
-
-/datum/controller/subsystem/npcai/Recover()
- simple_animal_list = SSnpcai.simple_animal_list
\ No newline at end of file
diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm
index b06d33f6f32..0de306b12eb 100644
--- a/code/controllers/subsystem/npcpool.dm
+++ b/code/controllers/subsystem/npcpool.dm
@@ -1,127 +1,33 @@
-#define PROCESSING_NPCS 0
-#define PROCESSING_DELEGATES 1
-#define PROCESSING_ASSISTANTS 2
-
SUBSYSTEM_DEF(npcpool)
- name = "NPC Pool"
- flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND
- priority = FIRE_PRIORITY_NPC
- runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ name = "NPC Pool"
+ flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND
+ priority = FIRE_PRIORITY_NPC
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
- var/list/canBeUsed = list()
- var/list/needsDelegate = list()
- var/list/needsAssistant = list()
-
- var/list/processing = list()
- var/list/currentrun = list()
- var/stage
+ var/list/currentrun = list()
/datum/controller/subsystem/npcpool/stat_entry()
- ..("NPCS:[processing.len]|D:[needsDelegate.len]|A:[needsAssistant.len]|U:[canBeUsed.len]")
-
-/datum/controller/subsystem/npcpool/proc/stop_processing(mob/living/carbon/human/interactive/I)
- processing -= I
- currentrun -= I
- needsDelegate -= I
- canBeUsed -= I
- needsAssistant -= I
+ var/list/activelist = GLOB.simple_animals[AI_ON]
+ ..("SimpleAnimals:[activelist.len]")
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
- //bot delegation and coordination systems
- //General checklist/Tasks for delegating a task or coordinating it (for SNPCs)
- // 1. Bot proximity to task target: if too far, delegate, if close, coordinate
- // 2. Bot Health/status: check health with bots in local area, if their health is higher, delegate task to them, else coordinate
- // 3. Process delegation: if a bot (or bots) has been delegated, assign them to the task.
- // 4. Process coordination: if a bot(or bots) has been asked to coordinate, assign them to help.
- // 5. Do all assignments: goes through the delegated/coordianted bots and assigns the right variables/tasks to them.
- if (!resumed)
- currentrun = processing.Copy()
- stage = PROCESSING_NPCS
- //cache for sanic speed (lists are references anyways)
- var/list/cachecurrentrun = currentrun
- var/list/cachecanBeUsed = canBeUsed
+ if(!resumed)
+ var/list/activelist = GLOB.simple_animals[AI_ON]
+ src.currentrun = activelist.Copy()
- if(stage == PROCESSING_NPCS)
- while(cachecurrentrun.len)
- var/mob/living/carbon/human/interactive/thing = cachecurrentrun[cachecurrentrun.len]
- --cachecurrentrun.len
+ var/list/currentrun = src.currentrun
- thing.InteractiveProcess()
+ while(currentrun.len)
+ var/mob/living/simple_animal/SA = currentrun[currentrun.len]
+ --currentrun.len
- var/checkInRange = view(SNPC_MAX_RANGE_FIND,thing)
- if(thing.IsDeadOrIncap(FALSE) || !(locate(thing.TARGET) in checkInRange))
- needsDelegate += thing
- else if(thing.doing & SNPC_FIGHTING)
- needsAssistant += thing
- else
- cachecanBeUsed += thing
-
- if (MC_TICK_CHECK)
- return
- stage = PROCESSING_DELEGATES
- cachecurrentrun = needsDelegate //localcache
- currentrun = cachecurrentrun
-
- if(stage == PROCESSING_DELEGATES)
- while(cachecurrentrun.len && cachecanBeUsed.len)
- var/mob/living/carbon/human/interactive/check = cachecurrentrun[cachecurrentrun.len]
- var/mob/living/carbon/human/interactive/candidate = cachecanBeUsed[cachecanBeUsed.len]
- --cachecurrentrun.len
-
- var/helpProb = 0
- var/list/chfac = check.faction
- var/list/canfac = candidate.faction
- var/facCount = LAZYLEN(chfac) * LAZYLEN(canfac)
-
- for(var/C in chfac)
- if(C in canfac)
- helpProb = min(100,helpProb + 25)
- if(helpProb >= 100)
- break
-
- if(facCount == 1 && helpProb)
- helpProb = 100
-
- if(prob(helpProb) && candidate.takeDelegate(check))
- --cachecanBeUsed.len
- candidate.change_eye_color(255, 0, 0)
- candidate.update_icons()
-
- if(MC_TICK_CHECK)
- return
- stage = PROCESSING_ASSISTANTS
- cachecurrentrun = needsAssistant //localcache
- currentrun = cachecurrentrun
-
- //no need for the stage check
-
- while(cachecurrentrun.len && cachecanBeUsed.len)
- var/mob/living/carbon/human/interactive/check = cachecurrentrun[cachecurrentrun.len]
- var/mob/living/carbon/human/interactive/candidate = cachecanBeUsed[cachecanBeUsed.len]
- --cachecurrentrun.len
-
- var/helpProb = 0
- var/list/chfac = check.faction
- var/list/canfac = candidate.faction
- var/facCount = LAZYLEN(chfac) * LAZYLEN(canfac)
-
- for(var/C in chfac)
- if(C in canfac)
- helpProb = min(100,helpProb + 25)
- if(helpProb >= 100)
- break
-
- if(facCount == 1 && helpProb)
- helpProb = 100
-
- if(prob(helpProb) && candidate.takeDelegate(check,FALSE))
- --cachecanBeUsed.len
- candidate.change_eye_color(255, 255, 0)
- candidate.update_icons()
-
- if(!cachecurrentrun.len || MC_TICK_CHECK) //don't change SS state if it isn't necessary
- return
-
-/datum/controller/subsystem/npcpool/Recover()
- processing = SSnpcpool.processing
\ No newline at end of file
+ if(!SA.ckey && !SA.notransform)
+ if(SA.stat != DEAD)
+ SA.handle_automated_movement()
+ if(SA.stat != DEAD)
+ SA.handle_automated_action()
+ if(SA.stat != DEAD)
+ SA.handle_automated_speech()
+ if(MC_TICK_CHECK)
+ return
\ No newline at end of file
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index eeef65bd2e2..b23c9fe030a 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -80,9 +80,9 @@
if("Mob")
debug_variables(SSmobs)
feedback_add_details("admin_verb","DMob")
- if("NPC AI")
- debug_variables(SSnpcai)
- feedback_add_details("admin_verb","DNPCAI")
+ if("NPC Pool")
+ debug_variables(SSnpcpool)
+ feedback_add_details("admin_verb","DNPCPool")
if("Shuttle")
debug_variables(SSshuttle)
feedback_add_details("admin_verb","DShuttle")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 446511c1ee6..65ecf90c52a 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -107,6 +107,7 @@
/datum/action/item_action
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
var/use_itemicon = TRUE
+
/datum/action/item_action/New(Target, custom_icon, custom_icon_state)
..()
var/obj/item/I = target
@@ -413,6 +414,19 @@
name = "Use [target.name]"
button.name = name
+/datum/action/item_action/voice_changer/toggle
+ name = "Toggle Voice Changer"
+
+/datum/action/item_action/voice_changer/voice
+ name = "Set Voice"
+
+/datum/action/item_action/voice_changer/voice/Trigger()
+ if(!IsAvailable())
+ return FALSE
+
+ var/obj/item/voice_changer/V = target
+ V.set_voice(usr)
+
// for clothing accessories like holsters
/datum/action/item_action/accessory
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm
index 8c172f3592f..184f1155da2 100644
--- a/code/datums/outfits/outfit.dm
+++ b/code/datums/outfits/outfit.dm
@@ -25,6 +25,7 @@
var/list/implants = null
var/list/cybernetic_implants = null
+ var/list/chameleon_extras //extra types for chameleon outfit changes, mostly guns
/datum/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
//to be overriden for customization depending on client prefs,species etc
@@ -151,4 +152,10 @@
H.r_store.add_fingerprint(H, 1)
if(H.wear_pda)
H.wear_pda.add_fingerprint(H, 1)
- return 1
\ No newline at end of file
+ return 1
+
+/datum/outfit/proc/get_chameleon_disguise_info()
+ var/list/types = list(uniform, suit, back, belt, gloves, shoes, head, mask, l_ear, r_ear, glasses, id, l_pocket, r_pocket, suit_store, r_hand, l_hand, pda)
+ types += chameleon_extras
+ listclearnulls(types)
+ return types
diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm
index 3ac473edfeb..09282ec77b1 100644
--- a/code/datums/outfits/outfit_admin.dm
+++ b/code/datums/outfits/outfit_admin.dm
@@ -123,7 +123,7 @@
/datum/outfit/admin/syndicate/spy
name = "Syndicate Spy"
uniform = /obj/item/clothing/under/suit_jacket/really_black
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
uplink_uses = 40
id_access = "Syndicate Agent"
@@ -228,7 +228,7 @@
back = /obj/item/storage/backpack
belt = /obj/item/storage/belt/utility/full/multitool
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/centcom
id = /obj/item/card/id
pda = /obj/item/pda
@@ -990,7 +990,7 @@
suit = /obj/item/clothing/suit/hooded/chaplain_hoodie
back = /obj/item/storage/backpack
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/syndicate
id = /obj/item/card/id/syndicate
l_hand = /obj/item/twohanded/dualsaber/red
@@ -1021,7 +1021,7 @@
suit = /obj/item/clothing/suit/draculacoat
back = /obj/item/storage/backpack
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/syndicate
id = /obj/item/card/id
backpack_contents = list(
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index d1377fef558..3a78dfc9b28 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -1067,13 +1067,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_tools
category = "Stealth and Camouflage Items"
-/datum/uplink_item/stealthy_tools/chameleon_jumpsuit
- name = "Chameleon Jumpsuit"
- desc = "A jumpsuit used to imitate the uniforms of Nanotrasen crewmembers."
- reference = "CJ"
- item = /obj/item/clothing/under/chameleon
- cost = 2
-
/datum/uplink_item/stealthy_tools/chameleon_stamp
name = "Chameleon Stamp"
desc = "A stamp that can be activated to imitate an official Nanotrasen Stamp. The disguised stamp will work exactly like the real stamp and will allow you to forge false documents to gain access or equipment; \
@@ -1092,20 +1085,19 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
surplus = 35
/datum/uplink_item/stealthy_tools/syndigaloshes
- name = "No-Slip Syndicate Shoes"
- desc = "These allow you to run on wet floors. They do not work on lubricated surfaces."
+ name = "No-Slip Chameleon Shoes"
+ desc = "These shoes will allow the wearer to run on wet floors and slippery objects without falling down. \
+ They do not work on heavily lubricated surfaces."
reference = "NSSS"
- item = /obj/item/clothing/shoes/syndigaloshes
+ item = /obj/item/clothing/shoes/chameleon/noslip
cost = 2
excludefrom = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_tools/syndigaloshes/nuke
- name = "Tactical No-Slip Brown Shoes"
- desc = "These allow you to run on wet floors. They do not work on lubricated surfaces, and the maker swears they're better than normal ones, somehow."
- reference = "NNSSS"
+ reference = "TNSSS"
cost = 4 //but they aren't
- gamemodes = list(/datum/game_mode/nuclear)
excludefrom = list()
+ gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_tools/chamsechud
name = "Chameleon Security HUD"
@@ -1146,12 +1138,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/card/id/syndicate
cost = 2
-/datum/uplink_item/stealthy_tools/voice_changer
- name = "Voice Changer"
- desc = "A conspicuous gas mask that mimics the voice named on your identification card. When no identification is worn, the mask will render your voice unrecognisable."
- reference = "VC"
- item = /obj/item/clothing/mask/gas/voice
- cost = 3
+/datum/uplink_item/stealthy_tools/chameleon
+ name = "Chameleon Kit"
+ desc = "A set of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more! \
+ Due to budget cuts, the shoes don't provide protection against slipping. The set comes with a complementary chameleon stamp."
+ reference = "CHAM"
+ item = /obj/item/storage/box/syndie_kit/chameleon
+ cost = 2
/datum/uplink_item/stealthy_tools/chameleon_proj
name = "Chameleon-Projector"
@@ -1210,7 +1203,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_tools/clownkit
name = "Honk Brand Infiltration Kit"
- desc = "All the tools you need to play the best prank Nanotrasen has ever seen. Includes a voice changer clown mask, magnetic clown shoes, and standard clown outfit, tools, and backpack."
+ desc = "All the tools you need to play the best prank Nanotrasen has ever seen. Includes a voice changer mask, magnetic clown shoes, and standard clown outfit, tools, and backpack."
reference = "HBIK"
item = /obj/item/storage/backpack/clown/syndie
cost = 6
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 37d8310ba95..58f51d59e3c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -219,7 +219,12 @@
destination.Entered(src)
for(var/atom/movable/AM in destination)
AM.Crossed(src)
-
+ var/turf/oldturf = get_turf(old_loc)
+ var/turf/destturf = get_turf(destination)
+ var/old_z = (oldturf ? oldturf.z : null)
+ var/dest_z = (destturf ? destturf.z : null)
+ if(old_z != dest_z)
+ onTransitZ(old_z, dest_z)
if(isturf(destination) && opacity)
var/turf/new_loc = destination
new_loc.reconsider_lights()
@@ -232,6 +237,11 @@
return 1
+/atom/movable/proc/onTransitZ(old_z,new_z)
+ for(var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care.
+ var/atom/movable/AM = item
+ AM.onTransitZ(old_z,new_z)
+
/mob/living/forceMove(atom/destination)
if(buckled)
addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE)
diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm
index bd934eefb44..0482a8e19ae 100644
--- a/code/game/gamemodes/blob/blobs/core.dm
+++ b/code/game/gamemodes/blob/blobs/core.dm
@@ -135,4 +135,9 @@
else
log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.")
else
- log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.")
\ No newline at end of file
+ log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.")
+
+/obj/structure/blob/core/onTransitZ(old_z, new_z)
+ if(overmind && is_station_level(new_z))
+ overmind.forceMove(get_turf(src))
+ return ..()
\ No newline at end of file
diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm
index fbe1774525f..2b0162829e1 100644
--- a/code/game/gamemodes/miniantags/abduction/gland.dm
+++ b/code/game/gamemodes/miniantags/abduction/gland.dm
@@ -320,69 +320,6 @@
for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it.
H.add_mob_blood(owner)
-/obj/item/organ/internal/heart/gland/bodysnatch
- cooldown_low = 600
- cooldown_high = 600
- human_only = 1
- uses = 1
-
-/obj/item/organ/internal/heart/gland/bodysnatch/activate()
- to_chat(owner, "You feel something moving around inside you...")
- //spawn cocoon with clone greytide snpc inside
- if(ishuman(owner))
- var/obj/effect/cocoon/abductor/C = new (get_turf(owner))
- C.Copy(owner)
- C.Start()
- owner.adjustBruteLoss(40)
- owner.add_splatter_floor()
-
-/obj/effect/cocoon/abductor
- name = "slimy cocoon"
- desc = "Something is moving inside."
- icon = 'icons/effects/effects.dmi'
- icon_state = "cocoon_large3"
- color = rgb(10,120,10)
- density = 1
- var/hatch_time = 0
-
-/obj/effect/cocoon/abductor/proc/Copy(mob/living/carbon/human/H)
- var/mob/living/carbon/human/interactive/greytide/clone = new(src)
- var/datum/dna/owner_dna = H.dna
- clone.rename_character(clone.name, owner_dna.real_name)
- clone.set_species(owner_dna.species.type)
- clone.dna = owner_dna.Clone()
- clone.body_accessory = H.body_accessory
- domutcheck(clone)
-
- for(var/obj/item/I in clone)
- if(istype(I, /obj/item/implant))
- continue
- if(istype(I, /obj/item/organ))
- continue
- qdel(I)
-
- //There's no define for this / get all items ?
- var/list/slots = list(slot_back,slot_w_uniform,slot_wear_suit,\
- slot_wear_mask,slot_head,slot_shoes,slot_gloves,slot_l_ear,slot_r_ear,\
- slot_glasses,slot_belt,slot_s_store,slot_l_store,slot_r_store,slot_wear_id,slot_wear_pda)
-
- for(var/slot in slots)
- var/obj/item/I = H.get_item_by_slot(slot)
- if(I)
- clone.equip_to_slot_or_del(new I.type(clone), slot)
-
-/obj/effect/cocoon/abductor/proc/Start()
- hatch_time = world.time + 600
- START_PROCESSING(SSobj, src)
-
-/obj/effect/cocoon/abductor/process()
- if(world.time > hatch_time)
- STOP_PROCESSING(SSobj, src)
- for(var/mob/M in contents)
- src.visible_message("[src] hatches!")
- M.forceMove(get_turf(src))
- qdel(src)
-
/obj/item/organ/internal/heart/gland/plasma
cooldown_low = 1200
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index b57fa0d78ad..bf64df2752c 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -741,6 +741,8 @@
throw_item.throw_at(target, 16, 3, src)
visible_message("[src] launches [throw_item.name] at [target.name]!")
+/obj/machinery/vending/onTransitZ()
+ return
/*
* Vending machine types
*/
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 057c52a2a4c..b7311519f7a 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -140,8 +140,8 @@
/obj/item/suppressor = 4,
/obj/item/clothing/under/chameleon = 2,
/obj/item/stamp/chameleon = 2,
- /obj/item/clothing/shoes/syndigaloshes = 5,
- /obj/item/clothing/mask/gas/voice = 2,
+ /obj/item/clothing/shoes/chameleon/noslip = 5,
+ /obj/item/clothing/mask/chameleon = 2,
/obj/item/dnascrambler = 1,
/obj/item/storage/backpack/satchel_flat = 2,
/obj/item/storage/toolbox/syndicate = 2,
diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/random_spawners.dm
index 510e5b90380..03e49c56a50 100644
--- a/code/game/objects/effects/spawners/random_spawners.dm
+++ b/code/game/objects/effects/spawners/random_spawners.dm
@@ -214,7 +214,7 @@
/obj/item/storage/pill_bottle/zoom = 1,
/obj/item/storage/pill_bottle/random_drug_bottle = 2,
/obj/item/storage/backpack/duffel/syndie/surgery = 1,
- /obj/item/clothing/shoes/syndigaloshes = 1,
+ /obj/item/clothing/shoes/chameleon/noslip = 1,
/obj/item/storage/belt/military = 1,
/obj/item/clothing/under/chameleon = 1,
/obj/item/storage/backpack/satchel_flat = 1,
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 08f3802623b..23509a06f63 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -588,12 +588,6 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/attack_hulk(mob/living/carbon/human/user)
return FALSE
-/obj/item/proc/update_slot_icon()
- if(!ismob(loc))
- return
- var/mob/owner = loc
- owner.regenerate_icons()
-
/obj/item/proc/openTip(location, control, params, user)
openToolTip(user, src, params, title = name, content = "[desc]", theme = "")
@@ -606,3 +600,9 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/MouseExited()
deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
+
+/obj/item/proc/update_slot_icon()
+ if(!ismob(loc))
+ return
+ var/mob/owner = loc
+ owner.regenerate_icons()
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 59a7a650383..941e90a31ef 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -35,7 +35,7 @@
return
if(H.mind)
span = H.mind.speech_span
- if((COMIC in H.mutations) || H.get_int_organ(/obj/item/organ/internal/cyberimp/brain/clown_voice) || istype(H.get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown))
+ if((COMIC in H.mutations) || H.get_int_organ(/obj/item/organ/internal/cyberimp/brain/clown_voice))
span = "sans"
if(spamcheck)
to_chat(user, "\The [src] needs to recharge!")
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 83a8c9304a1..00efc83668b 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -397,7 +397,6 @@ var/global/list/default_medbay_channels = list(
var/mob/living/carbon/human/H = M
displayname = H.voice
if(H.voice != real_name)
- jobname = "Unknown"
voicemask = TRUE
if(syndiekey && syndiekey.change_voice && connection.frequency == SYND_FREQ)
diff --git a/code/game/objects/items/devices/voice.dm b/code/game/objects/items/devices/voice.dm
new file mode 100644
index 00000000000..339271cdcca
--- /dev/null
+++ b/code/game/objects/items/devices/voice.dm
@@ -0,0 +1,44 @@
+/obj/item/voice_changer
+ name = "voice changer"
+ desc = "A voice scrambling module."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "voice_changer_off"
+
+ actions_types = list(/datum/action/item_action/voice_changer/toggle, /datum/action/item_action/voice_changer/voice)
+
+ var/obj/item/parent
+
+ var/voice
+ var/active
+
+/obj/item/voice_changer/New()
+ . = ..()
+
+ if(isitem(loc))
+ parent = loc
+ parent.actions |= actions
+
+/obj/item/voice_changer/Destroy()
+ if(isitem(parent))
+ parent.actions -= actions
+
+ return ..()
+
+/obj/item/voice_changer/attack_self(mob/user)
+ active = !active
+ icon_state = "voice_changer_[active ? "on" : "off"]"
+ to_chat(user, "You toggle [src] [active ? "on" : "off"].")
+
+ for(var/X in actions)
+ var/datum/action/A = X
+ A.UpdateButtonIcon()
+
+/obj/item/voice_changer/proc/set_voice(mob/user)
+ var/chosen_voice = input(user, "What voice would you like to mimic? Leave this empty to use the voice on your ID card.", "Set Voice Changer", voice) as text
+ if(!chosen_voice)
+ voice = null
+ to_chat(user, "You are now mimicking the voice on your ID card.")
+ return
+
+ voice = sanitize(copytext(chosen_voice, 1, MAX_MESSAGE_LEN))
+ to_chat(user, "You are now mimicking [voice].")
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index d2f2ed2c54d..c7627688b5e 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -668,4 +668,27 @@
/obj/item/dnainjector/m2h/Initialize()
block = MONKEYBLOCK
+ ..()
+
+
+/obj/item/dnainjector/comic
+ name = "DNA-Injector (Comic)"
+ desc = "Honk!"
+ datatype = DNA2_BUF_SE
+ value = 0xFFF
+ forcedmutation = TRUE
+
+/obj/item/dnainjector/comic/Initialize()
+ block = COMICBLOCK
+ ..()
+
+/obj/item/dnainjector/anticomic
+ name = "DNA-Injector (Ant-Comic)"
+ desc = "Honk...?"
+ datatype = DNA2_BUF_SE
+ value = 0x001
+ forcedmutation = TRUE
+
+/obj/item/dnainjector/anticomic/Initialize()
+ block = COMICBLOCK
..()
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 66efcfa4669..2b767d11db1 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -112,7 +112,7 @@
..()
new /obj/item/clothing/under/rank/clown(src)
new /obj/item/clothing/shoes/magboots/clown(src)
- new /obj/item/clothing/mask/gas/voice/clown(src)
+ new /obj/item/clothing/mask/chameleon(src)
new /obj/item/radio/headset/headset_service(src)
new /obj/item/pda/clown(src)
new /obj/item/storage/box/survival(src)
@@ -124,6 +124,7 @@
new /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana(src)
new /obj/item/instrument/bikehorn(src)
new /obj/item/bikehorn(src)
+ new /obj/item/dnainjector/comic(src)
/obj/item/storage/backpack/mime
name = "Parcel Parceaux"
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index ecf1acd701e..56db11ad058 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -5,7 +5,7 @@
if("bloodyspai") // 28TC
new /obj/item/twohanded/garrote(src)
new /obj/item/pinpointer/advpinpointer(src)
- new /obj/item/clothing/mask/gas/voice(src)
+ new /obj/item/clothing/mask/chameleon(src)
new /obj/item/clothing/under/chameleon(src)
new /obj/item/card/id/syndicate(src)
new /obj/item/flashlight/emp(src)
@@ -19,7 +19,7 @@
new /obj/item/chameleon(src)
new /obj/item/clothing/gloves/color/black/thief(src)
new /obj/item/card/id/syndicate(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
new /obj/item/storage/box/syndie_kit/safecracking(src)
return
@@ -83,14 +83,14 @@
new /obj/item/dnainjector/telemut/darkbundle(src)
new /obj/item/clothing/suit/hooded/chaplain_hoodie(src)
new /obj/item/card/id/syndicate(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
- new /obj/item/clothing/mask/gas/voice(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
+ new /obj/item/clothing/mask/chameleon(src)
return
if("gadgets") // 30TC
new /obj/item/clothing/gloves/color/yellow/power(src)
new /obj/item/pen/sleepy(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
new /obj/item/clothing/glasses/thermal/syndi(src)
new /obj/item/flashlight/emp(src)
new /obj/item/stamp/chameleon(src)
@@ -340,4 +340,21 @@ To apply, hold the injector a short distance away from the outer thigh before ap
new /obj/item/clothing/gloves/color/latex/nitrile(src)
new /obj/item/clothing/mask/balaclava(src)
new /obj/item/clothing/accessory/stethoscope(src)
- new /obj/item/book/manual/engineering_hacking(src)
\ No newline at end of file
+ new /obj/item/book/manual/engineering_hacking(src)
+
+/obj/item/storage/box/syndie_kit/chameleon
+ name = "chameleon kit"
+
+/obj/item/storage/box/syndie_kit/chameleon/New()
+ ..()
+ new /obj/item/clothing/under/chameleon(src)
+ new /obj/item/clothing/suit/chameleon(src)
+ new /obj/item/clothing/gloves/chameleon(src)
+ new /obj/item/clothing/shoes/chameleon(src)
+ new /obj/item/clothing/glasses/chameleon(src)
+ new /obj/item/clothing/head/chameleon(src)
+ new /obj/item/clothing/mask/chameleon(src)
+ new /obj/item/storage/backpack/chameleon(src)
+ new /obj/item/radio/headset/chameleon(src)
+ new /obj/item/stamp/chameleon(src)
+ new /obj/item/pda/chameleon(src)
\ No newline at end of file
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 6b4072a800b..4a455cd8e5d 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -72,7 +72,6 @@ var/list/admin_verbs_admin = list(
/client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */
/client/proc/change_human_appearance_self, /* Allows the human-based mob itself change its basic appearance */
/client/proc/debug_variables,
- /client/proc/show_snpc_verbs,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
/client/proc/toggle_mentor_chat,
/client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/
@@ -216,11 +215,6 @@ var/list/admin_verbs_proccall = list(
/client/proc/callproc_datum,
/client/proc/SDQL2_query
)
-var/list/admin_verbs_snpc = list(
- /client/proc/resetSNPC,
- /client/proc/customiseSNPC,
- /client/proc/hide_snpc_verbs
-)
var/list/admin_verbs_ticket = list(
/client/proc/openAdminTicketUI,
/client/proc/toggleticketlogs,
@@ -291,8 +285,6 @@ var/list/admin_verbs_ticket = list(
admin_verbs_proccall,
admin_verbs_show_debug_verbs,
/client/proc/readmin,
- admin_verbs_snpc,
- /client/proc/hide_snpc_verbs,
admin_verbs_ticket
)
@@ -1013,28 +1005,6 @@ var/list/admin_verbs_ticket = list(
log_admin("[key_name(usr)] told everyone to man up and deal with it.")
message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.")
-/client/proc/show_snpc_verbs()
- set name = "Show SNPC Verbs"
- set category = "Admin"
-
- if(!check_rights(R_ADMIN))
- return
-
- verbs += admin_verbs_snpc
- verbs -= /client/proc/show_snpc_verbs
- to_chat(src, "SNPC verbs have been toggled on.")
-
-/client/proc/hide_snpc_verbs()
- set name = "Hide SNPC Verbs"
- set category = "Admin"
-
- if(!check_rights(R_ADMIN))
- return
-
- verbs -= admin_verbs_snpc
- verbs += /client/proc/show_snpc_verbs
- to_chat(src, "SNPC verbs have been toggled off.")
-
/client/proc/toggle_advanced_interaction()
set name = "Toggle Advanced Admin Interaction"
set category = "Admin"
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 36c0e5cf90d..970defdd672 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -390,10 +390,7 @@
/datum/admins/proc/check_antagonists_line(mob/M, caption = "", close = 1)
var/logout_status
- if(istype(M, /mob/living/carbon/human/interactive))
- logout_status = " (snpc)"
- else
- logout_status = M.client ? "" : " (logged out)"
+ logout_status = M.client ? "" : " (logged out)"
var/dname = M.real_name
if(!dname)
dname = M
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 551076be62a..4597a838733 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -170,7 +170,7 @@ var/global/sent_syndicate_infiltration_team = 0
equip_or_collect(new /obj/item/pda(src), slot_in_backpack)
// Other gear
- equip_to_slot_or_del(new /obj/item/clothing/shoes/syndigaloshes(src), slot_shoes)
+ equip_to_slot_or_del(new /obj/item/clothing/shoes/chameleon/noslip(src), slot_shoes)
var/obj/item/card/id/syndicate/W = new(src)
if (flag_mgmt)
diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm
index 0328dea5b53..fc8b4f4d260 100644
--- a/code/modules/awaymissions/mission_code/spacehotel.dm
+++ b/code/modules/awaymissions/mission_code/spacehotel.dm
@@ -152,14 +152,6 @@
/obj/machinery/door/unpowered/hotel_door/autoclose()
if(!density && !operating && autoclose)
close()
-
-/obj/machinery/door/unpowered/hotel_door/emag_act(mob/user)
- if(isliving(user) && density)
- var/obj/effect/hotel_controller/H
- if(H.controller)
- H.controller.deploy_sec(user)
- ..()
-
/obj/item/card/hotel_card
name = "Key Card"
icon_state = "guest"
@@ -283,20 +275,4 @@
spawn(300)
if(D.occupant == deadbeat)
// they still haven't checked out...
- deploy_sec(deadbeat)
- checkout(roomid)
-
-/obj/effect/hotel_controller/proc/deploy_sec(mob/living/target)
- if(!istype(target) || !istype(get_area(target), /area/awaymission/spacehotel))
- return
-
- var/list/secs[0]
- for(var/mob/living/carbon/human/interactive/away/hotel/guard/M in get_area(src))
- if(!M.retal)
- secs += M
- var/mob/living/carbon/human/interactive/away/hotel/guard/S = safepick(secs)
- if(!S)
- return
-
- S.retal_target = target
- S.retal = 1
+ checkout(roomid)
\ No newline at end of file
diff --git a/code/modules/awaymissions/mission_code/spacehotel_npcs.dm b/code/modules/awaymissions/mission_code/spacehotel_npcs.dm
deleted file mode 100644
index 2a524ab738c..00000000000
--- a/code/modules/awaymissions/mission_code/spacehotel_npcs.dm
+++ /dev/null
@@ -1,132 +0,0 @@
-/mob/living/carbon/human/interactive/away/hotel
- away_area = /area/awaymission/spacehotel
- override_under = /obj/item/clothing/under/mafia
- chattyness = SNPC_CHANCE_TALK / 4
-
-/mob/living/carbon/human/interactive/away/hotel/Initialize(mapload)
- . = ..(mapload, /datum/species/skrell)
-
-/mob/living/carbon/human/interactive/away/hotel/doSetup()
- ..()
- MYID.access = list(access_syndicate)
- RPID.access = list(access_syndicate)
-
-/mob/living/carbon/human/interactive/away/hotel/guard
- default_job = /datum/job/officer
-
-/mob/living/carbon/human/interactive/away/hotel/guard/doSetup()
- ..("Guard")
-
- // anti-pinata cheese
- var/obj/item/implant/dust/D = new /obj/item/implant/dust(src)
- D.implant(src)
-
- for(var/obj/item/I in get_all_slots())
- I.flags |= NODROP
-
-/mob/living/carbon/human/interactive/away/hotel/guard/KnockOut()
- // you'll never take me alive (this triggers the implant)
- emote("deathgasp")
- if(stat != DEAD)
- // mission failed. we'll get em next time
- ..()
-
-/mob/living/carbon/human/interactive/away/hotel/chef
- default_job = /datum/job/chef
- away_area = /area/awaymission/spacehotel/kitchen
- override_under = /obj/item/clothing/under/mafia/vest
-
-/mob/living/carbon/human/interactive/away/hotel/bartender
- default_job = /datum/job/bartender
- override_under = /obj/item/clothing/under/mafia/vest
-
-/mob/living/carbon/human/interactive/away/hotel/concierge
- override_under = /obj/item/clothing/under/mafia/white
- away_area = /area/awaymission/spacehotel/reception
- var/list/known_guests[0]
- var/obj/effect/hotel_controller/hotel
- var/obj/item/card/id/last_seen_id = null
-
-/mob/living/carbon/human/interactive/away/hotel/concierge/random()
- ..()
- equip_or_collect(new /obj/item/clipboard(src), slot_l_hand)
-
-/mob/living/carbon/human/interactive/away/hotel/concierge/doSetup()
- ..("Concierge")
-
-/mob/living/carbon/human/interactive/away/hotel/concierge/setup_job()
- favoured_types = list(/obj/item/paper, /obj/item/clipboard)
- functions += list("stamping", "concierge")
- restrictedJob = 1
-
-/mob/living/carbon/human/interactive/away/hotel/concierge/proc/concierge()
- if(!hotel)
- hotel = hotel.controller
- if(!hotel)
- return
-
- var/verbs_use = pick_list(speak_file, "verbs_use")
- var/verbs_touch = pick_list(speak_file, "verbs_touch")
- var/verbs_move = pick_list(speak_file, "verbs_move")
- var/nouns_generic = pick_list(speak_file, "nouns_generic")
- var/adjective_insult = pick_list(speak_file, "adjective_insult")
- var/adjective_objects = pick_list(speak_file, "adjective_objects")
- var/adjective_generic = pick_list(speak_file, "adjective_generic")
- var/curse_words = pick_list(speak_file, "curse_words")
-
- var/found = 0
- for(var/mob/living/carbon/human/H in nearby - known_guests)
- pointed(H)
- known_guests += H
- found = 1
- if(found)
- say("Welcome to [hotel], [nouns_generic]! Please check in by [ing_verb(verbs_move)] and [ing_verb(verbs_use)] your [adjective_objects] ID onto the table.")
- return
-
- var/obj/item/card/id
- var/id_seen = 0
- for(var/obj/item/card/id/I in get_area(src))
- id_seen = 1
- if(I != last_seen_id)
- id = I
- if(!id_seen)
- last_seen_id = null
- return
- if(!id)
- return
- var/turf/idloc = get_turf(id)
- if(!Adjacent(idloc))
- tryWalk(idloc)
- else
- // is the last jerk that touched it over here?
- var/mob/id_owner
- for(var/mob/living/carbon/human/H in nearby)
- if(H.ckey == id.fingerprintslast)
- id_owner = H
- break
-
- if(!id_owner)
- say("Whose card is this?")
- pointed(id)
- return
-
- last_seen_id = id
- // Checking in or out?
- if(id_owner in hotel.guests)
- // Check out
- say ("Hope you enjoyed your [adjective_objects] stay at our [adjective_generic] hotel!")
- hotel.checkout(hotel.guests[id_owner])
- else
- // pick a room
- var/obj/machinery/door/unpowered/hotel_door/D = safepick(hotel.vacant_rooms)
- if(!D)
- say("Sorry, all the [adjective_objects] are occupied currently.")
- else
- // Check in
- say("$100 per 10 minutes. The cost will be automatically [past_verb(verbs_touch)] while you're checked in.")
-
- var/obj/item/card/hotel_card/K = hotel.checkin(D.id, id_owner, id)
- if(K)
- K.forceMove(idloc)
- else
- say("Your [adjective_insult] [curse_words] card was rejected.")
diff --git a/code/modules/awaymissions/snpc.dm b/code/modules/awaymissions/snpc.dm
deleted file mode 100644
index 8d65aa44a92..00000000000
--- a/code/modules/awaymissions/snpc.dm
+++ /dev/null
@@ -1,85 +0,0 @@
-/mob/living/carbon/human/interactive/away
- var/away_area = /area/awaymission // their overriden job area
- var/override_under = null // optional type for uniform, else default for job
- var/squad_member = 0 // was spawned by squad
- var/home_z
-
-/mob/living/carbon/human/interactive/away/Initialize(mapload)
- . = ..()
- TRAITS |= TRAIT_ROBUST
- faction += "away"
-
-/mob/living/carbon/human/interactive/away/random()
- ..()
- // a little hacky but it should prevent doubled uniforms
- if(ispath(override_under, /obj/item/clothing/under))
- var/old_under = w_uniform
- w_uniform = null
- equip_to_slot(new override_under(src), slot_w_uniform)
- qdel(old_under)
-
-/mob/living/carbon/human/interactive/away/doSetup()
- ..()
- var/datum/data/pda/app/messenger/M = MYPDA.find_program(/datum/data/pda/app/messenger)
- M.toff = 1
-
-/mob/living/carbon/human/interactive/away/job2area()
- return away_area
-
-/mob/living/carbon/human/interactive/away/doProcess()
- if(!home_z)
- home_z = get_turf(z)
- if(home_z != get_turf(z))
- gib()
- ..()
-
-/obj/effect/spawner/snpc_squad
- name = "squad spawner"
- icon = 'icons/mob/screen_gen.dmi'
- icon_state = "x"
- density = 0
- opacity = 0
- invisibility = 101
- var/squad_type = /mob/living/carbon/human/interactive/away
- var/squad_size = 3
-
- var/global/list/squads[0]
- var/list/squad
-
-/obj/effect/spawner/snpc_squad/New()
- START_PROCESSING(SSobj, src)
-
- squad = squads[squad_type]
- if(!squad)
- squad = list()
- squads[squad_type] = squad
- squad += src
-
-/obj/effect/spawner/snpc_squad/Destroy()
- squad = null
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/effect/spawner/snpc_squad/process()
- // check squad memebers
- var/living = 0
- for(var/mob/living/carbon/human/interactive/away/A in squad)
- if(!A.stat)
- living++
- else
- // see if anyone's looking, if not, despawn
- var/can_despawn = 1
- for(var/mob/living/M in viewers(A, world.view + 1))
- if(M.client)
- living++ // dead guy that can be seen still takes up a slot
- can_despawn = 0
- break
- if(can_despawn)
- squad -= A
- qdel(A)
-
- // spawn new ones
- if(living < squad_size && !length(viewers(src, world.view)))
- var/mob/living/carbon/human/interactive/away/A = new squad_type(loc)
- squad += A
- A.squad_member = 1
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 97d6d2feaed..0ec1202d0a6 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -597,8 +597,9 @@ BLIND // can't see anything
"Drask" = 'icons/mob/species/drask/uniform.dmi',
"Grey" = 'icons/mob/species/grey/uniform.dmi'
)
- var/has_sensor = 1//For the crew computer 2 = unable to change mode
- var/sensor_mode = 0
+ var/has_sensor = TRUE//For the crew computer 2 = unable to change mode
+ var/sensor_mode = SENSOR_OFF
+ var/random_sensor = TRUE
/*
1 = Report living/dead
2 = Report detailed damages
@@ -610,7 +611,8 @@ BLIND // can't see anything
var/basecolor
/obj/item/clothing/under/rank/New()
- sensor_mode = pick(0,1,2,3)
+ if(random_sensor)
+ sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS)
..()
/obj/item/clothing/under/Destroy()
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 70c2795690f..8d4bafb424d 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -181,11 +181,6 @@
item_state = "sexymime"
burn_state = FLAMMABLE
-/obj/item/clothing/mask/gas/death_commando
- name = "Death Commando Mask"
- icon_state = "death_commando_mask"
- item_state = "death_commando_mask"
-
/obj/item/clothing/mask/gas/cyborg
name = "cyborg visor"
desc = "Beep boop"
diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm
deleted file mode 100644
index b5b6298dcb2..00000000000
--- a/code/modules/clothing/masks/voice.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/obj/item/voice_changer
- name = "voice changer"
- desc = "A voice scrambling module. If you can see this, report it as a bug on the tracker."
- var/voice //If set and item is present in mask/suit, this name will be used for the wearer's speech.
- var/active
-
-/obj/item/clothing/mask/gas/voice
- name = "gas mask"
-// desc = "A face-covering mask that can be connected to an air supply. It seems to house some odd electronics."
- var/obj/item/voice_changer/changer
- burn_state = FIRE_PROOF
-
-/obj/item/clothing/mask/gas/voice/verb/Toggle_Voice_Changer()
- set category = "Object"
- set src in usr
-
- changer.active = !changer.active
- to_chat(usr, "You [changer.active ? "enable" : "disable"] the voice-changing module in \the [src].")
-
-/obj/item/clothing/mask/gas/voice/verb/Set_Voice(name as text)
- set category = "Object"
- set src in usr
-
- var/voice = sanitize(copytext(name,1,MAX_MESSAGE_LEN))
- if(!voice || !length(voice)) return
- changer.voice = voice
- to_chat(usr, "You are now mimicking [changer.voice].")
-
-/obj/item/clothing/mask/gas/voice/New()
- ..()
- changer = new(src)
-
-/obj/item/clothing/mask/gas/voice/clown
- name = "clown wig and mask"
- desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask."
- icon_state = "clown"
- item_state = "clown_hat"
- flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR
\ No newline at end of file
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 3f1d6746f71..f9455e7a0be 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -1,21 +1,3 @@
-/obj/item/clothing/shoes/syndigaloshes
- desc = "A pair of brown shoes. They seem to have extra grip."
- name = "brown shoes"
- icon_state = "brown"
- item_state = "brown"
- permeability_coefficient = 0.05
- flags = NOSLIP
- origin_tech = "syndicate=2"
- burn_state = FIRE_PROOF
- var/list/clothing_choices = list()
- silence_steps = 1
-
-/obj/item/clothing/shoes/syndigaloshes/black
- name = "black shoes"
- icon_state = "black"
- item_color = "black"
- desc = "A pair of black shoes. They seem to have extra grip."
-
/obj/item/clothing/shoes/mime
name = "mime shoes"
icon_state = "mime"
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
index aa62763a6d3..bfb326eae1d 100644
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm
@@ -269,14 +269,13 @@
/obj/item/rig_module/voice/New()
..()
voice_holder = new(src)
- voice_holder.active = 0
+ voice_holder.active = FALSE
/obj/item/rig_module/voice/installed()
..()
holder.speech = src
/obj/item/rig_module/voice/engage()
-
if(!..())
return 0
@@ -287,17 +286,17 @@
switch(choice)
if("Enable")
- active = 1
- voice_holder.active = 1
+ active = TRUE
+ voice_holder.active = TRUE
to_chat(usr, "You enable the speech synthesiser.")
if("Disable")
- active = 0
- voice_holder.active = 0
+ active = FALSE
+ voice_holder.active = FALSE
to_chat(usr, "You disable the speech synthesiser.")
if("Set Name")
var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN)
if(!raw_choice)
- return 0
+ return FALSE
voice_holder.voice = raw_choice
to_chat(usr, "You are now mimicking [voice_holder.voice].")
return 1
diff --git a/code/modules/clothing/under/chameleon.dm b/code/modules/clothing/under/chameleon.dm
index b320237996e..b08a1ce72c9 100644
--- a/code/modules/clothing/under/chameleon.dm
+++ b/code/modules/clothing/under/chameleon.dm
@@ -1,86 +1,478 @@
+#define EMP_RANDOMISE_TIME 300
+
+/datum/action/chameleon_outfit
+ name = "Select Chameleon Outfit"
+ button_icon_state = "chameleon_outfit"
+ var/list/outfit_options //By default, this list is shared between all instances. It is not static because if it were, subtypes would not be able to have their own. If you ever want to edit it, copy it first.
+
+/datum/action/chameleon_outfit/New()
+ ..()
+ initialize_outfits()
+
+/datum/action/chameleon_outfit/proc/initialize_outfits()
+ var/static/list/standard_outfit_options
+ if(!standard_outfit_options)
+ standard_outfit_options = list()
+ for(var/path in subtypesof(/datum/outfit/job))
+ var/datum/outfit/O = path
+ standard_outfit_options[initial(O.name)] = path
+ sortTim(standard_outfit_options, /proc/cmp_text_asc)
+ outfit_options = standard_outfit_options
+
+/datum/action/chameleon_outfit/Trigger()
+ return select_outfit(owner)
+
+/datum/action/chameleon_outfit/proc/select_outfit(mob/user)
+ if(!user || !IsAvailable())
+ return FALSE
+ var/selected = input("Select outfit to change into", "Chameleon Outfit") as null|anything in outfit_options
+ if(!IsAvailable() || QDELETED(src) || QDELETED(user))
+ return FALSE
+ var/outfit_type = outfit_options[selected]
+ if(!outfit_type)
+ return FALSE
+ var/datum/outfit/O = new outfit_type()
+ var/list/outfit_types = O.get_chameleon_disguise_info()
+
+ for(var/V in user.chameleon_item_actions)
+ var/datum/action/item_action/chameleon/change/A = V
+ var/done = FALSE
+ for(var/T in outfit_types)
+ for(var/name in A.chameleon_list)
+ if(A.chameleon_list[name] == T)
+ A.update_look(user, T)
+ outfit_types -= T
+ done = TRUE
+ break
+ if(done)
+ break
+ //hardsuit helmets/suit hoods
+ if(ispath(O.suit, /obj/item/clothing/suit/hooded) && ishuman(user))
+ var/mob/living/carbon/human/H = user
+ //make sure they are actually wearing the suit, not just holding it, and that they have a chameleon hat
+ if(istype(H.wear_suit, /obj/item/clothing/suit/chameleon) && istype(H.head, /obj/item/clothing/head/chameleon))
+ var/helmet_type
+ var/obj/item/clothing/suit/hooded/hooded = O.suit
+ helmet_type = initial(hooded.hoodtype)
+
+ if(helmet_type)
+ var/obj/item/clothing/head/chameleon/hat = H.head
+ hat.chameleon_action.update_look(user, helmet_type)
+ qdel(O)
+ return TRUE
+
+
+/datum/action/item_action/chameleon/change
+ name = "Chameleon Change"
+ var/list/chameleon_blacklist = list() //This is a typecache
+ var/list/chameleon_list = list()
+ var/chameleon_type = null
+ var/chameleon_name = "Item"
+
+ var/emp_timer
+
+/datum/action/item_action/chameleon/change/Grant(mob/M)
+ if(M && (owner != M))
+ if(!M.chameleon_item_actions)
+ M.chameleon_item_actions = list(src)
+ var/datum/action/chameleon_outfit/O = new /datum/action/chameleon_outfit()
+ O.Grant(M)
+ else
+ M.chameleon_item_actions |= src
+ ..()
+
+/datum/action/item_action/chameleon/change/Remove(mob/M)
+ if(M && (M == owner))
+ LAZYREMOVE(M.chameleon_item_actions, src)
+ if(!LAZYLEN(M.chameleon_item_actions))
+ var/datum/action/chameleon_outfit/O = locate(/datum/action/chameleon_outfit) in M.actions
+ qdel(O)
+ ..()
+
+/datum/action/item_action/chameleon/change/proc/initialize_disguises()
+ if(button)
+ button.name = "Change [chameleon_name] Appearance"
+
+ chameleon_blacklist |= typecacheof(target.type)
+ for(var/V in typesof(chameleon_type))
+ if(ispath(V) && ispath(V, /obj/item))
+ var/obj/item/I = V
+ if(chameleon_blacklist[V] || (initial(I.flags) & ABSTRACT) || !initial(I.icon_state))
+ continue
+ var/chameleon_item_name = "[initial(I.name)] ([initial(I.icon_state)])"
+ chameleon_list[chameleon_item_name] = I
+
+/datum/action/item_action/chameleon/change/proc/select_look(mob/user)
+ var/obj/item/picked_item
+ var/picked_name
+ picked_name = input("Select [chameleon_name] to change into", "Chameleon [chameleon_name]", picked_name) as null|anything in chameleon_list
+ if(!picked_name)
+ return
+ picked_item = chameleon_list[picked_name]
+ if(!picked_item)
+ return
+ update_look(user, picked_item)
+
+/datum/action/item_action/chameleon/change/proc/random_look(mob/user)
+ var/picked_name = pick(chameleon_list)
+ // If a user is provided, then this item is in use, and we
+ // need to update our icons and stuff
+
+ if(user)
+ update_look(user, chameleon_list[picked_name])
+
+ // Otherwise, it's likely a random initialisation, so we
+ // don't have to worry
+
+ else
+ update_item(chameleon_list[picked_name])
+
+/datum/action/item_action/chameleon/change/proc/update_look(mob/user, obj/item/picked_item)
+ if(isliving(user))
+ var/mob/living/C = user
+ if(C.stat != CONSCIOUS)
+ return
+
+ update_item(picked_item)
+ var/obj/item/thing = target
+ thing.update_slot_icon()
+ UpdateButtonIcon()
+
+/datum/action/item_action/chameleon/change/proc/update_item(obj/item/picked_item)
+ target.name = initial(picked_item.name)
+ target.desc = initial(picked_item.desc)
+ target.icon_state = initial(picked_item.icon_state)
+ if(isitem(target))
+ var/obj/item/I = target
+ I.item_state = initial(picked_item.item_state)
+ I.item_color = initial(picked_item.item_color)
+ if(istype(I, /obj/item/clothing) && istype(initial(picked_item), /obj/item/clothing))
+ var/obj/item/clothing/CL = I
+ var/obj/item/clothing/PCL = picked_item
+ CL.flags_cover = initial(PCL.flags_cover)
+ target.icon = initial(picked_item.icon)
+
+/datum/action/item_action/chameleon/change/Trigger()
+ if(!IsAvailable())
+ return
+
+ select_look(owner)
+ return 1
+
+/datum/action/item_action/chameleon/change/proc/emp_randomise(var/amount = EMP_RANDOMISE_TIME)
+ START_PROCESSING(SSprocessing, src)
+ random_look(owner)
+
+ var/new_value = world.time + amount
+ if(new_value > emp_timer)
+ emp_timer = new_value
+
+/datum/action/item_action/chameleon/change/process()
+ if(world.time > emp_timer)
+ STOP_PROCESSING(SSprocessing, src)
+ return
+ random_look(owner)
+
/obj/item/clothing/under/chameleon
//starts off as black
name = "black jumpsuit"
icon_state = "black"
item_state = "bl_suit"
item_color = "black"
- desc = "It's a plain jumpsuit. It seems to have a small dial on the wrist."
- origin_tech = "syndicate=2"
- var/list/clothing_choices = list()
- burn_state = FIRE_PROOF
- armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0)
+ desc = "It's a plain jumpsuit. It has a small dial on the wrist."
+ sensor_mode = SENSOR_OFF //Hey who's this guy on the Syndicate Shuttle??
+ random_sensor = FALSE
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- New()
- ..()
- var/blocked = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/rank/centcom) // Stops random coloured jumpsuit and undefined centcomm suit appearing in the list.
- for(var/U in subtypesof(/obj/item/clothing/under/color) - blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
+ var/datum/action/item_action/chameleon/change/chameleon_action
- for(var/U in subtypesof(/obj/item/clothing/under/rank) - blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
- return
+/obj/item/clothing/under/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/under
+ chameleon_action.chameleon_name = "Jumpsuit"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/under, /obj/item/clothing/under/color, /obj/item/clothing/under/rank), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+/obj/item/clothing/under/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
- attackby(obj/item/clothing/under/U as obj, mob/user as mob, params)
- ..()
- if(istype(U, /obj/item/clothing/under/chameleon))
- to_chat(user, "Nothing happens.")
- return
- if(istype(U, /obj/item/clothing/under))
- if(src.clothing_choices.Find(U))
- to_chat(user, "Pattern is already recognised by the suit.")
- return
- src.clothing_choices += U
- to_chat(user, "Pattern absorbed by the suit.")
+/obj/item/clothing/under/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+/obj/item/clothing/suit/chameleon
+ name = "armor"
+ desc = "A slim armored vest that protects against most types of damage."
+ icon_state = "armor"
+ item_state = "armor"
+ blood_overlay_type = "armor"
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- emp_act(severity)
- name = "psychedelic"
- desc = "Groovy!"
- icon_state = "psyche"
- item_color = "psyche"
- usr.update_inv_w_uniform()
- spawn(200)
- name = initial(name)
- icon_state = initial(icon_state)
- item_color = initial(item_color)
- desc = initial(desc)
- usr.update_inv_w_uniform()
- ..()
+ var/datum/action/item_action/chameleon/change/chameleon_action
+/obj/item/clothing/suit/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/suit
+ chameleon_action.chameleon_name = "Suit"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/suit/armor/abductor), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
- verb/change()
- set name = "Change Color"
- set category = "Object"
- set src in usr
+/obj/item/clothing/suit/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
- if(icon_state == "psyche")
- to_chat(usr, "Your suit is malfunctioning")
- return
+/obj/item/clothing/suit/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
- var/obj/item/clothing/under/A
- A = input("Select Colour to change it to", "BOOYEA", A) in clothing_choices
- if(!A)
- return
+/obj/item/clothing/glasses/chameleon
+ name = "Optical Meson Scanner"
+ desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting condition."
+ icon_state = "meson"
+ item_state = "meson"
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- desc = null
- permeability_coefficient = 0.90
+ var/datum/action/item_action/chameleon/change/chameleon_action
- desc = A.desc
- name = A.name
- icon_state = A.icon_state
- item_state = A.item_state
- item_color = A.item_color
- usr.update_inv_w_uniform() //so our overlays update.
+/obj/item/clothing/glasses/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/glasses
+ chameleon_action.chameleon_name = "Glasses"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+/obj/item/clothing/glasses/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+/obj/item/clothing/glasses/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
-/obj/item/clothing/under/chameleon/all/New()
- ..()
- var/blocked = list(/obj/item/clothing/under/chameleon, /obj/item/clothing/under/chameleon/all)
- //to prevent an infinite loop
- for(var/U in typesof(/obj/item/clothing/under)-blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
+/obj/item/clothing/gloves/chameleon
+ desc = "These gloves will protect the wearer from electric shock."
+ name = "insulated gloves"
+ icon_state = "yellow"
+ item_state = "ygloves"
+
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/gloves/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/gloves
+ chameleon_action.chameleon_name = "Gloves"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/gloves, /obj/item/clothing/gloves/color), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/gloves/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/gloves/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/head/chameleon
+ name = "grey cap"
+ desc = "It's a baseball hat in a tasteful grey colour."
+ icon_state = "greysoft"
+ item_color = "grey"
+
+ resistance_flags = NONE
+ armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/head/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/head
+ chameleon_action.chameleon_name = "Hat"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/head/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/head/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/mask/chameleon
+ name = "gas mask"
+ desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate
+ icon_state = "gas_alt"
+ item_state = "gas_alt"
+ resistance_flags = NONE
+ armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ flags = AIRTIGHT | BLOCK_GAS_SMOKE_EFFECT
+ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE
+ gas_transfer_coefficient = 0.01
+ permeability_coefficient = 0.01
+ flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
+
+ var/obj/item/voice_changer/voice_changer
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/mask/chameleon/Initialize()
+ . = ..()
+
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/mask
+ chameleon_action.chameleon_name = "Mask"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+ voice_changer = new(src)
+
+/obj/item/clothing/mask/chameleon/Destroy()
+ QDEL_NULL(voice_changer)
+ return ..()
+
+/obj/item/clothing/mask/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/mask/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/shoes/chameleon
+ name = "black shoes"
+ icon_state = "black"
+ item_color = "black"
+ desc = "A pair of black shoes."
+ permeability_coefficient = 0.05
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/shoes/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/shoes
+ chameleon_action.chameleon_name = "Shoes"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/shoes/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/shoes/chameleon/noslip
+ name = "black shoes"
+ icon_state = "black"
+ item_color = "black"
+ desc = "A pair of black shoes."
+ flags = NOSLIP
+
+/obj/item/clothing/shoes/chameleon/noslip/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/storage/backpack/chameleon
+ name = "backpack"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/storage/backpack/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/storage/backpack
+ chameleon_action.chameleon_name = "Backpack"
+ chameleon_action.initialize_disguises()
+
+/obj/item/storage/backpack/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/storage/backpack/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/storage/belt/chameleon
+ name = "toolbelt"
+ desc = "Holds tools."
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/storage/belt/chameleon/Initialize()
+ . = ..()
+
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/storage/belt
+ chameleon_action.chameleon_name = "Belt"
+ chameleon_action.initialize_disguises()
+
+/obj/item/storage/belt/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/storage/belt/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/radio/headset/chameleon
+ name = "radio headset"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/radio/headset/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/radio/headset
+ chameleon_action.chameleon_name = "Headset"
+ chameleon_action.initialize_disguises()
+
+/obj/item/radio/headset/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/radio/headset/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/pda/chameleon
+ name = "PDA"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/pda/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/pda
+ chameleon_action.chameleon_name = "PDA"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/pda/heads), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+
+/obj/item/pda/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/pda/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/stamp/chameleon
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/stamp/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/stamp
+ chameleon_action.chameleon_name = "Stamp"
+ chameleon_action.initialize_disguises()
+
+/obj/item/stamp/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
\ No newline at end of file
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index e937ec25ef9..6be1d3bcf70 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -671,8 +671,8 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_
/obj/item/gun/energy/kinetic_accelerator/crossbow,\
/obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\
/obj/item/cartridge/syndicate, /obj/item/clothing/under/chameleon,\
- /obj/item/clothing/shoes/syndigaloshes, /obj/item/card/id/syndicate,\
- /obj/item/clothing/mask/gas/voice, /obj/item/clothing/glasses/thermal,\
+ /obj/item/clothing/shoes/chameleon/noslip, /obj/item/card/id/syndicate,\
+ /obj/item/clothing/mask/chameleon, /obj/item/clothing/glasses/thermal,\
/obj/item/chameleon, /obj/item/card/emag,\
/obj/item/storage/toolbox/syndicate, /obj/item/aiModule,\
/obj/item/radio/headset/syndicate, /obj/item/grenade/plastic/c4,\
diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm
index cf8b897104c..17262ec4a7a 100644
--- a/code/modules/mining/minebot.dm
+++ b/code/modules/mining/minebot.dm
@@ -69,7 +69,7 @@
visible_message("[src] crackles and buzzes violently!")
/mob/living/simple_animal/hostile/mining_drone/sentience_act()
- AIStatus = AI_OFF
+ ..()
check_friendly_fire = 0
/mob/living/simple_animal/hostile/mining_drone/attackby(obj/item/I, mob/user, params)
diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm
index 0edb81c8321..43bc1b57226 100644
--- a/code/modules/mining/satchel_ore_boxdm.dm
+++ b/code/modules/mining/satchel_ore_boxdm.dm
@@ -83,6 +83,9 @@ obj/structure/ore_box/ex_act(severity, target)
O.forceMove(loc)
CHECK_TICK
+/obj/structure/ore_box/onTransitZ()
+ return
+
/obj/structure/ore_box/verb/empty_box()
set name = "Empty Ore Box"
set category = "Object"
diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm
index 922f3181c27..ca868b5e88f 100644
--- a/code/modules/mob/dead/dead.dm
+++ b/code/modules/mob/dead/dead.dm
@@ -20,4 +20,4 @@
for(var/datum/light_source/L in light_sources)
L.source_atom.update_light()
- return 1
+ return 1
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index aa714f0d527..f16d04e3e86 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -315,15 +315,7 @@
msg += "[p_they(TRUE)] [p_have()] a stupid expression on [p_their()] face.\n"
if(get_int_organ(/obj/item/organ/internal/brain))
- if(istype(src, /mob/living/carbon/human/interactive))
- var/mob/living/carbon/human/interactive/auto = src
- if(auto.showexaminetext)
- msg += "[p_they(TRUE)] [p_are()] appears to be some sort of sick automaton, [p_their()] eyes are glazed over and [p_their()] mouth is slightly agape.\n"
- if(auto.debugexamine)
- var/dodebug = auto.doing2string(auto.doing)
- var/interestdebug = auto.interest2string(auto.interest)
- msg += "[p_they(TRUE)] [p_are()] appears to be [interestdebug] and [dodebug].\n"
- else if(dna.species.show_ssd)
+ if(dna.species.show_ssd)
if(!key)
msg += "[p_they(TRUE)] [p_are()] totally catatonic. The stresses of life in deep-space must have been too much for [p_them()]. Any recovery is unlikely.\n"
else if(!client)
diff --git a/code/modules/mob/living/carbon/human/interactive/functions.dm b/code/modules/mob/living/carbon/human/interactive/functions.dm
deleted file mode 100644
index 06df994df5f..00000000000
--- a/code/modules/mob/living/carbon/human/interactive/functions.dm
+++ /dev/null
@@ -1,874 +0,0 @@
-/mob/living/carbon/human/interactive/proc/chatter(obj)
- var/verbs_use = pick_list(speak_file, "verbs_use")
- var/verbs_touch = pick_list(speak_file, "verbs_touch")
- var/verbs_move = pick_list(speak_file, "verbs_move")
- var/nouns_insult = pick_list(speak_file, "nouns_insult")
- var/nouns_generic = pick_list(speak_file, "nouns_generic")
- var/nouns_objects = pick_list(speak_file, "nouns_objects")
- var/nouns_body = pick_list(speak_file, "nouns_body")
- var/adjective_insult = pick_list(speak_file, "adjective_insult")
- var/adjective_objects = pick_list(speak_file, "adjective_objects")
- var/adjective_generic = pick_list(speak_file, "adjective_generic")
- var/curse_words = pick_list(speak_file, "curse_words")
-
- var/chatmsg = ""
-
- if(prob(10)) // 10% chance to broadcast it over the radio
- chatmsg = ";"
-
- if(prob(chattyness) || knownStrings.len < 10) // say a generic phrase, otherwise draw from our strings.
- if(doing & SNPC_INTERACTING)
- if(prob(chattyness))
- chatmsg += pick("This [nouns_objects] is a little [adjective_objects].",
- "Well [verbs_use] my [nouns_body], this [nouns_insult] is pretty [adjective_insult].",
- "[capitalize(curse_words)], what am I meant to do with this [adjective_insult] [nouns_objects].")
- else if(doing & SNPC_TRAVEL)
- if(prob(chattyness))
- chatmsg += pick("Oh [curse_words], [verbs_move]!",
- "Time to get my [adjective_generic] [adjective_insult] [nouns_body] elsewhere.",
- "I wonder if there is anything to [verbs_use] and [verbs_touch] somewhere else..")
- else if(doing & SNPC_FIGHTING)
- if(prob(chattyness))
- chatmsg += pick("I'm going to [verbs_use] you, you [adjective_insult] [nouns_insult]!",
- "Rend and [verbs_touch], Rend and [verbs_use]!",
- "You [nouns_insult], I'm going to [verbs_use] you right in the [nouns_body]. JUST YOU WAIT!")
- if(prob(chattyness/2))
- chatmsg = ";"
- var/what = pick(1,2,3,4,5)
- switch(what)
- if(1)
- chatmsg += "Well [curse_words], this is a [adjective_generic] situation."
- if(2)
- chatmsg += "Oh [curse_words], that [nouns_insult] was one hell of an [adjective_insult] [nouns_body]."
- if(3)
- chatmsg += "I want to [verbs_use] that [nouns_insult] when I find them."
- if(4)
- chatmsg += "[pick("Innocent","Guilty","Traitorous","Honk")] until proven [adjective_generic]!"
- if(5)
- var/toSay = ""
- for(var/i = 0; i < 5; i++)
- curse_words = pick_list(speak_file, "curse_words")
- toSay += "[curse_words] "
- chatmsg += "Hey [nouns_generic], why don't you go [copytext(toSay, 1, length(toSay))], you [nouns_insult]!"
- else if(prob(chattyness))
- chatmsg += pick(knownStrings)
- if(prob(25)) // cut out some phrases now and then to make sure we're fresh and new
- knownStrings -= pick(chatmsg)
-
- if(chatmsg != ";" && chatmsg != "")
- say(chatmsg)
-
-/mob/living/carbon/human/interactive/proc/traitor(obj)
-
- if(traitorType == SNPC_PSYCHO && nearby.len)
- traitorTarget = pick(nearby)
-
- if(prob(traitorScale))
- if(!Adjacent(traitorTarget) && !(traitorType == SNPC_PSYCHO))
- tryWalk(get_turf(traitorTarget))
- else
- switch(traitorType)
- if(SNPC_BRUTE)
- retal = 1
- retal_target = traitorTarget
- if(SNPC_STEALTH)
- if(istype(traitorTarget,/mob)) // it's inside something, lets kick their shit in
- var/mob/M = traitorTarget
- if(!M.stat)
- retal = 1
- retal_target = traitorTarget
- else
- var/obj/item/I = traitorTarget
- I.loc = get_turf(traitorTarget) // pull it outta them
- else
- take_to_slot(traitorTarget)
- if(SNPC_MARTYR)
- custom_emote(1, "[src]'s chest opens up, revealing a large mass of explosives and tangled wires!")
- if(inactivity_period <= 0)
- inactivity_period = 9999 // technically infinite
- if(do_after(src, 60, target = traitorTarget))
- custom_emote(1, "A fire bursts from [src]'s eyes, igniting white hot and consuming [p_their()] body in a flaming explosion!")
- explosion(src, 6, 6, 6)
- else
- inactivity_period = 0
- custom_emote(1, "[src]'s chest closes, hiding [p_their()] insides.")
- if(SNPC_PSYCHO)
- var/choice = pick(typesof(/obj/item/grenade/chem_grenade) - /obj/item/grenade/chem_grenade)
-
- new choice(src)
-
- retal = 1
- retal_target = traitorTarget
-
-/mob/living/carbon/human/interactive/proc/botany(obj)
- if(shouldModulePass())
- return
-
- if(enforceHome())
- return
-
- var/list/allContents = getAllContents()
-
- var/obj/item/reagent_containers/glass/beaker/bluespace/internalBeaker = locate(/obj/item/reagent_containers/glass/beaker/bluespace) in allContents
- var/obj/item/storage/bag/plants/internalBag = locate(/obj/item/storage/bag/plants) in allContents
-
- if(!internalBag)
- internalBag = new/obj/item/storage/bag/plants(src)
- if(!internalBeaker)
- internalBeaker = new/obj/item/reagent_containers/glass/beaker/bluespace(src)
- internalBeaker.name = "Grow-U-All Super Spray"
-
- if(internalBeaker && internalBag)
- var/obj/machinery/hydroponics/HP
-
- //consider the appropriate target
- var/list/considered = list()
-
- for(var/obj/machinery/hydroponics/tester in view(12,src))
- considered[tester] = 1
-
- if(!tester.myseed)
- considered[tester] += 50
- if(tester.weedlevel > 0)
- considered[tester] += 5
- if(tester.pestlevel > 0)
- considered[tester] += 5
- if(tester.nutrilevel < tester.maxnutri)
- considered[tester] += 15
- if(tester.waterlevel < tester.maxwater)
- considered[tester] += 15
- if(tester.harvest || tester.dead)
- considered[tester] += 100
- considered[tester] = max(1, considered[tester] - get_dist(src,tester))
-
- var/index = 0
- for(var/A in considered)
- ++index
- if(considered[A] > considered[HP] || !HP)
- HP = considered[index]
-
- if(HP)
- TARGET = HP
- if(!Adjacent(HP))
- tryWalk(get_turf(HP))
- else
- if(HP.harvest || HP.dead)
- HP.attack_hand(src)
- else if(!HP.myseed)
- var/obj/item/seeds/SEED = new /obj/item/seeds/random(src)
- custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!")
- HP.attackby(SEED, src)
- else
- var/change = 0
- if(HP.weedlevel > 0)
- change = 1
- if(!internalBeaker.reagents.has_reagent("atrazine", 10))
- internalBeaker.reagents.add_reagent("atrazine", 10)
- if(HP.pestlevel > 0)
- change = 1
- if(!internalBeaker.reagents.has_reagent("diethylamine", 10))
- internalBeaker.reagents.add_reagent("diethylamine", 10)
- if(HP.nutrilevel < HP.maxnutri)
- change = 1
- if(!internalBeaker.reagents.has_reagent("eznutriment", 15))
- internalBeaker.reagents.add_reagent("eznutriment", 15)
- if(!internalBeaker.reagents.has_reagent("diethylamine", 15))
- internalBeaker.reagents.add_reagent("diethylamine", 15)
- if(HP.waterlevel < HP.maxwater)
- change = 1
- if(!internalBeaker.reagents.has_reagent("holywater", 50))
- internalBeaker.reagents.add_reagent("holywater", 50)
- if(change)
- HP.attackby(internalBeaker,src)
-
- var/obj/item/reagent_containers/food/snacks/grown/GF = locate(/obj/item/reagent_containers/food/snacks/grown) in view(12,src)
- if(GF)
- if(!Adjacent(GF))
- tryWalk(get_turf(GF))
- else
- GF.attackby(internalBag,src)
-
- if(internalBag.contents.len)
- var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in range(12, src)
- if(!Adjacent(SF))
- tryWalk(get_turf(SF))
- else
- custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], upending the [internalBag]'s contents all over the [SF]!")
- //smartfridges call updateUsrDialog when you call attackby, so we're going to have to cheese-magic-space this
- for(var/obj/toLoad in internalBag.contents)
- if(contents.len >= SF.max_n_of_items)
- break
- if(SF.accept_check(toLoad))
- SF.load(toLoad, src)
- else
- qdel(toLoad) // destroy everything we dont need
-
-/mob/living/carbon/human/interactive/proc/bartend(obj)
- if(shouldModulePass())
- return
-
- if(enforceHome())
- return
-
- var/list/rangeCheck = oview(6,src)
- var/obj/structure/table/RT
-
- var/mob/living/carbon/human/serveTarget
-
- for(var/mob/living/carbon/human/H in rangeCheck)
- if(!locate(/obj/item/reagent_containers/food/drinks) in orange(1, H))
- serveTarget = H
-
-
- if(serveTarget)
- RT = locate(/obj/structure/table) in orange(1, serveTarget)
-
- if(RT && serveTarget)
- if(!Adjacent(RT))
- tryWalk(get_turf(RT))
- else
- var/drinkChoice = pick(typesof(/obj/item/reagent_containers/food/drinks) - /obj/item/reagent_containers/food/drinks)
- if(drinkChoice)
- var/obj/item/reagent_containers/food/drinks/D = new drinkChoice(get_turf(src))
- RT.attackby(D,src)
- say("[pick("Something to wet your whistle!","Down the hatch, a tasty beverage!","One drink, coming right up!","Tasty liquid for your oral intake!","Enjoy!")]")
- custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")], serving up a [D]!")
-
-/mob/living/carbon/human/interactive/proc/shitcurity(obj)
- var/list/allContents = getAllContents()
-
- if(retal && TARGET)
- if(Adjacent(TARGET))
- var/obj/item/restraints/R = locate() in allContents
- if(R)
- R.attack(TARGET, src) // go go bluespace restraint launcher!
- else if(TARGET in oview(7, src))
- var/obj/item/gun/G = locate() in allContents
- if(G)
- G.afterattack(TARGET, src)
-
-/mob/living/carbon/human/interactive/proc/clowning(obj)
- if(shouldModulePass())
- return
- var/list/allContents = getAllContents()
- var/list/rangeCheck = oview(12, src)
-
- var/mob/living/carbon/human/clownTarget
- var/list/clownPriority = list()
-
- var/obj/item/reagent_containers/spray/S
- if(!locate(/obj/item/reagent_containers/spray) in allContents)
- new/obj/item/reagent_containers/spray(src)
- else
- S = locate(/obj/item/reagent_containers/spray) in allContents
-
- for(var/mob/living/carbon/human/C in rangeCheck)
- var/pranksNearby = 100
- for(var/turf/simulated/T in orange(1, C))
- for(var/obj/item/A in T)
- if(istype(A,/obj/item/soap) || istype(A,/obj/item/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/grown/bananapeel))
- pranksNearby--
- if(T.wet)
- pranksNearby -= 10
- clownPriority[C] = pranksNearby
-
- for(var/A in clownPriority)
- if(!clownTarget)
- clownTarget = A
- else
- if(clownPriority[A] > clownPriority[clownTarget])
- clownTarget = clownPriority[A]
-
- if(clownTarget)
- if(!Adjacent(clownTarget))
- tryWalk(clownTarget)
- else
- var/hasPranked = 0
- for(var/A in allContents)
- if(prob(smartness/2) && !hasPranked)
- if(istype(A,/obj/item/soap))
- npcDrop(A)
- hasPranked = 1
- if(istype(A,/obj/item/reagent_containers/food/snacks/grown/banana))
- var/obj/item/reagent_containers/food/snacks/B = A
- B.attack(src, src)
- if(istype(A,/obj/item/grown/bananapeel))
- npcDrop(A)
- hasPranked = 1
- if(!hasPranked)
- if(S.reagents.total_volume <= 5)
- S.reagents.add_reagent("water", 25)
- S.afterattack(get_turf(pick(orange(1, clownTarget))),src)
-
-
-/mob/living/carbon/human/interactive/proc/healpeople(obj)
- var/shouldTryHeal = 0
- var/obj/item/stack/medical/M
-
- var/list/allContents = getAllContents()
-
- for(var/A in allContents)
- if(istype(A,/obj/item/stack/medical))
- shouldTryHeal = 1
- M = A
-
- var/obj/item/reagent_containers/hypospray/HPS
-
- if(!locate(/obj/item/reagent_containers/hypospray) in allContents)
- new/obj/item/reagent_containers/hypospray(src)
- else
- HPS = locate(/obj/item/reagent_containers/hypospray) in allContents
- if(!shouldTryHeal)
- shouldTryHeal = 2 // we have no stackables to use, lets use our internal hypospray instead
-
- if(shouldTryHeal == 1)
- for(var/mob/living/carbon/human/C in nearby)
- if(istype(C,/mob/living/carbon/human)) //I haven't the foggiest clue why this is turning up non-carbons but sure here whatever
- if(C.health <= 75)
- if(get_dist(src,C) <= 2)
- src.say("Wait, [C], let me heal you!")
- M.attack(C,src)
- sleep(25)
- else
- tryWalk(get_turf(C))
- else if(shouldTryHeal == 2)
- if(HPS)
- if(HPS.reagents.total_volume <= 0)
- HPS.reagents.add_reagent("omnizine",30)
- for(var/mob/living/carbon/human/C in nearby)
- if(istype(C,/mob/living/carbon/human))
- if(C.health <= 75 && C.reagents.get_reagent_amount("omnizine") <= 0) // make sure they wont be overdosing
- if(get_dist(src,C) <= 2)
- src.say("Wait, [C], let me heal you!")
- HPS.attack(C,src)
- sleep(25)
- else
- tryWalk(get_turf(C))
-
-
-/mob/living/carbon/human/interactive/proc/dojanitor(obj)
- if(shouldModulePass())
- return
- var/list/allContents = getAllContents()
- //now with bluespace magic!
- var/obj/item/reagent_containers/spray/SP = locate() in allContents
- if(!SP)
- SP = new /obj/item/reagent_containers/spray(src)
- var/obj/item/soap/SO = locate() in allContents
- if(!SO)
- SO = new /obj/item/soap(src)
-
- if(SP.reagents.total_volume <= 5)
- SP.reagents.add_reagent("cleaner", 25) // bluespess water delivery for AI
-
- var/obj/effect/decal/cleanable/TC
- TC = locate(/obj/effect/decal/cleanable) in view(6, src)
-
- if(TC)
- if(!Adjacent(TC))
- tryWalk(TC)
- else
- var/turf/T = get_turf(TC)
- if(T && !T.density)
- SP.afterattack(TC, src, 1)
- else
- SO.afterattack(TC, src, 1)
- qdel(TC)
-
-// START COOKING MODULE
-/mob/living/carbon/human/interactive/proc/refundrecipe(list/ingredients)
- for(var/obj/I in ingredients)
- I.loc = contents
-
-// Ingredients may be in the rangeCheck or inside the chef's inventory.
-// As they are checked off, they are moved to a seperate list as to not be counted twice
-// If the recipe fails (eg somebody else took an ingredient before it was picked up) the ingredients are dumped back to inventory
-// That way, the chef has a better chance of making a recipe next ptick, if he has more stuff on him
-/mob/living/carbon/human/interactive/proc/makerecipe(datum/recipe/R, allContents, rangeCheck)
- set background = 1
-
- var/list/ingredients[0]
-
- for(var/P in R.items)
- var/obj/item/I = locate(P) in allContents
- if(I)
- ingredients += I
- I.forceMove(null)
- continue
-
- I = locate(P) in rangeCheck
- TARGET = I
- if(I && !Adjacent(I))
- tryWalk(get_turf(I))
- sleep(get_dist(src, I))
- if(!I || !(I in rangeCheck))
- refundrecipe(ingredients)
- return 0
- custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], picking up [I].")
- ingredients += I
- I.forceMove(null)
-
- // cheaply cook the ingredients into result
- sleep(R.time)
- for(var/obj/I in ingredients)
- qdel(I)
- ingredients.Cut()
- var/result = new R.result(get_turf(src))
- custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] and, with a bang, \a [result] is cooked!")
- return 1
-
-/mob/living/carbon/human/interactive/proc/souschef(obj)
- if(shouldModulePass() || enforceHome() || prob(SNPC_FUZZY_CHANCE_HIGH) || prob(SNPC_FUZZY_CHANCE_HIGH))
- return
-
- if(doing & SNPC_SPECIAL)
- return
- doing |= SNPC_SPECIAL
-
- var/static/list/customableTypes = list(/obj/item/reagent_containers/food/snacks/customizable,/obj/item/reagent_containers/food/snacks/breadslice,/obj/item/reagent_containers/food/snacks/bun,/obj/item/reagent_containers/food/snacks/sliceable/flatdough,/obj/item/reagent_containers/food/snacks/boiledspaghetti,/obj/item/trash/plate,/obj/item/trash/bowl)
-
- var/static/list/rawtypes = list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/reagent_containers/food/snacks/rawcutlet, /obj/item/reagent_containers/food/snacks/rawsticks, /obj/item/reagent_containers/food/snacks/salmonmeat, /obj/item/reagent_containers/food/snacks/carpmeat, /obj/item/reagent_containers/food/snacks/catfishmeat, /obj/item/reagent_containers/food/snacks/spaghetti, /obj/item/reagent_containers/food/snacks/dough, /obj/item/reagent_containers/food/snacks/sliceable/flatdough, /obj/item/reagent_containers/food/snacks/doughslice, /obj/item/reagent_containers/food/snacks/meat, /obj/item/reagent_containers/food/snacks/boiledrice, /obj/item/reagent_containers/food/snacks/cheesewedge, /obj/item/reagent_containers/food/snacks/raw_bacon)
-
- try
- var/list/allContents = getAllContents()
- var/list/rangeCheck = view(6, src)
-
- //Bluespace in some inbuilt tools
- var/obj/item/kitchen/rollingpin/RP = locate() in allContents
- if(!RP)
- RP = new /obj/item/kitchen/rollingpin(src)
-
- var/obj/item/kitchen/knife/KK = locate() in allContents
- if(!KK)
- KK = new /obj/item/kitchen/knife(src)
-
- var/global/list/available_recipes
- if(!available_recipes)
- available_recipes = list()
- for(var/type in subtypesof(/datum/recipe))
- var/datum/recipe/recipe = new type
- if(recipe.result) // Ignore recipe subtypes that lack a result
- available_recipes += recipe
- else
- qdel(recipe)
-
- var/foundCookable = 0
-
- //Make some basic custom food
- var/foundCustom
- for(var/customType in customableTypes)
- var/A = locate(customType) in rangeCheck
- var/obj/item/reagent_containers/food/snacks/customizable/I = A
- if(A && (!istype(I) || I.ingredients.len <= 3))
- foundCustom = A // this will eventually wittle down to 0
- break
-
- var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in rangeCheck
- if(SF)
- if(SF.contents.len > 0)
- if(!Adjacent(SF))
- tryWalk(get_turf(SF))
- else
- custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")], grabbing various foodstuffs from [SF] and sticking them in its mouth!")
- for(var/obj/item/A in SF.contents)
- if(prob(smartness/2))
- var/count = SF.item_quants[A.name]
- if(count)
- SF.item_quants[A.name] = count - 1
- A.forceMove(src)
-
- if(foundCustom)
- var/obj/item/reagent_containers/food/snacks/FC = foundCustom
- for(var/obj/item/reagent_containers/food/snacks/toMake in allContents)
- if(prob(smartness))
- if(FC.reagents)
- if(toMake.reagents)
- var/raw = 0
- for(var/type in rawtypes)
- if(istype(toMake, type))
- raw = 1
- break
- if(!raw)
- npcDrop(toMake)
- FC.attackby(toMake, src)
- allContents -= toMake
- break
- else
- qdel(FC) // this food is usless, toss it
-
- // Process tool-based ingredients
- var/obj/item/reagent_containers/food/snacks/dough/D = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/sliceable/flatdough/FD = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/rawcutlet/RC = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/sliceable/cheesewheel/CW = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/grown/potato/PO = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/meat/ME = locate() in rangeCheck
- var/obj/item/reagent_containers/food/snacks/raw_bacon/RB = locate() in rangeCheck
-
- if(D)
- TARGET = D
- if(prob(50))
- tryWalk(get_turf(D))
- sleep(get_dist(src, D))
- D.attackby(RP, src)
- foundCookable = 1
- else if(FD)
- TARGET = FD
- if(prob(50))
- tryWalk(get_turf(FD))
- sleep(get_dist(src, FD))
- FD.attackby(KK, src)
- foundCookable = 1
- else if(RC)
- TARGET = RC
- if(prob(50))
- tryWalk(get_turf(RC))
- sleep(get_dist(src, RC))
- RC.attackby(KK, src)
- foundCookable = 1
- else if(CW)
- TARGET = CW
- if(prob(50))
- tryWalk(get_turf(CW))
- sleep(get_dist(src, CW))
- CW.attackby(KK, src)
- foundCookable = 1
- else if(PO)
- TARGET = PO
- if(prob(50))
- tryWalk(get_turf(PO))
- sleep(get_dist(src, PO))
- PO.attackby(KK, src)
- foundCookable = 1
- else if(ME)
- TARGET = ME
- if(prob(50))
- tryWalk(get_turf(ME))
- sleep(get_dist(src, ME))
- ME.attackby(KK, src)
- foundCookable = 1
- else if(RB)
- TARGET = RB
- if(prob(50))
- tryWalk(get_turf(RB))
- sleep(get_dist(src, RB))
- RB.attackby(KK, src)
- foundCookable = 1
-
- // refresh
- allContents = getAllContents()
- rangeCheck = view(6, src)
-
- // Find most complex recipe we can cook
- var/ingredientZone = rangeCheck + allContents
- var/highest_count = 0
- var/datum/recipe/winner = null
- for(var/datum/recipe/R in available_recipes)
- if(R.check_items(ingredientZone) >= 0)
- var/count = (R.items ? R.items.len : 0)
- if(count > highest_count)
- highest_count = count
- winner = R
-
- // make winner
- if(winner)
- if(makerecipe(winner, allContents, rangeCheck))
- foundCookable = 1
-
- // refresh
- allContents = getAllContents()
- rangeCheck = view(6, src)
-
- // dont display our ingredients
- var/list/finishedList = list()
- for(var/obj/item/reagent_containers/food/snacks/toDisplay in allContents)
- var/raw = 0
- for(var/type in rawtypes)
- if(istype(toDisplay, type))
- raw = 1
- break
- if(!raw)
- finishedList += toDisplay
-
- for(var/obj/item/reagent_containers/food/snacks/toGrab in rangeCheck)
- if(!(locate(/obj/structure/table/reinforced) in get_turf(toGrab))) //it's not being displayed
- var/raw = 0
- for(var/type in rawtypes)
- if(istype(toGrab, type))
- raw = 1
- break
- if(!raw)
- foundCookable = 1
- if(!Adjacent(toGrab))
- tryWalk(toGrab)
- else
- finishedList += toGrab
- toGrab.forceMove(src)
-
- if(finishedList.len)
- var/obj/structure/table/reinforced/RT
-
- for(var/obj/structure/table/reinforced/toCheck in rangeCheck)
- var/counted = 0
- for(var/obj/item/reagent_containers/food/snacks/S in get_turf(toCheck))
- ++counted
- if(counted < 12) // make sure theres not too much food here
- RT = toCheck
- break
-
- if(RT)
- foundCookable = 1
- if(!Adjacent(RT))
- tryWalk(get_turf(RT))
- else
- for(var/obj/item/reagent_containers/food/snacks/toPlop in finishedList)
- RT.attackby(toPlop,src)
-
- if(!foundCookable)
- var/chosenType = pick(rawtypes)
-
- var/obj/item/reagent_containers/food/snacks/newSnack = new chosenType(get_turf(src))
- TARGET = newSnack
- newSnack.reagents.remove_any((newSnack.reagents.total_volume/2)-1)
- newSnack.name = "synthetic [newSnack.name]"
- custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] as [p_they()] vomit[p_s()] [newSnack] from [p_their()] mouth!")
- catch(var/exception/e)
- log_runtime(e, src, "Caught in SNPC cooking module")
- doing &= ~SNPC_SPECIAL
-// END COOKING MODULE
-
-/mob/living/carbon/human/interactive/proc/combat(obj)
- set background = 1
- enforce_hands()
- if(canmove)
- if((graytide || (TRAITS & TRAIT_MEAN)) || retal)
- interest += targetInterestShift
- a_intent = INTENT_HARM
- zone_sel.selecting = pick("chest","r_leg","l_leg","r_arm","l_arm","head")
- doing |= SNPC_FIGHTING
- if(retal)
- TARGET = retal_target
- else
- var/mob/living/M = locate(/mob/living) in oview(7, src)
- if(!M)
- doing = doing & ~SNPC_FIGHTING
- else if(M != src && !compareFaction(M.faction))
- TARGET = M
-
- //no infighting
- if(retal)
- if(retal_target)
- if(compareFaction(retal_target.faction))
- retal = 0
- retal_target = null
- TARGET = null
- doing = 0
-
- //ensure we're using the best object possible
-
- var/obj/item/best
- var/foundFav = 0
- var/list/allContents = getAllContents()
- for(var/test in allContents)
- for(var/a in favoured_types)
- if(ispath(test,a) && !(doing & SNPC_FIGHTING)) // if we're not in combat and we find our favourite things, use them (for people like janitor and doctors)
- best = test
- foundFav = 1
- return
- if(!foundFav)
- if(istype(test,/obj/item))
- var/obj/item/R = test
- if(R.force > 2) // make sure we don't equip any non-weaponlike items, ie bags and stuff
- if(!best)
- best = R
- else
- if(best.force < R.force)
- best = R
- if(istype(R, /obj/item/gun))
- var/obj/item/gun/G = R
- if(G.can_shoot())
- best = R
- break // gun with ammo? screw the rest
- if(best && best != main_hand)
- take_to_slot(best,1)
-
- if(TARGET && (doing & SNPC_FIGHTING)) // this is a redundancy check
- var/mob/living/M = TARGET
- if(istype(M,/mob/living))
- if(M.health > 1 && (M in oview(src, 6)))
- //THROWING OBJECTS
- for(var/A in allContents)
- if(istype(A,/obj/item/gun)) // guns are for shooting, not throwing.
- continue
- if(prob(robustness))
- if(istype(A,/obj/item))
- var/obj/item/W = A
- if(W.throwforce > 19) // Only throw worthwile stuff, no more lobbing wrenches at wenches
- npcDrop(W, 1)
- throw_item(TARGET)
- if(istype(A,/obj/item/grenade)) // Allahu ackbar! ALLAHU ACKBARR!!
- var/obj/item/grenade/G = A
- G.attack_self(src)
- if(prob(smartness))
- npcDrop(G, 1)
- sleep(15)
- throw_item(TARGET)
-
- if(!main_hand && other_hand)
- swap_hands()
- if(main_hand)
- if(main_hand.force != 0)
- if(istype(main_hand,/obj/item/gun))
- var/obj/item/gun/G = main_hand
- if(G.can_trigger_gun(src))
- var/shouldFire = 1
- if(istype(main_hand, /obj/item/gun/energy))
- var/obj/item/gun/energy/P = main_hand
- var/stunning = 0
- for(var/A in P.ammo_type)
- if(ispath(A,/obj/item/ammo_casing/energy/electrode))
- stunning = 1
- var/mob/stunCheck = TARGET
- if(stunning && stunCheck.stunned)
- shouldFire = 0
- if(shouldFire)
- if(!G.can_shoot())
- G.shoot_with_empty_chamber(src)
- npcDrop(G, 1)
- else
- G.process_fire(TARGET, src)
- else
- if(get_dist(src,TARGET) > 6)
- if(!walk2derpless(TARGET))
- timeout++
- else
- var/obj/item/W = main_hand
- W.attack(TARGET, src)
- else
- npcDrop(G, 1)
- else
- if(targetRange(TARGET) > 2)
- tryWalk(TARGET)
- else
- if(Adjacent(TARGET))
- a_intent = pick(INTENT_DISARM, INTENT_HARM)
- M.attack_hand(src)
- timeout++
- else if(timeout >= 10 || !(targetRange(M) > 14))
- doing = doing & ~SNPC_FIGHTING
- timeout = 0
- TARGET = null
- retal = 0
- retal_target = null
- else
- timeout++
-
-/mob/living/carbon/human/interactive/proc/past_verb(word)
- if(copytext(word, length(word)) == "e")
- return "[word]d"
- else
- return "[word]ed"
-
-/mob/living/carbon/human/interactive/proc/ing_verb(word)
- if(copytext(word, length(word)) == "e")
- return "[copytext(word, 1, length(word))]ing"
- else
- return "[word]ing"
-
-/mob/living/carbon/human/interactive/proc/paperwork_sentence()
- var/verbs_use = pick_list(speak_file, "verbs_use")
- var/verbs_touch = pick_list(speak_file, "verbs_touch")
- var/verbs_move = pick_list(speak_file, "verbs_move")
- var/nouns_insult = pick_list(speak_file, "nouns_insult")
- var/nouns_generic = pick_list(speak_file, "nouns_generic")
- var/nouns_objects = pick_list(speak_file, "nouns_objects")
- var/nouns_body = pick_list(speak_file, "nouns_body")
- var/adjective_insult = pick_list(speak_file, "adjective_insult")
- var/adjective_objects = pick_list(speak_file, "adjective_objects")
- var/adjective_generic = pick_list(speak_file, "adjective_generic")
- var/curse_words = pick_list(speak_file, "curse_words")
- var/result = pick("fired", "promoted", "demoted", "terminated", "incinerated", "sent to CentCom", "borged")
-
- return pick(
- "The [adjective_insult] [pick(command_positions)] [past_verb(verbs_touch)] the [adjective_objects] [nouns_objects] and was [result].",
- "The [adjective_generic] [pick(engineering_positions)] [past_verb(verbs_touch)] the [adjective_insult] [nouns_objects]. [capitalize(curse_words)], the [nouns_insult] was [result].",
- "The [adjective_insult] [pick(security_positions)] [past_verb(verbs_touch)] the [adjective_generic] [nouns_insult] [pick(support_positions)]'s [nouns_body] and had to be [result].",
- "Medical had to [verbs_use] the [adjective_generic] [pick(command_positions)]'s [adjective_objects] [nouns_body] after the incident.",
- "I demand that [nouns_generic] respond with a [adjective_generic] update ASAP.",
- "Would like an update from [nouns_generic] regarding the [adjective_insult] [pick(command_positions)] being [result].",
- "The [pick(medical_positions)] needs to [verbs_move] faster when the crew's [adjective_objects] [nouns_body]s are injured, or they will be [result].",
- "[capitalize(curse_words)] [adjective_insult] [curse_words].",
- "
")
-
-/mob/living/carbon/human/interactive/proc/pickStamp(allContents)
- var/list/stamps[0]
- for(var/obj/item/stamp/S in allContents)
- stamps += S
- if(stamps.len)
- return pick(stamps)
-
- // just make one, maybe
- if(prob(SNPC_FUZZY_CHANCE_LOW / 2))
- for(var/p in favoured_types)
- if(ispath(p, /obj/item/stamp))
- return new p(src)
- return null
-
-/mob/living/carbon/human/interactive/proc/paperwork(obj)
- set background = 1
-
- if(shouldModulePass() || !prob(SNPC_FUZZY_CHANCE_LOW / 4))
- return
-
- var/list/allContents = getAllContents()
-
- var/obj/item/paper/P = locate() in allContents
- var/obj/item/stamp/S = pickStamp(allContents)
- var/mob/living/carbon/human/H = locate() in nearby
-
- if(!P)
- P = new /obj/item/paper(src)
-
- if(P && S && H)
- if(!P.stamped || !P.stamped.len)
- // generate form
- P.name = pick("Inspection Report", "Re: Crew", "Complaint", "To: CentCom")
- P.info = {"

Name | Status | Location | Control | |
| [Bot.hacked ? "(!) [Bot.name]" : Bot.name] ([Bot.model]) | " diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index 3ff26f7850c..970ac596482 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -451,7 +451,7 @@ response_harm = "kicks" gold_core_spawnable = CHEM_MOB_SPAWN_INVALID -/mob/living/simple_animal/pet/corgi/Ian/process_ai() +/mob/living/simple_animal/pet/corgi/Ian/Life() ..() //Feeding, chasing food, FOOOOODDDD @@ -578,7 +578,7 @@ return ..() -/mob/living/simple_animal/pet/corgi/Lisa/process_ai() +/mob/living/simple_animal/pet/corgi/Lisa/Life() ..() make_babies() diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index ae19106e1af..869c663e312 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -54,9 +54,8 @@ else if(prob(5)) emote("snuffles") -/mob/living/simple_animal/mouse/process_ai() +/mob/living/simple_animal/mouse/Life() ..() - if(prob(0.5)) stat = UNCONSCIOUS icon_state = "mouse_[mouse_color]_sleep" diff --git a/code/modules/mob/living/simple_animal/friendly/pug.dm b/code/modules/mob/living/simple_animal/friendly/pug.dm index e1e16ef42b2..36ce34094a0 100644 --- a/code/modules/mob/living/simple_animal/friendly/pug.dm +++ b/code/modules/mob/living/simple_animal/friendly/pug.dm @@ -19,7 +19,7 @@ see_in_dark = 5 gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY -/mob/living/simple_animal/pet/pug/process_ai() +/mob/living/simple_animal/pet/pug/Life() ..() if(!resting && !buckled) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 9d2fd385c47..9318dd3d8d3 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -30,8 +30,6 @@ var/stat_exclusive = 0 //Mobs with this set to 1 will exclusively attack things defined by stat_attack, stat_attack 2 means they will only attack corpses var/attack_same = 0 //Set us to 1 to allow us to attack our own faction, or 2, to only ever attack our own faction - var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) - //typecache of things this mob will attack in DestroySurroundings() if it has environment_smash var/list/environment_target_typecache = list( /obj/machinery/door/window, @@ -71,12 +69,6 @@ walk(src, 0) return 0 -/mob/living/simple_animal/hostile/process_ai() - ..() - if(!AICanContinue()) - return 0 - return 1 - /mob/living/simple_animal/hostile/handle_automated_action() if(AIStatus == AI_OFF) return 0 @@ -88,7 +80,7 @@ DestroySurroundings() if(!MoveToTarget(possible_targets)) //if we lose our target if(AIShouldSleep(possible_targets)) // we try to acquire a new one - AIStatus = AI_IDLE // otherwise we go idle + toggle_ai(AI_IDLE) // otherwise we go idle return 1 /mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) @@ -272,8 +264,8 @@ if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight search_objects = 0 target = null - if(AIStatus == AI_IDLE) - AIStatus = AI_ON + if(AIStatus != AI_ON && AIStatus != AI_OFF) + toggle_ai(AI_ON) FindTarget() else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one FindTarget() @@ -409,7 +401,7 @@ if(AI_IDLE) if(FindTarget(possible_targets, 1)) . = 1 - AIStatus = AI_ON //Wake up for more than one Life() cycle. + toggle_ai(AI_ON) //Wake up for more than one Life() cycle. else . = 0 @@ -426,3 +418,37 @@ /mob/living/simple_animal/hostile/proc/LosePatience() deltimer(lose_patience_timer_id) + +/mob/living/simple_animal/hostile/consider_wakeup() + ..() + var/list/tlist + var/turf/T = get_turf(src) + + if(!T) + return + + if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 + toggle_ai(AI_Z_OFF) + return + + var/cheap_search = isturf(T) && !(T.z == 1) // The original check for SSmapping's station z level trait, unfortunately it isn't here. + if(cheap_search) + tlist = ListTargetsLazy(T.z) + else + tlist = ListTargets() + + if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) + if(cheap_search) //Try again with full effort + FindTarget() + toggle_ai(AI_ON) + +/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + . = list() + for(var/I in SSmobs.clients_by_zlevel[_Z]) + var/mob/M = I + if(get_dist(M, src) < vision_range) + if(isturf(M.loc)) + . += M + else if(M.loc.type in hostile_machines) + . += M.loc diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm index c672ef0ff4d..391bc4ce67d 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm @@ -35,7 +35,7 @@ var/pre_attack_icon = "Goliath_preattack" loot = list(/obj/item/stack/sheet/animalhide/goliath_hide{layer = 4.1}) -/mob/living/simple_animal/hostile/asteroid/goliath/process_ai() +/mob/living/simple_animal/hostile/asteroid/goliath/Life() ..() handle_preattack() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm new file mode 100644 index 00000000000..60b0f7277c8 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm @@ -0,0 +1,268 @@ +/mob/living/simple_animal/hostile/asteroid/ + vision_range = 2 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + faction = list("mining") + weather_immunities = list("lava","ash") + obj_damage = 30 + environment_smash = 2 + minbodytemp = 0 + heat_damage_per_tick = 20 + response_help = "pokes" + response_disarm = "shoves" + response_harm = "strikes" + status_flags = 0 + a_intent = INTENT_HARM + var/throw_message = "bounces off of" + var/icon_aggro = null // for swapping to when we get aggressive + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + mob_size = MOB_SIZE_LARGE + +/mob/living/simple_animal/hostile/asteroid/Aggro() + ..() + icon_state = icon_aggro + +/mob/living/simple_animal/hostile/asteroid/LoseAggro() + ..() + if(stat == CONSCIOUS) + icon_state = icon_living + +/mob/living/simple_animal/hostile/asteroid/bullet_act(var/obj/item/projectile/P)//Reduces damage from most projectiles to curb off-screen kills + if(!stat) + Aggro() + if(P.damage < 30 && P.damage_type != BRUTE) + P.damage = (P.damage / 3) + visible_message("The [P] has a reduced effect on [src]!") + ..() + +/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM)//No floor tiling them to death, wiseguy + if(istype(AM, /obj/item)) + var/obj/item/T = AM + if(!stat) + Aggro() + if(T.throwforce <= 20) + visible_message("The [T.name] [src.throw_message] [src.name]!") + return + ..() + +/mob/living/simple_animal/hostile/asteroid/basilisk + name = "basilisk" + desc = "A territorial beast, covered in a thick shell that absorbs energy. Its stare causes victims to freeze from the inside." + icon = 'icons/mob/animal.dmi' + icon_state = "Basilisk" + icon_living = "Basilisk" + icon_aggro = "Basilisk_alert" + icon_dead = "Basilisk_dead" + icon_gib = "syndicate_gib" + move_to_delay = 20 + projectiletype = /obj/item/projectile/temp/basilisk + projectilesound = 'sound/weapons/pierce.ogg' + ranged = 1 + ranged_message = "stares" + ranged_cooldown_time = 30 + throw_message = "does nothing against the hard shell of" + vision_range = 2 + speed = 3 + maxHealth = 200 + health = 200 + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 12 + melee_damage_upper = 12 + attacktext = "bites into" + a_intent = INTENT_HARM + speak_emote = list("chitters") + attack_sound = 'sound/weapons/bladeslice.ogg' + aggro_vision_range = 9 + idle_vision_range = 2 + turns_per_move = 5 + loot = list(/obj/item/stack/ore/diamond{layer = 4.1}, + /obj/item/stack/ore/diamond{layer = 4.1}) + +/obj/item/projectile/temp/basilisk + name = "freezing blast" + icon_state = "ice_2" + damage = 0 + damage_type = BURN + nodamage = 1 + flag = "energy" + temperature = 50 + +/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(var/new_target) + if(..()) //we have a target + if(isliving(target)) + var/mob/living/L = target + if(L.bodytemperature > 261) + L.bodytemperature = 261 + visible_message("The [src.name]'s stare chills [L.name] to the bone!") + +/mob/living/simple_animal/hostile/asteroid/basilisk/ex_act(severity) + switch(severity) + if(1.0) + gib() + if(2.0) + adjustBruteLoss(140) + if(3.0) + adjustBruteLoss(110) + +/mob/living/simple_animal/hostile/asteroid/goliath + name = "goliath" + desc = "A massive beast that uses long tentacles to ensare its prey, threatening them is not advised under any conditions." + icon = 'icons/mob/animal.dmi' + icon_state = "Goliath" + icon_living = "Goliath" + icon_aggro = "Goliath_alert" + icon_dead = "Goliath_dead" + icon_gib = "syndicate_gib" + attack_sound = 'sound/weapons/punch4.ogg' + mouse_opacity = MOUSE_OPACITY_OPAQUE + move_to_delay = 40 + ranged = 1 + ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight + ranged_cooldown_time = 120 + friendly = "wails at" + speak_emote = list("bellows") + vision_range = 4 + speed = 3 + maxHealth = 300 + health = 300 + harm_intent_damage = 1 //Only the manliest of men can kill a Goliath with only their fists. + obj_damage = 100 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "pulverizes" + attack_sound = 'sound/weapons/punch1.ogg' + throw_message = "does nothing to the rocky hide of the" + aggro_vision_range = 9 + idle_vision_range = 5 + move_force = MOVE_FORCE_VERY_STRONG + move_resist = MOVE_FORCE_VERY_STRONG + pull_force = MOVE_FORCE_VERY_STRONG + var/pre_attack = 0 + loot = list(/obj/item/asteroid/goliath_hide{layer = 4.1}) + +/mob/living/simple_animal/hostile/asteroid/goliath/Life() + ..() + handle_preattack() + +/mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack() + if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack) + pre_attack++ + if(!pre_attack || stat || AIStatus == AI_IDLE) + return + icon_state = "Goliath_preattack" + +/mob/living/simple_animal/hostile/asteroid/goliath/revive() + move_force = MOVE_FORCE_VERY_STRONG + move_resist = MOVE_FORCE_VERY_STRONG + pull_force = MOVE_FORCE_VERY_STRONG + ..() + +/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire() + var/tturf = get_turf(target) + if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen + visible_message("The [src.name] digs its tentacles under [target.name]!") + new /obj/effect/goliath_tentacle/original(tturf) + ranged_cooldown = world.time + ranged_cooldown_time + icon_state = icon_aggro + pre_attack = 0 + return + +/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(damage) + ranged_cooldown-- + handle_preattack() + ..() + +/mob/living/simple_animal/hostile/asteroid/goliath/Aggro() + vision_range = aggro_vision_range + handle_preattack() + if(icon_state != icon_aggro) + icon_state = icon_aggro + return + +/obj/effect/goliath_tentacle/ + name = "Goliath tentacle" + icon = 'icons/mob/animal.dmi' + icon_state = "Goliath_tentacle" + var/latched = 0 + +/obj/effect/goliath_tentacle/New() + var/turftype = get_turf(src) + if(istype(turftype, /turf/simulated/mineral)) + var/turf/simulated/mineral/M = turftype + M.gets_drilled() + spawn(20) + Trip() + +/obj/effect/goliath_tentacle/original + +/obj/effect/goliath_tentacle/original/New() + for(var/obj/effect/goliath_tentacle/original/O in loc)//No more GG NO RE from 2+ goliaths simultaneously tentacling you + if(O != src) + qdel(src) + var/list/directions = cardinal.Copy() + var/counter + for(counter = 1, counter <= 3, counter++) + var/spawndir = pick(directions) + directions -= spawndir + var/turf/T = get_step(src,spawndir) + new /obj/effect/goliath_tentacle(T) + ..() + + +/obj/effect/goliath_tentacle/proc/Trip() + for(var/mob/living/M in src.loc) + M.Stun(5) + M.adjustBruteLoss(rand(10,15)) + latched = 1 + if(src && M) + visible_message("The [src.name] grabs hold of [M.name]!") + if(!latched) + qdel(src) + else + spawn(50) + qdel(src) + +/obj/item/asteroid/goliath_hide + name = "goliath hide plates" + desc = "Pieces of a goliath's rocky hide, these might be able to make your suit a bit more durable to attack from the local fauna." + icon = 'icons/obj/items.dmi' + icon_state = "goliath_hide" + flags = NOBLUDGEON + w_class = WEIGHT_CLASS_NORMAL + layer = 4 + +/obj/item/asteroid/goliath_hide/afterattack(atom/target, mob/user, proximity_flag) + if(proximity_flag) + if(istype(target, /obj/item/clothing/suit/space/hardsuit/mining) || istype(target, /obj/item/clothing/head/helmet/space/hardsuit/mining) || istype(target, /obj/item/clothing/suit/space/eva/plasmaman/miner) || istype(target, /obj/item/clothing/head/helmet/space/eva/plasmaman/miner)) + var/obj/item/clothing/C = target + var/current_armor = C.armor + if(current_armor["melee"] < 60) + current_armor["melee"] = min(current_armor["melee"] + 10, 60) + to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") + qdel(src) + else + to_chat(user, "You can't improve [C] any further.") + return + if(istype(target, /obj/mecha/working/ripley)) + var/obj/mecha/D = target + if(D.icon_state != "ripley-open") + to_chat(user, "You can't add armour onto the mech while someone is inside!") + return + var/list/damage_absorption = D.damage_absorption + if(damage_absorption["brute"] > 0.3) + damage_absorption["brute"] = max(damage_absorption["brute"] - 0.1, 0.3) + damage_absorption["bullet"] = damage_absorption["bullet"] - 0.05 + damage_absorption["fire"] = damage_absorption["fire"] - 0.05 + damage_absorption["laser"] = damage_absorption["laser"] - 0.025 + to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") + qdel(src) + D.overlays += image("icon"="mecha.dmi", "icon_state"="ripley-g-open") + D.desc = "Autonomous Power Loader Unit. Its armour is enhanced with some goliath hide plates." + if(damage_absorption["brute"] == 0.3) + D.overlays += image("icon"="mecha.dmi", "icon_state"="ripley-g-full-open") + D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - the pilot must be an experienced monster hunter." + else + to_chat(user, "You can't improve [D] any further!") + return diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index b4019cff988..867b0eb5073 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -80,10 +80,14 @@ var/deathmessage = "" var/death_sound = null //The sound played on death + var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) + var/can_have_ai = TRUE //once we have become sentient, we can never go back + var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. + /mob/living/simple_animal/Initialize() ..() - GLOB.simple_animal_list += src + GLOB.simple_animals[AIStatus] += src verbs -= /mob/verb/observe if(!can_hide) verbs -= /mob/living/simple_animal/verb/hide @@ -97,7 +101,18 @@ collar.forceMove(loc) collar = null master_commander = null - GLOB.simple_animal_list -= src + GLOB.simple_animals[AIStatus] -= src + if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src + + if(nest) + nest.spawned_mobs -= src + nest = null + + var/turf/T = get_turf(src) + if (T && AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + return ..() /mob/living/simple_animal/Login() @@ -111,12 +126,6 @@ health = Clamp(health, 0, maxHealth) med_hud_set_status() - -/mob/living/simple_animal/proc/process_ai() - handle_automated_movement() - handle_automated_action() - handle_automated_speech() - /mob/living/simple_animal/lay_down() ..() handle_resting_state_icons() @@ -562,6 +571,8 @@ /* End Inventory */ /mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion + toggle_ai(AI_OFF) + can_have_ai = FALSE return /mob/living/simple_animal/update_sight() @@ -581,4 +592,37 @@ return /mob/living/simple_animal/can_hear() - . = TRUE \ No newline at end of file + . = TRUE + +/mob/living/simple_animal/proc/consider_wakeup() + if(pulledby || shouldwakeup) + toggle_ai(AI_ON) + +/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(!ckey && !stat)//Not unconscious + if(AIStatus == AI_IDLE) + toggle_ai(AI_ON) + +/mob/living/simple_animal/proc/toggle_ai(togglestatus) + if(!can_have_ai && (togglestatus != AI_OFF)) + return + if(AIStatus != togglestatus) + if(togglestatus > 0 && togglestatus < 5) + if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) + var/turf/T = get_turf(src) + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + else + SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src + GLOB.simple_animals[AIStatus] -= src + GLOB.simple_animals[togglestatus] += src + AIStatus = togglestatus + else + stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") + +/mob/living/simple_animal/onTransitZ(old_z, new_z) + ..() + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src + toggle_ai(initial(AIStatus)) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/spawner.dm b/code/modules/mob/living/simple_animal/spawner.dm index 1a22dd353dd..38a9c176be7 100644 --- a/code/modules/mob/living/simple_animal/spawner.dm +++ b/code/modules/mob/living/simple_animal/spawner.dm @@ -29,7 +29,7 @@ spawned_mobs = null return ..() -/mob/living/simple_animal/hostile/spawner/process_ai() +/mob/living/simple_animal/hostile/spawner/Life() ..() spawn_mob() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 3e9ff0b7ba6..9f539282749 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -196,9 +196,11 @@ var/list/permanent_huds = list() var/list/actions = list() + var/list/datum/action/chameleon_item_actions var/list/progressbars = null //for stacking do_after bars var/list/tkgrabbed_objects = list() // Assoc list of items to TK grabs var/forced_look = null // This can either be a numerical direction or a soft object reference (UID). It makes the mob always face towards the selected thing. + var/registered_z \ No newline at end of file diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index a46b9a94fc7..427bcfcb659 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -193,7 +193,7 @@ close_spawn_windows() var/obj/O = locate("landmark*Observer-Start") to_chat(src, "Now teleporting.") - observer.loc = O.loc + observer.forceMove(O.loc) observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. client.prefs.update_preview_icon(1) observer.icon = client.prefs.preview_icon @@ -344,27 +344,27 @@ else if(IsSyndicateCommand(rank)) character.loc = pick(syndicateofficer) else - character.loc = pick(aroomwarp) + character.forceMove(pick(aroomwarp)) join_message = "has arrived" else if(spawning_at) S = spawntypes[spawning_at] if(S && istype(S)) if(S.check_job_spawning(rank)) - character.loc = pick(S.turfs) + character.forceMove(pick(S.turfs)) join_message = S.msg else to_chat(character, "Your chosen spawnpoint ([S.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") - character.loc = pick(latejoin) + character.forceMove(pick(latejoin)) join_message = "has arrived on the station" else - character.loc = pick(latejoin) + character.forceMove(pick(latejoin)) join_message = "has arrived on the station" character.lastarea = get_area(loc) // Moving wheelchair if they have one if(character.buckled && istype(character.buckled, /obj/structure/chair/wheelchair)) - character.buckled.loc = character.loc + character.buckled.forceMove(character.loc) character.buckled.dir = character.dir character = SSjobs.EquipRank(character, rank, 1) //equips the human @@ -464,8 +464,7 @@ dat += " [a.title].
| " - for(var/jobcat in categorizedJobs) - if(categorizedJobs[jobcat]["colBreak"]) - dat += " | "
- if(length(categorizedJobs[jobcat]["jobs"]) < 1)
- continue
- var/color = categorizedJobs[jobcat]["color"]
- dat += " " + if(num_jobs_available) + dat += "Choose from the following open positions: " + dat += "
|