From 9e3ccc8405af738c7af387e858bc73840254677d Mon Sep 17 00:00:00 2001 From: Aurorablade Date: Thu, 29 Dec 2016 10:36:42 -0500 Subject: [PATCH] I hate when ti does this --- code/game/machinery/doors/airlock.dm | 3947 +++++----------- .../living/carbon/human/species/species.dm | 4137 +++-------------- 2 files changed, 1807 insertions(+), 6277 deletions(-) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 680ae07ff93..f92ae8e5b43 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,2805 +1,1142 @@ - - - - - - - - - - - - - - - - - - - - - - History for code/game/machinery/doors/airlock.dm - ParadiseSS13/Paradise - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - - - - - - -
- -
-
- - -
-
-
- -
-
- - - -
    - -
  • -
    - -
    - - - - Watch - - - - -
    - -
    -
    -
    -
  • - -
  • - -
    - -
    - - -
    -
    - - -
    - -
  • - -
  • -
    - -
    - -
  • -
- -

- - /Paradise - -

- -
- -
- -
-
- - -
- -
- -
-
- Commits on Dec 24, 2016 -
- -
    -
  1. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - Merge remote-tracking branch 'refs/remotes/ParadiseSS13/master' into DWI - - -

    - - -
    - Aurorablade - committed - - - Dec 24, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Dec 14, 2016 -
- -
    -
  1. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - dammit tiger - - -

    - - -
    - Aurorablade - committed - - - Dec 14, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
  3. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - i probaly still didi it wrong - - -

    - - -
    - Aurorablade - committed - - - Dec 14, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  4. -
  5. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - fox box airlock - - -

    - - -
    - Aurorablade - committed - - - Dec 14, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  6. -
  7. - -
    -
    - - @tigercat2000 - -
    -
    -
    -

    - NanoUI Update: Seperate UI Data into ui_data() proc - - -

    - -
    This commit changes how NanoUI's live data updating system works.
    -Previously, data for the template was directly gathered in the
    -ui_interact proc. Now, it is seperated into the proc `ui_data`.
    -
    -To players, this does absolutely nothing.
    -To coders, this opens up the potential for a much more complicated and
    -fancy graphical updating system in the future, because the data is
    -available on-demand without ever having to call
    -nanomanager.try_update_ui.
    - -
    - tigercat2000 - committed - - - Dec 14, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  8. -
-
- Commits on Dec 6, 2016 -
- -
    -
  1. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - Merge remote-tracking branch 'refs/remotes/ParadiseSS13/master' into DWI - - -

    - - -
    - Aurorablade - committed - - - Dec 6, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
  3. - -
    -
    - - @Markolie - -
    -
    -
    -

    - Fix AI crew monitor tracking and door electrification - - -

    - - -
    - Markolie - committed - - - Dec 6, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  4. -
-
- Commits on Sep 17, 2016 -
- -
    -
  1. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - Merge branch 'master' of https://github.com/ParadiseSS13/Paradise int… - - -

    - -
    …o DWI
    - -
    - Aurorablade - committed - - - Sep 17, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Sep 12, 2016 -
- -
    -
  1. - -
    -
    - - @GeneralChaos81 - -
    -
    -
    -

    - Redo of clown/mime door fix. - - -

    - - -
    - GeneralChaos81 - committed - - - Sep 12, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Sep 1, 2016 -
- -
    -
  1. - -
    -
    - - @Aurorablade - -
    -
    -
    -

    - cultdoors - - -

    - - -
    - Aurorablade - committed - - - Sep 1, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Aug 23, 2016 -
- -
    -
  1. - -
    -
    - - @Crazylemon64 - -
    -
    -
    -

    - Moves airlocks to the `addtimer` system - - -

    - - -
    - Crazylemon64 - committed - - - Aug 23, 2016 - -
    - -
    - -
  2. -
-
- Commits on Aug 12, 2016 -
- -
    -
  1. - -
    -
    - - @tigercat2000 - -
    -
    -
    -

    - /tg/ mecha - Part 1 - - -

    - -
    Start of porting the /tg/station mecha update.
    -This commit only cleans up the code and adds the proper dependencies for
    -the mecha.
    -
    - - Removes relative pathing from /datum/events
    - - Updated Process_Spacemove() to use
    -   atom/movable/proc/get_spacemove_backup()
    -   - Basically just makes launching off of unanchored objects in space an
    -     OOP behaviour
    - - Removed styling atrocities and relative pathing from most of the mecha files.
    -  - Notable exceptions:
    -   - code/game/mecha/mech_bay.dm
    -   - code/game/mecha/mech_fabricator.dm
    -   - code/game/mecha/mecha_construction_paths.dm
    -   - code/game/mecha/mecha_parts.dm
    -   - code/game/mecha/mecha_wreckage.dm
    -   - code/game/mecha/paintkits.dm
    - - Removed dyndomove/dyndoattackby/dyndobulletact. No more icky dynamic
    -   calls. Replaced by specific code for the 3 modules that used it.
    - - Refactored module cooldown. It now uses addtimer, and power usage is
    -   hooked directly into the cooldown calls.
    - - Added atom/movable/proc/has_buckled_mobs(). Currently not that useful,
    -   but, necessary for porting the multi-buckling system from /tg/.
    - - Split code/game/mecha/equipment/tools/tools.dm into multiple files.
    - - Removed snowflake behaviour from exosuit drill. It now calls
    -   turf.drill_act(src).
    -  - Different from /tg/: Allows you to drill any wall/floor normally.
    - - Drill no longer gibs mobs. It deals 80 organ damage instead.
    - - Drill can be used to harvest dead mobs.
    - - Removed all global_iterator systems from mecha and mecha equipment.
    -   Everything now uses object processing.
    - - Mecha now have a turn sound variable and step sound variable. Updated
    -   all subtypes to use these instead of snowflake domove procs.
    - - Removed mecha_do_after, mecha now uses the normal do_after.
    - - Removed enter_after, same as above.
    - - /obj/mecha/Process_Spacemove no longer strangely calls the user's
    -   spacemove.
    - - /obj/mecha/return_pressure now uses return_air instead of copypasted
    -   cabin air detection.
    - - Same for /obj/mecha/return_temperature
    - - Added /obj/mecha/Exited. Basically, properly clears occupant refs,
    -   even if they teleport out/otherwise exit improperly.
    - - Added hooks for mecha action buttons; Didn't implement them yet.
    - - Moved mecha UI to code/game/mecha/mecha_topic.dm
    - - Fixed turfs not updating atmos when ChangeTurf(/turf/space) is used.
    - - Updated visible_message and audible_message. Both now use
    -   get_mobs_in_view() to ensure that mobs inside containers can see
    -   messages.
    - - Removed /obj/item/mecha_parts/mecha_equipment/tool subtype. It had no
    -   use. Any subtypes are now just subtypes of
    -   /obj/item/mecha_parts/mecha_equipment.
    - -
    - tigercat2000 - committed - - - Aug 12, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Aug 11, 2016 -
- -
    -
  1. - -
    -
    - - @monster860 - -
    -
    -
    -

    - Makes boldnotice and danger classes be be used - - -

    - - -
    - monster860 - committed - - - Aug 9, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Aug 7, 2016 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - Malf AI Powers Update - - -

    - - -
    - Fox-McCloud - committed - - - Aug 7, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Aug 2, 2016 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - Removs Airlock World Calls - - -

    - - -
    - Fox-McCloud - committed - - - Aug 2, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Jul 13, 2016 -
- -
    -
  1. - -
    -
    - - @DaveTheHeadcrab - -
    -
    -
    -

    - Plastic Explosives Update - - -

    - - -
    - DaveTheHeadcrab - committed - - - Jul 13, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Jul 7, 2016 -
- -
    -
  1. - -
    -
    - - @tigercat2000 - -
    -
    -
    -

    - Mass replace - - -

    - - -
    - tigercat2000 - committed - - - Jul 7, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Jul 6, 2016 -
- -
    -
  1. - -
    -
    - - @TullyBurnalot - -
    -
    -
    -

    - Fixed Mime Doors - - -

    - -
    It's not Tranquilite.
    -
    -It's not Tranqillite.
    -
    -It's Tranquillite.
    - -
    - TullyBurnalot - committed - - - Jul 6, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
  3. - -
    -
    - - @TullyBurnalot - -
    -
    -
    -

    - Fixed Clown/Mime Door Deconstruction - - -

    - -
    Fixes #4689
    -
    -- Deconstructing Clown Doors now gives Bananiums
    -- Deconstructing Mime Doors now gives Tranquilite
    -- Invisibility drives installed
    -
    -🆑
    -tweak: Deconstructing Clown Doors gives Bananium
    -tweat: Deconstructing Mime Doors gives Tranquilite
    - -
    - TullyBurnalot - committed - - - Jul 6, 2016 - -
    - -
    - -
  4. -
-
- Commits on May 26, 2016 -
- -
    -
  1. - -
    -
    - - @CrAzYPiLoT-SS13 - -
    -
    -
    -

    - Goddammit - - -

    - - -
    - CrAzYPiLoT-SS13 - committed - - - May 26, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on May 13, 2016 -
- -
    -
  1. - -
    -
    - - @AugRob - -
    -
    -
    -

    - Add screwdriver sound when opening airlock panels - - -

    - - -
    - AugRob - committed - - - May 13, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Apr 14, 2016 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - remove extras - - -

    - - -
    - Fox-McCloud - committed - - - Apr 14, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Apr 9, 2016 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - people like weird shit - - -

    - - -
    - Fox-McCloud - committed - - - Apr 9, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Apr 8, 2016 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - New Airlock Sounds - - -

    - - -
    - Fox-McCloud - committed - - - Apr 8, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Apr 5, 2016 -
- -
    -
  1. - -
    -
    - - @tigercat2000 - -
    -
    -
    -

    - Reduce lines by removing blank lines added by PJ's script - - -

    - - -
    - tigercat2000 - committed - - - Apr 5, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
  3. - -
    -
    - - @tigercat2000 - -
    -
    -
    -

    - Replace most (if not all) output << operators with to_chat(user, mess… - - -

    - -
    …age)
    -
    -Currently, to_chat is literally just a proc that does user << message.
    -But it'll let us do output modification in the future, especially for
    -something like Goon's HTML chat.
    -
    -Big thanks to PJB for his to_chat script, see
    -d3athrow/vgstation13#6625 for more details.
    - -
    - tigercat2000 - committed - - - Apr 5, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  4. -
-
- Commits on Mar 28, 2016 -
- -
    -
  1. - -
    -
    - - @Tastyfish - -
    -
    -
    -

    - Ports tg simple_animal bots - - -

    - - -
    - Tastyfish - committed - - - Mar 21, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Mar 11, 2016 -
- -
    -
  1. - -
    -
    - - @Tastyfish - -
    -
    -
    -

    - Makes doors not process unless controlled by an airlock controller, f… - - -

    - -
    …ixes fire alarm processing leak
    - -
    - Tastyfish - committed - - - Mar 11, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Mar 6, 2016 -
- -
    -
  1. - -
    -
    - - @Tastyfish - -
    -
    -
    -

    - Adds tranquillite - - -

    - -
    Changes Recitence to be approx on par with Honker
    -Adds invisible wall barrier
    - -
    - Tastyfish - committed - - - Mar 4, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Feb 28, 2016 -
- -
    -
  1. - -
    -
    - - @Tastyfish - -
    -
    -
    -

    - Fixes a bunch of things to not circumvent SetStun/SetWeaken/SetParaly… - - -

    - -
    …se that has no business doing so.
    - -
    - Tastyfish - committed - - - Feb 28, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Jan 30, 2016 -
