Refresh"
+ dat += "[PDAIMG(refresh)]Refresh"
if ((!isnull(cartridge)) && (mode == 0))
- dat += " |
Eject [cartridge]"
+ dat += " | [PDAIMG(eject)]Eject [cartridge]"
if (mode)
- dat += " |
Return"
+ dat += " | [PDAIMG(menu)]Return"
if (mode == 0)
dat += "
Retry"
+ dat += "[PDAIMG(refresh)]Retry"
else
switch (mode)
if (0)
@@ -221,38 +221,38 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += "
Notekeeper
Messenger
Honk Synthesizer
Sad Trombone
View Crew Manifest
Set Status Display
Power Monitor
Medical Records
[scanmode == 1 ? "Disable" : "Enable"] Medical Scanner
Security Records
Supply Records
Bots Access
Custodial Locator
Signaler System
Newscaster Access
[scanmode == 3 ? "Disable" : "Enable"] Reagent Scanner
[scanmode == 4 ? "Disable" : "Enable"] Halogen Counter
[scanmode == 5 ? "Disable" : "Enable"] Gas Scanner
Toggle Remote Door
Drone Phone
Atmospheric Scan
[fon ? "Disable" : "Enable"] Flashlight
Notekeeper V2.2
SpaceMessenger V3.9.6
Ringer: [silent == 1 ? "Off" : "On"] | "
- dat += "
Send / Receive: [toff == 1 ? "Off" : "On"] | "
- dat += "
Set Ringtone | "
- dat += "
Messages
Detected PDAs
SpaceMessenger V3.9.6
Clear Messages"
+ dat += "
Messages
Atmospheric Readings
Remote Signaling System
Crew Manifest
Station Status Display Interlink
Power Monitors - Please select one
Power Monitor
Medical Record List
Medical Record
Medical Data
Security Record List
Security Record
Security Data
Supply Record Interlink
Persistent Custodial Object Locator
Newscaster Access
Bots Interlink
refresh)
Return to bot list"
+ menu += "
Scan for active bots| "
if(jobban_isbanned(user, "appearance"))
@@ -490,6 +492,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "High"
dat += " " + dat += "Ambient Occlusion: [ambientocclusion ? "Enabled" : "Disabled"] " + dat += " | "
dat += "Special Role Settings" @@ -855,72 +859,72 @@ GLOBAL_LIST_EMPTY(preferences_datums) return job_engsec_low return 0 -/datum/preferences/proc/SetTraits(mob/user) - if(!SStraits) - to_chat(user, "The trait subsystem is still initializing! Try again in a minute.") +/datum/preferences/proc/SetQuirks(mob/user) + if(!SSquirks) + to_chat(user, "The quirk subsystem is still initializing! Try again in a minute.") return var/list/dat = list() - if(!SStraits.traits.len) - dat += "The trait subsystem hasn't finished initializing, please hold..." + if(!SSquirks.quirks.len) + dat += "The quirk subsystem hasn't finished initializing, please hold..." dat += "" else - dat += " " - dat += " Left-click to add or remove traits. You need one negative trait for every positive trait. "
+ dat += "\ - Traits are applied at roundstart and cannot normally be removed. " + dat += " Left-click to add or remove quirks. You need negative quirks to have positive ones. "
dat += "\ + Quirks are applied at roundstart and cannot normally be removed. " - dat += " \ - Trait balance remaining: [GetTraitBalance()] " - for(var/V in SStraits.traits) - var/datum/trait/T = SStraits.traits[V] - var/trait_name = initial(T.name) - var/has_trait - var/trait_cost = initial(T.value) * -1 + dat += " \ + Quirk balance remaining: [GetQuirkBalance()] " + for(var/V in SSquirks.quirks) + var/datum/quirk/T = SSquirks.quirks[V] + var/quirk_name = initial(T.name) + var/has_quirk + var/quirk_cost = initial(T.value) * -1 var/lock_reason = "This trait is unavailable." - var/trait_conflict = FALSE - for(var/_V in all_traits) - if(_V == trait_name) - has_trait = TRUE - if(initial(T.mood_trait) && CONFIG_GET(flag/disable_human_mood)) + var/quirk_conflict = FALSE + for(var/_V in all_quirks) + if(_V == quirk_name) + has_quirk = TRUE + if(initial(T.mood_quirk) && CONFIG_GET(flag/disable_human_mood)) lock_reason = "Mood is disabled." - trait_conflict = TRUE - if(has_trait) - if(trait_conflict) - all_traits -= trait_name - has_trait = FALSE + quirk_conflict = TRUE + if(has_quirk) + if(quirk_conflict) + all_quirks -= quirk_name + has_quirk = FALSE else - trait_cost *= -1 //invert it back, since we'd be regaining this amount - if(trait_cost > 0) - trait_cost = "+[trait_cost]" + quirk_cost *= -1 //invert it back, since we'd be regaining this amount + if(quirk_cost > 0) + quirk_cost = "+[quirk_cost]" var/font_color = "#AAAAFF" if(initial(T.value) != 0) font_color = initial(T.value) > 0 ? "#AAFFAA" : "#FFAAAA" - if(trait_conflict) - dat += "[trait_name] - [initial(T.desc)] \ + if(quirk_conflict) + dat += "[quirk_name] - [initial(T.desc)] \ LOCKED: [lock_reason] " else - if(has_trait) - dat += "[trait_name] - [initial(T.desc)] \ - [has_trait ? "Lose" : "Take"] ([trait_cost] pts.) " + if(has_quirk) + dat += "[quirk_name] - [initial(T.desc)] \ + [has_quirk ? "Lose" : "Take"] ([quirk_cost] pts.) " else - dat += "[trait_name] - [initial(T.desc)] \ - [has_trait ? "Lose" : "Take"] ([trait_cost] pts.) " + dat += "[quirk_name] - [initial(T.desc)] \ + [has_quirk ? "Lose" : "Take"] ([quirk_cost] pts.) " dat += " Trait Preferences ", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way
+ var/datum/browser/popup = new(user, "mob_occupation", "Quirk Preferences ", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way
popup.set_window_options("can_close=0")
popup.set_content(dat.Join())
popup.open(0)
return
-/datum/preferences/proc/GetTraitBalance()
+/datum/preferences/proc/GetQuirkBalance()
var/bal = 0
- for(var/V in all_traits)
- var/datum/trait/T = SStraits.traits[V]
+ for(var/V in all_quirks)
+ var/datum/quirk/T = SSquirks.quirks[V]
bal -= initial(T.value)
return bal
@@ -977,55 +981,49 @@ GLOBAL_LIST_EMPTY(preferences_datums)
user << browse(null, "window=mob_occupation")
ShowChoices(user)
if("update")
- var/trait = href_list["trait"]
- if(!SStraits.traits[trait])
+ var/quirk = href_list["trait"]
+ if(!SSquirks.quirks[quirk])
return
- var/value = SStraits.trait_points[trait]
+ var/value = SSquirks.quirk_points[quirk]
if(value == 0)
- if(trait in neutral_traits)
- neutral_traits -= trait
- all_traits -= trait
+ if(quirk in neutral_quirks)
+ neutral_quirks -= quirk
+ all_quirks -= quirk
else
- if(all_traits.len >= MAX_TRAITS)
- to_chat(user, "You can't have more than [MAX_TRAITS] traits!")
- return
- neutral_traits += trait
- all_traits += trait
+ neutral_quirks += quirk
+ all_quirks += quirk
else
- var/balance = GetTraitBalance()
- if(trait in positive_traits)
- positive_traits -= trait
- all_traits -= trait
- else if(trait in negative_traits)
+ var/balance = GetQuirkBalance()
+ if(quirk in positive_quirks)
+ positive_quirks -= quirk
+ all_quirks -= quirk
+ else if(quirk in negative_quirks)
if(balance + value < 0)
to_chat(user, "Refunding this would cause you to go below your balance!")
return
- negative_traits -= trait
- all_traits -= trait
+ negative_quirks -= quirk
+ all_quirks -= quirk
else if(value > 0)
- if(all_traits.len >= MAX_TRAITS)
- to_chat(user, "You can't have more than [MAX_TRAITS] traits!")
+ if(positive_quirks.len >= MAX_QUIRKS)
+ to_chat(user, "You can't have more than [MAX_QUIRKS] positive quirks!")
return
if(balance - value < 0)
- to_chat(user, "You don't have enough balance to gain this trait!")
+ to_chat(user, "You don't have enough balance to gain this quirk!")
return
- positive_traits += trait
- all_traits += trait
+ positive_quirks += quirk
+ all_quirks += quirk
else
- if(all_traits.len >= MAX_TRAITS)
- to_chat(user, "You can't have more than [MAX_TRAITS] traits!")
- return
- negative_traits += trait
- all_traits += trait
- SetTraits(user)
+ negative_quirks += quirk
+ all_quirks += quirk
+ SetQuirks(user)
if("reset")
- all_traits = list()
- positive_traits = list()
- negative_traits = list()
- neutral_traits = list()
- SetTraits(user)
+ all_quirks = list()
+ positive_quirks = list()
+ negative_quirks = list()
+ neutral_quirks = list()
+ SetQuirks(user)
else
- SetTraits(user)
+ SetQuirks(user)
return TRUE
switch(href_list["task"])
@@ -1535,6 +1533,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
cit_toggles ^= DIGESTION_NOISES
//END CITADEL EDIT
+ if("ambientocclusion")
+ ambientocclusion = !ambientocclusion
+ if(parent && parent.screen && parent.screen.len)
+ var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
+ PM.filters -= AMBIENT_OCCLUSION
+ if(ambientocclusion)
+ PM.filters += AMBIENT_OCCLUSION
+
if("save")
save_preferences()
save_character()
@@ -1642,4 +1648,4 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(icon_updates)
character.update_body()
character.update_hair()
- character.update_body_parts()
\ No newline at end of file
+ character.update_body_parts()
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index d2efc477c6..f17ddf8b7c 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -130,6 +130,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["uses_glasses_colour"]>> uses_glasses_colour
S["clientfps"] >> clientfps
S["parallax"] >> parallax
+ S["ambientocclusion"] >> ambientocclusion
S["menuoptions"] >> menuoptions
S["enable_tips"] >> enable_tips
S["tip_delay"] >> tip_delay
@@ -160,6 +161,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
clientfps = sanitize_integer(clientfps, 0, 1000, 0)
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
+ ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
@@ -210,6 +212,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["uses_glasses_colour"], uses_glasses_colour)
WRITE_FILE(S["clientfps"], clientfps)
WRITE_FILE(S["parallax"], parallax)
+ WRITE_FILE(S["ambientocclusion"], ambientocclusion)
WRITE_FILE(S["menuoptions"], menuoptions)
WRITE_FILE(S["enable_tips"], enable_tips)
WRITE_FILE(S["tip_delay"], tip_delay)
@@ -310,11 +313,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["job_engsec_med"] >> job_engsec_med
S["job_engsec_low"] >> job_engsec_low
- //Traits
- S["all_traits"] >> all_traits
- S["positive_traits"] >> positive_traits
- S["negative_traits"] >> negative_traits
- S["neutral_traits"] >> neutral_traits
+ //Quirks
+ S["all_quirks"] >> all_quirks
+ S["positive_quirks"] >> positive_quirks
+ S["negative_quirks"] >> negative_quirks
+ S["neutral_quirks"] >> neutral_quirks
//Citadel code
S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"]
@@ -421,10 +424,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
- all_traits = SANITIZE_LIST(all_traits)
- positive_traits = SANITIZE_LIST(positive_traits)
- negative_traits = SANITIZE_LIST(negative_traits)
- neutral_traits = SANITIZE_LIST(neutral_traits)
+ all_quirks = SANITIZE_LIST(all_quirks)
+ positive_quirks = SANITIZE_LIST(positive_quirks)
+ negative_quirks = SANITIZE_LIST(negative_quirks)
+ neutral_quirks = SANITIZE_LIST(neutral_quirks)
cit_character_pref_load(S)
@@ -491,11 +494,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["job_engsec_med"] , job_engsec_med)
WRITE_FILE(S["job_engsec_low"] , job_engsec_low)
- //Traits
- WRITE_FILE(S["all_traits"] , all_traits)
- WRITE_FILE(S["positive_traits"] , positive_traits)
- WRITE_FILE(S["negative_traits"] , negative_traits)
- WRITE_FILE(S["neutral_traits"] , neutral_traits)
+ //Quirks
+ WRITE_FILE(S["all_quirks"] , all_quirks)
+ WRITE_FILE(S["positive_quirks"] , positive_quirks)
+ WRITE_FILE(S["negative_quirks"] , negative_quirks)
+ WRITE_FILE(S["neutral_quirks"] , neutral_quirks)
cit_character_pref_save(S)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index b15109bcb1..9ab929ba20 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -81,12 +81,14 @@
/obj/item/clothing/equipped(mob/user, slot)
..()
-
+ if (!istype(user))
+ return
if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item?
- for(var/variable in user_vars_to_edit)
- if(variable in user.vars)
- user_vars_remembered[variable] = user.vars[variable]
- user.vars[variable] = user_vars_to_edit[variable]
+ if (LAZYLEN(user_vars_to_edit))
+ for(var/variable in user_vars_to_edit)
+ if(variable in user.vars)
+ LAZYSET(user_vars_remembered, variable, user.vars[variable])
+ user.vv_edit_var(variable, user_vars_to_edit[variable])
/obj/item/clothing/examine(mob/user)
..()
@@ -285,4 +287,4 @@ BLIND // can't see anything
Shreds.desc = "The sad remains of what used to be [name]."
deconstruct(FALSE)
else
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 1a596fbecd..7b8b417561 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -365,6 +365,7 @@
scan_reagents = 1
flags_1 = NODROP_1
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ resistance_flags = LAVA_PROOF | FIRE_PROOF
/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params)
if(istype(W, src) && W != src && W.loc == user)
diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm
index 439197f285..347bc809a3 100644
--- a/code/modules/clothing/glasses/engine_goggles.dm
+++ b/code/modules/clothing/glasses/engine_goggles.dm
@@ -93,7 +93,7 @@
MA.alpha = 180
MA.maptext = "[strength]k"
MA.color = "#64C864"
- MA.layer = AREA_LAYER
+ MA.layer = FLY_LAYER
pic.appearance = MA
flick_overlay(pic, list(user.client), 8)
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 2e9b7f83f2..4bf68c3bd0 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -158,12 +158,20 @@
strip_delay = 100
dog_fashion = null
+/obj/item/clothing/head/helmet/roman/fake
+ desc = "An ancient helmet made of plastic and leather."
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+
/obj/item/clothing/head/helmet/roman/legionaire
name = "roman legionaire helmet"
desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it."
icon_state = "roman_c"
item_state = "roman_c"
+/obj/item/clothing/head/helmet/roman/legionaire/fake
+ desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it."
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+
/obj/item/clothing/head/helmet/gladiator
name = "gladiator helmet"
desc = "Ave, Imperator, morituri te salutant."
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index bea33a95b6..d7a7e7f5e2 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -321,4 +321,27 @@
name = "headress of Nemes"
desc = "Lavish space tomb not included."
icon_state = "nemes_headdress"
- icon_state = "nemes_headdress"
\ No newline at end of file
+ icon_state = "nemes_headdress"
+
+/obj/item/clothing/head/frenchberet
+ name = "french beret"
+ desc = "A quality beret, infused with the aroma of chain-smoking, wine-swilling Parisians. You feel less inclined to engage military conflict, for some reason."
+ icon_state = "beretblack"
+
+/obj/item/clothing/head/frenchberet/speechModification(M)
+ if(copytext(M, 1, 2) != "*")
+ M = " [M]"
+ var/list/french_words = strings("french_replacement.json", "french")
+
+ for(var/key in french_words)
+ var/value = french_words[key]
+ if(islist(value))
+ value = pick(value)
+
+ M = replacetextEx(M, " [uppertext(key)]", " [uppertext(value)]")
+ M = replacetextEx(M, " [capitalize(key)]", " [capitalize(value)]")
+ M = replacetextEx(M, " [key]", " [value]")
+
+ if(prob(3))
+ M += pick(" Honh honh honh!"," Honh!"," Zut Alors!")
+ return trim(M)
\ No newline at end of file
diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm
index 53bd5f32a4..96b8517e92 100644
--- a/code/modules/clothing/masks/miscellaneous.dm
+++ b/code/modules/clothing/masks/miscellaneous.dm
@@ -47,7 +47,7 @@
/obj/item/clothing/mask/fakemoustache/italian/speechModification(M)
if(copytext(M, 1, 2) != "*")
M = " [M]"
- var/list/italian_words = strings("word_replacement.json", "italian")
+ var/list/italian_words = strings("italian_replacement.json", "italian")
for(var/key in italian_words)
var/value = italian_words[key]
diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm
index 7ffd9c1f40..df3aa5007f 100644
--- a/code/modules/clothing/shoes/bananashoes.dm
+++ b/code/modules/clothing/shoes/bananashoes.dm
@@ -10,7 +10,7 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/Initialize()
. = ..()
- AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE)
+ AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE, list(/obj/item/stack))
AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75)
if(always_noslip)
flags_1 |= NOSLIP_1
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index 68d7073612..f5b4a31290 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -126,7 +126,7 @@
if(user.transferItemToLoc(I, src))
jetpack = I
to_chat(user, "You successfully install the jetpack into [src].")
-
+ return
else if(istype(I, /obj/item/screwdriver))
if(!jetpack)
to_chat(user, "[src] has no jetpack installed.")
@@ -139,6 +139,8 @@
jetpack.forceMove(drop_location())
jetpack = null
to_chat(user, "You successfully remove the jetpack from [src].")
+ return
+ return ..()
/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot)
@@ -237,6 +239,9 @@
brightness_on = 7
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator)
+/obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize()
+ . = ..()
+ AddComponent(/datum/component/armor_plate)
/obj/item/clothing/suit/space/hardsuit/mining
icon_state = "hardsuit-mining"
@@ -250,6 +255,10 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+/obj/item/clothing/suit/space/hardsuit/mining/Initialize()
+ . = ..()
+ AddComponent(/datum/component/armor_plate)
+
//Syndicate hardsuit
/obj/item/clothing/head/helmet/space/hardsuit/syndi
name = "blood-red hardsuit helmet"
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index fb07b15a40..49b4fb7766 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -165,186 +165,6 @@
. = ..()
allowed = GLOB.detective_vest_allowed
-//Reactive armor
-/obj/item/clothing/suit/armor/reactive
- name = "reactive armor"
- desc = "Doesn't seem to do much for some reason."
- var/active = 0
- var/reactivearmor_cooldown_duration = 0 //cooldown specific to reactive armor
- var/reactivearmor_cooldown = 0
- icon_state = "reactiveoff"
- item_state = "reactiveoff"
- blood_overlay_type = "armor"
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
- actions_types = list(/datum/action/item_action/toggle)
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- hit_reaction_chance = 50
-
-
-/obj/item/clothing/suit/armor/reactive/attack_self(mob/user)
- src.active = !( src.active )
- if (src.active)
- to_chat(user, "[src] is now active.")
- src.icon_state = "reactive"
- src.item_state = "reactive"
- else
- to_chat(user, "[src] is now inactive.")
- src.icon_state = "reactiveoff"
- src.item_state = "reactiveoff"
- src.add_fingerprint(user)
- return
-
-/obj/item/clothing/suit/armor/reactive/emp_act(severity)
- active = 0
- src.icon_state = "reactiveoff"
- src.item_state = "reactiveoff"
- reactivearmor_cooldown = world.time + 200
- ..()
-
-//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!)
-/obj/item/clothing/suit/armor/reactive/teleport
- name = "reactive teleport armor"
- desc = "Someone separated our Research Director from his own head!"
- var/tele_range = 6
- var/rad_amount= 15
- reactivearmor_cooldown_duration = 100
-
-/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!active)
- return 0
- if(prob(hit_reaction_chance))
- var/mob/living/carbon/human/H = owner
- if(world.time < reactivearmor_cooldown)
- owner.visible_message("The reactive teleport system is still recharging! It fails to teleport [H]!")
- return
- owner.visible_message("The reactive teleport system flings [H] clear of [attack_text], shutting itself off in the process!")
- var/list/turfs = new/list()
- for(var/turf/T in orange(tele_range, H))
- if(T.density)
- continue
- if(T.x>world.maxx-tele_range || T.xPriority Announcement") - to_chat(target, "The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle. ") - target.playsound_local(null, 'sound/ai/shuttledock.ogg', 100) + target.playsound_local(source, 'sound/voice/bfreeze.ogg', 35, 0) + if("mech") + var/mech_dir = pick(GLOB.cardinals) + for(var/i in 1 to rand(4,9)) + if(prob(75)) + target.playsound_local(source, 'sound/mecha/mechstep.ogg', 40, 1) + source = get_step(source, mech_dir) + else + target.playsound_local(source, 'sound/mecha/mechturn.ogg', 40, 1) + mech_dir = pick(GLOB.cardinals) + sleep(10) //Deconstructing a wall if("wall_decon") - target.playsound_local(null, 'sound/items/welder.ogg', 15, 1) + target.playsound_local(source, 'sound/items/welder.ogg', 50, 1) sleep(105) - target.playsound_local(null, 'sound/items/welder2.ogg', 15, 1) + target.playsound_local(source, 'sound/items/welder2.ogg', 50, 1) sleep(15) - target.playsound_local(null, 'sound/items/ratchet.ogg', 15, 1) + target.playsound_local(source, 'sound/items/ratchet.ogg', 50, 1) //Hacking a door if("door_hack") - target.playsound_local(null, 'sound/items/screwdriver.ogg', 15, 1) - sleep(rand(10,30)) - for(var/i = rand(1,3), i>0, i--) - target.playsound_local(null, 'sound/weapons/empty.ogg', 15, 1) - sleep(rand(10,30)) - target.playsound_local(null, 'sound/machines/airlockforced.ogg', 15, 1) - if("esword") - target.playsound_local(null, 'sound/weapons/saberon.ogg',35,1) + target.playsound_local(source, 'sound/items/screwdriver.ogg', 50, 1) + sleep(rand(40,80)) + target.playsound_local(source, 'sound/machines/airlockforced.ogg', 30, 1) + qdel(src) + +/datum/hallucination/weird_sounds + +/datum/hallucination/weird_sounds/New(mob/living/carbon/C, forced = TRUE, sound_type) + set waitfor = FALSE + ..() + var/turf/source = random_far_turf() + if(!sound_type) + sound_type = pick("phone","hallelujah","highlander","hyperspace","game_over","creepy","tesla") + feedback_details += "Type: [sound_type]" + //Strange audio + switch(sound_type) + if("phone") + target.playsound_local(source, 'sound/weapons/ring.ogg', 15) + sleep(25) + target.playsound_local(source, 'sound/weapons/ring.ogg', 15) + sleep(25) + target.playsound_local(source, 'sound/weapons/ring.ogg', 15) + sleep(25) + target.playsound_local(source, 'sound/weapons/ring.ogg', 15) + if("hyperspace") + target.playsound_local(null, 'sound/effects/hyperspace_begin.ogg', 50) + if("hallelujah") + target.playsound_local(source, 'sound/effects/pray_chaplain.ogg', 50) + if("highlander") + target.playsound_local(null, 'sound/misc/highlander.ogg', 50) + if("game_over") + target.playsound_local(source, 'sound/misc/compiler-failure.ogg', 50) + if("laughter") + if(prob(50)) + target.playsound_local(source, 'sound/voice/human/womanlaugh.ogg', 50, 1) + else + target.playsound_local(source, pick('sound/voice/human/manlaugh1.ogg', 'sound/voice/human/manlaugh2.ogg'), 50, 1) + if("creepy") + //These sounds are (mostly) taken from Hidden: Source + target.playsound_local(source, pick(CREEPY_SOUNDS), 50, 1) + if("tesla") //Tesla loose! + target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 35, 1) + sleep(30) + target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 65, 1) + sleep(30) + target.playsound_local(source, 'sound/magic/lightningbolt.ogg', 100, 1) + + qdel(src) + +/datum/hallucination/stationmessage + +/datum/hallucination/stationmessage/New(mob/living/carbon/C, forced = TRUE, message) + set waitfor = FALSE + ..() + if(!message) + message = pick("ratvar","shuttle_dock","blob_alert","malf_ai","meteors","supermatter") + feedback_details += "Type: [message]" + switch(message) if("blob_alert") to_chat(target, " Biohazard Alert") to_chat(target, "Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak. ") - target.playsound_local(null, 'sound/ai/outbreak5.ogg', 100, 0) - if("tesla") //Tesla loose! - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 35, 1) - sleep(30) - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 65, 1) - sleep(30) - target.playsound_local(null, 'sound/magic/lightningbolt.ogg', 100, 1) + SEND_SOUND(target, 'sound/ai/outbreak5.ogg') + if("ratvar") + target.playsound_local(target, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE) + target.playsound_local(target, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE) + sleep(27) + target.playsound_local(target, 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE) + if("shuttle_dock") + to_chat(target, " Priority Announcement") + to_chat(target, "The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle. ") + SEND_SOUND(target, 'sound/ai/shuttledock.ogg') if("malf_ai") //AI is doomsdaying! to_chat(target, " Anomaly Alert") to_chat(target, "Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core. ") - target.playsound_local(null, 'sound/ai/aimalf.ogg', 100, 0) + SEND_SOUND(target, 'sound/ai/aimalf.ogg') if("meteors") //Meteors inbound! to_chat(target, " Meteor Alert") to_chat(target, "Meteors have been detected on collision course with the station. ") - target.playsound_local(null, 'sound/ai/meteors.ogg', 100, 0) - qdel(src) + SEND_SOUND(target, 'sound/ai/meteors.ogg') + if("supermatter") + SEND_SOUND(target, 'sound/magic/charge.ogg') + to_chat(target, "You feel reality distort for a moment...") /datum/hallucination/hudscrew - cost = 10 -/datum/hallucination/hudscrew/New(mob/living/carbon/T, forced = TRUE) +/datum/hallucination/hudscrew/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() //Screwy HUD @@ -933,9 +911,8 @@ GLOBAL_LIST_INIT(hallucinations_major, list( qdel(src) /datum/hallucination/fake_alert - cost = 15 -/datum/hallucination/fake_alert/New(mob/living/carbon/T, forced = TRUE, specific, duration = 150) +/datum/hallucination/fake_alert/New(mob/living/carbon/C, forced = TRUE, specific, duration = 150) set waitfor = FALSE ..() var/alert_type = pick("not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","weightless","fire","locked","hacked","temphot","tempcold","pressure") @@ -983,15 +960,14 @@ GLOBAL_LIST_INIT(hallucinations_major, list( if("hacked") target.throw_alert(alert_type, /obj/screen/alert/hacked, override = TRUE) if("charge") - target.throw_alert(alert_type,/obj/screen/alert/emptycell, override = TRUE) + target.throw_alert(alert_type, /obj/screen/alert/emptycell, override = TRUE) sleep(duration) target.clear_alert(alert_type, clear_override = TRUE) qdel(src) /datum/hallucination/items - cost = 15 -/datum/hallucination/items/New(mob/living/carbon/T, forced = TRUE) +/datum/hallucination/items/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() //Strange items @@ -1052,9 +1028,8 @@ GLOBAL_LIST_INIT(hallucinations_major, list( qdel(src) /datum/hallucination/dangerflash - cost = 15 -/datum/hallucination/dangerflash/New(mob/living/carbon/T, forced = TRUE) +/datum/hallucination/dangerflash/New(mob/living/carbon/C, forced = TRUE, danger_type) set waitfor = FALSE ..() //Flashes of danger @@ -1064,32 +1039,92 @@ GLOBAL_LIST_INIT(hallucinations_major, list( possible_points += F if(possible_points.len) var/turf/open/floor/danger_point = pick(possible_points) - - switch(rand(1,5)) - if(1) - target.halimage = image('icons/turf/space.dmi',danger_point,"[rand(1,25)]",TURF_LAYER) - if(2) - target.halimage = image('icons/turf/floors/lava.dmi',danger_point,"smooth",TURF_LAYER) - if(3) - target.halimage = image('icons/turf/floors/Chasms.dmi',danger_point,"smooth",TURF_LAYER) - if(4) - target.halimage = image('icons/effects/effects.dmi',danger_point,"anom",OBJ_LAYER+0.01) - if(5) - target.halimage = image('icons/effects/effects.dmi',danger_point,"electricity2",OBJ_LAYER+0.01) - - - if(target.client) - target.client.images += target.halimage - sleep(rand(200,450)) - if(target.client) - target.client.images -= target.halimage - QDEL_NULL(target.halimage) + if(!danger_type) + danger_type = pick("lava","chasm","anomaly") + switch(danger_type) + if("lava") + new /obj/effect/hallucination/danger/lava(danger_point, target) + if("chasm") + new /obj/effect/hallucination/danger/chasm(danger_point, target) + if("anomaly") + new /obj/effect/hallucination/danger/anomaly(danger_point, target) qdel(src) -/datum/hallucination/death - cost = 40 +/obj/effect/hallucination/danger + var/image/image -/datum/hallucination/death/New(mob/living/carbon/T, forced = TRUE) +/obj/effect/hallucination/danger/proc/show_icon() + return + +/obj/effect/hallucination/danger/proc/clear_icon() + if(image && target.client) + target.client.images -= image + +/obj/effect/hallucination/danger/Initialize(mapload, _target) + . = ..() + target = _target + show_icon() + QDEL_IN(src, rand(200, 450)) + +/obj/effect/hallucination/danger/Destroy() + clear_icon() + . = ..() + +/obj/effect/hallucination/danger/lava + name = "lava" + +/obj/effect/hallucination/danger/lava/show_icon() + image = image('icons/turf/floors/lava.dmi',src,"smooth",TURF_LAYER) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/lava/Crossed(atom/movable/AM) + if(AM == target) + target.adjustStaminaLoss(20) + new /datum/hallucination/fire(target) + +/obj/effect/hallucination/danger/chasm + name = "chasm" + +/obj/effect/hallucination/danger/chasm/show_icon() + image = image('icons/turf/floors/Chasms.dmi',src,"smooth",TURF_LAYER) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/chasm/Crossed(atom/movable/AM) + if(AM == target) + to_chat(target, "You fall into the chasm!") + target.Knockdown(40) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, target, "It's surprisingly shallow."), 15) + QDEL_IN(src, 30) + +/obj/effect/hallucination/danger/anomaly + name = "flux wave anomaly" + +/obj/effect/hallucination/danger/anomaly/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/hallucination/danger/anomaly/process() + if(prob(70)) + step(src,pick(GLOB.alldirs)) + +/obj/effect/hallucination/danger/anomaly/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/hallucination/danger/anomaly/show_icon() + image = image('icons/effects/effects.dmi',src,"electricity2",OBJ_LAYER+0.01) + if(target.client) + target.client.images += image + +/obj/effect/hallucination/danger/anomaly/Crossed(atom/movable/AM) + if(AM == target) + new /datum/hallucination/shock(target) + +/datum/hallucination/death + +/datum/hallucination/death/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() target.set_screwyhud(SCREWYHUD_DEAD) @@ -1107,8 +1142,8 @@ GLOBAL_LIST_INIT(hallucinations_major, list( fakemob = target //ever been so lonely you had to haunt yourself? if(fakemob) sleep(rand(20, 50)) - to_chat(target, "DEAD: [fakemob.name] says, \"[pick("rip","hey [target.first_name()]","you too?","is the AI rogue?",\ - "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "abductors","double agents","viruses","badmins","you")]")]\"") + to_chat(target, "DEAD: [fakemob.name] says, \"[pick("rip","why did i just drop dead?","hey [target.first_name()]","git gud","you too?","is the AI rogue?",\ + "i[prob(50)?" fucking":""] hate [pick("blood cult", "clock cult", "revenants", "this round","this","myself","admins","you")]")]\"") sleep(rand(70,90)) target.set_screwyhud(SCREWYHUD_NONE) target.SetKnockdown(0) @@ -1116,38 +1151,93 @@ GLOBAL_LIST_INIT(hallucinations_major, list( qdel(src) /datum/hallucination/fire - cost = 25 + var/active = TRUE + var/stage = 0 + var/image/fire_overlay -/datum/hallucination/fire/New(mob/living/carbon/T, forced = TRUE) +/datum/hallucination/fire/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() - var/image/fire_overlay = image('icons/mob/OnFire.dmi', target, "Standing", ABOVE_MOB_LAYER) + target.fire_stacks = max(target.fire_stacks, 0.1) //Placebo flammability + fire_overlay = image('icons/mob/OnFire.dmi', target, "Standing", ABOVE_MOB_LAYER) if(target.client) target.client.images += fire_overlay to_chat(target, "You're set on fire!") target.throw_alert("fire", /obj/screen/alert/fire, override = TRUE) sleep(20) - target.throw_alert("temp", /obj/screen/alert/hot, 1, override = TRUE) - sleep(30) - target.clear_alert("temp", clear_override = TRUE) - target.throw_alert("temp", /obj/screen/alert/hot, 2, override = TRUE) - sleep(30) - target.clear_alert("temp", clear_override = TRUE) - target.throw_alert("temp", /obj/screen/alert/hot, 3, override = TRUE) + for(var/i in 1 to 3) + if(target.fire_stacks <= 0) + clear_fire() + return + stage++ + update_temp() + sleep(30) for(var/i in 1 to rand(5, 10)) + if(target.fire_stacks <= 0) + clear_fire() + return target.adjustStaminaLoss(15) - sleep(25) + sleep(20) + clear_fire() + +/datum/hallucination/fire/proc/update_temp() + if(stage <= 0) + target.clear_alert("temp", clear_override = TRUE) + else + target.clear_alert("temp", clear_override = TRUE) + target.throw_alert("temp", /obj/screen/alert/hot, stage, override = TRUE) + +/datum/hallucination/fire/proc/clear_fire() + if(!active) + return + active = FALSE target.clear_alert("fire", clear_override = TRUE) - target.clear_alert("temp", clear_override = TRUE) if(target.client) target.client.images -= fire_overlay QDEL_NULL(fire_overlay) + while(stage > 0) + stage-- + update_temp() + sleep(30) qdel(src) -/datum/hallucination/husks - cost = 20 +/datum/hallucination/shock + var/image/shock_image + var/image/electrocution_skeleton_anim -/datum/hallucination/husks/New(mob/living/carbon/T, forced = TRUE) +/datum/hallucination/shock/New(mob/living/carbon/C, forced = TRUE) + set waitfor = FALSE + ..() + shock_image = image(target, target, dir = target.dir) + shock_image.appearance_flags |= KEEP_APART + shock_image.color = rgb(0,0,0) + shock_image.override = TRUE + electrocution_skeleton_anim = image('icons/mob/human.dmi', target, icon_state = "electrocuted_base", layer=ABOVE_MOB_LAYER) + electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART + to_chat(target, "You feel a powerful shock course through your body!") + if(target.client) + target.client.images |= shock_image + target.client.images |= electrocution_skeleton_anim + addtimer(CALLBACK(src, .proc/reset_shock_animation), 40) + target.playsound_local(get_turf(src), "sparks", 100, 1) + target.staminaloss += 50 + target.Stun(40) + target.jitteriness += 1000 + target.do_jitter_animation(target.jitteriness) + addtimer(CALLBACK(src, .proc/shock_drop), 20) + +/datum/hallucination/shock/proc/reset_shock_animation() + if(target.client) + target.client.images.Remove(shock_image) + target.client.images.Remove(electrocution_skeleton_anim) + +/datum/hallucination/shock/proc/shock_drop() + target.jitteriness = max(target.jitteriness - 990, 10) //Still jittery, but vastly less + target.Knockdown(60) + +/datum/hallucination/husks + +/datum/hallucination/husks/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() if(!target.halbody) @@ -1178,7 +1268,6 @@ GLOBAL_LIST_INIT(hallucinations_major, list( //hallucination projectile code in code/modules/projectiles/projectile/special.dm /datum/hallucination/stray_bullet - cost = 15 /datum/hallucination/stray_bullet/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE @@ -1194,29 +1283,4 @@ GLOBAL_LIST_INIT(hallucinations_major, list( H.hal_target = target H.preparePixelProjectile(target, start) H.fire() - qdel(src) - -//Reality Dissociation Syndrome hallucinations only trigger in special cases and have no cost -/datum/hallucination/rds - cost = 0 - -/datum/hallucination/rds/fourth_wall/New(mob/living/carbon/C, forced = TRUE) - ..() - to_chat(C, "[pick("Leave the server" , "Close the game window")] [pick("immediately", "right now")].") - -/datum/hallucination/rds/supermatter/New(mob/living/carbon/C, forced = TRUE) - ..() - SEND_SOUND(C, 'sound/magic/charge.ogg') - to_chat(C, "You feel reality distort for a moment...") - -/datum/hallucination/rds/narsie/New(mob/living/carbon/C, forced = TRUE) - C.playsound_local(C, 'sound/creatures/narsie_rises.ogg', 50, FALSE, pressure_affected = FALSE) - to_chat(C, "NAR-SIE HAS RISEN") - -/datum/hallucination/rds/ark/New(mob/living/carbon/C, forced = TRUE) - set waitfor = FALSE - ..() - C.playsound_local(C, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE) - C.playsound_local(C, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE) - sleep(27) - C.playsound_local(C, 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE) + qdel(src) \ No newline at end of file diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index ee5dc7e328..8972ceba12 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -298,6 +298,29 @@ icon_state = "grappabottle" list_reagents = list("grappa" = 100) +/obj/item/reagent_containers/food/drinks/bottle/sake + name = "Ryo's traditional sake" + desc = "Sweet as can be, and burns like fire going down." + icon_state = "sakebottle" + list_reagents = list("sake" = 100) + +/obj/item/reagent_containers/food/drinks/bottle/sake/Initialize() + . = ..() + if(prob(10)) + name = "Fluffy Tail Sake" + desc += " On the bottle is a picture of a kitsune with nine touchable tails." + icon_state = "sakebottle_k" + else if(prob(10)) + name = "Inubashiri's Home Brew" + desc += " Awoo." + icon_state = "sakebottle_i" + +/obj/item/reagent_containers/food/drinks/bottle/fernet + name = "Fernet Bronca" + desc = "A bottle of pure Fernet Bronca, produced in Cordoba Space Station" + icon_state = "fernetbottle" + list_reagents = list("fernet" = 100) + //////////////////////////JUICES AND STUFF /////////////////////// /obj/item/reagent_containers/food/drinks/bottle/orangejuice @@ -344,6 +367,15 @@ list_reagents = list("limejuice" = 100) foodtype = FRUIT +/obj/item/reagent_containers/food/drinks/bottle/menthol + name = "menthol" + desc = "Tastes naturally minty, and imparts a very mild numbing sensation." + icon_state = "mentholbox" + item_state = "carton" + lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' + isGlass = FALSE + list_reagents = list("menthol" = 100) ////////////////////////// MOLOTOV /////////////////////// /obj/item/reagent_containers/food/drinks/bottle/molotov diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm index 353bfc6538..3df4ca4bd0 100644 --- a/code/modules/food_and_drinks/food/customizables.dm +++ b/code/modules/food_and_drinks/food/customizables.dm @@ -158,7 +158,7 @@ desc = "A timeless classic." ingredients_placement = INGREDIENTS_STACKPLUSTOP icon = 'icons/obj/food/burgerbread.dmi' - icon_state = "bun" + icon_state = "custburg" foodtype = GRAIN diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 2522ec9cf2..edc2305261 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -299,8 +299,7 @@ qdel(S) return TRUE for(var/obj/item/stack/sheet/wetleather/WL in src) - var/obj/item/stack/sheet/leather/L = new(drop_location()) - L.amount = WL.amount + new /obj/item/stack/sheet/leather(drop_location(), WL.amount) qdel(WL) return TRUE return FALSE diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm index 43b3ba07ab..0ee01ae9e2 100644 --- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm +++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm @@ -139,7 +139,7 @@ required_reagents = list("tequila" = 2, "kahlua" = 1) /datum/chemical_reaction/tequila_sunrise - name = "tequila Sunrise" + name = "Tequila Sunrise" id = "tequilasunrise" results = list("tequilasunrise" = 3) required_reagents = list("tequila" = 2, "orangejuice" = 1) @@ -623,4 +623,61 @@ id = "crevice_spike" results = list("crevice_spike" = 6) required_reagents = list("limejuice" = 2, "capsaicin" = 4) //2 powdered delta (sour), 4 flanergide (spicy) - mix_message = "The mixture stings your eyes as it settles." \ No newline at end of file + mix_message = "The mixture stings your eyes as it settles." + +/datum/chemical_reaction/sake + name = "sake" + id = "sake" + results = list("sake" = 10) + required_reagents = list("rice" = 10) + required_catalysts = list("enzyme" = 5) + mix_message = "The rice grains ferment into a clear, sweet-smelling liquid." + +/datum/chemical_reaction/alexander + name = "Alexander" + id = "alexander" + results = list("alexander" = 3) + required_reagents = list("cognac" = 1, "creme_de_cacao" = 1, "cream" = 1) + +/datum/chemical_reaction/sidecar + name = "Sidecar" + id = "sidecar" + results = list("sidecar" = 4) + required_reagents = list("cognac" = 2, "triple_sec" = 1, "lemonjuice" = 1) + +/datum/chemical_reaction/between_the_sheets + name = "Between the Sheets" + id = "between_the_sheets" + results = list("between_the_sheets" = 5) + required_reagents = list("rum" = 1, "sidecar" = 4) + +/datum/chemical_reaction/kamikaze + name = "Kamikaze" + id = "kamikaze" + results = list("kamikaze" = 3) + required_reagents = list("vodka" = 1, "triple_sec" = 1, "limejuice" = 1) + +/datum/chemical_reaction/mojito + name = "Mojito" + id = "mojito" + results = list("mojito" = 5) + required_reagents = list("rum" = 1, "sugar" = 1, "limejuice" = 1, "sodawater" = 1, "menthol" = 1) + +/datum/chemical_reaction/fernet_cola + name = "Fernet Cola" + id = "fernet_cola" + results = list("fernet_cola" = 2) + required_reagents = list("fernet" = 1, "cola" = 1) + + +/datum/chemical_reaction/fanciulli + name = "Fanciulli" + id = "fanciulli" + results = list("fanciulli" = 2) + required_reagents = list("manhattan" = 1, "fernet" = 1) + +/datum/chemical_reaction/branca_menta + name = "Branca Menta" + id = "branca_menta" + results = list("branca_menta" = 3) + required_reagents = list("fernet" = 1, "creme_de_menthe" = 1, "ice" = 1) diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm index 401e55c9e5..7fe939ea29 100644 --- a/code/modules/goonchat/browserOutput.dm +++ b/code/modules/goonchat/browserOutput.dm @@ -46,8 +46,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic if(!owner) return - var/datum/asset/stuff = get_asset_datum(/datum/asset/simple/goonchat) - stuff.register() + var/datum/asset/stuff = get_asset_datum(/datum/asset/group/goonchat) stuff.send(owner) owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput") diff --git a/code/modules/goonchat/browserassets/html/browserOutput.html b/code/modules/goonchat/browserassets/html/browserOutput.html index d40fd75f39..fe3be9486c 100644 --- a/code/modules/goonchat/browserassets/html/browserOutput.html +++ b/code/modules/goonchat/browserassets/html/browserOutput.html @@ -6,6 +6,7 @@ + diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index 4f740f4e61..701167c853 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -70,6 +70,7 @@ begin_month = DECEMBER end_day = 2 end_month = JANUARY + drone_hat = /obj/item/clothing/head/festive /datum/holiday/new_year/getStationPrefix() return pick("Party","New","Hangover","Resolution") @@ -78,6 +79,7 @@ name = "Groundhog Day" begin_day = 2 begin_month = FEBRUARY + drone_hat = /obj/item/clothing/head/helmet/space/chronos /datum/holiday/valentines name = VALENTINES @@ -92,6 +94,7 @@ name = "Birthday of Space Station 13" begin_day = 16 begin_month = FEBRUARY + drone_hat = /obj/item/clothing/head/festive /datum/holiday/birthday/greet() var/game_age = text2num(time2text(world.timeofday, "YY")) - 3 @@ -202,6 +205,7 @@ name = "UFO Day" begin_day = 2 begin_month = JULY + drone_hat = /obj/item/clothing/mask/facehugger/dead /datum/holiday/UFO/getStationPrefix() //Is such a thing even possible? return pick("Ayy","Truth","Tsoukalos","Mulder") //Yes it is! @@ -272,11 +276,13 @@ name = "Smiling Day" begin_day = 7 begin_month = OCTOBER + drone_hat = /obj/item/clothing/head/papersack/smiley /datum/holiday/boss name = "Boss' Day" begin_day = 16 begin_month = OCTOBER + drone_hat = /obj/item/clothing/head/that /datum/holiday/halloween name = HALLOWEEN @@ -305,6 +311,7 @@ name = "Flowers Day" begin_day = 19 begin_month = NOVEMBER + drone_hat = /obj/item/reagent_containers/food/snacks/grown/moonflower /datum/holiday/hello name = "Saying-'Hello' Day" @@ -312,7 +319,7 @@ begin_month = NOVEMBER /datum/holiday/hello/greet() - return "[pick(list("Aloha", "Bonjour", "Hello", "Hi", "Greetings", "Salutations", "Bienvenidos", "Hola", "Howdy"))]! " + ..() + return "[pick(list("Aloha", "Bonjour", "Hello", "Hi", "Greetings", "Salutations", "Bienvenidos", "Hola", "Howdy", "Ni hao", "Guten Tag", "Konnichiwa", "G'day cunt"))]! " + ..() /datum/holiday/human_rights name = "Human-Rights Day" @@ -323,6 +330,7 @@ name = "Monkey Day" begin_day = 14 begin_month = DECEMBER + drone_hat = /obj/item/clothing/mask/gas/monkeymask /datum/holiday/thanksgiving name = "Thanksgiving in the United States" diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index 624394f878..6da117da88 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -262,8 +262,7 @@ if(!check_cost(D.materials, amount)) return FALSE - var/obj/item/stack/product = new D.build_path(loc) - product.amount = amount + new D.build_path(drop_location(), amount) for(var/R in D.make_reagents) beaker.reagents.add_reagent(R, D.make_reagents[R]*amount) else diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index dd1d1777d8..f0c7bba885 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -85,14 +85,15 @@ interact(user) return else if(istype(I, /obj/item/disk/plantgene)) - if(disk) - to_chat(user, "A data disk is already loaded into the machine!") - else - if(!user.transferItemToLoc(I, src)) - return - disk = I - to_chat(user, "You add [I] to the machine.") - interact(user) + if (operation) + to_chat(user, "Please complete current operation.") + return + eject_disk() + if(!user.transferItemToLoc(I, src)) + return + disk = I + to_chat(user, "You add [I] to the machine.") + interact(user) else ..() @@ -266,18 +267,13 @@ to_chat(usr, "You add [I] to the machine.") update_icon() else if(href_list["eject_disk"] && !operation) - if (disk) - disk.forceMove(drop_location()) - disk.verb_pickup() - disk = null - update_genes() - else - var/obj/item/I = usr.get_active_held_item() - if(istype(I, /obj/item/disk/plantgene)) - if(!usr.transferItemToLoc(I, src)) - return - disk = I - to_chat(usr, "You add [I] to the machine.") + var/obj/item/I = usr.get_active_held_item() + eject_disk() + if(istype(I, /obj/item/disk/plantgene)) + if(!usr.transferItemToLoc(I, src)) + return + disk = I + to_chat(usr, "You add [I] to the machine.") else if(href_list["op"] == "insert" && disk && disk.gene && seed) if(!operation) // Wait for confirmation operation = "insert" @@ -365,6 +361,16 @@ update_genes() update_icon() +/obj/machinery/plantgenes/proc/eject_disk() + if (disk && !operation) + if(Adjacent(usr) && !issilicon(usr)) + if (!usr.put_in_hands(disk)) + disk.forceMove(drop_location()) + else + disk.forceMove(drop_location()) + disk = null + update_genes() + /obj/machinery/plantgenes/proc/update_genes() core_genes = list() reagent_genes = list() diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index 630c072edb..8e55f8a15d 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -36,19 +36,8 @@ continue grassAmt += 1 + round(G.seed.potency * tile_coefficient) qdel(G) - var/obj/item/stack/tile/GT = new stacktype(user.loc) - while(grassAmt > GT.max_amount) - GT.amount = GT.max_amount - grassAmt -= GT.max_amount - GT = new stacktype(user.loc) - GT.amount = grassAmt - for(var/obj/item/stack/tile/T in user.loc) - if((T.type == stacktype) && (T.amount < T.max_amount)) - GT.merge(T) - if(GT.amount <= 0) - break + new stacktype(user.drop_location(), grassAmt) qdel(src) - return // Carpet /obj/item/seeds/grass/carpet diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index 9eaa8747b9..55c0da91f2 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -22,7 +22,7 @@ var/blood_type = null var/list/features = null var/factions = null - var/list/traits = null + var/list/quirks = null var/contains_sample = 0 /obj/item/seeds/replicapod/Initialize() @@ -42,7 +42,7 @@ blood_type = B.data["blood_type"] features = B.data["features"] factions = B.data["factions"] - factions = B.data["traits"] + factions = B.data["quirks"] contains_sample = TRUE visible_message("The [src] is injected with a fresh blood sample.") else @@ -114,7 +114,7 @@ podman.faction |= factions if(!features["mcolor"]) features["mcolor"] = "#59CE00" - for(var/V in traits) + for(var/V in quirks) new V(podman) podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman podman.set_cloned_appearance() diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index d44682991a..72c34b90cc 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -3,7 +3,7 @@ /obj/item/electronic_assembly name = "electronic assembly" - obj_flags = CAN_BE_HIT + obj_flags = CAN_BE_HIT | UNIQUE_RENAME desc = "It's a case, for building small electronics with." w_class = WEIGHT_CLASS_SMALL icon = 'icons/obj/assemblies/electronic_setups.dmi' @@ -17,6 +17,7 @@ var/obj/item/stock_parts/cell/battery // Internal cell which most circuits need to work. var/cell_type = /obj/item/stock_parts/cell var/can_charge = TRUE //Can it be charged in a recharger? + var/can_fire_equipped = FALSE //Can it fire/throw weapons when the assembly is being held? var/charge_sections = 4 var/charge_tick = FALSE var/charge_delay = 4 @@ -603,7 +604,11 @@ /obj/item/electronic_assembly/medium/gun name = "type-e electronic mechanism" icon_state = "setup_medium_gun" - desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic." + item_state = "circuitgun" + desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic. It can fire guns and throw items while the user is holding it." + lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + can_fire_equipped = TRUE /obj/item/electronic_assembly/medium/radio name = "type-f electronic mechanism" diff --git a/code/modules/integrated_electronics/core/saved_circuits.dm b/code/modules/integrated_electronics/core/saved_circuits.dm index b389b0d2d9..bc85cc4d9a 100644 --- a/code/modules/integrated_electronics/core/saved_circuits.dm +++ b/code/modules/integrated_electronics/core/saved_circuits.dm @@ -124,6 +124,10 @@ // Save modified name if(initial(name) != name) assembly_params["name"] = name + + // Save modified description + if(initial(desc) != desc) + assembly_params["desc"] = desc // Save panel status if(opened) @@ -142,6 +146,8 @@ // Validate name and color if(assembly_params["name"] && !reject_bad_name(assembly_params["name"], TRUE)) return "Bad assembly name." + if(assembly_params["desc"] && !reject_bad_text(assembly_params["desc"], TRUE)) + return "Bad assembly description." if(assembly_params["detail_color"] && !(assembly_params["detail_color"] in color_whitelist)) return "Bad assembly color." @@ -151,6 +157,10 @@ // Load modified name, if any. if(assembly_params["name"]) name = assembly_params["name"] + + // Load modified description, if any. + if(assembly_params["desc"]) + desc = assembly_params["desc"] // Load panel status if(assembly_params["opened"]) diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm index 859c8910f3..244b4cb71b 100644 --- a/code/modules/integrated_electronics/passive/power.dm +++ b/code/modules/integrated_electronics/passive/power.dm @@ -73,7 +73,7 @@ // For really fat machines. /obj/item/integrated_circuit/passive/power/relay/large name = "large tesla power relay" - desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industiral size!" + desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industrial size!" w_class = WEIGHT_CLASS_BULKY extended_desc = "The siphon drains 2 kW of power from an APC in the same room as it as long as it has charge remaining. It will always drain \ from the 'equipment' power channel." @@ -89,7 +89,7 @@ desc = "Produces electricity from chemicals." icon_state = "chemical_cell" extended_desc = "This is effectively an internal beaker. It will consume and produce power from plasma, slime jelly, welding fuel, carbon,\ - ethanol, nutriments, and blood in order of decreasing efficiency. It will consume fuel only if the battery can take more energy." + ethanol, nutriment, and blood in order of decreasing efficiency. It will consume fuel only if the battery can take more energy." container_type = OPENCONTAINER complexity = 4 inputs = list() diff --git a/code/modules/integrated_electronics/subtypes/access.dm b/code/modules/integrated_electronics/subtypes/access.dm index 63b258d782..bf710be0e2 100644 --- a/code/modules/integrated_electronics/subtypes/access.dm +++ b/code/modules/integrated_electronics/subtypes/access.dm @@ -1,6 +1,6 @@ /obj/item/integrated_circuit/input/card_reader name = "card reader" - desc = "A circuit that can read registred name, assignment and a PassKey string from an ID card." + desc = "A circuit that can read the registred name, assignment, and PassKey string from an ID card." icon_state = "card_reader" complexity = 4 diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm index 3a28c7b051..d4b854268b 100644 --- a/code/modules/integrated_electronics/subtypes/arithmetic.dm +++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm @@ -94,7 +94,7 @@ /obj/item/integrated_circuit/arithmetic/division name = "division circuit" - desc = "This circuit can divide numbers, just don't think about trying to divide by zero!" + desc = "This circuit can divide numbers. Don't even think about trying to divide by zero!" extended_desc = "The order that the calculation goes is; \ result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \ Null pins, and pins containing 0, are ignored. Pin A must be a number or the circuit will not function." @@ -142,8 +142,8 @@ /obj/item/integrated_circuit/arithmetic/sign name = "sign circuit" - desc = "This will say if a number is positive, negative, or zero." - extended_desc = "Will output 1, -1, or 0, depending on if A is a postive number, a negative number, or zero, respectively." + desc = "This circuit can tell if a number is positive, negative, or zero." + extended_desc = "Will output 1, -1, or 0, depending on if A is a positive number, a negative number, or zero, respectively." icon_state = "sign" inputs = list("A" = IC_PINTYPE_NUMBER) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -212,8 +212,8 @@ /obj/item/integrated_circuit/arithmetic/average name = "average circuit" - desc = "This circuit is of average quality, however it will compute the average for numbers you give it." - extended_desc = "Note that null pins are ignored, where as a pin containing 0 is included in the averaging calculation." + desc = "This circuit is of average quality. It will compute the average of the numbers you give it." + extended_desc = "Note that null pins are ignored, whereas a pin containing 0 is included in the averaging calculation." icon_state = "average" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -240,7 +240,7 @@ icon_state = "pi" inputs = list() spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - + /obj/item/integrated_circuit/arithmetic/pi/Initialize() . = ..() desc = "Not recommended for cooking. Outputs '[PI]' when it receives a pulse." @@ -276,7 +276,7 @@ /obj/item/integrated_circuit/arithmetic/square_root name = "square root circuit" - desc = "This outputs the square root of a number you input." + desc = "This outputs the square root of the number you input." icon_state = "square_root" inputs = list("A" = IC_PINTYPE_NUMBER) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm index 5f7dd24ad9..9382f70066 100644 --- a/code/modules/integrated_electronics/subtypes/converters.dm +++ b/code/modules/integrated_electronics/subtypes/converters.dm @@ -69,7 +69,7 @@ /obj/item/integrated_circuit/converter/refcode name = "reference encoder" - desc = "This circuit can encode a reference into a string, which can then be read by an EPV2 circuit." + desc = "This circuit can encode a reference into a string, which can then be read by a reference decoder circuit." icon_state = "ref-string" inputs = list("input" = IC_PINTYPE_REF) outputs = list("output" = IC_PINTYPE_STRING) @@ -88,7 +88,7 @@ /obj/item/integrated_circuit/converter/refdecode name = "reference decoder" - desc = "This circuit can convert an encoded reference to actual reference." + desc = "This circuit can convert an encoded reference to an actual reference." icon_state = "ref-string" inputs = list("input" = IC_PINTYPE_STRING) outputs = list("output" = IC_PINTYPE_REF) @@ -142,21 +142,18 @@ /obj/item/integrated_circuit/converter/concatenator name = "concatenator" - desc = "This joins many strings together to get one big string." + desc = "This can join up to 8 strings together to get one big string." complexity = 4 - inputs = list( - "A" = IC_PINTYPE_STRING, - "B" = IC_PINTYPE_STRING, - "C" = IC_PINTYPE_STRING, - "D" = IC_PINTYPE_STRING, - "E" = IC_PINTYPE_STRING, - "F" = IC_PINTYPE_STRING, - "G" = IC_PINTYPE_STRING, - "H" = IC_PINTYPE_STRING - ) + inputs = list() outputs = list("result" = IC_PINTYPE_STRING) activators = list("concatenate" = IC_PINTYPE_PULSE_IN, "on concatenated" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/number_of_pins = 8 + +/obj/item/integrated_circuit/converter/concatenator/Initialize() + for(var/i = 1 to number_of_pins) + inputs["input [i]"] = IC_PINTYPE_STRING + . = ..() /obj/item/integrated_circuit/converter/concatenator/do_work() var/result = null @@ -169,10 +166,22 @@ push_data() activate_pin(2) +/obj/item/integrated_circuit/converter/concatenator/small + name = "small concatenator" + desc = "This can join up to 4 strings together to get one big string." + complexity = 2 + number_of_pins = 4 + +/obj/item/integrated_circuit/converter/concatenator/large + name = "large concatenator" + desc = "This can join up to 16 strings together to get one very big string." + complexity = 6 + number_of_pins = 16 + /obj/item/integrated_circuit/converter/separator name = "separator" - desc = "This splits as single string into two at the relative split point." - extended_desc = "This circuits splits a given string into two, based on the string, and the index value. \ + desc = "This splits a single string into two at the relative split point." + extended_desc = "This circuit splits a given string into two, based on the string and the index value. \ The index splits the string after the given index, including spaces. So 'a person' with an index of '3' \ will split into 'a p' and 'erson'." icon_state = "split" @@ -203,11 +212,38 @@ activate_pin(2) +/obj/item/integrated_circuit/converter/indexer + name = "indexer" + desc = "This circuit takes a string and an index value, then returns the character found at in the string at the given index." + extended_desc = "Make sure the index is not longer or shorter than the string length. If you don't, the circuit will return empty." + icon_state = "split" + complexity = 4 + inputs = list( + "string to index" = IC_PINTYPE_STRING, + "index" = IC_PINTYPE_NUMBER, + ) + outputs = list( + "found character" = IC_PINTYPE_STRING + ) + activators = list("index" = IC_PINTYPE_PULSE_IN, "on indexed" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/indexer/do_work() + var/strin = get_pin_data(IC_INPUT, 1) + var/ind = get_pin_data(IC_INPUT, 2) + if(ind > 0 && ind <= length(strin)) + set_pin_data(IC_OUTPUT, 1, strin[ind]) + else + set_pin_data(IC_OUTPUT, 1, "") + push_data() + activate_pin(2) + /obj/item/integrated_circuit/converter/findstring name = "find text" - desc = "This gives position of sample in the string. Or returns 0." + desc = "This outputs the position of the sample in the string, or returns 0." extended_desc = "The first pin is the string to be examined. The second pin is the sample to be found. \ - For example, 'eat this burger' will give you position 4. This circuit isn't case sensitive." + For example, inputting 'my wife has caught on fire' with 'has' as the sample will give you position 9. \ + This circuit isn't case sensitive, and it does not ignore spaces." complexity = 4 inputs = list( "string" = IC_PINTYPE_STRING, @@ -216,14 +252,38 @@ outputs = list( "position" = IC_PINTYPE_NUMBER ) - activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT) + activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT, "found" = IC_PINTYPE_PULSE_OUT, "not found" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH /obj/item/integrated_circuit/converter/findstring/do_work() + var/position = findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) - set_pin_data(IC_OUTPUT, 1, findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) ) + set_pin_data(IC_OUTPUT, 1, position) + push_data() + + activate_pin(2) + if(position) + activate_pin(3) + else + activate_pin(4) + +/obj/item/integrated_circuit/converter/stringlength + name = "get length" + desc = "This circuit will return the number of characters in a string." + complexity = 1 + inputs = list( + "string" = IC_PINTYPE_STRING + ) + outputs = list( + "length" = IC_PINTYPE_NUMBER + ) + activators = list("get length" = IC_PINTYPE_PULSE_IN, "on acquisition" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/stringlength/do_work() + set_pin_data(IC_OUTPUT, 1, length(get_pin_data(IC_INPUT, 1))) push_data() activate_pin(2) @@ -232,7 +292,8 @@ name = "string exploder" desc = "This splits a single string into a list of strings." extended_desc = "This circuit splits a given string into a list of strings based on the string and given delimiter. \ - For example, 'eat this burger' will be converted to list('eat','this','burger')." + For example, 'eat this burger' will be converted to list('eat','this','burger'). Leave the delimiter null to get a list \ + of every individual character." icon_state = "split" complexity = 4 inputs = list( @@ -248,7 +309,10 @@ /obj/item/integrated_circuit/converter/exploders/do_work() var/strin = get_pin_data(IC_INPUT, 1) var/delimiter = get_pin_data(IC_INPUT, 2) - set_pin_data(IC_OUTPUT, 1, splittext(strin, delimiter)) + if(delimiter == null) + set_pin_data(IC_OUTPUT, 1, string2charlist(strin)) + else + set_pin_data(IC_OUTPUT, 1, splittext(strin, delimiter)) push_data() activate_pin(2) @@ -350,7 +414,7 @@ /obj/item/integrated_circuit/converter/rgb2hex name = "rgb to hexadecimal" desc = "This circuit can convert a RGB (Red, Green, Blue) color to a Hexadecimal RGB color." - extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. All go from 0-255." + extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. They all go from 0-255." icon_state = "rgb-hex" inputs = list( "red" = IC_PINTYPE_NUMBER, diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm index 481360f6cf..682d982373 100644 --- a/code/modules/integrated_electronics/subtypes/data_transfer.dm +++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm @@ -4,7 +4,7 @@ /obj/item/integrated_circuit/transfer/multiplexer name = "two multiplexer" - desc = "This is what those in the business tend to refer to as a 'mux' or data selector. It moves data from one of the selected inputs to the output." + desc = "This is what those in the business tend to refer to as a 'mux', or data selector. It moves data from one of the selected inputs to the output." extended_desc = "The first input pin is used to select which of the other input pins which has its data moved to the output. \ If the input selection is outside the valid range then no output is given." complexity = 2 @@ -147,7 +147,7 @@ /obj/item/integrated_circuit/transfer/wire_node name = "wire node" - desc = "Just wire node to make wiring more easy.Transfer pulse from in to out." + desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out." icon_state = "wire_node" activators = list("pulse in" = IC_PINTYPE_PULSE_IN, "pulse out" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index 042d6a3616..70e3bed5a7 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -150,7 +150,7 @@ /obj/item/integrated_circuit/input/slime_scanner name = "slime_scanner" - desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non associative." + desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative." icon_state = "medscan_adv" complexity = 12 inputs = list("target" = IC_PINTYPE_REF) @@ -193,8 +193,8 @@ /obj/item/integrated_circuit/input/plant_scanner name = "integrated plant analyzer" - desc = "A very small version of the plant analyser. This allows the machine to know all valuable params of plants in trays. \ - It cannot scan seeds nor fruits, only plants." + desc = "A very small version of the plant analyser. This allows the machine to know all valuable parameters of plants in trays. \ + It can only scan plants, not seeds or fruits." icon_state = "medscan_adv" complexity = 12 inputs = list("target" = IC_PINTYPE_REF) @@ -259,9 +259,9 @@ /obj/item/integrated_circuit/input/gene_scanner name = "gene scanner" - desc = "This circuit will scan plant for traits and reagent genes. Output is non-associative." + desc = "This circuit will scan the target plant for traits and reagent genes. Output is non-associative." extended_desc = "This allows the machine to scan plants in trays for reagent and trait genes. \ - It cannot scan seeds nor fruits, only plants." + It can only scan plants, not seeds or fruits." inputs = list( "target" = IC_PINTYPE_REF ) @@ -358,9 +358,9 @@ /obj/item/integrated_circuit/input/turfpoint name = "Tile pointer" - desc = "This circuit will get tile ref with given absolute coordinates." + desc = "This circuit will get a tile ref with the provided absolute coordinates." extended_desc = "If the machine cannot see the target, it will not be able to calculate the correct direction.\ - This circuit works only inside an assembly." + This circuit only works while inside an assembly." icon_state = "numberpad" complexity = 5 inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER) @@ -461,7 +461,7 @@ /obj/item/integrated_circuit/input/adjacent_locator name = "adjacent locator" desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \ - that is standing a meter away from the machine." + that is standing up to a meter away from the machine." extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \ give refs to nearby objects that are similar. If more than one valid object is found nearby, it will choose one of them at \ random." @@ -500,9 +500,9 @@ /obj/item/integrated_circuit/input/advanced_locator_list complexity = 6 name = "list advanced locator" - desc = "This is needed for certain devices that demand list of names for a targets to act upon. This type locates something \ - that is standing in given radius up to 8 meters. Output is non-associative. Input will only consider keys if associative." - extended_desc = "The first pin requires a list of kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \ + desc = "This is needed for certain devices that demand list of names for a target to act upon. This type locates something \ + that is standing in given radius of up to 8 meters. Output is non-associative. Input will only consider keys if associative." + extended_desc = "The first pin requires a list of the kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \ and will then provide a list of all found objects which are similar. \ The second pin is a radius." inputs = list("desired type ref" = IC_PINTYPE_LIST, "radius" = IC_PINTYPE_NUMBER) @@ -564,10 +564,10 @@ complexity = 6 name = "advanced locator" desc = "This is needed for certain devices that demand a reference for a target to act upon. This type locates something \ - that is standing in given radius up to 8 meters" - extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. This means that it will \ - give refs to nearby objects which are similar. If this pin is string, this locator will search for an \ - item by matching desired text in it's name and description. If more than one valid object is found nearby, it will choose one of them at \ + that is standing in given radius of up to 8 meters" + extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \ + give refs to nearby objects which are similar. If this pin is a string, the locator will search for an \ + item matching the desired text in its name and description. If more than one valid object is found nearby, it will choose one of them at \ random. The second pin is a radius." inputs = list("desired type" = IC_PINTYPE_ANY, "radius" = IC_PINTYPE_NUMBER) outputs = list("located ref" = IC_PINTYPE_REF) @@ -617,7 +617,7 @@ /obj/item/integrated_circuit/input/signaler name = "integrated signaler" - desc = "Signals from a signaler can be received with this, allowing for remote control. Additionally, it can send signals as well." + desc = "Signals from a signaler can be received with this, allowing for remote control. It can also send signals." extended_desc = "When a signal is received from another signaler, the 'on signal received' activator pin will be pulsed. \ The two input pins are to configure the integrated signaler's settings. Note that the frequency should not have a decimal in it, \ meaning the default frequency is expressed as 1457, not 145.7. To send a signal, pulse the 'send signal' activator pin." @@ -637,6 +637,7 @@ var/frequency = FREQ_SIGNALER var/code = DEFAULT_SIGNALER_CODE var/datum/radio_frequency/radio_connection + var/hearing_range = 1 /obj/item/integrated_circuit/input/signaler/Initialize() . = ..() @@ -690,15 +691,19 @@ return 0 activate_pin(3) - audible_message("[icon2html(src, hearers(src))] *beep* *beep*", null, 1) + audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) + for(var/CHM in get_hearers_in_view(hearing_range, src)) + if(ismob(CHM)) + var/mob/LM = CHM + LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) /obj/item/integrated_circuit/input/ntnet_packet name = "NTNet networking circuit" - desc = "Enables the sending and receiving of messages on NTNet via packet data protocol." - extended_desc = "Data can be send or received using the second pin on each side, \ + desc = "Enables the sending and receiving of messages over NTNet via packet data protocol." + extended_desc = "Data can be sent or received using the second pin on each side, \ with additonal data reserved for the third pin. When a message is received, the second activation pin \ - will pulse whatever's connected to it. Pulsing the first activation pin will send a message. Message \ - can be send to multiple recepients. Addresses must be separated with ; symbol." + will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \ + can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." icon_state = "signal" complexity = 2 cooldown_per_use = 1 @@ -751,11 +756,11 @@ /obj/item/integrated_circuit/input/ntnet_advanced name = "Low level NTNet transreciever" - desc = "Enables the sending and receiving of messages on NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity.." - extended_desc = "Data can be send or received using the second pin on each side, \ + desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity." + extended_desc = "Data can be sent or received using the second pin on each side, \ with additonal data reserved for the third pin. When a message is received, the second activation pin \ - will pulse whatever's connected to it. Pulsing the first activation pin will send a message. Message \ - can be send to multiple recepients. Addresses must be separated with ; symbol." + will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \ + can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." icon_state = "signal" complexity = 4 cooldown_per_use = 10 @@ -797,7 +802,7 @@ /obj/item/integrated_circuit/input/gps name = "global positioning system" desc = "This allows you to easily know the position of a machine containing this device." - extended_desc = "The GPS's coordinates it gives is absolute, not relative." + extended_desc = "The coordinates that the GPS outputs are absolute, not relative." icon_state = "gps" complexity = 4 inputs = list() @@ -824,7 +829,7 @@ /obj/item/integrated_circuit/input/microphone name = "microphone" - desc = "Useful for spying on people or for voice activated machines." + desc = "Useful for spying on people, or for voice-activated machines." extended_desc = "This will automatically translate most languages it hears to Galactic Common. \ The first activation pin is always pulsed when the circuit hears someone talk, while the second one \ is only triggered if it hears someone speaking a language other than Galactic Common." @@ -916,7 +921,7 @@ /obj/item/integrated_circuit/input/obj_scanner name = "scanner" desc = "Scans and obtains a reference for any objects you use on the assembly." - extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to it's location. \ + extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to its location. \ Useful for interaction with the grabber. The scanner only works using the help intent." icon_state = "recorder" complexity = 4 @@ -943,7 +948,7 @@ name = "internal battery monitor" desc = "This monitors the charge level of an internal battery." icon_state = "internalbm" - extended_desc = "This circuit will give you values of charge, max charge, and percentage of the internal battery on demand." + extended_desc = "This circuit will give you the values of charge, max charge, and the current percentage of the internal battery on demand." w_class = WEIGHT_CLASS_TINY complexity = 1 inputs = list() @@ -976,9 +981,9 @@ /obj/item/integrated_circuit/input/externalbm name = "external battery monitor" - desc = "This can help to watch battery state of any device in view" + desc = "This can read the battery state of any device in view." icon_state = "externalbm" - extended_desc = "This circuit will give you values of charge, max charge, and percentage of any device or battery in view" + extended_desc = "This circuit will give you the charge, max charge, and the current percentage values of any device or battery in view." w_class = WEIGHT_CLASS_TINY complexity = 2 inputs = list("target" = IC_PINTYPE_REF) @@ -1011,7 +1016,7 @@ /obj/item/integrated_circuit/input/ntnetsc name = "NTNet scanner" - desc = "This can return NTNet IDs of a component inside the given object, if there are any." + desc = "This can return the NTNet IDs of a component inside the given object, if there are any." icon_state = "signalsc" w_class = WEIGHT_CLASS_TINY complexity = 2 diff --git a/code/modules/integrated_electronics/subtypes/lists.dm b/code/modules/integrated_electronics/subtypes/lists.dm index c9744cc41c..a5c482c7de 100644 --- a/code/modules/integrated_electronics/subtypes/lists.dm +++ b/code/modules/integrated_electronics/subtypes/lists.dm @@ -17,8 +17,8 @@ /obj/item/integrated_circuit/lists/pick name = "pick circuit" - desc = "This circuit will pick a random element from the input list, and output said element." - extended_desc = "Input list is unmodified." + desc = "This circuit will pick a random element from the input list, and output that element." + extended_desc = "The input list is not modified." icon_state = "addition" outputs = list( "result" = IC_PINTYPE_ANY @@ -70,7 +70,7 @@ /obj/item/integrated_circuit/lists/search name = "search circuit" desc = "This circuit will get the index location of the desired element in a list." - extended_desc = "Search will start at 1 position and will return first matching position." + extended_desc = "Search will start at position 1 and will return the first matching position." inputs = list( "list" = IC_PINTYPE_LIST, "item" = IC_PINTYPE_ANY @@ -104,8 +104,8 @@ /obj/item/integrated_circuit/lists/filter name = "filter circuit" desc = "This circuit will search through a list for anything matching the desired element(s) and outputs two lists: \ - one containing just matching elements, and one with matching elements filtered out." - extended_desc = "Sample accepts lists. If no match is found, original list is sent to output 1." + one containing only the matching elements, and one with the matching elements filtered out." + extended_desc = "Sample accepts lists. If no match is found, the original list is sent to output 1." inputs = list( "input list" = IC_PINTYPE_LIST, "sample" = IC_PINTYPE_ANY @@ -168,7 +168,7 @@ /obj/item/integrated_circuit/lists/listset name = "list set circuit" desc = "This circuit will remove any duplicate entries from a list." - extended_desc = "If there are no duplicate entries, result list will be unchanged." + extended_desc = "If there are no duplicate entries, the output list will be unchanged." inputs = list( "list" = IC_PINTYPE_LIST ) @@ -189,7 +189,7 @@ /obj/item/integrated_circuit/lists/at name = "at circuit" desc = "This circuit will pick an element from a list by the input index." - extended_desc = "If there is no element with such index, result will be null." + extended_desc = "If there is no element at the given index, the result will be null." inputs = list( "list" = IC_PINTYPE_LIST, "index" = IC_PINTYPE_INDEX @@ -225,7 +225,7 @@ /obj/item/integrated_circuit/lists/delete name = "delete circuit" desc = "This circuit will remove an element from a list by the index." - extended_desc = "If there is no element with such index, result list will be unchanged." + extended_desc = "If there is no element at the given index, the result list will be unchanged." inputs = list( "list" = IC_PINTYPE_LIST, "index" = IC_PINTYPE_INDEX @@ -254,7 +254,7 @@ /obj/item/integrated_circuit/lists/write name = "write circuit" desc = "This circuit will write an element to a list at the given index location." - extended_desc = "If there is no element with such index, it will give the same list as before." + extended_desc = "If there is no element at the given index, it will output the same list as before." inputs = list( "list" = IC_PINTYPE_LIST, "index" = IC_PINTYPE_INDEX, @@ -313,7 +313,7 @@ /obj/item/integrated_circuit/lists/jointext name = "join text circuit" - desc = "This circuit will combine two lists into one and output it as a string." + desc = "This circuit will combine two lists into one, and output it as a string." extended_desc = "Default settings will encode the entire list into a string." icon_state = "join" inputs = list( @@ -351,7 +351,7 @@ /obj/item/integrated_circuit/lists/constructor name = "large list constructor" - desc = "This circuit will build a list out of sixteen input values." + desc = "This circuit will build a list out of up to sixteen input values." icon_state = "constr8" inputs = list() outputs = list( @@ -383,20 +383,20 @@ /obj/item/integrated_circuit/lists/constructor/small name = "list constructor" - desc = "This circuit will build a list out of four input values." + desc = "This circuit will build a list out of up to four input values." icon_state = "constr" number_of_pins = 4 /obj/item/integrated_circuit/lists/constructor/medium name = "medium list constructor" - desc = "This circuit will build a list out of eight input values." + desc = "This circuit will build a list out of up to eight input values." icon_state = "constr8" number_of_pins = 8 /obj/item/integrated_circuit/lists/deconstructor name = "large list deconstructor" - desc = "This circuit will write first sixteen entries of input list, starting with index, into the output values." + desc = "This circuit will write the first sixteen entries of its input list, starting with the index, into the output values." icon_state = "deconstr8" inputs = list( "input" = IC_PINTYPE_LIST, @@ -428,11 +428,11 @@ /obj/item/integrated_circuit/lists/deconstructor/small name = "list deconstructor" - desc = "This circuit will write first four entries of input list, starting with index, into the output values." + desc = "This circuit will write the first four entries of its input list, starting with the index, into the output values." icon_state = "deconstr" number_of_pins = 4 /obj/item/integrated_circuit/lists/deconstructor/medium name = "medium list deconstructor" - desc = "This circuit will write first eight entries of input list, starting with index, into the output values." + desc = "This circuit will write the first eight entries of its input list, starting with the index, into the output values." number_of_pins = 8 diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm index 41917f2a44..e9dca8c330 100644 --- a/code/modules/integrated_electronics/subtypes/logic.dm +++ b/code/modules/integrated_electronics/subtypes/logic.dm @@ -89,7 +89,7 @@ /obj/item/integrated_circuit/logic/binary/rslatch name = "RS latch" - desc = "This gate is a synchronized RS latch. If both R and S are true, state will not change." + desc = "This gate is a synchronized RS latch. If both R and S are true, its state will not change." icon_state = "sr_nor" inputs = list("S" = IC_PINTYPE_ANY,"R" = IC_PINTYPE_ANY) outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm index 1744a468f5..f4cc363afb 100644 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -6,8 +6,9 @@ desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it." extended_desc = "The firing mechanism can slot in any energy weapon. \ The first and second inputs need to be numbers which correspond to coordinates for the gun to fire at relative to the machine itself. \ - The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode is switch between \ - lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon." + The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode will switch between \ + lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon itself, not the assembly. If you wish to fire the gun while the circuit is in \ + hand, you will need to use an assembly that is a gun." complexity = 20 w_class = WEIGHT_CLASS_SMALL size = 3 @@ -80,7 +81,7 @@ /obj/item/integrated_circuit/manipulation/weapon_firing/do_work() if(!installed_gun || !installed_gun.handle_pins()) return - if(!isturf(assembly.loc)) + if(!isturf(assembly.loc) && !(assembly.can_fire_equipped && ishuman(assembly.loc))) return set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun)) push_data() @@ -99,6 +100,7 @@ var/target_x = CLAMP(T.x + xo.data, 0, world.maxx) var/target_y = CLAMP(T.y + yo.data, 0, world.maxy) + assembly.visible_message("[assembly] fires [installed_gun]!") shootAt(locate(target_x, target_y, T.z)) /obj/item/integrated_circuit/manipulation/weapon_firing/proc/shootAt(turf/target) @@ -134,8 +136,8 @@ desc = "This allows a machine to move in a given direction." icon_state = "locomotion" extended_desc = "The circuit accepts a 'dir' number as a direction to move towards. \ - Pulsing the 'step towards dir' activator pin will cause the machine to move a meter in that direction, assuming it is not \ - being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits." + Pulsing the 'step towards dir' activator pin will cause the machine to move one step in that direction, assuming it is not \ + being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits; only drone assemblies can move." w_class = WEIGHT_CLASS_SMALL complexity = 10 cooldown_per_use = 1 @@ -169,9 +171,9 @@ /obj/item/integrated_circuit/manipulation/grenade name = "grenade primer" desc = "This circuit comes with the ability to attach most types of grenades and prime them at will." - extended_desc = "Time between priming and detonation is limited to between 1 to 12 seconds but is optional. \ - If unset, not a number, or a number less than 1 then the grenade's built-in timing will be used. \ - Beware: Once primed there is no aborting the process!" + extended_desc = "The time between priming and detonation is limited to between 1 to 12 seconds, but is optional. \ + If the input is not set, not a number, or a number less than 1, the grenade's built-in timing will be used. \ + Beware: Once primed, there is no aborting the process!" icon_state = "grenade" complexity = 30 cooldown_per_use = 10 @@ -243,9 +245,9 @@ name = "plant manipulation module" desc = "Used to uproot weeds and harvest/plant trays." icon_state = "plant_m" - extended_desc = "The circuit accepts a reference to a hydroponic tray or an item in an adjacent tile. \ - Mode input(0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \ - Harvesting returns a list of the harvested plants." + extended_desc = "The circuit accepts a reference to a hydroponic tray or an item on an adjacent tile. \ + Mode input (0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \ + Harvesting outputs a list of the harvested plants." w_class = WEIGHT_CLASS_TINY complexity = 10 inputs = list("tray" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER,"item" = IC_PINTYPE_REF) @@ -345,9 +347,9 @@ /obj/item/integrated_circuit/manipulation/grabber name = "grabber" - desc = "A circuit with it's own inventory for items, used to grab and store things." + desc = "A circuit with its own inventory for items. Used to grab and store things." icon_state = "grabber" - extended_desc = "The circuit accepts a reference to an object to be grabbed and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects." + extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly." w_class = WEIGHT_CLASS_SMALL size = 3 cooldown_per_use = 5 @@ -384,6 +386,10 @@ var/obj/item/U for(U in contents) U.forceMove(T) + update_outputs() + activate_pin(2) + +/obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs() if(contents.len) set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len])) @@ -393,7 +399,6 @@ set_pin_data(IC_OUTPUT, 3, contents.len) set_pin_data(IC_OUTPUT, 4, contents) push_data() - activate_pin(2) /obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user) if(contents.len) @@ -401,16 +406,14 @@ var/obj/item/U for(U in contents) U.forceMove(T) - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, contents.len) + update_outputs() push_data() /obj/item/integrated_circuit/manipulation/claw name = "pulling claw" desc = "Circuit which can pull things.." icon_state = "pull_claw" - extended_desc = "The circuit accepts a reference to thing to be pulled. Modes: 0 for release. 1 for pull." + extended_desc = "This circuit accepts a reference to a thing to be pulled. Modes: 0 for release. 1 for pull." w_class = WEIGHT_CLASS_SMALL size = 3 cooldown_per_use = 5 @@ -464,9 +467,10 @@ /obj/item/integrated_circuit/manipulation/thrower name = "thrower" desc = "A compact launcher to throw things from inside or nearby tiles." - extended_desc = "The first and second inputs need to be numbers which correspond to coordinates to throw objects at relative to the machine itself. \ + extended_desc = "The first and second inputs need to be numbers which correspond to the coordinates to throw objects at relative to the machine itself. \ The 'fire' activator will cause the mechanism to attempt to throw objects at the coordinates, if possible. Note that the \ - projectile need to be inside the machine, or to be on an adjacent tile, and must be medium sized or smaller." + projectile needs to be inside the machine, or on an adjacent tile, and must be medium sized or smaller. The assembly \ + must also be a gun if you wish to throw something while the assembly is in hand." complexity = 25 w_class = WEIGHT_CLASS_SMALL size = 2 @@ -497,6 +501,9 @@ if(max_w_class && (A.w_class > max_w_class)) return + if(!assembly.can_fire_equipped && ishuman(assembly.loc)) + return + // Is the target inside the assembly or close to it? if(!check_target(A, exclude_components = TRUE)) return @@ -511,13 +518,21 @@ if(!M.temporarilyRemoveItemFromInventory(A)) return + // If the item is in a grabber circuit we'll update the grabber's outputs after we've thrown it. + var/obj/item/integrated_circuit/manipulation/grabber/G = A.loc + var/x_abs = CLAMP(T.x + target_x_rel, 0, world.maxx) var/y_abs = CLAMP(T.y + target_y_rel, 0, world.maxy) var/range = round(CLAMP(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1) + assembly.visible_message("[assembly] has thrown [A]!") A.forceMove(drop_location()) A.throw_at(locate(x_abs, y_abs, T.z), range, 3) + // If the item came from a grabber now we can update the outputs since we've thrown it. + if(G) + G.update_outputs() + /obj/item/integrated_circuit/manipulation/matman name = "material manager" desc = "This circuit is designed for automatic storage and distribution of materials." @@ -573,7 +588,7 @@ /obj/item/integrated_circuit/manipulation/matman/Initialize() var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0, - FALSE, list(/obj/item/stack, /obj/item/stack/ore/bluespace_crystal), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + FALSE, list(/obj/item/stack), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) materials.max_amount =100000 materials.precise_insertion = TRUE .=..() diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm index b54c433d35..fe74657532 100644 --- a/code/modules/integrated_electronics/subtypes/memory.dm +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -56,14 +56,14 @@ /obj/item/integrated_circuit/memory/large name = "large memory circuit" - desc = "This big circuit can hold eight pieces of data." + desc = "This big circuit can store eight pieces of data." icon_state = "memory8" power_draw_per_use = 4 number_of_pins = 8 /obj/item/integrated_circuit/memory/huge name = "large memory stick" - desc = "This stick of memory can hold up up to sixteen pieces of data." + desc = "This stick of memory can store up up to sixteen pieces of data." icon_state = "memory16" w_class = WEIGHT_CLASS_SMALL spawn_flags = IC_SPAWN_RESEARCH @@ -79,6 +79,7 @@ activators = list("push data" = IC_PINTYPE_PULSE_IN) var/accepting_refs = FALSE spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + number_of_pins = 0 /obj/item/integrated_circuit/memory/constant/do_work() var/datum/integrated_io/O = outputs[1] diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 6f7fd6eef5..2d820b016e 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -92,7 +92,7 @@ /obj/item/integrated_circuit/output/light/advanced name = "advanced light" - desc = "A light that takes a hexadecimal color value and a brightness value, and can be toggled on/off with a pulse." + desc = "A light that takes a hexadecimal color value and a brightness value, and can be toggled on/off by pulsing it." icon_state = "light_adv" complexity = 8 inputs = list( diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm index 018cd71006..e760b21f72 100644 --- a/code/modules/integrated_electronics/subtypes/power.dm +++ b/code/modules/integrated_electronics/subtypes/power.dm @@ -28,8 +28,8 @@ extended_desc = "This circuit transmits 20 kJ of electricity every time the activator pin is pulsed. The input pin must be \ a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \ inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \ - some power is lost due to ineffiency.Warning!Don't stack more than 1 power transmittors.it becomes less efficient for every other \ - transmission circuit in its own assembly and other nearby ones. " + some power is lost due to ineffiency. Warning! Don't stack more than 1 power transmitter, as it becomes less efficient for every other \ + transmission circuit in its own assembly and other nearby ones." w_class = WEIGHT_CLASS_BULKY complexity = 32 power_draw_per_use = 2000 diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm index 9f57dd6f43..73d162ff3a 100644 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -21,7 +21,7 @@ desc = "Unlike most electronics, creating smoke is completely intentional." icon_state = "smoke" extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \ - into the smoke clouds when activated. The reagents are consumed when smoke is made." + into the smoke clouds when activated. The reagents are consumed when the smoke is made." ext_cooldown = 1 container_type = OPENCONTAINER volume = 100 @@ -99,10 +99,10 @@ /obj/item/integrated_circuit/reagent/injector name = "integrated hypo-injector" - desc = "This scary looking thing is able to pump liquids into whatever it's pointed at." + desc = "This scary looking thing is able to pump liquids into, or suck liquids out of, whatever it's pointed at." icon_state = "injector" - extended_desc = "This autoinjector can push reagents into another container or someone else outside of the machine. The target \ - must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amount makes injector suck out reagents." + extended_desc = "This autoinjector can push up to 30 units of reagents into another container or someone else outside of the machine. The target \ + must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amounts makes the injector suck out reagents instead." container_type = OPENCONTAINER volume = 30 @@ -252,7 +252,7 @@ icon_state = "reagent_pump" extended_desc = "This is a pump which will move liquids from the source ref to the target ref. The third pin determines \ how much liquid is moved per pulse, between 0 and 50. The pump can move reagents to any open container inside the machine, or \ - outside the machine if it is next to the machine." + outside the machine if it is adjacent to the machine." complexity = 8 inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER) @@ -346,7 +346,7 @@ /obj/item/integrated_circuit/reagent/storage/cryo name = "cryo reagent storage" - desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. This will also suppress reactions." + desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. This will also prevent reactions." icon_state = "reagent_storage_cryo" extended_desc = "This is effectively an internal cryo beaker." @@ -359,7 +359,7 @@ /obj/item/integrated_circuit/reagent/storage/grinder name = "reagent grinder" - desc = "This is reagent grinder. It accepts a ref to something and refines it into reagents. It can store up to 100u." + desc = "This is a reagent grinder. It accepts a ref to something, and refines it into reagents. It can store up to 100u." icon_state = "blender" extended_desc = "" inputs = list( @@ -406,7 +406,7 @@ obj/item/integrated_circuit/reagent/storage/juicer name = "reagent juicer" - desc = "This is reagent juicer. It accepts a ref to something and refines it into reagents. It can store up to 100u." + desc = "This is a reagent juicer. It accepts a ref to something and refines it into reagents. It can store up to 100u." icon_state = "blender" extended_desc = "" inputs = list( @@ -454,7 +454,7 @@ obj/item/integrated_circuit/reagent/storage/juicer name = "reagent scanner" desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. On pulse this beaker will send list of contained reagents." icon_state = "reagent_scan" - extended_desc = "Mostly useful for reagent filter." + extended_desc = "Mostly useful for filtering reagents." complexity = 8 outputs = list( @@ -482,12 +482,12 @@ obj/item/integrated_circuit/reagent/storage/juicer /obj/item/integrated_circuit/reagent/filter name = "reagent filter" - desc = "Filtering liquids by list of desired or unwanted reagents." + desc = "Filters liquids by list of desired or unwanted reagents." icon_state = "reagent_filter" - extended_desc = "This is a filter which will move liquids from the source to the target. \ - It will move all reagents, except those in the unwanted list, given the fourth pin if amount value is positive, \ - or it will move only desired reagents if amount is negative. The third pin determines \ - how much reagent is moved per pulse, between 0 and 50. Amount is given for each separate reagent." + extended_desc = "This is a filter which will move liquids from the source to its target. \ + If the amount in the fourth pin is positive, it will move all reagents except those in the unwanted list. \ + If the amount in the fourth pin is negative, it will only move the reagents in the wanted list. \ + The third pin determines how many reagents are moved per pulse, between 0 and 50. Amount is given for each separate reagent." complexity = 8 inputs = list( diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index e0775f1a23..4445c1e1f3 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -37,10 +37,11 @@ activate_pin(2) /obj/item/integrated_circuit/smart/coord_basic_pathfinder - name = "coordinte pathfinder" + name = "coordinate pathfinder" desc = "This complex circuit is able to determine what direction a given target is." - extended_desc = "This circuit uses absolute coordintes to determine where the target is. If the machine \ - cannot see the target, it will not be able to calculate the correct direction.This circuit is working only in assembly." + extended_desc = "This circuit uses absolute coordinates to determine where the target is. If the machine \ + cannot see the target, it will not be able to calculate the correct direction. \ + This circuit will only work while inside an assembly." icon_state = "numberpad" complexity = 5 inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER,"ignore obstacles" = IC_PINTYPE_BOOLEAN) @@ -74,8 +75,8 @@ /obj/item/integrated_circuit/smart/advanced_pathfinder name = "advanced pathfinder" desc = "This circuit uses a complex processor for long-range pathfinding." - extended_desc = "This circuit uses absolute coordinates for target. A path will be generated taking obstacle input into account, \ - pathing around any instances of said input. The passkey provided from a card reader is used to create a valid path through doorways." + extended_desc = "This circuit uses absolute coordinates to find its target. A path will be generated to the target, taking obstacles into account, \ + and pathing around any instances of said input. The passkey provided from a card reader is used to calculate a valid path through airlocks." icon_state = "numberpad" complexity = 40 cooldown_per_use = 50 @@ -117,4 +118,4 @@ set_pin_data(IC_OUTPUT, 1, Xn) set_pin_data(IC_OUTPUT, 2, Yn) push_data() - activate_pin(2) \ No newline at end of file + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index 26bd1fc1d6..f72f5dbd74 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -53,8 +53,10 @@ /obj/item/integrated_circuit/time/delay/custom name = "custom delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit's delay can be customized, between 1/10th of a second to one hour. The delay is updated upon receiving a pulse." + desc = "This sends a pulse signal out after a delay defined in tenths of a second, critical for ensuring proper control \ + flow in a complex machine. This circuit's delay can be customized, between 1/10th of a second to one hour. \ + The delay is updated upon receiving a pulse." + extended_desc = "The delay is defined in tenths of a second. For instance, 4 will be a delay of 0.4 seconds, or 15 for 1.5 seconds." icon_state = "delay" inputs = list("delay time" = IC_PINTYPE_NUMBER) spawn_flags = IC_SPAWN_RESEARCH @@ -104,7 +106,9 @@ /obj/item/integrated_circuit/time/ticker/custom name = "custom ticker" - desc = "This advanced circuit sends an automatic pulse every given interval." + desc = "This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second." + extended_desc ="This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second. \ + For example, setting the time pin to 4 will send a pulse every 0.4 seconds, or 15 for every 1.5 seconds." icon_state = "tick-f" complexity = 8 delay = 2 SECONDS @@ -115,7 +119,7 @@ /obj/item/integrated_circuit/time/ticker/custom/on_data_written() var/delay_input = get_pin_data(IC_INPUT, 2) if(delay_input && isnum(delay_input) ) - var/new_delay = CLAMP(delay_input ,1 ,1 HOURS) + var/new_delay = CLAMP(delay_input ,1 ,1 HOURS) delay = new_delay ..() diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 3aad52e596..b4679ae479 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -226,9 +226,14 @@ GLOBAL_PROTECT(exp_to_update) if(!rolefound) play_records["Unknown"] += minutes else - play_records[EXP_TYPE_GHOST] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] Ghost EXP!") + if(holder && !holder.deadmined) + play_records[EXP_TYPE_ADMIN] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] Admin EXP!") + else + play_records[EXP_TYPE_GHOST] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] Ghost EXP!") else if(isobserver(mob)) play_records[EXP_TYPE_GHOST] += minutes if(announce_changes) diff --git a/code/modules/jobs/job_types/civilian.dm b/code/modules/jobs/job_types/civilian.dm index 3c36247c6d..e0202db132 100644 --- a/code/modules/jobs/job_types/civilian.dm +++ b/code/modules/jobs/job_types/civilian.dm @@ -91,7 +91,7 @@ Mime uniform = /obj/item/clothing/under/rank/mime mask = /obj/item/clothing/mask/gas/mime gloves = /obj/item/clothing/gloves/color/white - head = /obj/item/clothing/head/beret + head = /obj/item/clothing/head/frenchberet suit = /obj/item/clothing/suit/suspenders backpack_contents = list(/obj/item/reagent_containers/food/drinks/bottle/bottleofnothing=1) diff --git a/code/modules/keybindings/readme.md b/code/modules/keybindings/readme.md index 1170804436..6c0369d7c7 100644 --- a/code/modules/keybindings/readme.md +++ b/code/modules/keybindings/readme.md @@ -1,7 +1,7 @@ # In-code keypress handling system This whole system is heavily based off of forum_account's keyboard library. -Thanks to forum_account for saving the day, the library can be found [here](http://www.byond.com/developer/Forum_account/Keyboard)! +Thanks to forum_account for saving the day, the library can be found [here](https://secure.byond.com/developer/Forum_account/Keyboard)! .dmf macros have some very serious shortcomings. For example, they do not allow reusing parts of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index 5f2e9963e7..8b9cc09dff 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -35,7 +35,8 @@ return TRUE /datum/language/proc/get_icon() - return "[icon2html(icon, world, icon_state)]" + var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) + return sheet.icon_tag("language-[icon_state]") /datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2) if(!syllables || !syllables.len) diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 0bfb0b5f6d..36eb9d5e1b 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -25,6 +25,14 @@ armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) resistance_flags = FIRE_PROOF +/obj/item/clothing/suit/hooded/explorer/Initialize() + . = ..() + AddComponent(/datum/component/armor_plate) + +/obj/item/clothing/head/hooded/explorer/Initialize() + . = ..() + AddComponent(/datum/component/armor_plate) + /obj/item/clothing/mask/gas/explorer name = "explorer gas mask" desc = "A military-grade gas mask that can be connected to an air supply." diff --git a/code/modules/mining/equipment/goliath_hide.dm b/code/modules/mining/equipment/goliath_hide.dm index b266b92584..85feaecf73 100644 --- a/code/modules/mining/equipment/goliath_hide.dm +++ b/code/modules/mining/equipment/goliath_hide.dm @@ -9,38 +9,4 @@ novariants = FALSE flags_1 = NOBLUDGEON_1 w_class = WEIGHT_CLASS_NORMAL - layer = MOB_LAYER - var/static/list/goliath_platable_armor_typecache = typecacheof(list( - /obj/item/clothing/head/helmet/space/hardsuit/mining, - /obj/item/clothing/suit/space/hardsuit/mining, - /obj/item/clothing/head/hooded/explorer, - /obj/item/clothing/suit/hooded/explorer)) - -/obj/item/stack/sheet/animalhide/goliath_hide/afterattack(atom/target, mob/user, proximity_flag) - if(!proximity_flag) - return - if(is_type_in_typecache(target, goliath_platable_armor_typecache)) - var/obj/item/clothing/C = target - if(C.armor.melee < 60) - C.armor = C.armor.setRating(melee = min(C.armor.melee + 10, 60)) - to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") - use(1) - else - to_chat(user, "You can't improve [C] any further!") - else if(istype(target, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/D = target - if(D.hides < 3) - D.hides++ - D.armor = D.armor.setRating(\ - melee = min(D.armor.melee + 10, 70),\ - bullet = min(D.armor.bullet + 5, 50),\ - laser = min(D.armor.laser + 5, 50)) - to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") - D.update_icon() - if(D.hides == 3) - D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - its pilot must be an experienced monster hunter." - else - D.desc = "Autonomous Power Loader Unit. Its armour is enhanced with some goliath hide plates." - use(1) - else - to_chat(user, "You can't improve [D] any further!") + layer = MOB_LAYER \ No newline at end of file diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index bb5347a152..c7ba9ad6f4 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -84,7 +84,7 @@ /obj/machinery/mineral/processing_unit/Initialize() . = ..() proximity_monitor = new(src, 1) - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY) + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY, FALSE, list(/obj/item/stack)) stored_research = new /datum/techweb/specialized/autounlocking/smelter /obj/machinery/mineral/processing_unit/Destroy() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 63e00f5a38..d4735d1c75 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -28,7 +28,7 @@ /obj/machinery/mineral/ore_redemption/Initialize() . = ..() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE),INFINITY) + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE),INFINITY, FALSE, list(/obj/item/stack)) stored_research = new /datum/techweb/specialized/autounlocking/smelter /obj/machinery/mineral/ore_redemption/Destroy() @@ -303,13 +303,12 @@ desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num var/amount = round(min(desired,50,smelt_amount)) materials.use_amount(alloy.materials, amount) - var/output = new alloy.build_path(src) - if(istype(output, /obj/item/stack/sheet)) - var/obj/item/stack/sheet/produced_alloy = output - produced_alloy.amount = amount - unload_mineral(produced_alloy) + var/output + if(ispath(alloy.build_path, /obj/item/stack/sheet)) + output = new alloy.build_path(src, amount) else - unload_mineral(output) + output = new alloy.build_path(src) + unload_mineral(output) else to_chat(usr, "Required access not found.") return TRUE diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index e658f69395..0f84d11082 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -7,20 +7,23 @@ desc = "Controls a stacking machine... in theory." density = FALSE anchored = TRUE - var/obj/machinery/mineral/stacking_machine/machine = null + circuit = /obj/item/circuitboard/machine/stacking_unit_console + var/obj/machinery/mineral/stacking_machine/machine var/machinedir = SOUTHEAST - speed_process = TRUE /obj/machinery/mineral/stacking_unit_console/Initialize() . = ..() machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir)) if (machine) machine.CONSOLE = src - else - qdel(src) /obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user) . = ..() + + if(!machine) + to_chat(user, "[src] is not linked to a machine!") + return + var/obj/item/stack/sheet/s var/dat @@ -35,6 +38,13 @@ user << browse(dat, "window=console_stacking_machine") +/obj/machinery/mineral/stacking_unit_console/multitool_act(mob/living/user, obj/item/I) + if(istype(I, /obj/item/multitool)) + var/obj/item/multitool/M = I + M.buffer = src + to_chat(user, "You store linkage information in [I]'s buffer.") + return TRUE + /obj/machinery/mineral/stacking_unit_console/Topic(href, href_list) if(..()) return @@ -44,8 +54,7 @@ if(!(text2path(href_list["release"]) in machine.stack_list)) return //someone tried to spawn materials by spoofing hrefs var/obj/item/stack/sheet/inp = machine.stack_list[text2path(href_list["release"])] - var/obj/item/stack/sheet/out = new inp.type() - out.amount = inp.amount + var/obj/item/stack/sheet/out = new inp.type(null, inp.amount) inp.amount = 0 machine.unload_mineral(out) @@ -63,6 +72,7 @@ desc = "A machine that automatically stacks acquired materials. Controlled by a nearby console." density = TRUE anchored = TRUE + circuit = /obj/item/circuitboard/machine/stacking_machine var/obj/machinery/mineral/stacking_unit_console/CONSOLE var/stk_types = list() var/stk_amt = list() @@ -79,16 +89,26 @@ if(istype(AM, /obj/item/stack/sheet) && AM.loc == get_step(src, input_dir)) process_sheet(AM) +/obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/I) + if(istype(I, /obj/item/multitool)) + var/obj/item/multitool/M = I + if(!istype(M.buffer, /obj/machinery/mineral/stacking_unit_console)) + to_chat(user, "The [I] has no linkage data in its buffer.") + return FALSE + else + CONSOLE = M.buffer + CONSOLE.machine = src + to_chat(user, "You link [src] to the console in [I]'s buffer.") + return TRUE + /obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp) if(!(inp.type in stack_list)) //It's the first of this sheet added - var/obj/item/stack/sheet/s = new inp.type(src,0) - s.amount = 0 + var/obj/item/stack/sheet/s = new inp.type(src, 0) stack_list[inp.type] = s var/obj/item/stack/sheet/storage = stack_list[inp.type] storage.amount += inp.amount //Stack the sheets qdel(inp) //Let the old sheet garbage collect while(storage.amount > stack_amt) //Get rid of excessive stackage - var/obj/item/stack/sheet/out = new inp.type() - out.amount = stack_amt + var/obj/item/stack/sheet/out = new inp.type(null, stack_amt) unload_mineral(out) storage.amount -= stack_amt diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index ec4c9bd714..b7cc0db43f 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -16,7 +16,7 @@ /obj/machinery/mineral/mint/Initialize() . = ..() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50) + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50, FALSE, list(/obj/item/stack)) /obj/machinery/mineral/mint/process() var/turf/T = get_step(src, input_dir) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 01ac36c57a..c6cc8a70ac 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -400,7 +400,7 @@ SSticker.mode.make_antag_chance(humanc) if(humanc && CONFIG_GET(flag/roundstart_traits)) - SStraits.AssignTraits(humanc, humanc.client, TRUE) + SSquirks.AssignQuirks(humanc, humanc.client, TRUE) log_manifest(character.mind.key,character.mind,character,latejoin = TRUE) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 8bfe33b3ea..e11a4fc886 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -592,6 +592,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/list/possessible = list() for(var/mob/living/L in GLOB.alive_mob_list) + if(istype(L,/mob/living/carbon/human/dummy) || !get_turf(L)) //Haha no. + continue if(!(L in GLOB.player_list) && !L.mind) possessible += L diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index a7b28dd658..48951e4cd3 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -326,7 +326,10 @@ I.plane = initial(I.plane) I.appearance_flags &= ~NO_CLIENT_COLOR if(!no_move && !(I.flags_1 & DROPDEL_1)) //item may be moved/qdel'd immedietely, don't bother moving it - I.forceMove(newloc) + if (isnull(newloc)) + I.moveToNullspace() + else + I.forceMove(newloc) I.dropped(src) return TRUE diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 68c6ed5c83..05a14ef3b1 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -193,10 +193,10 @@ blood_data["real_name"] = real_name blood_data["features"] = dna.features blood_data["factions"] = faction - blood_data["traits"] = list() - for(var/V in roundstart_traits) - var/datum/trait/T = V - blood_data["traits"] += T.type + blood_data["quirks"] = list() + for(var/V in roundstart_quirks) + var/datum/quirk/T = V + blood_data["quirks"] += T.type return blood_data //get the id of the substance this mob use as blood. diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index f224384399..71286ef691 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -1,99 +1,99 @@ -/datum/emote/living/carbon - mob_type_allowed_typecache = list(/mob/living/carbon) - -/datum/emote/living/carbon/airguitar - key = "airguitar" - message = "is strumming the air and headbanging like a safari chimp." - restraint_check = TRUE - -/datum/emote/living/carbon/blink - key = "blink" - key_third_person = "blinks" - message = "blinks." - -/datum/emote/living/carbon/blink_r - key = "blink_r" - message = "blinks rapidly." - -/datum/emote/living/carbon/clap - key = "clap" - key_third_person = "claps" - message = "claps." - muzzle_ignore = TRUE - restraint_check = TRUE - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/carbon/clap/run_emote(mob/living/user, params) - . = ..() - if (.) - if (ishuman(user)) - // Need hands to clap - if (!user.get_bodypart("l_arm") || !user.get_bodypart("r_arm")) - return - var/clap = pick('sound/misc/clap1.ogg', - 'sound/misc/clap2.ogg', - 'sound/misc/clap3.ogg', - 'sound/misc/clap4.ogg') - playsound(user, clap, 50, 1, -1) - -/datum/emote/living/carbon/gnarl - key = "gnarl" - key_third_person = "gnarls" - message = "gnarls and shows its teeth..." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - -/datum/emote/living/carbon/moan - key = "moan" - key_third_person = "moans" - message = "moans!" - message_mime = "appears to moan!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/carbon/roll - key = "roll" - key_third_person = "rolls" - message = "rolls." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - restraint_check = TRUE - -/datum/emote/living/carbon/scratch - key = "scratch" - key_third_person = "scratches" - message = "scratches." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - restraint_check = TRUE - -/datum/emote/living/carbon/screech - key = "screech" - key_third_person = "screeches" - message = "screeches." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - -/datum/emote/living/carbon/sign - key = "sign" - key_third_person = "signs" - message_param = "signs the number %t." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - restraint_check = TRUE - -/datum/emote/living/carbon/sign/select_param(mob/user, params) - . = ..() - if(!isnum(text2num(params))) - return message - -/datum/emote/living/carbon/sign/signal - key = "signal" - key_third_person = "signals" - message_param = "raises %t fingers." - mob_type_allowed_typecache = list(/mob/living/carbon/human) - restraint_check = TRUE - -/datum/emote/living/carbon/tail - key = "tail" - message = "waves their tail." - mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) - -/datum/emote/living/carbon/wink - key = "wink" - key_third_person = "winks" - message = "winks." +/datum/emote/living/carbon + mob_type_allowed_typecache = list(/mob/living/carbon) + +/datum/emote/living/carbon/airguitar + key = "airguitar" + message = "is strumming the air and headbanging like a safari chimp." + restraint_check = TRUE + +/datum/emote/living/carbon/blink + key = "blink" + key_third_person = "blinks" + message = "blinks." + +/datum/emote/living/carbon/blink_r + key = "blink_r" + message = "blinks rapidly." + +/datum/emote/living/carbon/clap + key = "clap" + key_third_person = "claps" + message = "claps." + muzzle_ignore = TRUE + restraint_check = TRUE + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/carbon/clap/run_emote(mob/living/user, params) + . = ..() + if (.) + if (ishuman(user)) + // Need hands to clap + if (!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM)) + return + var/clap = pick('sound/misc/clap1.ogg', + 'sound/misc/clap2.ogg', + 'sound/misc/clap3.ogg', + 'sound/misc/clap4.ogg') + playsound(user, clap, 50, 1, -1) + +/datum/emote/living/carbon/gnarl + key = "gnarl" + key_third_person = "gnarls" + message = "gnarls and shows its teeth..." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + +/datum/emote/living/carbon/moan + key = "moan" + key_third_person = "moans" + message = "moans!" + message_mime = "appears to moan!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/carbon/roll + key = "roll" + key_third_person = "rolls" + message = "rolls." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + restraint_check = TRUE + +/datum/emote/living/carbon/scratch + key = "scratch" + key_third_person = "scratches" + message = "scratches." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + restraint_check = TRUE + +/datum/emote/living/carbon/screech + key = "screech" + key_third_person = "screeches" + message = "screeches." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + +/datum/emote/living/carbon/sign + key = "sign" + key_third_person = "signs" + message_param = "signs the number %t." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + restraint_check = TRUE + +/datum/emote/living/carbon/sign/select_param(mob/user, params) + . = ..() + if(!isnum(text2num(params))) + return message + +/datum/emote/living/carbon/sign/signal + key = "signal" + key_third_person = "signals" + message_param = "raises %t fingers." + mob_type_allowed_typecache = list(/mob/living/carbon/human) + restraint_check = TRUE + +/datum/emote/living/carbon/tail + key = "tail" + message = "waves their tail." + mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien) + +/datum/emote/living/carbon/wink + key = "wink" + key_third_person = "winks" + message = "winks." diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm index 018f04ab8f..beb183e842 100644 --- a/code/modules/mob/living/carbon/human/dummy.dm +++ b/code/modules/mob/living/carbon/human/dummy.dm @@ -19,6 +19,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) //Inefficient pooling/caching way. GLOBAL_LIST_EMPTY(human_dummy_list) +GLOBAL_LIST_EMPTY(dummy_mob_list) /proc/generate_or_wait_for_human_dummy(slotkey) if(!slotkey) @@ -31,6 +32,7 @@ GLOBAL_LIST_EMPTY(human_dummy_list) if(QDELETED(D)) D = new GLOB.human_dummy_list[slotkey] = D + GLOB.dummy_mob_list += D D.in_use = TRUE return D diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 050a4f6d09..4af2fa1134 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -708,7 +708,7 @@ if(toxloss > 10) to_chat(src, "You feel sick.") else if(toxloss > 20) - to_chat(src, "You feel nauseous.") + to_chat(src, "You feel nauseated.") else if(toxloss > 40) to_chat(src, "You feel very unwell!") if(oxyloss) @@ -733,8 +733,8 @@ if(0 to NUTRITION_LEVEL_STARVING) to_chat(src, "You're starving!") - if(roundstart_traits.len) - to_chat(src, "You have these traits: [get_trait_string()].") + if(roundstart_quirks.len) + to_chat(src, "You have these quirks: [get_trait_string()].") else if(wear_suit) wear_suit.add_fingerprint(M) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index be1cda38b6..a7474b6aaa 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -63,10 +63,6 @@ else if(eye_blurry) //blurry eyes heal slowly adjust_blurriness(-1) - if(has_trait(TRAIT_PACIFISM) && a_intent == INTENT_HARM) - to_chat(src, "You don't feel like harming anybody.") - a_intent_change(INTENT_HELP) - if (getBrainLoss() >= 60 && !incapacitated(TRUE)) SendSignal(COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage) if(prob(3)) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index a89c966012..fcb0269e35 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1559,9 +1559,9 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.gain_trauma(/datum/brain_trauma/mild/concussion) else if(!I.is_sharp()) - H.adjustBrainLoss(I.force / 5) + H.adjustBrainLoss(I.force * 0.2) - if(prob(I.force + ((100 - H.health)/2)) && H != user) + if(!I.is_sharp() && prob(I.force + ((100 - H.health) * 0.5)) && H != user) // rev deconversion through blunt trauma. var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev) if(rev) rev.remove_revolutionary(FALSE, user) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 8194296099..53b07cd7e4 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -207,9 +207,9 @@ if(breath_gases[/datum/gas/bz]) var/bz_partialpressure = (breath_gases[/datum/gas/bz][MOLES]/breath.total_moles())*breath_pressure if(bz_partialpressure > 1) - hallucination += 20 + hallucination += 10 else if(bz_partialpressure > 0.01) - hallucination += 5//Removed at 2 per tick so this will slowly build up + hallucination += 5 //TRITIUM if(breath_gases[/datum/gas/tritium]) var/tritium_partialpressure = (breath_gases[/datum/gas/tritium][MOLES]/breath.total_moles())*breath_pressure @@ -452,7 +452,7 @@ return adjustToxLoss(8, TRUE, TRUE) if(prob(30)) - to_chat(src, "You feel confused and nauseous...")//actual symptoms of liver failure + to_chat(src, "You feel confused and nauseated...")//actual symptoms of liver failure //////////////// diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm index 83d943ccbf..8a8c99bf32 100644 --- a/code/modules/mob/living/carbon/say.dm +++ b/code/modules/mob/living/carbon/say.dm @@ -13,7 +13,8 @@ message = T.TongueSpeech(message) if(wear_mask) message = wear_mask.speechModification(message) - + if(head) + message = head.speechModification(message) return message /mob/living/carbon/can_speak_vocal(message) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 0c63ad2ab4..47eb649d93 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -133,9 +133,6 @@ eye_blurry = max(eye_blurry-1, 0) if(client && !eye_blurry) clear_fullscreen("blurry") - if(has_trait(TRAIT_PACIFISM) && a_intent == INTENT_HARM) - to_chat(src, "You don't feel like harming anybody.") - a_intent_change(INTENT_HELP) /mob/living/proc/update_damage_hud() return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bf06cd1ebb..abf8302590 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -407,16 +407,14 @@ */ //Recursive function to find everything a mob is holding. Really shitty proc tbh. /mob/living/get_contents() - . = list() - . |= list(src) - for(var/obj/o in .) - var/list/newlist = list() - o.SendSignal(COMSIG_TRY_STORAGE_RETURN_INVENTORY, newlist) - . |= newlist - for(var/obj/item/clothing/under/U in .) - . |= U.contents - for(var/obj/item/folder/F in .) - . |= F.contents + var/list/ret = list() + ret |= contents //add our contents + for(var/i in ret.Copy()) //iterate storage objects + var/atom/A = i + A.SendSignal(COMSIG_TRY_STORAGE_RETURN_INVENTORY, ret) + for(var/obj/item/folder/F in ret.Copy()) //very snowflakey-ly iterate folders + ret |= F.contents + return ret // Living mobs use can_inject() to make sure that the mob is not syringe-proof in general. /mob/living/proc/can_inject() @@ -494,6 +492,11 @@ ExtinguishMob() fire_stacks = 0 update_canmove() + GET_COMPONENT(mood, /datum/component/mood) + if (mood) + QDEL_LIST_ASSOC_VAL(mood.mood_events) + mood.sanity = SANITY_GREAT + mood.update_mood() //proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again) @@ -938,7 +941,7 @@ /mob/living/rad_act(amount) . = ..() - if(!amount || amount < RAD_MOB_SKIN_PROTECTION) + if(!amount || (amount < RAD_MOB_SKIN_PROTECTION) || has_trait(TRAIT_RADIMMUNE)) return amount -= RAD_BACKGROUND_RADIATION // This will always be at least 1 because of how skin protection is calculated diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 2b2e0de0b3..b12133c5a1 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -317,8 +317,7 @@ return shock_damage /mob/living/emp_act(severity) - var/list/L = src.get_contents() - for(var/obj/O in L) + for(var/obj/O in contents) O.emp_act(severity) ..() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 6dfaa565fa..94b1e64f39 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -34,7 +34,7 @@ var/list/status_traits = list() - var/list/roundstart_traits = list() + var/list/roundstart_quirks = list() var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them. diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 6429c853dc..4fbe50518e 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -170,7 +170,8 @@ if(href_list["send"]) signaler.send_activation() - audible_message("[icon2html(src, world)] *beep* *beep*") + audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*") + playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) if(href_list["freq"]) var/new_frequency = (signaler.frequency + text2num(href_list["freq"])) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index faef19529e..662100eb7f 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -831,7 +831,7 @@ set_module = /obj/item/robot_module/janitor /mob/living/silicon/robot/modules/syndicate - icon_state = "syndie_bloodhound" + icon_state = "synd_sec" faction = list(ROLE_SYNDICATE) bubble_icon = "syndibot" req_access = list(ACCESS_SYNDICATE) @@ -859,7 +859,7 @@ return /mob/living/silicon/robot/modules/syndicate/medical - icon_state = "syndi-medi" + icon_state = "synd_medical" playstyle_string = "You are a Syndicate medical cyborg! \ You are armed with powerful medical tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \ Your hypospray will produce Restorative Nanites, a wonder-drug that will heal most types of bodily damages, including clone and brain damage. It also produces morphine for offense. \ diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index bdcde185d7..9926bc35fc 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -1,7 +1,7 @@ /obj/item/robot_module name = "Default" icon = 'icons/obj/module.dmi' - icon_state = "std_module" + icon_state = "std_mod" w_class = WEIGHT_CLASS_GIGANTIC item_state = "electronic" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 13680f55a8..bc8dd0c3ab 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -148,9 +148,14 @@ if(loc == get_turf(target)) if(!(check_bot(target) && prob(50))) //Target is not defined at the parent. 50% chance to still try and clean so we dont get stuck on the last blood drop. UnarmedAttack(target) //Rather than check at every step of the way, let's check before we do an action, so we can rescan before the other bot. + if(QDELETED(target)) //We done here. + target = null + mode = BOT_IDLE + return else shuffle = TRUE //Shuffle the list the next time we scan so we dont both go the same way. path = list() + if(!path || path.len == 0) //No path, need a new one //Try to produce a path to the target, and ignore airlocks to which it has access. path = get_path_to(src, target.loc, /turf/proc/Distance_cardinal, 0, 30, id=access_card) @@ -181,7 +186,7 @@ /obj/effect/decal/cleanable/ash, /obj/effect/decal/cleanable/greenglow, /obj/effect/decal/cleanable/dirt, - /obj/effect/decal/cleanable/deadcockroach, + /obj/effect/decal/cleanable/insectguts, /obj/effect/decal/remains ) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index f0cc8285e8..f47f936db7 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -164,12 +164,7 @@ update_controls() /mob/living/simple_animal/bot/floorbot/proc/empty_tiles() - var/atom/Tsec = drop_location() - - while(specialtiles > initial(tiletype.max_amount)) - new tiletype(Tsec,initial(tiletype.max_amount)) - specialtiles -= initial(tiletype.max_amount) - new tiletype(Tsec,specialtiles) + new tiletype(drop_location(), specialtiles) specialtiles = 0 tiletype = null @@ -378,8 +373,7 @@ if(prob(50)) drop_part(robot_arm, Tsec) - var/obj/item/stack/tile/plasteel/T = new (Tsec) - T.amount = 1 + new /obj/item/stack/tile/plasteel(Tsec, 1) do_sparks(3, TRUE, src) ..() diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index de7874351a..65b78bf844 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -2,7 +2,7 @@ name = "\improper honkbot" desc = "A little robot. It looks happy with its bike horn." icon = 'icons/mob/aibots.dmi' - icon_state = "honkbot" + icon_state = "honkbot1" density = FALSE anchored = FALSE health = 25 diff --git a/code/modules/mob/living/simple_animal/friendly/cockroach.dm b/code/modules/mob/living/simple_animal/friendly/cockroach.dm index 3887e4b6d9..c85890df88 100644 --- a/code/modules/mob/living/simple_animal/friendly/cockroach.dm +++ b/code/modules/mob/living/simple_animal/friendly/cockroach.dm @@ -6,7 +6,7 @@ health = 1 maxHealth = 1 turns_per_move = 5 - loot = list(/obj/effect/decal/cleanable/deadcockroach) + loot = list(/obj/effect/decal/cleanable/insectguts) 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) minbodytemp = 270 maxbodytemp = INFINITY @@ -52,11 +52,3 @@ /mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach. return - -/obj/effect/decal/cleanable/deadcockroach - name = "cockroach guts" - desc = "One bug squashed. Four more will rise in its place." - icon = 'icons/effects/blood.dmi' - icon_state = "xfloor1" - random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") - beauty = -300 diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm index 6dc76bb6a7..214ced3613 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm @@ -27,6 +27,7 @@ attacktext = "pummels" attack_sound = 'sound/weapons/punch1.ogg' dextrous = TRUE + held_items = list(null, null) possible_a_intents = list(INTENT_HELP, INTENT_GRAB, INTENT_DISARM, INTENT_HARM) faction = list("jungle") robust_searching = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm index 4c9c916085..4917d3b74d 100644 --- a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm +++ b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm @@ -9,7 +9,7 @@ icon_dead = "Fugu_dead" icon_gib = "syndicate_gib" mob_biotypes = list(MOB_ORGANIC, MOB_BEAST) - mouse_opacity = MOUSE_OPACITY_OPAQUE + mouse_opacity = MOUSE_OPACITY_ICON move_to_delay = 5 friendly = "floats near" speak_emote = list("puffs") diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 106381bade..47de9a0896 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -146,12 +146,12 @@ else status_traits[trait] |= list(source) -/mob/living/proc/add_trait_datum(trait, spawn_effects) //separate proc due to the way these ones are handled - if(has_trait(trait)) +/mob/living/proc/add_quirk(quirk, spawn_effects) //separate proc due to the way these ones are handled + if(has_trait(quirk)) return - if(!SStraits || !SStraits.traits[trait]) + if(!SSquirks || !SSquirks.quirks[quirk]) return - var/datum/trait/T = SStraits.traits[trait] + var/datum/quirk/T = SSquirks.quirks[quirk] new T (src, spawn_effects) return TRUE @@ -180,8 +180,8 @@ if(!LAZYLEN(status_traits[trait])) status_traits -= trait -/mob/living/proc/remove_trait_datum(trait) - var/datum/trait/T = roundstart_traits[trait] +/mob/living/proc/remove_quirk(quirk) + var/datum/quirk/T = roundstart_quirks[quirk] if(T) qdel(T) return TRUE @@ -201,11 +201,28 @@ else if(LAZYLEN(status_traits[trait])) return TRUE -/mob/living/proc/has_trait_datum(trait) - return roundstart_traits[trait] +/mob/living/proc/has_quirk(quirk) + return roundstart_quirks[quirk] -/mob/living/proc/remove_all_traits() - status_traits = list() +/mob/living/proc/remove_all_traits(remove_species_traits = FALSE, remove_organ_traits = FALSE, remove_quirks = FALSE) + + var/list/blacklisted_sources = list() + if(!remove_species_traits) + blacklisted_sources += SPECIES_TRAIT + if(!remove_organ_traits) + blacklisted_sources += ORGAN_TRAIT + if(!remove_quirks) + blacklisted_sources += ROUNDSTART_TRAIT + + for(var/kebab in status_traits) + var/skip + for(var/S in blacklisted_sources) + if(S in status_traits[kebab]) + skip = TRUE + break + if(!skip) + remove_trait(kebab, null, TRUE) + CHECK_TICK /////////////////////////////////// TRAIT PROCS //////////////////////////////////// diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c0d194c62b..3b44fed908 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -289,7 +289,7 @@ /mob/proc/show_inv(mob/user) return -//mob verbs are faster than object verbs. See http://www.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine() +//mob verbs are faster than object verbs. See https://secure.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine() /mob/verb/examinate(atom/A as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why set name = "Examine" set category = "IC" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 9ad832debe..2718392b69 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -70,7 +70,7 @@ message = src.say_quote(message, get_spans()) var/rendered = "DEAD: [name][alt_name] " - + log_message("DEAD: [message]", INDIVIDUAL_SAY_LOG) deadchat_broadcast(rendered, follow_target = src, speaker_key = K) /mob/proc/check_emote(message) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 674de5be50..07200c16a0 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -66,7 +66,7 @@ ..() to_chat(user, "Alt-click to fold it.") - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper) + var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper) assets.send(user) if(in_range(user, src) || isobserver(user)) @@ -300,7 +300,10 @@ if(!in_range(src, user)) return - stamps += " "
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/research_designs)
+ sheet.send(user)
+ return sheet.icon_tag(id)
////////////////////////////////////////
//Disks for transporting design datums//
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index b50c6df473..28609e921d 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -126,42 +126,47 @@
/datum/design/apc_board
name = "APC Module"
id = "power control"
- build_type = AUTOLATHE
+ build_type = AUTOLATHE | PROTOLATHE
materials = list(MAT_METAL = 100, MAT_GLASS = 100)
build_path = /obj/item/electronics/apc
category = list("initial", "Electronics")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/airlock_board
name = "Airlock Electronics"
id = "airlock_board"
- build_type = AUTOLATHE
+ build_type = AUTOLATHE | PROTOLATHE
materials = list(MAT_METAL = 50, MAT_GLASS = 50)
build_path = /obj/item/electronics/airlock
category = list("initial", "Electronics")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/firelock_board
name = "Firelock Circuitry"
id = "firelock_board"
- build_type = AUTOLATHE
+ build_type = AUTOLATHE | PROTOLATHE
materials = list(MAT_METAL = 50, MAT_GLASS = 50)
build_path = /obj/item/electronics/firelock
category = list("initial", "Electronics")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/airalarm_electronics
name = "Air Alarm Electronics"
id = "airalarm_electronics"
- build_type = AUTOLATHE
+ build_type = AUTOLATHE | PROTOLATHE
materials = list(MAT_METAL = 50, MAT_GLASS = 50)
build_path = /obj/item/electronics/airalarm
category = list("initial", "Electronics")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/firealarm_electronics
name = "Fire Alarm Electronics"
id = "firealarm_electronics"
- build_type = AUTOLATHE
+ build_type = AUTOLATHE | PROTOLATHE
materials = list(MAT_METAL = 50, MAT_GLASS = 50)
build_path = /obj/item/electronics/firealarm
category = list("initial", "Electronics")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
/datum/design/camera
name = "Camera"
@@ -451,7 +456,7 @@
name = "Speed Loader (.38)"
id = "c38"
build_type = AUTOLATHE
- materials = list(MAT_METAL = 30000)
+ materials = list(MAT_METAL = 20000)
build_path = /obj/item/ammo_box/c38
category = list("initial", "Security")
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index be3ec60637..5771ff9891 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -482,3 +482,19 @@
build_path = /obj/item/circuitboard/machine/dish_drive
category = list ("Misc. Machinery")
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
+
+/datum/design/board/stacking_unit_console
+ name = "Machine Design (Stacking Machine Console)"
+ desc = "The circuit board for a Stacking Machine Console."
+ id = "stack_console"
+ build_path = /obj/item/circuitboard/machine/stacking_unit_console
+ category = list ("Misc. Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING
+
+/datum/design/board/stacking_machine
+ name = "Machine Design (Stacking Machine)"
+ desc = "The circuit board for a Stacking Machine."
+ id = "stack_machine"
+ build_path = /obj/item/circuitboard/machine/stacking_machine
+ category = list ("Misc. Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING
\ No newline at end of file
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 8f79bb9a6d..fd3eefbde1 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -354,3 +354,17 @@
build_path = /obj/item/weldingtool/experimental
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+
+/////////////////////////////////////////
+////////////Armour//////////////
+/////////////////////////////////////////
+
+/datum/design/reactive_armour
+ name = "Reactive Armour Shell"
+ desc = "An experimental suit of armour capable of utilizing an implanted anomaly core to protect the user."
+ id = "reactive_armour"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 10000, MAT_DIAMOND = 5000, MAT_URANIUM = 8000, MAT_SILVER = 4500, MAT_GOLD = 5000)
+ build_path = /obj/item/reactive_armour_shell
+ category = list("Equipment")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
\ No newline at end of file
diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm
index 821e6cee38..79a9748b08 100644
--- a/code/modules/research/designs/smelting_designs.dm
+++ b/code/modules/research/designs/smelting_designs.dm
@@ -21,15 +21,23 @@
name = "Plasma + Glass alloy"
id = "plasmaglass"
build_type = SMELTER
- materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
+ materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plasmaglass
category = list("initial")
+/datum/design/plasmarglass_alloy
+ name = "Plasma + Metal + Glass alloy"
+ id = "plasmareinforcedglass"
+ build_type = SMELTER
+ materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_METAL = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
+ build_path = /obj/item/stack/sheet/plasmarglass
+ category = list("initial")
+
/datum/design/titaniumglass_alloy
name = "Titanium + Glass alloy"
id = "titaniumglass"
build_type = SMELTER
- materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
+ materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/titaniumglass
category = list("initial")
@@ -37,7 +45,7 @@
name = "Plasma + Titanium + Glass alloy"
id = "plastitaniumglass"
build_type = SMELTER
- materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT, MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
+ materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plastitaniumglass
category = list("initial")
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 23b39de3fe..42831cc013 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -44,24 +44,21 @@
/obj/machinery/rnd/experimentor/proc/SetTypeReactions()
- var/probWeight = 0
for(var/I in typesof(/obj/item))
if(istype(I, /obj/item/relic))
item_reactions["[I]"] = SCANTYPE_DISCOVER
else
item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
+
if(ispath(I, /obj/item/stock_parts) || ispath(I, /obj/item/grenade/chem_grenade) || ispath(I, /obj/item/kitchen))
var/obj/item/tempCheck = I
if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
- valid_items += 15
- valid_items += I
- probWeight++
+ valid_items["[I]"] += 15
if(ispath(I, /obj/item/reagent_containers/food))
var/obj/item/tempCheck = I
if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
- valid_items += rand(1,max(2,35-probWeight))
- valid_items += I
+ valid_items["[I]"] += rand(1,4)
if(ispath(I, /obj/item/construction/rcd) || ispath(I, /obj/item/grenade) || ispath(I, /obj/item/aicard) || ispath(I, /obj/item/storage/backpack/holding) || ispath(I, /obj/item/slime_extract) || ispath(I, /obj/item/onetankbomb) || ispath(I, /obj/item/transfer_valve))
var/obj/item/tempCheck = I
@@ -225,18 +222,6 @@
smoke.set_up(0, where)
smoke.start()
-/obj/machinery/rnd/experimentor/proc/pickWeighted(list/from)
- var/result = FALSE
- var/counter = 1
- while(!result)
- var/probtocheck = from[counter]
- if(prob(probtocheck))
- result = TRUE
- return from[counter+1]
- if(counter + 2 < from.len)
- counter = counter + 2
- else
- counter = 1
/obj/machinery/rnd/experimentor/proc/experiment(exp,obj/item/exp_on)
recentlyExperimented = 1
@@ -283,13 +268,13 @@
visible_message("[src] malfunctions, spewing toxic waste!")
for(var/turf/T in oview(1, src))
if(!T.density)
- if(prob(EFFECT_PROB_VERYHIGH))
+ if(prob(EFFECT_PROB_VERYHIGH) && !(locate(/obj/effect/decal/cleanable/greenglow) in T))
var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
reagentdecal.reagents.add_reagent("radium", 7)
else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
var/savedName = "[exp_on]"
ejectItem(TRUE)
- var/newPath = pickWeighted(valid_items)
+ var/newPath = text2path(pickweight(valid_items))
loaded_item = new newPath(src)
visible_message("[src] malfunctions, transforming [savedName] into [loaded_item]!")
investigate_log("Experimentor has transformed [savedName] into [loaded_item]", INVESTIGATE_EXPERIMENTOR)
diff --git a/code/modules/research/machinery/departmental_circuit_imprinter.dm b/code/modules/research/machinery/departmental_circuit_imprinter.dm
index 5049a25893..c51288b685 100644
--- a/code/modules/research/machinery/departmental_circuit_imprinter.dm
+++ b/code/modules/research/machinery/departmental_circuit_imprinter.dm
@@ -9,5 +9,6 @@
/obj/machinery/rnd/production/circuit_imprinter/department/science
name = "department circuit imprinter (Science)"
+ circuit = /obj/item/circuitboard/machine/circuit_imprinter/department/science
allowed_department_flags = DEPARTMENTAL_FLAG_ALL|DEPARTMENTAL_FLAG_SCIENCE
department_tag = "Science"
\ No newline at end of file
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 6c85a16498..329bbbebee 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -50,6 +50,7 @@ doesn't have toxins access.
var/research_control = TRUE
/obj/machinery/computer/rdconsole/production
+ circuit = /obj/item/circuitboard/computer/rdconsole/production
research_control = FALSE
/proc/CallMaterialName(ID)
@@ -192,9 +193,10 @@ doesn't have toxins access.
/obj/machinery/computer/rdconsole/emag_act(mob/user)
if(!(obj_flags & EMAGGED))
- to_chat(user, "You disable the security protocols")
+ to_chat(user, "You disable the security protocols[locked? " and unlock the console.":""].")
playsound(src, "sparks", 75, 1)
obj_flags |= EMAGGED
+ locked = FALSE
return ..()
/obj/machinery/computer/rdconsole/proc/list_categories(list/categories, menu_num as num)
@@ -217,6 +219,8 @@ doesn't have toxins access.
/obj/machinery/computer/rdconsole/proc/ui_header()
var/list/l = list()
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/research_designs)
+ l += "[sheet.css_tag()][RDSCREEN_NOBREAK]"
l += "[stored_research.organization] Research and Development Network"
l += "Available points: [round(stored_research.research_points)] (+[round(stored_research.last_bitcoins * 60)] / minute)"
l += "Security protocols: [obj_flags & EMAGGED ? "Disabled" : "Enabled"]"
@@ -815,6 +819,9 @@ doesn't have toxins access.
if(ls["ui_mode"])
ui_mode = text2num(ls["ui_mode"])
if(ls["lock_console"])
+ if(obj_flags & EMAGGED)
+ to_chat(usr, "Security protocol error: Unable to lock.")
+ return
if(allowed(usr))
lock_console(usr)
else
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index df1887c4db..c69d0ad728 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -93,7 +93,8 @@
description = "A refresher course on modern engineering technology."
prereq_ids = list("base")
design_ids = list("solarcontrol", "recharger", "powermonitor", "rped", "pacman", "adv_capacitor", "adv_scanning", "emitter", "high_cell", "adv_matter_bin",
- "atmosalerts", "atmos_control", "recycler", "autolathe", "high_micro_laser", "nano_mani", "mesons", "thermomachine", "rad_collector", "tesla_coil", "grounding_rod", "apc_control", "cell_charger")
+ "atmosalerts", "atmos_control", "recycler", "autolathe", "high_micro_laser", "nano_mani", "mesons", "thermomachine", "rad_collector", "tesla_coil", "grounding_rod",
+ "apc_control", "cell_charger", "power control", "airlock_board", "firelock_board", "airalarm_electronics", "firealarm_electronics", "cell_charger", "stack_console", "stack_machine")
research_cost = 7500
export_price = 5000
@@ -106,6 +107,15 @@
research_cost = 2500
export_price = 5000
+/datum/techweb_node/anomaly
+ id = "anomaly_research"
+ display_name = "Anomaly Research"
+ description = "Unlock the potential of the mysterious anomalies that appear on station."
+ prereq_ids = list("adv_engi", "practical_bluespace")
+ design_ids = list("reactive_armour")
+ research_cost = 2500
+ export_price = 5000
+
/datum/techweb_node/high_efficiency
id = "high_efficiency"
display_name = "High Efficiency Parts"
@@ -153,7 +163,6 @@
research_cost = 5000
export_price = 5000
-
/datum/techweb_node/bluespace_power
id = "bluespace_power"
display_name = "Bluespace Power Technology"
@@ -867,7 +876,7 @@
display_name = "Illegal Technology"
description = "Dangerous research used to create dangerous objects."
prereq_ids = list("adv_engi", "adv_weaponry", "explosive_weapons")
- design_ids = list("decloner", "borg_syndicate_module", "suppressor", "largecrossbow")
+ design_ids = list("decloner", "borg_syndicate_module", "suppressor", "largecrossbow", "donksofttoyvendor")
research_cost = 10000
export_price = 5000
hidden = TRUE
diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
index 73887fa6a9..b100e73bc7 100644
--- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
@@ -448,8 +448,7 @@ datum/status_effect/stabilized/blue/on_remove()
else if(istype(O, /obj/item/stack/sheet/hairlesshide))
to_chat(owner, "[linked_extract] kept your hands wet! It wets [O]!")
var/obj/item/stack/sheet/hairlesshide/HH = O
- var/obj/item/stack/sheet/wetleather/WL = new(get_turf(HH))
- WL.amount = HH.amount
+ new /obj/item/stack/sheet/wetleather(get_turf(HH), HH.amount)
qdel(HH)
..()
diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm
index 68aff64ee9..18b4e81398 100644
--- a/code/modules/research/xenobiology/crossbreeding/charged.dm
+++ b/code/modules/research/xenobiology/crossbreeding/charged.dm
@@ -66,10 +66,8 @@ Charged extracts:
colour = "metal"
/obj/item/slimecross/charged/metal/do_effect(mob/user)
- var/obj/item/stack/sheet/metal/M = new(get_turf(user))
- M.amount = 25
- var/obj/item/stack/sheet/plasteel/P = new(get_turf(user))
- P.amount = 10
+ new /obj/item/stack/sheet/metal(get_turf(user), 25)
+ new /obj/item/stack/sheet/plasteel(get_turf(user), 10)
user.visible_message("[src] grows into a plethora of metals!")
..()
@@ -85,8 +83,7 @@ Charged extracts:
colour = "dark purple"
/obj/item/slimecross/charged/darkpurple/do_effect(mob/user)
- var/obj/item/stack/sheet/mineral/plasma/M = new(get_turf(user))
- M.amount = 10
+ new /obj/item/stack/sheet/mineral/plasma(get_turf(user), 10)
user.visible_message("[src] produces a large amount of plasma!")
..()
@@ -113,8 +110,7 @@ Charged extracts:
colour = "bluespace"
/obj/item/slimecross/charged/bluespace/do_effect(mob/user)
- var/obj/item/stack/sheet/bluespace_crystal/M = new(get_turf(user))
- M.amount = 10
+ new /obj/item/stack/sheet/bluespace_crystal(get_turf(user), 10)
user.visible_message("[src] produces several sheets of polycrystal!")
..()
@@ -138,8 +134,7 @@ Charged extracts:
colour = "pyrite"
/obj/item/slimecross/charged/pyrite/do_effect(mob/user)
- var/obj/item/stack/sheet/mineral/bananium/M = new(get_turf(user))
- M.amount = 10
+ new /obj/item/stack/sheet/mineral/bananium(get_turf(user), 10)
user.visible_message("[src] solidifies with a horrifying banana stench!")
..()
diff --git a/code/modules/research/xenobiology/crossbreeding/industrial.dm b/code/modules/research/xenobiology/crossbreeding/industrial.dm
index 2503219e83..95f87ede0a 100644
--- a/code/modules/research/xenobiology/crossbreeding/industrial.dm
+++ b/code/modules/research/xenobiology/crossbreeding/industrial.dm
@@ -75,12 +75,7 @@ Industrial extracts:
/obj/item/slimecross/industrial/metal
colour = "metal"
plasmarequired = 3
- itempath = /obj/item/stack/sheet/metal
-
-/obj/item/slimecross/industrial/metal/do_after_spawn(obj/item/spawned)
- var/obj/item/stack/sheet/metal/M = spawned
- if(istype(M))
- M.amount = 10
+ itempath = /obj/item/stack/sheet/metal/ten
/obj/item/slimecross/industrial/yellow
colour = "yellow"
diff --git a/code/modules/server_tools/st_commands.dm b/code/modules/server_tools/st_commands.dm
deleted file mode 100644
index 1e071550e0..0000000000
--- a/code/modules/server_tools/st_commands.dm
+++ /dev/null
@@ -1,76 +0,0 @@
-/datum/server_tools_command
- var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
- var/help_text = "" //help text for this command
- var/required_parameters = 0 //number of parameters required for this command
- var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
-
-//override to implement command
-//sender is the display name of who sent the command
-//params is the trimmed string following the command name
-/datum/server_tools_command/proc/Run(sender, params)
- CRASH("[type] has no implementation for Run()")
-
-/world/proc/ListServiceCustomCommands(warnings_only)
- if(!warnings_only)
- . = list()
- var/list/command_name_types = list()
- var/list/warned_command_names = warnings_only ? list() : null
- for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
- var/datum/server_tools_command/stc = I
- var/command_name = initial(stc.name)
- var/static/list/warned_server_tools_names = list()
- if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
- if(warnings_only && !warned_command_names[command_name])
- SERVER_TOOLS_LOG("WARNING: Custom command [command_name] can't be used as it is empty or contains illegal characters!")
- warned_command_names[command_name] = TRUE
- continue
-
- if(command_name_types[command_name])
- if(warnings_only)
- SERVER_TOOLS_LOG("WARNING: Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
- continue
- command_name_types[stc] = command_name
-
- if(!warnings_only)
- .[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = initial(stc.required_parameters))
-
-/world/proc/HandleServiceCustomCommand(command, sender, params)
- var/static/list/cached_custom_server_tools_commands
- if(!cached_custom_server_tools_commands)
- cached_custom_server_tools_commands = list()
- for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
- var/datum/server_tools_command/stc = I
- cached_custom_server_tools_commands[lowertext(initial(stc.name))] = stc
-
- var/command_type = cached_custom_server_tools_commands[command]
- if(!command_type)
- return FALSE
- var/datum/server_tools_command/stc = new command_type
- return stc.Run(sender, params) || TRUE
-
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
diff --git a/code/modules/server_tools/st_interface.dm b/code/modules/server_tools/st_interface.dm
deleted file mode 100644
index ca7d54695b..0000000000
--- a/code/modules/server_tools/st_interface.dm
+++ /dev/null
@@ -1,135 +0,0 @@
-SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL)
-SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
-
-/proc/GetTestMerges()
- if(RunningService(TRUE) && fexists(SERVICE_PR_TEST_JSON))
- . = json_decode(file2text(SERVICE_PR_TEST_JSON))
- if(.)
- return
- return list()
-
-/world/proc/ServiceInit()
- if(!RunningService(TRUE))
- return
- ListServiceCustomCommands(TRUE)
- ExportService("[SERVICE_REQUEST_API_VERSION] [SERVER_TOOLS_API_VERSION]", TRUE)
-
-/proc/RunningService(skip_compat_check = FALSE)
- if(!skip_compat_check && !SERVER_TOOLS_READ_GLOBAL(server_tools_api_compatible))
- return FALSE
- . = world.params[SERVICE_WORLD_PARAM] != null
- if(. && world.system_type != MS_WINDOWS)
- SERVER_TOOLS_LOG("Warning: Server tools world parameter detected but not running on Windows. Aborting initialization!")
- return FALSE
-
-/proc/ServiceVersion()
- if(RunningService(TRUE))
- return world.params[SERVICE_VERSION_PARAM]
-
-/proc/ServiceAPIVersion()
- return SERVICE_API_VERSION_STRING
-
-/world/proc/ExportService(command, skip_compat_check = FALSE)
- . = FALSE
- if(!RunningService(skip_compat_check))
- return
- if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
- CRASH("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
- var/instance = params[SERVICE_INSTANCE_PARAM]
- if(!instance)
- instance = "TG Station Server" //maybe just upgraded
- call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance, command) //trust no retval
- return TRUE
-
-/world/proc/ChatBroadcast(message)
- ExportService("[SERVICE_REQUEST_IRC_BROADCAST] [message]")
-
-/world/proc/AdminBroadcast(message)
- ExportService("[SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE] [message]")
-
-/world/proc/ServiceEndProcess()
- SERVER_TOOLS_LOG("Sending shutdown request!");
- sleep(world.tick_lag) //flush the buffers
- ExportService(SERVICE_REQUEST_KILL_PROCESS)
-
-//called at the exact moment the world is supposed to reboot
-/world/proc/ServiceReboot()
- switch(SERVER_TOOLS_READ_GLOBAL(reboot_mode))
- if(REBOOT_MODE_HARD)
- SERVER_TOOLS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...")
- ServiceEndProcess()
- if(REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_WORLD_ANNOUNCE("The server is shutting down...")
- ServiceEndProcess()
- else
- ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know
-
-/world/proc/ServiceCommand(list/params)
- var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
- if(!their_sCK || !RunningService(TRUE))
- return FALSE //continue world/Topic
-
- var/sCK = world.params[SERVICE_WORLD_PARAM]
- if(their_sCK != sCK)
- return "Invalid comms key!";
-
- var/command = params[SERVICE_CMD_PARAM_COMMAND]
- if(!command)
- return "No command!"
-
- switch(command)
- if(SERVICE_CMD_API_COMPATIBLE)
- SERVER_TOOLS_WRITE_GLOBAL(server_tools_api_compatible, TRUE)
- return SERVICE_RETURN_SUCCESS
- if(SERVICE_CMD_HARD_REBOOT)
- if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_HARD)
- SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_HARD)
- SERVER_TOOLS_LOG("Hard reboot requested by service")
- SERVER_TOOLS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by service.")
- if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
- if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_SHUTDOWN)
- SERVER_TOOLS_LOG("Shutdown requested by service")
- message_admins("The world will shutdown at the end of the game. Requested by service.")
- if(SERVICE_CMD_WORLD_ANNOUNCE)
- var/msg = params["message"]
- if(!istext(msg) || !msg)
- return "No message set!"
- SERVER_TOOLS_WORLD_ANNOUNCE(msg)
- return SERVICE_RETURN_SUCCESS
- if(SERVICE_CMD_PLAYER_COUNT)
- return "[SERVER_TOOLS_CLIENT_COUNT]"
- if(SERVICE_CMD_LIST_CUSTOM)
- return json_encode(ListServiceCustomCommands(FALSE))
- else
- var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM])
- if(custom_command_result)
- return istext(custom_command_result) ? custom_command_result : SERVICE_RETURN_SUCCESS
- return "Unknown command: [command]"
-
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index ee02bd524f..89e513fd60 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -144,7 +144,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
var/smoke_spread = 0 //1 - harmless, 2 - harmful
var/smoke_amt = 0 //cropped at 10
- var/critfailchance = 0
var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2
action_icon = 'icons/mob/actions/actions_spells.dmi'
@@ -153,7 +152,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
base_action = /datum/action/spell_action/spell
/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
-
if(player_lock)
if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list))
to_chat(user, "You shouldn't have this spell! Something's wrong.")
@@ -168,15 +166,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
return 0
if(!skipcharge)
- switch(charge_type)
- if("recharge")
- if(charge_counter < charge_max)
- to_chat(user, still_recharging_msg)
- return 0
- if("charges")
- if(!charge_counter)
- to_chat(user, "[name] has no charges left.")
- return 0
+ if(!charge_check(user))
+ return 0
if(user.stat && !stat_allowed)
to_chat(user, "Not when you're incapacitated.")
@@ -236,6 +227,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
action.UpdateButtonIcon()
return 1
+/obj/effect/proc_holder/spell/proc/charge_check(mob/user, silent = FALSE)
+ switch(charge_type)
+ if("recharge")
+ if(charge_counter < charge_max)
+ if(!silent)
+ to_chat(user, still_recharging_msg)
+ return FALSE
+ if("charges")
+ if(!charge_counter)
+ if(!silent)
+ to_chat(user, "[name] has no charges left.")
+ return FALSE
+ return TRUE
+
/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount
switch(invocation_type)
if("shout")
@@ -297,10 +302,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
recharging = TRUE
if(sound)
playMagSound()
- if(prob(critfailchance))
- critfail(targets)
- else
- cast(targets,user=user)
+ cast(targets,user=user)
after_cast(targets)
if(action)
action.UpdateButtonIcon()
@@ -349,9 +351,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr)
return
-/obj/effect/proc_holder/spell/proc/critfail(list/targets)
- return
-
/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge
switch(charge_type)
if("recharge")
@@ -500,25 +499,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr)
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
- return 0
+ return FALSE
- switch(charge_type)
- if("recharge")
- if(charge_counter < charge_max)
- return 0
- if("charges")
- if(!charge_counter)
- return 0
+ if(!charge_check(user,TRUE))
+ return FALSE
if(user.stat && !stat_allowed)
- return 0
+ return FALSE
if(!ishuman(user))
if(clothes_req || human_req)
- return 0
+ return FALSE
if(nonabstract_req && (isbrain(user) || ispAI(user)))
- return 0
- return 1
+ return FALSE
+ return TRUE
/obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke)
range = -1 //Duh
diff --git a/code/modules/spells/spell_types/godhand.dm b/code/modules/spells/spell_types/godhand.dm
index 2def02b4c0..bf402b7a6a 100644
--- a/code/modules/spells/spell_types/godhand.dm
+++ b/code/modules/spells/spell_types/godhand.dm
@@ -13,10 +13,7 @@
throwforce = 0
throw_range = 0
throw_speed = 0
-
-/obj/item/melee/touch_attack/Initialize()
- attached_spell = loc
- . = ..()
+ var/charges = 1
/obj/item/melee/touch_attack/attack(mob/target, mob/living/carbon/user)
if(!iscarbon(user)) //Look ma, no hands
@@ -29,13 +26,13 @@
/obj/item/melee/touch_attack/afterattack(atom/target, mob/user, proximity)
user.say(catchphrase)
playsound(get_turf(user), on_use_sound,50,1)
- if(attached_spell)
- attached_spell.attached_hand = null
- qdel(src)
+ charges--
+ if(charges <= 0)
+ qdel(src)
/obj/item/melee/touch_attack/Destroy()
if(attached_spell)
- attached_spell.attached_hand = null
+ attached_spell.on_hand_destroy(src)
return ..()
/obj/item/melee/touch_attack/disintegrate
diff --git a/code/modules/spells/spell_types/mind_transfer.dm b/code/modules/spells/spell_types/mind_transfer.dm
index c08557649b..ffdd270994 100644
--- a/code/modules/spells/spell_types/mind_transfer.dm
+++ b/code/modules/spells/spell_types/mind_transfer.dm
@@ -19,13 +19,15 @@ Urist: I don't feel like figuring out how you store object spells so I'm leaving
Make sure spells that are removed from spell_list are actually removed and deleted when mind transferring.
Also, you never added distance checking after target is selected. I've went ahead and did that.
*/
-/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride)
+/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride, silent = FALSE)
if(!targets.len)
- to_chat(user, "No mind found!")
+ if(!silent)
+ to_chat(user, "No mind found!")
return
if(targets.len > 1)
- to_chat(user, "Too many minds! You're not a hive damnit!")
+ if(!silent)
+ to_chat(user, "Too many minds! You're not a hive damnit!")
return
var/mob/living/target = targets[1]
@@ -34,28 +36,34 @@ Also, you never added distance checking after target is selected. I've went ahea
var/t_is = target.p_are()
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
- to_chat(user, "[t_He] [t_is] too far away!")
+ if(!silent)
+ to_chat(user, "[t_He] [t_is] too far away!")
return
if(ismegafauna(target))
- to_chat(user, "This creature is too powerful to control!")
+ if(!silent)
+ to_chat(user, "This creature is too powerful to control!")
return
if(target.stat == DEAD)
- to_chat(user, "You don't particularly want to be dead!")
+ if(!silent)
+ to_chat(user, "You don't particularly want to be dead!")
return
if(!target.key || !target.mind)
- to_chat(user, "[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.")
+ if(!silent)
+ to_chat(user, "[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.")
return
if(user.suiciding)
- to_chat(user, "You're killing yourself! You can't concentrate enough to do this!")
+ if(!silent)
+ to_chat(user, "You're killing yourself! You can't concentrate enough to do this!")
return
var/datum/mind/TM = target.mind
if((target.anti_magic_check() || TM.has_antag_datum(/datum/antagonist/wizard) || TM.has_antag_datum(/datum/antagonist/cult) || TM.has_antag_datum(/datum/antagonist/clockcult) || TM.has_antag_datum(/datum/antagonist/changeling) || TM.has_antag_datum(/datum/antagonist/rev)) || cmptext(copytext(target.key,1,2),"@"))
- to_chat(user, "[target.p_their(TRUE)] mind is resisting your spell!")
+ if(!silent)
+ to_chat(user, "[target.p_their(TRUE)] mind is resisting your spell!")
return
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
@@ -77,3 +85,4 @@ Also, you never added distance checking after target is selected. I've went ahea
victim.Unconscious(unconscious_amount_victim)
SEND_SOUND(caster, sound('sound/magic/mandswap.ogg'))
SEND_SOUND(victim, sound('sound/magic/mandswap.ogg'))// only the caster and victim hear the sounds, that way no one knows for sure if the swap happened
+ return TRUE
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index 2000d8daea..0ffe02cec2 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -1,37 +1,46 @@
/obj/effect/proc_holder/spell/targeted/touch
- var/hand_path = "/obj/item/melee/touch_attack"
+ var/hand_path = /obj/item/melee/touch_attack
var/obj/item/melee/touch_attack/attached_hand = null
invocation_type = "none" //you scream on connecting, not summoning
include_user = 1
range = -1
-/obj/effect/proc_holder/spell/targeted/touch/Click(mob/user = usr)
- if(attached_hand)
- qdel(attached_hand)
+/obj/effect/proc_holder/spell/targeted/touch/proc/remove_hand(recharge = FALSE)
+ QDEL_NULL(attached_hand)
+ if(recharge)
charge_counter = charge_max
- attached_hand = null
- to_chat(user, "You draw the power out of your hand.")
- return FALSE
- ..()
+
+/obj/effect/proc_holder/spell/targeted/touch/proc/on_hand_destroy(obj/item/melee/touch_attack/hand)
+ if(hand != attached_hand)
+ CRASH("Incorrect touch spell hand.")
+ //Start recharging.
+ attached_hand = null
+ recharging = TRUE
+ action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
+ if(!QDELETED(attached_hand))
+ remove_hand(TRUE)
+ to_chat(user, "You draw the power out of your hand.")
+ return
+
for(var/mob/living/carbon/C in targets)
if(!attached_hand)
- if(!ChargeHand(C))
- return FALSE
- while(attached_hand)
- charge_counter = 0
- stoplag(1)
+ if(ChargeHand(C))
+ recharging = FALSE
+ return
-/obj/effect/proc_holder/spell/targeted/touch/can_cast(mob/user = usr)
- return ..() && attached_hand
+/obj/effect/proc_holder/spell/targeted/touch/charge_check(mob/user,silent = FALSE)
+ if(!QDELETED(attached_hand)) //Charge doesn't matter when putting the hand away.
+ return TRUE
+ else
+ return ..()
/obj/effect/proc_holder/spell/targeted/touch/proc/ChargeHand(mob/living/carbon/user)
attached_hand = new hand_path(src)
+ attached_hand.attached_spell = src
if(!user.put_in_hands(attached_hand))
- qdel(attached_hand)
- charge_counter = charge_max
- attached_hand = null
+ remove_hand(TRUE)
to_chat(user, "Your hands are full!")
return FALSE
to_chat(user, "You channel the power of the spell to your hand.")
@@ -41,7 +50,7 @@
/obj/effect/proc_holder/spell/targeted/touch/disintegrate
name = "Disintegrate"
desc = "This spell charges your hand with vile energy that can be used to violently explode victims."
- hand_path = "/obj/item/melee/touch_attack/disintegrate"
+ hand_path = /obj/item/melee/touch_attack/disintegrate
school = "evocation"
charge_max = 600
@@ -53,7 +62,7 @@
/obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone
name = "Flesh to Stone"
desc = "This spell charges your hand with the power to turn victims into inert statues for a long period of time."
- hand_path = "/obj/item/melee/touch_attack/fleshtostone"
+ hand_path = /obj/item/melee/touch_attack/fleshtostone
school = "transmutation"
charge_max = 600
diff --git a/code/modules/surgery/advanced/bioware/bioware.dm b/code/modules/surgery/advanced/bioware/bioware.dm
index 6c0a7529d0..f3435e874f 100644
--- a/code/modules/surgery/advanced/bioware/bioware.dm
+++ b/code/modules/surgery/advanced/bioware/bioware.dm
@@ -15,8 +15,9 @@
if(B.mod_type == mod_type)
qdel(src)
return
+ owner.bioware += src
on_gain()
-
+
/datum/bioware/Destroy()
owner = null
if(active)
diff --git a/code/modules/surgery/advanced/bioware/nerve_grounding.dm b/code/modules/surgery/advanced/bioware/nerve_grounding.dm
index 7834884f52..494cfaf59b 100644
--- a/code/modules/surgery/advanced/bioware/nerve_grounding.dm
+++ b/code/modules/surgery/advanced/bioware/nerve_grounding.dm
@@ -31,7 +31,7 @@
/datum/bioware/grounded_nerves
name = "Grounded Nerves"
desc = "Nerves form a safe path for electricity to traverse, protecting the body from electric shocks."
- mod_type = "nerves"
+ mod_type = BIOWARE_NERVES
var/prev_coeff
/datum/bioware/grounded_nerves/on_gain()
diff --git a/code/modules/surgery/advanced/bioware/nerve_splicing.dm b/code/modules/surgery/advanced/bioware/nerve_splicing.dm
index 770a8d67e6..944a05ed0e 100644
--- a/code/modules/surgery/advanced/bioware/nerve_splicing.dm
+++ b/code/modules/surgery/advanced/bioware/nerve_splicing.dm
@@ -31,7 +31,7 @@
/datum/bioware/spliced_nerves
name = "Spliced Nerves"
desc = "Nerves are connected to each other multiple times, greatly reducing the impact of stunning effects."
- mod_type = "nerves"
+ mod_type = BIOWARE_NERVES
/datum/bioware/spliced_nerves/on_gain()
..()
diff --git a/code/modules/surgery/advanced/bioware/vein_threading.dm b/code/modules/surgery/advanced/bioware/vein_threading.dm
index 99f6506de8..d618100a47 100644
--- a/code/modules/surgery/advanced/bioware/vein_threading.dm
+++ b/code/modules/surgery/advanced/bioware/vein_threading.dm
@@ -31,7 +31,7 @@
/datum/bioware/threaded_veins
name = "Threaded Veins"
desc = "The circulatory system is woven into a mesh, severely reducing the amount of blood lost from wounds."
- mod_type = "circulation"
+ mod_type = BIOWARE_CIRCULATION
/datum/bioware/threaded_veins/on_gain()
..()
diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm
index 1c81f31d45..360e58924b 100755
--- a/code/modules/surgery/organs/liver.dm
+++ b/code/modules/surgery/organs/liver.dm
@@ -41,7 +41,7 @@
C.reagents.metabolize(C, can_overdose=TRUE)
if(damage > 10 && prob(damage/3))//the higher the damage the higher the probability
- to_chat(C, "You feel [pick("nauseous", "dull pain in your lower body", "confused")].")
+ to_chat(C, "You feel [pick("nauseated", "a dull pain in your lower body", "confused")].")
if(damage > maxHealth)//cap liver damage
damage = maxHealth
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index ce5fafcd9f..a3fe2202b6 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -236,13 +236,13 @@
var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
if(bz_pp > BZ_trip_balls_min)
- H.hallucination += 20
+ H.hallucination += 10
H.reagents.add_reagent("bz_metabolites",5)
if(prob(33))
H.adjustBrainLoss(3, 150)
else if(bz_pp > 0.01)
- H.hallucination += 5//Removed at 2 per tick so this will slowly build up
+ H.hallucination += 5
H.reagents.add_reagent("bz_metabolites",1)
diff --git a/code/modules/tgs/core/_definitions.dm b/code/modules/tgs/core/_definitions.dm
new file mode 100644
index 0000000000..d5e1a0075b
--- /dev/null
+++ b/code/modules/tgs/core/_definitions.dm
@@ -0,0 +1,2 @@
+#define TGS_UNIMPLEMENTED "___unimplemented"
+#define TGS_VERSION_PARAMETER "server_service_version"
diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm
new file mode 100644
index 0000000000..79c42ed37b
--- /dev/null
+++ b/code/modules/tgs/core/core.dm
@@ -0,0 +1,144 @@
+/world/TgsNew(datum/tgs_event_handler/event_handler)
+ var/tgs_version = world.params[TGS_VERSION_PARAMETER]
+ if(!tgs_version)
+ return
+
+ var/path = SelectTgsApi(tgs_version)
+ if(!path)
+ TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.")
+
+ TGS_INFO_LOG("Activating API for version [tgs_version]")
+ var/datum/tgs_api/new_api = new path
+
+ var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default)
+ if(result && result != TGS_UNIMPLEMENTED)
+ TGS_WRITE_GLOBAL(tgs, new_api)
+ else
+ TGS_ERROR_LOG("Failed to activate API!")
+
+/world/proc/SelectTgsApi(tgs_version)
+ //remove the old 3.0 header
+ tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "")
+
+ var/list/version_bits = splittext(tgs_version, ".")
+
+ var/super = text2num(version_bits[1])
+ var/major = text2num(version_bits[2])
+ var/minor = text2num(version_bits[3])
+ var/patch = text2num(version_bits[4])
+
+ switch(super)
+ if(3)
+ switch(major)
+ if(2)
+ return /datum/tgs_api/v3210
+
+ if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion())
+ TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
+ return /datum/tgs_api/latest
+
+/world/TgsMaximumAPIVersion()
+ return "4.0.0.0"
+
+/world/TgsMinimumAPIVersion()
+ return "3.2.0.0"
+
+/world/TgsInitializationComplete()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.OnInitializationComplete()
+
+/world/proc/TgsTopic(T)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.OnTopic(T)
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsRevision()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.Revision()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsReboot()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.OnReboot()
+
+/world/TgsAvailable()
+ return TGS_READ_GLOBAL(tgs) != null
+
+/world/TgsVersion()
+ return world.params[TGS_VERSION_PARAMETER]
+
+/world/TgsInstanceName()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.InstanceName()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+
+/world/TgsTestMerges()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.TestMerges()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+ return list()
+
+/world/TgsEndProcess()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.EndProcess()
+
+/world/TgsChatChannelInfo()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ var/result = api.ChatChannelInfo()
+ if(result != TGS_UNIMPLEMENTED)
+ return result
+ return list()
+
+/world/TgsChatBroadcast(message, list/channels)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatBroadcast(message, channels)
+
+/world/TgsTargetedChatBroadcast(message, admin_only)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatTargetedBroadcast(message, admin_only)
+
+/world/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.ChatPrivateMessage(message, user)
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm
new file mode 100644
index 0000000000..5da73bfdc5
--- /dev/null
+++ b/code/modules/tgs/core/datum.dm
@@ -0,0 +1,74 @@
+TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
+
+/datum/tgs_api
+
+/datum/tgs_api/latest
+ parent_type = /datum/tgs_api/v3210
+
+TGS_PROTECT_DATUM(/datum/tgs_api)
+
+/datum/tgs_api/proc/ApiVersion()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnWorldNew(datum/tgs_event_handler/event_handler)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnInitializationComplete()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnTopic(T)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/OnReboot()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/InstanceName()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/TestMerges()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/EndProcess()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/Revision()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatChannelInfo()
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatBroadcast(message, list/channels)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tgs/core/default_event_handler.dm b/code/modules/tgs/core/default_event_handler.dm
new file mode 100644
index 0000000000..c0ea8eec46
--- /dev/null
+++ b/code/modules/tgs/core/default_event_handler.dm
@@ -0,0 +1,30 @@
+/datum/tgs_event_handler/tgs_default/HandleEvent(event_code)
+ //TODO
+ return
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm
new file mode 100644
index 0000000000..586cbbe427
--- /dev/null
+++ b/code/modules/tgs/includes.dm
@@ -0,0 +1,6 @@
+#include "core\_definitions.dm"
+#include "core\core.dm"
+#include "core\datum.dm"
+#include "core\default_event_handler.dm"
+#include "v3210\api.dm"
+#include "v3210\commands.dm"
diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm
new file mode 100644
index 0000000000..f9b32471de
--- /dev/null
+++ b/code/modules/tgs/v3210/api.dm
@@ -0,0 +1,248 @@
+#define REBOOT_MODE_NORMAL 0
+#define REBOOT_MODE_HARD 1
+#define REBOOT_MODE_SHUTDOWN 2
+
+#define SERVICE_WORLD_PARAM "server_service"
+#define SERVICE_INSTANCE_PARAM "server_instance"
+#define SERVICE_PR_TEST_JSON "prtestjob.json"
+#define SERVICE_INTERFACE_DLL "TGDreamDaemonBridge.dll"
+#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
+
+#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
+#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
+#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
+#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
+#define SERVICE_CMD_API_COMPATIBLE "api_compat"
+#define SERVICE_CMD_PLAYER_COUNT "client_count"
+
+#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
+#define SERVICE_CMD_PARAM_COMMAND "command"
+#define SERVICE_CMD_PARAM_SENDER "sender"
+#define SERVICE_CMD_PARAM_CUSTOM "custom"
+
+#define SERVICE_REQUEST_KILL_PROCESS "killme"
+#define SERVICE_REQUEST_IRC_BROADCAST "irc"
+#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
+#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
+#define SERVICE_REQUEST_API_VERSION "api_ver"
+
+#define SERVICE_RETURN_SUCCESS "SUCCESS"
+
+/datum/tgs_api/v3210
+ var/reboot_mode = REBOOT_MODE_NORMAL
+ var/comms_key
+ var/instance_name
+ var/originmastercommit
+ var/commit
+ var/list/cached_custom_tgs_chat_commands
+ var/warned_revison = FALSE
+ var/warned_custom_commands = FALSE
+
+/datum/tgs_api/v3210/ApiVersion()
+ return "3.2.1.0"
+
+/datum/tgs_api/v3210/proc/trim_left(text)
+ for (var/i = 1 to length(text))
+ if (text2ascii(text, i) > 32)
+ return copytext(text, i)
+ return ""
+
+/datum/tgs_api/v3210/proc/trim_right(text)
+ for (var/i = length(text), i > 0, i--)
+ if (text2ascii(text, i) > 32)
+ return copytext(text, 1, i + 1)
+ return ""
+
+/datum/tgs_api/v3210/proc/file2list(filename)
+ return splittext(trim_left(trim_right(file2text(filename))), "\n")
+
+/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version
+ . = FALSE
+ comms_key = world.params[SERVICE_WORLD_PARAM]
+ instance_name = world.params[SERVICE_INSTANCE_PARAM]
+ if(!instance_name)
+ instance_name = "TG Station Server" //maybe just upgraded
+
+ var/list/logs = file2list(".git/logs/HEAD")
+ if(logs.len)
+ logs = splittext(logs[logs.len - 1], " ")
+ commit = logs[2]
+ logs = file2list(".git/logs/refs/remotes/origin/master")
+ if(logs.len)
+ originmastercommit = splittext(logs[logs.len - 1], " ")[2]
+
+ if(world.system_type != MS_WINDOWS)
+ TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!")
+ return
+ ListServiceCustomCommands(TRUE)
+ ExportService("[SERVICE_REQUEST_API_VERSION] [ApiVersion()]", TRUE)
+ return TRUE
+
+//nothing to do for v3
+/datum/tgs_api/v3210/OnInitializationComplete()
+ return
+
+/datum/tgs_api/v3210/InstanceName()
+ return world.params[SERVICE_INSTANCE_PARAM]
+
+/datum/tgs_api/v3210/proc/ExportService(command, skip_compat_check = FALSE)
+ . = FALSE
+ if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
+ TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
+ return
+ call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
+ return TRUE
+
+/datum/tgs_api/v3210/OnTopic(T)
+ var/list/params = params2list(T)
+ var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
+ if(!their_sCK)
+ return FALSE //continue world/Topic
+
+ if(their_sCK != comms_key)
+ return "Invalid comms key!";
+
+ var/command = params[SERVICE_CMD_PARAM_COMMAND]
+ if(!command)
+ return "No command!"
+
+ switch(command)
+ if(SERVICE_CMD_API_COMPATIBLE)
+ return SERVICE_RETURN_SUCCESS
+ if(SERVICE_CMD_HARD_REBOOT)
+ if(reboot_mode != REBOOT_MODE_HARD)
+ reboot_mode = REBOOT_MODE_HARD
+ TGS_INFO_LOG("Hard reboot requested by service")
+ TGS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by TGS.")
+ if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
+ if(reboot_mode != REBOOT_MODE_SHUTDOWN)
+ reboot_mode = REBOOT_MODE_SHUTDOWN
+ TGS_INFO_LOG("Shutdown requested by service")
+ TGS_NOTIFY_ADMINS("The world will shutdown at the end of the game. Requested by TGS.")
+ if(SERVICE_CMD_WORLD_ANNOUNCE)
+ var/msg = params["message"]
+ if(!istext(msg) || !msg)
+ return "No message set!"
+ TGS_WORLD_ANNOUNCE(msg)
+ return SERVICE_RETURN_SUCCESS
+ if(SERVICE_CMD_PLAYER_COUNT)
+ return "[TGS_CLIENT_COUNT]"
+ if(SERVICE_CMD_LIST_CUSTOM)
+ return json_encode(ListServiceCustomCommands(FALSE))
+ else
+ var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM])
+ if(custom_command_result)
+ return istext(custom_command_result) ? custom_command_result : SERVICE_RETURN_SUCCESS
+ return "Unknown command: [command]"
+
+/datum/tgs_api/v3210/OnReboot()
+ switch(reboot_mode)
+ if(REBOOT_MODE_HARD)
+ TGS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...")
+ EndProcess()
+ if(REBOOT_MODE_SHUTDOWN)
+ TGS_WORLD_ANNOUNCE("The server is shutting down...")
+ EndProcess()
+ else
+ ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know
+
+/datum/tgs_api/v3210/TestMerges()
+ //do the best we can here as the datum can't be completed using the v3 api
+ . = list()
+ if(!fexists(SERVICE_PR_TEST_JSON))
+ return
+ var/list/json = json_decode(file2text(SERVICE_PR_TEST_JSON))
+ if(!json)
+ return
+ for(var/I in json)
+ var/datum/tgs_revision_information/test_merge/tm = new
+ tm.number = text2num(I)
+ var/list/entry = json[I]
+ tm.pull_request_commit = entry["commit"]
+ tm.author = entry["author"]
+ tm.title = entry["title"]
+ . += tm
+
+/datum/tgs_api/v3210/Revision()
+ if(!warned_revison)
+ TGS_ERROR_LOG("Use of TgsRevision on [ApiVersion()] origin_commit only points to master!")
+ warned_revison = TRUE
+ var/datum/tgs_revision_information/ri = new
+ ri.commit = commit
+ ri.origin_commit = originmastercommit
+
+/datum/tgs_api/v3210/EndProcess()
+ sleep(world.tick_lag) //flush the buffers
+ ExportService(SERVICE_REQUEST_KILL_PROCESS)
+
+/datum/tgs_api/v3210/ChatChannelInfo()
+ return list()
+
+/datum/tgs_api/v3210/ChatBroadcast(message, list/channels)
+ if(channels)
+ return TGS_UNIMPLEMENTED
+ ChatTargetedBroadcast(message, TRUE)
+ ChatTargetedBroadcast(message, FALSE)
+
+/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only)
+ ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]")
+
+/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only)
+ return TGS_UNIMPLEMENTED
+
+#undef REBOOT_MODE_NORMAL
+#undef REBOOT_MODE_HARD
+#undef REBOOT_MODE_SHUTDOWN
+
+#undef SERVICE_WORLD_PARAM
+#undef SERVICE_INSTANCE_PARAM
+#undef SERVICE_PR_TEST_JSON
+#undef SERVICE_INTERFACE_DLL
+#undef SERVICE_INTERFACE_FUNCTION
+
+#undef SERVICE_CMD_HARD_REBOOT
+#undef SERVICE_CMD_GRACEFUL_SHUTDOWN
+#undef SERVICE_CMD_WORLD_ANNOUNCE
+#undef SERVICE_CMD_LIST_CUSTOM
+#undef SERVICE_CMD_API_COMPATIBLE
+#undef SERVICE_CMD_PLAYER_COUNT
+
+#undef SERVICE_CMD_PARAM_KEY
+#undef SERVICE_CMD_PARAM_COMMAND
+#undef SERVICE_CMD_PARAM_SENDER
+#undef SERVICE_CMD_PARAM_CUSTOM
+
+#undef SERVICE_REQUEST_KILL_PROCESS
+#undef SERVICE_REQUEST_IRC_BROADCAST
+#undef SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE
+#undef SERVICE_REQUEST_WORLD_REBOOT
+#undef SERVICE_REQUEST_API_VERSION
+
+#undef SERVICE_RETURN_SUCCESS
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm
new file mode 100644
index 0000000000..71d7e32366
--- /dev/null
+++ b/code/modules/tgs/v3210/commands.dm
@@ -0,0 +1,78 @@
+#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
+#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
+#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
+
+/datum/tgs_api/v3210/proc/ListServiceCustomCommands(warnings_only)
+ if(!warnings_only)
+ . = list()
+ var/list/command_name_types = list()
+ var/list/warned_command_names = warnings_only ? list() : null
+ var/warned_about_the_dangers_of_robutussin = !warnings_only
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ if(!warned_about_the_dangers_of_robutussin)
+ TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!")
+ warned_about_the_dangers_of_robutussin = TRUE
+ var/datum/tgs_chat_command/stc = I
+ var/command_name = initial(stc.name)
+ if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
+ if(warnings_only && !warned_command_names[command_name])
+ TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!")
+ warned_command_names[command_name] = TRUE
+ continue
+
+ if(command_name_types[command_name])
+ if(warnings_only)
+ TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
+ continue
+ command_name_types[stc] = command_name
+
+ if(!warnings_only)
+ .[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = 0)
+
+/datum/tgs_api/v3210/proc/HandleServiceCustomCommand(command, sender, params)
+ if(!cached_custom_tgs_chat_commands)
+ cached_custom_tgs_chat_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = I
+ cached_custom_tgs_chat_commands[lowertext(initial(stc.name))] = stc
+
+ var/command_type = cached_custom_tgs_chat_commands[command]
+ if(!command_type)
+ return FALSE
+ var/datum/tgs_chat_command/stc = new command_type
+ var/datum/tgs_chat_user/user = new
+ user.friendly_name = sender
+ user.mention = sender
+ return stc.Run(user, params) || TRUE
+
+/*
+
+#undef SERVICE_JSON_PARAM_HELPTEXT
+#undef SERVICE_JSON_PARAM_ADMINONLY
+#undef SERVICE_JSON_PARAM_REQUIREDPARAMETERS
+
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm
index 5110b98e8e..ab7dc91739 100644
--- a/code/modules/tooltip/tooltip.dm
+++ b/code/modules/tooltip/tooltip.dm
@@ -42,6 +42,8 @@ Notes:
/datum/tooltip/New(client/C)
if (C)
owner = C
+ var/datum/asset/stuff = get_asset_datum(/datum/asset/simple/jquery)
+ stuff.send(owner)
owner << browse(file2text('code/modules/tooltip/tooltip.html'), "window=[control]")
..()
diff --git a/code/modules/tooltip/tooltip.html b/code/modules/tooltip/tooltip.html
index 67fb8a77f1..3e2f26a09f 100644
--- a/code/modules/tooltip/tooltip.html
+++ b/code/modules/tooltip/tooltip.html
@@ -93,7 +93,7 @@
|