diff --git a/.gitconfig b/.gitconfig index de5ff6f2542..a844b100a5c 100644 --- a/.gitconfig +++ b/.gitconfig @@ -2,4 +2,9 @@ name = mapmerge driver driver = ./mapmerge.sh %O %A %B recursive = text - +[merge "merge-dmi"] + name = iconfile merge driver + driver = ./tools/dmitool/dmimerge.sh %O %A %B +[merge "merge-dmm"] + name = mapmerge driver + driver = ./tools/mapmerge/mapmerge.sh %O %A %B \ No newline at end of file diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 8c546c9135a..85eeeae876b 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -11,3 +11,5 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME) #define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list + +#define INIT_ORDER_LIGHTING 0 \ No newline at end of file diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm deleted file mode 100644 index d46cab1d19f..00000000000 --- a/code/controllers/Processes/lighting.dm +++ /dev/null @@ -1,98 +0,0 @@ -/var/lighting_overlays_initialised = FALSE - -/var/list/lighting_update_lights = list() // List of lighting sources queued for update. -/var/list/lighting_update_corners = list() // List of lighting corners queued for update. -/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update. - -/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated. -/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated. -/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated. - - -/datum/controller/process/lighting - // Queues of update counts, waiting to be rolled into stats lists - var/list/stats_queues = list( - "Source" = list(), "Corner" = list(), "Overlay" = list()) - // Stats lists - var/list/stats_lists = list( - "Source" = list(), "Corner" = list(), "Overlay" = list()) - var/update_stats_every = (1 SECONDS) - var/next_stats_update = 0 - var/stat_updates_to_keep = 5 - -/datum/controller/process/lighting/setup() - name = "lighting" - - schedule_interval = 0 // run as fast as you possibly can - sleep_interval = 10 // Yield every 10% of a tick - defer_usage = 80 // Defer at 80% of a tick - create_all_lighting_overlays() - lighting_overlays_initialised = TRUE - - // Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load. - spawn(300) - doWork(1) - -/datum/controller/process/lighting/doWork(roundstart) - - lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated. - lighting_update_lights = list() - for(var/datum/light_source/L in lighting_update_lights_old) - - if(L.check() || L.destroyed || L.force_update) - L.remove_lum() - if(!L.destroyed) - L.apply_lum() - - else if(L.vis_update) //We smartly update only tiles that became (in) visible to use. - L.smart_vis_update() - - L.vis_update = FALSE - L.force_update = FALSE - L.needs_update = FALSE - - SCHECK - - lighting_update_corners_old = lighting_update_corners //Same as above. - lighting_update_corners = list() - for(var/A in lighting_update_corners_old) - var/datum/lighting_corner/C = A - - C.update_overlays() - - C.needs_update = FALSE - - SCHECK - - lighting_update_overlays_old = lighting_update_overlays //Same as above. - lighting_update_overlays = list() - - for(var/A in lighting_update_overlays_old) - var/atom/movable/lighting_overlay/O = A - O.update_overlay() - O.needs_update = 0 - SCHECK - - stats_queues["Source"] += lighting_update_lights_old.len - stats_queues["Corner"] += lighting_update_corners_old.len - stats_queues["Overlay"] += lighting_update_overlays_old.len - - if(next_stats_update <= world.time) - next_stats_update = world.time + update_stats_every - for(var/stat_name in stats_queues) - var/stat_sum = 0 - var/list/stats_queue = stats_queues[stat_name] - for(var/count in stats_queue) - stat_sum += count - stats_queue.Cut() - - var/list/stats_list = stats_lists[stat_name] - stats_list.Insert(1, stat_sum) - if(stats_list.len > stat_updates_to_keep) - stats_list.Cut(stats_list.len) - -/datum/controller/process/lighting/statProcess() - ..() - stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays") - for(var/stat_type in stats_lists) - stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]") diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm new file mode 100644 index 00000000000..01875cc397d --- /dev/null +++ b/code/controllers/subsystems/lighting.dm @@ -0,0 +1,166 @@ +/* +** Lighting Subsystem - Process the lighting! Do it! +*/ + +#define SSLIGHTING_STAGE_LIGHTS 1 +#define SSLIGHTING_STAGE_CORNERS 2 +#define SSLIGHTING_STAGE_OVERLAYS 3 +#define SSLIGHTING_STAGE_DONE 4 +// This subsystem's fire() method also gets called once during Master.Initialize(). +// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return. +// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it. +#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; } + +// Globals +/var/lighting_overlays_initialised = FALSE +/var/list/lighting_update_lights = list() // List of lighting sources queued for update. +/var/list/lighting_update_corners = list() // List of lighting corners queued for update. +/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update. + +SUBSYSTEM_DEF(lighting) + name = "Lighting" + wait = 2 // Ticks, not deciseconds + init_order = INIT_ORDER_LIGHTING + flags = SS_TICKER + + var/list/currentrun = list() + var/stage = null + + var/cost_lights = 0 + var/cost_corners = 0 + var/cost_overlays = 0 + +/datum/controller/subsystem/lighting/Initialize(timeofday) + if(!lighting_overlays_initialised) + // TODO - TG initializes starlight here. + create_all_lighting_overlays() + lighting_overlays_initialised = TRUE + + // Pre-process lighting once before the round starts. + internal_process_lights(FALSE, TRUE) + internal_process_corners(FALSE, TRUE) + internal_process_overlays(FALSE, TRUE) + return ..() + +/datum/controller/subsystem/lighting/fire(resumed = FALSE) + var/timer + if(!resumed) + ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle + ASSERT(stage == null) // Or somehow didn't finish all the steps from last cycle + stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course + + if(stage == SSLIGHTING_STAGE_LIGHTS) + timer = world.tick_usage + internal_process_lights(resumed) + cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer)) + if(state != SS_RUNNING) + return + resumed = 0 + stage = SSLIGHTING_STAGE_CORNERS + + if(stage == SSLIGHTING_STAGE_CORNERS) + timer = world.tick_usage + internal_process_corners(resumed) + cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer)) + if(state != SS_RUNNING) + return + resumed = 0 + stage = SSLIGHTING_STAGE_OVERLAYS + + if(stage == SSLIGHTING_STAGE_OVERLAYS) + timer = world.tick_usage + internal_process_overlays(resumed) + cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer)) + if(state != SS_RUNNING) + return + resumed = 0 + stage = SSLIGHTING_STAGE_DONE + + // Okay, we're done! Woo! Got thru a whole air_master cycle! + ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left + ASSERT(stage == SSLIGHTING_STAGE_DONE) // And that we didn't somehow skip past the last step + currentrun = null + stage = null + +/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE) + if (!resumed) + // We swap out the lists so any additions to the global list during a pause don't make things wierd. + src.currentrun = global.lighting_update_lights + global.lighting_update_lights = list() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/datum/light_source/L = currentrun[currentrun.len] + currentrun.len-- + + if(!L) continue + if(L.check() || L.destroyed || L.force_update) + L.remove_lum() + if(!L.destroyed) + L.apply_lum() + + else if(L.vis_update) //We smartly update only tiles that became (in) visible to use. + L.smart_vis_update() + + L.vis_update = FALSE + L.force_update = FALSE + L.needs_update = FALSE + + DUAL_TICK_CHECK + +/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE) + if (!resumed) + // We swap out the lists so any additions to the global list during a pause don't make things wierd. + src.currentrun = global.lighting_update_corners + global.lighting_update_corners = list() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/datum/lighting_corner/C = currentrun[currentrun.len] + currentrun.len-- + + if(!C) continue + C.update_overlays() + C.needs_update = FALSE + + DUAL_TICK_CHECK + +/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE) + if (!resumed) + // We swap out the lists so any additions to the global list during a pause don't make things wierd. + src.currentrun = global.lighting_update_overlays + global.lighting_update_overlays = list() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/atom/movable/lighting_overlay/O = currentrun[currentrun.len] + currentrun.len-- + + if(!O) continue + O.update_overlay() + O.needs_update = FALSE + + DUAL_TICK_CHECK + +/datum/controller/subsystem/lighting/stat_entry(msg_prefix) + var/list/msg = list(msg_prefix) + msg += "T:{" + msg += "S [total_lighting_sources] | " + msg += "C [total_lighting_corners] | " + msg += "O [total_lighting_overlays]" + msg += "}" + msg += "C:{" + msg += "S [round(cost_lights, 1)] | " + msg += "C [round(cost_corners, 1)] | " + msg += "O [round(cost_overlays, 1)]" + msg += "}" + ..(msg.Join()) + +#undef DUAL_TICK_CHECK +#undef SSLIGHTING_STAGE_LIGHTS +#undef SSLIGHTING_STAGE_CORNERS +#undef SSLIGHTING_STAGE_OVERLAYS +#undef SSLIGHTING_STAGE_STATS \ No newline at end of file diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index 8bfe71121d4..c40c5db48cf 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -173,19 +173,6 @@ item_state = "gift" w_class = ITEMSIZE_LARGE -/obj/item/weapon/legcuffs - name = "legcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "handcuff" - flags = CONDUCT - throwforce = 0 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MATERIAL = 1) - var/breakouttime = 300 //Deciseconds = 30s = 0.5 minute - sprite_sheets = list("Teshari" = 'icons/mob/species/seromi/handcuffs.dmi') - /obj/item/weapon/caution desc = "Caution! Wet Floor!" name = "wet floor sign" diff --git a/code/game/gamemodes/changeling/powers/cryo_sting.dm b/code/game/gamemodes/changeling/powers/cryo_sting.dm index e3e401e2ff2..4bb92a543e2 100644 --- a/code/game/gamemodes/changeling/powers/cryo_sting.dm +++ b/code/game/gamemodes/changeling/powers/cryo_sting.dm @@ -30,20 +30,4 @@ spawn(3 MINUTES) src << "Our cryogenic string is ready to be used once more." src.verbs |= /mob/proc/changeling_cryo_sting - return 1 - -/datum/reagent/cryotoxin //A much more potent version of frost oil. - name = "Cryotoxin" - id = "cryotoxin" - description = "Rapidly lowers the body's internal temperature." - reagent_state = LIQUID - color = "#B31008" - -/datum/reagent/cryotoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - if(alien == IS_DIONA) - return - M.bodytemperature = max(M.bodytemperature - 30 * TEMPERATURE_DAMAGE_COEFFICIENT, 0) - if(prob(3)) - M.emote("shiver") - ..() - return \ No newline at end of file + return 1 \ No newline at end of file diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 082ab9240da..9e3efc53dab 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -624,8 +624,10 @@ var/global/datum/controller/occupations/job_master . = spawnpos.msg else H << "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead." - H.forceMove(pick(latejoin)) + var/spawning = pick(latejoin) + H.forceMove(get_turf(spawning)) . = "will arrive to the station shortly by shuttle" else - H.forceMove(pick(latejoin)) + var/spawning = pick(latejoin) + H.forceMove(get_turf(spawning)) . = "has arrived on the station" diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index b9373e1818e..7a4e5e3ab68 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -131,8 +131,16 @@ var/new_organ = products[choice][1] var/obj/item/organ/O = new new_organ(get_turf(src)) O.status |= ORGAN_CUT_AWAY - var/mob/living/carbon/C = loaded_dna["donor"] + var/mob/living/carbon/human/C = loaded_dna["donor"] O.set_dna(C.dna) + O.species = C.species + + if(istype(O, /obj/item/organ/external)) + var/obj/item/organ/external/E = O + E.sync_colour_to_human(C) + + O.pixel_x = rand(-6.0, 6) + O.pixel_y = rand(-6.0, 6) if(O.species) // This is a very hacky way of doing of what organ/New() does if it has an owner diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 8797e7af8ba..1951ba7c477 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -547,8 +547,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() race = "[H.species.name]" log.parameters["intelligible"] = 1 else if(isbrain(M)) - var/mob/living/carbon/brain/B = M - race = "[B.species.name]" + race = "Brain" log.parameters["intelligible"] = 1 else if(M.isMonkey()) race = "Monkey" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 4ca97033015..6b4a09c6a5f 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -343,12 +343,12 @@ var/list/global/slot_flags_enumeration = list( return 0 if( !(istype(src, /obj/item/device/pda) || istype(src, /obj/item/weapon/pen) || is_type_in_list(src, H.wear_suit.allowed)) ) return 0 + if(slot_legcuffed) //Going to put this check above the handcuff check because the survival of the universe depends on it. + if(!istype(src, /obj/item/weapon/handcuffs/legcuffs)) //Putting it here might actually do nothing. + return 0 if(slot_handcuffed) - if(!istype(src, /obj/item/weapon/handcuffs)) - return 0 - if(slot_legcuffed) - if(!istype(src, /obj/item/weapon/legcuffs)) - return 0 + if(!istype(src, /obj/item/weapon/handcuffs) || istype(src, /obj/item/weapon/handcuffs/legcuffs)) //Legcuffs are a child of handcuffs, but we don't want to use legcuffs as handcuffs... + return 0 //In theory, this would never happen, but let's just do the legcuff check anyways. if(slot_in_backpack) //used entirely for equipping spawned mobs or at round start var/allow = 0 if(H.back && istype(H.back, /obj/item/weapon/storage/backpack)) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index ccfb6716913..5a8a73ee727 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -317,6 +317,20 @@ item_state = "headset" ks2type = /obj/item/device/encryptionkey/heads/hos +/obj/item/device/radio/headset/mmi_radio + name = "brain-integrated radio" + desc = "MMIs and synthetic brains are often equipped with these." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + var/mmiowner = null + var/radio_enabled = 1 + +/obj/item/device/radio/headset/mmi_radio/receive_range(freq, level) + if (!radio_enabled || istype(src.loc.loc, /mob/living/silicon) || istype(src.loc.loc, /obj/item/organ/internal)) + return -1 //Transciever Disabled. + return ..(freq, level, 1) + /obj/item/device/radio/headset/attackby(obj/item/weapon/W as obj, mob/user as mob) // ..() user.set_machine(src) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index d903499ee00..ccbbb5fea79 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -353,8 +353,8 @@ REAGENT SCANNER /obj/item/device/slime_scanner name = "slime scanner" - icon_state = "adv_spectrometer" - item_state = "analyzer" + icon_state = "xenobio" + item_state = "xenobio" origin_tech = list(TECH_BIO = 1) w_class = ITEMSIZE_SMALL flags = CONDUCT diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index da6e436b271..ee21fc86a77 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -22,6 +22,14 @@ charge_costs = list(500) stacktype = /obj/item/stack/rods +/obj/item/stack/rods/New() + ..() + recipes = rods_recipes + +var/global/list/datum/stack_recipe/rods_recipes = list( \ + new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), + new/datum/stack_recipe("catwalk", /obj/structure/catwalk, 2, time = 80, one_per_turf = 1, on_floor = 1)) + /obj/item/stack/rods/attackby(obj/item/W as obj, mob/user as mob) if (istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W @@ -55,7 +63,7 @@ ..() - +/* /obj/item/stack/rods/attack_self(mob/user as mob) src.add_fingerprint(user) @@ -87,3 +95,4 @@ F.add_fingerprint(usr) use(2) return +*/ \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 8e112d96555..f9cbc65b59a 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -40,3 +40,22 @@ spawner_type = /mob/living/simple_animal/hostile/carp deliveryamt = 5 origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +/obj/item/weapon/grenade/spawnergrenade/spider + name = "spider delivery grenade" + spawner_type = /mob/living/simple_animal/hostile/giant_spider/hunter + deliveryamt = 3 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +//Sometimes you just need a sudden influx of spiders. +/obj/item/weapon/grenade/spawnergrenade/spider/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + item_state = "briefcase" + flags = CONDUCT + force = 8.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + deliveryamt = 6 \ No newline at end of file diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 62941c24f08..17bb6b84beb 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -181,3 +181,84 @@ var/last_chew = 0 icon = 'icons/obj/bureaucracy.dmi' breakouttime = 200 cuff_type = "duct tape" + +//Legcuffs. Not /really/ handcuffs, but its close enough. +/obj/item/weapon/handcuffs/legcuffs + name = "legcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "handcuff" + flags = CONDUCT + throwforce = 0 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MATERIAL = 1) + breakouttime = 300 //Deciseconds = 30s = 0.5 minute + cuff_type = "legcuffs" + sprite_sheets = list("Teshari" = 'icons/mob/species/seromi/handcuffs.dmi') + elastic = 0 + cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now. + +/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) + if(!user.IsAdvancedToolUser()) + return + + if ((CLUMSY in user.mutations) && prob(50)) + user << "Uh ... how do those things work?!" + place_legcuffs(user, user) + return + + if(!C.handcuffed) + if (C == user) + place_legcuffs(user, user) + return + + //check for an aggressive grab (or robutts) + if(can_place(C, user)) + place_legcuffs(C, user) + else + user << "You need to have a firm grip on [C] before you can put \the [src] on!" + +/obj/item/weapon/handcuffs/legcuffs/proc/place_legcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src.loc, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + return 0 + + if (!H.has_organ_for_slot(slot_legcuffed)) + user << "\The [H] needs at least two ankles before you can cuff them together!" + return 0 + + if(istype(H.shoes,/obj/item/clothing/shoes/magboots/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. + user << "\The [src] won't fit around \the [H.shoes]!" + return 0 + + user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") + + if(!do_after(user,30)) + return 0 + + if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime + return 0 + + H.attack_log += text("\[[time_stamp()]\] Has been legcuffed (attempt) by [user.name] ([user.ckey])") + user.attack_log += text("\[[time_stamp()]\] Attempted to legcuff [H.name] ([H.ckey])") + msg_admin_attack("[key_name(user)] attempted to legcuff [key_name(H)]") + feedback_add_details("legcuffs","H") + + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(H) + + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src + if(dispenser) + lcuffs = new(get_turf(user)) + else + user.drop_from_inventory(lcuffs) + lcuffs.loc = target + target.legcuffed = lcuffs + target.update_inv_legcuffed() + return 1 diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 24a21660cb2..38130f27395 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -239,7 +239,7 @@ new /obj/item/weapon/crowbar/alien(src) new /obj/item/weapon/wirecutters/alien(src) new /obj/item/device/multitool/alien(src) - new /obj/item/stack/cable_coil(src,30,"white") + new /obj/item/stack/cable_coil/alien(src) /obj/item/weapon/storage/belt/medical/alien name = "alien belt" diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index 6ff7ff8f722..2c56cd690bd 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -850,7 +850,7 @@ icon_state = "jaws_pry" item_state = "jawsoflife" matter = list(MAT_METAL=150, MAT_SILVER=50) - origin_tech = list(TECH_MATERIALS = 2, TECH_ENGINEERING = 2) + origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) usesound = 'sound/items/jaws_pry.ogg' force = 15 toolspeed = 0.25 diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index 4da7cc6aebd..f79b6be0aeb 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -208,7 +208,7 @@ prob(1);/obj/item/clothing/suit/storage/vest/heavy/merc, prob(1);/obj/item/weapon/beartrap, prob(1);/obj/item/weapon/handcuffs, - prob(1);/obj/item/weapon/legcuffs, + prob(1);/obj/item/weapon/handcuffs/legcuffs, prob(2);/obj/item/weapon/reagent_containers/syringe/drugs, prob(1);/obj/item/weapon/reagent_containers/syringe/steroid) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 25966efadb9..0bf155dc49d 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -6,9 +6,13 @@ name = "catwalk" desc = "Cats really don't like these things." density = 0 + var/health = 100 + var/maxhealth = 100 anchored = 1.0 /obj/structure/catwalk/initialize() + for(var/obj/structure/catwalk/O in range(1)) + O.update_icon() for(var/obj/structure/catwalk/C in get_turf(src)) if(C != src) warning("Duplicate [type] in [loc] ([x], [y], [z])") @@ -18,6 +22,7 @@ /obj/structure/catwalk/Destroy() var/turf/location = loc . = ..() + location.alpha = initial(location.alpha) for(var/obj/structure/catwalk/L in orange(location, 1)) L.update_icon() @@ -55,6 +60,8 @@ qdel(src) if(2.0) qdel(src) + if(3.0) + qdel(src) return /obj/structure/catwalk/attackby(obj/item/C as obj, mob/user as mob) @@ -67,6 +74,14 @@ new /obj/item/stack/rods(src.loc) new /obj/structure/lattice(src.loc) qdel(src) + if(istype(C, /obj/item/weapon/screwdriver)) + if(health < maxhealth) + to_chat(user, "You begin repairing \the [src.name] with \the [C.name].") + if(do_after(user, 20, src)) + health = maxhealth + else + take_damage(C.force) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return ..() /obj/structure/catwalk/Crossed() @@ -79,4 +94,12 @@ return 1 if(target && target.z < src.z) return 0 - return 1 \ No newline at end of file + return 1 + +/obj/structure/catwalk/proc/take_damage(amount) + health -= amount + if(health <= 0) + visible_message("\The [src] breaks down!") + playsound(loc, 'sound/effects/grillehit.ogg', 50, 1) + new /obj/item/stack/rods(get_turf(src)) + Destroy() \ No newline at end of file diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index fc2807fedeb..98bae005f8d 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -60,7 +60,7 @@ if(health <= 0) visible_message("\The [src] breaks down!") playsound(loc, 'sound/effects/grillehit.ogg', 50, 1) - new /obj/item/stack/rods(get_turf(usr)) + new /obj/item/stack/rods(get_turf(src)) qdel(src) /obj/structure/railing/proc/NeighborsCheck(var/UpdateNeighbors = 1) @@ -134,6 +134,9 @@ if(usr.incapacitated()) return 0 + if (!can_touch(usr) || ismouse(usr)) + return + if(anchored) to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") return 0 @@ -150,6 +153,9 @@ if(usr.incapacitated()) return 0 + if (!can_touch(usr) || ismouse(usr)) + return + if(anchored) to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") return 0 @@ -166,6 +172,9 @@ if(usr.incapacitated()) return 0 + if (!can_touch(usr) || ismouse(usr)) + return + if(anchored) to_chat(usr, "It is fastened to the floor therefore you can't flip it!") return 0 @@ -249,6 +258,7 @@ else playsound(loc, 'sound/effects/grillehit.ogg', 50, 1) take_damage(W.force) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) return ..() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 90b754421b8..0228b1d9430 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -25,7 +25,7 @@ if(cistern && !open) if(!contents.len) - user << "The cistern is empty." + to_chat(user, "The cistern is empty.") return else var/obj/item/I = pick(contents) @@ -33,7 +33,7 @@ user.put_in_hands(I) else I.loc = get_turf(src) - user << "You find \an [I] in the cistern." + to_chat(user, "You find \an [I] in the cistern.") w_items -= I.w_class return @@ -45,7 +45,7 @@ /obj/structure/toilet/attackby(obj/item/I as obj, mob/living/user as mob) if(istype(I, /obj/item/weapon/crowbar)) - user << "You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]." + to_chat(user, "You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"].") playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, 1) if(do_after(user, 30)) user.visible_message("[user] [cistern ? "replaces the lid on the cistern" : "lifts the lid off the cistern"]!", "You [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]!", "You hear grinding porcelain.") @@ -62,7 +62,7 @@ if(G.state>1) if(!GM.loc == get_turf(src)) - user << "[GM.name] needs to be on the toilet." + to_chat(user, "[GM.name] needs to be on the toilet.") return if(open && !swirlie) user.visible_message("[user] starts to give [GM.name] a swirlie!", "You start to give [GM.name] a swirlie!") @@ -76,19 +76,19 @@ user.visible_message("[user] slams [GM.name] into the [src]!", "You slam [GM.name] into the [src]!") GM.adjustBruteLoss(5) else - user << "You need a tighter grip." + to_chat(user, "You need a tighter grip.") if(cistern && !istype(user,/mob/living/silicon/robot)) //STOP PUTTING YOUR MODULES IN THE TOILET. if(I.w_class > 3) - user << "\The [I] does not fit." + to_chat(user, "\The [I] does not fit.") return if(w_items + I.w_class > 5) - user << "The cistern is full." + to_chat(user, "The cistern is full.") return user.drop_item() I.loc = src w_items += I.w_class - user << "You carefully place \the [I] into the cistern." + to_chat(user, "You carefully place \the [I] into the cistern.") return @@ -108,12 +108,12 @@ var/mob/living/GM = G.affecting if(G.state>1) if(!GM.loc == get_turf(src)) - user << "[GM.name] needs to be on the urinal." + to_chat(user, "[GM.name] needs to be on the urinal.") return user.visible_message("[user] slams [GM.name] into the [src]!", "You slam [GM.name] into the [src]!") GM.adjustBruteLoss(8) else - user << "You need a tighter grip." + to_chat(user, "You need a tighter grip.") @@ -158,10 +158,10 @@ /obj/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) if(I.type == /obj/item/device/analyzer) - user << "The water temperature seems to be [watertemp]." + to_chat(user, "The water temperature seems to be [watertemp].") if(istype(I, /obj/item/weapon/wrench)) var/newtemp = input(user, "What setting would you like to set the temperature valve to?", "Water Temperature Valve") in temperature_settings - user << "You begin to adjust the temperature valve with \the [I]." + to_chat(user, "You begin to adjust the temperature valve with \the [I].") playsound(src.loc, I.usesound, 50, 1) if(do_after(user, 50 * I.toolspeed)) watertemp = newtemp @@ -321,9 +321,9 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M if(temperature >= H.species.heat_level_1) - H << "The water is searing hot!" + to_chat(H, "The water is searing hot!") else if(temperature <= H.species.cold_level_1) - H << "The water is freezing cold!" + to_chat(H, "The water is freezing cold!") /obj/item/weapon/bikehorn/rubberducky name = "rubber ducky" @@ -346,7 +346,7 @@ if(!usr.Adjacent(src)) return ..() if(!thing.reagents || thing.reagents.total_volume == 0) - usr << "\The [thing] is empty." + to_chat(usr, "\The [thing] is empty.") return // Clear the vessel. visible_message("\The [usr] tips the contents of \the [thing] into \the [src].") @@ -360,7 +360,7 @@ if (H.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - user << "You try to move your [temp.name], but cannot!" + to_chat(user, "You try to move your [temp.name], but cannot!") return if(isrobot(user) || isAI(user)) @@ -370,10 +370,10 @@ return if(busy) - user << "Someone's already washing here." + to_chat(user, "Someone's already washing here.") return - usr << "You start washing your hands." + to_chat(usr, "You start washing your hands.") busy = 1 sleep(40) @@ -389,7 +389,7 @@ /obj/structure/sink/attackby(obj/item/O as obj, mob/user as mob) if(busy) - user << "Someone's already washing here." + to_chat(user, "Someone's already washing here.") return var/obj/item/weapon/reagent_containers/RG = O @@ -417,7 +417,7 @@ return 1 else if(istype(O, /obj/item/weapon/mop)) O.reagents.add_reagent("water", 5) - user << "You wet \the [O] in \the [src]." + to_chat(user, "You wet \the [O] in \the [src].") playsound(loc, 'sound/effects/slosh.ogg', 25, 1) return @@ -427,7 +427,7 @@ var/obj/item/I = O if(!I || !istype(I,/obj/item)) return - usr << "You start washing \the [I]." + to_chat(usr, "You start washing \the [I].") busy = 1 sleep(40) diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm index 44446af94e7..ef36121b12c 100644 --- a/code/game/turfs/simulated/floor_attackby.dm +++ b/code/game/turfs/simulated/floor_attackby.dm @@ -6,13 +6,13 @@ if(flooring) if(istype(C, /obj/item/weapon/crowbar)) if(broken || burnt) - user << "You remove the broken [flooring.descriptor]." + to_chat(user, "You remove the broken [flooring.descriptor].") make_plating() else if(flooring.flags & TURF_IS_FRAGILE) - user << "You forcefully pry off the [flooring.descriptor], destroying them in the process." + to_chat(user, "You forcefully pry off the [flooring.descriptor], destroying them in the process.") make_plating() else if(flooring.flags & TURF_REMOVE_CROWBAR) - user << "You lever off the [flooring.descriptor]." + to_chat(user, "You lever off the [flooring.descriptor].") make_plating(1) else return @@ -21,35 +21,35 @@ else if(istype(C, /obj/item/weapon/screwdriver) && (flooring.flags & TURF_REMOVE_SCREWDRIVER)) if(broken || burnt) return - user << "You unscrew and remove the [flooring.descriptor]." + to_chat(user, "You unscrew and remove the [flooring.descriptor].") make_plating(1) playsound(src, C.usesound, 80, 1) return else if(istype(C, /obj/item/weapon/wrench) && (flooring.flags & TURF_REMOVE_WRENCH)) - user << "You unwrench and remove the [flooring.descriptor]." + to_chat(user, "You unwrench and remove the [flooring.descriptor].") make_plating(1) playsound(src, C.usesound, 80, 1) return else if(istype(C, /obj/item/weapon/shovel) && (flooring.flags & TURF_REMOVE_SHOVEL)) - user << "You shovel off the [flooring.descriptor]." + to_chat(user, "You shovel off the [flooring.descriptor].") make_plating(1) playsound(src, 'sound/items/Deconstruct.ogg', 80, 1) return else if(istype(C, /obj/item/stack/cable_coil)) - user << "You must remove the [flooring.descriptor] first." + to_chat(user, "You must remove the [flooring.descriptor] first.") return else if(istype(C, /obj/item/stack/cable_coil)) if(broken || burnt) - user << "This section is too damaged to support anything. Use a welder to fix the damage." + to_chat(user, "This section is too damaged to support anything. Use a welder to fix the damage.") return var/obj/item/stack/cable_coil/coil = C coil.turf_place(src, user) return else if(istype(C, /obj/item/stack)) if(broken || burnt) - user << "This section is too damaged to support anything. Use a welder to fix the damage." + to_chat(user, "This section is too damaged to support anything. Use a welder to fix the damage.") return var/obj/item/stack/S = C var/decl/flooring/use_flooring @@ -64,7 +64,7 @@ return // Do we have enough? if(use_flooring.build_cost && S.amount < use_flooring.build_cost) - user << "You require at least [use_flooring.build_cost] [S.name] to complete the [use_flooring.descriptor]." + to_chat(user, "You require at least [use_flooring.build_cost] [S.name] to complete the [use_flooring.descriptor].") return // Stay still and focus... if(use_flooring.build_time && !do_after(user, use_flooring.build_time)) @@ -81,10 +81,10 @@ if(welder.isOn() && (is_plating())) if(broken || burnt) if(welder.remove_fuel(0,user)) - user << "You fix some dents on the broken plating." + to_chat(user, "You fix some dents on the broken plating.") playsound(src, welder.usesound, 80, 1) icon_state = "plating" burnt = null broken = null else - user << "You need more welding fuel to complete this task." \ No newline at end of file + to_chat(user, "You need more welding fuel to complete this task.") \ No newline at end of file diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm index fd2e3ed4ca8..0f60c01e053 100644 --- a/code/modules/busy_space/organizations.dm +++ b/code/modules/busy_space/organizations.dm @@ -221,6 +221,7 @@ "Never Talk To Strangers", "Sacrificial Victim", "Unwitting Accomplice", + "Witting Accomplice", "Bad For Business", "Just Testing", "Size Isn't Everything", @@ -256,7 +257,35 @@ "Callsign", "Three Ships in a Trenchcoat", "Not Wearing Pants", - "Ridiculous Naming Convention" + "Ridiculous Naming Convention", + "God Dammit Morpheus", + "It Seemed Like a Good Idea", + "Legs All the Way Up", + "Purchase Necessary", + "Some Assembly Required", + "Buy One Get None Free", + "BRB", + "SHIP NAME HERE", + "Questionable Ethics", + "Accept Most Substitutes", + "I Blame the Government", + "Garbled Gibberish", + "Thinking Emoji", + "Is This Thing On?", + "Make My Day", + "No Vox Here", + "Savings and Values", + "Secret Name", + "Can't Find My Keys", + "Look Over There!", + "Made You Look!", + "Take Nothing Seriously", + "It Comes In Lime, Too", + "Loot Me", + "Nothing To Declare", + "Sneaking Suspicion", + "Bass Ackwards", + "Good Things Come to Those Who Freight" ) diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index e3d4ad4cc56..cda6411b1c8 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -67,8 +67,8 @@ item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") /obj/item/clothing/head/beret/sec/navy/hos - name = "officer beret" - desc = "A navy blue beret with a head of security's rank emblem. For officers that are more inclined towards style than safety." + name = "Head of Security beret" + desc = "A navy blue beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." icon_state = "beret_navy_hos" item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") @@ -85,8 +85,8 @@ item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") /obj/item/clothing/head/beret/sec/corporate/hos - name = "officer beret" - desc = "A corporate black beret with a head of security's rank emblem. For officers that are more inclined towards style than safety." + name = "Head of Security beret" + desc = "A corporate black beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." icon_state = "beret_corporate_hos" item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") @@ -188,4 +188,4 @@ /obj/item/clothing/head/surgery/navyblue desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is navy blue." icon_state = "surgcap_navyblue" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") \ No newline at end of file + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") diff --git a/code/modules/examine/descriptions/atmospherics.dm b/code/modules/examine/descriptions/atmospherics.dm index e01600aa6d6..ad17f989f75 100644 --- a/code/modules/examine/descriptions/atmospherics.dm +++ b/code/modules/examine/descriptions/atmospherics.dm @@ -1,6 +1,6 @@ /obj/machinery/atmospherics/pipe description_info = "This pipe, and all other pipes, can be connected or disconnected by a wrench. The internal pressure of the pipe must \ - be below 300 kPa to do this. More pipes can be obtained from the pipe dispenser." + be less than 200 kPa above the ambient pressure to do this. More pipes can be obtained from the pipe dispenser." /obj/machinery/atmospherics/pipe/New() //This is needed or else 20+ lines of copypasta to dance around inheritence. ..() diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm index 47629abda10..25a80c2a28d 100644 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -150,4 +150,4 @@ flags = OPENCONTAINER | NOREACT complexity = 8 spawn_flags = IC_SPAWN_RESEARCH - origin_tech = list(TECH_MATERIALS = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) \ No newline at end of file + origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2) \ No newline at end of file diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 690eba93264..e02f9762810 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -125,7 +125,7 @@ if (force) total_lighting_overlays-- global.lighting_update_overlays -= src - global.lighting_update_overlays_old -= src + LAZYREMOVE(SSlighting.currentrun, src) var/turf/T = loc if(istype(T)) diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index 981de658649..b3e3cf7aa22 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -1,6 +1,14 @@ +// Create lighting overlays on all turfs with dynamic lighting in areas with dynamic lighting. /proc/create_all_lighting_overlays() - for(var/zlevel = 1 to world.maxz) - create_lighting_overlays_zlevel(zlevel) + for(var/area/A in world) + if(!A.dynamic_lighting) + continue + for(var/turf/T in A) + if(!T.dynamic_lighting) + continue + new /atom/movable/lighting_overlay(T, TRUE) + CHECK_TICK + CHECK_TICK /proc/create_lighting_overlays_zlevel(var/zlevel) ASSERT(zlevel) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index d3bd624f9dc..d9312966d66 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -16,6 +16,30 @@ var/mob/living/carbon/brain/brainmob = null//The current occupant. var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. var/obj/mecha = null//This does not appear to be used outside of reference in mecha.dm. + var/obj/item/device/radio/headset/mmi_radio/radio = null//Let's give it a radio. + +/obj/item/device/mmi/New() + radio = new(src)//Spawns a radio inside the MMI. + +/obj/item/device/mmi/verb/toggle_radio() + set name = "Toggle Brain Radio" + set desc = "Enables or disables the integrated brain radio, which is only usable outside of a body." + set category = "Object" + set src in usr + set popup_menu = 1 + if(!usr.canmove || usr.stat || usr.restrained()) + return 0 + + if (radio.radio_enabled == 1) + radio.radio_enabled = 0 + to_chat (usr, "You have disabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been disabled.") + else if (radio.radio_enabled == 0) + radio.radio_enabled = 1 + to_chat (usr, "You have enabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been enabled.") + else + to_chat (usr, "You were unable to toggle the [src]'s radio.") /obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob) if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO @@ -110,48 +134,15 @@ if(isrobot(loc)) var/mob/living/silicon/robot/borg = loc borg.mmi = null + qdel_null(radio) qdel_null(brainmob) return ..() /obj/item/device/mmi/radio_enabled name = "radio-enabled man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio." + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio. Wait, don't they all?" origin_tech = list(TECH_BIO = 4) - var/obj/item/device/radio/radio = null//Let's give it a radio. - - New() - ..() - radio = new(src)//Spawns a radio inside the MMI. - radio.broadcasting = 1//So it's broadcasting from the start. - - verb//Allows the brain to toggle the radio functions. - Toggle_Broadcasting() - set name = "Toggle Broadcasting" - set desc = "Toggle broadcasting channel on or off." - set category = "MMI" - set src = usr.loc//In user location, or in MMI in this case. - set popup_menu = 0//Will not appear when right clicking. - - if(brainmob.stat)//Only the brainmob will trigger these so no further check is necessary. - brainmob << "Can't do that while incapacitated or dead." - - radio.broadcasting = radio.broadcasting==1 ? 0 : 1 - brainmob << "Radio is [radio.broadcasting==1 ? "now" : "no longer"] broadcasting." - - Toggle_Listening() - set name = "Toggle Listening" - set desc = "Toggle listening channel on or off." - set category = "MMI" - set src = usr.loc - set popup_menu = 0 - - if(brainmob.stat) - brainmob << "Can't do that while incapacitated or dead." - - radio.listening = radio.listening==1 ? 0 : 1 - brainmob << "Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast." - /obj/item/device/mmi/emp_act(severity) if(!brainmob) return @@ -177,13 +168,14 @@ /obj/item/device/mmi/digital/New() src.brainmob = new(src) - src.brainmob.add_language("Robot Talk") +// src.brainmob.add_language("Robot Talk")//No binary without a binary communication device src.brainmob.add_language(LANGUAGE_GALCOM) src.brainmob.add_language(LANGUAGE_EAL) src.brainmob.loc = src src.brainmob.container = src src.brainmob.stat = 0 src.brainmob.silent = 0 + radio = new(src) dead_mob_list -= src.brainmob /obj/item/device/mmi/digital/attackby(var/obj/item/O as obj, var/mob/user as mob) @@ -271,7 +263,7 @@ src.brainmob << "You are a [src], brought into existence on [station_name()]." src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI." src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." - src.brainmob << "Use say #b to speak to other artificial intelligences." +// src.brainmob << "Use say #b to speak to other artificial intelligences." src.brainmob.mind.assigned_role = "Synthetic Brain" var/turf/T = get_turf_or_move(src.loc) diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index aad80373d81..efe2abc4971 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -60,5 +60,5 @@ /mob/living/carbon/brain/isSynthetic() return istype(loc, /obj/item/device/mmi) -/mob/living/carbon/brain/binarycheck() - return isSynthetic() +///mob/living/carbon/brain/binarycheck()//No binary without a binary communication device +// return isSynthetic() diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index d933dc5d47d..8797cd596ac 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -22,7 +22,7 @@ verb="asks" if(prob(emp_damage*4)) - if(prob(10))//10% chane to drop the message entirely + if(prob(10))//10% chance to drop the message entirely return else message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher @@ -31,8 +31,16 @@ speaking.broadcast(src,trim(message)) return - if(istype(container, /obj/item/device/mmi/radio_enabled)) - var/obj/item/device/mmi/radio_enabled/R = container - if(R.radio) - spawn(0) R.radio.hear_talk(src, sanitize(message), verb, speaking) ..(trim(message), speaking, verb) + +/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) + ..() + if(message_mode) + var/obj/item/device/mmi/R = container + if (R.radio && R.radio.radio_enabled) + if(message_mode == "general") + message_mode = null + return R.radio.talk_into(src,message,message_mode,verb,speaking) + else + src << "Your radio is disabled." + return 0 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index fb4c3f50f95..3fb8fe8ac9d 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -220,15 +220,15 @@ M.visible_message("[M] tries to pat out [src]'s flames!", "You try to pat out [src]'s flames! Hot!") if(do_mob(M, src, 15)) - src.fire_stacks -= 0.5 + src.adjust_fire_stacks(-0.5) if (prob(10) && (M.fire_stacks <= 0)) - M.fire_stacks += 1 + M.adjust_fire_stacks(1) M.IgniteMob() if (M.on_fire) M.visible_message("The fire spreads from [src] to [M]!", "The fire spreads to you as well!") else - src.fire_stacks -= 0.5 //Less effective than stop, drop, and roll - also accounting for the fact that it takes half as long. + src.adjust_fire_stacks(-0.5) //Less effective than stop, drop, and roll - also accounting for the fact that it takes half as long. if (src.fire_stacks <= 0) M.visible_message("[M] successfully pats out [src]'s flames.", "You successfully pat out [src]'s flames.") @@ -264,8 +264,8 @@ M.visible_message("[M] hugs [src] to make [t_him] feel better!", \ "You hug [src] to make [t_him] feel better!") if(M.fire_stacks >= (src.fire_stacks + 3)) - src.fire_stacks += 1 - M.fire_stacks -= 1 + src.adjust_fire_stacks(1) + M.adjust_fire_stacks(-1) if(M.on_fire) src.IgniteMob() AdjustParalysis(-3) diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index e0dd9998cc6..34a864bf27a 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -10,7 +10,7 @@ delete_inventory() /mob/living/carbon/human/skrell/New(var/new_loc) - h_style = "Skrell Male Tentacles" + h_style = "Skrell Short Tentacles" ..(new_loc, "Skrell") /mob/living/carbon/human/tajaran/New(var/new_loc) diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm index ebb16683a84..86597afba6b 100644 --- a/code/modules/mob/living/carbon/resist.dm +++ b/code/modules/mob/living/carbon/resist.dm @@ -2,7 +2,7 @@ //drop && roll if(on_fire && !buckled) - fire_stacks -= 1.2 + adjust_fire_stacks(-1.2) Weaken(3) spin(32,2) visible_message( @@ -66,38 +66,43 @@ drop_from_inventory(handcuffed) /mob/living/carbon/proc/escape_legcuffs() - if(!canClick()) - return + //if(!(last_special <= world.time)) return + //This line represent a significant buff to grabs... + // We don't have to check the click cooldown because /mob/living/verb/resist() has done it for us, we can simply set the delay setClickCooldown(100) if(can_break_cuffs()) //Don't want to do a lot of logic gating here. break_legcuffs() return - var/obj/item/weapon/legcuffs/HC = legcuffed + var/obj/item/weapon/handcuffs/legcuffs/LC = legcuffed - //A default in case you are somehow legcuffed with something that isn't an obj/item/weapon/legcuffs type + //A default in case you are somehow legcuffed with something that isn't an obj/item/weapon/handcuffs/legcuffs type var/breakouttime = 1200 var/displaytime = 2 //Minutes to display in the "this will take X minutes." - //If you are legcuffed with actual legcuffs... Well what do I know, maybe someone will want to legcuff you with toilet paper in the future... - if(istype(HC)) - breakouttime = HC.breakouttime + //If you are legcuffed with actual legcuffs... Well what do I know, maybe someone will want to handcuff you with toilet paper in the future... + if(istype(LC)) + breakouttime = LC.breakouttime displaytime = breakouttime / 600 //Minutes + var/mob/living/carbon/human/H = src + if(istype(H) && H.shoes && istype(H.shoes,/obj/item/clothing/shoes/magboots/rig)) + breakouttime /= 2 + displaytime /= 2 + visible_message( - "[usr] attempts to remove \the [HC]!", - "You attempt to remove \the [HC]. (This will take around [displaytime] minutes and you need to stand still)" + "\The [src] attempts to remove \the [LC]!", + "You attempt to remove \the [LC]. (This will take around [displaytime] minutes and you need to stand still)" ) - if(do_after(src, breakouttime, incapacitation_flags = INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED)) - if(!legcuffed || buckled) + if(do_after(src, breakouttime, incapacitation_flags = INCAPACITATION_DISABLED & INCAPACITATION_KNOCKDOWN)) + if(!legcuffed) return visible_message( - "[src] manages to remove \the [legcuffed]!", + "\The [src] manages to remove \the [legcuffed]!", "You successfully remove \the [legcuffed]." ) - drop_from_inventory(legcuffed) legcuffed = null update_inv_legcuffed() diff --git a/code/modules/mob/living/simple_animal/aliens/hivebot.dm b/code/modules/mob/living/simple_animal/aliens/hivebot.dm index d3193adfb67..02f48d13fa2 100644 --- a/code/modules/mob/living/simple_animal/aliens/hivebot.dm +++ b/code/modules/mob/living/simple_animal/aliens/hivebot.dm @@ -1,6 +1,11 @@ +// Hivebots are tuned towards how many default lasers are needed to kill them. +// As such, if laser damage is ever changed, you should change this define. +#define LASERS_TO_KILL *40 + +// Default hivebot is melee, and a bit more meaty, so it can meatshield for their ranged friends. /mob/living/simple_animal/hostile/hivebot - name = "Hivebot" - desc = "A small robot" + name = "hivebot" + desc = "A robot. It appears to be somewhat reslient, but lacking a true weapon." icon = 'icons/mob/hivebot.dmi' icon_state = "basic" icon_living = "basic" @@ -8,16 +13,16 @@ faction = "hivebot" intelligence_level = SA_ROBOTIC - maxHealth = 15 - health = 15 + maxHealth = 3 LASERS_TO_KILL + health = 3 LASERS_TO_KILL speed = 4 - melee_damage_lower = 2 - melee_damage_upper = 3 + melee_damage_lower = 15 + melee_damage_upper = 15 attacktext = "clawed" projectilesound = 'sound/weapons/Gunshot.ogg' - projectiletype = /obj/item/projectile/hivebotbullet + projectiletype = /obj/item/projectile/bullet/hivebot min_oxy = 0 max_oxy = 0 @@ -29,19 +34,102 @@ max_n2 = 0 minbodytemp = 0 + cooperative = TRUE + firing_lines = TRUE + investigates = TRUE + + speak_chance = 1 + speak = list( + "Resuming task: Protect area.", + "No threats found.", + "Error: No targets found." + ) + emote_hear = list("humms ominously", "whirrs softly", "grinds a gear") + emote_see = list("looks around the area", "turns from side to side") + say_understood = list("Affirmative.", "Positive") + say_cannot = list("Denied.", "Negative") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + +// Subtypes. + +// Melee like the base type, but more fragile. +/mob/living/simple_animal/hostile/hivebot/swarm + name = "swarm hivebot" + desc = "A robot. It looks fragile and weak" + maxHealth = 1 LASERS_TO_KILL + health = 1 LASERS_TO_KILL + melee_damage_lower = 3 + melee_damage_upper = 3 + +// This one has a semi-weak ranged attack. /mob/living/simple_animal/hostile/hivebot/range - name = "Hivebot" - desc = "A smallish robot, this one is armed!" + name = "ranged hivebot" + desc = "A robot. It has a simple ballistic weapon." ranged = 1 + maxHealth = 2 LASERS_TO_KILL + health = 2 LASERS_TO_KILL +// This one shoots a burst of three, and is considerably more dangerous. /mob/living/simple_animal/hostile/hivebot/range/rapid + name = "rapid hivebot" + desc = "A robot. It has a fast firing ballistic rifle." + icon_living = "strong" rapid = 1 + maxHealth = 2 LASERS_TO_KILL + health = 2 LASERS_TO_KILL -/mob/living/simple_animal/hostile/hivebot/strong - name = "Strong Hivebot" - desc = "A robot, this one is armed and looks tough!" - health = 80 - ranged = 1 +// Shoots EMPs, to screw over other robots. +/mob/living/simple_animal/hostile/hivebot/range/ion + name = "engineering hivebot" + desc = "A robot. It has a tool which emits focused electromagnetic pulses, which are deadly to other synthetic adverseries." + projectiletype = /obj/item/projectile/ion + projectilesound = 'sound/weapons/Laser.ogg' + icon_living = "engi" + ranged = TRUE + maxHealth = 2 LASERS_TO_KILL + health = 2 LASERS_TO_KILL + +// Shoots deadly lasers. +/mob/living/simple_animal/hostile/hivebot/range/laser + name = "laser hivebot" + desc = "A robot. It has an energy weapon." + projectiletype = /obj/item/projectile/beam/blue + projectilesound = 'sound/weapons/Laser.ogg' + maxHealth = 2 LASERS_TO_KILL + health = 2 LASERS_TO_KILL + +// Beefy and ranged. +/mob/living/simple_animal/hostile/hivebot/range/strong + name = "strong hivebot" + desc = "A robot. This one has reinforced plating, and looks tougher." + icon_living = "strong" + maxHealth = 4 LASERS_TO_KILL + health = 4 LASERS_TO_KILL + melee_damage_lower = 15 + melee_damage_upper = 15 + +// Also beefy, but tries to stay at their 'home', ideal for base defense. +/mob/living/simple_animal/hostile/hivebot/range/guard + name = "guard hivebot" + desc = "A robot. It seems to be guarding something." + returns_home = TRUE + maxHealth = 4 LASERS_TO_KILL + health = 4 LASERS_TO_KILL + +// This one is intended for players to use. Well rounded and can make other hivebots follow them with verbs. +/mob/living/simple_animal/hostile/hivebot/range/player + name = "commander hivebot" + desc = "A robot. This one seems to direct the others, and it has a laser weapon." + icon_living = "commander" + maxHealth = 5 LASERS_TO_KILL + health = 5 LASERS_TO_KILL + projectiletype = /obj/item/projectile/beam/blue + projectilesound = 'sound/weapons/Laser.ogg' + melee_damage_lower = 15 // Needed to force open airlocks. + melee_damage_upper = 15 + +// Procs. /mob/living/simple_animal/hostile/hivebot/death() ..() @@ -52,6 +140,42 @@ s.start() qdel(src) +/mob/living/simple_animal/hostile/hivebot/speech_bubble_appearance() + return "synthetic_evil" + +/mob/living/simple_animal/hostile/hivebot/verb/command_follow() + set name = "Command - Follow" + set category = "Hivebot" + set desc = "This will ask other hivebots to follow you." + + say("Delegating new task: Follow.") + + for(var/mob/living/simple_animal/hostile/hivebot/buddy in hearers(src)) + if(buddy.faction != faction) + continue + if(buddy == src) + continue + buddy.set_follow(src) + buddy.FollowTarget() + spawn(rand(5, 10)) + buddy.say( pick(buddy.say_understood) ) + +/mob/living/simple_animal/hostile/hivebot/verb/command_stop() + set name = "Command - Stop Following" + set category = "Hivebot" + set desc = "This will ask other hivebots to cease following you." + + say("Delegating new task: Stop following.") + + for(var/mob/living/simple_animal/hostile/hivebot/buddy in hearers(src)) + if(buddy.faction != faction) + continue + if(buddy == src) + continue + buddy.LoseFollow() + spawn(rand(5, 10)) + buddy.say( pick(buddy.say_understood) ) + /mob/living/simple_animal/hostile/hivebot/tele//this still needs work name = "Beacon" desc = "Some odd beacon thing" @@ -107,6 +231,6 @@ if(prob(2))//Might be a bit low, will mess with it likely warpbots() -/obj/item/projectile/hivebotbullet +/obj/item/projectile/bullet/hivebot damage = 10 damage_type = BRUTE diff --git a/code/modules/mob/living/simple_animal/animals/giant_spider.dm b/code/modules/mob/living/simple_animal/animals/giant_spider.dm index 8d7a2ccb329..a087d4429a0 100644 --- a/code/modules/mob/living/simple_animal/animals/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/animals/giant_spider.dm @@ -43,6 +43,17 @@ var/poison_per_bite = 5 var/poison_chance = 10 var/poison_type = "spidertoxin" + var/image/eye_layer = null + +/mob/living/simple_animal/hostile/giant_spider/proc/add_eyes() + if(!eye_layer) + var/overlay_layer = LIGHTING_LAYER+0.1 + eye_layer = image(icon, "[icon_state]-eyes", overlay_layer) + + overlays += eye_layer + +/mob/living/simple_animal/hostile/giant_spider/proc/remove_eyes() + overlays -= eye_layer //nursemaids - these create webs and eggs /mob/living/simple_animal/hostile/giant_spider/nurse @@ -61,6 +72,7 @@ var/fed = 0 var/atom/cocoon_target + var/egg_inject_chance = 5 //hunters have the most poison and move the fastest, so they can find prey /mob/living/simple_animal/hostile/giant_spider/hunter @@ -96,33 +108,43 @@ /mob/living/simple_animal/hostile/giant_spider/New(var/location, var/atom/parent) get_light_and_color(parent) + add_eyes() ..() -/mob/living/simple_animal/hostile/giant_spider/PunchTarget() - . = ..() - if(isliving(.)) - var/mob/living/L = . - if(L.reagents) - L.reagents.add_reagent(poison_type, poison_per_bite) - if(prob(poison_chance)) - L << "You feel a tiny prick." - L.reagents.add_reagent(poison_type, poison_per_bite) +/mob/living/simple_animal/hostile/giant_spider/death() + remove_eyes() + ..() -/mob/living/simple_animal/hostile/giant_spider/nurse/PunchTarget() +/mob/living/simple_animal/hostile/giant_spider/DoPunch(var/atom/A) . = ..() - if(ishuman(.)) - var/mob/living/carbon/human/H = . - if(prob(5)) - var/obj/item/organ/external/O = pick(H.organs) - if(!(O.robotic >= ORGAN_ROBOT)) - var/eggcount - for(var/obj/I in O.implants) - if(istype(I, /obj/effect/spider/eggcluster)) - eggcount ++ - if(!eggcount) - var/eggs = new /obj/effect/spider/eggcluster/small(O, src) - O.implants += eggs - H << "The [src] injects something into your [O.name]!" + if(.) // If we succeeded in hitting. + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + L.reagents.add_reagent(poison_type, poison_per_bite) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) + +/mob/living/simple_animal/hostile/giant_spider/nurse/DoPunch(var/atom/A) + . = ..() + if(.) // If we succeeded in hitting. + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(prob(egg_inject_chance)) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(H.can_inject(src, null, target_zone)) + var/obj/item/organ/external/O = H.get_organ(target_zone) + var/eggcount + for(var/obj/I in O.implants) + if(istype(I, /obj/effect/spider/eggcluster)) + eggcount ++ + if(!eggcount) + var/eggs = new /obj/effect/spider/eggcluster/small(O, src) + O.implants += eggs + to_chat(H, "\The [src] injects something into your [O.name]!") /mob/living/simple_animal/hostile/giant_spider/handle_stance() . = ..() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 1cfbc84bb40..0697f81714f 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1230,14 +1230,22 @@ // This is the actual act of 'punching'. Override for special behaviour. /mob/living/simple_animal/proc/DoPunch(var/atom/A) if(!Adjacent(target_mob)) // They could've moved in the meantime. - return + return FALSE + var/damage_to_do = rand(melee_damage_lower, melee_damage_upper) for(var/datum/modifier/M in modifiers) if(!isnull(M.outgoing_melee_damage_percent)) damage_to_do *= M.outgoing_melee_damage_percent + // SA attacks can be blocked with shields. + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack")) + return FALSE + A.attack_generic(src, damage_to_do, attacktext) + return TRUE //The actual top-level ranged attack proc /mob/living/simple_animal/proc/ShootTarget() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 464baaa142d..d1a336dbb6e 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -501,6 +501,10 @@ if(!dense_object && (locate(/obj/structure/lattice) in oview(1, src))) dense_object++ + if(!dense_object && (locate(/obj/structure/catwalk) in oview(1, src))) + dense_object++ + + //Lastly attempt to locate any dense objects we could push off of //TODO: If we implement objects drifing in space this needs to really push them //Due to a few issues only anchored and dense objects will now work. diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index bc64f3e7647..0e3b1f76955 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -1020,6 +1020,17 @@ icon_state = "teshari_mushroom" species_allowed = list("Teshari") +// Vox things + vox_braid_long + name = "Long Vox braid" + icon_state = "vox_longbraid" + species_allowed = list("Vox") + + vox_braid_short + name = "Short Vox Braid" + icon_state = "vox_shortbraid" + species_allowed = list("Vox") + vox_quills_short name = "Short Vox Quills" icon_state = "vox_shortquills" diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm index e35646ce7fc..d4492d625ce 100644 --- a/code/modules/planet/sif.dm +++ b/code/modules/planet/sif.dm @@ -9,7 +9,7 @@ var/datum/planet/sif/planet_sif = null breathable atmosphere, a magnetic field, weather, and similar gravity to Earth. It is currently the capital planet of Vir. \ Its center of government is the equatorial city and site of first settlement, New Reykjavik." // Ripped straight from the wiki. current_time = new /datum/time/sif() // 32 hour clocks are nice. - expected_z_levels = list(1) // To be changed when real map is finished. +// expected_z_levels = list(1) // To be changed when real map is finished. planetary_wall_type = /turf/unsimulated/wall/planetary/sif /datum/planet/sif/New() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index ebfa5330864..edcc43bb225 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -565,15 +565,19 @@ obj/structure/cable/proc/cableColor(var/colorC) w_class = ITEMSIZE_SMALL /obj/item/stack/cable_coil/examine(mob/user) - if(get_dist(src, user) > 1) - return + var/msg = "" if(get_amount() == 1) - to_chat(user, "A short piece of power cable.") + msg += "A short piece of power cable." else if(get_amount() == 2) - to_chat(user, "A piece of power cable.") + msg += "A piece of power cable." else - to_chat(user, "A coil of power cable. There are [get_amount()] lengths of cable in the coil.") + msg += "A coil of power cable." + + if(get_dist(src, user) <= 1) + msg += " There are [get_amount()] lengths of cable in the coil." + + to_chat(user, msg) /obj/item/stack/cable_coil/verb/make_restraint() @@ -875,4 +879,52 @@ obj/structure/cable/proc/cableColor(var/colorC) /obj/item/stack/cable_coil/random/New() stacktype = /obj/item/stack/cable_coil color = pick(COLOR_RED, COLOR_BLUE, COLOR_LIME, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN, COLOR_SILVER, COLOR_GRAY, COLOR_BLACK, COLOR_MAROON, COLOR_OLIVE, COLOR_LIME, COLOR_TEAL, COLOR_NAVY, COLOR_PURPLE, COLOR_BEIGE, COLOR_BROWN) - ..() \ No newline at end of file + ..() + +//Endless alien cable coil + +/obj/item/stack/cable_coil/alien + name = "alien spool" + icon = 'icons/obj/abductor.dmi' + icon_state = "coil" + amount = MAXCOIL + max_amount = MAXCOIL + color = COLOR_SILVER + desc = "A spool of cable. No matter how hard you try, you can never seem to get to the end." + throwforce = 10 + w_class = ITEMSIZE_SMALL + throw_speed = 2 + throw_range = 5 + matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 20) + flags = CONDUCT + slot_flags = SLOT_BELT + attack_verb = list("whipped", "lashed", "disciplined", "flogged") + stacktype = null + +/obj/item/stack/cable_coil/alien/New(loc, length = MAXCOIL, var/param_color = null) //There has to be a better way to do this. + if(embed_chance == -1) //From /obj/item, don't want to do what the normal cable_coil does + if(sharp) + embed_chance = force/w_class + else + embed_chance = force/(w_class*3) + update_icon() + +/obj/item/stack/cable_coil/alien/update_icon() + icon_state = initial(icon_state) + +/obj/item/stack/cable_coil/alien/use() //It's endless + return + +/obj/item/stack/cable_coil/alien/add() //Still endless + return + +/obj/item/stack/cable_coil/alien/update_wclass() + return + +/obj/item/stack/cable_coil/alien/examine(mob/user) + var/msg = "A spool of cable." + + if(get_dist(src, user) <= 1) + msg += " It doesn't seem to have a beginning, or an end." + + to_chat(user, msg) \ No newline at end of file diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index d3cde870492..2c1699c5f60 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -16,7 +16,7 @@ /obj/item/weapon/cell/process() if(self_recharge) - give(charge_amount / CELLRATE) + give(charge_amount) else return PROCESS_KILL @@ -59,6 +59,7 @@ return 0 var/used = min(charge, amount) charge -= used + update_icon() return used // Checks if the specified amount can be provided. If it can, it removes the amount @@ -78,24 +79,29 @@ if(maxcharge < amount) return 0 var/amount_used = min(maxcharge-charge,amount) charge += amount_used + update_icon() return amount_used /obj/item/weapon/cell/examine(mob/user) - if(get_dist(src, user) > 1) - return + var/msg = desc - if(maxcharge <= 2500) - user << "[desc]\nThe manufacturer's label states this cell has a power rating of [maxcharge], and that you should not swallow it.\nThe charge meter reads [round(src.percent() )]%." - else - user << "This power cell has an exciting chrome finish, as it is an uber-capacity cell type! It has a power rating of [maxcharge]!\nThe charge meter reads [round(src.percent() )]%." + if(get_dist(src, user) <= 1) + msg += " It has a power rating of [maxcharge].\nThe charge meter reads [round(src.percent() )]%." + to_chat(user, msg) +/* + if(maxcharge <= 2500) + to_chat(user, "[desc]\nThe manufacturer's label states this cell has a power rating of [maxcharge], and that you should not swallow it.\nThe charge meter reads [round(src.percent() )]%.") + else + to_chat(user, "This power cell has an exciting chrome finish, as it is an uber-capacity cell type! It has a power rating of [maxcharge]!\nThe charge meter reads [round(src.percent() )]%.") +*/ /obj/item/weapon/cell/attackby(obj/item/W, mob/user) ..() if(istype(W, /obj/item/weapon/reagent_containers/syringe)) var/obj/item/weapon/reagent_containers/syringe/S = W - user << "You inject the solution into the power cell." + to_chat(user, "You inject the solution into the power cell.") if(S.reagents.has_reagent("phoron", 5)) @@ -149,6 +155,8 @@ charge -= charge / severity if (charge < 0) charge = 0 + + update_icon() ..() /obj/item/weapon/cell/ex_act(severity) diff --git a/code/modules/supermatter/setup_supermatter.dm b/code/modules/power/supermatter/setup_supermatter.dm similarity index 100% rename from code/modules/supermatter/setup_supermatter.dm rename to code/modules/power/supermatter/setup_supermatter.dm diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm similarity index 94% rename from code/modules/supermatter/supermatter.dm rename to code/modules/power/supermatter/supermatter.dm index b80d21b64d6..51993daed4c 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -264,15 +264,7 @@ if(eye_shield < 1) l.hallucination = max(0, min(200, l.hallucination + power * config_hallucination_power * sqrt( 1 / max(1,get_dist(l, src)) ) ) ) -/* - //adjusted range so that a power of 170 (pretty high) results in 9 tiles, roughly the distance from the core to the engine monitoring room. - //note that the rads given at the maximum range is a constant 0.2 - as power increases the maximum range merely increases. - for(var/mob/living/l in range(src, round(sqrt(power / 2)))) - var/radius = max(get_dist(l, src), 1) - var/rads = (power / 10) * ( 1 / (radius**2) ) - l.apply_effect(rads, IRRADIATE) -*/ - radiation_repository.radiate(src, power * 1.5) //Better close those shutters! + radiation_repository.radiate(src, max(power * 1.5, 50) ) //Better close those shutters! power -= (power/DECAY_FACTOR)**3 //energy losses due to radiation @@ -335,16 +327,6 @@ ui.set_auto_update(1) -/* -/obj/machinery/power/supermatter/proc/transfer_energy() - for(var/obj/machinery/power/rad_collector/R in rad_collectors) - var/distance = get_dist(R, src) - if(distance <= 15) - //for collectors using standard phoron tanks at 1013 kPa, the actual power generated will be this power*POWER_FACTOR*20*29 = power*POWER_FACTOR*580 - R.receive_pulse(power * POWER_FACTOR * (min(3/distance, 1))**2) - return -*/ - /obj/machinery/power/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 3144f9eb095..c4344c052b8 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -202,8 +202,9 @@ admin_attack_log(firer, target_mob, attacker_message, victim_message, admin_message) else - target_mob.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [target_mob]/[target_mob.ckey] with \a [src]" - msg_admin_attack("UNKNOWN shot [target_mob] ([target_mob.ckey]) with \a [src] (JMP)") + if(target_mob) // Sometimes the target_mob gets gibbed or something. + target_mob.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [target_mob]/[target_mob.ckey] with \a [src]" + msg_admin_attack("UNKNOWN shot [target_mob] ([target_mob.ckey]) with \a [src] (JMP)") //sometimes bullet_act() will want the projectile to continue flying if (result == PROJECTILE_CONTINUE) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index 7e0ee4ffb40..3f660ca1643 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -284,13 +284,21 @@ /datum/reagent/frostoil/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) return - M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, 0) + M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, 215) if(prob(1)) M.emote("shiver") if(istype(M, /mob/living/simple_animal/slime)) M.bodytemperature = max(M.bodytemperature - rand(10,20), 0) holder.remove_reagent("capsaicin", 5) +/datum/reagent/frostoil/cryotoxin //A longer lasting version of frost oil. + name = "Cryotoxin" + id = "cryotoxin" + description = "Lowers the body's internal temperature." + reagent_state = LIQUID + color = "#B31008" + metabolism = REM * 0.5 + /datum/reagent/capsaicin name = "Capsaicin Oil" id = "capsaicin" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index d9e0dad733e..c71a7de362d 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -105,8 +105,8 @@ /datum/reagent/toxin/phoron/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_VOX) - M.adjustOxyLoss(-removed * 9) - return + M.adjustOxyLoss(-100 * removed) //5 oxyloss healed per tick. + return //You're wasting plasma (a semi-limited chemical) to save someone, so it might as well be somewhat strong. ..() /datum/reagent/toxin/phoron/touch_turf(var/turf/simulated/T, var/amount) diff --git a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm index 8d19e493300..874d4cc6765 100644 --- a/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/drinkingglass/drinkingglass.dm @@ -146,3 +146,22 @@ underlays += I else continue side = "right" + +/obj/item/weapon/reagent_containers/food/drinks/glass2/afterattack(var/obj/target, var/mob/user, var/proximity) + if(user.a_intent == I_HURT) //We only want splashing to be done if they are on harm intent. + if(!is_open_container() || !proximity) + return 1 + if(standard_splash_mob(user, target)) + return 1 + if(reagents && reagents.total_volume) //They are on harm intent, aka wanting to spill it. + user << "You splash the solution onto [target]." + reagents.splash(target, reagents.total_volume) + return 1 + else + return + +/obj/item/weapon/reagent_containers/food/drinks/glass2/standard_feed_mob(var/mob/user, var/mob/target) + if(afterattack()) //Check to see if harm intent & splash. + return + else + ..() //If they're splashed, no need to do anything else. \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 3c3e4562b45..7835b56d7dd 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -78,25 +78,27 @@ /obj/item/weapon/reagent_containers/glass/afterattack(var/obj/target, var/mob/user, var/proximity) - if(!is_open_container() || !proximity) - return + if(!is_open_container() || !proximity) //Is the container open & are they next to whatever they're clicking? + return //If not, do nothing. - for(var/type in can_be_placed_into) + for(var/type in can_be_placed_into) //Is it something it can be placed into? if(istype(target, type)) return - if(standard_splash_mob(user, target)) - return - if(standard_dispenser_refill(user, target)) - return - if(standard_pour_into(user, target)) + if(standard_dispenser_refill(user, target)) //Are they clicking a water tank/some dispenser? return - if(reagents && reagents.total_volume) - user << "You splash the solution onto [target]." - reagents.splash(target, reagents.total_volume) + if(standard_pour_into(user, target)) //Pouring into another beaker? return + if(user.a_intent == I_HURT) //Harm intent? + if(standard_splash_mob(user, target)) //If harm intent and can splash a mob, go ahead. + return + if(reagents && reagents.total_volume) //Otherwise? Splash the floor. + user << "You splash the solution onto [target]." + reagents.splash(target, reagents.total_volume) + return + /obj/item/weapon/reagent_containers/glass/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) var/tmp_label = sanitizeSafe(input(user, "Enter a label for [name]", "Label", label_text), MAX_NAME_LEN) diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 5602028771c..a6e6a948501 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -648,14 +648,14 @@ other types of metals and chemistry for reagents). /datum/design/item/weapon/slimebation id = "slimebation" - req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3, TECH_POWER = 3, TECH_COMBAT = 3) + req_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2, TECH_POWER = 3, TECH_COMBAT = 3) materials = list(DEFAULT_WALL_MATERIAL = 5000) build_path = /obj/item/weapon/melee/baton/slime sort_string = "TBAAB" /datum/design/item/weapon/slimetaser id = "slimetaser" - req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4, TECH_POWER = 4, TECH_COMBAT = 4) + req_tech = list(TECH_MATERIAL = 3, TECH_BIO = 3, TECH_POWER = 4, TECH_COMBAT = 4) materials = list(DEFAULT_WALL_MATERIAL = 5000) build_path = /obj/item/weapon/gun/energy/taser/xeno sort_string = "TBAAC" @@ -744,6 +744,16 @@ other types of metals and chemistry for reagents). build_path = /obj/item/device/aicard sort_string = "VACAA" +/datum/design/item/dronebrain + name = "Robotic intelligence circuit" + id = "dronebrain" + req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_DATA = 4) + build_type = PROTOLATHE | PROSFAB + materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500) + build_path = /obj/item/device/mmi/digital/robot + category = "Misc" + sort_string = "VACAC" + /datum/design/item/posibrain name = "Positronic brain" id = "posibrain" @@ -764,16 +774,6 @@ other types of metals and chemistry for reagents). category = "Misc" sort_string = "VACBA" -/datum/design/item/mmi_radio - name = "Radio-enabled man-machine interface" - id = "mmi_radio" - req_tech = list(TECH_DATA = 2, TECH_BIO = 4) - build_type = PROTOLATHE | PROSFAB - materials = list(DEFAULT_WALL_MATERIAL = 1200, "glass" = 500) - build_path = /obj/item/device/mmi/radio_enabled - category = "Misc" - sort_string = "VACBB" - /datum/design/item/beacon name = "Bluespace tracking beacon design" id = "beacon" diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index fac9df67f85..6bf1ea58ef6 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -139,6 +139,11 @@ var/check_delay = 60 //periodically recheck if we need to rebuild a shield use_power = 0 idle_power_usage = 0 + var/global/list/blockedturfs = list( + /turf/space, + /turf/simulated/open, + /turf/simulated/floor/outdoors, + ) /obj/machinery/shieldgen/Destroy() collapse_shields() @@ -169,7 +174,7 @@ /obj/machinery/shieldgen/proc/create_shields() for(var/turf/target_tile in range(2, src)) - if (istype(target_tile,/turf/space) && !(locate(/obj/machinery/shield) in target_tile)) + if (is_type_in_list(target_tile,blockedturfs) && !(locate(/obj/machinery/shield) in target_tile)) if (malfunction && prob(33) || !malfunction) var/obj/machinery/shield/S = new/obj/machinery/shield(target_tile) deployed_shields += S diff --git a/code/modules/shieldgen/shield_gen_external.dm b/code/modules/shieldgen/shield_gen_external.dm index 8086d0b7e3b..2d7dd383d95 100644 --- a/code/modules/shieldgen/shield_gen_external.dm +++ b/code/modules/shieldgen/shield_gen_external.dm @@ -3,7 +3,11 @@ /obj/machinery/shield_gen/external name = "hull shield generator" - + var/global/list/blockedturfs = list( + /turf/space, + /turf/simulated/open, + /turf/simulated/floor/outdoors, + ) /obj/machinery/shield_gen/external/New() ..() @@ -18,7 +22,7 @@ for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++) for (var/y_offset = -field_radius; y_offset <= field_radius; y_offset++) T = locate(gen_turf.x + x_offset, gen_turf.y + y_offset, gen_turf.z) - if (istype(T, /turf/space)) + if (is_type_in_list(T,blockedturfs)) //check neighbors of T if (locate(/turf/simulated/) in orange(1, T)) out += T diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index e0ba3b1a3f7..2bb64345539 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -259,8 +259,7 @@ ..() /obj/machinery/artifact/bullet_act(var/obj/item/projectile/P) - if(istype(P,/obj/item/projectile/bullet) ||\ - istype(P,/obj/item/projectile/hivebotbullet)) + if(istype(P,/obj/item/projectile/bullet)) if(my_effect.trigger == TRIGGER_FORCE) my_effect.ToggleActivate() if(secondary_effect && secondary_effect.trigger == TRIGGER_FORCE && prob(25)) diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm index 3fdc758f84e..a1aba76dab2 100644 --- a/code/modules/xenobio/items/weapons.dm +++ b/code/modules/xenobio/items/weapons.dm @@ -6,7 +6,7 @@ slot_flags = SLOT_BELT force = 9 lightcolor = "#33CCFF" - origin_tech = list(TECH_COMBAT = 2, TECH_BIO = 4) + origin_tech = list(TECH_COMBAT = 2, TECH_BIO = 2) agonyforce = 10 //It's not supposed to be great at stunning human beings. hitcost = 48 //Less zap for less cost description_info = "This baton will stun a slime or other lesser lifeform for about five seconds, if hit with it while on." diff --git a/code/stylesheet.dm b/code/stylesheet.dm index 14f095ae9de..b376f13aebc 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -88,7 +88,7 @@ h1.alert, h2.alert {color: #000000;} .alien {color: #543354;} .tajaran {color: #803B56;} .tajaran_signlang {color: #941C1C;} -.skrell {color: #00CED1;} +.skrell {color: #00B0B3;} .soghun {color: #228B22;} .solcom {color: #22228B;} .changeling {color: #800080;} diff --git a/html/changelog.html b/html/changelog.html index 2debd896134..cb2923c0f14 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,83 @@ -->