- -
    -
  1. - -
    -
    - - @VampyrBytes - -
    -
    -
    -

    - Fixes #3425 - - -

    - -
    There were a couple of things going on... the spawn(-1) which should
    -have meant that the door shut straight away was being negated by the
    -sleeps. Also, If the door had just been used, it would still be marked
    -as operating and so wouldn't shut
    - -
    - VampyrBytes - committed - - - Jan 30, 2016 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Nov 22, 2015 -
- -
    -
  1. - -
    -
    - - @Tastyfish - -
    -
    -
    -

    - refactored datum/effect/system because it was a complete mess - - -

    - - -
    - Tastyfish - committed - - - Nov 22, 2015 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Oct 5, 2015 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - Removes Update Triggers File - - -

    - - -
    - Fox-McCloud - committed - - - Oct 5, 2015 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Sep 21, 2015 -
- -
    -
  1. - -
    -
    - - @Markolie - -
    -
    -
    -

    - Fix door crush bug - - -

    - - -
    - Markolie - committed - - - Sep 21, 2015 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- Commits on Sep 18, 2015 -
- -
    -
  1. - -
    -
    - - @Fox-McCloud - -
    -
    -
    -

    - Fixes up a few Destroys - - -

    - - -
    - Fox-McCloud - committed - - - Sep 18, 2015 - - - - -
    - - - -
    - -
    -
    - -
    - -
  2. -
-
- -
- -
- -
- -
- - -
-
- -
- - - - - - - -
- - - You can't perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - +/* + New methods: + pulse - sends a pulse into a wire for hacking purposes + cut - cuts a wire and makes any necessary state changes + mend - mends a wire and makes any necessary state changes + canAIControl - 1 if the AI can control the airlock, 0 if not (then check canAIHack to see if it can hack in) + canAIHack - 1 if the AI can hack into the airlock to recover control, 0 if not. Also returns 0 if the AI does not *need* to hack it. + arePowerSystemsOn - 1 if the main or backup power are functioning, 0 if not. Does not check whether the power grid is charged or an APC has equipment on or anything like that. (Check (stat & NOPOWER) for that) + requiresIDs - 1 if the airlock is requiring IDs, 0 if not + isAllPowerCut - 1 if the main and backup power both have cut wires. + regainMainPower - handles the effect of main power coming back on. + loseMainPower - handles the effect of main power going offline. Usually (if one isn't already running) spawn a thread to count down how long it will be offline - counting down won't happen if main power was completely cut along with backup power, though, the thread will just sleep. + loseBackupPower - handles the effect of backup power going offline. + regainBackupPower - handles the effect of main power coming back on. + shock - has a chance of electrocuting its target. +*/ + + +// Wires for the airlock are located in the datum folder, inside the wires datum folder. + + +/obj/machinery/door/airlock + name = "airlock" + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door_closed" + power_channel = ENVIRON + + var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. + var/hackProof = 0 // if 1, this door can't be hacked by the AI + var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/main_power_lost_until = 0 //World time when main power is restored. + var/backup_power_lost_until = -1 //World time when backup power is restored. + var/electrified_timer = null + var/main_power_timer = null + var/backup_power_timer = null + var/spawnPowerRestoreRunning = 0 + var/welded = null + var/locked = 0 + var/lights = 1 // bolt lights show by default + var/datum/wires/airlock/wires = null + var/aiDisabledIdScanner = 0 + var/aiHacking = 0 + var/obj/machinery/door/airlock/closeOther = null + var/closeOtherId = null + var/lockdownbyai = 0 + var/assembly_type = /obj/structure/door_assembly + var/mineral = null + var/justzap = 0 + var/safe = 1 + normalspeed = 1 + var/obj/item/weapon/airlock_electronics/electronics = null + var/hasShocked = 0 //Prevents multiple shocks from happening + var/frozen = 0 //special condition for airlocks that are frozen shut, this will look weird on not normal airlocks because of a lack of special overlays. + autoclose = 1 + explosion_block = 1 + var/doorOpen = 'sound/machines/airlock.ogg' + var/doorClose = 'sound/machines/airlock.ogg' + var/doorDeni = 'sound/machines/DeniedBeep.ogg' // i'm thinkin' Deni's + var/boltUp = 'sound/machines/BoltsUp.ogg' + var/boltDown = 'sound/machines/BoltsDown.ogg' + var/is_special = 0 + +/obj/machinery/door/airlock/command + name = "Airlock" + icon = 'icons/obj/doors/Doorcom.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_com + +/obj/machinery/door/airlock/security + name = "Airlock" + icon = 'icons/obj/doors/Doorsec.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_sec + +/obj/machinery/door/airlock/engineering + name = "Airlock" + icon = 'icons/obj/doors/Dooreng.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_eng + +/obj/machinery/door/airlock/medical + name = "Airlock" + icon = 'icons/obj/doors/Doormed.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_med + +/obj/machinery/door/airlock/maintenance + name = "Maintenance Access" + icon = 'icons/obj/doors/Doormaint.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_mai + +/obj/machinery/door/airlock/external + name = "External Airlock" + icon = 'icons/obj/doors/Doorext.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + +/obj/machinery/door/airlock/glass + name = "Glass Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + opacity = 0 + glass = 1 + doorOpen = 'sound/machines/windowdoor.ogg' + doorClose = 'sound/machines/windowdoor.ogg' + +/obj/machinery/door/airlock/centcom + name = "Airlock" + icon = 'icons/obj/doors/Doorele.dmi' + opacity = 0 + +/obj/machinery/door/airlock/vault + name = "Vault" + icon = 'icons/obj/doors/vault.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites. + explosion_block = 2 + +/obj/machinery/door/airlock/freezer + name = "Freezer Airlock" + icon = 'icons/obj/doors/Doorfreezer.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_fre + +/obj/machinery/door/airlock/hatch + name = "Airtight Hatch" + icon = 'icons/obj/doors/Doorhatchele.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_hatch + +/obj/machinery/door/airlock/hatch/gamma + name = "Gamma Level Hatch" + hackProof = 1 + aiControlDisabled = 1 + unacidable = 1 + is_special = 1 + +/obj/machinery/door/airlock/maintenance_hatch + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorhatchmaint2.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_mhatch + +/obj/machinery/door/airlock/glass_command + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorcomglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_com + glass = 1 + +/obj/machinery/door/airlock/glass_engineering + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorengglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_eng + glass = 1 + +/obj/machinery/door/airlock/glass_security + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorsecglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_sec + glass = 1 + +/obj/machinery/door/airlock/glass_medical + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doormedglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_med + glass = 1 + +/obj/machinery/door/airlock/mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doormining.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_min + +/obj/machinery/door/airlock/atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmo.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + +/obj/machinery/door/airlock/research + name = "Airlock" + icon = 'icons/obj/doors/Doorresearch.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_research + +/obj/machinery/door/airlock/glass_research + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorresearchglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_research + glass = 1 + heat_proof = 1 + +/obj/machinery/door/airlock/glass_mining + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorminingglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_min + glass = 1 + +/obj/machinery/door/airlock/glass_atmos + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Dooratmoglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + glass = 1 + +/obj/machinery/door/airlock/gold + name = "Gold Airlock" + icon = 'icons/obj/doors/Doorgold.dmi' + mineral = "gold" + +/obj/machinery/door/airlock/silver + name = "Silver Airlock" + icon = 'icons/obj/doors/Doorsilver.dmi' + mineral = "silver" + +/obj/machinery/door/airlock/diamond + name = "Diamond Airlock" + icon = 'icons/obj/doors/Doordiamond.dmi' + mineral = "diamond" + +/obj/machinery/door/airlock/uranium + name = "Uranium Airlock" + desc = "And they said I was crazy." + icon = 'icons/obj/doors/Dooruranium.dmi' + mineral = "uranium" + var/event_step = 20 + +/obj/machinery/door/airlock/uranium/New() + ..() + addtimer(src, "radiate", event_step) + +/obj/machinery/door/airlock/uranium/proc/radiate() + if(prob(50)) + for(var/mob/living/L in range (3,src)) + L.apply_effect(15,IRRADIATE,0) + addtimer(src, "radiate", event_step) + +/obj/machinery/door/airlock/plasma + name = "plasma airlock" + desc = "No way this can end badly." + icon = 'icons/obj/doors/Doorplasma.dmi' + mineral = "plasma" + +/obj/machinery/door/airlock/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/obj/machinery/door/airlock/plasma/proc/ignite(exposed_temperature) + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/obj/machinery/door/airlock/plasma/proc/PlasmaBurn(temperature) + atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 500) + new/obj/structure/door_assembly( src.loc ) + qdel(src) + +/obj/machinery/door/airlock/plasma/BlockSuperconductivity() //we don't stop the heat~ + return 0 + +/obj/machinery/door/airlock/bananium + name = "Bananium Airlock" + icon = 'icons/obj/doors/Doorbananium.dmi' + mineral = "bananium" + doorOpen = 'sound/items/bikehorn.ogg' + doorClose = 'sound/items/bikehorn.ogg' + +/obj/machinery/door/airlock/tranquillite + name = "Tranquillite Airlock" + icon = 'icons/obj/doors/Doorfreezer.dmi' + mineral = "tranquillite" + doorOpen = null + doorClose = null + +/obj/machinery/door/airlock/sandstone + name = "Sandstone Airlock" + icon = 'icons/obj/doors/Doorsand.dmi' + mineral = "sandstone" + +/obj/machinery/door/airlock/science + name = "Airlock" + icon = 'icons/obj/doors/Doorsci.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_science + +/obj/machinery/door/airlock/glass_science + name = "Glass Airlocks" + icon = 'icons/obj/doors/Doorsciglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_science + glass = 1 + +/obj/machinery/door/airlock/highsecurity + name = "High Tech Security Airlock" + icon = 'icons/obj/doors/hightechsecurity.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity + explosion_block = 2 + +/obj/machinery/door/airlock/highsecurity/red + name = "Secure Armory Airlock" + hackProof = 1 + aiControlDisabled = 1 + +/obj/machinery/door/airlock/shuttle + name = "shuttle airlock" + icon = 'icons/obj/doors/doorshuttle.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_shuttle + +/obj/machinery/door/airlock/alien + name = "Alien Airlock" + desc = "A mysterious alien airlock with a complicated opening mechanism." + hackProof = 1 + icon = 'icons/obj/doors/Doorplasma.dmi' + is_special = 1 + +/obj/machinery/door/airlock/alien/bumpopen(mob/living/user as mob) + if(istype(user,/mob/living/carbon/alien) || isrobot(user) || isAI(user)) + ..(user) + else + to_chat(user, "You do not know how to operate this airlock's mechanism.") + return + +/obj/machinery/door/airlock/alien/attackby(C as obj, mob/user as mob, params) + if(isalien(user) || isrobot(user) || isAI(user)) + ..(C, user) + else + to_chat(user, "You do not know how to operate this airlock's mechanism.") + return + + +/* +About the new airlock wires panel: +* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. +* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) +* two wires for power. Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be \red open, but bolts-raising will not work. Cutting these wires may electrocute the user. +* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. +* two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. +* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. +* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. +* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) +* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. +* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. +*/ +// You can find code for the airlock wires in the wire datum folder. + + +/obj/machinery/door/airlock/Destroy() + qdel(wires) + wires = null + if(main_power_timer) + deltimer(main_power_timer) + main_power_timer = null + if(backup_power_timer) + deltimer(backup_power_timer) + backup_power_timer = null + if(electrified_timer) + deltimer(electrified_timer) + electrified_timer = null + return ..() + +/obj/machinery/door/airlock/bumpopen(mob/living/user as mob) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite + if(!issilicon(usr)) + if(src.isElectrified()) + if(!src.justzap) + if(src.shock(user, 100)) + src.justzap = 1 + spawn (10) + src.justzap = 0 + return + else /*if(src.justzap)*/ + return + else if(user.hallucination > 50 && prob(10) && src.operating == 0) + to_chat(user, "You feel a powerful shock course through your body!") + user.adjustStaminaLoss(50) + user.AdjustStunned(5) + return + ..(user) + +/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user as mob) + ..(user) + + +/obj/machinery/door/airlock/autoclose() + autoclose_timer = 0 + if(!qdeleted(src) && !density && !operating && !locked && !welded && autoclose) + close() + return + +/obj/machinery/door/airlock/proc/isElectrified() + if(src.electrified_until != 0) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/isWireCut(var/wireIndex) + // You can find the wires in the datum folder. + return wires.IsIndexCut(wireIndex) + +/obj/machinery/door/airlock/proc/canAIControl() + return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/canAIHack() + return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/arePowerSystemsOn() + if(stat & (NOPOWER|BROKEN)) + return 0 + return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) + +/obj/machinery/door/airlock/requiresID() + return !(src.isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) + +/obj/machinery/door/airlock/proc/isAllPowerLoss() + if(stat & (NOPOWER|BROKEN)) + return 1 + if(mainPowerCablesCut() && backupPowerCablesCut()) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/mainPowerCablesCut() + return src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2) + +/obj/machinery/door/airlock/proc/backupPowerCablesCut() + return src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2) + +/obj/machinery/door/airlock/proc/loseMainPower() + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + if(main_power_lost_until > 0) + main_power_timer = addtimer(src, "regainMainPower", SecondsToTicks(60), 1) + + // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running + if(backup_power_lost_until == -1 && !backupPowerCablesCut()) + backup_power_lost_until = world.time + SecondsToTicks(10) + backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(10), 1) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + +/obj/machinery/door/airlock/proc/loseBackupPower() + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + if(backup_power_lost_until > 0) + backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(60), 1) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + +/obj/machinery/door/airlock/proc/regainMainPower() + main_power_timer = null + + if(!mainPowerCablesCut()) + main_power_lost_until = 0 + // If backup power is currently active then disable, otherwise let it count down and disable itself later + if(!backup_power_lost_until) + backup_power_lost_until = -1 + update_icon() + +/obj/machinery/door/airlock/proc/regainBackupPower() + backup_power_timer = null + + if(!backupPowerCablesCut()) + // Restore backup power only if main power is offline, otherwise permanently disable + backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 + update_icon() + +/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) + if(electrified_timer) + deltimer(electrified_timer) + electrified_timer = null + + var/message = "" + if(isWireCut(AIRLOCK_WIRE_ELECTRIFY) && arePowerSystemsOn()) + message = text("The electrification wire is cut - Door permanently electrified.") + electrified_until = -1 + else if(duration && !arePowerSystemsOn()) + message = text("The door is unpowered - Cannot electrify the door.") + electrified_until = 0 + else if(!duration && electrified_until != 0) + message = "The door is now un-electrified." + electrified_until = 0 + else if(duration) //electrify door for the given duration seconds + if(usr) + shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + else + shockedby += text("\[[time_stamp()]\] - EMP)") + message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." + electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) + if(duration != -1) + electrified_timer = addtimer(src, "electrify", SecondsToTicks(duration), 1, 0) + + if(feedback && message) + to_chat(usr, message) + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise +// The preceding comment was borrowed from the grille's shock script +/obj/machinery/door/airlock/shock(mob/user, prb) + if(!arePowerSystemsOn()) + return 0 + if(hasShocked) + return 0 //Already shocked someone recently? + if(..()) + hasShocked = 1 + sleep(10) + hasShocked = 0 + return 1 + else + return 0 + +/obj/machinery/door/airlock/update_icon() + if(overlays) overlays.Cut() + overlays = list() + if(emergency && arePowerSystemsOn()) + overlays += image('icons/obj/doors/doorint.dmi', "elights") + if(density) + if(locked && lights) + icon_state = "door_locked" + else + icon_state = "door_closed" + if(p_open || welded || frozen) + if(p_open) + overlays += image(icon, "panel_open") + if(welded) + overlays += image(icon, "welded") + if(frozen) + overlays += image(icon, "frozen") + + else + icon_state = "door_open" + + return + + +/obj/machinery/door/airlock/do_animate(animation) + switch(animation) + if("opening") + if(overlays) overlays.Cut() + if(p_open) + spawn(2) // The only work around that works. Downside is that the door will be gone for a millisecond. + flick("o_door_opening", src) //can not use flick due to BYOND bug updating overlays right before flicking + update_icon() + else + flick("door_opening", src)//[stat ? "_stat":] + update_icon() + if("closing") + if(overlays) overlays.Cut() + if(p_open) + spawn(2) + flick("o_door_closing", src) + update_icon() + else + flick("door_closing", src) + update_icon() + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && src.arePowerSystemsOn()) + playsound(src, doorDeni, 50, 0, 3) + flick("door_deny", src) + return + +/obj/machinery/door/airlock/attack_ai(mob/user as mob) + ui_interact(user) + +/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls - [src]", 600, 375) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = default_state) + var/data[0] + + data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) + data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) + data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) + data["open"] = !density + + var/commands[0] + commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner,"enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised", "disabled" = "Dropped", "danger" = 0, "act" = 0) + commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0) + commands[++commands.len] = list("name" = "Emergency Access","command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0) + + data["commands"] = commands + return data + +/obj/machinery/door/airlock/proc/hack(mob/user as mob) + if(src.aiHacking==0) + src.aiHacking=1 + spawn(20) + //TODO: Make this take a minute + to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") + sleep(50) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") + sleep(20) + to_chat(user, "Attempting to hack into airlock. This may take some time.") + sleep(200) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Upload access confirmed. Loading control program into airlock software.") + sleep(170) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Transfer complete. Forcing airlock to execute program.") + sleep(50) + //disable blocked control + src.aiControlDisabled = 2 + to_chat(user, "Receiving control information from airlock.") + sleep(10) + //bring up airlock dialog + src.aiHacking = 0 + if(user) + src.attack_ai(user) + +/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(src.isElectrified()) + if(istype(mover, /obj/item)) + var/obj/item/i = mover + if(i.materials[MAT_METAL]) + var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return ..() + +/obj/machinery/door/airlock/attack_hand(mob/user as mob) + if(!istype(user, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 100)) + return + + if(ishuman(user) && prob(40) && src.density) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) + if(!istype(H.head, /obj/item/clothing/head/helmet)) + visible_message("\red [user] headbutts the airlock.") + var/obj/item/organ/external/affecting = H.get_organ("head") + H.Stun(5) + H.Weaken(5) + if(affecting.take_damage(10, 0)) + H.UpdateDamageIcon() + else + visible_message("\red [user] headbutts the airlock. Good thing they're wearing a helmet.") + return + + if(src.p_open) + wires.Interact(user) + else + ..(user) + return + +/obj/machinery/door/airlock/CanUseTopic(var/mob/user) + if(!issilicon(user)) + return STATUS_CLOSE + + if(operating < 0) //emagged + to_chat(user, "Unable to interface: Internal error.") + return STATUS_CLOSE + if(!src.canAIControl()) + if(src.canAIHack(user)) + src.hack(user) + else + if(src.isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. + to_chat(user, "Unable to interface: Connection timed out.") + else + to_chat(user, "Unable to interface: Connection refused.") + return STATUS_CLOSE + + return ..() + +/obj/machinery/door/airlock/Topic(href, href_list, var/nowindow = 0) + if(..()) + return 1 + + var/activate = text2num(href_list["activate"]) + switch(href_list["command"]) + if("idscan") + if(src.isWireCut(AIRLOCK_WIRE_IDSCAN)) + to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.") + else if(activate && src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 0 + to_chat(usr, "IdScan feature has been enabled.") + else if(!activate && !src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 1 + to_chat(usr, "IdScan feature has been disabled.") + if("main_power") + if(!main_power_lost_until) + src.loseMainPower() + if("backup_power") + if(!backup_power_lost_until) + src.loseBackupPower() + if("bolts") + if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) + to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.") + else if(activate && src.lock()) + to_chat(usr, "The door bolts have been dropped.") + else if(!activate && src.unlock()) + to_chat(usr, "The door bolts have been raised.") + if("electrify_temporary") + if(activate && src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + to_chat(usr, text("The electrification wire is cut - Door permanently electrified.")) + else if(!activate && electrified_until != 0) + to_chat(usr, "The door is now un-electrified.") + electrify(0) + else if(activate) //electrify door for 30 seconds + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + to_chat(usr, "The door is now electrified for thirty seconds.") + electrify(30) + if("electrify_permanently") + if(src.isWireCut(AIRLOCK_WIRE_ELECTRIFY)) + to_chat(usr, text("The electrification wire is cut - Cannot electrify the door.")) + else if(!activate && electrified_until != 0) + to_chat(usr, "The door is now un-electrified.") + electrify(0) + else if(activate) + shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + to_chat(usr, "The door is now electrified.") + electrify(-1) + if("open") + if(src.welded) + to_chat(usr, text("The airlock has been welded shut!")) + else if(src.locked) + to_chat(usr, text("The door bolts are down!")) + else if(activate && density) + open() + else if(!activate && !density) + close() + if("safeties") + // Safeties! We don't need no stinking safeties! + if(src.isWireCut(AIRLOCK_WIRE_SAFETY)) + to_chat(usr, text("The safety wire is cut - Cannot secure the door.")) + else if(activate && src.safe) + safe = 0 + else if(!activate && !src.safe) + safe = 1 + if("timing") + // Door speed control + if(src.isWireCut(AIRLOCK_WIRE_SPEED)) + to_chat(usr, text("The timing wire is cut - Cannot alter timing.")) + else if(activate && src.normalspeed) + normalspeed = 0 + else if(!activate && !src.normalspeed) + normalspeed = 1 + if("lights") + // Bolt lights + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.") + else if(!activate && src.lights) + lights = 0 + to_chat(usr, "The door bolt lights have been disabled.") + else if(activate && !src.lights) + lights = 1 + to_chat(usr, "The door bolt lights have been enabled.") + if("emergency") + // Emergency access + if(src.emergency) + emergency = 0 + to_chat(usr, "Emergency access has been disabled.") + else + emergency = 1 + to_chat(usr, "Emergency access has been enabled.") + + update_icon() + return 1 + +/obj/machinery/door/airlock/attackby(C as obj, mob/user as mob, params) +// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user)) + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll)) + return + + src.add_fingerprint(user) + if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating ) && src.density)) + var/obj/item/weapon/weldingtool/W = C + if(W.remove_fuel(0,user)) + if(frozen) + frozen = 0 + if(!src.welded) + src.welded = 1 + else + src.welded = null + src.update_icon() + return + else + return + else if(istype(C, /obj/item/weapon/screwdriver)) + src.p_open = !( src.p_open ) + src.update_icon() + else if(istype(C, /obj/item/weapon/wirecutters)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/multitool)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/assembly/signaler)) + return src.attack_hand(user) + else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE + var/obj/item/weapon/pai_cable/cable = C + cable.plugin(src, user) + else if(istype(C, /obj/item/weapon/crowbar) || istype(C, /obj/item/weapon/twohanded/fireaxe) ) + var/beingcrowbarred = null + if(istype(C, /obj/item/weapon/crowbar) ) + beingcrowbarred = 1 //derp, Agouri + else + beingcrowbarred = 0 + if( beingcrowbarred && src.p_open && (operating == -1 || (density && welded && operating != 1 && !src.arePowerSystemsOn() && !src.locked)) ) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") + if(do_after(user,40, target = src)) + to_chat(user, "\blue You removed the airlock electronics!") + + var/obj/structure/door_assembly/da = new assembly_type(src.loc) + da.anchored = 1 + if(mineral) + da.glass = mineral + //else if(glass) + else if(glass && !da.glass) + da.glass = 1 + da.state = 1 + da.created_name = src.name + da.update_state() + + var/obj/item/weapon/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/weapon/airlock_electronics( src.loc ) + if(!src.req_access) + src.check_access() + if(src.req_access.len) + ae.conf_access = src.req_access + else if(src.req_one_access.len) + ae.conf_access = src.req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.loc = src.loc + if(operating == -1) + ae.icon_state = "door_electronics_smoked" + operating = 0 + + qdel(src) + return + else if(arePowerSystemsOn()) + to_chat(user, "\blue The airlock's motors resist your efforts to force it.") + else if(locked) + to_chat(user, "\blue The airlock's bolts prevent it from being forced.") + else if( !welded && !operating ) + if(density) + if(beingcrowbarred == 0) //being fireaxe'd + var/obj/item/weapon/twohanded/fireaxe/F = C + if(F.wielded) + spawn(0) open(1) + else + to_chat(user, "\red You need to be wielding \the [C] to do that.") + else + spawn(0) open(1) + else + if(beingcrowbarred == 0) + var/obj/item/weapon/twohanded/fireaxe/F = C + if(F.wielded) + spawn(0) close(1) + else + to_chat(user, "\red You need to be wielding \the [C] to do that.") + else + spawn(0) close(1) + else + ..() + return + +/obj/machinery/door/airlock/plasma/attackby(C as obj, mob/user as mob, params) + if(is_hot(C) > 300) + message_admins("Plasma airlock ignited by [key_name_admin(user)] in ([x],[y],[z] - JMP)") + log_game("Plasma wall ignited by [key_name(user)] in ([x],[y],[z])") + ignite(is_hot(C)) + ..() + +/obj/machinery/door/airlock/open(var/forced=0) + if( operating || welded || locked ) + return 0 + if(!forced) + if( !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) + return 0 + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + if(forced) + playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) + else + playsound(src.loc, doorOpen, 30, 1) + if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) + src.closeOther.close() + return ..() + +/obj/machinery/door/airlock/close(var/forced=0, var/override = 0) + if((operating & !override) || welded || locked) + return + if(!forced) + //despite the name, this wire is for general door control. + //Bolts are already covered by the check for locked, above + if( !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR) ) + return + if(safe) + for(var/turf/turf in locs) + if(locate(/mob/living) in turf) + // playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) //THE BUZZING IT NEVER STOPS -Pete + spawn (60) + autoclose() + return + + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + if(forced) + playsound(src.loc, 'sound/machines/airlockforced.ogg', 30, 1) + else + playsound(src.loc, doorClose, 30, 1) + var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src)) + if(killthis) + killthis.ex_act(2)//Smashin windows + + if(density) + return 1 + operating = 1 + do_animate("closing") + src.layer = 3.1 + if(!override) sleep(5) + src.density = 1 + if(!safe) + crush() + if(!override) sleep(5) + update_icon() + if(visible && !glass) + set_opacity(1) + operating = 0 + air_update_turf(1) + update_freelook_sight() + if(safe) + if(locate(/mob/living) in get_turf(src)) + open() + + return + +/obj/machinery/door/airlock/proc/lock(var/forced=0) + if(locked) + return 0 + + if(operating && !forced) return 0 + + src.locked = 1 + playsound(src, boltDown, 30, 0, 3) + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/unlock(var/forced=0) + if(!src.locked) + return + + if(!forced) + if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) return + + src.locked = 0 + playsound(src,boltUp, 30, 0, 3) + update_icon() + return 1 + +/obj/machinery/door/airlock/New() + ..() + wires = new(src) + if(src.closeOtherId != null) + spawn (5) + for(var/obj/machinery/door/airlock/A in airlocks) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + if(frozen) + welded = 1 + update_icon() + +/obj/machinery/door/airlock/hatch/gamma/attackby(C as obj, mob/user as mob, params) +// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user)) + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll)) + return + + if(istype(C, /obj/item/weapon/grenade/plastic/c4)) + to_chat(user, "The hatch is coated with a product that prevents the shaped charge from sticking!") + return + + if(istype(C, /obj/item/mecha_parts/mecha_equipment/rcd) || istype(C, /obj/item/weapon/rcd)) + to_chat(user, "The hatch is made of an advanced compound that cannot be deconstructed using an RCD.") + return + + src.add_fingerprint(user) + if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density)) + var/obj/item/weapon/weldingtool/W = C + if(W.remove_fuel(0,user)) + if(frozen) + frozen = 0 + if(!src.welded) + src.welded = 1 + else + src.welded = null + src.update_icon() + return + else + return + + +/obj/machinery/door/airlock/highsecurity/red/attackby(C as obj, mob/user as mob, params) +// to_chat(world, text("airlock attackby src [] obj [] mob []", src, C, user)) + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll)) + return + + src.add_fingerprint(user) + if((istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density)) + var/obj/item/weapon/weldingtool/W = C + if(W.remove_fuel(0,user)) + if(frozen) + frozen = 0 + if(!src.welded) + src.welded = 1 + else + src.welded = null + src.update_icon() + return + else + return + +/obj/machinery/door/airlock/CanAStarPass(var/obj/item/weapon/card/id/ID) +//Airlock is passable if it is open (!density), bot has access, and is not bolted shut) + return !density || (check_access(ID) && !locked && arePowerSystemsOn()) + +/obj/machinery/door/airlock/emp_act(var/severity) + if(prob(40/severity)) + var/duration = world.time + SecondsToTicks(30 / severity) + if(duration > electrified_until) + electrify(duration) + ..() + +/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason + ..() + if(stat & NOPOWER) + // If we lost power, disable electrification + // Keeping door lights on, runs on internal battery or something. + electrified_until = 0 + update_icon() + +/obj/machinery/door/airlock/proc/prison_open() + if(arePowerSystemsOn()) + src.unlock() + src.open() + src.lock() + +/obj/machinery/door/airlock/hostile_lockdown(mob/origin) + // Must be powered and have working AI wire. + if(canAIControl(src) && !stat) + locked = FALSE //For airlocks that were bolted open. + safe = FALSE //DOOR CRUSH + close() + lock() //Bolt it! + electrified_until = -1 //Shock it! + if(origin) + shockedby += "\[[time_stamp()]\][origin](ckey:[origin.ckey])" + + +/obj/machinery/door/airlock/disable_lockdown() + // Must be powered and have working AI wire. + if(canAIControl(src) && !stat) + unlock() + electrified_until = 0 + open() + safe = TRUE + +////////////////////////////////// +/* + Cult Airlocks +*/ + +/obj/machinery/door/airlock/cult + name = "cult airlock" + icon = 'icons/obj/doors/doorcult.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_cult + hackProof = 1 + aiControlDisabled = 1 + var/friendly = FALSE + +/obj/machinery/door/airlock/cult/New() + ..() + +/obj/machinery/door/airlock/cult/canAIControl(mob/user) + return (iscultist(user)) + +/obj/machinery/door/airlock/cult/allowed(mob/M) + if(!density) + return 1 + if(friendly || \ + iscultist(M) || \ + istype(M, /mob/living/simple_animal/shade) || \ + istype(M, /mob/living/simple_animal/construct)) + return 1 + else + var/atom/throwtarget + throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(M, src))) + M << pick(sound('sound/hallucinations/turn_around1.ogg',0,1,50), sound('sound/hallucinations/turn_around2.ogg',0,1,50)) + M.Weaken(2) + spawn(0) + M.throw_at(throwtarget, 5, 1,src) + return 0 + +/obj/machinery/door/airlock/cult/narsie_act() + return + +/obj/machinery/door/airlock/cult/friendly + friendly = TRUE diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index fb3938043db..cfd925fd14f 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -1,3472 +1,665 @@ - - - - - - - - - - - - - - - - - - - - - - Paradise/species.dm at master · ParadiseSS13/Paradise - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - - - - - - -
- -
-
- - -
-
-
- -
-
- - - -
    - -
  • -
    - -
    - - - - Watch - - - - -
    - -
    -
    -
    -
  • - -
  • - -
    - -
    - - -
    -
    - - -
    - -
  • - -
  • -
    - -
    - -
  • -
- -

- - /Paradise - -

- -
- -
- -
-
- - - -Permalink - - - -
- -
- - - -
- -
- - Find file - - -
- -
- - -
- Fetching contributors… -
- -
- - Cannot retrieve contributors at this time -
-
- -
-
-
- -
- Raw - Blame - History -
- - - - - -
- -
- -
- -
- 666 lines (556 sloc) - - 25.2 KB -
-
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
Datum-based species. Should make for much cleaner and easier to maintain mutantrace code.
*/
-
/datum/species
var/name // Species name.
var/name_plural // Pluralized name (since "[name]s" is not always valid)
var/path // Species path
var/icobase = 'icons/mob/human_races/r_human.dmi' // Normal icon set.
var/deform = 'icons/mob/human_races/r_def_human.dmi' // Mutated icon set.
-
// Damage overlay and masks.
var/damage_overlays = 'icons/mob/human_races/masks/dam_human.dmi'
var/damage_mask = 'icons/mob/human_races/masks/dam_mask_human.dmi'
var/blood_mask = 'icons/mob/human_races/masks/blood_human.dmi'
-
var/eyes = "eyes_s" // Icon for eyes.
var/blurb = "A completely nondescript species." // A brief lore summary for use in the chargen screen.
var/butt_sprite = "human"
-
var/primitive_form // Lesser form, if any (ie. monkey for humans)
var/greater_form // Greater form, if any, ie. human for monkeys.
var/tail // Name of tail image in species effects icon file.
var/unarmed //For empty hand harm-intent attack
var/unarmed_type = /datum/unarmed_attack
var/slowdown = 0 // Passive movement speed malus (or boost, if negative)
var/silent_steps = 0 // Stops step noises
-
var/breath_type = "oxygen" // Non-oxygen gas breathed, if any.
var/poison_type = "plasma" // Poisonous air.
var/exhale_type = "carbon_dioxide" // Exhaled gas type.
-
var/cold_level_1 = 260 // Cold damage level 1 below this point.
var/cold_level_2 = 200 // Cold damage level 2 below this point.
var/cold_level_3 = 120 // Cold damage level 3 below this point.
var/cold_env_multiplier = 1 // Damage multiplier for being in a cold environment
-
var/heat_level_1 = 360 // Heat damage level 1 above this point.
var/heat_level_2 = 400 // Heat damage level 2 above this point.
var/heat_level_3 = 460 // Heat damage level 3 above this point; used for body temperature
var/hot_env_multiplier = 1 // Damage multiplier for being in a hot environment
var/heat_level_3_breathe = 1000 // Heat damage level 3 above this point; used for breathed air temperature
-
var/body_temperature = 310.15 //non-IS_SYNTHETIC species will try to stabilize at this temperature. (also affects temperature processing)
var/reagent_tag //Used for metabolizing reagents.
-
var/siemens_coeff = 1 //base electrocution coefficient
-
var/invis_sight = SEE_INVISIBLE_LIVING
var/darksight = 2
var/hazard_high_pressure = HAZARD_HIGH_PRESSURE // Dangerously high pressure.
var/warning_high_pressure = WARNING_HIGH_PRESSURE // High pressure warning.
var/warning_low_pressure = WARNING_LOW_PRESSURE // Low pressure warning.
var/hazard_low_pressure = HAZARD_LOW_PRESSURE // Dangerously low pressure.
-
var/list/atmos_requirements = list(
"min_oxy" = 16,
"max_oxy" = 0,
"min_nitro" = 0,
"max_nitro" = 0,
"min_tox" = 0,
"max_tox" = 0.005,
"min_co2" = 0,
"max_co2" = 10,
"sa_para" = 1,
"sa_sleep" = 5
)
-
var/brute_mod = null // Physical damage reduction/malus.
var/burn_mod = null // Burn damage reduction/malus.
-
var/total_health = 100
var/punchdamagelow = 0 //lowest possible punch damage
var/punchdamagehigh = 9 //highest possible punch damage
var/punchstunthreshold = 9 //damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/list/default_genes = list()
-
var/ventcrawler = 0 //Determines if the mob can go through the vents.
var/has_fine_manipulation = 1 // Can use small items.
-
var/list/allowed_consumed_mobs = list() //If a species can consume mobs, put the type of mobs it can consume here.
-
var/flags = 0 // Various specific features.
var/clothing_flags = 0 // Underwear and socks.
var/exotic_blood
var/bodyflags = 0
var/dietflags = 0 // Make sure you set this, otherwise it won't be able to digest a lot of foods
-
var/list/abilities = list() // For species-derived or admin-given powers
-
var/blood_color = "#A10808" //Red.
var/flesh_color = "#FFC896" //Pink.
var/single_gib_type = /obj/effect/decal/cleanable/blood/gibs
var/remains_type = /obj/effect/decal/remains/human //What sort of remains is left behind when the species dusts
var/base_color //Used when setting species.
-
//Used in icon caching.
var/race_key = 0
var/icon/icon_template
var/is_small
var/show_ssd = 1
var/virus_immune
var/can_revive_by_healing // Determines whether or not this species can be revived by simply healing them
-
//Death vars.
var/death_message = "seizes up and falls limp, their eyes dead and lifeless..."
var/list/suicide_messages = list(
"is attempting to bite their tongue off!",
"is jamming their thumbs into their eye sockets!",
"is twisting their own neck!",
"is holding their breath!")
-
// Language/culture vars.
var/default_language = "Galactic Common" // Default language is used when 'say' is used without modifiers.
var/language = "Galactic Common" // Default racial language, if any.
var/secondary_langs = list() // The names of secondary languages that are available to this species.
var/list/speech_sounds // A list of sounds to potentially play when speaking.
var/list/speech_chance // The likelihood of a speech sound playing.
var/scream_verb = "screams"
var/male_scream_sound = 'sound/goonstation/voice/male_scream.ogg'
var/female_scream_sound = 'sound/goonstation/voice/female_scream.ogg'
-
//Default hair/headacc style vars.
var/default_hair //Default hair style for newly created humans unless otherwise set.
var/default_hair_colour
var/default_fhair //Default facial hair style for newly created humans unless otherwise set.
var/default_fhair_colour
var/default_headacc //Default head accessory style for newly created humans unless otherwise set.
var/default_headacc_colour
-
//Defining lists of icon skin tones for species that have them.
var/list/icon_skin_tones = list()
-
// Determines the organs that the species spawns with and
var/list/has_organ = list( // which required-organ checks are conducted.
"heart" = /obj/item/organ/internal/heart,
"lungs" = /obj/item/organ/internal/lungs,
"liver" = /obj/item/organ/internal/liver,
"kidneys" = /obj/item/organ/internal/kidneys,
"brain" = /obj/item/organ/internal/brain,
"appendix" = /obj/item/organ/internal/appendix,
"eyes" = /obj/item/organ/internal/eyes
)
var/vision_organ // If set, this organ is required for vision. Defaults to "eyes" if the species has them.
var/list/has_limbs = list(
"chest" = list("path" = /obj/item/organ/external/chest),
"groin" = list("path" = /obj/item/organ/external/groin),
"head" = list("path" = /obj/item/organ/external/head),
"l_arm" = list("path" = /obj/item/organ/external/arm),
"r_arm" = list("path" = /obj/item/organ/external/arm/right),
"l_leg" = list("path" = /obj/item/organ/external/leg),
"r_leg" = list("path" = /obj/item/organ/external/leg/right),
"l_hand" = list("path" = /obj/item/organ/external/hand),
"r_hand" = list("path" = /obj/item/organ/external/hand/right),
"l_foot" = list("path" = /obj/item/organ/external/foot),
"r_foot" = list("path" = /obj/item/organ/external/foot/right)
)
var/cyborg_type = "Cyborg"
var/list/proc/species_abilities = list()
-
/datum/species/New()
//If the species has eyes, they are the default vision organ
if(!vision_organ && has_organ["eyes"])
vision_organ = /obj/item/organ/internal/eyes
-
unarmed = new unarmed_type()
-
/datum/species/proc/get_random_name(var/gender)
var/datum/language/species_language = all_languages[language]
return species_language.get_random_name(gender)
-
/datum/species/proc/create_organs(var/mob/living/carbon/human/H) //Handles creation of mob organs.
-
for(var/obj/item/organ/internal/iorgan in H.internal_organs)
if(iorgan in H.internal_organs)
qdel(iorgan)
-
for(var/obj/item/organ/organ in H.contents)
if(organ in H.organs)
qdel(organ)
-
if(H.organs) H.organs.Cut()
if(H.organs_by_name) H.organs_by_name.Cut()
-
H.organs = list()
H.internal_organs = list()
H.organs_by_name = list()
-
for(var/limb_type in has_limbs)
var/list/organ_data = has_limbs[limb_type]
var/limb_path = organ_data["path"]
var/obj/item/organ/O = new limb_path(H)
organ_data["descriptor"] = O.name
-
for(var/index in has_organ)
var/organ = has_organ[index]
// organ new code calls `insert` on its own
new organ(H)
-
for(var/name in H.organs_by_name)
H.organs |= H.organs_by_name[name]
-
for(var/obj/item/organ/external/O in H.organs)
O.owner = H
-
/datum/species/proc/handle_breath(var/datum/gas_mixture/breath, var/mob/living/carbon/human/H)
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
-
var/O2_used = 0
var/N2_used = 0
var/Tox_used = 0
var/CO2_used = 0
-
//Partial pressure of the O2 in our breath
var/O2_pp = (breath.oxygen/breath.total_moles()) * breath_pressure
// Partial pressure of Nitrogen
var/N2_pp = (breath.nitrogen/breath.total_moles()) * breath_pressure
// Partial pressure of plasma
var/Tox_pp = (breath.toxins/breath.total_moles()) * breath_pressure
// Partial pressure of CO2
var/CO2_pp = (breath.carbon_dioxide/breath.total_moles()) * breath_pressure
-
if(O2_pp < atmos_requirements["min_oxy"])
if(prob(20))
spawn(0)
H.emote("gasp")
-
H.failed_last_breath = 1
if(O2_pp > 0)
var/ratio = atmos_requirements["min_oxy"] / O2_pp
H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS))
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
H.throw_alert("oxy", /obj/screen/alert/oxy)
else if(atmos_requirements["max_oxy"] && O2_pp > atmos_requirements["max_oxy"])
var/ratio = (breath.oxygen / atmos_requirements["max_oxy"]) * 1000
H.adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE))
H.throw_alert("oxy", /obj/screen/alert/too_much_oxy)
else
H.clear_alert("oxy")
if(atmos_requirements["min_oxy"]) //species breathes this gas, so, they got their air
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
O2_used = breath.oxygen / 6
-
if(N2_pp < atmos_requirements["min_nitro"])
if(prob(20))
spawn(0)
H.emote("gasp")
-
H.failed_last_breath = 1
if(N2_pp > 0)
var/ratio = atmos_requirements["min_nitro"] / N2_pp
H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS))
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
H.throw_alert("nitro", /obj/screen/alert/nitro)
else if(atmos_requirements["max_nitro"] && N2_pp > atmos_requirements["max_nitro"])
var/ratio = (breath.nitrogen / atmos_requirements["max_nitro"]) * 1000
H.adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE))
H.throw_alert("nitro", /obj/screen/alert/too_much_nitro)
else
H.clear_alert("nitro")
if(atmos_requirements["min_nitro"]) //species breathes this gas, so they got their air
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
N2_used = breath.nitrogen / 6
-
if(Tox_pp < atmos_requirements["min_tox"])
if(prob(20))
spawn(0)
H.emote("gasp")
-
H.failed_last_breath = 1
if(Tox_pp > 0)
var/ratio = atmos_requirements["min_tox"] / Tox_pp
H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS))
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
H.throw_alert("tox_in_air", /obj/screen/alert/not_enough_tox)
else if(atmos_requirements["max_tox"] && Tox_pp > atmos_requirements["max_tox"])
var/ratio = (breath.toxins / atmos_requirements["max_tox"]) * 10
if(H.reagents)
H.reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE))
H.throw_alert("tox_in_air", /obj/screen/alert/tox_in_air)
else
H.clear_alert("tox_in_air")
if(atmos_requirements["min_tox"]) //species breathes this gas, so, they got their air
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
Tox_used = breath.toxins / 6
-
if(CO2_pp < atmos_requirements["min_co2"])
if(prob(20))
spawn(0)
H.emote("gasp")
-
H.failed_last_breath = 1
if(CO2_pp)
var/ratio = atmos_requirements["min_co2"] / CO2_pp
H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS))
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
H.throw_alert("co2", /obj/screen/alert/not_enough_co2)
else if(atmos_requirements["max_co2"] && CO2_pp > atmos_requirements["max_co2"])
if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
H.co2overloadtime = world.time
else if(world.time - H.co2overloadtime > 120)
H.Paralyse(5)
H.adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business
if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
H.adjustOxyLoss(8)
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
spawn(0)
H.emote("cough")
else
H.clear_alert("co2")
H.co2overloadtime = 0
if(atmos_requirements["min_co2"]) //species breathes this gas, so they got their air
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
CO2_used = breath.carbon_dioxide / 6
-
breath.oxygen -= O2_used
breath.nitrogen -= N2_used
breath.toxins -= Tox_used
breath.carbon_dioxide -= CO2_used
breath.carbon_dioxide += O2_used
-
-
if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here.
for(var/datum/gas/sleeping_agent/SA in breath.trace_gases)
var/SA_pp = (SA.moles / breath.total_moles()) * breath_pressure
if(SA_pp > atmos_requirements["sa_para"]) // Enough to make us paralysed for a bit
H.Paralyse(3) // 3 gives them one second to wake up and run away a bit!
if(SA_pp > atmos_requirements["sa_sleep"]) // Enough to make us sleep as well
// This value is large because breaths are taken only once every 4 life ticks.
H.AdjustSleeping(8, bound_lower = 0, bound_upper = 10)
else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
if(prob(20))
spawn(0)
H.emote(pick("giggle", "laugh"))
-
handle_temperature(breath, H)
return 1
-
/datum/species/proc/handle_temperature(datum/gas_mixture/breath, var/mob/living/carbon/human/H) // called by human/life, handles temperatures
if(abs(310.15 - breath.temperature) > 50) // Hot air hurts :(
if(H.status_flags & GODMODE) return 1 //godmode
if(breath.temperature < cold_level_1)
if(prob(20))
to_chat(H, "<span class='warning'>You feel your face freezing and an icicle forming in your lungs!</span>")
else if(breath.temperature > heat_level_1)
if(prob(20))
to_chat(H, "<span class='warning'>You feel your face burning and a searing heat in your lungs!</span>")
-
-
-
switch(breath.temperature)
if(-INFINITY to cold_level_3)
H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Cold")
-
if(cold_level_3 to cold_level_2)
H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Cold")
-
if(cold_level_2 to cold_level_1)
H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Cold")
-
if(heat_level_1 to heat_level_2)
H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Heat")
-
if(heat_level_2 to heat_level_3_breathe)
H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Heat")
-
if(heat_level_3_breathe to INFINITY)
H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Heat")
-
/datum/species/proc/handle_post_spawn(var/mob/living/carbon/C) //Handles anything not already covered by basic species assignment.
grant_abilities(C)
return
-
/datum/species/proc/updatespeciescolor(var/mob/living/carbon/human/H) //Handles changing icobase for species that have multiple skin colors.
return
-
/datum/species/proc/grant_abilities(var/mob/living/carbon/human/H)
for(var/proc/ability in species_abilities)
H.verbs += ability
return
-
/datum/species/proc/handle_pre_change(var/mob/living/carbon/human/H)
if(H.butcher_results)//clear it out so we don't butcher a actual human.
H.butcher_results = null
remove_abilities(H)
return
-
/datum/species/proc/remove_abilities(var/mob/living/carbon/human/H)
for(var/proc/ability in species_abilities)
H.verbs -= ability
return
-
// Do species-specific reagent handling here
// Return 1 if it should do normal processing too
// Return 0 if it shouldn't deplete and do its normal effect
// Other return values will cause weird badness
/datum/species/proc/handle_reagents(var/mob/living/carbon/human/H, var/datum/reagent/R)
return 1
-
// For special snowflake species effects
// (Slime People changing color based on the reagents they consume)
/datum/species/proc/handle_life(var/mob/living/carbon/human/H)
return 1
-
/datum/species/proc/handle_dna(var/mob/living/carbon/C, var/remove) //Handles DNA mutations, as that doesn't work at init. Make sure you call genemutcheck on any blocks changed here
return
-
// Used for species-specific names (Vox, etc)
/datum/species/proc/makeName(var/gender,var/mob/living/carbon/human/H=null)
if(gender==FEMALE) return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names))
else return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names))
-
/datum/species/proc/handle_death(var/mob/living/carbon/human/H) //Handles any species-specific death events (such as dionaea nymph spawns).
return
-
/datum/species/proc/handle_attack_hand(var/mob/living/carbon/human/H, var/mob/living/carbon/human/M) //Handles any species-specific attackhand events.
return
-
/datum/species/proc/say_filter(mob/M, message, datum/language/speaking)
return message
-
/datum/species/proc/equip(var/mob/living/carbon/human/H)
return
-
/datum/species/proc/can_understand(var/mob/other)
return
-
// Called in life() when the mob has no client.
/datum/species/proc/handle_npc(var/mob/living/carbon/human/H)
return
-
//Species unarmed attacks
-
/datum/unarmed_attack
var/attack_verb = list("attack") // Empty hand hurt intent verb.
var/damage = 0 // How much flat bonus damage an attack will do. This is a *bonus* guaranteed damage amount on top of the random damage attacks do.
var/attack_sound = "punch"
var/miss_sound = 'sound/weapons/punchmiss.ogg'
var/sharp = 0
var/edge = 0
-
/datum/unarmed_attack/punch
attack_verb = list("punch")
-
/datum/unarmed_attack/punch/weak
attack_verb = list("flail")
-
/datum/unarmed_attack/diona
attack_verb = list("lash", "bludgeon")
-
/datum/unarmed_attack/claws
attack_verb = list("scratch", "claw")
attack_sound = 'sound/weapons/slice.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
sharp = 1
edge = 1
-
/datum/unarmed_attack/claws/armalis
attack_verb = list("slash", "claw")
damage = 6
-
/datum/species/proc/handle_can_equip(obj/item/I, slot, disable_warning = 0, mob/living/carbon/human/user)
return 0
-
/datum/species/proc/handle_vision(mob/living/carbon/human/H)
// Right now this just handles blind, blurry, and similar states
if(H.blinded || H.eye_blind)
H.overlay_fullscreen("blind", /obj/screen/fullscreen/blind)
H.throw_alert("blind", /obj/screen/alert/blind)
else
H.clear_fullscreen("blind")
H.clear_alert("blind")
-
-
if(H.disabilities & NEARSIGHTED) //this looks meh but saves a lot of memory by not requiring to add var/prescription
if(H.glasses) //to every /obj/item
var/obj/item/clothing/glasses/G = H.glasses
if(G.prescription)
H.clear_fullscreen("nearsighted")
else
H.overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1)
else
H.overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1)
else
H.clear_fullscreen("nearsighted")
-
if(H.eye_blurry)
H.overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry)
else
H.clear_fullscreen("blurry")
-
if(H.druggy)
H.overlay_fullscreen("high", /obj/screen/fullscreen/high)
H.throw_alert("high", /obj/screen/alert/high)
else
H.clear_fullscreen("high")
H.clear_alert("high")
-
/datum/species/proc/handle_hud_icons(mob/living/carbon/human/H)
if(!H.client)
return
if(H.healths)
if(H.stat == DEAD)
H.healths.icon_state = "health7"
else
switch(H.hal_screwyhud)
if(1) H.healths.icon_state = "health6"
if(2) H.healths.icon_state = "health7"
if(5) H.healths.icon_state = "health0"
else
switch(100 - ((flags & NO_PAIN) ? 0 : H.traumatic_shock) - H.staminaloss)
if(100 to INFINITY) H.healths.icon_state = "health0"
if(80 to 100) H.healths.icon_state = "health1"
if(60 to 80) H.healths.icon_state = "health2"
if(40 to 60) H.healths.icon_state = "health3"
if(20 to 40) H.healths.icon_state = "health4"
if(0 to 20) H.healths.icon_state = "health5"
else H.healths.icon_state = "health6"
-
if(H.healthdoll)
if(H.stat == DEAD)
H.healthdoll.icon_state = "healthdoll_DEAD"
if(H.healthdoll.overlays.len)
H.healthdoll.overlays.Cut()
else
var/list/new_overlays = list()
var/list/cached_overlays = H.healthdoll.cached_healthdoll_overlays
// Use the dead health doll as the base, since we have proper "healthy" overlays now
H.healthdoll.icon_state = "healthdoll_DEAD"
for(var/obj/item/organ/external/O in H.organs)
var/damage = O.burn_dam + O.brute_dam
var/comparison = (O.max_damage/5)
var/icon_num = 0
if(damage)
icon_num = 1
if(damage > (comparison))
icon_num = 2
if(damage > (comparison*2))
icon_num = 3
if(damage > (comparison*3))
icon_num = 4
if(damage > (comparison*4))
icon_num = 5
new_overlays += "[O.limb_name][icon_num]"
H.healthdoll.overlays += (new_overlays - cached_overlays)
H.healthdoll.overlays -= (cached_overlays - new_overlays)
H.healthdoll.cached_healthdoll_overlays = new_overlays
-
switch(H.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
H.throw_alert("nutrition", /obj/screen/alert/fat)
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL)
H.clear_alert("nutrition")
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
H.throw_alert("nutrition", /obj/screen/alert/hungry)
else
H.throw_alert("nutrition", /obj/screen/alert/starving)
return 1
-
/*
Returns the path corresponding to the corresponding organ
It'll return null if the organ doesn't correspond, so include null checks when using this!
*/
//Fethas Todo:Do i need to redo this?
/datum/species/proc/return_organ(var/organ_slot)
if(!(organ_slot in has_organ))
return null
return has_organ[organ_slot]
-
-
/datum/species/proc/update_sight(mob/living/carbon/human/H)
H.sight = initial(H.sight)
H.see_in_dark = darksight
H.see_invisible = invis_sight
-
if(H.client.eye != H)
var/atom/A = H.client.eye
if(A.update_remote_sight(H)) //returns 1 if we override all other sight updates.
return
-
if(H.mind && H.mind.vampire)
if(H.mind.vampire.get_ability(/datum/vampire_passive/full))
H.sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
H.see_in_dark = 8
H.see_invisible = SEE_INVISIBLE_MINIMUM
else if(H.mind.vampire.get_ability(/datum/vampire_passive/vision))
H.sight |= SEE_MOBS
-
for(var/obj/item/organ/internal/cyberimp/eyes/E in H.internal_organs)
H.sight |= E.vision_flags
if(E.dark_view)
H.see_in_dark = E.dark_view
if(E.see_invisible)
H.see_invisible = min(H.see_invisible, E.see_invisible)
-
-
if(H.seer == 1)
var/obj/effect/rune/R = locate() in H.loc
if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"])
H.see_invisible = SEE_INVISIBLE_OBSERVER
else
H.see_invisible = SEE_INVISIBLE_LIVING
H.seer = 0
-
var/lesser_darkview_bonus = INFINITY
// my glasses, I can't see without my glasses
if(H.glasses)
var/obj/item/clothing/glasses/G = H.glasses
H.sight |= G.vision_flags
lesser_darkview_bonus = G.darkness_view
if(G.invis_override)
H.see_invisible = G.invis_override
else
H.see_invisible = min(G.invis_view, H.see_invisible)
-
// better living through hat trading
if(H.head)
if(istype(H.head, /obj/item/clothing/head))
var/obj/item/clothing/head/hat = H.head
H.sight |= hat.vision_flags
-
if(hat.darkness_view) // Pick the lowest of the two darkness_views between the glasses and helmet.
lesser_darkview_bonus = min(hat.darkness_view,lesser_darkview_bonus)
-
if(hat.helmet_goggles_invis_view)
H.see_invisible = min(hat.helmet_goggles_invis_view, H.see_invisible)
-
if(istype(H.back, /obj/item/weapon/rig)) ///aghhh so snowflakey
var/obj/item/weapon/rig/rig = H.back
if(rig.visor)
if(!rig.helmet || (H.head && rig.helmet == H.head))
if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses)
var/obj/item/clothing/glasses/G = rig.visor.vision.glasses
if(istype(G))
H.see_in_dark = (G.darkness_view ? G.darkness_view : darksight) // Otherwise we keep our darkness view with togglable nightvision.
if(G.vision_flags) // MESONS
H.sight |= G.vision_flags
-
H.see_invisible = min(G.invis_view, H.see_invisible)
-
if(lesser_darkview_bonus != INFINITY)
H.see_in_dark = max(lesser_darkview_bonus, H.see_in_dark)
-
if(H.vision_type)
H.see_in_dark = max(H.see_in_dark, H.vision_type.see_in_dark, darksight)
H.see_invisible = H.vision_type.see_invisible
if(H.vision_type.light_sensitive)
H.weakeyes = 1
H.sight |= H.vision_type.sight_flags
-
if(XRAY in H.mutations)
H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
H.see_in_dark = max(H.see_in_dark, 8)
H.see_invisible = SEE_INVISIBLE_MINIMUM
-
if(H.see_override) //Override all
H.see_invisible = H.see_override
- -
- -
- - - - -
- -
- - -
-
- -
- - - - - - - -
- - - You can't perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - +/* + Datum-based species. Should make for much cleaner and easier to maintain mutantrace code. +*/ + +/datum/species + var/name // Species name. + var/name_plural // Pluralized name (since "[name]s" is not always valid) + var/path // Species path + var/icobase = 'icons/mob/human_races/r_human.dmi' // Normal icon set. + var/deform = 'icons/mob/human_races/r_def_human.dmi' // Mutated icon set. + + // Damage overlay and masks. + var/damage_overlays = 'icons/mob/human_races/masks/dam_human.dmi' + var/damage_mask = 'icons/mob/human_races/masks/dam_mask_human.dmi' + var/blood_mask = 'icons/mob/human_races/masks/blood_human.dmi' + + var/eyes = "eyes_s" // Icon for eyes. + var/blurb = "A completely nondescript species." // A brief lore summary for use in the chargen screen. + var/butt_sprite = "human" + + var/primitive_form // Lesser form, if any (ie. monkey for humans) + var/greater_form // Greater form, if any, ie. human for monkeys. + var/tail // Name of tail image in species effects icon file. + var/unarmed //For empty hand harm-intent attack + var/unarmed_type = /datum/unarmed_attack + var/slowdown = 0 // Passive movement speed malus (or boost, if negative) + var/silent_steps = 0 // Stops step noises + + var/breath_type = "oxygen" // Non-oxygen gas breathed, if any. + var/poison_type = "plasma" // Poisonous air. + var/exhale_type = "carbon_dioxide" // Exhaled gas type. + + var/cold_level_1 = 260 // Cold damage level 1 below this point. + var/cold_level_2 = 200 // Cold damage level 2 below this point. + var/cold_level_3 = 120 // Cold damage level 3 below this point. + var/cold_env_multiplier = 1 // Damage multiplier for being in a cold environment + + var/heat_level_1 = 360 // Heat damage level 1 above this point. + var/heat_level_2 = 400 // Heat damage level 2 above this point. + var/heat_level_3 = 460 // Heat damage level 3 above this point; used for body temperature + var/hot_env_multiplier = 1 // Damage multiplier for being in a hot environment + var/heat_level_3_breathe = 1000 // Heat damage level 3 above this point; used for breathed air temperature + + var/body_temperature = 310.15 //non-IS_SYNTHETIC species will try to stabilize at this temperature. (also affects temperature processing) + var/reagent_tag //Used for metabolizing reagents. + + var/siemens_coeff = 1 //base electrocution coefficient + + var/invis_sight = SEE_INVISIBLE_LIVING + var/darksight = 2 + var/hazard_high_pressure = HAZARD_HIGH_PRESSURE // Dangerously high pressure. + var/warning_high_pressure = WARNING_HIGH_PRESSURE // High pressure warning. + var/warning_low_pressure = WARNING_LOW_PRESSURE // Low pressure warning. + var/hazard_low_pressure = HAZARD_LOW_PRESSURE // Dangerously low pressure. + + var/list/atmos_requirements = list( + "min_oxy" = 16, + "max_oxy" = 0, + "min_nitro" = 0, + "max_nitro" = 0, + "min_tox" = 0, + "max_tox" = 0.005, + "min_co2" = 0, + "max_co2" = 10, + "sa_para" = 1, + "sa_sleep" = 5 + ) + + var/brute_mod = null // Physical damage reduction/malus. + var/burn_mod = null // Burn damage reduction/malus. + + var/total_health = 100 + var/punchdamagelow = 0 //lowest possible punch damage + var/punchdamagehigh = 9 //highest possible punch damage + var/punchstunthreshold = 9 //damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical + var/list/default_genes = list() + + var/ventcrawler = 0 //Determines if the mob can go through the vents. + var/has_fine_manipulation = 1 // Can use small items. + + var/list/allowed_consumed_mobs = list() //If a species can consume mobs, put the type of mobs it can consume here. + + var/flags = 0 // Various specific features. + var/clothing_flags = 0 // Underwear and socks. + var/exotic_blood + var/bodyflags = 0 + var/dietflags = 0 // Make sure you set this, otherwise it won't be able to digest a lot of foods + + var/list/abilities = list() // For species-derived or admin-given powers + + var/blood_color = "#A10808" //Red. + var/flesh_color = "#FFC896" //Pink. + var/single_gib_type = /obj/effect/decal/cleanable/blood/gibs + var/remains_type = /obj/effect/decal/remains/human //What sort of remains is left behind when the species dusts + var/base_color //Used when setting species. + + //Used in icon caching. + var/race_key = 0 + var/icon/icon_template + var/is_small + var/show_ssd = 1 + var/virus_immune + var/can_revive_by_healing // Determines whether or not this species can be revived by simply healing them + + //Death vars. + var/death_message = "seizes up and falls limp, their eyes dead and lifeless..." + var/list/suicide_messages = list( + "is attempting to bite their tongue off!", + "is jamming their thumbs into their eye sockets!", + "is twisting their own neck!", + "is holding their breath!") + + // Language/culture vars. + var/default_language = "Galactic Common" // Default language is used when 'say' is used without modifiers. + var/language = "Galactic Common" // Default racial language, if any. + var/secondary_langs = list() // The names of secondary languages that are available to this species. + var/list/speech_sounds // A list of sounds to potentially play when speaking. + var/list/speech_chance // The likelihood of a speech sound playing. + var/scream_verb = "screams" + var/male_scream_sound = 'sound/goonstation/voice/male_scream.ogg' + var/female_scream_sound = 'sound/goonstation/voice/female_scream.ogg' + + //Default hair/headacc style vars. + var/default_hair //Default hair style for newly created humans unless otherwise set. + var/default_hair_colour + var/default_fhair //Default facial hair style for newly created humans unless otherwise set. + var/default_fhair_colour + var/default_headacc //Default head accessory style for newly created humans unless otherwise set. + var/default_headacc_colour + + //Defining lists of icon skin tones for species that have them. + var/list/icon_skin_tones = list() + + // Determines the organs that the species spawns with and + var/list/has_organ = list( // which required-organ checks are conducted. + "heart" = /obj/item/organ/internal/heart, + "lungs" = /obj/item/organ/internal/lungs, + "liver" = /obj/item/organ/internal/liver, + "kidneys" = /obj/item/organ/internal/kidneys, + "brain" = /obj/item/organ/internal/brain, + "appendix" = /obj/item/organ/internal/appendix, + "eyes" = /obj/item/organ/internal/eyes + ) + var/vision_organ // If set, this organ is required for vision. Defaults to "eyes" if the species has them. + var/list/has_limbs = list( + "chest" = list("path" = /obj/item/organ/external/chest), + "groin" = list("path" = /obj/item/organ/external/groin), + "head" = list("path" = /obj/item/organ/external/head), + "l_arm" = list("path" = /obj/item/organ/external/arm), + "r_arm" = list("path" = /obj/item/organ/external/arm/right), + "l_leg" = list("path" = /obj/item/organ/external/leg), + "r_leg" = list("path" = /obj/item/organ/external/leg/right), + "l_hand" = list("path" = /obj/item/organ/external/hand), + "r_hand" = list("path" = /obj/item/organ/external/hand/right), + "l_foot" = list("path" = /obj/item/organ/external/foot), + "r_foot" = list("path" = /obj/item/organ/external/foot/right) + ) + var/cyborg_type = "Cyborg" + var/list/proc/species_abilities = list() + +/datum/species/New() + //If the species has eyes, they are the default vision organ + if(!vision_organ && has_organ["eyes"]) + vision_organ = /obj/item/organ/internal/eyes + + unarmed = new unarmed_type() + +/datum/species/proc/get_random_name(var/gender) + var/datum/language/species_language = all_languages[language] + return species_language.get_random_name(gender) + +/datum/species/proc/create_organs(var/mob/living/carbon/human/H) //Handles creation of mob organs. + + for(var/obj/item/organ/internal/iorgan in H.internal_organs) + if(iorgan in H.internal_organs) + qdel(iorgan) + + for(var/obj/item/organ/organ in H.contents) + if(organ in H.organs) + qdel(organ) + + if(H.organs) H.organs.Cut() + if(H.organs_by_name) H.organs_by_name.Cut() + + H.organs = list() + H.internal_organs = list() + H.organs_by_name = list() + + for(var/limb_type in has_limbs) + var/list/organ_data = has_limbs[limb_type] + var/limb_path = organ_data["path"] + var/obj/item/organ/O = new limb_path(H) + organ_data["descriptor"] = O.name + + for(var/index in has_organ) + var/organ = has_organ[index] + // organ new code calls `insert` on its own + new organ(H) + + for(var/name in H.organs_by_name) + H.organs |= H.organs_by_name[name] + + for(var/obj/item/organ/external/O in H.organs) + O.owner = H + +/datum/species/proc/handle_breath(var/datum/gas_mixture/breath, var/mob/living/carbon/human/H) + var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME + + var/O2_used = 0 + var/N2_used = 0 + var/Tox_used = 0 + var/CO2_used = 0 + + //Partial pressure of the O2 in our breath + var/O2_pp = (breath.oxygen/breath.total_moles()) * breath_pressure + // Partial pressure of Nitrogen + var/N2_pp = (breath.nitrogen/breath.total_moles()) * breath_pressure + // Partial pressure of plasma + var/Tox_pp = (breath.toxins/breath.total_moles()) * breath_pressure + // Partial pressure of CO2 + var/CO2_pp = (breath.carbon_dioxide/breath.total_moles()) * breath_pressure + + if(O2_pp < atmos_requirements["min_oxy"]) + if(prob(20)) + spawn(0) + H.emote("gasp") + + H.failed_last_breath = 1 + if(O2_pp > 0) + var/ratio = atmos_requirements["min_oxy"] / O2_pp + H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS)) + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.throw_alert("oxy", /obj/screen/alert/oxy) + else if(atmos_requirements["max_oxy"] && O2_pp > atmos_requirements["max_oxy"]) + var/ratio = (breath.oxygen / atmos_requirements["max_oxy"]) * 1000 + H.adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + H.throw_alert("oxy", /obj/screen/alert/too_much_oxy) + else + H.clear_alert("oxy") + if(atmos_requirements["min_oxy"]) //species breathes this gas, so, they got their air + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + O2_used = breath.oxygen / 6 + + if(N2_pp < atmos_requirements["min_nitro"]) + if(prob(20)) + spawn(0) + H.emote("gasp") + + H.failed_last_breath = 1 + if(N2_pp > 0) + var/ratio = atmos_requirements["min_nitro"] / N2_pp + H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS)) + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.throw_alert("nitro", /obj/screen/alert/nitro) + else if(atmos_requirements["max_nitro"] && N2_pp > atmos_requirements["max_nitro"]) + var/ratio = (breath.nitrogen / atmos_requirements["max_nitro"]) * 1000 + H.adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + H.throw_alert("nitro", /obj/screen/alert/too_much_nitro) + else + H.clear_alert("nitro") + if(atmos_requirements["min_nitro"]) //species breathes this gas, so they got their air + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + N2_used = breath.nitrogen / 6 + + if(Tox_pp < atmos_requirements["min_tox"]) + if(prob(20)) + spawn(0) + H.emote("gasp") + + H.failed_last_breath = 1 + if(Tox_pp > 0) + var/ratio = atmos_requirements["min_tox"] / Tox_pp + H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS)) + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.throw_alert("tox_in_air", /obj/screen/alert/not_enough_tox) + else if(atmos_requirements["max_tox"] && Tox_pp > atmos_requirements["max_tox"]) + var/ratio = (breath.toxins / atmos_requirements["max_tox"]) * 10 + if(H.reagents) + H.reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + H.throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) + else + H.clear_alert("tox_in_air") + if(atmos_requirements["min_tox"]) //species breathes this gas, so, they got their air + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + Tox_used = breath.toxins / 6 + + if(CO2_pp < atmos_requirements["min_co2"]) + if(prob(20)) + spawn(0) + H.emote("gasp") + + H.failed_last_breath = 1 + if(CO2_pp) + var/ratio = atmos_requirements["min_co2"] / CO2_pp + H.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS)) + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.throw_alert("co2", /obj/screen/alert/not_enough_co2) + else if(atmos_requirements["max_co2"] && CO2_pp > atmos_requirements["max_co2"]) + if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so. + H.co2overloadtime = world.time + else if(world.time - H.co2overloadtime > 120) + H.Paralyse(5) + H.adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business + if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good! + H.adjustOxyLoss(8) + if(prob(20)) // Lets give them some chance to know somethings not right though I guess. + spawn(0) + H.emote("cough") + else + H.clear_alert("co2") + H.co2overloadtime = 0 + if(atmos_requirements["min_co2"]) //species breathes this gas, so they got their air + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + CO2_used = breath.carbon_dioxide / 6 + + breath.oxygen -= O2_used + breath.nitrogen -= N2_used + breath.toxins -= Tox_used + breath.carbon_dioxide -= CO2_used + breath.carbon_dioxide += O2_used + + + if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here. + for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) + var/SA_pp = (SA.moles / breath.total_moles()) * breath_pressure + if(SA_pp > atmos_requirements["sa_para"]) // Enough to make us paralysed for a bit + H.Paralyse(3) // 3 gives them one second to wake up and run away a bit! + if(SA_pp > atmos_requirements["sa_sleep"]) // Enough to make us sleep as well + // This value is large because breaths are taken only once every 4 life ticks. + H.AdjustSleeping(8, bound_lower = 0, bound_upper = 10) + else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning + if(prob(20)) + spawn(0) + H.emote(pick("giggle", "laugh")) + + handle_temperature(breath, H) + return 1 + +/datum/species/proc/handle_temperature(datum/gas_mixture/breath, var/mob/living/carbon/human/H) // called by human/life, handles temperatures + if(abs(310.15 - breath.temperature) > 50) // Hot air hurts :( + if(H.status_flags & GODMODE) return 1 //godmode + if(breath.temperature < cold_level_1) + if(prob(20)) + to_chat(H, "You feel your face freezing and an icicle forming in your lungs!") + else if(breath.temperature > heat_level_1) + if(prob(20)) + to_chat(H, "You feel your face burning and a searing heat in your lungs!") + + + + switch(breath.temperature) + if(-INFINITY to cold_level_3) + H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Cold") + + if(cold_level_3 to cold_level_2) + H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Cold") + + if(cold_level_2 to cold_level_1) + H.apply_damage(cold_env_multiplier*COLD_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Cold") + + if(heat_level_1 to heat_level_2) + H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head", used_weapon = "Excessive Heat") + + if(heat_level_2 to heat_level_3_breathe) + H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head", used_weapon = "Excessive Heat") + + if(heat_level_3_breathe to INFINITY) + H.apply_damage(hot_env_multiplier*HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head", used_weapon = "Excessive Heat") + +/datum/species/proc/handle_post_spawn(var/mob/living/carbon/C) //Handles anything not already covered by basic species assignment. + grant_abilities(C) + return + +/datum/species/proc/updatespeciescolor(var/mob/living/carbon/human/H) //Handles changing icobase for species that have multiple skin colors. + return + +/datum/species/proc/grant_abilities(var/mob/living/carbon/human/H) + for(var/proc/ability in species_abilities) + H.verbs += ability + return + +/datum/species/proc/handle_pre_change(var/mob/living/carbon/human/H) + if(H.butcher_results)//clear it out so we don't butcher a actual human. + H.butcher_results = null + remove_abilities(H) + return + +/datum/species/proc/remove_abilities(var/mob/living/carbon/human/H) + for(var/proc/ability in species_abilities) + H.verbs -= ability + return + +// Do species-specific reagent handling here +// Return 1 if it should do normal processing too +// Return 0 if it shouldn't deplete and do its normal effect +// Other return values will cause weird badness +/datum/species/proc/handle_reagents(var/mob/living/carbon/human/H, var/datum/reagent/R) + return 1 + +// For special snowflake species effects +// (Slime People changing color based on the reagents they consume) +/datum/species/proc/handle_life(var/mob/living/carbon/human/H) + return 1 + +/datum/species/proc/handle_dna(var/mob/living/carbon/C, var/remove) //Handles DNA mutations, as that doesn't work at init. Make sure you call genemutcheck on any blocks changed here + return + +// Used for species-specific names (Vox, etc) +/datum/species/proc/makeName(var/gender,var/mob/living/carbon/human/H=null) + if(gender==FEMALE) return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) + else return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + +/datum/species/proc/handle_death(var/mob/living/carbon/human/H) //Handles any species-specific death events (such as dionaea nymph spawns). + return + +/datum/species/proc/handle_attack_hand(var/mob/living/carbon/human/H, var/mob/living/carbon/human/M) //Handles any species-specific attackhand events. + return + +/datum/species/proc/say_filter(mob/M, message, datum/language/speaking) + return message + +/datum/species/proc/equip(var/mob/living/carbon/human/H) + return + +/datum/species/proc/can_understand(var/mob/other) + return + +// Called in life() when the mob has no client. +/datum/species/proc/handle_npc(var/mob/living/carbon/human/H) + return + +//Species unarmed attacks + +/datum/unarmed_attack + var/attack_verb = list("attack") // Empty hand hurt intent verb. + var/damage = 0 // How much flat bonus damage an attack will do. This is a *bonus* guaranteed damage amount on top of the random damage attacks do. + var/attack_sound = "punch" + var/miss_sound = 'sound/weapons/punchmiss.ogg' + var/sharp = 0 + var/edge = 0 + +/datum/unarmed_attack/punch + attack_verb = list("punch") + +/datum/unarmed_attack/punch/weak + attack_verb = list("flail") + +/datum/unarmed_attack/diona + attack_verb = list("lash", "bludgeon") + +/datum/unarmed_attack/claws + attack_verb = list("scratch", "claw") + attack_sound = 'sound/weapons/slice.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + sharp = 1 + edge = 1 + +/datum/unarmed_attack/claws/armalis + attack_verb = list("slash", "claw") + damage = 6 + +/datum/species/proc/handle_can_equip(obj/item/I, slot, disable_warning = 0, mob/living/carbon/human/user) + return 0 + +/datum/species/proc/handle_vision(mob/living/carbon/human/H) + // Right now this just handles blind, blurry, and similar states + if(H.blinded || H.eye_blind) + H.overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + H.throw_alert("blind", /obj/screen/alert/blind) + else + H.clear_fullscreen("blind") + H.clear_alert("blind") + + + if(H.disabilities & NEARSIGHTED) //this looks meh but saves a lot of memory by not requiring to add var/prescription + if(H.glasses) //to every /obj/item + var/obj/item/clothing/glasses/G = H.glasses + if(G.prescription) + H.clear_fullscreen("nearsighted") + else + H.overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1) + else + H.overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1) + else + H.clear_fullscreen("nearsighted") + + if(H.eye_blurry) + H.overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry) + else + H.clear_fullscreen("blurry") + + if(H.druggy) + H.overlay_fullscreen("high", /obj/screen/fullscreen/high) + H.throw_alert("high", /obj/screen/alert/high) + else + H.clear_fullscreen("high") + H.clear_alert("high") + +/datum/species/proc/handle_hud_icons(mob/living/carbon/human/H) + if(!H.client) + return + if(H.healths) + if(H.stat == DEAD) + H.healths.icon_state = "health7" + else + switch(H.hal_screwyhud) + if(1) H.healths.icon_state = "health6" + if(2) H.healths.icon_state = "health7" + if(5) H.healths.icon_state = "health0" + else + switch(100 - ((flags & NO_PAIN) ? 0 : H.traumatic_shock) - H.staminaloss) + if(100 to INFINITY) H.healths.icon_state = "health0" + if(80 to 100) H.healths.icon_state = "health1" + if(60 to 80) H.healths.icon_state = "health2" + if(40 to 60) H.healths.icon_state = "health3" + if(20 to 40) H.healths.icon_state = "health4" + if(0 to 20) H.healths.icon_state = "health5" + else H.healths.icon_state = "health6" + + if(H.healthdoll) + if(H.stat == DEAD) + H.healthdoll.icon_state = "healthdoll_DEAD" + if(H.healthdoll.overlays.len) + H.healthdoll.overlays.Cut() + else + var/list/new_overlays = list() + var/list/cached_overlays = H.healthdoll.cached_healthdoll_overlays + // Use the dead health doll as the base, since we have proper "healthy" overlays now + H.healthdoll.icon_state = "healthdoll_DEAD" + for(var/obj/item/organ/external/O in H.organs) + var/damage = O.burn_dam + O.brute_dam + var/comparison = (O.max_damage/5) + var/icon_num = 0 + if(damage) + icon_num = 1 + if(damage > (comparison)) + icon_num = 2 + if(damage > (comparison*2)) + icon_num = 3 + if(damage > (comparison*3)) + icon_num = 4 + if(damage > (comparison*4)) + icon_num = 5 + new_overlays += "[O.limb_name][icon_num]" + H.healthdoll.overlays += (new_overlays - cached_overlays) + H.healthdoll.overlays -= (cached_overlays - new_overlays) + H.healthdoll.cached_healthdoll_overlays = new_overlays + + switch(H.nutrition) + if(NUTRITION_LEVEL_FULL to INFINITY) + H.throw_alert("nutrition", /obj/screen/alert/fat) + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL) + H.clear_alert("nutrition") + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) + H.throw_alert("nutrition", /obj/screen/alert/hungry) + else + H.throw_alert("nutrition", /obj/screen/alert/starving) + return 1 + +/* +Returns the path corresponding to the corresponding organ +It'll return null if the organ doesn't correspond, so include null checks when using this! +*/ +//Fethas Todo:Do i need to redo this? +/datum/species/proc/return_organ(var/organ_slot) + if(!(organ_slot in has_organ)) + return null + return has_organ[organ_slot] + + +/datum/species/proc/update_sight(mob/living/carbon/human/H) + H.sight = initial(H.sight) + H.see_in_dark = darksight + H.see_invisible = invis_sight + + if(H.client.eye != H) + var/atom/A = H.client.eye + if(A.update_remote_sight(H)) //returns 1 if we override all other sight updates. + return + + if(H.mind && H.mind.vampire) + if(H.mind.vampire.get_ability(/datum/vampire_passive/full)) + H.sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + H.see_in_dark = 8 + H.see_invisible = SEE_INVISIBLE_MINIMUM + else if(H.mind.vampire.get_ability(/datum/vampire_passive/vision)) + H.sight |= SEE_MOBS + + for(var/obj/item/organ/internal/cyberimp/eyes/E in H.internal_organs) + H.sight |= E.vision_flags + if(E.dark_view) + H.see_in_dark = E.dark_view + if(E.see_invisible) + H.see_invisible = min(H.see_invisible, E.see_invisible) + + + if(H.seer == 1) + var/obj/effect/rune/R = locate() in H.loc + if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) + H.see_invisible = SEE_INVISIBLE_OBSERVER + else + H.see_invisible = SEE_INVISIBLE_LIVING + H.seer = 0 + + var/lesser_darkview_bonus = INFINITY + // my glasses, I can't see without my glasses + if(H.glasses) + var/obj/item/clothing/glasses/G = H.glasses + H.sight |= G.vision_flags + lesser_darkview_bonus = G.darkness_view + if(G.invis_override) + H.see_invisible = G.invis_override + else + H.see_invisible = min(G.invis_view, H.see_invisible) + + // better living through hat trading + if(H.head) + if(istype(H.head, /obj/item/clothing/head)) + var/obj/item/clothing/head/hat = H.head + H.sight |= hat.vision_flags + + if(hat.darkness_view) // Pick the lowest of the two darkness_views between the glasses and helmet. + lesser_darkview_bonus = min(hat.darkness_view,lesser_darkview_bonus) + + if(hat.helmet_goggles_invis_view) + H.see_invisible = min(hat.helmet_goggles_invis_view, H.see_invisible) + + if(istype(H.back, /obj/item/weapon/rig)) ///aghhh so snowflakey + var/obj/item/weapon/rig/rig = H.back + if(rig.visor) + if(!rig.helmet || (H.head && rig.helmet == H.head)) + if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) + var/obj/item/clothing/glasses/G = rig.visor.vision.glasses + if(istype(G)) + H.see_in_dark = (G.darkness_view ? G.darkness_view : darksight) // Otherwise we keep our darkness view with togglable nightvision. + if(G.vision_flags) // MESONS + H.sight |= G.vision_flags + + H.see_invisible = min(G.invis_view, H.see_invisible) + + if(lesser_darkview_bonus != INFINITY) + H.see_in_dark = max(lesser_darkview_bonus, H.see_in_dark) + + if(H.vision_type) + H.see_in_dark = max(H.see_in_dark, H.vision_type.see_in_dark, darksight) + H.see_invisible = H.vision_type.see_invisible + if(H.vision_type.light_sensitive) + H.weakeyes = 1 + H.sight |= H.vision_type.sight_flags + + if(XRAY in H.mutations) + H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + H.see_in_dark = max(H.see_in_dark, 8) + H.see_invisible = SEE_INVISIBLE_MINIMUM + + if(H.see_override) //Override all + H.see_invisible = H.see_override