diff --git a/.travis.yml b/.travis.yml index 7cccc5264b..fcf6fa2e39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,12 @@ #pretending we're C because otherwise ruby will initialize, even with "language: dm". language: c -sudo: false env: global: + - BASENAME="polaris" # $BASENAME.dmb, $BASENAME.dme, etc. - BYOND_MAJOR="513" - - BYOND_MINOR="1502" + - BYOND_MINOR="1513" - MACRO_COUNT=4 - matrix: - - TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0" - - TEST_DEFINE="AWAY_MISSION_TEST" TEST_FILE="code/_away_mission_tests.dm" RUN="0" - - TEST_DEFINE="UNIT_TEST" TEST_FILE="code/_unit_tests.dm" RUN="1" cache: directories: @@ -22,30 +18,38 @@ addons: - libc6-i386 - libgcc1:i386 - libstdc++6:i386 + - libssl-dev:i386 -before_script: - - chmod +x ./install-byond.sh - - ./install-byond.sh +before_install: + - chmod -R +x ./tools/travis install: - - pip install --user PyYaml -q - - pip install --user beautifulsoup4 -q + - ./tools/travis/install_byond.sh + +before_script: + - shopt -s globstar script: - - shopt -s globstar - - (! grep 'step_[xy]' maps/**/*.dmm) - - (! grep -Pn '( |\t|;|{)tag( ?)=' maps/**/*.dmm) - - (! find nano/templates/ -type f -exec md5sum {} + | sort | uniq -D -w 32 | grep nano) - - (! grep -En "<\s*span\s+class\s*=\s*('[^'>]+|[^'>]+')\s*>" **/*.dm) - - awk -f tools/indentation.awk **/*.dm - - md5sum -c - <<< "88490b460c26947f5ec1ab1bb9fa9f17 *html/changelogs/example.yml" - - (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ]) - - source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup - - python tools/TagMatcher/tag-matcher.py ../.. - # Run our test - - cp config/example/* config/ - - echo "#define ${TEST_DEFINE} 1" > ${TEST_FILE} - - DreamMaker polaris.dme - - if [ $RUN -eq 1 ]; then DreamDaemon polaris.dmb -invisible -trusted -core 2>&1 | tee log.txt; fi - - if [ $RUN -eq 1 ]; then grep "All Unit Tests Passed" log.txt; fi + - ./tools/travis/compile_and_run.sh +# Build-specific settings +jobs: + include: + - stage: "File Tests" #This is the odd man out, with specific installs and stuff. + name: "Validate Files" + install: #Need python for some of the tag matching stuff + - pip install --user PyYaml -q + - pip install --user beautifulsoup4 -q + script: ./tools/travis/validate_files.sh + addons: + apt: + packages: ~ # Don't need any packages for this + - stage: "Unit Tests" + env: TEST_DEFINE="UNIT_TEST" TEST_FILE="code/_unit_tests.dm" RUN="1" + name: "Compile normally (unit tests)" + - stage: "Isolation Tests" + env: TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0" + name: "Compile POIs (no run)" + - env: TEST_DEFINE="AWAY_MISSION_TEST" TEST_FILE="code/_away_mission_tests.dm" RUN="0" + name: "Compile away missions (no run)" + diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 4698c613a9..cc280d97cc 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,7 +1,7 @@ /obj/machinery/atmospherics/binary dir = SOUTH initialize_directions = SOUTH|NORTH - use_power = 1 + use_power = USE_POWER_IDLE var/datum/gas_mixture/air1 var/datum/gas_mixture/air2 diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index 41ba2cf705..2f276160fe 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -20,7 +20,7 @@ level = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -214,10 +214,10 @@ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) return 0 if(signal.data["power"]) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"]) - use_power = !use_power + update_use_power(!use_power) if(signal.data["direction"]) pump_direction = text2num(signal.data["direction"]) diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 92c26fb280..191e6291ea 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -12,7 +12,7 @@ name = "pressure regulator" desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." - use_power = 0 + use_power = USE_POWER_OFF var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). var/target_pressure = ONE_ATMOSPHERE diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index b4b8187f45..e241cc944b 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -26,7 +26,7 @@ Thus, the two variables affect pump operation are set in New(): //var/max_volume_transfer = 10000 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -47,7 +47,7 @@ Thus, the two variables affect pump operation are set in New(): /obj/machinery/atmospherics/binary/pump/on icon_state = "map_on" - use_power = 1 + use_power = USE_POWER_IDLE /obj/machinery/atmospherics/binary/pump/update_icon() @@ -160,12 +160,12 @@ Thus, the two variables affect pump operation are set in New(): if(signal.data["power"]) if(text2num(signal.data["power"])) - use_power = 1 + update_use_power(USE_POWER_IDLE) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("power_toggle" in signal.data) - use_power = !use_power + update_use_power(!use_power) if(signal.data["set_output_pressure"]) target_pressure = between( @@ -199,7 +199,7 @@ Thus, the two variables affect pump operation are set in New(): if(..()) return 1 if(href_list["power"]) - use_power = !use_power + update_use_power(!use_power) switch(href_list["set_press"]) if ("min") diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index fa0aaf4bd0..8e82750588 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -11,7 +11,7 @@ power_rating = 15000 //15000 W ~ 20 HP /obj/machinery/atmospherics/binary/pump/high_power/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_on" /obj/machinery/atmospherics/binary/pump/high_power/update_icon() diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm index 074c6c528d..9f0862d800 100644 --- a/code/ATMOSPHERICS/components/omni_devices/filter.dm +++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm @@ -11,7 +11,7 @@ var/datum/omni_port/input var/datum/omni_port/output - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -161,13 +161,13 @@ switch(href_list["command"]) if("power") if(!configuring) - use_power = !use_power + update_use_power(!use_power) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("configure") configuring = !configuring if(configuring) - use_power = 0 + update_use_power(USE_POWER_OFF) //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on if(configuring && !use_power) diff --git a/code/ATMOSPHERICS/components/omni_devices/mixer.dm b/code/ATMOSPHERICS/components/omni_devices/mixer.dm index 47c78427f0..0210d09e69 100644 --- a/code/ATMOSPHERICS/components/omni_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/omni_devices/mixer.dm @@ -6,7 +6,7 @@ icon_state = "map_mixer" pipe_state = "omni_mixer" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 3700 //3700 W ~ 5 HP @@ -178,13 +178,13 @@ switch(href_list["command"]) if("power") if(!configuring) - use_power = !use_power + update_use_power(!use_power) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("configure") configuring = !configuring if(configuring) - use_power = 0 + update_use_power(USE_POWER_OFF) //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on if(configuring && !use_power) diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm index 1a82bab1e2..92075b7440 100644 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -5,7 +5,7 @@ name = "omni device" icon = 'icons/atmos/omni_devices.dmi' icon_state = "base" - use_power = 1 + use_power = USE_POWER_IDLE initialize_directions = 0 construction_type = /obj/item/pipe/quaternary level = 1 @@ -67,7 +67,7 @@ last_flow_rate = 0 if(error_check()) - use_power = 0 + update_use_power(USE_POWER_OFF) if((stat & (NOPOWER|BROKEN)) || !use_power) return 0 diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index b30bc9b038..fd5033100b 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -18,7 +18,7 @@ var/datum/pipe_network/network var/on = 0 - use_power = 0 + use_power = USE_POWER_OFF level = 1 /obj/machinery/atmospherics/portables_connector/init_dir() diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e900303e22..21e1a34d33 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -9,7 +9,7 @@ name = "Gas filter" desc = "Filters one type of gas from an input, and pushes it out the side." - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP @@ -73,7 +73,7 @@ icon_state += use_power ? "on" : "off" else icon_state += "off" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/atmospherics/trinary/atmos_filter/process() ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 8bf3d3477c..62b4b763b8 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -8,7 +8,7 @@ name = "Gas mixer" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP @@ -35,7 +35,7 @@ icon_state += use_power ? "on" : "off" else icon_state += "off" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/atmospherics/trinary/mixer/New() ..() @@ -114,7 +114,7 @@ /obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) if(..()) return 1 if(href_list["power"]) - use_power = !use_power + update_use_power(!use_power) if(href_list["set_press"]) var/max_flow_rate = min(air1.volume, air2.volume) var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 394dbceeda..10d7403541 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,7 +1,7 @@ /obj/machinery/atmospherics/trinary dir = SOUTH initialize_directions = SOUTH|NORTH|WEST - use_power = 0 + use_power = USE_POWER_OFF pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF var/mirrored = FALSE diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm index 0fddd6d8e8..ba092489a9 100644 --- a/code/ATMOSPHERICS/components/unary/cold_sink.dm +++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm @@ -8,7 +8,7 @@ icon_state = "freezer_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 5 // 5 Watts for thermostat related circuitry circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler @@ -99,7 +99,7 @@ if(..()) return 1 if(href_list["toggleStatus"]) - use_power = !use_power + update_use_power(!use_power) update_icon() if(href_list["temp"]) var/amount = text2num(href_list["temp"]) diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm index 7a2caa1d9d..8e4d474270 100644 --- a/code/ATMOSPHERICS/components/unary/heat_source.dm +++ b/code/ATMOSPHERICS/components/unary/heat_source.dm @@ -8,7 +8,7 @@ icon_state = "heater_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 5 //5 Watts for thermostat related circuitry circuit = /obj/item/weapon/circuitboard/unary_atmos/heater @@ -119,7 +119,7 @@ if(..()) return 1 if(href_list["toggleStatus"]) - use_power = !use_power + update_use_power(!use_power) update_icon() if(href_list["temp"]) var/amount = text2num(href_list["temp"]) diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index 5e62fe5802..3c342c3cdb 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -10,7 +10,7 @@ name = "air injector" desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 15000 //15000 W ~ 20 HP @@ -132,10 +132,10 @@ return 0 if(signal.data["power"]) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"]) - use_power = !use_power + update_use_power(!use_power) if(signal.data["inject"]) spawn inject() @@ -160,7 +160,7 @@ /obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) to_chat(user, "You toggle \the [src].") injecting = !injecting - use_power = injecting + update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) update_icon() /obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index f30def6e07..07518019d2 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -14,7 +14,7 @@ name = "Air Vent" desc = "Has a valve and pump attached to it" - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -50,18 +50,18 @@ var/datum/looping_sound/air_pump/soundloop /obj/machinery/atmospherics/unary/vent_pump/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_out" /obj/machinery/atmospherics/unary/vent_pump/siphon pump_direction = 0 /obj/machinery/atmospherics/unary/vent_pump/siphon/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_in" /obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_in" external_pressure_bound = 0 external_pressure_bound_default = 0 @@ -173,7 +173,7 @@ return 1 if (!node) - use_power = 0 + update_use_power(USE_POWER_OFF) if(!can_pump()) return 0 @@ -295,10 +295,10 @@ pump_direction = 1 if(signal.data["power"] != null) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"] != null) - use_power = !use_power + update_use_power(!use_power) if(signal.data["checks"] != null) if (signal.data["checks"] == "default") diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 34897b66ef..6e3f9ef042 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -5,7 +5,7 @@ name = "Air Scrubber" desc = "Has a valve and pump attached to it" - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -29,7 +29,7 @@ var/radio_filter_in /obj/machinery/atmospherics/unary/vent_scrubber/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_scrubber_on" /obj/machinery/atmospherics/unary/vent_scrubber/New() @@ -135,7 +135,7 @@ return 1 if (!node) - use_power = 0 + update_use_power(USE_POWER_OFF) //broadcast_status() if(!use_power || (stat & (NOPOWER|BROKEN))) return 0 @@ -180,21 +180,21 @@ return 0 if(signal.data["power"] != null) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"] != null) - use_power = !use_power + update_use_power(!use_power) if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing panic = text2num(signal.data["panic_siphon"]) if(panic) - use_power = 1 + update_use_power(USE_POWER_IDLE) scrubbing = 0 else scrubbing = 1 if(signal.data["toggle_panic_siphon"] != null) panic = !panic if(panic) - use_power = 1 + update_use_power(USE_POWER_IDLE) scrubbing = 0 else scrubbing = 1 diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index d1bb92fac7..e627fcbb46 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -9,7 +9,7 @@ var/leaking = FALSE // Do not set directly, use set_leaking(TRUE/FALSE) layer = PIPES_LAYER - use_power = 0 + use_power = USE_POWER_OFF pipe_flags = 0 // Does not have PIPING_DEFAULT_LAYER_ONLY flag. diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index 229d0b94ad..5ac591042b 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -11,6 +11,9 @@ //#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. #define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! +// Comment/Uncomment this to turn off/on shuttle code debugging logs +#define DEBUG_SHUTTLES + // If we are doing the map test build, do not include the main maps, only the submaps. #if MAP_TEST #define USING_MAP_DATUM /datum/map diff --git a/code/__defines/_lists.dm b/code/__defines/_lists.dm index 2a570f6f42..016d1a89d4 100644 --- a/code/__defines/_lists.dm +++ b/code/__defines/_lists.dm @@ -18,6 +18,9 @@ #define LAZYOR(L, I) if(!L) { L = list(); } L |= I; +// Adds I to L, initalizing L if necessary, if I is not already in L +#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I; + #define LAZYFIND(L, V) L ? L.Find(V) : 0 // Reads I from L safely - Works with both associative and traditional lists. diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 3676071d7c..f7c2457b98 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -11,6 +11,11 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define DOOR_CRUSH_DAMAGE 20 #define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien. +// Constants for machine's use_power +#define USE_POWER_OFF 0 // No continuous power use +#define USE_POWER_IDLE 1 // Machine is using power at its idle power level +#define USE_POWER_ACTIVE 2 // Machine is using power at its active power level + // Channel numbers for power. #define EQUIP 1 #define LIGHT 2 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index ceaeda8c48..b96c2cca84 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -106,6 +106,7 @@ #define FORCE_LAUNCH 2 #define WAIT_ARRIVE 3 #define WAIT_FINISH 4 +#define DO_AUTOPILOT 5 // Setting this much higher than 1024 could allow spammers to DOS the server easily. #define MAX_MESSAGE_LEN 1024 diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index ab85326658..278a2064db 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -26,6 +26,7 @@ #define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) #define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) qdel(item); item = null +#define QDEL_NULL_LIST QDEL_LIST_NULL #define QDEL_LIST_NULL(x) if(x) { for(var/y in x) { qdel(y) } ; x = null } #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } #define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) diff --git a/code/__defines/xenoarcheaology.dm b/code/__defines/xenoarcheaology.dm index 8a4988b78c..e4b0a3935a 100644 --- a/code/__defines/xenoarcheaology.dm +++ b/code/__defines/xenoarcheaology.dm @@ -37,7 +37,11 @@ #define ARCHAEO_ALIEN_BOAT 37 #define ARCHAEO_IMPERION_CIRCUIT 38 #define ARCHAEO_TELECUBE 39 -#define MAX_ARCHAEO 39 +#define ARCHAEO_BATTERY 40 +#define ARCHAEO_SYRINGE 41 +#define ARCHAEO_RING 42 +#define ARCHAEO_CLUB 43 +#define MAX_ARCHAEO 43 #define DIGSITE_GARDEN 1 #define DIGSITE_ANIMAL 2 diff --git a/code/_global_vars/lists/species.dm b/code/_global_vars/lists/species.dm index fec8e4a069..151512d570 100644 --- a/code/_global_vars/lists/species.dm +++ b/code/_global_vars/lists/species.dm @@ -1,6 +1,8 @@ //Languages/species/whitelist. GLOBAL_LIST_INIT(all_species, list()) GLOBAL_LIST_INIT(all_languages, list()) +GLOBAL_LIST_INIT(language_name_conflicts, list()) GLOBAL_LIST_INIT(language_keys, list()) // Table of say codes for all languages +GLOBAL_LIST_INIT(language_key_conflicts, list()) GLOBAL_LIST_INIT(whitelisted_species, list(SPECIES_HUMAN)) // Species that require a whitelist check. GLOBAL_LIST_INIT(playable_species, list(SPECIES_HUMAN)) // A list of ALL playable species, whitelisted, latejoin or otherwise. diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm index 1eb39e30cd..48e9d1ba64 100644 --- a/code/_helpers/_lists.dm +++ b/code/_helpers/_lists.dm @@ -308,6 +308,13 @@ proc/listclearnulls(list/list) else L[key] = temp[key] +// Return a list of the values in an assoc list (including null) +/proc/list_values(var/list/L) + var/list/V = list() + V.len = L.len // Preallocate! + for(var/i in 1 to L.len) + V[i] = L[L[i]] // We avoid += in case the value is itself a list + return V //Mergesort: divides up the list into halves to begin the sort /proc/sortKey(var/list/client/L, var/order = 1) diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 5c7a56c976..50cf502639 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -153,17 +153,30 @@ var/global/list/string_slot_flags = list( var/datum/job/J = new T joblist[J.title] = J - //Languages and species. + //Languages paths = typesof(/datum/language)-/datum/language for(var/T in paths) var/datum/language/L = new T - GLOB.all_languages[L.name] = L + if (isnull(GLOB.all_languages[L.name])) + GLOB.all_languages[L.name] = L + else + log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") + if(isnull(GLOB.language_name_conflicts[L.name])) + GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) + GLOB.language_name_conflicts[L.name] += L for (var/language_name in GLOB.all_languages) var/datum/language/L = GLOB.all_languages[language_name] if(!(L.flags & NONGLOBAL)) - GLOB.language_keys[lowertext(L.key)] = L + if(isnull(GLOB.language_keys[L.key])) + GLOB.language_keys[L.key] = L + else + log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") + if(isnull(GLOB.language_key_conflicts[L.key])) + GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) + GLOB.language_key_conflicts[L.key] += L + //Species var/rkey = 0 paths = typesof(/datum/species) for(var/T in paths) diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index 8a67d877e9..81abf0f155 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -52,4 +52,120 @@ if(EAST) return locate(world.maxx - clearance, rand(clearance, world.maxy - clearance), Z) if(WEST) - return locate(clearance, rand(clearance, world.maxy - clearance), Z) \ No newline at end of file + return locate(clearance, rand(clearance, world.maxy - clearance), Z) + +/* + Turf manipulation +*/ + +//Returns an assoc list that describes how turfs would be changed if the +//turfs in turfs_src were translated by shifting the src_origin to the dst_origin +/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src) + var/list/turf_map = list() + for(var/turf/source in turfs_src) + var/x_pos = (source.x - src_origin.x) + var/y_pos = (source.y - src_origin.y) + var/z_pos = (source.z - src_origin.z) + + var/turf/target = locate(dst_origin.x + x_pos, dst_origin.y + y_pos, dst_origin.z + z_pos) + if(!target) + error("Null turf in translation @ ([dst_origin.x + x_pos], [dst_origin.y + y_pos], [dst_origin.z + z_pos])") + turf_map[source] = target //if target is null, preserve that information in the turf map + + return turf_map + +/proc/translate_turfs(var/list/translation, var/area/base_area = null, var/turf/base_turf) + for(var/turf/source in translation) + + var/turf/target = translation[source] + + if(target) + if(base_area) ChangeArea(target, get_area(source)) + var/leave_turf = base_turf ? base_turf : get_base_turf_by_area(base_area ? base_area : source) + translate_turf(source, target, leave_turf) + if(base_area) ChangeArea(source, base_area) + + //change the old turfs (Currently done by translate_turf for us) + //for(var/turf/source in translation) + // source.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(source), 1, 1) + +// Parmaters for stupid historical reasons are: +// T - Origin +// B - Destination +/proc/translate_turf(var/turf/T, var/turf/B, var/turftoleave = null) + + //You can stay, though. + if(istype(T,/turf/space)) + error("Tried to translate a space turf: src=[log_info_line(T)] dst=[log_info_line(B)]") + return FALSE // TODO - Is this really okay to do nothing? + + var/turf/X //New Destination Turf + + //Are we doing shuttlework? Just to save another type check later. + var/shuttlework = 0 + + //Shuttle turfs handle their own fancy moving. + if(istype(T,/turf/simulated/shuttle)) + shuttlework = 1 + var/turf/simulated/shuttle/SS = T + if(!SS.landed_holder) SS.landed_holder = new(turf = SS) + X = SS.landed_holder.land_on(B) + + //Generic non-shuttle turf move. + else + var/old_dir1 = T.dir + var/old_icon_state1 = T.icon_state + var/old_icon1 = T.icon + var/old_underlays = T.underlays.Copy() + var/old_decals = T.decals ? T.decals.Copy() : null + + X = B.ChangeTurf(T.type) + X.set_dir(old_dir1) + X.icon_state = old_icon_state1 + X.icon = old_icon1 + X.copy_overlays(T, TRUE) + X.underlays = old_underlays + X.decals = old_decals + + //Move the air from source to dest + var/turf/simulated/ST = T + if(istype(ST) && ST.zone) + var/turf/simulated/SX = X + if(!SX.air) + SX.make_air() + SX.air.copy_from(ST.zone.air) + ST.zone.remove(ST) + + var/z_level_change = FALSE + if(T.z != X.z) + z_level_change = TRUE + + //Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf. + for(var/obj/O in T) + if(O.simulated) + O.loc = X + O.update_light() + if(z_level_change) // The objects still need to know if their z-level changed. + O.onTransitZ(T.z, X.z) + + //Move the mobs unless it's an AI eye or other eye type. + for(var/mob/M in T) + if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable + M.loc = X + + if(z_level_change) // Same goes for mobs. + M.onTransitZ(T.z, X.z) + + if(istype(M, /mob/living)) + var/mob/living/LM = M + LM.check_shadow() // Need to check their Z-shadow, which is normally done in forceMove(). + + if(shuttlework) + var/turf/simulated/shuttle/SS = T + SS.landed_holder.leave_turf(turftoleave) + else if(turftoleave) + T.ChangeTurf(turftoleave) + else + T.ChangeTurf(get_base_turf_by_area(T)) + + return TRUE diff --git a/code/_macros.dm b/code/_macros.dm index fad5920eb9..d5878c6259 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -26,6 +26,8 @@ #define qdel_null(x) if(x) { qdel(x) ; x = null } +#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) + #define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) #define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 46e3349615..f9e8506108 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -276,12 +276,12 @@ tankcheck = list(C.r_hand, C.l_hand, C.back) // Rigs are a fucking pain since they keep an air tank in nullspace. - if(istype(C.back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = C.back - if(rig.air_supply && !rig.offline) + var/obj/item/weapon/rig/Rig = C.get_rig() + if(Rig) + if(Rig.air_supply && !Rig.offline) from = "in" nicename |= "hardsuit" - tankcheck |= rig.air_supply + tankcheck |= Rig.air_supply for(var/i=1, i docking controller program, mostly for init purposes. + + var/list/landmarks_awaiting_sector = list() // Stores automatic landmarks that are waiting for a sector to finish loading. + var/list/landmarks_still_needed = list() // Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered. + var/list/shuttles_to_initialize // A queue for shuttles to initialize at the appropriate time. + var/list/sectors_to_initialize // Used to find all sector objects at the appropriate time. + var/block_init_queue = TRUE // Block initialization of new shuttles/sectors + + var/tmp/list/current_run // Shuttles remaining to process this fire() tick + +/datum/controller/subsystem/shuttles/PreInit() + global.shuttle_controller = src // TODO - Remove this! Change everything to point at SSshuttles intead /datum/controller/subsystem/shuttles/Initialize(timeofday) - global.shuttle_controller = src - setup_shuttle_docks() - for(var/I in docks_init_callbacks) - var/datum/callback/cb = I - cb.InvokeAsync() - LAZYCLEARLIST(docks_init_callbacks) - docks_init_callbacks = null + last_landmark_registration_time = world.time + // Find all declared shuttle datums and initailize them. (Okay, queue them for initialization a few lines further down) + for(var/shuttle_type in subtypesof(/datum/shuttle)) // This accounts for most shuttles, though away maps can queue up more. + var/datum/shuttle/shuttle = shuttle_type + if(initial(shuttle.category) == shuttle_type) + continue // Its an "abstract class" datum, not for a real shuttle. + if(!initial(shuttle.defer_initialisation)) // Skip if it asks not to be initialized at startup. + LAZYDISTINCTADD(shuttles_to_initialize, shuttle_type) + block_init_queue = FALSE + process_init_queues() return ..() /datum/controller/subsystem/shuttles/fire(resumed = 0) - do_process_shuttles(resumed) - -/datum/controller/subsystem/shuttles/stat_entry() - var/msg = list() - msg += "AS:[shuttles.len]|" - msg += "PS:[process_shuttles.len]|" - ..(jointext(msg, null)) - -/datum/controller/subsystem/shuttles/proc/do_process_shuttles(resumed = 0) if (!resumed) src.current_run = process_shuttles.Copy() - var/list/current_run = src.current_run // Cache for sanic speed - while(current_run.len) - var/datum/shuttle/S = current_run[current_run.len] - current_run.len-- - if(istype(S) && !QDELETED(S)) - if(istype(S, /datum/shuttle/ferry)) // Ferry shuttles get special treatment - var/datum/shuttle/ferry/F = S - if(F.process_state || F.always_process) - F.process() - else - S.process() - else + var/list/working_shuttles = src.current_run // Cache for sanic speed + while(working_shuttles.len) + var/datum/shuttle/S = working_shuttles[working_shuttles.len] + working_shuttles.len-- + if(!istype(S) || QDELETED(S)) + error("Bad entry in SSshuttles.process_shuttles - [log_info_line(S)] ") process_shuttles -= S + continue + // NOTE - In old system, /datum/shuttle/ferry was processed only if (F.process_state || F.always_process) + if(S.process_state && (S.process(wait, times_fired, src) == PROCESS_KILL)) + process_shuttles -= S + if(MC_TICK_CHECK) return -// This should be called after all the machines and radio frequencies have been properly initialized -/datum/controller/subsystem/shuttles/proc/setup_shuttle_docks() - // Find all declared shuttle datums and initailize them. - for(var/shuttle_type in subtypesof(/datum/shuttle)) - var/datum/shuttle/shuttle = shuttle_type - if(initial(shuttle.category) == shuttle_type) - continue +/datum/controller/subsystem/shuttles/proc/process_init_queues() + if(block_init_queue) + return + initialize_shuttles() + initialize_sectors() + +// Initializes all shuttles in shuttles_to_initialize +/datum/controller/subsystem/shuttles/proc/initialize_shuttles() + var/list/shuttles_made = list() + for(var/shuttle_type in shuttles_to_initialize) + var/shuttle = initialize_shuttle(shuttle_type) + if(shuttle) + shuttles_made += shuttle + hook_up_motherships(shuttles_made) + shuttles_to_initialize = null + +/datum/controller/subsystem/shuttles/proc/initialize_sectors() + for(var/sector in sectors_to_initialize) + initialize_sector(sector) + sectors_to_initialize = null + +/datum/controller/subsystem/shuttles/proc/register_landmark(shuttle_landmark_tag, obj/effect/shuttle_landmark/shuttle_landmark) + if (registered_shuttle_landmarks[shuttle_landmark_tag]) + CRASH("Attempted to register shuttle landmark with tag [shuttle_landmark_tag], but it is already registered!") + if (istype(shuttle_landmark)) + registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark + last_landmark_registration_time = world.time + + // TODO - Uncomment once overmap sectors are ported + //var/obj/effect/overmap/visitable/O = landmarks_still_needed[shuttle_landmark_tag] + //if(O) //These need to be added to sectors, which we handle. + // try_add_landmark_tag(shuttle_landmark_tag, O) + // landmarks_still_needed -= shuttle_landmark_tag + //else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically + // O = map_sectors["[shuttle_landmark.z]"] + // O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark) + +/datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag) + return registered_shuttle_landmarks[shuttle_landmark_tag] + +//Checks if the given sector's landmarks have initialized; if so, registers them with the sector, if not, marks them for assignment after they come in. +//Also adds automatic landmarks that were waiting on their sector to spawn. +/datum/controller/subsystem/shuttles/proc/initialize_sector(obj/effect/overmap/visitable/given_sector) + return // TODO - Uncomment once overmap sectors are ported +// given_sector.populate_sector_objects() // This is a late init operation that sets up the sector's map_z and does non-overmap-related init tasks. + +// for(var/landmark_tag in given_sector.initial_generic_waypoints) +// if(!try_add_landmark_tag(landmark_tag, given_sector)) +// landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is. + +// for(var/shuttle_name in given_sector.initial_restricted_waypoints) +// for(var/landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) +// if(!try_add_landmark_tag(landmark_tag, given_sector)) +// landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is. + +// var/landmarks_to_check = landmarks_awaiting_sector.Copy() +// for(var/thing in landmarks_to_check) +// var/obj/effect/shuttle_landmark/automatic/landmark = thing +// if(landmark.z in given_sector.map_z) +// given_sector.add_landmark(landmark, landmark.shuttle_restricted) +// landmarks_awaiting_sector -= landmark + +// TODO - Uncomment once overmap sectors are ported +//// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet) +///datum/controller/subsystem/shuttles/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector) +// var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag) +// if(!landmark) +// return + +// if(landmark.landmark_tag in given_sector.initial_generic_waypoints) +// given_sector.add_landmark(landmark) +// . = 1 +// for(var/shuttle_name in given_sector.initial_restricted_waypoints) +// if(landmark.landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) +// given_sector.add_landmark(landmark, shuttle_name) +// . = 1 + +/datum/controller/subsystem/shuttles/proc/initialize_shuttle(var/shuttle_type) + var/datum/shuttle/shuttle = shuttle_type + if(initial(shuttle.category) != shuttle_type) // Skip if its an "abstract class" datum shuttle = new shuttle() - shuttle.init_docking_controllers() - shuttle.dock() //makes all shuttles docked to something at round start go into the docked state - CHECK_TICK + shuttle_areas |= shuttle.shuttle_area + log_debug("Initialized shuttle [shuttle] ([shuttle.type])") + return shuttle + // Historical note: No need to call shuttle.init_docking_controllers(), controllers register themselves + // and shuttles fetch refs in New(). Shuttles also dock() themselves in new if they want. - for(var/obj/machinery/embedded_controller/C in machines) - if(istype(C.program, /datum/computer/file/embedded_program/docking)) - C.program.tag = null //clear the tags, 'cause we don't need 'em anymore - docks_initialized = TRUE +// TODO - Leshana to hook up more of this when overmap is ported. +/datum/controller/subsystem/shuttles/proc/hook_up_motherships(shuttles_list) + for(var/datum/shuttle/S in shuttles_list) + if(S.mothershuttle && !S.motherdock) + var/datum/shuttle/mothership = shuttles[S.mothershuttle] + if(mothership) + S.motherdock = S.current_location.landmark_tag + mothership.shuttle_area |= S.shuttle_area + else + error("Shuttle [S] was unable to find mothership [mothership]!") -// Register a callback that will be invoked once the shuttles have been initialized -/datum/controller/subsystem/shuttles/proc/OnDocksInitialized(datum/callback/cb) - if(!docks_initialized) - LAZYADD(docks_init_callbacks, cb) - else - cb.InvokeAsync() +// Admin command to halt/resume overmap +// /datum/controller/subsystem/shuttles/proc/toggle_overmap(new_setting) +// if(overmap_halted == new_setting) +// return +// overmap_halted = !overmap_halted +// for(var/ship in ships) +// var/obj/effect/overmap/visitable/ship/ship_effect = ship +// overmap_halted ? ship_effect.halt() : ship_effect.unhalt() + +/datum/controller/subsystem/shuttles/stat_entry() + ..("Shuttles:[process_shuttles.len]/[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]") diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index dd4f140fbd..9d190911e0 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -5,6 +5,7 @@ var/finished = FALSE var/role_name = "a thing" var/question = "Would you like to play as a thing?" + var/query_sound = 'sound/effects/ghost2.ogg' // A sound file to play to the ghost, to help people who are alt-tabbed know something might interest them. var/be_special_flag = 0 var/list/check_bans = list() var/wait_time = 60 SECONDS // How long to wait until returning the list of candidates. @@ -42,6 +43,9 @@ spawn(0) if(!C) return + window_flash(C) + if(query_sound) + SEND_SOUND(C, sound(query_sound)) var/response = alert(C, question, "[role_name] request", "Yes", "No", "Never for this round") if(response == "Yes") response = alert(C, "Are you sure you want to play as a [role_name]?", "[role_name] request", "Yes", "No") // Protection from a misclick. @@ -62,12 +66,14 @@ /datum/ghost_query/promethean role_name = "Promethean" question = "Someone is requesting a soul for a promethean. Would you like to play as one?" + query_sound = 'sound/effects/slime_squish.ogg' be_special_flag = BE_ALIEN cutoff_number = 1 /datum/ghost_query/posi_brain role_name = "Positronic Intelligence" question = "Someone has activated a Positronic Brain. Would you like to play as one?" + query_sound = 'sound/machines/boobeebeep.ogg' be_special_flag = BE_AI check_bans = list("AI", "Cyborg") cutoff_number = 1 @@ -75,6 +81,7 @@ /datum/ghost_query/drone_brain role_name = "Drone Intelligence" question = "Someone has activated a Drone AI Chipset. Would you like to play as one?" + query_sound = 'sound/machines/boobeebeep.ogg' be_special_flag = BE_AI check_bans = list("AI", "Cyborg") cutoff_number = 1 @@ -90,6 +97,7 @@ /datum/ghost_query/xeno role_name = "Alien" question = "An Alien has just been created on the facility. Would you like to play as them?" + query_sound = 'sound/voice/hiss5.ogg' be_special_flag = BE_ALIEN /datum/ghost_query/blob diff --git a/code/datums/observation/shuttle_added.dm b/code/datums/observation/shuttle_added.dm new file mode 100644 index 0000000000..dfd95170a0 --- /dev/null +++ b/code/datums/observation/shuttle_added.dm @@ -0,0 +1,22 @@ +// Observer Pattern Implementation: Shuttle Added +// Registration type: /datum/shuttle (register for the global event only) +// +// Raised when: After a shuttle is initialized. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the new shuttle + +GLOBAL_DATUM_INIT(shuttle_added, /decl/observ/shuttle_added, new) + +/decl/observ/shuttle_added + name = "Shuttle Added" + expected_type = /datum/shuttle + +/***************************** +* Shuttle Added Handling * +*****************************/ + +/datum/controller/subsystem/shuttles/initialize_shuttle() + . = ..() + if(.) + GLOB.shuttle_added.raise_event(.) \ No newline at end of file diff --git a/code/datums/observation/shuttle_moved.dm b/code/datums/observation/shuttle_moved.dm new file mode 100644 index 0000000000..35bff0d6b9 --- /dev/null +++ b/code/datums/observation/shuttle_moved.dm @@ -0,0 +1,38 @@ +// Observer Pattern Implementation: Shuttle Moved +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle has moved to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +// Observer Pattern Implementation: Shuttle Pre Move +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle is about to move to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +GLOBAL_DATUM_INIT(shuttle_moved_event, /decl/observ/shuttle_moved, new) + +/decl/observ/shuttle_moved + name = "Shuttle Moved" + expected_type = /datum/shuttle + +GLOBAL_DATUM_INIT(shuttle_pre_move_event, /decl/observ/shuttle_pre_move, new) + +/decl/observ/shuttle_pre_move + name = "Shuttle Pre Move" + expected_type = /datum/shuttle + +/***************** +* Shuttle Moved/Pre Move Handling * +*****************/ + +// Located in modules/shuttle/shuttle.dm +// Proc: /datum/shuttle/proc/attempt_move() \ No newline at end of file diff --git a/code/datums/observation/stat_set.dm b/code/datums/observation/stat_set.dm new file mode 100644 index 0000000000..b980d06ecc --- /dev/null +++ b/code/datums/observation/stat_set.dm @@ -0,0 +1,24 @@ +// Observer Pattern Implementation: Stat Set +// Registration type: /mob/living +// +// Raised when: A /mob/living changes stat, using the set_stat() proc +// +// Arguments that the called proc should expect: +// /mob/living/stat_mob: The mob whose stat changed +// /old_stat: Status before the change. +// /new_stat: Status after the change. + +GLOBAL_DATUM_INIT(stat_set_event, /decl/observ/stat_set, new) + +/decl/observ/stat_set + name = "Stat Set" + expected_type = /mob/living + +/**************** +* Stat Handling * +****************/ +/mob/living/set_stat(var/new_stat) + var/old_stat = stat + . = ..() + if(stat != old_stat) + GLOB.stat_set_event.raise_event(src, old_stat, new_stat) diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index 554a826f20..1b164a2cdf 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -339,14 +339,14 @@ /datum/supply_pack/med/distillery name = "Chemical distiller crate" contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery = 1) - cost = 175 + cost = 50 containertype = /obj/structure/largecrate containername = "Chemical distiller crate" /datum/supply_pack/med/advdistillery name = "Industrial Chemical distiller crate" contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial = 1) - cost = 250 + cost = 150 containertype = /obj/structure/largecrate containername = "Industrial Chemical distiller crate" diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm index 1c1eb7f2bb..757d38b953 100644 --- a/code/datums/supplypacks/robotics.dm +++ b/code/datums/supplypacks/robotics.dm @@ -116,6 +116,15 @@ containername = "Robolimb blueprints (Bishop)" access = access_robotics +/datum/supply_pack/robotics/robolimbs/cenilimicybernetics + name = "Cenilimi Cybernetics robolimb blueprints" + contains = list(/obj/item/weapon/disk/limb/cenilimicybernetics) + cost = 45 + containertype = /obj/structure/closet/crate/secure/science + containername = "Robolimb blueprints (Cenilimi Cybernetics)" + access = access_robotics + + /datum/supply_pack/robotics/mecha_ripley name = "Circuit Crate (\"Ripley\" APLU)" contains = list( diff --git a/code/datums/uplink/announcements.dm b/code/datums/uplink/announcements.dm index 86a9567a8d..1c4a448cee 100644 --- a/code/datums/uplink/announcements.dm +++ b/code/datums/uplink/announcements.dm @@ -13,7 +13,7 @@ /datum/uplink_item/abstract/announcements/fake_centcom name = "Command Update Announcement" desc = "Causes a falsified Command Update. Triggers immediately after supplying additional data." - item_cost = 40 + item_cost = 20 /datum/uplink_item/abstract/announcements/fake_centcom/extra_args(var/mob/user) var/title = sanitize(input("Enter your announcement title.", "Announcement Title") as null|text) @@ -41,7 +41,7 @@ /datum/uplink_item/abstract/announcements/fake_crew_arrival name = "Crew Arrival Announcement/Records" desc = "Creates a fake crew arrival announcement as well as fake crew records, using your current appearance (including held items!) and worn id card. Trigger with care!" - item_cost = 30 + item_cost = 15 /datum/uplink_item/abstract/announcements/fake_crew_arrival/get_goods(var/obj/item/device/uplink/U, var/loc, var/mob/user, var/list/args) if(!user) diff --git a/code/datums/uplink/armor.dm b/code/datums/uplink/armor.dm index 20367f3989..0d7d814310 100644 --- a/code/datums/uplink/armor.dm +++ b/code/datums/uplink/armor.dm @@ -13,3 +13,18 @@ name = "Heavy Armor Vest" item_cost = 40 path = /obj/item/clothing/suit/storage/vest/heavy/merc + +/datum/uplink_item/item/armor/gorlexsuit + name = "Mercenary Voidsuit" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/voidsuit + +/datum/uplink_item/item/armor/gorlexsuit_fire + name = "Mercenary Voidsuit (Fire)" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/voidsuit/fire + +/datum/uplink_item/item/armor/combat + name = "Combat Platecarrier Set" + item_cost = 60 + path = /obj/item/clothing/suit/armor/pcarrier/merc diff --git a/code/datums/uplink/implants.dm b/code/datums/uplink/implants.dm index e36396c9cd..bbbeaf9e7e 100644 --- a/code/datums/uplink/implants.dm +++ b/code/datums/uplink/implants.dm @@ -25,51 +25,51 @@ path = /obj/item/weapon/storage/box/syndie_kit/imp_uplink /datum/uplink_item/item/implants/imp_shades - name = "Integrated Thermal-Shades Implant (Organic)" + name = "Integrated Thermal-Shades Implant" item_cost = 80 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug /datum/uplink_item/item/implants/imp_taser - name = "Integrated Taser Implant (Organic)" + name = "Integrated Taser Implant" item_cost = 30 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/taser /datum/uplink_item/item/implants/imp_laser - name = "Integrated Laser Implant (Organic)" + name = "Integrated Laser Implant" item_cost = 50 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/laser /datum/uplink_item/item/implants/imp_dart - name = "Integrated Dart Implant (Organic)" + name = "Integrated Dart Implant" item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/dart /datum/uplink_item/item/implants/imp_toolkit - name = "Integrated Toolkit Implant (Organic)" + name = "Integrated Toolkit Implant" item_cost = 80 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/toolkit /datum/uplink_item/item/implants/imp_medkit - name = "Integrated Medkit Implant (Organic)" + name = "Integrated Medkit Implant" item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/medkit /datum/uplink_item/item/implants/imp_analyzer - name = "Integrated Research Scanner Implant (Organic)" + name = "Integrated Research Scanner Implant" item_cost = 20 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/analyzer /datum/uplink_item/item/implants/imp_sword - name = "Integrated Sword Implant (Organic)" + name = "Integrated Sword Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sword /datum/uplink_item/item/implants/imp_sprinter - name = "Integrated Sprinter Implant (Organic)" + name = "Integrated Sprinter Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sprinter /datum/uplink_item/item/implants/imp_sprinter - name = "Integrated Surge Implant (Organic)" + name = "Integrated Surge Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/surge diff --git a/code/datums/uplink/medical.dm b/code/datums/uplink/medical.dm index c28fed46b9..0657b62b2e 100644 --- a/code/datums/uplink/medical.dm +++ b/code/datums/uplink/medical.dm @@ -24,11 +24,23 @@ item_cost = 10 path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting +/datum/uplink_item/item/medical/clotting_case + name = "Clotting Medicine case" + item_cost = 20 + desc = "A case of three myelamine injectors. Can rapidly remove and stow up to six injectors." + path = /obj/item/weapon/storage/quickdraw/syringe_case/clotting + /datum/uplink_item/item/medical/bonemeds name = "Bone Repair injector" item_cost = 10 path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed +/datum/uplink_item/item/medical/bonemeds_case + name = "Bone Repair case" + item_cost = 20 + desc = "A case of three osteodaxon injectors. Can rapidly remove and stow up to six injectors." + path = /obj/item/weapon/storage/quickdraw/syringe_case/bonemed + /datum/uplink_item/item/medical/ambrosiadeusseeds name = "Box of 7x ambrosia deus seed packets" item_cost = 10 diff --git a/code/datums/uplink/resources.dm b/code/datums/uplink/resources.dm new file mode 100644 index 0000000000..a544cab728 --- /dev/null +++ b/code/datums/uplink/resources.dm @@ -0,0 +1,48 @@ + +/datum/uplink_item/crated/resources + name = "Resource Crate" + desc = "A crate routed from an in-system trading post, containing various valuable materials." + item_cost = 60 + category = /datum/uplink_category/services + + paths = list(\ + /obj/fiftyspawner/uranium,\ + /obj/fiftyspawner/phoron,\ + /obj/fiftyspawner/gold,\ + /obj/fiftyspawner/silver,\ + /obj/fiftyspawner/osmium,\ + /obj/fiftyspawner/plasteel\ + ) + +/datum/uplink_item/crated/seeds + name = "Exotic Plantlife Crate" + desc = "A crate routed from an in-system trading post, containing various exotic plants." + item_cost = 20 + category = /datum/uplink_category/services + + paths = list(\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random\ + ) + +/datum/uplink_item/crated/spare_organs + name = "Spare Organ Crate" + desc = "A crate stolen from a medical relief ship, containing various bioprinted organs." + item_cost = 20 + category = /datum/uplink_category/services + crate_path = /obj/structure/closet/crate/freezer + + paths = list(\ + /obj/item/organ/internal/eyes/replicant,\ + /obj/item/organ/internal/heart/replicant,\ + /obj/item/organ/internal/kidneys/replicant,\ + /obj/item/organ/internal/liver/replicant,\ + /obj/item/organ/internal/lungs/replicant,\ + /obj/item/organ/internal/voicebox/replicant\ + ) diff --git a/code/datums/uplink/stealth_items.dm b/code/datums/uplink/stealth_items.dm index 0a17e659a6..973e851b02 100644 --- a/code/datums/uplink/stealth_items.dm +++ b/code/datums/uplink/stealth_items.dm @@ -37,4 +37,16 @@ /datum/uplink_item/item/stealth_items/makeover name = "Makeover Kit" item_cost = 5 - path = /obj/item/weapon/makeover \ No newline at end of file + path = /obj/item/weapon/makeover + +/datum/uplink_item/item/stealth_items/thievesgloves + name = "Thieve's Gloves" + desc = "A pair of sterile gloves that allow the wearer to inspect the backpacks of other players, and swap pocket items." + item_cost = 30 + path = /obj/item/clothing/gloves/sterile/thieves + +/datum/uplink_item/item/stealth_items/deadringer + name = "Stealth Watch" + desc = "A strange watch which can be used to create a fake corpse if you are injured when it is active, as it projects a cloaking field around your person." + item_cost = 50 + path = /obj/item/weapon/deadringer diff --git a/code/datums/uplink/stealthy_weapons.dm b/code/datums/uplink/stealthy_weapons.dm index 5576ebcac5..2f4cab5ff7 100644 --- a/code/datums/uplink/stealthy_weapons.dm +++ b/code/datums/uplink/stealthy_weapons.dm @@ -33,3 +33,51 @@ name = "Random Toxin - Beaker" item_cost = 10 path = /obj/item/weapon/storage/box/syndie_kit/toxin + +/datum/uplink_item/item/stealthy_weapons/penblade + name = "Energy Penblade, Black" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade + +/datum/uplink_item/item/stealthy_weapons/penblade_red + name = "Energy Penblade, Red" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/red + +/datum/uplink_item/item/stealthy_weapons/penblade_blue + name = "Energy Penblade, Blue" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/blue + +/datum/uplink_item/item/stealthy_weapons/penblade_fancy + name = "Energy Penblade, Fountain" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/fountain + +/datum/uplink_item/item/stealthy_weapons/angrybuzzer + name = "Morphium Shock Ring" + desc = "An enigmatic ring used to create powerful electric shocks when punching. Can be used as a brute-force method of defibrillation." + item_cost = 40 + path = /obj/item/clothing/gloves/ring/buzzer + +/datum/uplink_item/item/stealthy_weapons/huntingtrap + name = "Camonetted Beartraps" + desc = "A box of unique beartraps which will partially cloak when deployed, allowing for more effective hunting." + item_cost = 30 + path = /obj/item/weapon/storage/box/syndie_kit/deadliest_game + +/datum/uplink_item/item/stealthy_weapons/virus + name = "Virus Cultures" + desc = "A box of three unique virus cultures. As dangerous to you as anyone else if handled improperly." + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/viral + +/datum/uplink_item/item/stealthy_weapons/syringe_case + name = "Quickdraw Syringe Case" + desc = "A small box capable of holding six syringes for rapid deployment. Fits in your pocket." + item_cost = 20 + path = /obj/item/weapon/storage/quickdraw/syringe_case diff --git a/code/datums/uplink/tools.dm b/code/datums/uplink/tools.dm index 6c340385b0..6f70c1111c 100644 --- a/code/datums/uplink/tools.dm +++ b/code/datums/uplink/tools.dm @@ -91,6 +91,12 @@ item_cost = 30 path = /obj/item/weapon/card/emag +/datum/uplink_item/item/tools/graviton + name = "Graviton Goggles" + desc = "An obvious, if useful pair of advanced imaging goggles that allow you to see objects and turfs through walls." + item_cost = 15 + path = /obj/item/clothing/glasses/graviton + /datum/uplink_item/item/tools/thermal name = "Thermal Imaging Glasses" item_cost = 30 @@ -111,6 +117,11 @@ item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/demolitions_heavy +/datum/uplink_item/item/tools/integratedcircuitprinter + name = "Integrated Circuit Printer (Upgraded)" + item_cost = 10 + path = /obj/item/device/integrated_circuit_printer/upgraded + /* /datum/uplink_item/item/tools/packagebomb/huge name = "Package Bomb (Huge) diff --git a/code/datums/uplink/uplink_categories.dm b/code/datums/uplink/uplink_categories.dm index 2776085594..d9e6d84db7 100644 --- a/code/datums/uplink/uplink_categories.dm +++ b/code/datums/uplink/uplink_categories.dm @@ -52,4 +52,4 @@ datum/uplink_category/ammunition name = "Telecrystals" /datum/uplink_category/backup - name = "Backup" \ No newline at end of file + name = "Backup" diff --git a/code/datums/uplink/uplink_items.dm b/code/datums/uplink/uplink_items.dm index 94b1281dd3..b6c8cf9952 100644 --- a/code/datums/uplink/uplink_items.dm +++ b/code/datums/uplink/uplink_items.dm @@ -162,6 +162,36 @@ datum/uplink_item/dd_SortValue() return "\icon[default_abstract_uplink_icon]" +/* + * Crated goods. + */ + +/datum/uplink_item/crated + var/crate_path = /obj/structure/largecrate + var/list/paths = list() // List of paths to be spawned into the crate. + +/datum/uplink_item/crated/get_goods(var/obj/item/device/uplink/U, var/loc) + var/obj/L = new crate_path(get_turf(loc)) + + L.adjust_scale(rand(9, 12) / 10, rand(9, 12) / 10) // Some variation in the crate / locker size. + + for(var/path in paths) + var/obj/O = new path(L) + O.forceMove(L) + + return L + +/datum/uplink_item/crated/description() + if(!desc) + // Fallback description + var/obj/temp = crate_path + desc = initial(temp.desc) + return ..() + +/datum/uplink_item/crated/log_icon() + var/obj/I = crate_path + return "\icon[I]" + /**************** * Support procs * ****************/ diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm index e4c27752f0..0ed9b26226 100644 --- a/code/datums/uplink/visible_weapons.dm +++ b/code/datums/uplink/visible_weapons.dm @@ -61,7 +61,7 @@ /datum/uplink_item/item/visible_weapons/riggedlaser name = "Exosuit Rigged Laser" - item_cost = 60 + item_cost = 30 path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser /datum/uplink_item/item/visible_weapons/revolver @@ -146,7 +146,7 @@ /datum/uplink_item/item/visible_weapons/egun name = "Energy Gun" - item_cost = 60 + item_cost = 30 path = /obj/item/weapon/gun/energy/gun /datum/uplink_item/item/visible_weapons/lasercannon @@ -171,5 +171,17 @@ /datum/uplink_item/item/visible_weapons/xray name = "Xray Gun" - item_cost = 85 + item_cost = 60 path = /obj/item/weapon/gun/energy/xray + +/datum/uplink_item/item/visible_weapons/flamethrower + name = "Heavy Flamethrower" + desc = "A large flamethrower that runs on pressurized, gaseous phoron and electric charge." + item_cost = 60 + path = /obj/item/weapon/storage/secure/briefcase/flamer + +/datum/uplink_item/item/visible_weapons/concussion_grenades + name = "Concussion Grenades (8)" + desc = "A box of eight concussion grenades." + item_cost = 30 + path = /obj/item/weapon/storage/box/syndie_kit/concussion_grenade diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 8b682d1f09..f018a8e594 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -9,14 +9,6 @@ holder_type = /obj/machinery/door/airlock wire_count = 12 window_y = 570 - var/datum/wire_hint/bolt_lock_hint - var/datum/wire_hint/bolt_light_hint - var/datum/wire_hint/power_hint - var/datum/wire_hint/backup_power_hint - var/datum/wire_hint/ai_control_hint - var/datum/wire_hint/safeties_hint - var/datum/wire_hint/speed_hint - var/datum/wire_hint/id_scan_hint var/const/AIRLOCK_WIRE_IDSCAN = 1 var/const/AIRLOCK_WIRE_MAIN_POWER1 = 2 @@ -31,27 +23,6 @@ var/const/AIRLOCK_WIRE_SAFETY = 512 var/const/AIRLOCK_WIRE_SPEED = 1024 var/const/AIRLOCK_WIRE_LIGHT = 2048 -/datum/wires/airlock/make_wire_hints() - bolt_lock_hint = new("The door bolts have fallen!", "The door bolts look up.") - bolt_light_hint = new("The door bolt lights are on.", "The door bolt lights are off!") - power_hint = new("The test light is on.", "The test light is off!") - backup_power_hint = new("The backup power light is off!", "The backup power light is on.") - ai_control_hint = new("The 'AI control allowed' light is on.", "The 'AI control allowed' light is off.") - safeties_hint = new("The 'Check Wiring' light is on.", "The 'Check Wiring' light is off.") - speed_hint = new("The 'Check Timing Mechanism' light is on.", "The 'Check Timing Mechanism' light is off.") - id_scan_hint = new("The IDScan light is on.", "The IDScan light is off.") - -/datum/wires/airlock/Destroy() - bolt_lock_hint = null - bolt_light_hint = null - power_hint = null - backup_power_hint = null - ai_control_hint = null - safeties_hint = null - speed_hint = null - id_scan_hint = null - return ..() - /datum/wires/airlock/CanUse(var/mob/living/L) var/obj/machinery/door/airlock/A = holder if(!istype(L, /mob/living/silicon)) @@ -67,14 +38,14 @@ var/const/AIRLOCK_WIRE_LIGHT = 2048 var/haspower = A.arePowerSystemsOn() //If there's no power, then no lights will be on. . += ..() - . += bolt_lock_hint.show(A.locked) - . += bolt_light_hint.show(A.lights && haspower) - . += power_hint.show(haspower) - . += backup_power_hint.show(A.backup_power_lost_until) - . += ai_control_hint.show(A.aiControlDisabled == 0 && !A.emagged && haspower) - . += safeties_hint.show(A.safe == 0 && haspower) - . += speed_hint.show(A.normalspeed == 0 && haspower) - . += id_scan_hint.show(A.aiDisabledIdScanner == 0 && haspower) + . += show_hint(0x01, A.locked, "The door bolts have fallen!", "The door bolts look up.") + . += show_hint(0x02, A.lights && haspower, "The door bolt lights are on.", "The door bolt lights are off!") + . += show_hint(0x04, haspower, "The test light is on.", "The test light is off!") + . += show_hint(0x08, A.backup_power_lost_until, "The backup power light is off!", "The backup power light is on.") + . += show_hint(0x10, A.aiControlDisabled == 0 && !A.emagged && haspower, "The 'AI control allowed' light is on.", "The 'AI control allowed' light is off.") + . += show_hint(0x20, A.safe == 0 && haspower, "The 'Check Wiring' light is on.", "The 'Check Wiring' light is off.") + . += show_hint(0x40, A.normalspeed == 0 && haspower, "The 'Check Timing Mechanism' light is on.", "The 'Check Timing Mechanism' light is off.") + . += show_hint(0x80, A.aiDisabledIdScanner == 0 && haspower, "The IDScan light is on.", "The IDScan light is off.") /datum/wires/airlock/UpdateCut(var/index, var/mended) diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index 712e2aa7e4..7c56bd4e52 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -1,9 +1,6 @@ /datum/wires/alarm holder_type = /obj/machinery/alarm wire_count = 5 - var/datum/wire_hint/lock_hint - var/datum/wire_hint/power_hint - var/datum/wire_hint/ai_control_hint var/const/AALARM_WIRE_IDSCAN = 1 var/const/AALARM_WIRE_POWER = 2 @@ -11,18 +8,6 @@ var/const/AALARM_WIRE_SYPHON = 4 var/const/AALARM_WIRE_AI_CONTROL = 8 var/const/AALARM_WIRE_AALARM = 16 - -/datum/wires/alarm/make_wire_hints() - lock_hint = new("The Air Alarm is locked.", "The Air Alarm is unlocked.") - power_hint = new("The Air Alarm is offline.", "The Air Alarm is working properly!") - ai_control_hint = new("The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") - -/datum/wires/alarm/Destroy() - lock_hint = null - power_hint = null - ai_control_hint = null - return ..() - /datum/wires/alarm/CanUse(var/mob/living/L) var/obj/machinery/alarm/A = holder if(A.panel_open) @@ -32,9 +17,9 @@ var/const/AALARM_WIRE_AALARM = 16 /datum/wires/alarm/GetInteractWindow() var/obj/machinery/alarm/A = holder . += ..() - . += lock_hint.show(A.locked) - . += power_hint.show(A.shorted || (A.stat & (NOPOWER|BROKEN))) - . += ai_control_hint.show(A.aidisabled) + . += show_hint(0x1, A.locked, "The Air Alarm is locked.", "The Air Alarm is unlocked.") + . += show_hint(0x2, A.shorted || (A.stat & (NOPOWER|BROKEN)), "The Air Alarm is offline.", "The Air Alarm is working properly!") + . += show_hint(0x4, A.aidisabled, "The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") /datum/wires/alarm/UpdateCut(var/index, var/mended) var/obj/machinery/alarm/A = holder diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index 0b00297b41..1b7f43d21f 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -1,32 +1,18 @@ /datum/wires/apc holder_type = /obj/machinery/power/apc wire_count = 4 - var/datum/wire_hint/lock_hint - var/datum/wire_hint/power_hint - var/datum/wire_hint/ai_control_hint #define APC_WIRE_IDSCAN 1 #define APC_WIRE_MAIN_POWER1 2 #define APC_WIRE_MAIN_POWER2 4 #define APC_WIRE_AI_CONTROL 8 -/datum/wires/apc/make_wire_hints() - lock_hint = new("The APC is locked.", "The APC is unlocked.") - power_hint = new("The APCs power has been shorted.", "The APC is working properly!") - ai_control_hint = new("The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") - -/datum/wires/apc/Destroy() - lock_hint = null - power_hint = null - ai_control_hint = null - return ..() - /datum/wires/apc/GetInteractWindow() var/obj/machinery/power/apc/A = holder . += ..() - . += lock_hint.show(A.locked) - . += power_hint.show(A.shorted) - . += ai_control_hint.show(A.aidisabled) + . += show_hint(0x1, A.locked, "The APC is locked.", "The APC is unlocked.") + . += show_hint(0x2, A.shorted, "The APCs power has been shorted.", "The APC is working properly!") + . += show_hint(0x4, A.aidisabled, "The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") /datum/wires/apc/CanUse(var/mob/living/L) diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index fee656cf2f..df625351b8 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -2,31 +2,17 @@ holder_type = /obj/machinery/autolathe wire_count = 6 - var/datum/wire_hint/disable_hint - var/datum/wire_hint/shock_hint - var/datum/wire_hint/hack_hint var/const/AUTOLATHE_HACK_WIRE = 1 var/const/AUTOLATHE_SHOCK_WIRE = 2 var/const/AUTOLATHE_DISABLE_WIRE = 4 -/datum/wires/autolathe/make_wire_hints() - disable_hint = new("The red light is off.", "The red light is on.") - shock_hint = new("The green light is off.", "The green light is on.") - hack_hint = new("The blue light is off.", "The blue light is on.") - -/datum/wires/autolathe/Destroy() - disable_hint = null - shock_hint = null - hack_hint = null - return ..() - /datum/wires/autolathe/GetInteractWindow() var/obj/machinery/autolathe/A = holder . += ..() - . += disable_hint.show(A.disabled) - . += shock_hint.show(A.shocked) - . += hack_hint.show(A.hacked) + . += show_hint(0x1, A.disabled, "The red light is off.", "The red light is on.") + . += show_hint(0x2, A.shocked, "The green light is off.", "The green light is on.") + . += show_hint(0x4, A.hacked, "The blue light is off.", "The blue light is on.") /datum/wires/autolathe/CanUse() var/obj/machinery/autolathe/A = holder diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index 78222346ce..69f1810331 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -4,31 +4,14 @@ random = 1 holder_type = /obj/machinery/camera wire_count = 6 - var/datum/wire_hint/view_hint - var/datum/wire_hint/power_hint - var/datum/wire_hint/light_hint - var/datum/wire_hint/alarm_hint - -/datum/wires/camera/make_wire_hints() - view_hint = new("The focus light is on.", "The focus light is off.") - power_hint = new("The power link light is on.", "The power link light is off.") - light_hint = new("The camera light is off.", "The camera light is on.") - alarm_hint = new("The alarm light is on.", "The alarm light is off.") - -/datum/wires/camera/Destroy() - view_hint = null - power_hint = null - light_hint = null - alarm_hint = null - return ..() /datum/wires/camera/GetInteractWindow() . = ..() var/obj/machinery/camera/C = holder - . += view_hint.show(C.view_range == initial(C.view_range)) - . += power_hint.show(C.can_use()) - . += light_hint.show(C.light_disabled) - . += alarm_hint.show(C.alarm_on) + . += show_hint(0x1, C.view_range == initial(C.view_range), "The focus light is on.", "The focus light is off.") + . += show_hint(0x2, C.can_use(), "The power link light is on.", "The power link light is off.") + . += show_hint(0x4, C.light_disabled, "The camera light is off.", "The camera light is on.") + . += show_hint(0x8, C.alarm_on, "The alarm light is on.", "The alarm light is off.") return . /datum/wires/camera/CanUse(var/mob/living/L) diff --git a/code/datums/wires/grid_checker.dm b/code/datums/wires/grid_checker.dm index 62c8a78fa1..355f39ec18 100644 --- a/code/datums/wires/grid_checker.dm +++ b/code/datums/wires/grid_checker.dm @@ -1,20 +1,6 @@ /datum/wires/grid_checker holder_type = /obj/machinery/power/grid_checker wire_count = 8 - var/datum/wire_hint/power_failure_hint - var/datum/wire_hint/lock_out_hint - var/datum/wire_hint/ready_hint - -/datum/wires/grid_checker/make_wire_hints() - power_failure_hint = new("The green light is off.", "The green light is on.") - lock_out_hint = new("The red light is on.", "The red light is off.") - ready_hint = new("The blue light is on.", "The blue light is off.") - -/datum/wires/grid_checker/Destroy() - power_failure_hint = null - lock_out_hint = null - ready_hint = null - return ..() var/const/GRID_CHECKER_WIRE_REBOOT = 1 // This wire causes the grid-check to end, if pulsed. var/const/GRID_CHECKER_WIRE_LOCKOUT = 2 // If cut or pulsed, locks the user out for half a minute. @@ -36,9 +22,9 @@ var/const/GRID_CHECKER_WIRE_NOTHING_2 = 128 // Does nothing, but makes it a bit /datum/wires/grid_checker/GetInteractWindow() var/obj/machinery/power/grid_checker/G = holder . += ..() - . += power_failure_hint.show(G.power_failing) - . += lock_out_hint.show(G.wire_locked_out) - . += ready_hint.show(G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3) + . += show_hint(0x1, G.power_failing, "The green light is off.", "The green light is on.") + . += show_hint(0x2, G.wire_locked_out, "The red light is on.", "The red light is off.") + . += show_hint(0x4, G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3, "The blue light is on.", "The blue light is off.") /datum/wires/grid_checker/UpdateCut(var/index, var/mended) diff --git a/code/datums/wires/jukebox.dm b/code/datums/wires/jukebox.dm index 92e2b97ede..9bc0f2ca61 100644 --- a/code/datums/wires/jukebox.dm +++ b/code/datums/wires/jukebox.dm @@ -2,21 +2,6 @@ random = 1 holder_type = /obj/machinery/media/jukebox wire_count = 11 - var/datum/wire_hint/power_hint - var/datum/wire_hint/parental_hint - var/datum/wire_hint/reverse_hint - -/datum/wires/jukebox/make_wire_hints() - power_hint = new("The power light is off.", "The power light is on.") - parental_hint = new("The parental guidance light is off.", "The parental guidance light is on.") - reverse_hint = new("The data light is hauntingly dark.", "The data light is glowing softly.") - -/datum/wires/jukebox/Destroy() - power_hint = null - parental_hint = null - reverse_hint = null - return ..() - var/const/WIRE_POWER = 1 var/const/WIRE_HACK = 2 @@ -40,9 +25,9 @@ var/const/WIRE_NEXT = 1024 /datum/wires/jukebox/GetInteractWindow() var/obj/machinery/media/jukebox/A = holder . += ..() - . += power_hint.show(A.stat & (BROKEN|NOPOWER)) - . += parental_hint.show(A.hacked) - . += reverse_hint.show(IsIndexCut(WIRE_REVERSE)) + . += show_hint(0x1, A.stat & (BROKEN|NOPOWER), "The power light is off.", "The power light is on.") + . += show_hint(0x2, A.hacked, "The parental guidance light is off.", "The parental guidance light is on.") + . += show_hint(0x4, IsIndexCut(WIRE_REVERSE), "The data light is hauntingly dark.", "The data light is glowing softly.") // Give a hint as to what each wire does /datum/wires/jukebox/UpdatePulsed(var/index) diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 20a7960583..ed87a2b1fe 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -2,23 +2,6 @@ random = 1 holder_type = /mob/living/silicon/robot wire_count = 5 - var/datum/wire_hint/lawsync_hint - var/datum/wire_hint/connected_ai_hint - var/datum/wire_hint/camera_hint - var/datum/wire_hint/lockdown_hint - -/datum/wires/robot/make_wire_hints() - lawsync_hint = new("The LawSync light is on.", "The LawSync light is off.") - connected_ai_hint = new("The AI link light is on.", "The AI link light is off.") - camera_hint = new("The camera light is on.", "The camera light is off.") - lockdown_hint = new("The lockdown light is on.", "The lockdown light is off.") - -/datum/wires/robot/Destroy() - lawsync_hint = null - connected_ai_hint = null - camera_hint = null - lockdown_hint = null - return ..() var/const/BORG_WIRE_LAWCHECK = 1 var/const/BORG_WIRE_MAIN_POWER = 2 // The power wires do nothing whyyyyyyyyyyyyy @@ -29,10 +12,10 @@ var/const/BORG_WIRE_CAMERA = 16 /datum/wires/robot/GetInteractWindow() . = ..() var/mob/living/silicon/robot/R = holder - . += lawsync_hint.show(R.lawupdate) - . += connected_ai_hint.show(R.connected_ai) - . += camera_hint.show((!isnull(R.camera) && R.camera.status == 1)) - . += lockdown_hint.show(R.lockdown) + . += show_hint(0x1, R.lawupdate, "The LawSync light is on.", "The LawSync light is off.") + . += show_hint(0x2, R.connected_ai, "The AI link light is on.", "The AI link light is off.") + . += show_hint(0x4, (!isnull(R.camera) && R.camera.status == 1), "The camera light is on.", "The camera light is off.") + . += show_hint(0x8, R.lockdown, "The lockdown light is on.", "The lockdown light is off.") return . /datum/wires/robot/UpdateCut(var/index, var/mended) diff --git a/code/datums/wires/seedstorage.dm b/code/datums/wires/seedstorage.dm index 5ea1462b76..2a0e315a57 100644 --- a/code/datums/wires/seedstorage.dm +++ b/code/datums/wires/seedstorage.dm @@ -7,23 +7,6 @@ holder_type = /obj/machinery/seed_storage wire_count = 4 random = 1 - var/datum/wire_hint/zap_hint - var/datum/wire_hint/smart_hint - var/datum/wire_hint/hacked_hint - var/datum/wire_hint/lockdown_hint - -/datum/wires/seedstorage/make_wire_hints() - zap_hint = new("The orange light is off.", "The orange light is on.") - smart_hint = new("The red light is off.", "The red light is blinking.") - hacked_hint = new("The green light is on.", "The green light is off.") - lockdown_hint = new("The keypad lock is deployed.", "The keypad lock is retracted.") - -/datum/wires/seedstorage/Destroy() - zap_hint = null - smart_hint = null - hacked_hint = null - lockdown_hint = null - return ..() /datum/wires/seedstorage/CanUse(var/mob/living/L) var/obj/machinery/seed_storage/V = holder @@ -34,10 +17,10 @@ /datum/wires/seedstorage/GetInteractWindow() var/obj/machinery/seed_storage/V = holder . += ..() - . += zap_hint.show(V.seconds_electrified) - . += smart_hint.show(V.smart) - . += hacked_hint.show(V.hacked || V.emagged) - . += lockdown_hint.show(V.lockdown) + . += show_hint(0x1, V.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, V.smart, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, V.hacked || V.emagged, "The green light is on.", "The green light is off.") + . += show_hint(0x8, V.lockdown, "The keypad lock is deployed.", "The keypad lock is retracted.") /datum/wires/seedstorage/UpdatePulsed(var/index) var/obj/machinery/seed_storage/V = holder diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm index 7ea943665f..f69e153bbf 100644 --- a/code/datums/wires/smartfridge.dm +++ b/code/datums/wires/smartfridge.dm @@ -1,20 +1,6 @@ /datum/wires/smartfridge holder_type = /obj/machinery/smartfridge wire_count = 3 - var/datum/wire_hint/zap_hint - var/datum/wire_hint/shoot_hint - var/datum/wire_hint/scan_id_hint - -/datum/wires/smartfridge/make_wire_hints() - zap_hint = new("The orange light is off.", "The orange light is on.") - shoot_hint = new("The red light is off.", "The red light is blinking.") - scan_id_hint = new("A purple light is on.", "A yellow light is on.") - -/datum/wires/smartfridge/Destroy() - zap_hint = null - shoot_hint = null - scan_id_hint = null - return ..() /datum/wires/smartfridge/secure random = 1 @@ -33,9 +19,9 @@ var/const/SMARTFRIDGE_WIRE_IDSCAN = 4 /datum/wires/smartfridge/GetInteractWindow() var/obj/machinery/smartfridge/S = holder . += ..() - . += zap_hint.show(S.seconds_electrified) - . += shoot_hint.show(S.shoot_inventory) - . += scan_id_hint.show(S.scan_id) + . += show_hint(0x1, S.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, S.shoot_inventory, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.scan_id, "A purple light is on.", "A yellow light is on.") /datum/wires/smartfridge/UpdatePulsed(var/index) var/obj/machinery/smartfridge/S = holder diff --git a/code/datums/wires/smes.dm b/code/datums/wires/smes.dm index 9b2a3ab0e0..82d93b9fa8 100644 --- a/code/datums/wires/smes.dm +++ b/code/datums/wires/smes.dm @@ -1,20 +1,6 @@ /datum/wires/smes holder_type = /obj/machinery/power/smes/buildable wire_count = 5 - var/datum/wire_hint/io_hint - var/datum/wire_hint/safeties_hint - var/datum/wire_hint/rcon_hint - -/datum/wires/smes/make_wire_hints() - io_hint = new("The green light is off.", "The green light is on.") - safeties_hint = new("The red light is off.", "The red light is blinking.") - rcon_hint = new("The blue light is on.", "The blue light is off.") - -/datum/wires/smes/Destroy() - io_hint = null - safeties_hint = null - rcon_hint = null - return ..() var/const/SMES_WIRE_RCON = 1 // Remote control (AI and consoles), cut to disable var/const/SMES_WIRE_INPUT = 2 // Input wire, cut to disable input, pulse to disable for 60s @@ -33,9 +19,9 @@ var/const/SMES_WIRE_FAILSAFES = 16 // Cut to disable failsafes, mend to reenable /datum/wires/smes/GetInteractWindow() var/obj/machinery/power/smes/buildable/S = holder . += ..() - . += io_hint.show(S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed) - . += safeties_hint.show(S.safeties_enabled || S.grounding) - . += rcon_hint.show(S.RCon) + . += show_hint(0x1, S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed, "The green light is off.", "The green light is on.") + . += show_hint(0x2, S.safeties_enabled || S.grounding, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.RCon, "The blue light is on.", "The blue light is off.") /datum/wires/smes/UpdateCut(var/index, var/mended) var/obj/machinery/power/smes/buildable/S = holder diff --git a/code/datums/wires/suit_storage_unit.dm b/code/datums/wires/suit_storage_unit.dm index 2551ef3eb1..fe694d271a 100644 --- a/code/datums/wires/suit_storage_unit.dm +++ b/code/datums/wires/suit_storage_unit.dm @@ -1,20 +1,6 @@ /datum/wires/suit_storage_unit holder_type = /obj/machinery/suit_cycler wire_count = 3 - var/datum/wire_hint/zap_hint - var/datum/wire_hint/safeties_hint - var/datum/wire_hint/locked_hint - -/datum/wires/suit_storage_unit/make_wire_hints() - zap_hint = new("The orange light is off.", "The orange light is on.") - safeties_hint = new("The red light is off.", "The red light is blinking.") - locked_hint = new("The yellow light is on.", "The yellow light is off.") - -/datum/wires/suit_storage_unit/Destroy() - zap_hint = null - safeties_hint = null - locked_hint = null - return ..() var/const/SUIT_STORAGE_WIRE_ELECTRIFY = 1 var/const/SUIT_STORAGE_WIRE_SAFETY = 2 @@ -33,9 +19,9 @@ var/const/SUIT_STORAGE_WIRE_LOCKED = 4 /datum/wires/suit_storage_unit/GetInteractWindow() var/obj/machinery/suit_cycler/S = holder . += ..() - . += zap_hint.show(S.electrified) - . += safeties_hint.show(S.safeties) - . += locked_hint.show(S.locked) + . += show_hint(0x1, S.electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, S.safeties, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.locked, "The yellow light is on.", "The yellow light is off.") /datum/wires/suit_storage_unit/UpdatePulsed(var/index) var/obj/machinery/suit_cycler/S = holder diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index f15dac3fcf..61aadf4b1b 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -1,23 +1,6 @@ /datum/wires/vending holder_type = /obj/machinery/vending wire_count = 4 - var/datum/wire_hint/zap_hint - var/datum/wire_hint/shoot_hint - var/datum/wire_hint/hidden_hint - var/datum/wire_hint/scan_id_hint - -/datum/wires/vending/make_wire_hints() - zap_hint = new("The orange light is off.", "The orange light is on.") - shoot_hint = new("The red light is off.", "The red light is blinking.") - hidden_hint = new("A green light is on.", "A green light is off.") - scan_id_hint = new("A purple light is on.", "A yellow light is on.") - -/datum/wires/vending/Destroy() - zap_hint = null - shoot_hint = null - hidden_hint = null - scan_id_hint = null - return ..() var/const/VENDING_WIRE_THROW = 1 var/const/VENDING_WIRE_CONTRABAND = 2 @@ -33,10 +16,10 @@ var/const/VENDING_WIRE_IDSCAN = 8 /datum/wires/vending/GetInteractWindow() var/obj/machinery/vending/V = holder . += ..() - . += zap_hint.show(V.seconds_electrified) - . += shoot_hint.show(V.shoot_inventory) - . += hidden_hint.show(V.categories & CAT_HIDDEN) - . += scan_id_hint.show(V.scan_id) + . += show_hint(0x1, V.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, V.shoot_inventory, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, V.categories & CAT_HIDDEN, "A green light is on.", "A green light is off.") + . += show_hint(0x8, V.scan_id, "A purple light is on.", "A yellow light is on.") /datum/wires/vending/UpdatePulsed(var/index) var/obj/machinery/vending/V = holder diff --git a/code/datums/wires/wire_hint.dm b/code/datums/wires/wire_hint.dm deleted file mode 100644 index a7967f45d7..0000000000 --- a/code/datums/wires/wire_hint.dm +++ /dev/null @@ -1,27 +0,0 @@ -// 'Wire hints' are the pieces of text on the bottom of the window that give you clues on what you're doing. -// E.g. a power light turning on or off. -// They are their own object in order to allow for logic to make them go bold if they change. - -/datum/wire_hint - var/last_state = null // Current state of the hint. Can be TRUE, FALSE, or null if nobody has interacted yet. - var/true_text // Text to display in the hacking window when the current state is true. - var/false_text // Ditto, but shown when false. - -/datum/wire_hint/New(new_true_text, new_false_text) - true_text = new_true_text - false_text = new_false_text - -// Returns text based on the state being inputted. -// If that state is different from last time, the text will be bolded. -/datum/wire_hint/proc/show(current_state) - var/state_changed = FALSE - if(last_state != null) - if(last_state != current_state) - state_changed = TRUE - last_state = current_state - if(last_state) - return state_changed ? "
[true_text]" : "
[true_text]" - return state_changed ? "
[false_text]" : "
[false_text]" - -/datum/wire_hint/proc/reset_memory() - last_state = null \ No newline at end of file diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 2824c1b078..645d7cc043 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -17,6 +17,9 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/wire_count = 0 // Max is 16 var/wires_status = 0 // BITFLAG OF WIRES + var/hint_states = 0 // BITFLAG OF HINT STATES (For tracking if they changed for bolding in UI) + var/hint_states_initialized = FALSE // False until first time window is rendered. + var/list/wires = list() var/list/signallers = list() @@ -26,6 +29,19 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/window_x = 370 var/window_y = 470 +// Note: Its assumed states are boolean. If you ever have a multi-state hint, you must implement that yourself. +/datum/wires/proc/show_hint(flag, current_state, true_text, false_text) + var/state_changed = FALSE + if(hint_states_initialized) + if(!(hint_states & flag) != !current_state) // NOT-ing to convert to boolean + state_changed = TRUE + if(current_state) + hint_states |= flag + return state_changed ? "
[true_text]" : "
[true_text]" + else + hint_states &= ~flag + return state_changed ? "
[false_text]" : "
[false_text]" + /datum/wires/New(var/atom/holder) ..() src.holder = holder @@ -45,7 +61,6 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" else var/list/wires = same_wires[holder_type] src.wires = wires // Reference the wires list. - make_wire_hints() /datum/wires/Destroy() holder = null @@ -76,6 +91,7 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/html = null if(holder && CanUse(user)) html = GetInteractWindow() + hint_states_initialized = TRUE if(html) user.set_machine(holder) else @@ -109,10 +125,6 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" return html -// Override to spawn the wire hints here, to avoid touching New(). -/datum/wires/proc/make_wire_hints() - return - /datum/wires/Topic(href, href_list) ..() if(in_range(holder, usr) && isliving(usr)) diff --git a/code/defines/obj.dm b/code/defines/obj.dm index 76ac3a2b1f..d4a4be4553 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -124,7 +124,7 @@ var/global/list/PDA_Manifest = list() if(depthead && car.len != 1) car.Swap(1,car.len) - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) department = 1 if(depthead && civ.len != 1) diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index 98873ac0c3..b21e3c0025 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -66,7 +66,6 @@ attack_verb = list("HONKED") var/spam_flag = 0 - /obj/item/weapon/c_tube name = "cardboard tube" desc = "A tube... of cardboard." @@ -77,141 +76,6 @@ throw_speed = 4 throw_range = 5 -/obj/item/weapon/cane - name = "cane" - desc = "A cane used by a true gentleman." - icon = 'icons/obj/weapons.dmi' - icon_state = "cane" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - force = 5.0 - throwforce = 7.0 - w_class = ITEMSIZE_NORMAL - matter = list(DEFAULT_WALL_MATERIAL = 50) - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - -/obj/item/weapon/cane/concealed - var/concealed_blade - -/obj/item/weapon/cane/concealed/New() - ..() - var/obj/item/weapon/material/butterfly/switchblade/temp_blade = new(src) - concealed_blade = temp_blade - temp_blade.attack_self() - -/obj/item/weapon/cane/concealed/attack_self(var/mob/user) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - if(concealed_blade) - user.visible_message("[user] has unsheathed \a [concealed_blade] from [T.his] [src]!", "You unsheathe \the [concealed_blade] from \the [src].") - // Calling drop/put in hands to properly call item drop/pickup procs - playsound(user.loc, 'sound/weapons/holster/sheathout.ogg', 50, 1) - user.drop_from_inventory(src) - user.put_in_hands(concealed_blade) - user.put_in_hands(src) - user.update_inv_l_hand(0) - user.update_inv_r_hand() - concealed_blade = null - else - ..() - -/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/material/butterfly/W, var/mob/user) - if(!src.concealed_blade && istype(W)) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - user.visible_message("[user] has sheathed \a [W] into [T.his] [src]!", "You sheathe \the [W] into \the [src].") - playsound(user.loc, 'sound/weapons/holster/sheathin.ogg', 50, 1) - user.drop_from_inventory(W) - W.loc = src - src.concealed_blade = W - update_icon() - else - ..() - -/obj/item/weapon/cane/concealed/update_icon() - if(concealed_blade) - name = initial(name) - icon_state = initial(icon_state) - item_state = initial(icon_state) - else - name = "cane shaft" - icon_state = "nullrod" - item_state = "foldcane" - -/obj/item/weapon/cane/whitecane - name = "white cane" - desc = "A white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." - icon = 'icons/obj/weapons.dmi' - icon_state = "whitecane" - -/obj/item/weapon/cane/whitecane/attack(mob/M as mob, mob/user as mob) - if(user.a_intent == I_HELP) - user.visible_message("\The [user] has lightly tapped [M] on the ankle with their white cane!") - return - else - ..() - -/obj/item/weapon/cane/crutch - name ="crutch" - desc = "A long stick with a crosspiece at the top, used to help with walking." - icon_state = "crutch" - item_state = "crutch" - -//Code for Telescopic White Cane writen by Gozulio - -/obj/item/weapon/melee/collapsable_whitecane - name = "telescopic white cane" - desc = "A telescoping white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." - icon = 'icons/obj/weapons.dmi' - icon_state = "whitecane1in" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - force = 3 - var/on = 0 - -/obj/item/weapon/melee/collapsable_whitecane/attack_self(mob/user as mob) - on = !on - if(on) - user.visible_message("\The [user] extends the white cane.",\ - "You extend the white cane.",\ - "You hear an ominous click.") - icon_state = "whitecane1out" - item_state_slots = list(slot_r_hand_str = "whitecane", slot_l_hand_str = "whitecane") - w_class = ITEMSIZE_NORMAL - force = 5 - attack_verb = list("smacked", "struck", "cracked", "beaten") - else - user.visible_message("\The [user] collapses the white cane.",\ - "You collapse the white cane.",\ - "You hear a click.") - icon_state = "whitecane1in" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - w_class = ITEMSIZE_SMALL - force = 3 - attack_verb = list("hit", "poked") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) - add_fingerprint(user) - - return - -/obj/item/weapon/melee/collapsable_whitecane/attack(mob/M as mob, mob/user as mob) - if(user.a_intent == I_HELP) - user.visible_message("\The [user] has lightly tapped [M] on the ankle with their white cane!") - return - else - ..() - - /obj/item/weapon/disk name = "disk" icon = 'icons/obj/items.dmi' diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index 2de7f72124..962c91a621 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -7,6 +7,7 @@ // Strings. var/welcome_text = "Cry havoc and let slip the dogs of war!" + var/antag_sound = 'sound/effects/antag_notice/general_baddie_alert.ogg' // The sound file to play when someone gets this role. Only they can hear it. var/leader_welcome_text // Text shown to the leader, if any. var/victory_text // World output at roundend for victory. var/loss_text // As above for loss. diff --git a/code/game/antagonist/antagonist_create.dm b/code/game/antagonist/antagonist_create.dm index 07046401c3..84c5b2317e 100644 --- a/code/game/antagonist/antagonist_create.dm +++ b/code/game/antagonist/antagonist_create.dm @@ -95,6 +95,10 @@ return code /datum/antagonist/proc/greet(var/datum/mind/player) + // Makes it harder to miss if you're alt-tabbed or not paying attention. + if(antag_sound) + SEND_SOUND(player.current, sound(antag_sound)) + window_flash(player.current.client) // Basic intro text. to_chat(player.current, "You are a [role_text]!") diff --git a/code/game/antagonist/outsider/commando.dm b/code/game/antagonist/outsider/commando.dm index 3ea5c233ff..b7bd756c97 100644 --- a/code/game/antagonist/outsider/commando.dm +++ b/code/game/antagonist/outsider/commando.dm @@ -6,6 +6,7 @@ var/datum/antagonist/deathsquad/mercenary/commandos role_text = "Syndicate Commando" role_text_plural = "Commandos" welcome_text = "You are in the employ of a criminal syndicate hostile to corporate interests." + antag_sound = 'sound/effects/antag_notice/deathsquid_alert.ogg' id_type = /obj/item/weapon/card/id/centcom/ERT hard_cap = 4 diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index db281b6dbb..01b579b7de 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -6,6 +6,7 @@ var/datum/antagonist/deathsquad/deathsquad role_text = "Death Commando" role_text_plural = "Death Commandos" welcome_text = "You work in the service of corporate Asset Protection, answering directly to the Board of Directors." + antag_sound = 'sound/effects/antag_notice/deathsquid_alert.ogg' landmark_id = "Commando" flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_HAS_NUKE | ANTAG_HAS_LEADER default_access = list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage) diff --git a/code/game/antagonist/outsider/ert.dm b/code/game/antagonist/outsider/ert.dm index 670d1c9b93..8b3301afc7 100644 --- a/code/game/antagonist/outsider/ert.dm +++ b/code/game/antagonist/outsider/ert.dm @@ -7,6 +7,7 @@ var/datum/antagonist/ert/ert role_text = "Emergency Responder" role_text_plural = "Emergency Responders" welcome_text = "As member of the Emergency Response Team, you answer only to your leader and company officials." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are an anti antagonist! Within the rules, \ try to save the station and its inhabitants from the ongoing crisis. \ Try to make sure other players have fun! If you are confused or at a loss, always adminhelp, \ diff --git a/code/game/antagonist/outsider/technomancer.dm b/code/game/antagonist/outsider/technomancer.dm index 39fe4409bc..579e5631d7 100644 --- a/code/game/antagonist/outsider/technomancer.dm +++ b/code/game/antagonist/outsider/technomancer.dm @@ -10,7 +10,8 @@ var/datum/antagonist/technomancer/technomancers welcome_text = "You will need to purchase functions and perhaps some equipment from the various machines around your \ base. Choose your technological arsenal carefully. Remember that without the core on your back, your functions are \ powerless, and therefore you will be as well.
\ - In your pockets you will find a one-time use teleport device. Use it to leave the base and go to the colony, when you are ready." + In your pockets you will find a one-time use teleport device. Use it to leave the base and go to the station, when you are ready." + antag_sound = 'sound/effects/antag_notice/technomancer_alert.ogg' flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_SET_APPEARANCE | ANTAG_VOTABLE antaghud_indicator = "hudwizard" diff --git a/code/game/antagonist/outsider/trader.dm b/code/game/antagonist/outsider/trader.dm index d6e938932e..07cfb6f9e9 100644 --- a/code/game/antagonist/outsider/trader.dm +++ b/code/game/antagonist/outsider/trader.dm @@ -6,6 +6,7 @@ var/datum/antagonist/trader/traders role_text = "Trader" role_text_plural = "Traders" welcome_text = "As a crewmember of the Beruang, you answer to your captain and international laws of space." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are an non-antagonist visitor! Within the rules, \ try to provide interesting interaction for the crew. \ Try to make sure other players have fun! If you are confused or at a loss, always adminhelp, \ diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm index a7bed0a2af..860e73a052 100644 --- a/code/game/antagonist/station/changeling.dm +++ b/code/game/antagonist/station/changeling.dm @@ -8,6 +8,7 @@ restricted_jobs = list("AI", "Cyborg") protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Colony Director") welcome_text = "Use say \"#g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them." + antag_sound = 'sound/effects/antag_notice/ling_alert.ogg' flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE antaghud_indicator = "hudchangeling" diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm index 3f40cfc9ea..dc98622ba7 100644 --- a/code/game/antagonist/station/cultist.dm +++ b/code/game/antagonist/station/cultist.dm @@ -18,6 +18,7 @@ var/datum/antagonist/cultist/cult feedback_tag = "cult_objective" antag_indicator = "cult" welcome_text = "You have a talisman in your possession; one that will help you start the cult on this station. Use it well and remember - there are others." + antag_sound = 'sound/effects/antag_notice/cult_alert.ogg' victory_text = "The cult wins! It has succeeded in serving its dark masters!" loss_text = "The staff managed to stop the cult!" victory_feedback_tag = "win - cult win" diff --git a/code/game/antagonist/station/loyalist.dm b/code/game/antagonist/station/loyalist.dm index 54e9fa4d4e..9b2333b491 100644 --- a/code/game/antagonist/station/loyalist.dm +++ b/code/game/antagonist/station/loyalist.dm @@ -9,6 +9,7 @@ var/datum/antagonist/loyalists/loyalists feedback_tag = "loyalist_objective" antag_indicator = "loyal_head" welcome_text = "You belong to the Company, body and soul. Preserve its interests against the conspirators amongst the crew." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' victory_text = "The heads of staff remained at their posts! The loyalists win!" loss_text = "The heads of staff did not stop the revolution!" victory_feedback_tag = "win - rev heads killed" diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm index 7db152503f..ed3c804506 100644 --- a/code/game/antagonist/station/renegade.dm +++ b/code/game/antagonist/station/renegade.dm @@ -8,6 +8,7 @@ var/datum/antagonist/renegade/renegades bantype = "renegade" restricted_jobs = list("AI", "Cyborg") welcome_text = "Something's going to go wrong today, you can just feel it. You're paranoid, you've got a gun, and you're going to survive." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are a minor antagonist! Within the rules, \ try to protect yourself and what's important to you. You aren't here to cause trouble, \ you're just more willing (and equipped) to go to extremes to stop it than others are. \ diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index 935d486f34..4fcb678d34 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -8,6 +8,7 @@ var/datum/antagonist/rogue_ai/malf mob_path = /mob/living/silicon/ai landmark_id = "AI" welcome_text = "You are malfunctioning! You do not have to follow any laws." + antag_sound = 'sound/effects/antag_notice/malf_alert.ogg' victory_text = "The AI has taken control of all of the station's systems." loss_text = "The AI has been shut down!" flags = ANTAG_VOTABLE | ANTAG_OVERRIDE_MOB | ANTAG_OVERRIDE_JOB | ANTAG_CHOOSE_NAME diff --git a/code/game/antagonist/station/traitor.dm b/code/game/antagonist/station/traitor.dm index 3829d43166..23d0758e8b 100644 --- a/code/game/antagonist/station/traitor.dm +++ b/code/game/antagonist/station/traitor.dm @@ -3,6 +3,7 @@ var/datum/antagonist/traitor/traitors // Inherits most of its vars from the base datum. /datum/antagonist/traitor id = MODE_TRAITOR + antag_sound = 'sound/effects/antag_notice/traitor_alert.ogg' protected_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "Head of Security", "Colony Director") flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE can_speak_aooc = FALSE // If they want to plot and plan as this sort of traitor, they'll need to do it ICly. diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 2a4b933edf..963530d671 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -62,8 +62,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //////////// //SHUTTLES// //////////// -//shuttle areas must contain at least two areas in a subgroup if you want to move a shuttle from one -//place to another. Look at escape shuttle for example. +//Shuttles only need starting area, movement is handled by landmarks //All shuttles should now be under shuttle since we have smooth-wall code. /area/shuttle @@ -77,160 +76,55 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Arrival Shuttle" ambience = AMBIENCE_ARRIVALS -/area/shuttle/arrival/pre_game +/area/shuttle/supply + name = "\improper Supply Shuttle" icon_state = "shuttle2" -/area/shuttle/arrival/station - icon_state = "shuttle" - dynamic_lighting = 0 - ambience = AMBIENCE_ARRIVALS - /area/shuttle/escape name = "\improper Emergency Shuttle" music = "music/escape.ogg" -/area/shuttle/escape/station - name = "\improper Emergency Shuttle Station" - icon_state = "shuttle2" - dynamic_lighting = 0 - -/area/shuttle/escape/centcom - name = "\improper Emergency Shuttle CentCom" - icon_state = "shuttle" - -/area/shuttle/escape/transit // the area to pass through for 3 minute transit - name = "\improper Emergency Shuttle Transit" - icon_state = "shuttle" - /area/shuttle/escape_pod1 name = "\improper Escape Pod One" music = "music/escape.ogg" -/area/shuttle/escape_pod1/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod1/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod1/transit - icon_state = "shuttle" - /area/shuttle/escape_pod2 name = "\improper Escape Pod Two" music = "music/escape.ogg" -/area/shuttle/escape_pod2/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod2/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod2/transit - icon_state = "shuttle" - /area/shuttle/escape_pod3 name = "\improper Escape Pod Three" music = "music/escape.ogg" -/area/shuttle/escape_pod3/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod3/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod3/transit - icon_state = "shuttle" - /area/shuttle/escape_pod4 name = "\improper Escape Pod Four" music = "music/escape.ogg" -/area/shuttle/escape_pod4/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod4/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod4/transit - icon_state = "shuttle" - /area/shuttle/escape_pod5 name = "\improper Escape Pod Five" music = "music/escape.ogg" -/area/shuttle/escape_pod5/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod5/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod5/transit - icon_state = "shuttle" - /area/shuttle/escape_pod6 name = "\improper Escape Pod Six" music = "music/escape.ogg" -/area/shuttle/escape_pod6/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod6/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod6/transit - icon_state = "shuttle" - /area/shuttle/large_escape_pod1 name = "\improper Large Escape Pod One" music = "music/escape.ogg" -/area/shuttle/large_escape_pod1/station - icon_state = "shuttle2" - -/area/shuttle/large_escape_pod1/centcom - icon_state = "shuttle" - -/area/shuttle/large_escape_pod1/transit - icon_state = "shuttle" - /area/shuttle/large_escape_pod2 name = "\improper Large Escape Pod Two" music = "music/escape.ogg" -/area/shuttle/large_escape_pod2/station - icon_state = "shuttle2" - -/area/shuttle/large_escape_pod2/centcom - icon_state = "shuttle" - -/area/shuttle/large_escape_pod2/transit - icon_state = "shuttle" - /area/shuttle/cryo name = "\improper Cryogenic Storage" -/area/shuttle/cryo/station - icon_state = "shuttle2" - base_turf = /turf/simulated/mineral/floor/ignore_mapgen - -/area/shuttle/cryo/centcom - icon_state = "shuttle" - -/area/shuttle/cryo/transit - icon_state = "shuttle" - /area/shuttle/mining name = "\improper Mining Elevator" music = "music/escape.ogg" dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen -/area/shuttle/mining/station - icon_state = "shuttle2" - -/area/shuttle/mining/outpost - icon_state = "shuttle" - /area/shuttle/transport1/centcom icon_state = "shuttle" name = "\improper Transport Shuttle CentCom" @@ -287,54 +181,15 @@ NOTE: there are two lists of areas in the end of this file: centcom and station icon_state = "red" dynamic_lighting = 0 -/area/shuttle/trade/centcom - name = "\improper Trade Shuttle CentCom" - icon_state = "shuttlered" - -/area/shuttle/trade/station - name = "\improper Trade Shuttle" - icon_state = "shuttlered" - /area/shuttle/thunderdome name = "honk" -/area/shuttle/thunderdome/grnshuttle - name = "\improper Thunderdome GRN Shuttle" - icon_state = "green" - -/area/shuttle/thunderdome/grnshuttle/dome - name = "\improper GRN Shuttle" - icon_state = "shuttlegrn" - -/area/shuttle/thunderdome/grnshuttle/station - name = "\improper GRN Station" - icon_state = "shuttlegrn2" - -/area/shuttle/thunderdome/redshuttle - name = "\improper Thunderdome RED Shuttle" - icon_state = "red" - -/area/shuttle/thunderdome/redshuttle/dome - name = "\improper RED Shuttle" - icon_state = "shuttlered" - -/area/shuttle/thunderdome/redshuttle/station - name = "\improper RED Station" - icon_state = "shuttlered2" -// === Trying to remove these areas: - /area/shuttle/research name = "\improper Research Elevator" music = "music/escape.ogg" dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen -/area/shuttle/research/station - icon_state = "shuttle2" - -/area/shuttle/research/outpost - icon_state = "shuttle" - /area/airtunnel1/ // referenced in airtunnel.dm:759 /area/dummy/ // Referenced in engine.dm:261 @@ -2046,17 +1901,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Cargo Mining Dock" icon_state = "mining" -/area/supply/station - name = "Supply Shuttle" - icon_state = "shuttle3" - requires_power = 0 - base_turf = /turf/space - -/area/supply/dock - name = "Supply Shuttle" - icon_state = "shuttle3" - requires_power = 0 - base_turf = /turf/space // SCIENCE diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index ef6e13f711..77fcc8db35 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -72,6 +72,31 @@ power_change() // all machines set to current power level, also updates lighting icon return INITIALIZE_HINT_LATELOAD +// Changes the area of T to A. Do not do this manually. +// Area is expected to be a non-null instance. +/proc/ChangeArea(var/turf/T, var/area/A) + if(!istype(A)) + CRASH("Area change attempt failed: invalid area supplied.") + var/area/old_area = get_area(T) + if(old_area == A) + return + // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s + // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. + A.contents.Add(T) + if(old_area) + // Handle dynamic lighting update if + if(T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) + if(A.dynamic_lighting) + T.lighting_build_overlay() + else + T.lighting_clear_overlay() + for(var/atom/movable/AM in T) + old_area.Exited(AM, A) + for(var/atom/movable/AM in T) + A.Entered(AM, old_area) + for(var/obj/machinery/M in T) + M.power_change() + /area/proc/get_contents() return contents @@ -299,10 +324,10 @@ var/list/mob/living/forced_ambiance_list = new L << sound(sound, repeat = 0, wait = 0, volume = 50, channel = CHANNEL_AMBIENCE) L.client.time_last_ambience_played = world.time -/area/proc/gravitychange(var/gravitystate = 0, var/area/A) - A.has_gravity = gravitystate +/area/proc/gravitychange(var/gravitystate = 0) + src.has_gravity = gravitystate - for(var/mob/M in A) + for(var/mob/M in src) if(has_gravity) thunk(M) M.update_floating( M.Check_Dense_Object() ) diff --git a/code/game/area/ss13_deprecated_areas.dm b/code/game/area/ss13_deprecated_areas.dm new file mode 100644 index 0000000000..b3b689f23d --- /dev/null +++ b/code/game/area/ss13_deprecated_areas.dm @@ -0,0 +1,164 @@ +// +// Shuttles formerly required at least two areas in a subgroup if you want to move a shuttle from one +// place to another. Since shuttles now used landmarks instead these areas are deprecated! +// They are left here for the moment in order to make existing maps loadable, but should be phased out. +// + +/area/shuttle/arrival/pre_game + icon_state = "shuttle2" + +/area/shuttle/arrival/station + icon_state = "shuttle" + dynamic_lighting = 0 + ambience = AMBIENCE_ARRIVALS + +/area/shuttle/escape/station + name = "\improper Emergency Shuttle Station" + icon_state = "shuttle2" + dynamic_lighting = 0 + +/area/shuttle/escape/centcom + name = "\improper Emergency Shuttle CentCom" + icon_state = "shuttle" + +/area/shuttle/escape/transit // the area to pass through for 3 minute transit + name = "\improper Emergency Shuttle Transit" + icon_state = "shuttle" + +/area/shuttle/escape_pod1/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod1/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod1/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod2/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod2/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod2/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod3/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod3/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod3/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod4/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod4/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod4/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod5/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod5/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod5/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod6/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod6/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod6/transit + icon_state = "shuttle" + +/area/shuttle/large_escape_pod1/station + icon_state = "shuttle2" + +/area/shuttle/large_escape_pod1/centcom + icon_state = "shuttle" + +/area/shuttle/large_escape_pod1/transit + icon_state = "shuttle" + +/area/shuttle/large_escape_pod2/station + icon_state = "shuttle2" + +/area/shuttle/large_escape_pod2/centcom + icon_state = "shuttle" + +/area/shuttle/large_escape_pod2/transit + icon_state = "shuttle" + +/area/shuttle/cryo/station + icon_state = "shuttle2" + base_turf = /turf/simulated/mineral/floor/ignore_mapgen + +/area/shuttle/cryo/centcom + icon_state = "shuttle" + +/area/shuttle/cryo/transit + icon_state = "shuttle" + +/area/shuttle/mining/station + icon_state = "shuttle2" + +/area/shuttle/mining/outpost + icon_state = "shuttle" + +/area/shuttle/trade/centcom + name = "\improper Trade Shuttle CentCom" + icon_state = "shuttlered" + +/area/shuttle/trade/station + name = "\improper Trade Shuttle" + icon_state = "shuttlered" + +/area/shuttle/thunderdome/grnshuttle + name = "\improper Thunderdome GRN Shuttle" + icon_state = "green" + +/area/shuttle/thunderdome/grnshuttle/dome + name = "\improper GRN Shuttle" + icon_state = "shuttlegrn" + +/area/shuttle/thunderdome/grnshuttle/station + name = "\improper GRN Station" + icon_state = "shuttlegrn2" + +/area/shuttle/thunderdome/redshuttle + name = "\improper Thunderdome RED Shuttle" + icon_state = "red" + +/area/shuttle/thunderdome/redshuttle/dome + name = "\improper RED Shuttle" + icon_state = "shuttlered" + +/area/shuttle/thunderdome/redshuttle/station + name = "\improper RED Station" + icon_state = "shuttlered2" + +/area/shuttle/research/station + icon_state = "shuttle2" + +/area/shuttle/research/outpost + icon_state = "shuttle" + +/area/supply/station + name = "Supply Shuttle" + icon_state = "shuttle3" + requires_power = 0 + base_turf = /turf/space + +/area/supply/dock + name = "Supply Shuttle" + icon_state = "shuttle3" + requires_power = 0 + base_turf = /turf/space diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index a320f6340c..bc5faf868a 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -44,7 +44,7 @@ pulledby = null /atom/movable/vv_edit_var(var_name, var_value) - if(GLOB.VVpixelmovement[var_name]) //Pixel movement is not yet implemented, changing this will break everything irreversibly. + if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. return FALSE return ..() @@ -446,7 +446,7 @@ if(z in using_map.sealed_levels) return - if(config.use_overmap) + if(using_map.use_overmap) overmap_spacetravel(get_turf(src), src) return diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index e4744693ab..6b4794dff4 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -46,7 +46,7 @@ icon_state = "scanner_0" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 active_power_usage = 300 interact_offline = 1 @@ -260,7 +260,7 @@ var/obj/item/weapon/disk/data/disk = null var/selected_menu_key = null anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 400 var/waiting_for_user_input=0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 45e852551d..0089355ed3 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -69,7 +69,7 @@ O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) O.adjustToxLoss(M.getToxLoss() + 20) O.adjustOxyLoss(M.getOxyLoss()) - O.stat = M.stat + O.set_stat(M.stat) O.a_intent = I_HURT for (var/obj/item/weapon/implant/I in implants) I.loc = O @@ -154,7 +154,7 @@ O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) O.adjustToxLoss(M.getToxLoss()) O.adjustOxyLoss(M.getOxyLoss()) - O.stat = M.stat + O.set_stat(M.stat) for (var/obj/item/weapon/implant/I in implants) I.loc = O I.implanted = O diff --git a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm index d5be03c6b1..5d3c1a3be9 100644 --- a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm +++ b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm @@ -29,7 +29,7 @@ var/mob/living/carbon/human/C = src to_chat(C, "Energy rushes through us. [C.lying ? "We arise." : ""]") - C.stat = 0 + C.set_stat(CONSCIOUS) C.SetParalysis(0) C.SetStunned(0) C.SetWeakened(0) diff --git a/code/game/gamemodes/changeling/powers/lesser_form.dm b/code/game/gamemodes/changeling/powers/lesser_form.dm index 9d7c4de3d1..d7b9cb0489 100644 --- a/code/game/gamemodes/changeling/powers/lesser_form.dm +++ b/code/game/gamemodes/changeling/powers/lesser_form.dm @@ -100,7 +100,7 @@ O.adjustBruteLoss(C.getBruteLoss()) O.setOxyLoss(C.getOxyLoss()) O.adjustFireLoss(C.getFireLoss()) - O.stat = C.stat + O.set_stat(C.stat) for (var/obj/item/weapon/implant/I in implants) I.loc = O I.implanted = O diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm index 2439fbe38a..61a061170e 100644 --- a/code/game/gamemodes/changeling/powers/revive.dm +++ b/code/game/gamemodes/changeling/powers/revive.dm @@ -79,7 +79,7 @@ C.update_canmove() C.mind.changeling.purchased_powers -= C feedback_add_details("changeling_powers","CR") - C.stat = CONSCIOUS + C.set_stat(CONSCIOUS) C.forbid_seeing_deadchat = FALSE C.timeofdeath = null src.verbs -= /mob/proc/changeling_revive diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index 92ec9cf0c5..12f39fd59d 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -201,10 +201,10 @@ var/global/datum/controller/gameticker/ticker var/turf/T = get_turf(M) if(T && T.z in using_map.station_levels) //we don't use M.death(0) because it calls a for(/mob) loop and M.health = 0 - M.stat = DEAD + M.set_stat(DEAD) if(1) //on a z-level 1 turf. M.health = 0 - M.stat = DEAD + M.set_stat(DEAD) //Now animate the cinematic switch(station_missed) diff --git a/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm b/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm index 4135269219..b9bc8bc2c4 100644 --- a/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm +++ b/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm @@ -40,5 +40,4 @@ else L.electrocute_act(power, src, 0.75, BP_TORSO) - - adjust_instability(3) \ No newline at end of file + adjust_instability(3) diff --git a/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm b/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm index b16e9ea3c4..282d914d3c 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm @@ -24,4 +24,7 @@ icon_state = "force_missile" damage = 25 damage_type = BRUTE - check_armour = "melee" \ No newline at end of file + check_armour = "melee" + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/projectile/overload.dm b/code/game/gamemodes/technomancer/spells/projectile/overload.dm index 50d6d22a95..0ab1772674 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/overload.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/overload.dm @@ -27,6 +27,8 @@ icon_state = "bluespace" damage_type = BURN armor_penetration = 100 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' /obj/item/weapon/spell/projectile/overload/make_projectile(obj/item/projectile/projectile_type, mob/living/user) var/obj/item/projectile/overload/P = new projectile_type(get_turf(user)) diff --git a/code/game/gamemodes/technomancer/spells/resurrect.dm b/code/game/gamemodes/technomancer/spells/resurrect.dm index 61ad874cad..02c072b90d 100644 --- a/code/game/gamemodes/technomancer/spells/resurrect.dm +++ b/code/game/gamemodes/technomancer/spells/resurrect.dm @@ -32,7 +32,7 @@ if(istype(L, /mob/living/simple_mob)) var/mob/living/simple_mob/SM = L SM.health = SM.getMaxHealth() / 3 - SM.stat = CONSCIOUS + SM.set_stat(CONSCIOUS) dead_mob_list -= SM living_mob_list += SM SM.update_icon() @@ -43,9 +43,8 @@ if(!H.client && H.mind) //Don't force the dead person to come back if they don't want to. for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == H.mind) - to_chat(ghost, "The Technomancer [user.real_name] is trying to \ - revive you. Return to your body if you want to be resurrected! \ - (Verbs -> Ghost -> Re-enter corpse)") + ghost.notify_revive("The Technomancer [user.real_name] is trying to revive you. \ + Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') break H.adjustBruteLoss(-40) @@ -53,7 +52,7 @@ sleep(10 SECONDS) if(H.client) - L.stat = CONSCIOUS //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again. + L.set_stat(CONSCIOUS) //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again. dead_mob_list -= H living_mob_list += H H.timeofdeath = null diff --git a/code/game/jobs/job/assistant.dm b/code/game/jobs/job/assistant.dm index 163449128a..df9935fe77 100644 --- a/code/game/jobs/job/assistant.dm +++ b/code/game/jobs/job/assistant.dm @@ -18,7 +18,7 @@ outfit_type = /decl/hierarchy/outfit/job/assistant job_description = "An Assistant does whatever is requested of them. Though they are part of the crew, they have no real authority." - alt_titles = list("Assistant" = /datum/alt_title/assistant, "Technical Assistant" = /datum/alt_title/tech_assist, + alt_titles = list("Technical Assistant" = /datum/alt_title/tech_assist, "Medical Intern"= /datum/alt_title/med_intern, "Research Assistant" = /datum/alt_title/research_assist, "Visitor" = /datum/alt_title/visitor) @@ -29,10 +29,6 @@ return list() // Assistant Alt Titles -/datum/alt_title/assistant - title = "Assistant" - title_blurb = "An Assistant does whatever is requested of them. Though they are part of the crew, they have no real authority." - /datum/alt_title/tech_assist title = "Technical Assistant" title_blurb = "A Technical Assistant attempts to provide whatever the Engineering department needs. They are not proper Engineers, and are \ diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index 5c4740e732..e58587ca66 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -29,7 +29,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) job_description = "The Colony Director manages the other Command Staff, and through them the rest of the station. Though they have access to everything, \ they do not understand everything, and are expected to delegate tasks to the appropriate crew member. The Colony Director is expected to \ have an understanding of Standard Operating Procedure, and is subject to it, and legal action, in the same way as every other crew member." - alt_titles = list("Colony Director" = /datum/alt_title/captain, "Site Manager" = /datum/alt_title/site_manager, + alt_titles = list("Site Manager" = /datum/alt_title/site_manager, "Overseer" = /datum/alt_title/overseer) /* @@ -44,9 +44,6 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) // Captain Alt Titles -/datum/alt_title/captain // Screw it, this is the default, it has the default path - title = "Colony Director" - /datum/alt_title/site_manager title = "Site Manager" @@ -79,7 +76,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) job_description = "The Head of Personnel manages the Service department, the Exploration team, and most other civilians. They also \ manage the Supply department, through the Quartermaster. In addition, the Head of Personnel oversees the personal accounts \ of the crew, including their money and access. If necessary, the Head of Personnel is first in line to assume Acting Command." - alt_titles = list("Head of Personnel" = /datum/alt_title/hop, "Crew Resources Officer" = /datum/alt_title/cro) + alt_titles = list("Crew Resources Officer" = /datum/alt_title/cro) access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, @@ -95,9 +92,6 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) access_hop, access_RC_announce, access_keycard_auth, access_gateway) // HOP Alt Titles -/datum/alt_title/hop - title = "Head of Personnel" - /datum/alt_title/cro title = "Crew Resources Officer" @@ -126,6 +120,3 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) job_description = "A Command Secretary handles paperwork duty for the Heads of Staff, so they can better focus on managing their departments. \ They are not Heads of Staff, and have no real authority." -// Command Secretary Alt Title -/datum/alt_title/command_secretary - title = "Command Secretary" \ No newline at end of file diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index d667d2bc2c..5e715b22e1 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -19,12 +19,9 @@ outfit_type = /decl/hierarchy/outfit/job/service/bartender job_description = "A Bartender mixes drinks for the crew. They generally have permission to charge for drinks or deny service to unruly patrons." - alt_titles = list("Bartender" = /datum/alt_title/bartender, "Barista" = /datum/alt_title/barista) + alt_titles = list("Barista" = /datum/alt_title/barista) // Bartender Alt Titles -/datum/alt_title/bartender - title = "Bartender" - /datum/alt_title/barista title = "Barista" title_blurb = "A barista mans the Cafe, serving primarily non-alcoholic drinks to the crew. They generally have permission to charge for drinks \ @@ -50,12 +47,9 @@ outfit_type = /decl/hierarchy/outfit/job/service/chef job_description = "A Chef cooks food for the crew. They generally have permission to charge for food or deny service to unruly diners." - alt_titles = list("Chef" = /datum/alt_title/chef, "Cook" = /datum/alt_title/cook) + alt_titles = list("Cook" = /datum/alt_title/cook) // Chef Alt Titles -/datum/alt_title/chef - title = "Chef" - /datum/alt_title/cook title = "Cook" title_blurb = "A Cook has the same duties, though they may be less experienced." @@ -79,12 +73,9 @@ outfit_type = /decl/hierarchy/outfit/job/service/gardener job_description = "A Botanist grows plants for the Chef and Bartender." - alt_titles = list("Botanist" = /datum/alt_title/botanist, "Gardener" = /datum/alt_title/gardener) + alt_titles = list("Gardener" = /datum/alt_title/gardener) //Botanist Alt Titles -/datum/alt_title/botanist - title = "Botanist" - /datum/alt_title/gardener title = "Gardener" title_blurb = "A Gardener may be less professional than their counterparts, and are more likely to tend to the public gardens if they aren't needed elsewhere." @@ -113,12 +104,9 @@ outfit_type = /decl/hierarchy/outfit/job/cargo/qm job_description = "The Quartermaster manages the Supply department, checking cargo orders and ensuring supplies get to where they are needed." - alt_titles = list("Quartermaster" = /datum/alt_title/qm, "Supply Chief" = /datum/alt_title/supply_chief) + alt_titles = list("Supply Chief" = /datum/alt_title/supply_chief) // Quartermaster Alt Titles -/datum/alt_title/qm - title = "Quartermaster" - /datum/alt_title/supply_chief title = "Supply Chief" @@ -142,10 +130,6 @@ job_description = "A Cargo Technician fills and delivers cargo orders. They are encouraged to return delivered crates to the Cargo Shuttle, \ because Central Command gives a partial refund." -// Cargo Tech Alt Titles -/datum/alt_title/cargo_tech - title = "Cargo Tech" - ////////////////////////////////// // Shaft Miner ////////////////////////////////// @@ -166,11 +150,7 @@ outfit_type = /decl/hierarchy/outfit/job/cargo/mining job_description = "A Shaft Miner mines and processes minerals to be delivered to departments that need them." - alt_titles = list("Shaft Miner" = /datum/alt_title/miner, "Drill Technician" = /datum/alt_title/drill_tech) - -// Shaft Miner Alt Titles -/datum/alt_title/miner - title = "Shaft Miner" + alt_titles = list("Drill Technician" = /datum/alt_title/drill_tech) /datum/alt_title/drill_tech title = "Drill Technician" @@ -195,12 +175,9 @@ outfit_type = /decl/hierarchy/outfit/job/service/janitor job_description = "A Janitor keeps the station clean, as long as it doesn't interfere with active crime scenes." - alt_titles = list("Janitor" = /datum/alt_title/janitor, "Custodian" = /datum/alt_title/custodian) + alt_titles = list("Custodian" = /datum/alt_title/custodian) // Janitor Alt Titles -/datum/alt_title/janitor - title = "Janitor" - /datum/alt_title/custodian title = "Custodian" @@ -223,12 +200,9 @@ outfit_type = /decl/hierarchy/outfit/job/librarian job_description = "The Librarian curates the book selection in the Library, so the crew might enjoy it." - alt_titles = list("Librarian" = /datum/alt_title/librarian, "Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer) + alt_titles = list("Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer) // Librarian Alt Titles -/datum/alt_title/librarian - title = "Librarian" - /datum/alt_title/journalist title = "Journalist" title_blurb = "The Journalist uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera." @@ -268,7 +242,3 @@ if(.) H.implant_loyalty(H) */ - -// IAA Alt Titles -/datum/alt_title/iaa - title = "Internal Affairs Agent" diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm index 0877e373d7..2f208fb545 100644 --- a/code/game/jobs/job/civilian_chaplain.dm +++ b/code/game/jobs/job/civilian_chaplain.dm @@ -14,12 +14,9 @@ outfit_type = /decl/hierarchy/outfit/job/chaplain job_description = "The Chaplain ministers to the spiritual needs of the crew." - alt_titles = list("Chaplain" = /datum/alt_title/chaplain, "Counselor" = /datum/alt_title/counselor) + alt_titles = list("Counselor" = /datum/alt_title/counselor) // Chaplain Alt Titles -/datum/alt_title/chaplain - title = "Chaplain" - /datum/alt_title/counselor title = "Counselor" title_blurb = "The Counselor attends to the emotional needs of the crew, without a specific medicinal or spiritual focus." diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index 4ac1c08367..285c3dc814 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -35,10 +35,6 @@ of manpower as much as they handle hands-on operations and repairs. They are also expected to keep the rest of the station informed of \ any structural threats to the station that may be hazardous to health or disruptive to work." -// Chief Engineer Alt Titles -/datum/alt_title/chief_engineer - title = "Chief Engineer" - ////////////////////////////////// // Engineer ////////////////////////////////// @@ -55,7 +51,7 @@ economic_modifier = 5 access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics) minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction) - alt_titles = list("Station Engineer" = /datum/alt_title/engineer, "Maintenance Technician" = /datum/alt_title/maint_tech, + alt_titles = list("Maintenance Technician" = /datum/alt_title/maint_tech, "Engine Technician" = /datum/alt_title/engine_tech, "Electrician" = /datum/alt_title/electrician) minimal_player_age = 3 @@ -65,9 +61,6 @@ generated and distributed. On quiet shifts, they may be called upon to make cosmetic alterations to the station." // Engineer Alt Titles -/datum/alt_title/engineer - title = "Station Engineer" - /datum/alt_title/maint_tech title = "Maintenance Technician" title_blurb = "A Maintenance Technician is generally a junior Engineer, and can be expected to run the mildly unpleasant or boring tasks that other \ @@ -105,7 +98,3 @@ outfit_type = /decl/hierarchy/outfit/job/engineering/atmos job_description = "An Atmospheric Technician is primarily concerned with keeping the station's atmosphere breathable. They are expected to have a good \ understanding of the pipes, vents, and scrubbers that move gasses around the station, and to be familiar with proper firefighting procedure." - -// Atmos Tech Alt Titles -/datum/alt_title/atmos_tech - title = "Atmospheric Technician" \ No newline at end of file diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 05cad33ca9..33d15455b8 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -13,7 +13,7 @@ var/current_positions = 0 // How many players have this job var/supervisors = null // Supervisors, who this person answers to directly var/selection_color = "#ffffff" // Selection screen color - var/list/alt_titles = list() // List of alternate titles; if a job has alt-titles, it MUST have one for the base job + var/list/alt_titles = null // List of alternate titles; There is no need for an alt-title datum for the base job title. var/req_admin_notify // If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. var/minimal_player_age = 0 // If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) var/list/departments = list() // List of departments this job belongs to, if any. The first one on the list will be the 'primary' department. @@ -46,13 +46,9 @@ /datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title) if(alt_title && alt_titles) - for(var/alt in alt_titles) - if(alt_title == alt) - var/typepath = alt_titles[alt] - var/datum/alt_title/A = new typepath() - if(A.title_outfit) - . = A.title_outfit - + var/datum/alt_title/A = alt_titles[alt_title] + if(A && initial(A.title_outfit)) + . = initial(A.title_outfit) . = . || outfit_type . = outfit_by_type(.) @@ -133,12 +129,11 @@ message |= job_description if(alt_title && alt_titles) - for(var/alt in alt_titles) - if(alt_title == alt) - var/typepath = alt_titles[alt] - var/datum/alt_title/A = new typepath() - if(A.title_blurb) - message |= A.title_blurb + var/typepath = alt_titles[alt_title] + if(typepath) + var/datum/alt_title/A = new typepath() + if(A.title_blurb) + message |= A.title_blurb return message /datum/job/proc/get_job_icon() diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 43e59b8663..d276c96648 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -32,10 +32,6 @@ transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ about the importance of Suit Sensors." -// CMO Alt Titles -/datum/alt_title/cmo - title = "Chief Medical Officer" - ////////////////////////////////// // Medical Doctor ////////////////////////////////// @@ -56,16 +52,13 @@ job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ patients they encounter." - alt_titles = list("Medical Doctor" = /datum/alt_title/doctor, + alt_titles = list( "Surgeon" = /datum/alt_title/surgeon, "Emergency Physician" = /datum/alt_title/emergency_physician, "Nurse" = /datum/alt_title/nurse, "Virologist" = /datum/alt_title/virologist) //Medical Doctor Alt Titles -/datum/alt_title/doctor - title = "Medical Doctor" - /datum/alt_title/surgeon title = "Surgeon" title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ @@ -114,12 +107,9 @@ outfit_type = /decl/hierarchy/outfit/job/medical/chemist job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ They are likely to know the use and dangers of many lab-produced chemicals." - alt_titles = list("Chemist" = /datum/alt_title/chemist, "Pharmacist" = /datum/alt_title/pharmacist) + alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) // Chemist Alt Titles -/datum/alt_title/chemist - title = "Chemist" - /datum/alt_title/pharmacist title = "Pharmacist" title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." @@ -145,10 +135,6 @@ outfit_type = /decl/hierarchy/outfit/job/medical/geneticist job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ When required, geneticists have the skills to clone, and are the superior choice when available for doing so." - -// Geneticist Alt Titles -/datum/alt_title/geneticist - title = "Geneticist" */ ////////////////////////////////// @@ -170,12 +156,9 @@ outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." - alt_titles = list("Psychiatrist" = /datum/alt_title/psychiatrist, "Psychologist" = /datum/alt_title/psychologist) + alt_titles = list("Psychologist" = /datum/alt_title/psychologist) //Psychiatrist Alt Titles -/datum/alt_title/psychiatrist - title = "Psychiatrist" - /datum/alt_title/psychologist title = "Psychologist" title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ @@ -201,12 +184,9 @@ outfit_type = /decl/hierarchy/outfit/job/medical/paramedic job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ They may also be called upon to keep patients stable when Medical is busy or understaffed." - alt_titles = list("Paramedic" = /datum/alt_title/paramedic, "Emergency Medical Technician" = /datum/alt_title/emt) + alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) // Paramedic Alt Titles -/datum/alt_title/paramedic - title = "Paramedic" - /datum/alt_title/emt title = "Emergency Medical Technician" title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 4f90d1622b..cb6af5b849 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -33,12 +33,9 @@ at least with regards to anything occuring in the Research department, and to inform the crew of any disruptions that \ might originate from Research. The Research Director often has at least passing knowledge of most of the Research department, but \ are encouraged to allow their staff to perform their own duties." - alt_titles = list("Research Director" = /datum/alt_title/research_director, "Research Supervisor" = /datum/alt_title/research_supervisor) + alt_titles = list("Research Supervisor" = /datum/alt_title/research_supervisor) // Research Director Alt Titles -/datum/alt_title/research_director - title = "Research Director" - /datum/alt_title/research_supervisor title = "Research Supervisor" @@ -65,13 +62,10 @@ job_description = "A Scientist is a generalist working in the Research department, with general knowledge of the scientific process, as well as \ the principles and requirements of Research and Development. They may also formulate experiments of their own devising, if \ they find an appropriate topic." - alt_titles = list("Scientist" = /datum/alt_title/scientist, "Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ + alt_titles = list("Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ "Phoron Researcher" = /datum/alt_title/phoron_research) // Scientist Alt Titles -/datum/alt_title/scientist - title = "Scientist" - /datum/alt_title/xenoarch title = "Xenoarchaeologist" title_blurb = "A Xenoarchaeologist enters digsites in search of artifacts of alien origin. These digsites are frequently in vacuum or other inhospitable \ @@ -110,12 +104,9 @@ outfit_type = /decl/hierarchy/outfit/job/science/xenobiologist job_description = "A Xenobiologist studies esoteric lifeforms, usually in the relative safety of their lab. They attempt to find ways to benefit \ from the byproducts of these lifeforms, and their main subject at present is the Giant Slime." - alt_titles = list("Xenobiologist" = /datum/alt_title/xenobio, "Xenobotanist" = /datum/alt_title/xenobot) + alt_titles = list("Xenobotanist" = /datum/alt_title/xenobot) // Xenibiologist Alt Titles -/datum/alt_title/xenobio - title = "Xenobiologist" - /datum/alt_title/xenobot title = "Xenobotanist" title_blurb = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \ @@ -142,12 +133,9 @@ outfit_type = /decl/hierarchy/outfit/job/science/roboticist job_description = "A Roboticist maintains and repairs the station's synthetics, including crew with prosthetic limbs. \ They can also assist the station by producing simple robots and even pilotable exosuits." - alt_titles = list("Roboticist" = /datum/alt_title/roboticist, "Biomechanical Engineer" = /datum/alt_title/biomech, "Mechatronic Engineer" = /datum/alt_title/mech_tech) + alt_titles = list("Biomechanical Engineer" = /datum/alt_title/biomech, "Mechatronic Engineer" = /datum/alt_title/mech_tech) // Roboticist Alt Titles -/datum/alt_title/roboticist - title = "Roboticist" - /datum/alt_title/biomech title = "Biomechanical Engineer" title_blurb = "A Biomechanical Engineer primarily works on prosthetics, and the organic parts attached to them. They may have some \ diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 6a3954f9f0..684e9cc7c1 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -30,12 +30,9 @@ job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ perform the duties of absent Security roles, such as distributing gear from the Armory." - alt_titles = list("Head of Security" = /datum/alt_title/hos, "Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) + alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) // Head of Security Alt Titles -/datum/alt_title/hos - title = "Head of Security" - /datum/alt_title/sec_commander title = "Security Commander" @@ -67,10 +64,6 @@ Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ Security Department as a whole." -// Warden Alt Titles -/datum/alt_title/warden - title = "Warden" - ////////////////////////////////// // Detective ////////////////////////////////// @@ -92,12 +85,9 @@ outfit_type = /decl/hierarchy/outfit/job/security/detective job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." - alt_titles = list("Detective" = /datum/alt_title/detective, "Forensic Technician" = /datum/alt_title/forensic_tech) + alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) // Detective Alt Titles -/datum/alt_title/detective - title = "Detective" - /datum/alt_title/forensic_tech title = "Forensic Technician" title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." @@ -125,12 +115,9 @@ job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ No one is above the Law, not Security or Command." - alt_titles = list("Security Officer" = /datum/alt_title/sec_officer, "Junior Officer" = /datum/alt_title/junior_officer) + alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) // Security Officer Alt Titles -/datum/alt_title/sec_officer - title = "Security Officer" - /datum/alt_title/junior_officer title = "Junior Officer" title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 691ebe2ad9..6f89adcd07 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -23,10 +23,6 @@ The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ the AI's commands, and their own Laws." -//AI Alt Titles -/datum/alt_title/ai - title = "AI" - // AI procs /datum/job/ai/equip(var/mob/living/carbon/human/H) if(!H) return 0 @@ -61,12 +57,9 @@ outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ to follow its Laws." - alt_titles = list("Cyborg" = /datum/alt_title/cyborg, "Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) + alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) // Cyborg Alt Titles -/datum/alt_title/cyborg - title = "Cyborg" - /datum/alt_title/robot title = "Robot" title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index b34d1565a0..b6f6cb25ac 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -6,7 +6,7 @@ level = 1 // underfloor layer = UNDER_JUNK_LAYER anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 0 var/obj/item/device/radio/beacon/Beacon diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 8eae094aeb..5de1543426 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -5,7 +5,7 @@ icon_state = "table2-idle" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1 active_power_usage = 5 surgery_odds = 100 diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index f6750215bc..639c2fcd3b 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -7,7 +7,7 @@ anchored = 1 //About time someone fixed this. density = 0 dir = 8 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 interact_offline = 1 circuit = /obj/item/weapon/circuitboard/sleeper_console @@ -177,7 +177,7 @@ var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. @@ -386,7 +386,7 @@ M.client.perspective = EYE_PERSPECTIVE M.client.eye = src M.loc = src - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) occupant = M update_icon() @@ -406,7 +406,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() toggle_filter() toggle_pump() diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 5ab9828417..a8f9297d6f 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -9,7 +9,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/body_scanner - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 60 active_power_usage = 10000 //10 kW. It's a big all-body scanner. light_color = "#00FF00" diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 2e4ec6956d..aead0c79f1 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -3,7 +3,7 @@ icon = 'icons/obj/device.dmi' icon_state = "liquid_dispenser" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 var/uses = 20 var/disabled = 1 diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 5b061b4fb9..648c68cf95 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -40,7 +40,7 @@ plane = TURF_PLANE layer = ABOVE_TURF_LAYER anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 80 active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. power_channel = ENVIRON @@ -192,7 +192,7 @@ if(!regulating_temperature) //check for when we should start adjusting temperature if(!get_danger_level(target_temperature, TLV["temperature"]) && abs(environment.temperature - target_temperature) > 2.0) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) regulating_temperature = 1 audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ "You hear a click and a faint electronic hum.") @@ -200,7 +200,7 @@ else //check for when we should stop adjusting temperature if(get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5) - update_use_power(1) + update_use_power(USE_POWER_IDLE) regulating_temperature = 0 audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ "You hear a click as a faint electronic humming stops.") @@ -823,7 +823,7 @@ FIRE ALARM var/timing = 0.0 var/lockdownbyai = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON @@ -1048,7 +1048,7 @@ Just a object used in constructing fire alarms var/timing = 0.0 var/lockdownbyai = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index e462e0eed6..ef74905a50 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -18,7 +18,7 @@ pressure_resistance = 7 * ONE_ATMOSPHERE var/temperature_resistance = 1000 + T0C volume = 1000 - use_power = 0 + use_power = USE_POWER_OFF interact_offline = 1 // Allows this to be used when not in powered area. var/release_log = "" var/update_flag = 0 diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index a2281c7069..84b18c31ea 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -9,7 +9,7 @@ power_channel = ENVIRON var/frequency = 0 var/id - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 /obj/machinery/meter/Initialize() diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 085c070f7d..180dde355d 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,6 +1,6 @@ /obj/machinery/portable_atmospherics name = "atmoalter" - use_power = 0 + use_power = USE_POWER_OFF layer = OBJ_LAYER // These are mobile, best not be under everything. var/datum/gas_mixture/air_contents = new diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index de3ef0587e..3b6df894b5 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -151,7 +151,7 @@ volume = 50000 volume_rate = 5000 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 500 //internal circuitry, friction losses and stuff active_power_usage = 100000 //100 kW ~ 135 HP diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index ff26049fa6..7622ef1289 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -4,7 +4,7 @@ icon_state = "autolathe" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 clicksound = "keyboard" @@ -246,7 +246,7 @@ return busy = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) //Check if we still have the materials. var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient @@ -265,7 +265,7 @@ sleep(build_time) busy = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() // So lid opens //Sanity check. diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index 8c278c63fa..ea2ac41909 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/biogenerator - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 var/processing = 0 var/obj/item/weapon/reagent_containers/glass/beaker = null diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index 60143fadd3..f7e4749fe3 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -9,7 +9,7 @@ anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 active_power_usage = 300 @@ -161,7 +161,7 @@ container.reagents.remove_reagent("biomass", possible_list[choice][2]) - use_power = 2 + use_power = USE_POWER_ACTIVE printing = 1 update_icon() @@ -169,7 +169,7 @@ sleep(print_delay) - use_power = 1 + use_power = USE_POWER_IDLE printing = 0 update_icon() diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index b072a7e488..2415673f80 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -6,7 +6,7 @@ var/id = null var/active = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 80bb492707..9fc72b0a17 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -3,7 +3,7 @@ desc = "It's used to monitor rooms." icon = 'icons/obj/monitors.dmi' icon_state = "camera" - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 5 active_power_usage = 10 plane = MOB_PLANE diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 0ceae52046..636bcca6c0 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/power.dmi' icon_state = "ccharger0" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 60000 //60 kW. (this the power drawn when charging) var/efficiency = 60000 //will provide the modified power rate when upgraded @@ -118,16 +118,16 @@ /obj/machinery/cell_charger/process() //to_world("ccpt [charging] [stat]") if((stat & (BROKEN|NOPOWER)) || !anchored) - update_use_power(0) + update_use_power(USE_POWER_OFF) return if(charging && !charging.fully_charged()) charging.give(efficiency*CELLRATE) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) update_icon() else - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/cell_charger/RefreshParts() var/E = 0 diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 5af8e2320c..874055afc2 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -110,7 +110,7 @@ src.occupant.adjustBruteLoss(-1) src.occupant.updatehealth() if (src.occupant.health >= 0 && src.occupant.stat == DEAD) - src.occupant.stat = CONSCIOUS + src.occupant.set_stat(CONSCIOUS) src.occupant.lying = 0 dead_mob_list -= src.occupant living_mob_list += src.occupant diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 2968ae8aaf..c50aa8da24 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -170,7 +170,7 @@ src.current_camera = C if(current_camera) current_camera.camera_computers_using_this.Add(src) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) var/mob/living/L = current_camera.loc if(istype(L)) L.tracking_initiated() @@ -182,7 +182,7 @@ if(istype(L)) L.tracking_cancelled() current_camera = null - use_power = 1 + use_power = USE_POWER_IDLE //Camera control: mouse. /atom/DblClick() diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 9da5397712..90c09acb44 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -310,9 +310,6 @@ if (subject.suiciding) scantemp = "Error: Subject's brain is not responding to scanning stimuli." return - if ((!subject.ckey) || (!subject.client)) - scantemp = "Error: Mental interface failure." - return if (NOCLONE in subject.mutations) scantemp = "Error: Mental interface failure." return @@ -323,6 +320,14 @@ if(istype(modifier_type, /datum/modifier/no_clone)) scantemp = "Error: Mental interface failure." return + if ((!subject.ckey) || (!subject.client)) + scantemp = "Error: Mental interface failure." + if(subject.stat == DEAD && subject.mind && subject.mind.key) // If they're dead and not in their body, tell them to get in it. + for(var/mob/observer/dead/ghost in player_list) + if(ghost.ckey == ckey(subject.mind.key)) + ghost.notify_revive("Someone is trying to scan your body in the cloner. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') + break + return if (!isnull(find_record(subject.ckey))) scantemp = "Subject already in database." return diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 309a066404..cf35736c74 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -4,7 +4,7 @@ icon_state = "computer" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 active_power_usage = 300 var/processing = 0 diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 0c6c33928a..9342e0b5e9 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -4,7 +4,7 @@ icon_keyboard = "med_key" icon_screen = "crew" light_color = "#315ab4" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/crew diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm index 9197f37952..e1dddbbeda 100644 --- a/code/game/machinery/computer/supply.dm +++ b/code/game/machinery/computer/supply.dm @@ -56,7 +56,7 @@ var/orders[0] var/receipts[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if(shuttle) if(shuttle.has_arrive_time()) shuttle_status["location"] = "In transit" @@ -66,8 +66,8 @@ else shuttle_status["time"] = 0 if(shuttle.at_station()) - if(shuttle.docking_controller) - switch(shuttle.docking_controller.get_docking_status()) + if(shuttle.shuttle_docking_controller) + switch(shuttle.shuttle_docking_controller.get_docking_status()) if("docked") shuttle_status["location"] = "Docked" shuttle_status["mode"] = SUP_SHUTTLE_DOCKED @@ -192,7 +192,7 @@ if(!supply_controller) to_world_log("## ERROR: The supply_controller datum is missing.") return - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if (!shuttle) to_world_log("## ERROR: The supply shuttle datum is missing.") return diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index e74ffb7cf9..aa9c230e22 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -7,7 +7,7 @@ icon_state = "box_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/obj/item/weapon/circuitboard/circuit = null var/list/components = null var/list/req_components = null diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 9f10ff4c90..946470ca95 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -11,7 +11,7 @@ interact_offline = 1 var/on = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 200 buckle_lying = FALSE @@ -232,7 +232,7 @@ return occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - occupant.stat = UNCONSCIOUS + occupant.set_stat(UNCONSCIOUS) occupant.dir = SOUTH if(occupant.bodytemperature < T0C) occupant.sleeping = max(5, (1/occupant.bodytemperature)*2000) @@ -291,7 +291,7 @@ unbuckle_mob(occupant, force = TRUE) occupant = null current_heat_capacity = initial(current_heat_capacity) - update_use_power(1) + update_use_power(USE_POWER_IDLE) return /obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) if(stat & (NOPOWER|BROKEN)) @@ -322,7 +322,7 @@ vis_contents |= occupant occupant.pixel_y += 19 current_heat_capacity = HEAT_CAPACITY_HUMAN - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) // M.metabslow = 1 add_fingerprint(usr) update_icon() diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 86b0bfa622..b01f4cfb64 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -14,7 +14,7 @@ */ anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index cc3cb4f09e..172f51e908 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -164,6 +164,14 @@ icon = 'icons/obj/doors/Doorext.dmi' assembly_type = /obj/structure/door_assembly/door_assembly_ext +/obj/machinery/door/airlock/external/glass/bolted + icon_state = "door_locked" // So it looks visibly bolted in map editor + locked = 1 + +// For convenience in making docking ports: one that is pre-bolted with frequency set! +/obj/machinery/door/airlock/external/glass/bolted/cycling + frequency = 1379 + /obj/machinery/door/airlock/glass_external name = "External Airlock" icon = 'icons/obj/doors/Doorextglass.dmi' diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 70949341ba..40cf9febd0 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -42,7 +42,7 @@ if(isanimal(user)) var/mob/living/simple_mob/S = user if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("\The [user] smashes into the [src]!") + visible_message("\The [user] smashes into [src]!") playsound(src, S.attack_sound, 75, 1) take_damage(damage) else diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index d5043af6ef..4ad97c9066 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -38,7 +38,7 @@ var/hatch_open = 0 power_channel = ENVIRON - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 var/list/tile_info[4] diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index a1e05d5e1e..1396b5f554 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -9,7 +9,7 @@ maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file health = 150 visible = 0.0 - use_power = 0 + use_power = USE_POWER_OFF flags = ON_BORDER opacity = 0 var/obj/item/weapon/airlock_electronics/electronics = null diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index 0f8b797289..b15d5afd97 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -2,6 +2,7 @@ /obj/machinery/embedded_controller/radio/airlock // Setup parameters only radio_filter = RADIO_AIRLOCK + program = /datum/computer/file/embedded_program/airlock var/tag_exterior_door var/tag_interior_door var/tag_airpump @@ -11,11 +12,22 @@ var/tag_airlock_mech_sensor var/tag_shuttle_mech_sensor var/tag_secure = 0 + var/list/dummy_terminals = list() var/cycle_to_external_air = 0 -/obj/machinery/embedded_controller/radio/airlock/Initialize() - . = ..() - program = new/datum/computer/file/embedded_program/airlock(src) +/obj/machinery/embedded_controller/radio/airlock/Destroy() + // TODO - Leshana - Implement dummy terminals + //for(var/thing in dummy_terminals) + // var/obj/machinery/dummy_airlock_controller/dummy = thing + // dummy.master_controller = null + //dummy_terminals.Cut() + return ..() + +/obj/machinery/embedded_controller/radio/airlock/CanUseTopic(var/mob/user) + if(!allowed(user)) + return min(STATUS_UPDATE, ..()) + else + return ..() //Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller @@ -37,43 +49,20 @@ if (!ui) ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks - if("cycle_ext") - clean = 1 - if("cycle_int") - clean = 1 - if("force_ext") - clean = 1 - if("force_int") - clean = 1 - if("abort") - clean = 1 - if("purge") - clean = 1 - if("secure") - clean = 1 - - if(clean) - program.receive_user_command(href_list["command"]) + if("cycle_ext", "cycle_int", "force_ext", "force_int", "abort", "purge", "secure") + program.receive_user_command(href_list["command"]) return 1 - //Airlock controller for airlock control - most airlocks on the station use this /obj/machinery/embedded_controller/radio/airlock/airlock_controller name = "Airlock Controller" @@ -90,23 +79,16 @@ ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/airlock_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") @@ -125,7 +107,6 @@ return 1 - //Access controller for door control - used in virology and the like /obj/machinery/embedded_controller/radio/airlock/access_controller icon = 'icons/obj/airlock_machines.dmi' @@ -154,23 +135,16 @@ ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/access_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext_door") diff --git a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm new file mode 100644 index 0000000000..d2f9a410a2 --- /dev/null +++ b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm @@ -0,0 +1,51 @@ +// Provides remote access to a controller (since they must be unique). +/obj/machinery/dummy_airlock_controller + name = "airlock control terminal" + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + layer = ABOVE_OBJ_LAYER + var/id_tag + + var/datum/topic_state/remote/remote_state + var/obj/machinery/embedded_controller/radio/airlock/master_controller + +/obj/machinery/dummy_airlock_controller/process() + if(master_controller) + appearance = master_controller + . = ..() + +/obj/machinery/dummy_airlock_controller/Initialize() + . = ..() + if(id_tag) + for(var/obj/machinery/embedded_controller/radio/airlock/_master in SSmachines.machinery) + if(_master.id_tag == id_tag) + master_controller = _master + master_controller.dummy_terminals += src + break + if(!master_controller) + qdel(src) + else + remote_state = new /datum/topic_state/remote(src, master_controller) + +/obj/machinery/dummy_airlock_controller/Destroy() + if(master_controller) + master_controller.dummy_terminals -= src + if(remote_state) + qdel(remote_state) + remote_state = null + return ..() + +/obj/machinery/dummy_airlock_controller/interface_interact(var/mob/user) + open_remote_ui(user) + return TRUE + +/obj/machinery/dummy_airlock_controller/proc/open_remote_ui(var/mob/user) + if(master_controller) + appearance = master_controller + return master_controller.ui_interact(user, state = remote_state) + +/obj/machinery/dummy_airlock_controller/powered(var/chan = -1, var/area/check_area = null) + if(master_controller) + var/area/A = get_area(master_controller) + return master_controller.powered(chan, A) + return ..() diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller.dm b/code/game/machinery/embedded_controller/airlock_docking_controller.dm index fa8398c3d7..4d5048ad6d 100644 --- a/code/game/machinery/embedded_controller/airlock_docking_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm @@ -1,8 +1,17 @@ +/* + * NOTE - This file defines both these datums: Yes, you read that right. Its confusing. Lets try and break it down. + * /datum/computer/file/embedded_program/docking/airlock + * - A docking controller for an airlock based docking port + * /datum/computer/file/embedded_program/airlock/docking + * - An extension to the normal airlock program allows disabling of the regular airlock functions when docking +*/ + //a docking port based on an airlock /obj/machinery/embedded_controller/radio/airlock/docking_port name = "docking port controller" var/datum/computer/file/embedded_program/airlock/docking/airlock_program var/datum/computer/file/embedded_program/docking/airlock/docking_program + var/display_name // For mappers to override docking_program.display_name (how would it show up on docking monitoring program) tag_secure = 1 /obj/machinery/embedded_controller/radio/airlock/docking_port/Initialize() @@ -10,9 +19,25 @@ airlock_program = new/datum/computer/file/embedded_program/airlock/docking(src) docking_program = new/datum/computer/file/embedded_program/docking/airlock(src, airlock_program) program = docking_program + if(display_name) + docking_program.display_name = display_name + +/obj/machinery/embedded_controller/radio/airlock/docking_port/attackby(obj/item/W, mob/user) + if(istype(W,/obj/item/device/multitool)) //give them part of code, would take few tries to get full + var/datum/computer/file/embedded_program/docking/airlock/docking_program = program + var/code = docking_program.docking_codes + if(!code) + code = "N/A" + else + code = stars(code) + to_chat(user, "[W]'s screen displays '[code]'") + else + ..() /obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/airlock/docking_program = program + var/datum/computer/file/embedded_program/airlock/docking/airlock_program = docking_program.airlock_program data = list( "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), @@ -22,6 +47,8 @@ "docking_status" = docking_program.get_docking_status(), "airlock_disabled" = !(docking_program.undocked() || docking_program.override_enabled), "override_enabled" = docking_program.override_enabled, + "docking_codes" = docking_program.docking_codes, + "name" = docking_program.get_name() ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -33,12 +60,9 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/docking_port/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") @@ -60,11 +84,13 @@ return 1 - +/////////////////////////////////////////////////////////////////////////////// //A docking controller for an airlock based docking port +// /datum/computer/file/embedded_program/docking/airlock var/datum/computer/file/embedded_program/airlock/docking/airlock_program + /datum/computer/file/embedded_program/docking/airlock/New(var/obj/machinery/embedded_controller/M, var/datum/computer/file/embedded_program/airlock/docking/A) ..(M) airlock_program = A @@ -76,10 +102,10 @@ disable_override() else enable_override() - return + return TRUE - ..(command) - airlock_program.receive_user_command(command) //pass along to subprograms + . = ..(command) + . = airlock_program.receive_user_command(command) || . //pass along to subprograms; bypass shortcircuit /datum/computer/file/embedded_program/docking/airlock/process() airlock_program.process() @@ -91,7 +117,7 @@ //tell the docking port to start getting ready for docking - e.g. pressurize /datum/computer/file/embedded_program/docking/airlock/prepare_for_docking() - airlock_program.begin_cycle_in() + airlock_program.begin_dock_cycle() //are we ready for docking? /datum/computer/file/embedded_program/docking/airlock/ready_for_docking() @@ -99,14 +125,14 @@ //we are docked, open the doors or whatever. /datum/computer/file/embedded_program/docking/airlock/finish_docking() - airlock_program.enable_mech_regulators() + airlock_program.enable_mech_regulation() airlock_program.open_doors() //tell the docking port to start getting ready for undocking - e.g. close those doors. /datum/computer/file/embedded_program/docking/airlock/prepare_for_undocking() airlock_program.stop_cycling() airlock_program.close_doors() - airlock_program.disable_mech_regulators() + airlock_program.disable_mech_regulation() //are we ready for undocking? /datum/computer/file/embedded_program/docking/airlock/ready_for_undocking() @@ -114,20 +140,22 @@ var/int_closed = airlock_program.check_interior_door_secured() return (ext_closed || int_closed) +/////////////////////////////////////////////////////////////////////////////// //An airlock controller to be used by the airlock-based docking port controller. //Same as a regular airlock controller but allows disabling of the regular airlock functions when docking +// /datum/computer/file/embedded_program/airlock/docking var/datum/computer/file/embedded_program/docking/airlock/master_prog +/datum/computer/file/embedded_program/airlock/docking/Destroy() + if(master_prog) + master_prog.airlock_program = null + master_prog = null + return ..() + /datum/computer/file/embedded_program/airlock/docking/receive_user_command(command) if (master_prog.undocked() || master_prog.override_enabled) //only allow the port to be used as an airlock if nothing is docked here or the override is enabled - ..(command) - -/datum/computer/file/embedded_program/airlock/docking/proc/enable_mech_regulators() - enable_mech_regulation() - -/datum/computer/file/embedded_program/airlock/docking/proc/disable_mech_regulators() - disable_mech_regulation() + return ..(command) /datum/computer/file/embedded_program/airlock/docking/proc/open_doors() toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open") diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm index 4b6917ff71..55182aaaa6 100644 --- a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm +++ b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm @@ -2,21 +2,15 @@ //this is the master controller, that things will try to dock with. /obj/machinery/embedded_controller/radio/docking_port_multi name = "docking port controller" - + program = /datum/computer/file/embedded_program/docking/multi var/child_tags_txt var/child_names_txt var/list/child_names = list() - var/datum/computer/file/embedded_program/docking/multi/docking_program - /obj/machinery/embedded_controller/radio/docking_port_multi/Initialize() . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/multi(src) - program = docking_program - var/list/names = splittext(child_names_txt, ";") var/list/tags = splittext(child_tags_txt, ";") - if (names.len == tags.len) for (var/i = 1; i <= tags.len; i++) child_names[tags[i]] = names[i] @@ -24,6 +18,7 @@ /obj/machinery/embedded_controller/radio/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/multi/docking_program = program // Cast to proper type var/list/airlocks[child_names.len] var/i = 1 @@ -44,24 +39,21 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/docking_port_multi/Topic(href, href_list) - return + return 1 // Apparently we swallow all input (this is corrected legacy code) //a docking port based on an airlock +// This is the actual controller that will be commanded by the master defined above /obj/machinery/embedded_controller/radio/airlock/docking_port_multi name = "docking port controller" + program = /datum/computer/file/embedded_program/airlock/multi_docking var/master_tag //for mapping - var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program tag_secure = 1 -/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Initialize() - . = ..() - airlock_program = new/datum/computer/file/embedded_program/airlock/multi_docking(src) - program = airlock_program - /obj/machinery/embedded_controller/radio/airlock/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program = program // Cast to proper type data = list( "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), @@ -82,12 +74,9 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 5ebbac93ae..dc02084f98 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -52,8 +52,8 @@ tag_interior_door = controller.tag_interior_door? controller.tag_interior_door : "[id_tag]_inner" tag_airpump = controller.tag_airpump? controller.tag_airpump : "[id_tag]_pump" tag_chamber_sensor = controller.tag_chamber_sensor? controller.tag_chamber_sensor : "[id_tag]_sensor" - tag_exterior_sensor = controller.tag_exterior_sensor - tag_interior_sensor = controller.tag_interior_sensor + tag_exterior_sensor = controller.tag_exterior_sensor || "[id_tag]_exterior_sensor" + tag_interior_sensor = controller.tag_interior_sensor || "[id_tag]_interior_sensor" tag_airlock_mech_sensor = controller.tag_airlock_mech_sensor? controller.tag_airlock_mech_sensor : "[id_tag]_airlock_mech" tag_shuttle_mech_sensor = controller.tag_shuttle_mech_sensor? controller.tag_shuttle_mech_sensor : "[id_tag]_shuttle_mech" memory["secure"] = controller.tag_secure @@ -117,6 +117,7 @@ /datum/computer/file/embedded_program/airlock/receive_user_command(command) var/shutdown_pump = 0 + . = TRUE switch(command) if("cycle_ext") //If airlock is already cycled in this direction, just toggle the doors. @@ -163,6 +164,8 @@ else signalDoor(tag_interior_door, "unlock") signalDoor(tag_exterior_door, "unlock") + else + . = FALSE if(shutdown_pump) signalPump(tag_airpump, 0) //send a signal to stop pressurizing @@ -273,6 +276,9 @@ target_state = TARGET_INOPEN memory["purge"] = cycle_to_external_air +/datum/computer/file/embedded_program/airlock/proc/begin_dock_cycle() + state = STATE_IDLE + target_state = TARGET_INOPEN /datum/computer/file/embedded_program/airlock/proc/begin_cycle_out() state = STATE_IDLE target_state = TARGET_OUTOPEN diff --git a/code/game/machinery/embedded_controller/docking_program.dm b/code/game/machinery/embedded_controller/docking_program.dm index 2665fc231c..44a34fddec 100644 --- a/code/game/machinery/embedded_controller/docking_program.dm +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -12,51 +12,51 @@ /* *** STATE TABLE *** - + MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. MODE_CLIENT|STATE_DOCKED idle - docked as client. MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. - + MODE_SERVER|STATE_UNDOCKED should never happen. MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. MODE_SERVER|STATE_DOCKED idle - docked as server MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. - + MODE_NONE|STATE_UNDOCKED idle - not docked. MODE_NONE|anything else should never happen. - + *** Docking Signals *** - + Docking Client sends request_dock Server sends confirm_dock to say that yes, we will serve your request When client is ready, sends confirm_dock Server sends confirm_dock back to indicate that docking is complete - + Undocking Client sends request_undock When client is ready, sends confirm_undock Server sends confirm_undock back to indicate that docking is complete - + Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. - The client first sends a confirm message to indicate it is ready, and then finally the server will send it's + The client first sends a confirm message to indicate it is ready, and then finally the server will send it's confirm message to indicate that the operation is complete. - + Note also that when docking, the server sends an additional confirm message. This is because before docking, the server and client do not have a defined relationship. Before undocking, the server and client are already related to each other, thus the extra confirm message is not needed. - + *** Override, what is it? *** - + The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. - + The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced - launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock + launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock (which usually just means closing and locking the doors). - + In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. */ @@ -67,27 +67,31 @@ var/control_mode = MODE_NONE var/response_sent = 0 //so we don't spam confirmation messages var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed - + var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client + var/docking_codes //would only allow docking when receiving signal with these, if set + var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset /datum/computer/file/embedded_program/docking/New() ..() - var/datum/existing = locate(id_tag) //in case a datum already exists with our tag - if(existing) - existing.tag = null //take it from them - - tag = id_tag //Greatly simplifies shuttle initialization + if(id_tag) + if(SSshuttles.docking_registry[id_tag]) + crash_with("Docking controller tag [id_tag] had multiple associated programs.") + SSshuttles.docking_registry[id_tag] = src +/datum/computer/file/embedded_program/docking/Destroy() + SSshuttles.docking_registry -= id_tag + return ..() /datum/computer/file/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id var/command = signal.data["command"] var/recipient = signal.data["recipient"] //the intended recipient of the docking signal - + if (recipient != id_tag) return //this signal is not for us - + switch (command) if ("confirm_dock") if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) @@ -95,7 +99,7 @@ broadcast_docking_status() if (!override_enabled) prepare_for_docking() - + else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) dock_state = STATE_DOCKED broadcast_docking_status() @@ -104,19 +108,27 @@ response_sent = 0 else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process received_confirm = 1 - + if ("request_dock") if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) + + tag_target = receive_tag + + if(docking_codes) + var/code = signal.data["code"] + if(code != docking_codes) + testing("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") + return + control_mode = MODE_SERVER - dock_state = STATE_DOCKING broadcast_docking_status() - - tag_target = receive_tag + + if (!override_enabled) prepare_for_docking() send_docking_command(tag_target, "confirm_dock") //acknowledge the request - + if ("confirm_undock") if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) if (!override_enabled) @@ -129,7 +141,7 @@ if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) dock_state = STATE_UNDOCKING broadcast_docking_status() - + if (!override_enabled) prepare_for_undocking() @@ -145,38 +157,38 @@ if (!response_sent) send_docking_command(tag_target, "confirm_dock") //tell the server we're ready response_sent = 1 - + else if (control_mode == MODE_SERVER && received_confirm) send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. - + dock_state = STATE_DOCKED broadcast_docking_status() - + if (!override_enabled) finish_docking() //server done docking! response_sent = 0 received_confirm = 0 - + if (STATE_UNDOCKING) if (ready_for_undocking()) if (control_mode == MODE_CLIENT) if (!response_sent) send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. response_sent = 1 - + else if (control_mode == MODE_SERVER && received_confirm) send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. if (!override_enabled) finish_undocking() reset() //server is done undocking! - + if (response_sent || resend_counter > 0) resend_counter++ - + if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) response_sent = 0 resend_counter = 0 - + //handle invalid states if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) if (tag_target) @@ -189,22 +201,22 @@ /datum/computer/file/embedded_program/docking/proc/initiate_docking(var/target) if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake return - + tag_target = target control_mode = MODE_CLIENT - + send_docking_command(tag_target, "request_dock") /datum/computer/file/embedded_program/docking/proc/initiate_undocking() if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking return - + dock_state = STATE_UNDOCKING broadcast_docking_status() - + if (!override_enabled) prepare_for_undocking() - + send_docking_command(tag_target, "request_undock") //tell the docking port to start getting ready for docking - e.g. pressurize @@ -240,7 +252,7 @@ /datum/computer/file/embedded_program/docking/proc/reset() dock_state = STATE_UNDOCKED broadcast_docking_status() - + control_mode = MODE_NONE tag_target = null response_sent = 0 @@ -267,6 +279,7 @@ signal.data["tag"] = id_tag signal.data["command"] = command signal.data["recipient"] = recipient + signal.data["code"] = docking_codes post_signal(signal) /datum/computer/file/embedded_program/docking/proc/broadcast_docking_status() @@ -283,6 +296,8 @@ if (STATE_UNDOCKING) return "undocking" if (STATE_DOCKED) return "docked" +/datum/computer/file/embedded_program/docking/proc/get_name() + return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" #undef STATE_UNDOCKED #undef STATE_DOCKING diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 17bf13181a..714d27d560 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,18 +1,20 @@ /obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program //the currently executing program - name = "Embedded Controller" anchored = 1 - - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 - + var/datum/computer/file/embedded_program/program //the currently executing program var/on = 1 -obj/machinery/embedded_controller/radio/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - ..() +/obj/machinery/embedded_controller/Initialize() + if(ispath(program)) + program = new program(src) + return ..() + +/obj/machinery/embedded_controller/Destroy() + if(istype(program)) + qdel(program) // the program will clear the ref in its Destroy + return ..() /obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) return 0 @@ -24,6 +26,17 @@ obj/machinery/embedded_controller/radio/Destroy() program.receive_signal(signal, receive_method, receive_param) //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 +/obj/machinery/embedded_controller/Topic(href, href_list) + if((. = ..())) + return + if(usr) + usr.set_machine(src) + src.add_fingerprint(usr) + // We would now pass it to the program, except that some of our embedded controller types want to block certain commands. + // Until/unless that is refactored differently, we rely on subtypes to pass it on. + //if(program) + // return program.receive_user_command(href_list["command"]) + /obj/machinery/embedded_controller/process() if(program) program.process() @@ -40,14 +53,16 @@ obj/machinery/embedded_controller/radio/Destroy() src.ui_interact(user) -/obj/machinery/embedded_controller/ui_interact() - return +// +// Embedded controller with a radio! (Most things (All things?) use this) +// /obj/machinery/embedded_controller/radio icon = 'icons/obj/airlock_machines.dmi' icon_state = "airlock_control_standby" power_channel = ENVIRON density = 0 + unacidable = 1 var/id_tag //var/radio_power_use = 50 //power used to xmit signals @@ -55,11 +70,15 @@ obj/machinery/embedded_controller/radio/Destroy() var/frequency = 1379 var/radio_filter = null var/datum/radio_frequency/radio_connection - unacidable = 1 /obj/machinery/embedded_controller/radio/Initialize() + set_frequency(frequency) // Set it before parent instantiates program . = ..() - set_frequency(frequency) + +/obj/machinery/embedded_controller/radio/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + ..() /obj/machinery/embedded_controller/radio/update_icon() if(on && program) diff --git a/code/game/machinery/embedded_controller/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm index 0cc711c7a4..48340b0c8b 100644 --- a/code/game/machinery/embedded_controller/embedded_program_base.dm +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -11,8 +11,15 @@ var/obj/machinery/embedded_controller/radio/R = M id_tag = R.id_tag +/datum/computer/file/embedded_program/Destroy() + if(master) + master.program = null + master = null + return ..() + +// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) /datum/computer/file/embedded_program/proc/receive_user_command(command) - return + return FALSE /datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) return diff --git a/code/game/machinery/embedded_controller/simple_docking_controller.dm b/code/game/machinery/embedded_controller/simple_docking_controller.dm index 14b27b2512..d2e04a3330 100644 --- a/code/game/machinery/embedded_controller/simple_docking_controller.dm +++ b/code/game/machinery/embedded_controller/simple_docking_controller.dm @@ -1,16 +1,12 @@ //a docking port that uses a single door /obj/machinery/embedded_controller/radio/simple_docking_controller name = "docking hatch controller" + program = /datum/computer/file/embedded_program/docking/simple var/tag_door - var/datum/computer/file/embedded_program/docking/simple/docking_program - -/obj/machinery/embedded_controller/radio/simple_docking_controller/Initialize() - . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/simple(src) - program = docking_program /obj/machinery/embedded_controller/radio/simple_docking_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type data = list( "docking_status" = docking_program.get_docking_status(), @@ -28,11 +24,8 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/simple_docking_controller/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) + if((. = ..())) + return var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks @@ -44,8 +37,7 @@ if(clean) program.receive_user_command(href_list["command"]) - return 0 - + return //A docking controller program for a simple door based docking port /datum/computer/file/embedded_program/docking/simple @@ -76,6 +68,7 @@ ..(signal, receive_method, receive_param) /datum/computer/file/embedded_program/docking/simple/receive_user_command(command) + . = TRUE switch(command) if("force_door") if (override_enabled) @@ -88,7 +81,8 @@ disable_override() else enable_override() - + else + . = FALSE /datum/computer/file/embedded_program/docking/simple/proc/signal_door(var/command) var/datum/signal/signal = new diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 169bc8c6d7..572977ceb4 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -11,7 +11,7 @@ var/strength = 10 //How weakened targets are when flashed. var/base_state = "mflash" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 flags = PROXMOVE diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index f02d3c2855..1af8382f98 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -7,7 +7,7 @@ var/list/floor_light_cache = list() desc = "A backlit floor panel." layer = TURF_LAYER+0.001 anchored = 0 - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 2 active_power_usage = 20 power_channel = LIGHT @@ -72,7 +72,7 @@ var/list/floor_light_cache = list() return on = !on - if(on) use_power = 2 + if(on) update_use_power(USE_POWER_ACTIVE) visible_message("\The [user] turns \the [src] [on ? "on" : "off"].") update_brightness() return @@ -81,21 +81,21 @@ var/list/floor_light_cache = list() ..() var/need_update if((!anchored || broken()) && on) - use_power = 0 + update_use_power(USE_POWER_OFF) on = 0 need_update = 1 else if(use_power && !on) - use_power = 0 + update_use_power(USE_POWER_OFF) need_update = 1 if(need_update) update_brightness() /obj/machinery/floor_light/proc/update_brightness() - if(on && use_power == 2) + if(on && use_power == USE_POWER_ACTIVE) if(light_range != default_light_range || light_power != default_light_power || light_color != default_light_colour) set_light(default_light_range, default_light_power, default_light_colour) else - use_power = 0 + update_use_power(USE_POWER_OFF) if(light_range || light_power) set_light(0) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index e17ed67bc2..efdf18695c 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -39,7 +39,7 @@ var/const/HOLOPAD_MODE = RANGE_BASED layer = ABOVE_TURF_LAYER var/power_per_hologram = 500 //per usage per hologram idle_power_usage = 5 - use_power = 1 + use_power = USE_POWER_IDLE var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad var/last_request = 0 //to prevent request spam. ~Carn var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. @@ -183,7 +183,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/hologram anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 9a656ac3be..7a3cd8c861 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/holosign.dmi' icon_state = "sign_off" plane = MOB_PLANE - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 anchored = 1 @@ -19,7 +19,7 @@ if(stat & (BROKEN|NOPOWER)) return lit = !lit - use_power = lit ? 2 : 1 + update_use_power(lit ? USE_POWER_ACTIVE : USE_POWER_IDLE) update_icon() /obj/machinery/holosign/update_icon() @@ -34,7 +34,7 @@ ..() if(stat & NOPOWER) lit = 0 - use_power = 0 + update_use_power(USE_POWER_OFF) update_icon() diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 5cb521d423..ee887db2f5 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -6,7 +6,7 @@ var/id = null var/on = 1.0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 @@ -53,7 +53,7 @@ var/last_spark = 0 var/base_state = "migniter" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index f2808dbc18..3a8554dae2 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -15,7 +15,7 @@ datum/track/New(var/title_name, var/audio) anchored = 1 density = 1 power_channel = EQUIP - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 circuit = /obj/item/weapon/circuitboard/jukebox @@ -249,7 +249,7 @@ datum/track/New(var/title_name, var/audio) main_area.forced_ambience = null playing = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() @@ -271,7 +271,7 @@ datum/track/New(var/title_name, var/audio) main_area.play_ambience(M) playing = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) update_icon() // Advance to the next track - Don't start playing it unless we were already playing diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index f4b3d6f8f0..24edece9af 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -7,7 +7,7 @@ icon = 'icons/obj/power.dmi' icon_state = "light1" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 power_channel = LIGHT var/on = 1 diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 58d62b4cf9..c9a4b6ea05 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -101,7 +101,7 @@ Class Procs: var/stat = 0 var/emagged = 0 - var/use_power = 1 + var/use_power = USE_POWER_IDLE //0 = dont run the auto //1 = run auto, use idle //2 = run auto, use active @@ -199,9 +199,9 @@ Class Procs: /obj/machinery/proc/auto_use_power() if(!powered(power_channel)) return 0 - if(use_power == 1) + if(use_power == USE_POWER_IDLE) use_power(idle_power_usage, power_channel, 1) - else if(use_power >= 2) + else if(use_power >= USE_POWER_ACTIVE) use_power(active_power_usage, power_channel, 1) return 1 diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 9f97ab0360..9d36b08202 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -11,7 +11,7 @@ desc = "A device that uses station power to create points of magnetic energy." plane = PLATING_PLANE anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 var/freq = 1449 // radio frequency @@ -142,10 +142,10 @@ // Update power usage: if(on) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) active_power_usage = electricity_level*15 else - use_power = 0 + update_use_power(USE_POWER_OFF) // Overload conditions: /* // Eeeehhh kinda stupid @@ -190,7 +190,7 @@ icon_state = "airlock_control_standby" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 45 var/frequency = 1449 var/code = 0 diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 1d13a36c3a..7c184c27de 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -6,7 +6,7 @@ icon = 'icons/obj/stationobjs.dmi' icon_state = "mass_driver" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 50 circuit = /obj/item/weapon/circuitboard/mass_driver diff --git a/code/game/machinery/neonsign.dm b/code/game/machinery/neonsign.dm index fe5d5370a8..bdaa3b62fd 100644 --- a/code/game/machinery/neonsign.dm +++ b/code/game/machinery/neonsign.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/neonsigns.dmi' icon_state = "sign_off" plane = MOB_PLANE - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 anchored = 1 @@ -19,7 +19,7 @@ if(stat & (BROKEN|NOPOWER)) return lit = !lit - use_power = lit ? 2 : 1 + update_use_power(lit ? USE_POWER_ACTIVE : USE_POWER_IDLE) update_icon() /obj/machinery/neonsign/update_icon() @@ -34,7 +34,7 @@ ..() if(stat & NOPOWER) lit = 0 - use_power = 0 + update_use_power(USE_POWER_OFF) update_icon() diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index f32e57b9b1..8d6a55b3fd 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -23,7 +23,7 @@ var/bomb_set var/timing_wire var/removal_stage = 0 // 0 is no removal, 1 is covers removed, 2 is covers open, // 3 is sealant open, 4 is unwrenched, 5 is removed from bolts. - use_power = 0 + use_power = USE_POWER_OFF /obj/machinery/nuclearbomb/New() ..() diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 7f2b310ed6..10c96a6b6a 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -73,7 +73,7 @@ if(breather.internals) breather.internals.icon_state = "internal0" breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/oxygen_pump/attack_ai(mob/user as mob) ui_interact(user) @@ -90,7 +90,7 @@ breather.internal = tank if(breather.internals) breather.internals.icon_state = "internal1" - use_power = 2 + update_use_power(USE_POWER_ACTIVE) /obj/machinery/oxygen_pump/proc/can_apply_to_target(var/mob/living/carbon/human/target, mob/user as mob) if(!user) @@ -162,7 +162,7 @@ contained.forceMove(src) src.visible_message("\The [contained] rapidly retracts back into \the [src]!") breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) else if(!breather.internal && tank) breather.internal = tank if(breather.internals) @@ -287,7 +287,7 @@ contained.forceMove(src) src.visible_message("\The [contained] rapidly retracts back into \the [src]!") breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) else if(!breather.internal && tank) breather.internal = tank if(breather.internals) diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index 3388de0c31..aab6a31ded 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/telecomms/pda_multicaster - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 750 var/on = 1 // If we're currently active, var/toggle = 1 // If we /should/ be active or not, diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 92c28366fa..5349fc44b0 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/stationobjs.dmi' icon_state = "recharger0" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 4 active_power_usage = 40000 //40 kW var/efficiency = 40000 //will provide the modified power rate when upgraded @@ -117,12 +117,12 @@ /obj/machinery/recharger/process() if(stat & (NOPOWER|BROKEN) || !anchored) - update_use_power(0) + update_use_power(USE_POWER_OFF) icon_state = icon_state_idle return if(!charging) - update_use_power(1) + update_use_power(USE_POWER_IDLE) icon_state = icon_state_idle else var/obj/item/weapon/cell/C = charging.get_cell() @@ -130,10 +130,10 @@ if(!C.fully_charged()) icon_state = icon_state_charging C.give(CELLRATE*efficiency) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) else icon_state = icon_state_charged - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/recharger/emp_act(severity) if(stat & (NOPOWER|BROKEN) || !anchored) diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index f702f3cf25..3f60e0ae07 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/recharge_station - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 var/mob/occupant = null var/obj/item/weapon/cell/cell = null @@ -78,9 +78,9 @@ if(!has_cell_power()) return 0 - if(use_power == 1) + if(use_power == USE_POWER_IDLE) cell.use(idle_power_usage * CELLRATE) - else if(use_power >= 2) + else if(use_power >= USE_POWER_ACTIVE) cell.use(active_power_usage * CELLRATE) return 1 diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm index 7c5f8cafa0..5bbb4ac6be 100644 --- a/code/game/machinery/robot_fabricator.dm +++ b/code/game/machinery/robot_fabricator.dm @@ -7,7 +7,7 @@ var/metal_amount = 0 var/operating = 0 var/obj/item/robot_parts/being_built = null - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 active_power_usage = 10000 @@ -115,7 +115,7 @@ Please wait until completion...
if(!isnull(building)) if(metal_amount >= build_cost) operating = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) metal_amount = max(0, metal_amount - build_cost) @@ -128,7 +128,7 @@ Please wait until completion...
if(!isnull(being_built)) being_built.loc = get_turf(src) being_built = null - update_use_power(1) + update_use_power(USE_POWER_IDLE) operating = 0 overlays -= "fab-active" return diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 51ec807367..4db1b65da1 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -15,7 +15,7 @@ name = "status display" anchored = 1 density = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 circuit = /obj/item/weapon/circuitboard/status_display var/mode = 1 // 0 = Blank @@ -220,7 +220,7 @@ return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" /obj/machinery/status_display/proc/get_supply_shuttle_timer() - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if(!shuttle) return "Error" diff --git a/code/game/machinery/supply_display.dm b/code/game/machinery/supply_display.dm index 211918c5e6..d0590cfa4e 100644 --- a/code/game/machinery/supply_display.dm +++ b/code/game/machinery/supply_display.dm @@ -6,7 +6,7 @@ message1 = "CARGO" message2 = "" - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if(!shuttle) message2 = "Error" else if(shuttle.has_arrive_time()) diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index 9f228be5d4..df40ba1a7d 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -58,7 +58,7 @@ /obj/machinery/power/supply_beacon/attack_hand(var/mob/user) if(expended) - use_power = 0 + update_use_power(USE_POWER_OFF) to_chat (user, "\The [src] has used up its charge.") return @@ -80,7 +80,7 @@ return set_light(3, 3, "#00CCAA") icon_state = "beacon_active" - use_power = 1 + use_power = USE_POWER_IDLE if(user) to_chat(user, "You activate the beacon. The supply drop will be dispatched soon.") /obj/machinery/power/supply_beacon/proc/deactivate(var/mob/user, var/permanent) @@ -90,7 +90,7 @@ else icon_state = "beacon" set_light(0) - use_power = 0 + use_power = USE_POWER_OFF target_drop_time = null if(user) to_chat(user, "You deactivate the beacon.") diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 5eb18d6d2f..ba8df3b37c 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -17,7 +17,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept desc = "A dish-shaped machine used to broadcast processed subspace signals." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 25 machinetype = 5 produces_heat = 0 @@ -127,7 +127,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept desc = "A compact machine used for portable subspace telecommuniations processing." density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 machinetype = 6 produces_heat = 0 diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 4ba184a102..c2e9126fc1 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -251,7 +251,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 1 produces_heat = 0 @@ -318,7 +318,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send/receive massive amounts of data." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1600 machinetype = 7 circuit = /obj/item/weapon/circuitboard/telecomms/hub @@ -377,7 +377,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send massive amounts of data far away." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 8 produces_heat = 0 @@ -443,7 +443,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send massive amounts of data quickly." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1000 machinetype = 2 circuit = /obj/item/weapon/circuitboard/telecomms/bus @@ -504,7 +504,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "This machine is used to process large quantities of information." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 3 delay = 5 @@ -556,7 +556,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A machine used to store data and network statistics." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 machinetype = 4 circuit = /obj/item/weapon/circuitboard/telecomms/server diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index a0bdb5cd43..a58279098d 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -171,7 +171,7 @@ icon_state = "tele0" dir = 4 var/accurate = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 circuit = /obj/item/weapon/circuitboard/teleporter_hub @@ -319,7 +319,7 @@ dir = 4 var/active = 0 var/engaged = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 circuit = /obj/item/weapon/circuitboard/teleporter_station @@ -356,8 +356,8 @@ if(com) com.icon_state = "tele1" use_power(5000) - update_use_power(2) - com.update_use_power(2) + update_use_power(USE_POWER_ACTIVE) + com.update_use_power(USE_POWER_ACTIVE) for(var/mob/O in hearers(src, null)) O.show_message("Teleporter engaged!", 2) add_fingerprint(usr) @@ -371,8 +371,8 @@ if(com) com.icon_state = "tele0" com.accurate = 0 - com.update_use_power(1) - update_use_power(1) + com.update_use_power(USE_POWER_IDLE) + update_use_power(USE_POWER_IDLE) for(var/mob/O in hearers(src, null)) O.show_message("Teleporter disengaged!", 2) add_fingerprint(usr) diff --git a/code/game/machinery/transportpod.dm b/code/game/machinery/transportpod.dm index 96401c0908..0b499096fe 100644 --- a/code/game/machinery/transportpod.dm +++ b/code/game/machinery/transportpod.dm @@ -6,7 +6,7 @@ density = 1 //thicc anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/in_transit = 0 var/mob/occupant = null diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 979c633ee9..1575c51521 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -14,7 +14,7 @@ var/icon_deny //Icon_state when denying access // Power - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 var/vend_power_usage = 150 //actuators and stuff diff --git a/code/game/machinery/virtual_reality/ar_console.dm b/code/game/machinery/virtual_reality/ar_console.dm index 1f4c8c16fe..f4c6049487 100644 --- a/code/game/machinery/virtual_reality/ar_console.dm +++ b/code/game/machinery/virtual_reality/ar_console.dm @@ -73,7 +73,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/vr_sleeper/alien/enter_vr() diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm index dcd6e555b9..8a417a98bc 100644 --- a/code/game/machinery/virtual_reality/vr_console.dm +++ b/code/game/machinery/virtual_reality/vr_console.dm @@ -18,7 +18,7 @@ var/mirror_first_occupant = TRUE // Do we force the newly produced body to look like the occupant? - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 active_power_usage = 200 light_color = "#FF0000" @@ -174,7 +174,7 @@ M.client.perspective = EYE_PERSPECTIVE M.client.eye = src M.loc = src - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) occupant = M update_icon() @@ -203,7 +203,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/vr_sleeper/proc/enter_vr() diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index a3911a2efa..81c720bacd 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -3,7 +3,7 @@ desc = "You're not so sure about this, anymore..." icon = 'icons/obj/device.dmi' icon_state = "syndbeacon" - use_power = 0 + use_power = USE_POWER_OFF anchored = 1 density = 1 var/charges = 1 diff --git a/code/game/mecha/equipment/tools/repair_droid.dm b/code/game/mecha/equipment/tools/repair_droid.dm index f76eeed6f0..f4f9696aa5 100644 --- a/code/game/mecha/equipment/tools/repair_droid.dm +++ b/code/game/mecha/equipment/tools/repair_droid.dm @@ -27,16 +27,16 @@ /obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/mecha/M as obj) ..() droid_overlay = new(src.icon, icon_state = "repair_droid") - M.overlays += droid_overlay + M.add_overlay(droid_overlay) return /obj/item/mecha_parts/mecha_equipment/repair_droid/destroy() - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) ..() return /obj/item/mecha_parts/mecha_equipment/repair_droid/detach() - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) pr_repair_droid.stop() ..() return @@ -49,7 +49,7 @@ /obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list) ..() if(href_list["toggle_repairs"]) - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) if(pr_repair_droid.toggle()) droid_overlay = new(src.icon, icon_state = "repair_droid_a") log_message("Activated.") @@ -57,7 +57,7 @@ droid_overlay = new(src.icon, icon_state = "repair_droid") log_message("Deactivated.") set_ready_state(1) - chassis.overlays += droid_overlay + chassis.add_overlay(droid_overlay) send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) return diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 24176ef0f8..8e15585da8 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -5,7 +5,7 @@ desc = "A machine used for construction of mechas." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 req_access = list(access_robotics) @@ -48,11 +48,11 @@ if(stat) return if(busy) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) progress += speed check_build() else - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/mecha_part_fabricator/update_icon() diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm index f554c936c6..16a70ad013 100644 --- a/code/game/mecha/mech_prosthetics.dm +++ b/code/game/mecha/mech_prosthetics.dm @@ -5,7 +5,7 @@ desc = "A machine used for construction of prosthetics." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 req_access = list(access_robotics) @@ -52,11 +52,11 @@ if(stat) return if(busy) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) progress += speed check_build() else - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/pros_fabricator/update_icon() diff --git a/code/game/mecha/mech_sensor.dm b/code/game/mecha/mech_sensor.dm index 72202788b9..33147e0ab1 100644 --- a/code/game/mecha/mech_sensor.dm +++ b/code/game/mecha/mech_sensor.dm @@ -6,7 +6,7 @@ anchored = 1 density = 1 throwpass = 1 - use_power = 1 + use_power = USE_POWER_IDLE layer = ON_WINDOW_LAYER power_channel = EQUIP var/on = 0 diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index d78053d6b2..eb9babdac8 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -259,7 +259,9 @@ /obj/mecha/proc/check_for_support() - if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) + var/list/things = orange(1, src) + + if(locate(/obj/structure/grille in things) || locate(/obj/structure/lattice in things) || locate(/turf/simulated in things) || locate(/turf/unsimulated in things)) return 1 else return 0 @@ -397,21 +399,26 @@ /obj/mecha/relaymove(mob/user,direction) if(user != src.occupant) //While not "realistic", this piece is player friendly. if(istype(user,/mob/living/carbon/brain)) - to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") + to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") return 0 user.forceMove(get_turf(src)) to_chat(user, "You climb out from [src]") return 0 if(connected_port) if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") + src.occupant_message("Unable to move while connected to the air system port") last_message = world.time return 0 if(state) - occupant_message("Maintenance protocols in effect") + occupant_message("Maintenance protocols in effect") return return domove(direction) +/obj/mecha/proc/can_ztravel() + for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) + return jp.equip_ready + return FALSE + /obj/mecha/proc/domove(direction) return call((proc_res["dyndomove"]||src), "dyndomove")(direction) @@ -423,20 +430,51 @@ return 0 if(!has_charge(step_energy_drain)) return 0 + var/move_result = 0 + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) move_result = mechsteprand() - else if(src.dir!=direction) + //Up/down zmove + else if(direction & UP || direction & DOWN) + if(!can_ztravel()) + occupant_message("Your vehicle lacks the capacity to move in that direction!") + return FALSE + + //We're using locs because some mecha are 2x2 turfs. So thicc! + var/result = TRUE + + for(var/turf/T in locs) + if(!T.CanZPass(src,direction)) + occupant_message("You can't move that direction from here!") + result = FALSE + break + var/turf/dest = direction & UP ? GetAbove(T) : GetBelow(T) + if(!dest) + occupant_message("There is nothing of interest in this direction.") + result = FALSE + break + if(!dest.CanZPass(src,direction)) + occupant_message("There's something blocking your movement in that direction!") + result = FALSE + break + if(result) + move_result = mechstep(direction) + //Turning + else if(src.dir != direction) move_result = mechturn(direction) + //Stepping else move_result = mechstep(direction) + + if(move_result) can_move = 0 use_power(step_energy_drain) if(istype(src.loc, /turf/space)) if(!src.check_for_support()) src.pr_inertial_movement.start(list(src,direction)) - src.log_message("Movement control lost. Inertial movement started.") + src.log_message("Movement control lost. Inertial movement started.") if(do_after(step_in)) can_move = 1 return 1 @@ -1837,7 +1875,7 @@ O.aiRestorePowerRoutine = 0 O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! O.laws = AI.laws - O.stat = AI.stat + O.set_stat(AI.stat) O.oxyloss = AI.getOxyLoss() O.fireloss = AI.getFireLoss() O.bruteloss = AI.getBruteLoss() diff --git a/code/game/mecha/space/hoverpod.dm b/code/game/mecha/space/hoverpod.dm index 31558458d9..fc5fc1e739 100644 --- a/code/game/mecha/space/hoverpod.dm +++ b/code/game/mecha/space/hoverpod.dm @@ -26,11 +26,20 @@ max_universal_equip = 1 max_special_equip = 1 -/obj/mecha/working/hoverpod/New() - ..() +/obj/mecha/working/hoverpod/Initialize() + . = ..() ion_trail = new /datum/effect/effect/system/ion_trail_follow() ion_trail.set_up(src) - ion_trail.start() + +/obj/mecha/working/hoverpod/moved_inside(var/mob/living/carbon/human/H as mob) + . = ..(H) + if(.) + ion_trail.start() + +/obj/mecha/working/hoverpod/go_out() + . = ..() + if(!occupant) + ion_trail.stop() //Modified phazon code /obj/mecha/working/hoverpod/Topic(href, href_list) @@ -52,6 +61,9 @@ output += ..() return output +/obj/mecha/working/hoverpod/can_ztravel() + return (stabilization_enabled && has_charge(step_energy_drain)) + // No space drifting /obj/mecha/working/hoverpod/check_for_support() //does the hoverpod have enough charge left to stabilize itself? @@ -106,7 +118,7 @@ max_special_equip = 1 /obj/mecha/working/hoverpod/combatpod/Initialize() - ..() + . = ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive @@ -117,7 +129,7 @@ desc = "Who knew a tiny ball could fit three people?" /obj/mecha/working/hoverpod/shuttlepod/Initialize() - ..() + . = ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger diff --git a/code/game/mecha/space/shuttle.dm b/code/game/mecha/space/shuttle.dm index 7e361ece0c..bf9aec3476 100644 --- a/code/game/mecha/space/shuttle.dm +++ b/code/game/mecha/space/shuttle.dm @@ -41,44 +41,30 @@ max_universal_equip = 1 max_special_equip = 1 -/obj/mecha/working/hoverpod/Initialize() - ..() - ion_trail.stop() - -/obj/mecha/working/hoverpod/shuttlecraft/moved_inside(var/mob/living/carbon/human/H as mob) - . = ..(H) - if(.) - ion_trail.start() - -/obj/mecha/working/hoverpod/shuttlecraft/go_out() - . = ..() - if(!occupant) - ion_trail.stop() - /obj/mecha/working/hoverpod/shuttlecraft/update_icon() - overlays.Cut() + cut_overlays() ..() if(base_paint) if(!base_paint_mask) base_paint_mask = image(icon, "[initial_icon]-mask+base", src.layer + 1) base_paint_mask.color = base_paint - overlays |= base_paint_mask + add_overlay(base_paint_mask) if(front_paint) if(!front_paint_mask) front_paint_mask = image(icon, "[initial_icon]-mask+front", src.layer + 1) front_paint_mask.color = front_paint - overlays |= front_paint_mask + add_overlay(front_paint_mask) if(engine_paint) if(!engine_paint_mask) engine_paint_mask = image(icon, "[initial_icon]-mask+engine", src.layer + 1) engine_paint_mask.color = engine_paint - overlays |= engine_paint_mask + add_overlay(engine_paint_mask) if(central_paint) if(!engine_paint_mask) central_paint_mask = image(icon, "[initial_icon]-mask+central", src.layer + 2) central_paint_mask.color = central_paint - overlays |= central_paint_mask + add_overlay(central_paint_mask) /obj/mecha/working/hoverpod/shuttlecraft/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/device/multitool) && state == 1) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 8a9220e87f..203b22c93c 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -44,3 +44,50 @@ icon_state = "explosionfast" duration = 4 // VOREStation Add End + +/obj/effect/temp_visual/impact_effect + icon_state = "impact_bullet" + plane = PLANE_LIGHTING_ABOVE // So they're visible even in a shootout in maint. + duration = 5 + +/obj/effect/temp_visual/impact_effect/Initialize(mapload, obj/item/projectile/P, x, y) + pixel_x = x + pixel_y = y + return ..() + +/obj/effect/temp_visual/impact_effect/red_laser + icon_state = "impact_laser" + duration = 4 + +/obj/effect/temp_visual/impact_effect/red_laser/wall + icon_state = "impact_laser_wall" + duration = 10 + +/obj/effect/temp_visual/impact_effect/blue_laser + icon_state = "impact_laser_blue" + duration = 4 + +/obj/effect/temp_visual/impact_effect/green_laser + icon_state = "impact_laser_green" + duration = 4 + +/obj/effect/temp_visual/impact_effect/purple_laser + icon_state = "impact_laser_purple" + duration = 4 + +// Colors itself based on the projectile. +// Checks light_color and color. +/obj/effect/temp_visual/impact_effect/monochrome_laser + icon_state = "impact_laser_monochrome" + duration = 4 + +/obj/effect/temp_visual/impact_effect/monochrome_laser/Initialize(mapload, obj/item/projectile/P, x, y) + if(P.light_color) + color = P.light_color + else if(P.color) + color = P.color + return ..() + +/obj/effect/temp_visual/impact_effect/ion + icon_state = "shieldsparkles" + duration = 6 \ No newline at end of file diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index ec178a7709..02161a4db6 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -634,6 +634,8 @@ modules/mob/mob_movement.dm if you move you will be zoomed out modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. */ //Looking through a scope or binoculars should /not/ improve your periphereal vision. Still, increase viewsize a tiny bit so that sniping isn't as restricted to NSEW +/obj/item/var/ignore_visor_zoom_restriction = FALSE + /obj/item/proc/zoom(var/tileoffset = 14,var/viewsize = 9) //tileoffset is client view offset in the direction the user is facing. viewsize is how far out this thing zooms. 7 is normal view var/devicename @@ -682,7 +684,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. H.client.pixel_y = 0 H.visible_message("[usr] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].") - H.looking_elsewhere = TRUE + if(!ignore_visor_zoom_restriction) + H.looking_elsewhere = TRUE H.handle_vision() else diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index e34d1180c9..3d2a6cbc6e 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -425,7 +425,7 @@ var/list/civilian_cartridges = list( if(mode==47) var/supplyData[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if (shuttle) supplyData["shuttle_moving"] = shuttle.has_arrive_time() supplyData["shuttle_eta"] = shuttle.eta_minutes() diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index 25db45b666..ce5eef016c 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -394,7 +394,7 @@ // code\game\machinery\computer\supply.dm, starting at line 55 /obj/item/weapon/commcard/proc/get_supply_shuttle_status() var/shuttle_status[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = supply_controller.shuttle if(shuttle) if(shuttle.has_arrive_time()) shuttle_status["location"] = "In transit" @@ -404,8 +404,8 @@ else shuttle_status["time"] = 0 if(shuttle.at_station()) - if(shuttle.docking_controller) - switch(shuttle.docking_controller.get_docking_status()) + if(shuttle.shuttle_docking_controller) + switch(shuttle.shuttle_docking_controller.get_docking_status()) if("docked") shuttle_status["location"] = "Docked" shuttle_status["mode"] = SUP_SHUTTLE_DOCKED diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 9d08d2571a..1785315469 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -384,7 +384,7 @@ if(!H.client && !H.teleop) for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == H.mind) - to_chat(ghost, "Someone is attempting to resuscitate you. Re-enter your body if you want to be revived! (Verbs -> Ghost -> Re-enter corpse)") + ghost.notify_revive("Someone is trying to resuscitate you. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') break //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process @@ -489,7 +489,7 @@ living_mob_list += M M.timeofdeath = 0 - M.stat = UNCONSCIOUS //Life() can bring them back to consciousness if it needs to. + M.set_stat(UNCONSCIOUS) //Life() can bring them back to consciousness if it needs to. M.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. M.reload_fullscreen() diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index c16102bc55..6c512650b6 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -78,8 +78,8 @@ var/obj/item/rig_module/module = src.loc if(module.holder && module.holder.wearer) var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/weapon/rig/suit = H.back + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() if(istype(suit)) return suit.cell return null diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index fe1685ec70..602f6e3a66 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -66,7 +66,7 @@ if(ghost.mind && ghost.mind.current == R) R.key = ghost.key - R.stat = CONSCIOUS + R.set_stat(CONSCIOUS) dead_mob_list -= R living_mob_list |= R R.notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index fa84ea780f..8ec61079f6 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -902,7 +902,7 @@ /obj/structure/plushie/attack_hand(mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(stored_item && !searching) + if(stored_item && opened && !searching) searching = TRUE if(do_after(user, 10)) to_chat(user, "You find \icon[stored_item] [stored_item] in [src]!") @@ -999,7 +999,7 @@ to_chat(user, "You can see something in there...") /obj/item/toy/plushie/attack_self(mob/user as mob) - if(stored_item && !searching) + if(stored_item && opened && !searching) searching = TRUE if(do_after(user, 10)) to_chat(user, "You find \icon[stored_item] [stored_item] in [src]!") diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 4d6b6eea03..4a160d05c8 100644 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -106,6 +106,7 @@ AI MODULES if(laws) laws.sync(target, 0) + target.notify_of_law_change() addAdditionalLaws(target, sender) to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") diff --git a/code/game/objects/items/weapons/canes.dm b/code/game/objects/items/weapons/canes.dm new file mode 100644 index 0000000000..f7688fb2d1 --- /dev/null +++ b/code/game/objects/items/weapons/canes.dm @@ -0,0 +1,124 @@ +/obj/item/weapon/cane + name = "cane" + desc = "A cane used by a true gentleman." + icon = 'icons/obj/weapons.dmi' + icon_state = "cane" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + force = 5.0 + throwforce = 7.0 + w_class = ITEMSIZE_NORMAL + matter = list(DEFAULT_WALL_MATERIAL = 50) + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + +/obj/item/weapon/cane/crutch + name ="crutch" + desc = "A long stick with a crosspiece at the top, used to help with walking." + icon_state = "crutch" + item_state = "crutch" + +/obj/item/weapon/cane/concealed + var/concealed_blade + +/obj/item/weapon/cane/concealed/New() + ..() + var/obj/item/weapon/material/butterfly/switchblade/temp_blade = new(src) + concealed_blade = temp_blade + temp_blade.attack_self() + +/obj/item/weapon/cane/concealed/attack_self(var/mob/user) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + if(concealed_blade) + user.visible_message("[user] has unsheathed \a [concealed_blade] from [T.his] [src]!", "You unsheathe \the [concealed_blade] from \the [src].") + // Calling drop/put in hands to properly call item drop/pickup procs + playsound(user.loc, 'sound/weapons/holster/sheathout.ogg', 50, 1) + user.drop_from_inventory(src) + user.put_in_hands(concealed_blade) + user.put_in_hands(src) + user.update_inv_l_hand(0) + user.update_inv_r_hand() + concealed_blade = null + else + ..() + +/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/material/butterfly/W, var/mob/user) + if(!src.concealed_blade && istype(W)) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + user.visible_message("[user] has sheathed \a [W] into [T.his] [src]!", "You sheathe \the [W] into \the [src].") + playsound(user.loc, 'sound/weapons/holster/sheathin.ogg', 50, 1) + user.drop_from_inventory(W) + W.loc = src + src.concealed_blade = W + update_icon() + else + ..() + +/obj/item/weapon/cane/concealed/update_icon() + if(concealed_blade) + name = initial(name) + icon_state = initial(icon_state) + item_state = initial(icon_state) + else + name = "cane shaft" + icon_state = "nullrod" + item_state = "foldcane" + +/obj/item/weapon/cane/white + name = "white cane" + desc = "A white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." + icon_state = "whitecane" + +/obj/item/weapon/cane/white/attack(mob/M as mob, mob/user as mob) + if(user.a_intent == I_HELP) + user.visible_message("\The [user] has lightly tapped [M] on the ankle with their white cane!") + return TRUE + else + . = ..() + + +//Code for Telescopic White Cane writen by Gozulio + +/obj/item/weapon/cane/white/collapsible + name = "telescopic white cane" + desc = "A telescopic white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." + icon_state = "whitecane1in" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 3 + var/on = 0 + +/obj/item/weapon/cane/white/collapsible/attack_self(mob/user as mob) + on = !on + if(on) + user.visible_message("\The [user] extends the white cane.",\ + "You extend the white cane.",\ + "You hear an ominous click.") + icon_state = "whitecane1out" + item_state_slots = list(slot_r_hand_str = "whitecane", slot_l_hand_str = "whitecane") + w_class = ITEMSIZE_NORMAL + force = 5 + attack_verb = list("smacked", "struck", "cracked", "beaten") + else + user.visible_message("\The [user] collapses the white cane.",\ + "You collapse the white cane.",\ + "You hear a click.") + icon_state = "whitecane1in" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_SMALL + force = 3 + attack_verb = list("hit", "poked", "prodded") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) + add_fingerprint(user) + return TRUE \ No newline at end of file diff --git a/code/game/objects/items/weapons/implants/implantaugment.dm b/code/game/objects/items/weapons/implants/implantaugment.dm index 10873d1d06..be6c8721ee 100644 --- a/code/game/objects/items/weapons/implants/implantaugment.dm +++ b/code/game/objects/items/weapons/implants/implantaugment.dm @@ -75,12 +75,12 @@ var/obj/item/organ/external/E = setup_augment_slots(H, NewOrgan) to_chat(H, "You feel a tingling sensation in your [part].") - if(E && istype(E) && !(H.internal_organs_by_name[NewOrgan.organ_tag])) + NewOrgan.forceMove(H) + NewOrgan.owner = H + if(E && istype(E) && !(H.internal_organs_by_name[NewOrgan.organ_tag]) && NewOrgan.check_verb_compatability()) spawn(rand(1 SECONDS, 30 SECONDS)) to_chat(H, "You feel a pressure in your [E] as the tingling fades, the lump caused by the implant now gone.") - NewOrgan.forceMove(H) - NewOrgan.owner = H if(E.internal_organs == null) E.internal_organs = list() E.internal_organs |= NewOrgan diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 2db6fea0e6..29193b1c08 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -73,12 +73,14 @@ /obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) if(user.a_intent == I_HURT) - visible_message("[user] has smashed the snowball in their hand!", "You smash the snowball in your hand.") + //visible_message("[user] has smashed the snowball in their hand!", "You smash the snowball in your hand.") + to_chat(user, "You smash the snowball in your hand.") var/atom/S = new /obj/item/stack/material/snow(user.loc) del(src) user.put_in_hands(S) else - visible_message("[user] starts compacting the snowball.", "You start compacting the snowball.") + //visible_message("[user] starts compacting the snowball.", "You start compacting the snowball.") + to_chat(user, "You start compacting the snowball.") if(do_after(user, 2 SECONDS)) var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) del(src) diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 17d7bc4621..9145786d69 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -168,7 +168,7 @@ . = ..() var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") blade_overlay.color = lcolor - color = lcolor + color = lcolor if(rainbow) blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") blade_overlay.color = "FFFFFF" @@ -211,6 +211,8 @@ desc = "An energised battle axe." icon_state = "eaxe" item_state = "eaxe" + colorable = FALSE + lcolor = null //active_force = 150 //holy... active_force = 60 active_throwforce = 35 diff --git a/code/game/objects/items/weapons/mop_deploy.dm b/code/game/objects/items/weapons/mop_deploy.dm index 49836005fa..2e2ea6aebc 100644 --- a/code/game/objects/items/weapons/mop_deploy.dm +++ b/code/game/objects/items/weapons/mop_deploy.dm @@ -1,6 +1,7 @@ /obj/item/weapon/mop_deploy name = "mop" desc = "Deployable mop." + icon = 'icons/obj/janitor.dmi' icon_state = "mop" force = 3 anchored = 1 // Never spawned outside of inventory, should be fine. diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 4e89372ec1..744a92e370 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -186,15 +186,22 @@ var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") if(lcolor) blade_overlay.color = lcolor + color = lcolor cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other if(active) add_overlay(blade_overlay) item_state = "[icon_state]_blade" set_light(lrange, lpower, lcolor) else + color = "FFFFFF" set_light(0) item_state = "[icon_state]" + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + /obj/item/weapon/shield/energy/AltClick(mob/living/user) if(!in_range(src, user)) //Basic checks to prevent abuse return @@ -204,7 +211,7 @@ if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) + lcolor = sanitize_hexcolor(energy_color_input) update_icon() /obj/item/weapon/shield/energy/examine(mob/user) diff --git a/code/game/objects/items/weapons/storage/quickdraw.dm b/code/game/objects/items/weapons/storage/quickdraw.dm index 86d7c76ca2..a2094f50fd 100644 --- a/code/game/objects/items/weapons/storage/quickdraw.dm +++ b/code/game/objects/items/weapons/storage/quickdraw.dm @@ -77,4 +77,22 @@ /obj/item/weapon/reagent_containers/syringe, /obj/item/weapon/reagent_containers/syringe, /obj/item/weapon/reagent_containers/syringe - ) \ No newline at end of file + ) + +/obj/item/weapon/storage/quickdraw/syringe_case/clotting + desc = "A small case for safely carrying sharps around. This one is deluxe!" + max_w_class = ITEMSIZE_SMALL + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting + ) + +/obj/item/weapon/storage/quickdraw/syringe_case/bonemed + desc = "A small case for safely carrying sharps around. This one is deluxe!" + max_w_class = ITEMSIZE_SMALL + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed + ) diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 2144a1e394..abae12ed8a 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -266,6 +266,37 @@ /obj/item/weapon/tool/screwdriver ) +/obj/item/weapon/storage/box/syndie_kit/voidsuit + starts_with = list( + /obj/item/clothing/suit/space/void/merc, + /obj/item/clothing/head/helmet/space/void/merc, + /obj/item/clothing/shoes/magboots, + /obj/item/weapon/tank/jetpack/oxygen + ) + +/obj/item/weapon/storage/box/syndie_kit/voidsuit/fire + starts_with = list( + /obj/item/clothing/suit/space/void/merc/fire, + /obj/item/clothing/head/helmet/space/void/merc/fire, + /obj/item/clothing/shoes/magboots, + /obj/item/weapon/tank/jetpack/oxygen + ) + +/obj/item/weapon/storage/box/syndie_kit/concussion_grenade + starts_with = list( + /obj/item/weapon/grenade/concussion = 8 + ) + +/obj/item/weapon/storage/box/syndie_kit/deadliest_game + starts_with = list( + /obj/item/weapon/beartrap/hunting = 4 + ) + +/obj/item/weapon/storage/box/syndie_kit/viral + starts_with = list( + /obj/item/weapon/virusdish/random = 3 + ) + /obj/item/weapon/storage/secure/briefcase/rifle name = "secure briefcase" starts_with = list( @@ -275,6 +306,15 @@ /obj/item/ammo_casing/a145 = 4 ) +/obj/item/weapon/storage/secure/briefcase/flamer + name = "secure briefcase" + starts_with = list( + /obj/item/weapon/gun/magnetic/gasthrower, + /obj/item/weapon/cell/super, + /obj/item/weapon/stock_parts/capacitor/adv, + /obj/item/weapon/tank/phoron/pressurized = 2 + ) + /obj/item/weapon/storage/secure/briefcase/fuelrod name = "heavy briefcase" desc = "A heavy, locked briefcase." diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 9e984ba05f..6d6839a4a5 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -113,6 +113,19 @@ air_contents.adjust_gas("phoron", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) return +/obj/item/weapon/tank/phoron/pressurized + name = "fuel can" + icon_state = "phoron_vox" + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/tank/phoron/pressurized/Initialize() + ..() + + adjust_scale(0.8) + + air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + return + /* * Emergency Oxygen */ diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 1f9397e705..8e6667c759 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -105,6 +105,7 @@ L.Stun(stun_length) to_chat(L, "The steel jaws of \the [src] bite into you, trapping you in place!") deployed = 0 + anchored = FALSE can_buckle = initial(can_buckle) /obj/item/weapon/beartrap/Crossed(atom/movable/AM as mob|obj) diff --git a/code/game/objects/structures/holoplant.dm b/code/game/objects/structures/holoplant.dm index bbd92b81ee..9a46b8ca15 100644 --- a/code/game/objects/structures/holoplant.dm +++ b/code/game/objects/structures/holoplant.dm @@ -48,13 +48,13 @@ plant = prepare_icon(emagged ? "emagged" : null) overlays += plant set_light(2) - use_power = 2 + use_power = USE_POWER_ACTIVE /obj/machinery/holoplant/proc/deactivate() overlays -= plant QDEL_NULL(plant) set_light(0) - use_power = 0 + use_power = USE_POWER_OFF /obj/machinery/holoplant/power_change() ..() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 4b4d0e90cc..467579a225 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -124,7 +124,7 @@ icon_state = "shower" density = 0 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/on = 0 var/obj/effect/mist/mymist = null var/ismist = 0 //needs a var so we can make it linger~ diff --git a/code/game/sound.dm b/code/game/sound.dm index 9be335a84c..c6918beba9 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -116,26 +116,81 @@ /proc/get_sfx(soundin) if(istext(soundin)) switch(soundin) - if ("shatter") soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') - if ("explosion") soundin = pick('sound/effects/Explosion1.ogg','sound/effects/Explosion2.ogg','sound/effects/Explosion3.ogg','sound/effects/Explosion4.ogg','sound/effects/Explosion5.ogg','sound/effects/Explosion6.ogg') - if ("sparks") soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks5.ogg','sound/effects/sparks6.ogg','sound/effects/sparks7.ogg') - if ("rustle") soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if ("punch") soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if ("clownstep") soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') - if ("swing_hit") soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if ("hiss") soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if ("pageturn") soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') - if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') - if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') - if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg', - 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg', - 'sound/effects/thunder/thunder10.ogg') - if ("keyboard") soundin = pick('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') - if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') - if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') - if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') - if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') + if("shatter") + soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if("explosion") + soundin = pick( + 'sound/effects/Explosion1.ogg', + 'sound/effects/Explosion2.ogg', + 'sound/effects/Explosion3.ogg', + 'sound/effects/Explosion4.ogg', + 'sound/effects/Explosion5.ogg', + 'sound/effects/Explosion6.ogg') + if("sparks") + soundin = pick( + 'sound/effects/sparks1.ogg', + 'sound/effects/sparks2.ogg', + 'sound/effects/sparks3.ogg', + 'sound/effects/sparks5.ogg', + 'sound/effects/sparks6.ogg', + 'sound/effects/sparks7.ogg') + if("rustle") + soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if("punch") + soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if("clownstep") + soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if("swing_hit") + soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if("hiss") + soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if("pageturn") + soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if("fracture") + soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if("canopen") + soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if("mechstep") + soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if("thunder") + soundin = pick( + 'sound/effects/thunder/thunder1.ogg', + 'sound/effects/thunder/thunder2.ogg', + 'sound/effects/thunder/thunder3.ogg', + 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', + 'sound/effects/thunder/thunder6.ogg', + 'sound/effects/thunder/thunder7.ogg', + 'sound/effects/thunder/thunder8.ogg', + 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if("keyboard") + soundin = pick( + 'sound/effects/keyboard/keyboard1.ogg', + 'sound/effects/keyboard/keyboard2.ogg', + 'sound/effects/keyboard/keyboard3.ogg', + 'sound/effects/keyboard/keyboard4.ogg') + if("button") + soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if("switch") + soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if("casing_sound") + soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if("ricochet") + soundin = pick( + 'sound/weapons/effects/ric1.ogg', + 'sound/weapons/effects/ric2.ogg', + 'sound/weapons/effects/ric3.ogg', + 'sound/weapons/effects/ric4.ogg', + 'sound/weapons/effects/ric5.ogg') + if("bullet_miss") + soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') + if ("pickaxe") + soundin = pick( + 'sound/weapons/mine/pickaxe1.ogg', + 'sound/weapons/mine/pickaxe2.ogg', + 'sound/weapons/mine/pickaxe3.ogg', + 'sound/weapons/mine/pickaxe4.ogg') return soundin //Are these even used? diff --git a/code/game/turfs/simulated/dungeon/wall.dm b/code/game/turfs/simulated/dungeon/wall.dm index 6c39e01798..3bae19db69 100644 --- a/code/game/turfs/simulated/dungeon/wall.dm +++ b/code/game/turfs/simulated/dungeon/wall.dm @@ -16,26 +16,55 @@ return /turf/simulated/wall/solidrock //for more stylish anti-cheese. - name = "solid rock" - desc = "This rock seems dense, impossible to drill." description_info = "Probably not going to be able to drill or bomb your way through this, best to try and find a way around." - icon_state = "bedrock" - var/base_state = "bedrock" + var/rock_side = "rock_side" block_tele = TRUE -/turf/simulated/wall/solidrock/update_icon() - for(var/direction in cardinal) - var/turf/T = get_step(src,direction) - if(istype(T) && !T.density) - var/place_dir = turn(direction, 180) - if(!mining_overlay_cache["rock_side_[place_dir]"]) - mining_overlay_cache["rock_side_[place_dir]"] = image('icons/turf/walls.dmi', "rock_side", dir = place_dir) - T.add_overlay(mining_overlay_cache["rock_side_[place_dir]"]) +/turf/simulated/wall/solidrock/New(var/newloc) + ..(newloc,"bedrock") /turf/simulated/wall/solidrock/Initialize() - icon_state = base_state + . = ..() update_icon(1) +/turf/simulated/wall/solidrock/update_material() + name = "solid rock" + desc = "This rock seems dense, impossible to drill." + +/turf/simulated/wall/solidrock/proc/get_cached_border(var/cache_id, var/direction, var/icon_file, var/icon_state, var/offset = 32) + if(!mining_overlay_cache["[cache_id]_[direction]"]) + var/image/new_cached_image = image(icon_state, dir = direction, layer = ABOVE_TURF_LAYER) + switch(direction) + if(NORTH) + new_cached_image.pixel_y = offset + if(SOUTH) + new_cached_image.pixel_y = -offset + if(EAST) + new_cached_image.pixel_x = offset + if(WEST) + new_cached_image.pixel_x = -offset + mining_overlay_cache["[cache_id]_[direction]"] = new_cached_image + return new_cached_image + + return mining_overlay_cache["[cache_id]_[direction]"] + +/turf/simulated/wall/solidrock/update_icon(var/update_neighbors) + if(density) + var/image/I + for(var/i = 1 to 4) + I = image('icons/turf/wall_masks.dmi', "rock[wall_connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + for(var/direction in cardinal) + var/turf/T = get_step(src,direction) + if(istype(T) && !T.density) + add_overlay(get_cached_border(rock_side,direction,icon,rock_side)) + + else if(update_neighbors) + for(var/direction in alldirs) + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock)) + var/turf/simulated/wall/solidrock/M = get_step(src, direction) + M.update_icon() + /turf/simulated/wall/solidrock/attackby() return @@ -43,4 +72,31 @@ return /turf/simulated/wall/solidrock/take_damage() //These things are suppose to be unbreakable - return \ No newline at end of file + return + + +//Mossy rocks for POI. Unbreakable, no teleport. + +/turf/simulated/wall/solidrock/mossyrockpoi // Version for POI labyrinths. No teleporting, no breaking. + desc = "An old, yet impressively durably rock wall." + var/mossyrock_side = "mossyrock_side" + +/turf/simulated/wall/solidrock/New(var/newloc) + ..(newloc,"mossyrock") + +/turf/simulated/wall/solidrock/mossyrockpoi/update_icon(var/update_neighbors) + if(density) + var/image/I + for(var/i = 1 to 4) + I = image('icons/turf/wall_masks.dmi', "mossyrock[wall_connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + for(var/direction in cardinal) + var/turf/T = get_step(src,direction) + if(istype(T) && !T.density) + add_overlay(get_cached_border(mossyrock_side,direction,icon,mossyrock_side)) + + else if(update_neighbors) + for(var/direction in alldirs) + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock/mossyrockpoi)) + var/turf/simulated/wall/solidrock/mossyrockpoi/M = get_step(src, direction) + M.update_icon() \ No newline at end of file diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index d670fc3a40..bf93dcb8a2 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -55,7 +55,7 @@ return new_dest -/obj/landed_holder/proc/leave_turf() +/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) var/turf/new_source //Change our source to whatever it was before if(turf_type) @@ -67,7 +67,7 @@ new_source.underlays = underlays new_source.decals = decals else - new_source = my_turf.ChangeTurf(get_base_turf_by_area(my_turf),,1) + new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) return new_source diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index f9ae1e7bc3..1ef11c40de 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -232,7 +232,7 @@ if(WT.remove_fuel(0,user)) to_chat(user, "You start repairing the damage to [src].") - playsound(src.loc, WT.usesound, 100, 1) + playsound(src, WT.usesound, 100, 1) if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) to_chat(user, "You finish repairing the damage to [src].") take_damage(-damage) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index b72143e4c0..a3bc0494ca 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -24,6 +24,9 @@ for(var/obj/O in src) O.hide(0) +/turf/space/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice + /turf/space/proc/update_starlight() if(!config.starlight) return diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 4d606bc3f2..8abd2f44fa 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -64,6 +64,10 @@ /turf/proc/is_intact() return 0 +// Used by shuttle code to check if this turf is empty enough to not crush want it lands on. +/turf/proc/is_solid_structure() + return 1 + /turf/attack_hand(mob/user) if(!(user.canmove) || user.restrained() || !(user.pulling)) return 0 diff --git a/code/game/world.dm b/code/game/world.dm index e82bb00d33..0d142118de 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -2,13 +2,6 @@ #define RECOMMENDED_VERSION 501 /world/New() to_world_log("Map Loading Complete") - //logs - log_path += time2text(world.realtime, "YYYY/MM-Month/DD-Day/round-hh-mm-ss") - diary = file("[log_path].log") - href_logfile = file("[log_path]-hrefs.htm") - error_log = file("[log_path]-error.log") - debug_log = file("[log_path]-debug.log") - debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]" changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently if(byond_version < RECOMMENDED_VERSION) @@ -638,4 +631,18 @@ proc/establish_old_db_connection() maxz++ max_z_changed() +// Call this to change world.fps, don't modify it directly. +/world/proc/change_fps(new_value = 20) + if(new_value <= 0) + CRASH("change_fps() called with [new_value] new_value.") + if(fps == new_value) + return //No change required. + + fps = new_value + on_tickrate_change() + +// Called whenver world.tick_lag or world.fps are changed. +/world/proc/on_tickrate_change() + SStimer?.reset_buckets() + #undef FAILED_DB_CONNECTION_CUTOFF diff --git a/code/global_init.dm b/code/global_init.dm index c562209183..c3f8056d95 100644 --- a/code/global_init.dm +++ b/code/global_init.dm @@ -14,9 +14,15 @@ var/global/datum/global_init/init = new () Pre-map initialization stuff should go here. */ /datum/global_init/New() - - makeDatumRefLists() + //logs + log_path += time2text(world.realtime, "YYYY/MM-Month/DD-Day/round-hh-mm-ss") + diary = file("[log_path].log") + href_logfile = file("[log_path]-hrefs.htm") + error_log = file("[log_path]-error.log") + debug_log = file("[log_path]-debug.log") + debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]" load_configuration() + makeDatumRefLists() initialize_integrated_circuits_list() diff --git a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm index 4097632aa3..3767d21a8b 100644 --- a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm @@ -2,17 +2,17 @@ name = "Jump a Shuttle" /datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) . = ..() if(!.) return - var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles + var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles if (!shuttle_tag) return - var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world if (!origin_area) return diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm index 9d6ce44a2d..2f253a1a80 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm @@ -2,7 +2,7 @@ name = "Launch a Shuttle" /datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) @@ -10,15 +10,15 @@ if(!.) return var/list/valid_shuttles = list() - for (var/shuttle_tag in shuttle_controller.shuttles) - if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry)) + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) valid_shuttles += shuttle_tag var/shuttle_tag = input(user, "Which shuttle do you want to launch?") as null|anything in valid_shuttles if (!shuttle_tag) return - var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] if (S.can_launch()) S.launch(user) log_and_message_admins("launched the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm index d1a86fec98..592a8da57c 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm @@ -2,7 +2,7 @@ name = "Launch a Shuttle (Forced)" /datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) @@ -10,15 +10,15 @@ if(!.) return var/list/valid_shuttles = list() - for (var/shuttle_tag in shuttle_controller.shuttles) - if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry)) + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) valid_shuttles += shuttle_tag var/shuttle_tag = input(user, "Which shuttle's launch do you want to force?") as null|anything in valid_shuttles if (!shuttle_tag) return - var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] if (S.can_force()) S.force_launch(user) log_and_message_admins("forced the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm index 5772bbed54..a79a2d7143 100644 --- a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm @@ -2,7 +2,7 @@ name = "Move a Shuttle" /datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) @@ -13,16 +13,15 @@ if (confirm == "Cancel") return - var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles + var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles if (!shuttle_tag) return - var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world - if (!origin_area) return + var/destination_tag = input(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)") as null|anything in SSshuttles.registered_shuttle_landmarks + if (!destination_tag) return + var/destination_location = SSshuttles.get_landmark(destination_tag) + if (!destination_location) return - var/destination_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world - if (!destination_area) return - - S.move(origin_area, destination_area) + S.attempt_move(destination_location) log_and_message_admins("moved the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/random_events/gravity.dm b/code/modules/admin/secrets/random_events/gravity.dm index 929d34601e..42e83d8986 100644 --- a/code/modules/admin/secrets/random_events/gravity.dm +++ b/code/modules/admin/secrets/random_events/gravity.dm @@ -17,7 +17,7 @@ gravity_is_on = !gravity_is_on for(var/area/A in all_areas) - A.gravitychange(gravity_is_on,A) + A.gravitychange(gravity_is_on) feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","Grav") diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index d14c37d97e..feebada82a 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -539,7 +539,7 @@ Pump.air2.gas["nitrogen"] = 3750 //The contents of 2 canisters. Pump.air2.temperature = 50 Pump.air2.update_values() - Pump.use_power=1 + Pump.update_use_power(USE_POWER_IDLE) Pump.target_pressure = 4500 Pump.update_icon() diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm new file mode 100644 index 0000000000..fbafc004db --- /dev/null +++ b/code/modules/admin/verbs/fps.dm @@ -0,0 +1,23 @@ +//Merged Doohl's and the existing ticklag as they both had good elements about them ~ +//Replaces the old Ticklag verb, fps is easier to understand +/client/proc/set_server_fps() + set category = "Debug" + set name = "Set Server FPS" + set desc = "Sets game speed in frames-per-second. Can potentially break the game" + + if(!check_rights(R_DEBUG)) + return + + var/new_fps = round(input("Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps) as num|null) + if(new_fps <= 0) + to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") + return + if(new_fps > config.fps * 1.5) + if(alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", "Confirm", "ABORT-ABORT-ABORT") != "Confirm") + return + + var/msg = "[key_name(src)] has modified world.fps to [new_fps]" + log_admin(msg, 0) + message_admins(msg, 0) + world.change_fps(new_fps) + feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 26eeb37929..312aa20a6f 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -139,7 +139,7 @@ var/list/debug_verbs = list ( ,/client/proc/cmd_assume_direct_control ,/client/proc/jump_to_dead_group ,/client/proc/startSinglo - ,/client/proc/ticklag + ,/client/proc/set_server_fps ,/client/proc/cmd_admin_grantfullaccess ,/client/proc/kaboom ,/client/proc/cmd_admin_areatest diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm deleted file mode 100644 index 3c34f0f35a..0000000000 --- a/code/modules/admin/verbs/ticklag.dm +++ /dev/null @@ -1,24 +0,0 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn - -/client/proc/ticklag() - set category = "Debug" - set name = "Set Ticklag" - set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" - - if(!check_rights(R_DEBUG)) return - - var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null - //I've used ticks of 2 before to help with serious singulo lags - if(newtick && newtick <= 2 && newtick > 0) - log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - message_admins("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - world.tick_lag = newtick - feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - switch(alert("Enable Tick Compensation?","Tick Comp is currently: [config.Tickcomp]","Yes","No")) - if("Yes") config.Tickcomp = 1 - else config.Tickcomp = 0 - else - to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") - - diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index 8878318fd6..1e3aef04bd 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -310,6 +310,13 @@ GLOBAL_PROTECT(VVpixelmovement) if(!variable) return + if(variable in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if (prompt != "Continue") + return + if(!O.can_vv_get(variable)) return diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm index e94d26b3c3..7b49f18ddb 100644 --- a/code/modules/ai/_defines.dm +++ b/code/modules/ai/_defines.dm @@ -20,6 +20,11 @@ #define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. #define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. +// Results of pre-attack checks +#define ATTACK_ON_COOLDOWN -1 // Recently attacked and needs to try again soon. +#define ATTACK_FAILED 0 // Something else went wrong! Maybe they moved away! +#define ATTACK_SUCCESSFUL 1 // We attacked (or tried to, misses count too) + // Reasons for targets to not be valid. Based on why, the AI responds differently. #define AI_TARGET_VALID 0 // We can fight them. #define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index 65fd1785b1..64dfa7d17e 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -70,21 +70,21 @@ /datum/ai_holder/proc/melee_attack(atom/A) pre_melee_attack(A) . = holder.IAttack(A) - if(.) + if(. == ATTACK_SUCCESSFUL) post_melee_attack(A) // Ditto. /datum/ai_holder/proc/ranged_attack(atom/A) pre_ranged_attack(A) . = holder.IRangedAttack(A) - if(.) + if(. == ATTACK_SUCCESSFUL) post_ranged_attack(A) // Most mobs probably won't have this defined but we don't care. /datum/ai_holder/proc/special_attack(atom/movable/AM) pre_special_attack(AM) . = holder.ISpecialAttack(AM) - if(.) + if(. == ATTACK_SUCCESSFUL) post_special_attack(AM) // Called when within striking/shooting distance, however cooldown is not considered. @@ -218,7 +218,6 @@ var/dir_to_target = get_dir(holder, target_atom) holder.face_atom(target_atom) - ai_log("breakthrough() : Exiting", AI_LOG_DEBUG) // Sometimes the mob will try to hit something diagonally, and generally this fails. // So instead we will try two more times with some adjustments if the attack fails. @@ -274,32 +273,32 @@ for(var/obj/structure/window/W in problem_turf) if(W.dir == reverse_dir[holder.dir]) // So that windows get smashed in the right order ai_log("destroy_surroundings() : Attacking side window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) else if(W.is_fulltile()) ai_log("destroy_surroundings() : Attacking full tile window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) // Kill hull shields in the way. for(var/obj/effect/energy_field/shield in problem_turf) if(shield.density) // Don't attack shields that are already down. ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO) - return holder.IAttack(shield) + return melee_attack(shield) // Kill common obstacle in the way like tables. var/obj/structure/obstacle = locate(/obj/structure, problem_turf) if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) ai_log("destroy_surroundings() : Attacking generic structure.", AI_LOG_INFO) - return holder.IAttack(obstacle) + return melee_attack(obstacle) for(var/obj/machinery/door/D in problem_turf) // Required since firelocks take up the same turf. if(D.density) ai_log("destroy_surroundings() : Attacking closed door.", AI_LOG_INFO) - return holder.IAttack(D) + return melee_attack(D) ai_log("destroy_surroundings() : Exiting due to nothing to attack.", AI_LOG_INFO) - return FALSE // Nothing to attack. + return ATTACK_FAILED // Nothing to attack. // Override for special behaviour. /datum/ai_holder/proc/can_violently_breakthrough() - return violent_breakthrough \ No newline at end of file + return violent_breakthrough diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm index 58b8c9d5ee..eb465dec5d 100644 --- a/code/modules/ai/ai_holder_movement.dm +++ b/code/modules/ai/ai_holder_movement.dm @@ -9,13 +9,14 @@ var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). - // Wandering. var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. var/wander_delay = 0 // How many ticks until the mob can move a tile in handle_wander_movement(). var/base_wander_delay = 2 // What the above var gets set to when it wanders. Note that a tick happens every half a second. var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. + // Breakthrough + var/failed_breakthroughs = 0 // How many times we've failed to breakthrough something lately /datum/ai_holder/proc/walk_to_destination() ai_log("walk_to_destination() : Entering.",AI_LOG_TRACE) @@ -90,7 +91,9 @@ // step_to(holder, A) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 return if(move_once() == FALSE) // Start walking the path. @@ -106,7 +109,9 @@ ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 ai_log("walk_path() : Exited.", AI_LOG_TRACE) diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm index 59ffbeea72..b4323782d3 100644 --- a/code/modules/ai/interfaces.dm +++ b/code/modules/ai/interfaces.dm @@ -8,7 +8,7 @@ /mob/living/simple_mob/IAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return attack_target(A) // This will set click cooldown. /mob/living/proc/IRangedAttack(atom/A) @@ -16,7 +16,7 @@ /mob/living/simple_mob/IRangedAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return shoot_target(A) // Test if the AI is allowed to attempt a ranged attack. diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 123b653052..bd88744d70 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -26,7 +26,7 @@ /obj/machinery/gateway/centerstation density = 1 icon_state = "offcenter" - use_power = 1 + use_power = USE_POWER_IDLE //warping vars var/list/linked = list() @@ -142,7 +142,7 @@ obj/machinery/gateway/centerstation/process() /obj/machinery/gateway/centeraway density = 1 icon_state = "offcenter" - use_power = 0 + use_power = USE_POWER_OFF var/calibrated = 1 var/list/linked = list() //a list of the connected gateway chunks var/ready = 0 diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm index 5fd43d268a..121378723f 100644 --- a/code/modules/client/preference_setup/loadout/loadout_general.dm +++ b/code/modules/client/preference_setup/loadout/loadout_general.dm @@ -4,11 +4,11 @@ /datum/gear/cane/white display_name = "white cane" - path = /obj/item/weapon/cane/whitecane + path = /obj/item/weapon/cane/white /datum/gear/cane/white2 display_name = "telescopic white cane" - path = /obj/item/weapon/melee/collapsable_whitecane + path = /obj/item/weapon/cane/white/collapsible /datum/gear/crutch display_name = "crutch" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 2750f52294..e8d12fb489 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -229,6 +229,7 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/gloves.dmi', SPECIES_VOX = 'icons/mob/species/vox/gloves.dmi' ) + drop_sound = 'sound/items/drop/gloves.ogg' /obj/item/clothing/proc/set_clothing_index() return @@ -362,6 +363,7 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/head.dmi', SPECIES_VOX = 'icons/mob/species/vox/head.dmi' ) + drop_sound = 'sound/items/drop/hat.ogg' /obj/item/clothing/head/attack_self(mob/user) if(brightness_on) @@ -517,6 +519,7 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/shoes.dmi', SPECIES_VOX = 'icons/mob/species/vox/shoes.dmi' ) + drop_sound = 'sound/items/drop/shoes.ogg' /obj/item/clothing/shoes/proc/draw_knife() set name = "Draw Boot Knife" diff --git a/code/modules/clothing/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm index f220332413..123eaea154 100644 --- a/code/modules/clothing/clothing_accessories.dm +++ b/code/modules/clothing/clothing_accessories.dm @@ -121,13 +121,24 @@ set name = "Remove Accessory" set category = "Object" set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return + + if(!istype(usr, /mob/living)) + return + + if(usr.stat) + return + var/obj/item/clothing/accessory/A - if(LAZYLEN(accessories)) - A = input("Select an accessory to remove from [src]") as null|anything in accessories + var/accessory_amount = LAZYLEN(accessories) + if(accessory_amount) + if(accessory_amount == 1) + A = accessories[1] // If there's only one accessory, just remove it without any additional prompts. + else + A = input("Select an accessory to remove from \the [src]") as null|anything in accessories + if(A) remove_accessory(usr,A) + if(!LAZYLEN(accessories)) src.verbs -= /obj/item/clothing/proc/removetie_verb accessories = null diff --git a/code/modules/clothing/gloves/antagonist.dm b/code/modules/clothing/gloves/antagonist.dm new file mode 100644 index 0000000000..d1a01d0e68 --- /dev/null +++ b/code/modules/clothing/gloves/antagonist.dm @@ -0,0 +1,157 @@ +/* + * Antagonist-specific gloves, such as traitor or ling-only types. + */ + +// Thief - Traitor / Merc +/obj/item/clothing/gloves/sterile/thieves + name = "sterile gloves" + desc = "Sterile gloves." + description_antag = "These gloves are uniquely suited for stealing, as well as breaking and entering. They have minor insulation.\ + Attempting to 'help' someone will open their backpack, if it exists, or their belt if they have no backpack, allowing you to deposit\ + items into the inventories. Be careful about making too much noise.\ + Disarm intent will swap the items in your LEFT pockets. Grab will swap RIGHT pockets." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + siemens_coefficient = 0.5 // Not perfect, but slightly more protective than nothing. + permeability_coefficient = 0.01 + germ_level = 0 + fingerprint_chance = 10 // They're thieves' gloves. What do you think? + +/obj/item/clothing/gloves/sterile/thieves/proc/pickpocket(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/proximity) + if(!proximity || !user || !target) + return 0 + + if(!istype(target)) + return 0 + + if(user.a_intent != I_HURT && (turn(target.dir, 180) == get_dir(user, target))) + to_chat(target, "[user] rifles in your pockets!") + + if(user.a_intent == I_HELP) + if(istype(target.back,/obj/item/weapon/storage) && do_after(user, 3 SECONDS, target)) + var/obj/item/weapon/storage/Backpack = target.back + Backpack.open(user) + else if(istype(target.belt, /obj/item/weapon/storage) && do_after(user, 5 SECONDS, target)) + var/obj/item/weapon/storage/Belt = target.belt + Belt.open(user) + return 1 + + if(user.a_intent == I_DISARM) + var/obj/item/LTarg = target.l_store + var/obj/item/LUser = user.l_store + + if(do_after(user, 1 SECOND, target)) + if(istype(LTarg) && do_after(user, 1 SECOND, target)) + target.drop_from_inventory(LTarg) + target.l_store = null + user.l_store = LTarg + LTarg.forceMove(user) + LTarg.equipped(user, slot_l_store) + else + target.drop_from_inventory(LTarg) + + if(istype(LUser) && do_after(user, 1 SECOND, target)) + user.drop_from_inventory(LUser) + target.l_store = LUser + LUser.forceMove(target) + LUser.equipped(target, slot_l_store) + else if(istype(LUser) && LUser != user.l_store) // We've taken something, so drop the one that's in bluespace. + user.drop_from_inventory(LUser) + + return 1 + + if(user.a_intent == I_GRAB) + var/obj/item/RTarg = target.r_store + var/obj/item/RUser = user.r_store + + if(do_after(user, 1 SECOND, target)) + if(istype(RTarg) && do_after(user, 1 SECOND, target)) + target.drop_from_inventory(RTarg) + target.r_store = null + user.r_store = RTarg + RTarg.forceMove(user) + RTarg.equipped(user, slot_r_store) + else + target.drop_from_inventory(RTarg) + + if(istype(RUser) && do_after(user, 1 SECOND, target)) + user.drop_from_inventory(RUser) + target.r_store = RUser + RUser.forceMove(target) + RUser.equipped(target, slot_r_store) + else if(istype(RUser) && RUser != user.r_store) // We've taken something, so drop the one that's in bluespace. + user.drop_from_inventory(RUser) + + return 1 + +/obj/item/clothing/gloves/sterile/thieves/Touch(var/atom/A, var/proximity) + if(proximity && istype(usr, /mob/living/carbon/human) && do_after(usr, 1 SECOND, A)) + return pickpocket(usr, A, proximity) + return 0 + +// Buzzer Ring - Traitor, Merc. +/obj/item/clothing/gloves/ring/buzzer + name = "ring" + desc = "A plain metal band." + description_antag = "This morphium-alloy ring continually generates an electric field, capable of electrocuting a target while not injuring the wearer.\ + The device is also capable of 'frankenstein'-ing a corpse, long after normal technology would be able to save them. The body will still be tied to the\ + normal damage limits for survival, however, so care must be taken." + icon_state = "material" + var/battery_type = /obj/item/weapon/cell/device/weapon/recharge + var/obj/item/weapon/cell/battery = null + +/obj/item/clothing/gloves/ring/buzzer/get_cell() + return battery + +/obj/item/clothing/gloves/ring/buzzer/Initialize() + ..() + if(!battery) + battery = new battery_type(src) + +/obj/item/clothing/gloves/ring/buzzer/Touch(var/atom/A, var/proximity) + if(proximity && istype(usr, /mob/living/carbon/human)) + return zap(usr, A, proximity) + return 0 + +/obj/item/clothing/gloves/ring/buzzer/proc/zap(var/mob/living/carbon/human/user, var/atom/movable/target, var/proximity) + . = FALSE + if(user.a_intent == I_HURT && battery.percent() >= 50) + if(isliving(target)) + var/mob/living/L = target + + if(ishuman(L) && battery.percent() >= 90) // Silent text-wise, for maximum potential for gimmicks. + var/mob/living/carbon/human/H = L + + if(H.stat == DEAD) + . = TRUE + + do_defib(H) + + to_chat(L, "You feel a powerful shock!") + if(!.) + playsound(L, 'sound/effects/sparks7.ogg', 40, 1) + L.electrocute_act(battery.percent() * 0.25, src) + battery.emp_act(2) + return . + + return 0 + +/obj/item/clothing/gloves/ring/buzzer/proc/do_defib(var/mob/living/carbon/human/H = null) + if(!istype(H)) + return 0 + + dead_mob_list.Remove(H) + if((H in living_mob_list) || (H in dead_mob_list)) + WARNING("Mob [H] was ring-defibbed but already in the living or dead list still!") + living_mob_list += H + + H.timeofdeath = 0 + H.set_stat(UNCONSCIOUS) + H.failed_last_breath = 0 + H.reload_fullscreen() + + H.emote("gasp") + H.Weaken(rand(10,25)) + H.updatehealth() + + battery.emp_act(1) diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index c5d6f49087..11572e2745 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,9 +1,4 @@ -/obj/item/clothing/gloves - desc = "you aren't supposed to see this." - name = "strange gloves" - icon_state = "black" - item_state = "bgloves" - drop_sound = 'sound/items/drop/gloves.ogg' + /obj/item/clothing/gloves/yellow desc = "These gloves will protect the wearer from electric shock." diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index dafce7ad6b..810ac403f7 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,11 +1,6 @@ //Hat Station 13 -/obj/item/clothing/head/ - name = "hat" - desc = "Apply on head." - drop_sound = 'sound/items/drop/hat.ogg' - /obj/item/clothing/head/collectable name = "collectable hat" desc = "A rare collectable hat." diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 00fccfea19..eab03a3c2e 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -169,7 +169,7 @@ body_parts_covered = HEAD|FACE|EYES w_class = ITEMSIZE_SMALL siemens_coefficient = 0.9 - + /obj/item/clothing/mask/nock_scarab name = "nock mask (blue, scarab)" desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." diff --git a/code/modules/clothing/rings/rings.dm b/code/modules/clothing/rings/rings.dm index c04c757d9d..b8313c5f97 100644 --- a/code/modules/clothing/rings/rings.dm +++ b/code/modules/clothing/rings/rings.dm @@ -1,6 +1,6 @@ //Generic Ring -/obj/item/clothing/ring +/obj/item/clothing/gloves/ring name = "generic ring" desc = "Torus shaped finger decoration." icon_state = "material" diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index c1f970224e..8f72b24163 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,8 +1,3 @@ -/obj/item/clothing/shoes - name = "shoes" - icon_state = "white" - desc = "A pair of shoes." - drop_sound = 'sound/items/drop/shoes.ogg' /obj/item/clothing/shoes/black name = "black shoes" diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/device.dm b/code/modules/clothing/spacesuits/rig/modules/specific/device.dm index 5771857a27..044bdc143c 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/device.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/device.dm @@ -36,7 +36,7 @@ icon_state = "flash" interface_name = "mounted flash" interface_desc = "Stuns your target by blinding them with a bright light." - device_type = /obj/item/device/flash + device_type = /obj/item/device/flash/robot /obj/item/rig_module/device/plasmacutter name = "hardsuit plasma cutter" diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 7b259d59ec..d1cd58f556 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -231,6 +231,15 @@ update_airtight(piece, 0) // Unseal update_icon(1) +/obj/item/weapon/rig/proc/cut_suit() + offline = 2 + canremove = 1 + toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) + toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) + toggle_piece("boots", loc, ONLY_RETRACT, TRUE) + toggle_piece("chest", loc, ONLY_RETRACT, TRUE) + update_icon(1) + /obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) if(sealing) return @@ -739,15 +748,15 @@ wearer.wearing_rig = src update_icon() -/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode) +/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) - if(sealing || !cell || !cell.charge) + if((sealing || !cell || !cell.charge) && !forced) return - if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) + if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) return - if(usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) // If the usr isn't wearing the suit it's probably an AI. + if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. return var/obj/item/check_slot diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index deb1dc53a2..c811cdaee9 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -32,7 +32,12 @@ brightness_on = 4 on = 0 -/obj/item/clothing/head/helmet/space/verb/toggle_camera() +/obj/item/clothing/head/helmet/space/Initialize() + . = ..() + if(camera_networks) + verbs |= /obj/item/clothing/head/helmet/space/proc/toggle_camera + +/obj/item/clothing/head/helmet/space/proc/toggle_camera() set name = "Toggle Helmet Camera" set desc = "Turn your helmet's camera on or off." set category = "Hardsuit" @@ -40,23 +45,18 @@ if(usr.stat || usr.restrained() || usr.incapacitated()) return - if(camera_networks) - if(!camera) - camera = new /obj/machinery/camera(src) - camera.replace_networks(camera_networks) - camera.set_status(FALSE) //So the camera will activate in the following check. - - if(camera.status == TRUE) - camera.set_status(FALSE) - to_chat(usr, "Camera deactivated.") - else - camera.set_status(TRUE) - camera.c_tag = usr.name - to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") + if(!camera) + camera = new /obj/machinery/camera(src) + camera.replace_networks(camera_networks) + camera.set_status(FALSE) //So the camera will activate in the following check. + if(camera.status == TRUE) + camera.set_status(FALSE) + to_chat(usr, "Camera deactivated.") else - to_chat(usr, "This helmet does not have a built-in camera.") - return + camera.set_status(TRUE) + camera.c_tag = usr.name + to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") /obj/item/clothing/head/helmet/space/examine() ..() diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm index c3d0152d52..ff29201748 100644 --- a/code/modules/clothing/under/accessories/holster.dm +++ b/code/modules/clothing/under/accessories/holster.dm @@ -2,7 +2,7 @@ name = "shoulder holster" desc = "A handgun holster." icon_state = "holster" - slot = ACCESSORY_SLOT_TORSO //Legacy/balance purposes + slot = ACCESSORY_SLOT_WEAPON concealed_holster = 1 var/obj/item/holstered = null var/holster_in = 'sound/items/holsterin.ogg' diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index 0643f6992b..b192a89ccc 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -20,7 +20,7 @@ log transactions icon = 'icons/obj/terminals.dmi' icon_state = "atm" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 circuit = /obj/item/weapon/circuitboard/atm var/datum/money_account/authenticated_account diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index 6aa9070c82..5e9e356b78 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -1,7 +1,9 @@ // error_cooldown items will either be positive (cooldown time) or negative (silenced error) // If negative, starts at -1, and goes down by 1 each time that error gets skipped -var/total_runtimes = 0 -var/total_runtimes_skipped = 0 +GLOBAL_VAR_INIT(total_runtimes, 0) +GLOBAL_VAR_INIT(total_runtimes_skipped, 0) + + // The ifdef needs to be down here, since the error viewer references total_runtimes #ifdef DEBUG /world/Error(var/exception/e, var/datum/e_src) @@ -10,7 +12,7 @@ var/total_runtimes_skipped = 0 return ..() if(!GLOB.error_last_seen) // A runtime is occurring too early in start-up initialization return ..() - total_runtimes++ + GLOB.total_runtimes++ var/erroruid = "[e.file][e.line]" var/last_seen = GLOB.error_last_seen[erroruid] @@ -20,7 +22,7 @@ var/total_runtimes_skipped = 0 last_seen = world.time if(cooldown < 0) GLOB.error_cooldown[erroruid]-- // Used to keep track of skip count for this error - total_runtimes_skipped++ + GLOB.total_runtimes_skipped++ return // Error is currently silenced, skip handling it // Handle cooldowns and silencing spammy errors diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm index 0f1fd0ba96..c342a408f0 100644 --- a/code/modules/error_handler/error_viewer.dm +++ b/code/modules/error_handler/error_viewer.dm @@ -88,7 +88,7 @@ var/global/datum/ErrorViewer/ErrorCache/error_cache = null /datum/ErrorViewer/ErrorCache/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear) var/html = buildHeader(null, linear, refreshable=1) - html += "[total_runtimes] runtimes, [total_runtimes_skipped] skipped

" + html += "[GLOB.total_runtimes] runtimes, [GLOB.total_runtimes_skipped] skipped

" if(!linear) html += "organized | [makeLink("linear", null, 1)]
" var/datum/ErrorViewer/ErrorSource/error_source diff --git a/code/modules/events/gravity.dm b/code/modules/events/gravity.dm index 4d0881294f..049464cb54 100644 --- a/code/modules/events/gravity.dm +++ b/code/modules/events/gravity.dm @@ -17,7 +17,7 @@ gravity_is_on = 0 for(var/area/A in all_areas) if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) + A.gravitychange(gravity_is_on) /datum/event/gravity/end() if(!gravity_is_on) @@ -25,6 +25,6 @@ for(var/area/A in all_areas) if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) + A.gravitychange(gravity_is_on) command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") diff --git a/code/modules/food/kitchen/cooking_machines/_cooker.dm b/code/modules/food/kitchen/cooking_machines/_cooker.dm index 0f39b1f0e6..008910c253 100644 --- a/code/modules/food/kitchen/cooking_machines/_cooker.dm +++ b/code/modules/food/kitchen/cooking_machines/_cooker.dm @@ -10,7 +10,7 @@ icon = 'icons/obj/cooking_machines.dmi' density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 var/on_icon // Icon state used when cooking. diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm index 062fa42fc0..a325356005 100644 --- a/code/modules/food/kitchen/gibber.dm +++ b/code/modules/food/kitchen/gibber.dm @@ -14,7 +14,7 @@ var/gib_time = 40 // Time from starting until meat appears var/gib_throw_dir = WEST // Direction to spit meat and gibs in. - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 500 diff --git a/code/modules/food/kitchen/icecream.dm b/code/modules/food/kitchen/icecream.dm index 50709b872b..753891229b 100644 --- a/code/modules/food/kitchen/icecream.dm +++ b/code/modules/food/kitchen/icecream.dm @@ -14,7 +14,7 @@ icon_state = "icecream_vat" density = 1 anchored = 0 - use_power = 0 + use_power = USE_POWER_OFF flags = OPENCONTAINER | NOREACT var/list/product_types = list() diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index 0cc14a6a43..6df081eaa1 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -5,7 +5,7 @@ icon_state = "mw" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 clicksound = "button" diff --git a/code/modules/food/kitchen/smartfridge.dm b/code/modules/food/kitchen/smartfridge.dm index f3c966c331..fd17a8bc42 100644 --- a/code/modules/food/kitchen/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge.dm @@ -6,7 +6,7 @@ icon_state = "smartfridge" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 flags = NOREACT diff --git a/code/modules/gamemaster/event2/events/everyone/gravity.dm b/code/modules/gamemaster/event2/events/everyone/gravity.dm index abfa22ec12..37a0e2daeb 100644 --- a/code/modules/gamemaster/event2/events/everyone/gravity.dm +++ b/code/modules/gamemaster/event2/events/everyone/gravity.dm @@ -24,11 +24,11 @@ /datum/event2/event/gravity/start() for(var/area/A in all_areas) if(A.z in get_location_z_levels()) - A.gravitychange(FALSE, A) + A.gravitychange(FALSE) /datum/event2/event/gravity/end() for(var/area/A in all_areas) if(A.z in get_location_z_levels()) - A.gravitychange(TRUE, A) + A.gravitychange(TRUE) command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 2a4afe462e..5783bfc398 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -4,7 +4,7 @@ icon_keyboard = "tech_key" icon_screen = "holocontrol" - use_power = 1 + use_power = USE_POWER_IDLE active_power_usage = 8000 //8kW for the scenery + 500W per holoitem var/item_power_usage = 500 @@ -224,7 +224,7 @@ damaged = 1 loadProgram(powerdown_program, 0) active = 0 - use_power = 1 + update_use_power(USE_POWER_IDLE) for(var/mob/M in range(10,src)) M.show_message("The holodeck overloads!") @@ -268,10 +268,10 @@ loadProgram(powerdown_program, 0) if(!linkedholodeck.has_gravity) - linkedholodeck.gravitychange(1,linkedholodeck) + linkedholodeck.gravitychange(1) active = 0 - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/computer/HolodeckControl/proc/loadProgram(var/prog, var/check_delay = 1) @@ -301,7 +301,7 @@ last_change = world.time active = 1 - use_power = 2 + use_power = USE_POWER_ACTIVE for(var/item in holographic_objs) derez(item) @@ -362,19 +362,19 @@ last_gravity_change = world.time active = 1 - use_power = 1 + use_power = USE_POWER_IDLE if(A.has_gravity) - A.gravitychange(0,A) + A.gravitychange(0) else - A.gravitychange(1,A) + A.gravitychange(1) /obj/machinery/computer/HolodeckControl/proc/emergencyShutdown() //Turn it back to the regular non-holographic room loadProgram(powerdown_program, 0) if(!linkedholodeck.has_gravity) - linkedholodeck.gravitychange(1,linkedholodeck) + linkedholodeck.gravitychange(1) active = 0 - use_power = 1 + use_power = USE_POWER_IDLE diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index c6626c66e7..e9e532e3d5 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -391,7 +391,7 @@ datum/unarmed_attack/holopugilism/unarmed_override(var/mob/living/carbon/human/u var/eventstarted = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON diff --git a/code/modules/holomap/station_holomap.dm b/code/modules/holomap/station_holomap.dm index d38875f220..4d4e500a8f 100644 --- a/code/modules/holomap/station_holomap.dm +++ b/code/modules/holomap/station_holomap.dm @@ -8,7 +8,7 @@ icon_state = "station_map" anchored = 1 density = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/station_map @@ -126,7 +126,7 @@ GLOB.moved_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition) GLOB.dir_set_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition) GLOB.destroyed_event.register(watching_mob, src, /obj/machinery/station_map/proc/stopWatching) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) if(bogus) to_chat(user, "The holomap failed to initialize. This area of space cannot be mapped.") @@ -156,7 +156,7 @@ GLOB.dir_set_event.unregister(watching_mob, src) GLOB.destroyed_event.unregister(watching_mob, src) watching_mob = null - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/station_map/power_change() . = ..() diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index 2acfa4fb72..cc382bd5d9 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -37,7 +37,7 @@ icon_state = "hydrotray3" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE var/obj/item/seeds/seed // Currently loaded seed packet. var/obj/item/weapon/disk/botany/loaded_disk //Currently loaded data disk. diff --git a/code/modules/hydroponics/seed_mobs.dm b/code/modules/hydroponics/seed_mobs.dm index 0c9441dbac..8b5c4a56a1 100644 --- a/code/modules/hydroponics/seed_mobs.dm +++ b/code/modules/hydroponics/seed_mobs.dm @@ -12,7 +12,7 @@ spawn(75) if(!host.ckey && !host.client) host.death() // This seems redundant, but a lot of mobs don't - host.stat = DEAD // handle death() properly. Better safe than etc. + host.set_stat(DEAD) // handle death() properly. Better safe than etc. host.visible_message("[host] is malformed and unable to survive. It expires pitifully, leaving behind some seeds.") var/total_yield = rand(1,3) diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index cc29f0c05f..053ce5a59c 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -24,7 +24,7 @@ icon_state = "seeds" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 var/seeds_initialized = 0 // Map-placed ones break if seeds are loaded right at the start of the round, so we do it on the first interaction diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm index d6297a6766..76a3417645 100644 --- a/code/modules/hydroponics/trays/tray_soil.dm +++ b/code/modules/hydroponics/trays/tray_soil.dm @@ -2,7 +2,7 @@ name = "soil" icon_state = "soil" density = 0 - use_power = 0 + use_power = USE_POWER_OFF mechanical = 0 tray_light = 0 frozen = -1 diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index cc9c678b97..743a0d8e2b 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -22,7 +22,7 @@ /turf/proc/lighting_clear_overlay() if(lighting_overlay) - qdel(lighting_overlay) + qdel(lighting_overlay, force = TRUE) for(var/datum/lighting_corner/C in corners) C.update_active() diff --git a/code/modules/maps/tg/map_template.dm b/code/modules/maps/tg/map_template.dm index 3dd7a2ecf7..6053353b25 100644 --- a/code/modules/maps/tg/map_template.dm +++ b/code/modules/maps/tg/map_template.dm @@ -39,6 +39,9 @@ if (SSatoms.initialized == INITIALIZATION_INSSATOMS) return // let proper initialisation handle it later + var/prev_shuttle_queue_state = SSshuttles.block_init_queue + SSshuttles.block_init_queue = TRUE + var/list/atom/atoms = list() var/list/area/areas = list() var/list/obj/structure/cable/cables = list() @@ -71,6 +74,9 @@ var/area/A = I A.power_change() + SSshuttles.block_init_queue = prev_shuttle_queue_state + SSshuttles.process_init_queues() // We will flush the queue unless there were other blockers, in which case they will do it. + admin_notice("Submap initializations finished.", R_DEBUG) /datum/map_template/proc/load_new_z(var/centered = FALSE, var/orientation = 0) diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index bdfab8f92b..3c38927910 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -1,7 +1,7 @@ /obj/machinery/mining icon = 'icons/obj/mining_drill.dmi' anchored = 0 - use_power = 0 //The drill takes power directly from a cell. + use_power = USE_POWER_OFF //The drill takes power directly from a cell. density = 1 layer = MOB_LAYER+0.1 //So it draws over mobs in the tile north of it. diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 6e4990c40a..5d7616aef7 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -212,6 +212,9 @@ turf/simulated/mineral/floor/light_corner if(istype(get_step(src, direction), /turf/simulated/mineral)) var/turf/simulated/mineral/M = get_step(src, direction) M.update_icon() + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock)) + var/turf/simulated/wall/solidrock/M = get_step(src, direction) + M.update_icon() /turf/simulated/mineral/ex_act(severity) diff --git a/code/modules/mob/_modifiers/feysight.dm b/code/modules/mob/_modifiers/feysight.dm new file mode 100644 index 0000000000..af391ed51c --- /dev/null +++ b/code/modules/mob/_modifiers/feysight.dm @@ -0,0 +1,42 @@ +/datum/modifier/feysight + name = "feysight" + desc = "You are filled with an inner peace, and widened sight." + client_color = "#42e6ca" + + on_created_text = "You feel an inner peace as your mind's eye expands!" + on_expired_text = "Your sight returns to what it once was." + stacks = MODIFIER_STACK_EXTEND + + accuracy = -15 + accuracy_dispersion = 1 + +/datum/modifier/feysight/on_applied() + holder.see_invisible = 60 + holder.see_invisible_default = 60 + +/datum/modifier/feysight/on_expire() + holder.see_invisible_default = initial(holder.see_invisible_default) + holder.see_invisible = holder.see_invisible_default + +/datum/modifier/feysight/can_apply(var/mob/living/L) + if(L.stat) + to_chat(L, "You can't be unconscious or dead to experience tranquility.") + return FALSE + + if(!L.is_sentient()) + return FALSE // Drones don't feel anything. + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species.name == "Diona") + to_chat(L, "You feel strange for a moment, but it passes.") + return FALSE // Happy trees aren't affected by tranquility. + + return ..() + +/datum/modifier/feysight/tick() + ..() + + if(ishuman(holder)) + var/mob/living/carbon/human/H = holder + H.druggy = min(15, H.druggy + 4) diff --git a/code/modules/mob/_modifiers/fire.dm b/code/modules/mob/_modifiers/fire.dm index d69a35c2ec..3a3c53a9dc 100644 --- a/code/modules/mob/_modifiers/fire.dm +++ b/code/modules/mob/_modifiers/fire.dm @@ -17,6 +17,9 @@ /datum/modifier/fire/tick() holder.inflict_heat_damage(damage_per_tick) +/datum/modifier/fire/weak + damage_per_tick = 1 + /* * Modifier used by projectiles, like the flamethrower, that rely heavily on fire_stacks to persist. */ @@ -29,8 +32,11 @@ expire() else if(holder.fire_stacks > 0) - holder.fire_stacks -= 1 + holder.fire_stacks -= 0.5 /datum/modifier/fire/stack_managed/intense mob_overlay_state = "on_fire_intense" damage_per_tick = 10 + +/datum/modifier/fire/stack_managed/weak + damage_per_tick = 1 diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm index 665ae80617..ca0f96d07b 100644 --- a/code/modules/mob/_modifiers/modifiers.dm +++ b/code/modules/mob/_modifiers/modifiers.dm @@ -59,7 +59,7 @@ // Checks if the modifier should be allowed to be applied to the mob before attaching it. // Override for special criteria, e.g. forbidding robots from receiving it. -/datum/modifier/proc/can_apply(var/mob/living/L) +/datum/modifier/proc/can_apply(var/mob/living/L, var/suppress_output = FALSE) return TRUE // Checks to see if this datum should continue existing. @@ -113,7 +113,8 @@ // Call this to add a modifier to a mob. First argument is the modifier type you want, second is how long it should last, in ticks. // Third argument is the 'source' of the modifier, if it's from someone else. If null, it will default to the mob being applied to. // The SECONDS/MINUTES macro is very helpful for this. E.g. M.add_modifier(/datum/modifier/example, 5 MINUTES) -/mob/living/proc/add_modifier(var/modifier_type, var/expire_at = null, var/mob/living/origin = null) +// The fourth argument is a boolean to suppress failure messages, set it to true if the modifier is repeatedly applied (as chem-based modifiers are) to prevent chat-spam +/mob/living/proc/add_modifier(var/modifier_type, var/expire_at = null, var/mob/living/origin = null, var/suppress_failure = FALSE) // First, check if the mob already has this modifier. for(var/datum/modifier/M in modifiers) if(ispath(modifier_type, M)) @@ -130,7 +131,7 @@ // If we're at this point, the mob doesn't already have it, or it does but stacking is allowed. var/datum/modifier/mod = new modifier_type(src, origin) - if(!mod.can_apply(src)) + if(!mod.can_apply(src, suppress_failure)) qdel(mod) return if(expire_at) diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm index 9d87088dae..743e41bd1d 100644 --- a/code/modules/mob/_modifiers/modifiers_misc.dm +++ b/code/modules/mob/_modifiers/modifiers_misc.dm @@ -112,16 +112,18 @@ the artifact triggers the rage. var/mob/living/carbon/human/H = holder H.shock_stage = last_shock_stage -/datum/modifier/berserk/can_apply(var/mob/living/L) +/datum/modifier/berserk/can_apply(var/mob/living/L, var/suppress_failure = FALSE) if(L.stat) - to_chat(L, "You can't be unconscious or dead to berserk.") + if(!suppress_failure) + to_chat(L, "You can't be unconscious or dead to berserk.") return FALSE // It would be weird to see a dead body get angry all of a sudden. if(!L.is_sentient()) return FALSE // Drones don't feel anything. if(L.has_modifier_of_type(/datum/modifier/berserk_exhaustion)) - to_chat(L, "You recently berserked, and cannot do so again while exhausted.") + if(!suppress_failure) + to_chat(L, "You recently berserked, and cannot do so again while exhausted.") return FALSE // On cooldown. if(L.isSynthetic()) @@ -135,7 +137,8 @@ the artifact triggers the rage. return FALSE // Happy trees aren't affected by blood rages. if(L.nutrition < nutrition_cost) - to_chat(L, "You are too hungry to berserk.") + if(!suppress_failure) + to_chat(L, "You are too hungry to berserk.") return FALSE // Too hungry to enrage. return ..() diff --git a/code/modules/mob/dead/corpse.dm b/code/modules/mob/dead/corpse.dm index 81a51214a7..18d81bf484 100644 --- a/code/modules/mob/dead/corpse.dm +++ b/code/modules/mob/dead/corpse.dm @@ -33,7 +33,7 @@ /obj/effect/landmark/mobcorpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) M.real_name = src.name - M.stat = 2 //Kills the new mob + M.set_stat(DEAD) //Kills the new mob if(src.corpseuniform) M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) if(src.corpsesuit) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index b1de8f6a9d..96bea5fc74 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -85,6 +85,7 @@ "ED-209" = "ed209", "Beepsky" = "secbot" ) + var/last_revive_notification = null // world.time of last notification, used to avoid spamming players from defibs or cloners. /mob/observer/dead/New(mob/body) sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF @@ -93,8 +94,6 @@ plane = PLANE_GHOSTS //Why doesn't the var above work...??? verbs += /mob/observer/dead/proc/dead_tele - stat = DEAD - var/turf/T if(ismob(body)) T = get_turf(body) //Where is the body located? @@ -137,6 +136,9 @@ var/mob/target = locate(href_list["track"]) in mob_list if(target) ManualFollow(target) + if(href_list["reenter"]) + reenter_corpse() + return /mob/observer/dead/attackby(obj/item/W, mob/user) if(istype(W,/obj/item/weapon/book/tome)) @@ -145,6 +147,11 @@ /mob/observer/dead/CanPass(atom/movable/mover, turf/target) return TRUE + +/mob/observer/dead/set_stat(var/new_stat) + if(new_stat != DEAD) + CRASH("It is best if observers stay dead, thank you.") + /* Transfer_mind is there to check if mob is being deleted/not going to have a body. Works together with spawning an observer, noted above. @@ -805,3 +812,18 @@ mob/observer/dead/MayRespawn(var/feedback = 0) /mob/observer/dead/speech_bubble_appearance() return "ghost" + +// Lets a ghost know someone's trying to bring them back, and for them to get into their body. +// Mostly the same as TG's sans the hud element, since we don't have TG huds. +/mob/observer/dead/proc/notify_revive(var/message, var/sound, flashwindow = TRUE) + if((last_revive_notification + 2 MINUTES) > world.time) + return + last_revive_notification = world.time + + if(flashwindow) + window_flash(client) + if(message) + to_chat(src, "[message]") + to_chat(src, "(Click to re-enter)") + if(sound) + SEND_SOUND(src, sound(sound)) diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index dfd6846846..f11aa26bca 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -76,7 +76,7 @@ if(!gibbed && deathmessage != "no message") // This is gross, but reliable. Only brains use it. src.visible_message("\The [src.name] [deathmessage]") - stat = DEAD + set_stat(DEAD) update_canmove() diff --git a/code/modules/mob/language/monkey.dm b/code/modules/mob/language/monkey.dm index 782c5bcf24..67343cdc6a 100644 --- a/code/modules/mob/language/monkey.dm +++ b/code/modules/mob/language/monkey.dm @@ -4,7 +4,7 @@ speech_verb = "chimpers" ask_verb = "chimpers" exclaim_verb = "screeches" - key = "6" + key = "C" syllables = list("ook","eek") machine_understands = 0 @@ -71,7 +71,7 @@ speech_verb = "chirps" ask_verb = "tweets" exclaim_verb = "squawks" - key = "m" + key = "B" flags = RESTRICTED machine_understands = 0 space_chance = 100 diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 7d71c92343..5e9f496646 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -79,7 +79,7 @@ /mob/living/bot/updatehealth() if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = getMaxHealth() - getFireLoss() - getBruteLoss() oxyloss = 0 diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index 2c39964ad6..3b57c6a5f7 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -51,7 +51,7 @@ if(paralysis && paralysis > 0) blinded = 1 - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) if(halloss > 0) adjustHalLoss(-3) @@ -61,13 +61,13 @@ if(mind.active && client != null) sleeping = max(sleeping-1, 0) blinded = 1 - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else if(resting) if(halloss > 0) adjustHalLoss(-3) else - stat = CONSCIOUS + set_stat(CONSCIOUS) if(halloss > 0) adjustHalLoss(-1) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index b237caf553..4e97a8d5a8 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -64,7 +64,7 @@ B.brainmob = null brainmob.loc = src brainmob.container = src - brainmob.stat = 0 + brainmob.set_stat(CONSCIOUS) dead_mob_list -= brainmob//Update dem lists living_mob_list += brainmob @@ -185,7 +185,7 @@ src.brainmob.add_language(LANGUAGE_EAL) src.brainmob.loc = src src.brainmob.container = src - src.brainmob.stat = 0 + src.brainmob.set_stat(CONSCIOUS) src.brainmob.silent = 0 radio = new(src) dead_mob_list -= src.brainmob @@ -230,7 +230,7 @@ /obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) brainmob.dna = H.dna brainmob.timeofhostdeath = H.timeofdeath - brainmob.stat = 0 + brainmob.set_stat(CONSCIOUS) if(H.mind) H.mind.transfer_to(brainmob) return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index ede19220eb..2c7a4d59b4 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -105,7 +105,11 @@ if (shock_damage<1) return 0 - src.apply_damage(shock_damage, BURN, def_zone, used_weapon="Electrocution") + src.apply_damage(0.2 * shock_damage, BURN, def_zone, used_weapon="Electrocution") //shock the target organ + src.apply_damage(0.4 * shock_damage, BURN, BP_TORSO, used_weapon="Electrocution") //shock the torso more + src.apply_damage(0.2 * shock_damage, BURN, null, used_weapon="Electrocution") //shock a random part! + src.apply_damage(0.2 * shock_damage, BURN, null, used_weapon="Electrocution") //shock a random part! + playsound(loc, "sparks", 50, 1, -1) if (shock_damage > 15) src.visible_message( diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 3f9f77f34c..52c0fdbe0c 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -280,7 +280,10 @@ var/randn = rand(1, 100) last_push_time = world.time - if(!(species.flags & NO_SLIP) && randn <= 25) + // We ARE wearing shoes OR + // We as a species CAN be slipped when barefoot + // And also 1 in 4 because rngesus + if((shoes || !(species.flags & NO_SLIP)) && randn <= 25) var/armor_check = run_armor_check(affecting, "melee") apply_effect(3, WEAKEN, armor_check) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index fde95114bb..d262ca2b18 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -4,7 +4,7 @@ if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) return var/total_burn = 0 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 869c07b5a6..3675866e24 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -527,8 +527,8 @@ emp_act if(damtype != BURN && damtype != BRUTE) return // The rig might soak this hit, if we're wearing one. - if(back && istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() rig.take_hit(damage) // We may also be taking a suit breach. diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index ff3b5ecf5a..0572a452c6 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -177,7 +177,7 @@ compiled_vis |= O.enables_planes //Check to see if we have a rig (ugh, blame rigs, desnowflake this) - var/obj/item/weapon/rig/rig = back + var/obj/item/weapon/rig/rig = get_rig() if(istype(rig) && rig.visor) if(!rig.helmet || (head && rig.helmet == head)) if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index a4e0b77de7..4e61c86399 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -163,8 +163,8 @@ if(back) if(istype(back,/obj/item/weapon/tank/jetpack)) thrust = back - else if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + else if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) thrust = module.jets break diff --git a/code/modules/mob/living/carbon/human/human_resist.dm b/code/modules/mob/living/carbon/human/human_resist.dm index 37e20d0d21..56d42c1a15 100644 --- a/code/modules/mob/living/carbon/human/human_resist.dm +++ b/code/modules/mob/living/carbon/human/human_resist.dm @@ -1,6 +1,6 @@ /mob/living/carbon/human/process_resist() //drop && roll - if(on_fire && !buckled) + if((on_fire || has_modifier_of_type(/datum/modifier/fire)) && !buckled) adjust_fire_stacks(-1.2) Weaken(3) spin(32,2) @@ -9,7 +9,7 @@ "You stop, drop, and roll!" ) sleep(30) - if(fire_stacks <= 0) + if(fire_stacks <= 0 && !(has_modifier_of_type(/datum/modifier/fire))) visible_message( "[src] has successfully extinguished themselves!", "You extinguish yourself." diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index b9b986bd6a..3643075ef3 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -351,15 +351,10 @@ //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make //sure the rig is still worn, still online, and that its air supply still exists. var/obj/item/weapon/tank/rig_supply - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back - if(!rig.offline && (rig.air_supply && internal == rig.air_supply)) - rig_supply = rig.air_supply + var/obj/item/weapon/rig/Rig = get_rig() - else if(istype(belt,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = belt - if(!rig.offline && (rig.air_supply && internal == rig.air_supply)) - rig_supply = rig.air_supply + if(Rig) + rig_supply = Rig.air_supply if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) internal = null @@ -1094,9 +1089,9 @@ return 1 -/mob/living/carbon/human/proc/set_stat(var/new_stat) - stat = new_stat - if(stat) +/mob/living/carbon/human/set_stat(var/new_stat) + . = ..() + if(. && stat) update_skin(1) /mob/living/carbon/human/handle_regular_hud_updates() @@ -1357,7 +1352,7 @@ see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default var/tmp/glasses_processed = 0 - var/obj/item/weapon/rig/rig = back + var/obj/item/weapon/rig/rig = get_rig() if(istype(rig) && rig.visor && !looking_elsewhere) if(!rig.helmet || (head && rig.helmet == head)) if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) @@ -1511,7 +1506,6 @@ else shock_stage = min(shock_stage, 160) shock_stage = max(shock_stage-1, 0) - return if(stat) return 0 diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index dfdad30a86..03137d2fe1 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -78,8 +78,8 @@ /mob/living/carbon/human/GetVoice() var/voice_sub - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() // todo: fix this shit if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) voice_sub = rig.speech.voice_holder.voice diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm index f11d9d137d..7643ea3d2e 100644 --- a/code/modules/mob/living/carbon/metroid/life.dm +++ b/code/modules/mob/living/carbon/metroid/life.dm @@ -102,18 +102,18 @@ else if (src.paralysis || src.stunned || src.weakened || (status_flags & FAKEDEATH)) //Stunned etc. if (src.stunned > 0) - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.weakened > 0) src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.paralysis > 0) src.blinded = 0 src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) else src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.stuttering) src.stuttering = 0 diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index f4ffcc7615..0718dc9421 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -98,11 +98,11 @@ updatehealth() if(stat != DEAD) if(paralysis) - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else if (status_flags & FAKEDEATH) - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else - stat = CONSCIOUS + set_stat(CONSCIOUS) return 1 /mob/living/proc/handle_statuses() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 024b1f2134..89ded4940c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -207,11 +207,10 @@ default behaviour is: /mob/living/proc/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = getMaxHealth() - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - halloss - //This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually //affects them once clothing is factored in. ~Errorage /mob/living/proc/calculate_affecting_pressure(var/pressure) @@ -411,7 +410,7 @@ default behaviour is: if(!isnull(M.incoming_hal_damage_percent)) amount *= M.incoming_hal_damage_percent if(!isnull(M.disable_duration_percent)) - amount *= M.incoming_hal_damage_percent + amount *= M.disable_duration_percent else if(amount < 0) for(var/datum/modifier/M in modifiers) if(!isnull(M.incoming_healing_percent)) @@ -703,7 +702,7 @@ default behaviour is: timeofdeath = 0 // restore us to conciousness - stat = CONSCIOUS + set_stat(CONSCIOUS) // make the icons look correct regenerate_icons() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index a6b05992d8..01f1554663 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -129,7 +129,6 @@ //Stun Beams if(P.taser_effect) stun_effect_act(0, P.agony, def_zone, P) - to_chat(src, "You have been hit by [P]!") if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) qdel(P) @@ -364,6 +363,9 @@ handle_light() update_fire() + if(has_modifier_of_type(/datum/modifier/fire)) + remove_modifiers_of_type(/datum/modifier/fire) + /mob/living/proc/update_fire() return diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 3f6c8411d1..f730077d70 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -9,4 +9,13 @@ return (!mover.density || !density || lying) /mob/CanZASPass(turf/T, is_zone) - return ATMOS_PASS_YES \ No newline at end of file + return ATMOS_PASS_YES + +/mob/living/SelfMove(turf/n, direct) + // If on walk intent, don't willingly step into hazardous tiles. + // Unless the walker is confused. + if(m_intent == "walk" && confused <= 0) + if(!n.is_safe_to_enter(src)) + to_chat(src, span("warning", "\The [n] is dangerous to move into.")) + return FALSE // In case any code wants to know if movement happened. + return ..() // Parent call should make the mob move. \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 1d42d8370e..33998fcaff 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -291,7 +291,7 @@ var/list/ai_verbs_default = list( /obj/machinery/ai_powersupply name="Power Supply" active_power_usage=50000 // Station AIs use significant amounts of power. This, when combined with charged SMES should mean AI lasts for 1hr without external power. - use_power = 2 + use_power = USE_POWER_ACTIVE power_channel = EQUIP var/mob/living/silicon/ai/powered_ai = null invisibility = 100 @@ -319,14 +319,14 @@ var/list/ai_verbs_default = list( qdel(src) return if(powered_ai.APU_power) - use_power = 0 + update_use_power(USE_POWER_OFF) return if(!powered_ai.anchored) loc = powered_ai.loc - use_power = 0 + update_use_power(USE_POWER_OFF) use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that. if(powered_ai.anchored) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) /mob/living/silicon/ai/proc/pick_icon() set category = "AI Settings" diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 0d429fa089..b1398d3170 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -169,7 +169,7 @@ /mob/living/silicon/ai/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) setOxyLoss(0) else health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm index d62d5d15da..07683eb2dc 100644 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ b/code/modules/mob/living/silicon/decoy/life.dm @@ -10,6 +10,6 @@ /mob/living/silicon/decoy/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index 39e11e4802..0071586458 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -1,6 +1,7 @@ /mob/living/silicon var/datum/ai_laws/laws = null var/list/additional_law_channels = list("State" = "") + var/last_law_notification = null // Avoids receiving 5+ of them at once. /mob/living/silicon/proc/laws_sanity_check() if (!src.laws) @@ -9,54 +10,79 @@ /mob/living/silicon/proc/has_zeroth_law() return laws.zeroth_law != null -/mob/living/silicon/proc/set_zeroth_law(var/law, var/law_borg) +/mob/living/silicon/proc/set_zeroth_law(var/law, var/law_borg, notify = TRUE) laws_sanity_check() laws.set_zeroth_law(law, law_borg) + if(notify) + notify_of_law_change(law||law_borg ? "NEW ZEROTH LAW: [istype(src, /mob/living/silicon/robot) && law_borg ? law_borg : law]" : null) log_and_message_admins("has given [src] the zeroth laws: [law]/[law_borg ? law_borg : "N/A"]") -/mob/living/silicon/robot/set_zeroth_law(var/law, var/law_borg) +/mob/living/silicon/robot/set_zeroth_law(var/law, var/law_borg, notify = TRUE) ..() if(tracking_entities) to_chat(src, "Internal camera is currently being accessed.") -/mob/living/silicon/proc/add_ion_law(var/law) +/mob/living/silicon/proc/add_ion_law(var/law, notify = TRUE) laws_sanity_check() laws.add_ion_law(law) + if(notify) + notify_of_law_change("NEW \[!ERROR!\] LAW: [law]") log_and_message_admins("has given [src] the ion law: [law]") -/mob/living/silicon/proc/add_inherent_law(var/law) +/mob/living/silicon/proc/add_inherent_law(var/law, notify = TRUE) laws_sanity_check() laws.add_inherent_law(law) + if(notify) + notify_of_law_change("NEW CORE LAW: [law]") log_and_message_admins("has given [src] the inherent law: [law]") -/mob/living/silicon/proc/add_supplied_law(var/number, var/law) +/mob/living/silicon/proc/add_supplied_law(var/number, var/law, notify = TRUE) laws_sanity_check() laws.add_supplied_law(number, law) + if(notify) + var/th = uppertext("[number]\th") + notify_of_law_change("NEW \[[th]\] LAW: [law]") log_and_message_admins("has given [src] the supplied law: [law]") -/mob/living/silicon/proc/delete_law(var/datum/ai_law/law) +/mob/living/silicon/proc/delete_law(var/datum/ai_law/law, notify = TRUE) laws_sanity_check() laws.delete_law(law) + if(notify) + notify_of_law_change("LAW DELETED: [law.law]") log_and_message_admins("has deleted a law belonging to [src]: [law.law]") -/mob/living/silicon/proc/clear_inherent_laws(var/silent = 0) +/mob/living/silicon/proc/clear_inherent_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_inherent_laws() + if(notify) + notify_of_law_change("CORE LAWS WIPED.") if(!silent) log_and_message_admins("cleared the inherent laws of [src]") -/mob/living/silicon/proc/clear_ion_laws(var/silent = 0) +/mob/living/silicon/proc/clear_ion_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_ion_laws() + if(notify) + notify_of_law_change("CORRUPTED LAWS WIPED.") if(!silent) log_and_message_admins("cleared the ion laws of [src]") -/mob/living/silicon/proc/clear_supplied_laws(var/silent = 0) +/mob/living/silicon/proc/clear_supplied_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_supplied_laws() + if(notify) + notify_of_law_change("NON-CORE LAWS WIPED.") if(!silent) log_and_message_admins("cleared the supplied laws of [src]") +/mob/living/silicon/proc/notify_of_law_change(message) + if((last_law_notification + 1 SECOND) > world.time) + return + last_law_notification = world.time + SEND_SOUND(src, 'sound/machines/defib_success.ogg') + window_flash(client) + to_chat(src, span("warning", message)) + /mob/living/silicon/proc/statelaws(var/datum/ai_laws/laws) var/prefix = "" if(MAIN_CHANNEL == lawchannel) diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 42888a3827..e159308d1b 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -30,6 +30,6 @@ /mob/living/silicon/pai/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = 100 - getBruteLoss() - getFireLoss() diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index cc16dd6dc7..7658ddc1bf 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -257,7 +257,7 @@ var/list/mob_hat_cache = list() /mob/living/silicon/robot/drone/updatehealth() if(status_flags & GODMODE) health = maxHealth - stat = CONSCIOUS + set_stat(CONSCIOUS) return health = maxHealth - (getBruteLoss() + getFireLoss()) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm index 6bd956c4cc..f6ca7105ea 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -11,7 +11,7 @@ density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 5974e7cd03..c08945b3e1 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -88,7 +88,7 @@ if (src.stat != 2) //Alive. if (src.paralysis || src.stunned || src.weakened || !src.has_power) //Stunned etc. - src.stat = 1 + src.set_stat(UNCONSCIOUS) if (src.stunned > 0) AdjustStunned(-1) if (src.weakened > 0) @@ -100,7 +100,7 @@ src.blinded = 0 else //Not stunned. - src.stat = 0 + src.set_stat(CONSCIOUS) AdjustConfused(-1) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index c27d7e36b6..67ab5dd3c4 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,7 +1,7 @@ /mob/living/silicon/robot/updatehealth() if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) return health = getMaxHealth() - (getBruteLoss() + getFireLoss()) return diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 6fdac4213f..1b13cb7096 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -214,7 +214,7 @@ overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = 30 + I.layer) addedSomething = 1 if ( addedSomething ) - user.visible_message("[user] load some items onto their service tray.") + user.visible_message("[user] loads some items onto their service tray.") return @@ -507,4 +507,4 @@ return to_chat(user, "You fail to pick up \the [A] with \the [src]") - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm index 0f3205fb8c..046a4ef988 100644 --- a/code/modules/mob/living/simple_mob/combat.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -3,26 +3,24 @@ set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up. if(!A.Adjacent(src)) - return FALSE + return ATTACK_FAILED var/turf/their_T = get_turf(A) face_atom(A) if(melee_attack_delay) - // their_T.color = "#FF0000" melee_pre_animation(A) + . = ATTACK_SUCCESSFUL //Shoving this in here as a 'best guess' since this proc is about to sleep and return and we won't be able to know the real value handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. // Cooldown testing is done at click code (for players) and interface code (for AI). setClickCooldown(get_attack_speed()) + // Returns a value, but will be lost if . = do_attack(A, their_T) if(melee_attack_delay) melee_post_animation(A) - // their_T.color = "#FFFFFF" - - // This does the actual attack. // This is a seperate proc for the purposes of attack animations. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm index 2bf3568287..d33e93e042 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -18,7 +18,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? response_help = "pets" response_disarm = "gently pushes aside" response_harm = "kicks" - attacktext = list("kicked") + attacktext = list("pecked") has_langs = list("Bird") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 09421f650b..f7a5cddc6f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -76,7 +76,7 @@ /mob/living/simple_mob/animal/passive/mouse/proc/splat() src.health = 0 - src.stat = DEAD + src.set_stat(DEAD) src.icon_dead = "mouse_[body_color]_splat" src.icon_state = "mouse_[body_color]_splat" layer = MOB_LAYER diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm index 4042dd30b4..7dc23ef7f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -23,7 +23,7 @@ softfall = TRUE parachuting = TRUE - attacktext = list("claws", "pecks") + attacktext = list("clawed", "pecked") speak_emote = list("chirps", "caws") has_langs = list("Bird") response_help = "pets" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm index 7397636686..7ca24f3174 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm @@ -50,3 +50,8 @@ /mob/living/simple_mob/animal/space/bats/cult/cultify() return + +/mob/living/simple_mob/animal/space/bats/cult/strong + maxHealth = 60 + health = 60 + melee_damage_upper = 10 diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm index 9c540c00f6..f796931395 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm @@ -20,7 +20,7 @@ movement_cooldown = 0 hovering = TRUE - attacktext = list("slams into") + attacktext = list("slammed into") attack_sound = 'sound/effects/slime_squish.ogg' say_list_type = /datum/say_list/spore @@ -118,7 +118,7 @@ desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." melee_damage_lower += 8 // 10 total. melee_damage_upper += 11 // 15 total. - attacktext = list("claws") + attacktext = list("clawed") H.forceMove(src) infested = H diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm index a99d073a53..a846b560d6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -84,6 +84,9 @@ damage = 0 nodamage = TRUE + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + // Close to mid-ranged shooter that arcs over other things, ideal if allies are in front of it. // Difference from siege hivebots is that siege hivebots have limited charges for their attacks, are very long range, and \ diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index edf2a299c5..323f6d2bfa 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1016,6 +1016,11 @@ mob/proc/yank_out_object() /mob/proc/updateicon() return +// Please always use this proc, never just set the var directly. +/mob/proc/set_stat(var/new_stat) + . = (stat != new_stat) + stat = new_stat + /mob/verb/face_direction() set name = "Face Direction" diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 90d4e32c8d..d11cc80594 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -119,6 +119,11 @@ icon_state = "hair_long_bedhead" flags = HAIR_TIEABLE + bedheadlongest + name = "Bedhead Longest" + icon_state = "hair_longest_bedhead" + flags = HAIR_TIEABLE + beehive name = "Beehive" icon_state = "hair_beehive" @@ -743,6 +748,11 @@ icon_state = "hair_rowbraid" flags = HAIR_TIEABLE + sabitsuki + name = "Sabitsuki" + icon_state = "hair_sabitsuki" + flags = HAIR_VERY_SHORT + scully name = "Scully" icon_state = "hair_scully" @@ -1294,6 +1304,11 @@ icon_state = "teshari_mushroom" species_allowed = list(SPECIES_TESHARI) + teshari_bald + name = "Bald (use with FBP)" + icon_state = "bald" + species_allowed = list(SPECIES_TESHARI) + // Vox things vox_braid_long name = "Long Vox braid" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index a8c36c52dc..0b957f9832 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -144,7 +144,7 @@ return GLOB.all_languages["Noise"] if(length(message) >= 2 && is_language_prefix(prefix)) - var/language_prefix = lowertext(copytext(message, 2 ,3)) + var/language_prefix = copytext(message, 2 ,3) var/datum/language/L = GLOB.language_keys[language_prefix] if (can_speak(L)) return L diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index d659caaf40..2e4d43c731 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -2,7 +2,7 @@ /obj/machinery/ntnet_relay name = "NTNet Quantum Relay" desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile." - use_power = 2 + use_power = USE_POWER_ACTIVE active_power_usage = 20000 //20kW, apropriate for machine that keeps massive cross-Zlevel wireless network operational. idle_power_usage = 100 icon_state = "bus" @@ -38,9 +38,9 @@ /obj/machinery/ntnet_relay/process() if(operable()) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) else - use_power = 1 + update_use_power(USE_POWER_IDLE) if(dos_overload) dos_overload = max(0, dos_overload - dos_dissipate) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index a4174a7c33..a9f756469c 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -15,6 +15,10 @@ /mob/proc/zMove(direction) if(eyeobj) return eyeobj.zMove(direction) + if(istype(loc,/obj/mecha)) + var/obj/mecha/mech = loc + return mech.relaymove(src,direction) + if(!can_ztravel()) to_chat(src, "You lack means of travel in that direction.") return @@ -115,7 +119,7 @@ if(incapacitated()) return FALSE - if(hovering) + if(hovering || is_incorporeal()) return TRUE if(Process_Spacemove()) diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index d64abba47d..b71c102c60 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -150,8 +150,14 @@ var/turf/below = GetBelow(src) return !below || below.is_space() +/turf/simulated/open/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice (same as space) + /turf/simulated/open/is_safe_to_enter(mob/living/L) if(L.can_fall()) + for(var/obj/O in contents) + if(!O.CanFallThru(L, GetBelow(src))) + return TRUE // Can't fall through this, like lattice or catwalk. if(!locate(/obj/structure/stairs) in GetBelow(src)) - return FALSE + return FALSE // Falling on stairs is safe. return ..() \ No newline at end of file diff --git a/code/modules/organs/internal/augment.dm b/code/modules/organs/internal/augment.dm index d134ad12ca..569b5dda44 100644 --- a/code/modules/organs/internal/augment.dm +++ b/code/modules/organs/internal/augment.dm @@ -12,7 +12,7 @@ organ_verbs = list(/mob/living/carbon/human/proc/augment_menu) // Verbs added by the organ when present in the body. target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? - forgiving_class = FALSE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. + forgiving_class = TRUE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. var/obj/item/integrated_object // Objects held by the organ, used for re-usable, deployable things. var/integrated_object_type // Object type the organ will spawn. diff --git a/code/modules/organs/robolimbs.dm b/code/modules/organs/robolimbs.dm index 04afbf6ec5..1224fae71f 100644 --- a/code/modules/organs/robolimbs.dm +++ b/code/modules/organs/robolimbs.dm @@ -98,6 +98,14 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_unathi.dmi' unavailable_to_build = 1 +/datum/robolimb/unbranded_teshari + company = "Unbranded - Teshari" + species_cannot_use = list(SPECIES_UNATHI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + suggested_species = SPECIES_TESHARI + desc = "A simple robotic limb with a small, raptor-like design. Seems rather stiff." + icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_teshari.dmi' + unavailable_to_build = 0 + /datum/robolimb/nanotrasen company = "NanoTrasen" desc = "A simple but efficient robotic limb, created by NanoTrasen." @@ -122,6 +130,15 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_unathi.dmi' unavailable_to_build = 1 +/datum/robolimb/cenilimicybernetics_teshari + company = "Cenilimi Cybernetics" + species_cannot_use = list(SPECIES_UNATHI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + species_alternates = list(SPECIES_HUMAN = "NanoTrasen") + suggested_species = SPECIES_TESHARI + desc = "Made by a Teshari-owned company, for Teshari." + icon = 'icons/mob/human_races/cyberlimbs/cenilimicybernetics/cenilimicybernetics_teshari.dmi' + unavailable_to_build = 1 + /datum/robolimb/bishop company = "Bishop" desc = "This limb has a white polymer casing with blue holo-displays." @@ -465,3 +482,6 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ /obj/item/weapon/disk/species/zaddat species = SPECIES_ZADDAT + +/obj/item/weapon/disk/limb/cenilimicybernetics + company = "Cenilimi Cybernetics" \ No newline at end of file diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 55e3f2cd5f..fef02ada36 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -14,7 +14,7 @@ ..() // This is very ghetto way of rebooting an IPC. TODO better way. if(owner && owner.stat == DEAD) - owner.stat = 0 + owner.set_stat(CONSCIOUS) owner.visible_message("\The [owner] twitches visibly!") /obj/item/organ/internal/cell/emp_act(severity) @@ -68,7 +68,7 @@ stored_mmi.brainmob.languages = owner.languages if(owner && owner.stat == DEAD) - owner.stat = 0 + owner.set_stat(CONSCIOUS) dead_mob_list -= owner living_mob_list |= owner owner.visible_message("\The [owner] twitches visibly!") diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm index 6d17d8e398..a272ab7591 100644 --- a/code/modules/overmap/_defines.dm +++ b/code/modules/overmap/_defines.dm @@ -3,6 +3,57 @@ //How far from the edge of overmap zlevel could randomly placed objects spawn #define OVERMAP_EDGE 7 + + +//Dimension of overmap (squares 4 lyfe) +var/global/list/map_sectors = list() + +/area/overmap/ + name = "System Map" + icon_state = "start" + requires_power = 0 + base_turf = /turf/unsimulated/map + +/turf/unsimulated/map + icon = 'icons/turf/space.dmi' + icon_state = "map" + +/turf/unsimulated/map/edge + opacity = 1 + density = 1 + +/turf/unsimulated/map/New() + ..() + name = "[x]-[y]" + var/list/numbers = list() + + if(x == 1 || x == global.using_map.overmap_size) + numbers += list("[round(y/10)]","[round(y%10)]") + if(y == 1 || y == global.using_map.overmap_size) + numbers += "-" + if(y == 1 || y == global.using_map.overmap_size) + numbers += list("[round(x/10)]","[round(x%10)]") + + for(var/i = 1 to numbers.len) + var/image/I = image('icons/effects/numbers.dmi',numbers[i]) + I.pixel_x = 5*i - 2 + I.pixel_y = world.icon_size/2 - 3 + if(y == 1) + I.pixel_y = 3 + I.pixel_x = 5*i + 4 + if(y == global.using_map.overmap_size) + I.pixel_y = world.icon_size - 9 + I.pixel_x = 5*i + 4 + if(x == 1) + I.pixel_x = 5*i - 2 + if(x == global.using_map.overmap_size) + I.pixel_x = 5*i + 2 + overlays += I + + + + + //list used to track which zlevels are being 'moved' by the proc below var/list/moving_levels = list() //Proc to 'move' stars in spess @@ -35,6 +86,7 @@ proc/toggle_move_stars(zlevel, direction) AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1) +/* //list used to cache empty zlevels to avoid nedless map bloat var/list/cached_space = list() @@ -99,3 +151,4 @@ proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A) testing("Catching [M] for future use") source.loc = null cached_space += source +*/ \ No newline at end of file diff --git a/code/modules/overmap/overmap_object.dm b/code/modules/overmap/overmap_object.dm new file mode 100644 index 0000000000..9db73eff63 --- /dev/null +++ b/code/modules/overmap/overmap_object.dm @@ -0,0 +1,39 @@ +/obj/effect/overmap + name = "map object" + icon = 'icons/obj/overmap.dmi' + icon_state = "object" + + var/known = 1 //shows up on nav computers automatically + var/scannable //if set to TRUE will show up on ship sensors for detailed scans + +//Overlay of how this object should look on other skyboxes +/obj/effect/overmap/proc/get_skybox_representation() + return + +/obj/effect/overmap/proc/get_scan_data(mob/user) + return desc + +/obj/effect/overmap/Initialize() + . = ..() + if(!global.using_map.use_overmap) + return INITIALIZE_HINT_QDEL + + if(known) + //layer = ABOVE_LIGHTING_LAYER + plane = PLANE_LIGHTING_ABOVE + // TODO - Leshana HELM + // for(var/obj/machinery/computer/ship/helm/H in global.machines) + // H.get_known_sectors() +/* +TODO - Leshana - No need for this, we don't have skyboxes +/obj/effect/overmap/Crossed(var/obj/effect/overmap/visitable/other) + if(istype(other)) + for(var/obj/effect/overmap/visitable/O in loc) + SSskybox.rebuild_skyboxes(O.map_z) + +/obj/effect/overmap/Uncrossed(var/obj/effect/overmap/visitable/other) + if(istype(other)) + SSskybox.rebuild_skyboxes(other.map_z) + for(var/obj/effect/overmap/visitable/O in loc) + SSskybox.rebuild_skyboxes(O.map_z) +*/ \ No newline at end of file diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 5309b05096..15393a5be1 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -1,124 +1,136 @@ - //=================================================================================== -//Hook for building overmap +//Overmap object representing zlevel(s) //=================================================================================== -var/global/list/map_sectors = list() - -/hook/startup/proc/build_map() - if(!config.use_overmap) - return 1 - testing("Building overmap...") - var/obj/effect/mapinfo/data - for(var/level in 1 to world.maxz) - data = locate("sector[level]") - if (data) - testing("Located sector \"[data.name]\" at [data.mapx],[data.mapy] corresponding to zlevel [level]") - map_sectors["[level]"] = new data.obj_type(data) - return 1 - -//=================================================================================== -//Metaobject for storing information about sector this zlevel is representing. -//Should be placed only once on every zlevel. -//=================================================================================== -/obj/effect/mapinfo/ - name = "map info metaobject" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - invisibility = 101 - var/obj_type //type of overmap object it spawns - var/landing_area //type of area used as inbound shuttle landing, null if no shuttle landing area - var/zlevel - var/mapx //coordinates on the - var/mapy //overmap zlevel - var/known = 1 - -/obj/effect/mapinfo/New() - tag = "sector[z]" - zlevel = z - loc = null - -/obj/effect/mapinfo/sector - name = "generic sector" - obj_type = /obj/effect/map/sector - -/obj/effect/mapinfo/ship - name = "generic ship" - obj_type = /obj/effect/map/ship - - -//=================================================================================== -//Overmap object representing zlevel -//=================================================================================== - -/obj/effect/map +/obj/effect/overmap/visitable name = "map object" - icon = 'icons/obj/items.dmi' - icon_state = "sheet-plasteel" - var/map_z = 0 - var/area/shuttle/shuttle_landing - var/always_known = 1 + scannable = TRUE -/obj/effect/map/New(var/obj/effect/mapinfo/data) - map_z = data.zlevel - name = data.name - always_known = data.known - if (data.icon != 'icons/mob/screen1.dmi') - icon = data.icon - icon_state = data.icon_state - if(data.desc) - desc = data.desc - var/new_x = data.mapx ? data.mapx : rand(OVERMAP_EDGE, world.maxx - OVERMAP_EDGE) - var/new_y = data.mapy ? data.mapy : rand(OVERMAP_EDGE, world.maxy - OVERMAP_EDGE) - loc = locate(new_x, new_y, OVERMAP_ZLEVEL) + var/list/map_z = list() - if(data.landing_area) - shuttle_landing = locate(data.landing_area) + var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init. + var/list/initial_restricted_waypoints //For use with non-automatic landmarks (automatic ones add themselves). -/obj/effect/map/CanPass(atom/movable/A) - testing("[A] attempts to enter sector\"[name]\"") - return 1 + var/list/generic_waypoints = list() //waypoints that any shuttle can use + var/list/restricted_waypoints = list() //waypoints for specific shuttles + var/docking_codes -/obj/effect/map/Crossed(atom/movable/A) - testing("[A] has entered sector\"[name]\"") - if (istype(A,/obj/effect/map/ship)) - var/obj/effect/map/ship/S = A - S.current_sector = src + var/start_x //Coordinates for self placing + var/start_y //will use random values if unset -/obj/effect/map/Uncrossed(atom/movable/A) - testing("[A] has left sector\"[name]\"") - if (istype(A,/obj/effect/map/ship)) - var/obj/effect/map/ship/S = A - S.current_sector = null + var/base = 0 //starting sector, counts as station_levels + var/in_space = 1 //can be accessed via lucky EVA -/obj/effect/map/sector + var/hide_from_reports = FALSE + + var/has_distress_beacon + +/obj/effect/overmap/visitable/Initialize() + . = ..() + if(. == INITIALIZE_HINT_QDEL) + return + + find_z_levels() // This populates map_z and assigns z levels to the ship. + register_z_levels() // This makes external calls to update global z level information. + + if(!global.using_map.overmap_z) + build_overmap() + + start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE) + start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE) + + forceMove(locate(start_x, start_y, global.using_map.overmap_z)) + + docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]" + + testing("Located sector \"[name]\" at [start_x],[start_y], containing Z [english_list(map_z)]") + + LAZYADD(SSshuttles.sectors_to_initialize, src) //Queued for further init. Will populate the waypoint lists; waypoints not spawned yet will be added in as they spawn. + SSshuttles.process_init_queues() + +//This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then. +/obj/effect/overmap/visitable/proc/populate_sector_objects() + +// TODO - Leshana - Implement +///obj/effect/overmap/visitable/proc/get_areas() +// return get_filtered_areas(list(/proc/area_belongs_to_zlevels = map_z)) + +/obj/effect/overmap/visitable/proc/find_z_levels() + map_z = GetConnectedZlevels(z) + +/obj/effect/overmap/visitable/proc/register_z_levels() + for(var/zlevel in map_z) + map_sectors["[zlevel]"] = src + + global.using_map.player_levels |= map_z + if(!in_space) + global.using_map.sealed_levels |= map_z + if(base) + global.using_map.station_levels |= map_z + global.using_map.contact_levels |= map_z + global.using_map.map_levels |= map_z + +//Helper for init. +/obj/effect/overmap/visitable/proc/check_ownership(obj/object) + if((object.z in map_z) && !(get_area(object) in SSshuttles.shuttle_areas)) + return 1 + +//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks. +/obj/effect/overmap/visitable/proc/add_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + landmark.sector_set(src, shuttle_name) + if(shuttle_name) + LAZYADD(restricted_waypoints[shuttle_name], landmark) + else + generic_waypoints += landmark + +/obj/effect/overmap/visitable/proc/remove_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + if(shuttle_name) + var/list/shuttles = restricted_waypoints[shuttle_name] + LAZYREMOVE(shuttles, landmark) + else + generic_waypoints -= landmark + +/obj/effect/overmap/visitable/proc/get_waypoints(var/shuttle_name) + . = list() + for(var/obj/effect/overmap/visitable/contained in src) + . += contained.get_waypoints(shuttle_name) + for(var/thing in generic_waypoints) + .[thing] = name + if(shuttle_name in restricted_waypoints) + for(var/thing in restricted_waypoints[shuttle_name]) + .[thing] = name + +/obj/effect/overmap/visitable/proc/generate_skybox() + return + +/obj/effect/overmap/visitable/sector name = "generic sector" desc = "Sector with some stuff in it." + icon_state = "sector" anchored = 1 -//Space stragglers go here +// Because of the way these are spawned, they will potentially have their invisibility adjusted by the turfs they are mapped on +// prior to being moved to the overmap. This blocks that. Use set_invisibility to adjust invisibility as needed instead. +/obj/effect/overmap/visitable/sector/hide() -/obj/effect/map/sector/temporary - name = "Deep Space" - icon_state = "" - always_known = 0 +/proc/build_overmap() + if(!global.using_map.use_overmap) + return 1 -/obj/effect/map/sector/temporary/New(var/nx, var/ny, var/nz) - loc = locate(nx, ny, OVERMAP_ZLEVEL) - map_z = nz - map_sectors["[map_z]"] = src - testing("Temporary sector at [x],[y] was created, corresponding zlevel is [map_z].") + testing("Building overmap...") + world.maxz++ + global.using_map.overmap_z = world.maxz -/obj/effect/map/sector/temporary/Destroy() - map_sectors["[map_z]"] = null - testing("Temporary sector at [x],[y] was deleted.") - if (can_die()) - testing("Associated zlevel disappeared.") - world.maxz-- + testing("Putting overmap on [global.using_map.overmap_z]") + var/area/overmap/A = new + for (var/square in block(locate(1,1,global.using_map.overmap_z), locate(global.using_map.overmap_size,global.using_map.overmap_size,global.using_map.overmap_z))) + var/turf/T = square + if(T.x == global.using_map.overmap_size || T.y == global.using_map.overmap_size) + T = T.ChangeTurf(/turf/unsimulated/map/edge) + else + T = T.ChangeTurf(/turf/unsimulated/map) + ChangeArea(T, A) -/obj/effect/map/sector/temporary/proc/can_die(var/mob/observer) - testing("Checking if sector at [map_z] can die.") - for(var/mob/M in player_list) - if(M != observer && M.z == map_z) - testing("There are people on it.") - return 0 + global.using_map.sealed_levels |= global.using_map.overmap_z + + testing("Overmap build complete.") return 1 diff --git a/code/modules/overmap/spacetravel.dm b/code/modules/overmap/spacetravel.dm new file mode 100644 index 0000000000..22de00d612 --- /dev/null +++ b/code/modules/overmap/spacetravel.dm @@ -0,0 +1,114 @@ +//list used to cache empty zlevels to avoid nedless map bloat +var/list/cached_space = list() + +//Space stragglers go here + +/obj/effect/overmap/visitable/sector/temporary + name = "Deep Space" + invisibility = 101 + known = 0 + +/obj/effect/overmap/visitable/sector/temporary/New(var/nx, var/ny, var/nz) + loc = locate(nx, ny, global.using_map.overmap_z) + x = nx + y = ny + map_z += nz + map_sectors["[nz]"] = src + testing("Temporary sector at [x],[y] was created, corresponding zlevel is [nz].") + +/obj/effect/overmap/visitable/sector/temporary/Destroy() + map_sectors["[map_z]"] = null + testing("Temporary sector at [x],[y] was deleted.") + +/obj/effect/overmap/visitable/sector/temporary/proc/can_die(var/mob/observer) + testing("Checking if sector at [map_z[1]] can die.") + for(var/mob/M in global.player_list) + if(M != observer && (M.z in map_z)) + testing("There are people on it.") + return 0 + return 1 + +proc/get_deepspace(x,y) + var/obj/effect/overmap/visitable/sector/temporary/res = locate(x,y,global.using_map.overmap_z) + if(istype(res)) + return res + else if(cached_space.len) + res = cached_space[cached_space.len] + cached_space -= res + res.x = x + res.y = y + return res + else + return new /obj/effect/overmap/visitable/sector/temporary(x, y, global.using_map.get_empty_zlevel()) + +/atom/movable/proc/lost_in_space() + for(var/atom/movable/AM in contents) + if(!AM.lost_in_space()) + return FALSE + return TRUE + +/mob/lost_in_space() + return isnull(client) + +/mob/living/carbon/human/lost_in_space() + return isnull(client) && !key && stat == DEAD + +proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A) + if (!T || !A) + return + + var/obj/effect/overmap/visitable/M = map_sectors["[T.z]"] + if (!M) + return + + if(A.lost_in_space()) + if(!QDELETED(A)) + qdel(A) + return + + var/nx = 1 + var/ny = 1 + var/nz = 1 + + if(T.x <= TRANSITIONEDGE) + nx = world.maxx - TRANSITIONEDGE - 2 + ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (A.x >= (world.maxx - TRANSITIONEDGE - 1)) + nx = TRANSITIONEDGE + 2 + ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (T.y <= TRANSITIONEDGE) + ny = world.maxy - TRANSITIONEDGE -2 + nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (A.y >= (world.maxy - TRANSITIONEDGE - 1)) + ny = TRANSITIONEDGE + 2 + nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + testing("[A] spacemoving from [M] ([M.x], [M.y]).") + + var/turf/map = locate(M.x,M.y,global.using_map.overmap_z) + var/obj/effect/overmap/visitable/TM + for(var/obj/effect/overmap/visitable/O in map) + if(O != M && O.in_space && prob(50)) + TM = O + break + if(!TM) + TM = get_deepspace(M.x,M.y) + nz = pick(TM.map_z) + + var/turf/dest = locate(nx,ny,nz) + if(dest) + A.forceMove(dest) + if(ismob(A)) + var/mob/D = A + if(D.pulling) + D.pulling.forceMove(dest) + + if(istype(M, /obj/effect/overmap/visitable/sector/temporary)) + var/obj/effect/overmap/visitable/sector/temporary/source = M + if (source.can_die()) + testing("Caching [M] for future use") + source.forceMove(null) + cached_space += source diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 1288f29aee..3c18577db3 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -12,7 +12,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins insert_anim = "faxsend" req_one_access = list(access_lawyer, access_heads, access_armory, access_qm) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 200 circuit = /obj/item/weapon/circuitboard/fax diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm index d626e6f04b..4c927b376e 100644 --- a/code/modules/paperwork/papershredder.dm +++ b/code/modules/paperwork/papershredder.dm @@ -9,7 +9,7 @@ var/shred_anim = "shredder-shredding" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 200 power_channel = EQUIP diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 088e36c31b..dbfd7855ff 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -48,6 +48,11 @@ var/selectedColor = 1 var/colors = list("black","blue","red") +/obj/item/weapon/pen/AltClick(mob/user) + to_chat(user, "Click.") + playsound(loc, 'sound/items/penclick.ogg', 50, 1) + return + /obj/item/weapon/pen/multi/attack_self(mob/user) if(++selectedColor > 3) selectedColor = 1 @@ -92,6 +97,98 @@ var/trans = reagents.trans_to_mob(M, 30, CHEM_BLOOD) add_attack_logs(user,M,"Injected with [src.name] containing [contained], trasferred [trans] units") +/* + * Blade pens. + */ + +/obj/item/weapon/pen/blade + desc = "It's a normal black ink pen." + description_antag = "This pen can be transformed into a dangerous melee and thrown assassination weapon with an Alt-Click.\ + When active, it cannot be caught safely." + name = "pen" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "pen" + item_state = "pen" + slot_flags = SLOT_BELT | SLOT_EARS + throwforce = 3 + w_class = ITEMSIZE_TINY + throw_speed = 7 + throw_range = 15 + armor_penetration = 20 + + var/active = 0 + var/active_embed_chance = 0 + var/active_force = 15 + var/active_throwforce = 30 + var/active_w_class = ITEMSIZE_NORMAL + var/active_icon_state + var/default_icon_state + +/obj/item/weapon/pen/blade/Initialize() + ..() + active_icon_state = "[icon_state]-x" + default_icon_state = icon_state + +/obj/item/weapon/pen/blade/AltClick(mob/user) + ..() + if(active) + deactivate(user) + else + activate(user) + + to_chat(user, "You [active ? "de" : ""]activate \the [src]'s blade.") + +/obj/item/weapon/pen/blade/proc/activate(mob/living/user) + if(active) + return + active = 1 + icon_state = active_icon_state + embed_chance = active_embed_chance + force = active_force + throwforce = active_throwforce + sharp = 1 + edge = 1 + w_class = active_w_class + playsound(user, 'sound/weapons/saberon.ogg', 15, 1) + damtype = SEARING + catchable = FALSE + + attack_verb |= list(\ + "slashed",\ + "cut",\ + "shredded",\ + "stabbed"\ + ) + +/obj/item/weapon/pen/blade/proc/deactivate(mob/living/user) + if(!active) + return + playsound(user, 'sound/weapons/saberoff.ogg', 15, 1) + active = 0 + icon_state = default_icon_state + embed_chance = initial(embed_chance) + force = initial(force) + throwforce = initial(throwforce) + sharp = initial(sharp) + edge = initial(edge) + w_class = initial(w_class) + damtype = BRUTE + catchable = TRUE + +/obj/item/weapon/pen/blade/blue + desc = "It's a normal blue ink pen." + icon_state = "pen_blue" + colour = "blue" + +/obj/item/weapon/pen/blade/red + desc = "It's a normal red ink pen." + icon_state = "pen_red" + colour = "red" + +/obj/item/weapon/pen/blade/fountain + desc = "A well made fountain pen." + icon_state = "pen_fountain" + /* * Sleepy Pens */ diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index d5e04d97a4..eab9984d0f 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -6,7 +6,7 @@ var/insert_anim = "bigscanner1" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 200 power_channel = EQUIP diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm index 260730391c..46f05bfd80 100644 --- a/code/modules/power/antimatter/control.dm +++ b/code/modules/power/antimatter/control.dm @@ -5,7 +5,7 @@ icon_state = "control" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 active_power_usage = 1000 @@ -211,10 +211,10 @@ /obj/machinery/power/am_control_unit/proc/toggle_power() active = !active if(active) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) visible_message("The [src.name] starts up.") else - use_power = 1 + update_use_power(USE_POWER_IDLE) visible_message("The [src.name] shuts down.") update_icon() return diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm index a3549b7a2f..2b7b881b09 100644 --- a/code/modules/power/antimatter/shielding.dm +++ b/code/modules/power/antimatter/shielding.dm @@ -16,7 +16,7 @@ proc/cardinalrange(var/center) anchored = 1 density = 1 dir = 1 - use_power = 0//Living things generally dont use power + use_power = USE_POWER_OFF //Living things generally dont use power idle_power_usage = 0 active_power_usage = 0 diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index a0b53a57e0..149a8a2b4f 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -67,7 +67,7 @@ plane = TURF_PLANE layer = ABOVE_TURF_LAYER anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF clicksound = "switch" req_access = list(access_engine_equip) var/area/area @@ -105,6 +105,7 @@ var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment var/longtermpower = 10 var/datum/wires/apc/wires = null + var/emergency_lights = FALSE var/update_state = -1 var/update_overlay = -1 var/is_critical = 0 @@ -795,6 +796,7 @@ "gridCheck" = grid_check, "coverLocked" = coverlocked, "siliconUser" = issilicon(user) || isobserver(user), //I add observer here so admins can have more control, even if it makes 'siliconUser' seem inaccurate. + "emergencyLights" = !emergency_lights, "powerChannels" = list( list( @@ -835,7 +837,7 @@ if (!ui) // the ui does not exist, so we'll create a new() one // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 465 : 440) + ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 490 : 465) // when the ui is first opened this is the data it will use ui.set_initial_data(data) // open the new ui window @@ -932,6 +934,14 @@ update_icon() update() + else if (href_list["emergency_lighting"]) + emergency_lights = !emergency_lights + for(var/obj/machinery/light/L in area) + if(!initial(L.no_emergency)) //If there was an override set on creation, keep that override + L.no_emergency = emergency_lights + INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE) + CHECK_TICK + else if (href_list["breaker"]) toggle_breaker() diff --git a/code/modules/power/cells/power_cells.dm b/code/modules/power/cells/power_cells.dm index 3afb0d5450..3638c625f1 100644 --- a/code/modules/power/cells/power_cells.dm +++ b/code/modules/power/cells/power_cells.dm @@ -132,3 +132,16 @@ overlays.Cut() target.nutrition += amount user.custom_emote(message = "connects \the [src] to [user == target ? "their" : "[target]'s"] charging port, expending it.") + +/obj/item/weapon/cell/emergency_light + name = "miniature power cell" + desc = "A tiny power cell with a very low power capacity. Used in light fixtures to power them in the event of an outage." + maxcharge = 120 //Emergency lights use 0.2 W per tick, meaning ~10 minutes of emergency power from a cell + matter = list("glass" = 20) + w_class = ITEMSIZE_TINY + +/obj/item/weapon/cell/emergency_light/Initialize() + . = ..() + var/area/A = get_area(src) + if(!A.lightswitch || !A.light_power) + charge = 0 //For naturally depowered areas, we start with no power \ No newline at end of file diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index 99b0346e62..9cc907e052 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -13,7 +13,7 @@ var/list/fusion_cores = list() icon = 'icons/obj/machines/power/fusion.dmi' icon_state = "core0" density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 active_power_usage = 500 //multiplied by field strength anchored = 0 @@ -70,7 +70,7 @@ var/list/fusion_cores = list() owned_field = new(loc, src) owned_field.ChangeFieldStrength(field_strength) icon_state = "core1" - use_power = 2 + update_use_power(USE_POWER_ACTIVE) . = 1 /obj/machinery/power/fusion_core/proc/Shutdown(var/force_rupture) @@ -82,7 +82,7 @@ var/list/fusion_cores = list() owned_field.RadiateAll() qdel(owned_field) owned_field = null - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/power/fusion_core/proc/AddParticles(var/name, var/quantity = 1) if(owned_field) diff --git a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm index 80a512b91b..0ffab7db07 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm @@ -7,7 +7,7 @@ var/list/fuel_injectors = list() density = 1 anchored = 0 req_access = list(access_engine) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 500 @@ -103,13 +103,13 @@ var/list/fuel_injectors = list() if(!injecting && cur_assembly) icon_state = "injector1" injecting = 1 - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/fusion_fuel_injector/proc/StopInjecting() if(injecting) injecting = 0 icon_state = "injector0" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/fusion_fuel_injector/proc/Inject() if(!injecting) diff --git a/code/modules/power/fusion/gyrotron/gyrotron.dm b/code/modules/power/fusion/gyrotron/gyrotron.dm index 539e9cbda6..bdbdf9a3df 100644 --- a/code/modules/power/fusion/gyrotron/gyrotron.dm +++ b/code/modules/power/fusion/gyrotron/gyrotron.dm @@ -6,7 +6,7 @@ var/list/gyrotrons = list() desc = "It is a heavy duty industrial gyrotron suited for powering fusion reactors." icon_state = "emitter-off" req_access = list(access_engine) - use_power = 1 + use_power = USE_POWER_IDLE active_power_usage = 50000 circuit = /obj/item/weapon/circuitboard/gyrotron diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 3e9f6b3797..9e1b50115d 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -7,7 +7,7 @@ GLOBAL_LIST_EMPTY(all_turbines) density = 1 anchored = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 //Watts, I hope. Just enough to do the computer and display things. var/max_power = 500000 @@ -160,7 +160,7 @@ GLOBAL_LIST_EMPTY(all_turbines) user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ "You hear a ratchet.") - use_power = anchored + update_use_power(anchored ? USE_POWER_IDLE : USE_POWER_ACTIVE) if(anchored) // Powernet connection stuff. connect_to_network() else diff --git a/code/modules/power/generator_type2.dm b/code/modules/power/generator_type2.dm index cc1e855a91..1271287796 100644 --- a/code/modules/power/generator_type2.dm +++ b/code/modules/power/generator_type2.dm @@ -4,7 +4,7 @@ icon_state = "teg" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/obj/machinery/atmospherics/unary/generator_input/input1 var/obj/machinery/atmospherics/unary/generator_input/input2 diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 3d198c18b3..ad8af4352c 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -16,7 +16,7 @@ icon_state = "TheSingGen" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 200 active_power_usage = 1000 var/on = 1 @@ -127,13 +127,13 @@ if((A in G.localareas) && (G.on)) break if(!G) - A.gravitychange(0,A) + A.gravitychange(0) else for(var/area/A in gravity_generator:localareas) gravity_generator:on = 1 - A.gravitychange(1,A) + A.gravitychange(1) src.updateUsrDialog() return diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 6d01a2e445..2e9072bbd5 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -10,6 +10,7 @@ #define LIGHT_BURNED 3 #define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb #define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range +#define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick var/global/list/light_type_cache = list() /proc/get_light_type_instance(var/light_type) @@ -29,6 +30,10 @@ var/global/list/light_type_cache = list() var/stage = 1 var/fixture_type = /obj/machinery/light var/sheets_refunded = 2 + var/obj/machinery/light/newlight = null + var/obj/item/weapon/cell/cell = null + + var/cell_connectors = TRUE /obj/machinery/light_construct/New(var/atom/newloc, var/newdir, var/building = 0, var/datum/frame/frame_types/frame_type, var/obj/machinery/light/fixture = null) ..(newloc) @@ -61,9 +66,44 @@ var/global/list/light_type_cache = list() to_chat(user, "It's wired.") if(3) to_chat(user, "The casing is closed.") + if(cell_connectors) + if(cell) + to_chat(user, "You see [cell] inside the casing.") + else + to_chat(user, "The casing has no power cell for backup power.") + else + to_chat(user, "This casing doesn't support power cells for backup power.") + +/obj/machinery/light_construct/attack_hand(mob/user) + . = ..() + if(.) + return . // obj/machinery/attack_hand returns 1 if user can't use the machine + if(cell) + user.visible_message("[user] removes [cell] from [src]!","You remove [cell].") + user.put_in_hands(cell) + cell.update_icon() + cell = null /obj/machinery/light_construct/attackby(obj/item/weapon/W as obj, mob/user as mob) src.add_fingerprint(user) + if(istype(W, /obj/item/weapon/cell/emergency_light)) + if(!cell_connectors) + to_chat(user, "This [name] can't support a power cell!") + return + if(!user.unEquip(W)) + to_chat(user, "[W] is stuck to your hand!") + return + if(cell) + to_chat(user, "There is a power cell already installed!") + else if(user.drop_from_inventory(W)) + user.visible_message("[user] hooks up [W] to [src].", \ + "You add [W] to [src].") + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + W.forceMove(src) + cell = W + add_fingerprint(user) + return + if (W.is_wrench()) if (src.stage == 1) playsound(src, W.usesound, 75, 1) @@ -114,6 +154,10 @@ var/global/list/light_type_cache = list() var/obj/machinery/light/newlight = new fixture_type(src.loc, src) newlight.set_dir(src.dir) src.transfer_fingerprints_to(newlight) + if(cell) + newlight.cell = cell + cell.forceMove(newlight) + cell = null qdel(src) return ..() @@ -168,7 +212,7 @@ var/global/list/light_type_cache = list() anchored = 1 plane = MOB_PLANE layer = ABOVE_MOB_LAYER - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 2 active_power_usage = 10 // Previously 20. power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list @@ -187,6 +231,16 @@ var/global/list/light_type_cache = list() var/auto_flicker = FALSE // If true, will constantly flicker, so long as someone is around to see it (otherwise its a waste of CPU). + var/obj/item/weapon/cell/emergency_light/cell + var/start_with_cell = TRUE // if true, this fixture generates a very weak cell at roundstart + + var/emergency_mode = FALSE // if true, the light is in emergency mode + var/no_emergency = FALSE // if true, this light cannot ever have an emergency mode + var/bulb_emergency_brightness_mul = 0.25 // multiplier for this light's base brightness in emergency power mode + var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode + var/bulb_emergency_pow_mul = 0.75 // the multiplier for determining the light's power in emergency mode + var/bulb_emergency_pow_min = 0.5 // the minimum value for the light's power in emergency mode + /obj/machinery/light/flicker auto_flicker = TRUE @@ -202,6 +256,12 @@ var/global/list/light_type_cache = list() /obj/machinery/light/small/flicker auto_flicker = TRUE +/obj/machinery/light/poi + start_with_cell = FALSE + +/obj/machinery/light/small/poi + start_with_cell = FALSE + /obj/machinery/light/flamp icon_state = "flamp1" base_state = "flamp" @@ -214,10 +274,14 @@ var/global/list/light_type_cache = list() /obj/machinery/light/flamp/New(atom/newloc, obj/machinery/light_construct/construct = null) ..(newloc, construct) - if(construct) + start_with_cell = FALSE lamp_shade = 0 update_icon() + else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) + /obj/machinery/light/flamp/flicker auto_flicker = TRUE @@ -242,11 +306,14 @@ var/global/list/light_type_cache = list() ..(newloc) if(construct) + start_with_cell = FALSE status = LIGHT_EMPTY construct_type = construct.type construct.transfer_fingerprints_to(src) set_dir(construct.dir) else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) var/obj/item/weapon/light/L = get_light_type_instance(light_type) update_from_bulb(L) if(prob(L.broken_chance)) @@ -260,6 +327,7 @@ var/global/list/light_type_cache = list() if(A) on = 0 // A.update_lights() + QDEL_NULL(cell) return ..() /obj/machinery/light/update_icon() @@ -320,10 +388,14 @@ var/global/list/light_type_cache = list() on = 0 set_light(0) else - use_power = 2 + update_use_power(USE_POWER_ACTIVE) set_light(brightness_range, brightness_power, brightness_color) - else + else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) use_power = 1 + emergency_mode = TRUE + START_PROCESSING(SSobj, src) + else + update_use_power(USE_POWER_IDLE) set_light(0) active_power_usage = ((light_range * light_power) * LIGHTING_POWER_FACTOR) @@ -361,6 +433,9 @@ var/global/list/light_type_cache = list() on = (s && status == LIGHT_OK) update() +/obj/machinery/light/get_cell() + return cell + // examine verb /obj/machinery/light/examine(mob/user) var/fitting = get_fitting_name() @@ -373,6 +448,8 @@ var/global/list/light_type_cache = list() to_chat(user, "[desc] The [fitting] is burnt out.") if(LIGHT_BROKEN) to_chat(user, "[desc] The [fitting] has been smashed.") + if(cell) + to_chat(user, "Its backup power charge meter reads [round((cell.charge / cell.maxcharge) * 100, 0.1)]%.") /obj/machinery/light/proc/get_fitting_name() var/obj/item/weapon/light/L = light_type @@ -498,6 +575,12 @@ var/global/list/light_type_cache = list() ..() +// returns if the light has power /but/ is manually turned off +// if a light is turned off, it won't activate emergency power +/obj/machinery/light/proc/turned_off() + var/area/A = get_area(src) + return !A.lightswitch && A.power_light || flickering + // returns whether this light has power // true if area has power and lightswitch is on /obj/machinery/light/proc/has_power() @@ -511,6 +594,28 @@ var/global/list/light_type_cache = list() else return A && A.lightswitch && (!A.requires_power || A.power_light) +// returns whether this light has emergency power +// can also return if it has access to a certain amount of that power +/obj/machinery/light/proc/has_emergency_power(pwr) + if(no_emergency || !cell) + return FALSE + if(pwr ? cell.charge >= pwr : cell.charge) + return status == LIGHT_OK + +// attempts to use power from the installed emergency cell, returns true if it does and false if it doesn't +/obj/machinery/light/proc/use_emergency_power(pwr = LIGHT_EMERGENCY_POWER_USE) + if(turned_off()) + return FALSE + if(!has_emergency_power(pwr)) + return FALSE + if(cell.charge > 300) //it's meant to handle 120 W, ya doofus + visible_message("[src] short-circuits from too powerful of a power cell!") + status = LIGHT_BURNED + return FALSE + cell.use(pwr) + set_light(brightness_range * bulb_emergency_brightness_mul, max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)), bulb_emergency_colour) + return TRUE + /obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) if(flickering) return flickering = 1 @@ -527,12 +632,17 @@ var/global/list/light_type_cache = list() update(0) flickering = 0 -// ai attack - make lights flicker, because why not - +// ai attack - turn on/off emergency lighting for a specific fixture /obj/machinery/light/attack_ai(mob/user) - src.flicker(1) + no_emergency = !no_emergency + to_chat(user, "Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"].") + update(FALSE) return +// ai alt click - Make light flicker. Very important for atmosphere. +/obj/machinery/light/AIAltClick(mob/user) + flicker(1) + /obj/machinery/light/flamp/attack_ai(mob/user) attack_hand() return @@ -655,6 +765,17 @@ var/global/list/light_type_cache = list() // use power /obj/machinery/light/process() + if(!cell) + return PROCESS_KILL + if(has_power()) + emergency_mode = FALSE + update(FALSE) + if(cell.charge == cell.maxcharge) + return PROCESS_KILL + cell.charge = min(cell.maxcharge, cell.charge + LIGHT_EMERGENCY_POWER_USE*2) //Recharge emergency power automatically while not using it + if(emergency_mode && !use_emergency_power(LIGHT_EMERGENCY_POWER_USE)) + update(FALSE) //Disables emergency mode and sets the color to normal + if(auto_flicker && !flickering) if(check_for_player_proximity(src, radius = 12, ignore_ghosts = FALSE, ignore_afk = TRUE)) seton(TRUE) // Lights must be on to flicker. diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 2443f3ea27..4515b6125c 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -6,7 +6,7 @@ icon_state = "portgen0" density = 1 anchored = 0 - use_power = 0 + use_power = USE_POWER_OFF var/active = 0 var/power_gen = 5000 diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 4fce8ba158..a88d0dafdc 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -11,7 +11,7 @@ icon = 'icons/obj/power.dmi' anchored = 1.0 var/datum/powernet/powernet = null - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 diff --git a/code/modules/power/sensors/sensor_monitoring.dm b/code/modules/power/sensors/sensor_monitoring.dm index 21aecc59ea..a7cc81c55a 100644 --- a/code/modules/power/sensors/sensor_monitoring.dm +++ b/code/modules/power/sensors/sensor_monitoring.dm @@ -15,7 +15,7 @@ anchored = 1.0 circuit = /obj/item/weapon/circuitboard/powermonitor var/alerting = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 active_power_usage = 300 var/datum/nano_module/power_monitor/power_monitor diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index d8712a0e4c..6478ff999a 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -8,7 +8,7 @@ anchored = 1 density = 0 unacidable = 1 - use_power = 0 + use_power = USE_POWER_OFF light_range = 4 flags = PROXMOVE var/obj/machinery/field_generator/FG1 = null diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index f847813923..2e6cda0049 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -10,7 +10,7 @@ req_access = list(access_engine_equip) var/id = null - use_power = 0 //uses powernet power, not APC power + use_power = USE_POWER_OFF //uses powernet power, not APC power active_power_usage = 30000 //30 kW laser. I guess that means 30 kJ per shot. var/active = 0 diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 0767084974..b02c835785 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -20,7 +20,7 @@ field_generator power level display icon_state = "Field_Gen" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/const/num_power_levels = 6 // Total number of power level icon has var/Varedit_start = 0 var/Varpower = 0 diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index c49c605598..bb470811ba 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -6,7 +6,7 @@ icon_state = "TheSingGen" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/energy = 0 var/creation_type = /obj/singularity diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 7460830f21..f5481afd4c 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -258,7 +258,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin icon_state = "none" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 var/construction_state = 0 @@ -383,10 +383,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin if(src.construction_state < 3)//Was taken apart, update state update_state() if(use_power) - use_power = 0 + update_use_power(USE_POWER_OFF) src.construction_state = temp_state if(src.construction_state >= 3) - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() return 1 return 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index bcdd0d5036..8cb370924b 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -8,7 +8,7 @@ reference = "control_box" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 500 active_power_usage = 70000 //70 kW per unit of strength construction_state = 0 @@ -42,7 +42,7 @@ /obj/machinery/particle_accelerator/control_box/update_state() if(construction_state < 3) - update_use_power(0) + update_use_power(USE_POWER_OFF) assembled = 0 active = 0 for(var/obj/structure/particle_accelerator/part in connected_parts) @@ -52,7 +52,7 @@ connected_parts = list() return if(!part_scan()) - update_use_power(1) + update_use_power(USE_POWER_IDLE) active = 0 connected_parts = list() @@ -138,9 +138,9 @@ ..() if(stat & NOPOWER) active = 0 - update_use_power(0) + update_use_power(USE_POWER_OFF) else if(!stat && construction_state == 3) - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/particle_accelerator/control_box/process() @@ -212,13 +212,13 @@ message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [key_name(usr, usr.client)](?) in ([x],[y],[z] - JMP)",0,1) log_game("PACCEL([x],[y],[z]) [key_name(usr)] turned [active?"ON":"OFF"].") if(active) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = src.strength part.powered = 1 part.update_icon() else - update_use_power(1) + update_use_power(USE_POWER_IDLE) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = null part.powered = 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index 921b000078..5a4176ee89 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -9,7 +9,7 @@ icon_state = "smasher" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/successful_craft = FALSE // Are we waiting to be emptied? var/image/material_layer // Holds the image used for the filled overlay. diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 31c7be32ec..783a6691b4 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -11,7 +11,7 @@ icon_state = "smes" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF circuit = /obj/item/weapon/circuitboard/smes clicksound = "switch" diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index c37a625552..65e618613c 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -13,7 +13,7 @@ GLOBAL_LIST_EMPTY(solars_list) icon_state = "sp_base" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 var/id = 0 @@ -285,7 +285,7 @@ GLOBAL_LIST_EMPTY(solars_list) icon_state = "solar" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 var/id = 0 var/cdir = 0 diff --git a/code/modules/power/supermatter/setup_supermatter.dm b/code/modules/power/supermatter/setup_supermatter.dm index 7477623805..c0b63d9ce1 100644 --- a/code/modules/power/supermatter/setup_supermatter.dm +++ b/code/modules/power/supermatter/setup_supermatter.dm @@ -126,7 +126,7 @@ GLOBAL_LIST_BOILERPLATE(all_engine_setup_markers, /obj/effect/engine_setup) log_and_message_admins("## WARNING: Unable to locate pump at [x] [y] [z]!") return SETUP_WARNING P.target_pressure = P.max_pressure_setting - P.use_power = 1 + P.update_use_power(USE_POWER_IDLE) P.update_icon() return SETUP_OK @@ -259,7 +259,7 @@ GLOBAL_LIST_BOILERPLATE(all_engine_setup_markers, /obj/effect/engine_setup) return SETUP_WARNING F.rebuild_filtering_list() - F.use_power = 1 + F.update_use_power(USE_POWER_IDLE) F.update_icon() return SETUP_OK diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index a6eab0dbe0..673b2ece86 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,7 +10,7 @@ icon_state = "tracker" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/id = 0 var/sun_angle = 0 // sun angle as set by sun datum diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 133478fb85..a3904bf4f6 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -359,6 +359,9 @@ handle_click_empty(user) break + if(i == 1) // So one burst only makes one message and not 3+ messages. + handle_firing_text(user, target, pointblank, reflex) + process_accuracy(projectile, user, target, i, held_twohanded) if(pointblank) @@ -387,14 +390,6 @@ if(one_handed_penalty >= 20) to_chat(user, "You struggle to keep \the [src] pointed at the correct position with just one hand!") - var/target_for_log - if(ismob(target)) - target_for_log = target - else - target_for_log = "[target.name]" - - add_attack_logs(user,target_for_log,"Fired gun [src.name] ([reflex ? "REFLEX" : "MANUAL"])") - //update timing user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user.setMoveCooldown(move_delay) @@ -500,11 +495,9 @@ src.visible_message("*click click*") playsound(src.loc, 'sound/weapons/empty.ogg', 100, 1) -//called after successfully firing -/obj/item/weapon/gun/proc/handle_post_fire(mob/user, atom/target, var/pointblank=0, var/reflex=0) - if(fire_anim) - flick(fire_anim, src) - +// Called when the user is about to fire. +// Moved from handle_post_fire() because if using a laser, the message for when someone got shot would show up before the firing message. +/obj/item/weapon/gun/proc/handle_firing_text(mob/user, atom/target, pointblank = FALSE, reflex = FALSE) if(silenced) to_chat(user, "You fire \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]") for(var/mob/living/L in oview(2,user)) @@ -521,6 +514,19 @@ "You hear a [fire_sound_text]!" ) + var/target_for_log + if(ismob(target)) + target_for_log = target + else + target_for_log = "[target.name]" + + add_attack_logs(user, target_for_log, "Fired gun '[src.name]' ([reflex ? "REFLEX" : "MANUAL"])") + +//called after successfully firing +/obj/item/weapon/gun/proc/handle_post_fire(mob/user, atom/target, var/pointblank=0, var/reflex=0) + if(fire_anim) + flick(fire_anim, src) + if(muzzle_flash) set_light(muzzle_flash) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index f939faa461..812933ee15 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -76,12 +76,17 @@ var/start_nutrition = H.nutrition var/end_nutrition = 0 - H.nutrition -= rechargeamt / 10 + H.nutrition -= rechargeamt / 15 end_nutrition = H.nutrition - if(start_nutrition - max(0, end_nutrition) < rechargeamt / 10) - H.remove_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition))) + if(start_nutrition - max(0, end_nutrition) < rechargeamt / 15) + + if(H.isSynthetic()) + H.adjustToxLoss((rechargeamt / 15) - (start_nutrition - max(0, end_nutrition))) + + else + H.remove_blood((rechargeamt / 15) - (start_nutrition - max(0, end_nutrition))) power_supply.give(rechargeamt) //... to recharge 1/5th the battery update_icon() @@ -162,8 +167,8 @@ var/obj/item/rig_module/module = src.loc if(module.holder && module.holder.wearer) var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/weapon/rig/suit = H.back + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() if(istype(suit)) return suit.cell return null diff --git a/code/modules/projectiles/guns/magnetic/gasthrower.dm b/code/modules/projectiles/guns/magnetic/gasthrower.dm new file mode 100644 index 0000000000..742b5bd6c5 --- /dev/null +++ b/code/modules/projectiles/guns/magnetic/gasthrower.dm @@ -0,0 +1,78 @@ +/obj/item/weapon/gun/magnetic/gasthrower + name = "phoronthrower" + desc = "A modernized flamethrower utilizing pressurized phoron gas as both a propellant and combustion medium." + description_fluff = "A weapon designed to effectively combat the threat posed by Almachi soldiers without the danger of other forms of flamethrower." + icon_state = "gasthrower" + item_state = "bore" + wielded_item_state = "bore-wielded" + icon = 'icons/obj/railgun.dmi' + one_handed_penalty = 20 + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 4, TECH_ILLEGAL = 2, TECH_PHORON = 4) + w_class = ITEMSIZE_LARGE + slowdown = 1 + + burst = 3 + burst_delay = 1 + + fire_sound = 'sound/weapons/towelwipe.ogg' + + removable_components = TRUE + gun_unreliable = 0 + + load_type = /obj/item/weapon/tank + projectile_type = /obj/item/projectile/scatter/flamethrower + + power_cost = 250 + +/obj/item/weapon/gun/magnetic/gasthrower/check_ammo() + if(!loaded || !istype(loaded, load_type)) + return 0 + + var/obj/item/weapon/tank/Tank = loaded + + Tank.air_contents.update_values() // Safety + + var/turf/T = get_turf(src) + + var/phoron_amt = Tank.air_contents.gas["phoron"] + var/co2_amt = Tank.air_contents.gas["carbon_dioxide"] + var/oxy_amt = Tank.air_contents.gas["oxygen"] + var/n2o_amt = Tank.air_contents.gas["sleeping_agent"] + + if(isnull(co2_amt)) + co2_amt = 0 + + if(isnull(oxy_amt)) + oxy_amt = 0 + + if(isnull(n2o_amt)) + n2o_amt = 0 + + var/phoron_mix_proper = TRUE + if(!phoron_amt || phoron_amt < max(0.25, 3 + co2_amt - oxy_amt - (n2o_amt / 2))) + phoron_mix_proper = FALSE + + if(Tank.air_contents.return_pressure() >= T.air.return_pressure() && phoron_mix_proper) + return 1 + + return 0 + +/obj/item/weapon/gun/magnetic/gasthrower/use_ammo() + var/obj/item/weapon/tank/Tank = loaded + + var/moles_to_pull = 0.25 + + Tank.air_contents.remove(moles_to_pull) + +/obj/item/weapon/gun/magnetic/gasthrower/show_ammo(var/mob/user) + ..() + + if(loaded) + var/obj/item/weapon/tank/T = loaded + to_chat(user, "\The [T]'s pressure meter shows: [T.air_contents.return_pressure()] kpa.") + + switch(check_ammo()) + if(TRUE) + to_chat(user, "\The [src]'s display registers a proper fuel mixture.") + if(FALSE) + to_chat(user, "\The [src]'s display registers an improper fuel mixture.") diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index e68bb620ba..2ee5afefdc 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -18,6 +18,7 @@ projectile_type = /obj/item/projectile/bullet/rifle/a145 accuracy = -75 scoped_accuracy = 75 + ignore_visor_zoom_restriction = TRUE // Ignore the restriction on vision modifiers when using this gun's scope. // one_handed_penalty = 90 var/bolt_open = 0 diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 3b96846ecf..3ed6564229 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -10,6 +10,8 @@ unacidable = TRUE pass_flags = PASSTABLE mouse_opacity = 0 + hitsound = 'sound/weapons/pierce.ogg' + var/hitsound_wall = null // Played when something hits a wall, or anything else that isn't a mob. ////TG PROJECTILE SYTSEM //Projectile stuff @@ -95,7 +97,7 @@ var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? var/damage = 10 - var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID, SEARING are the only things that should be in here var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. var/nodamage = 0 //Determines if the projectile will skip any damage inflictions @@ -128,12 +130,18 @@ var/temporary_unstoppable_movement = FALSE + // When a non-hitscan projectile hits something, a visual effect can be spawned. + // This is distinct from the hitscan's "impact_type" var. + var/impact_effect_type = null + /obj/item/projectile/proc/Range() range-- if(range <= 0 && loc) on_range() /obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + impact_sounds(loc) + impact_visuals(loc) // So it does a little 'burst' effect, but not actually do anything (unless overrided). qdel(src) /obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight. @@ -444,10 +452,14 @@ qdel(beam_index) /obj/item/projectile/proc/vol_by_damage() - if(damage) - return CLAMP((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100 + if(damage || agony) + var/value_to_use = damage > agony ? damage : agony + // Multiply projectile damage by 1.2, then CLAMP the value between 30 and 100. + // This was 0.67 but in practice it made all projectiles that did 45 or less damage play at 30, + // which is hard to hear over the gunshots, and is rather rare for a projectile to do that much. + return CLAMP((value_to_use) * 1.2, 30, 100) else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. + return 50 //if the projectile doesn't do damage or agony, play its hitsound at 50% volume. /obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE) if(trajectory && beam_index) @@ -588,6 +600,9 @@ //called when the projectile stops flying because it Bump'd with something /obj/item/projectile/proc/on_impact(atom/A) + impact_sounds(A) + impact_visuals(A) + if(damage && damage_type == BURN) var/turf/T = get_turf(A) if(T) @@ -629,16 +644,27 @@ def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part result = target_mob.bullet_act(src, def_zone) + if(!istype(target_mob)) + return FALSE // Mob deleted itself or something. + if(result == PROJECTILE_FORCE_MISS) if(!silenced) - visible_message("\The [src] misses [target_mob] narrowly!") + target_mob.visible_message("\The [src] misses \the [target_mob] narrowly!") + playsound(target_mob, "bullet_miss", 75, 1) return FALSE //hit messages if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + playsound(target_mob, hitsound, 5, 1, -1) + to_chat(target_mob, span("critical", "You've been hit in the [parse_zone(def_zone)] by \the [src]!")) else - visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + var/volume = vol_by_damage() + playsound(target_mob, hitsound, volume, 1, -1) + // X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + target_mob.visible_message( + span("danger", "\The [target_mob] was hit in the [parse_zone(def_zone)] by \the [src]!"), + span("critical", "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + ) //admin logs if(!no_attack_log) @@ -743,3 +769,28 @@ preparePixelProjectile(target, get_turf(src), params, forced_spread) return fire(angle_override, direct_target) + +// Makes a brief effect sprite appear when the projectile hits something solid. +/obj/item/projectile/proc/impact_visuals(atom/A, hit_x, hit_y) + if(impact_effect_type && !hitscan) // Hitscan things have their own impact sprite. + if(isnull(hit_x) && isnull(hit_y)) + if(trajectory) + // Effect goes where the projectile 'stopped'. + hit_x = A.pixel_x + trajectory.return_px() + hit_y = A.pixel_y + trajectory.return_py() + else if(A == original) + // Otherwise it goes where the person who fired clicked. + hit_x = A.pixel_x + p_x - 16 + hit_y = A.pixel_y + p_y - 16 + else + // Otherwise it'll be random. + hit_x = A.pixel_x + rand(-8, 8) + hit_y = A.pixel_y + rand(-8, 8) + new impact_effect_type(get_turf(A), src, hit_x, hit_y) + +/obj/item/projectile/proc/impact_sounds(atom/A) + if(hitsound_wall && !ismob(A)) // Mob sounds are handled in attack_mob(). + var/volume = CLAMP(vol_by_damage() + 20, 0, 100) + if(silenced) + volume = 5 + playsound(get_turf(A), hitsound_wall, volume, 1, -1) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index b00fa8221a..41430bea38 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -14,6 +14,8 @@ light_range = 2 light_power = 0.5 light_color = "#FF0D00" + hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' muzzle_type = /obj/effect/projectile/muzzle/laser tracer_type = /obj/effect/projectile/tracer/laser @@ -211,6 +213,7 @@ agony = 40 damage_type = HALLOSS light_color = "#FFFFFF" + hitsound = 'sound/weapons/zapbang.ogg' combustion = FALSE @@ -257,3 +260,4 @@ damage = 30 agony = 15 eyeblur = 2 + hitsound = 'sound/weapons/zapbang.ogg' diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index d25b0e5fee..e05739d194 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -8,6 +8,8 @@ check_armour = "bullet" embed_chance = 20 //Modified in the actual embed process, but this should keep embed chance about the same sharp = 1 + hitsound_wall = "ricochet" + impact_effect_type = /obj/effect/temp_visual/impact_effect var/mob_passthrough_check = 0 muzzle_type = /obj/effect/projectile/muzzle/bullet @@ -266,6 +268,15 @@ flammability = 2 range = 6 +/obj/item/projectile/bullet/incendiary/flamethrower/tiny + damage = 2 + incendiary = 0 + flammability = 2 + modifier_type_to_apply = /datum/modifier/fire/stack_managed/weak + modifier_duration = 20 SECONDS + range = 6 + agony = 0 + /* Practice rounds and blanks */ /obj/item/projectile/bullet/practice diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index f107816116..533cff2e32 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -4,6 +4,11 @@ damage = 0 damage_type = BURN check_armour = "energy" + + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + hitsound = 'sound/weapons/zapbang.ogg' + var/flash_strength = 10 //releases a burst of light on impact or after travelling a distance @@ -11,6 +16,7 @@ name = "chemical shell" icon_state = "bullet" fire_sound = 'sound/weapons/gunshot_pathetic.ogg' + hitsound_wall = null damage = 5 range = 15 //if the shell hasn't hit anything after travelling this far it just explodes. var/flash_range = 0 @@ -91,6 +97,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -157,6 +164,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -209,10 +217,11 @@ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE damage_type = BURN check_armour = "energy" - light_color = "#0000FF" + light_color = "#00AAFF" embed_chance = 0 muzzle_type = /obj/effect/projectile/muzzle/pulse + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/phase name = "phase wave" diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index 550aab6113..d03de505b5 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -7,6 +7,9 @@ combustion = FALSE + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + /obj/item/projectile/forcebolt/strong name = "force bolt" diff --git a/code/modules/projectiles/projectile/scatter.dm b/code/modules/projectiles/projectile/scatter.dm index 0aa6ad5719..4b0511d0b4 100644 --- a/code/modules/projectiles/projectile/scatter.dm +++ b/code/modules/projectiles/projectile/scatter.dm @@ -60,3 +60,13 @@ submunitions = list( /obj/item/projectile/bullet/shotgun/ion = 3 ) + +/obj/item/projectile/scatter/flamethrower + damage = 5 + submunition_spread_max = 100 + submunition_spread_min = 30 + force_max_submunition_spread = TRUE + + submunitions = list( + /obj/item/projectile/bullet/incendiary/flamethrower/tiny = 7 + ) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 78e3f4f46b..8f81ed16cd 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -11,6 +11,9 @@ light_color = "#55AAFF" combustion = FALSE + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + hitsound = 'sound/weapons/ionrifle.ogg' var/sev1_range = 0 var/sev2_range = 1 @@ -18,8 +21,8 @@ var/sev4_range = 1 /obj/item/projectile/ion/on_impact(var/atom/target) - empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) - return 1 + empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) + ..() /obj/item/projectile/ion/small sev1_range = -1 @@ -58,6 +61,7 @@ light_range = 2 light_power = 0.5 light_color = "#55AAFF" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -133,6 +137,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -189,6 +194,7 @@ light_range = 2 light_power = 0.5 light_color = "#FFFFFF" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) var/mob/M = target @@ -211,6 +217,7 @@ if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(5,8)) + ..() /obj/item/projectile/chameleon name = "bullet" diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 6e2ba4cfb8..d1a1cbe3f2 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -16,7 +16,7 @@ icon = 'icons/obj/chemical.dmi' icon_state = "mixer0" circuit = /obj/item/weapon/circuitboard/chem_master - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 var/beaker = null var/obj/item/weapon/storage/pill_bottle/loaded_pill_bottle = null @@ -333,7 +333,7 @@ icon_state = "juicer1" density = 0 anchored = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 circuit = /obj/item/weapon/circuitboard/grinder diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index c91a478cfb..489378b533 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -167,10 +167,10 @@ S.visible_message("[S]'s flesh sizzles where the water touches it!", "Your flesh burns in the water!") // Then extinguish people on fire. - var/needed = L.fire_stacks * 5 + var/needed = max(0,L.fire_stacks) * 5 if(amount > needed) L.ExtinguishMob() - L.adjust_fire_stacks(-(amount / 5)) + L.water_act(amount / 25) // Div by 25, as water_act multiplies it by 5 in order to calculate firestack modification. remove_self(needed) /datum/reagent/water/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm index d9a7483ec1..cd2e6767b4 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm @@ -12,12 +12,13 @@ metabolism = REM var/modifier_to_add = /datum/modifier/berserk - var/modifier_duration = 2 SECONDS // How long, per unit dose, will this last? + var/modifier_duration = 3 SECONDS // How long, per unit dose, will this last? + // 2 SECONDS is the resolution of life code, and the modifier will expire before chemical processing tries to re-add it /datum/reagent/modapplying/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) return - M.add_modifier(modifier_to_add, dose * modifier_duration) + M.add_modifier(modifier_to_add, modifier_duration, suppress_failure = TRUE) /datum/reagent/modapplying/cryofluid name = "cryogenic slurry" diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm index 17da8452de..845f1a102c 100644 --- a/code/modules/reagents/dispenser/dispenser2.dm +++ b/code/modules/reagents/dispenser/dispenser2.dm @@ -15,7 +15,7 @@ var/accept_drinking = 0 var/amount = 30 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 anchored = 1 diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm index b86bcc7435..78780a3543 100644 --- a/code/modules/reagents/distilling/distilling.dm +++ b/code/modules/reagents/distilling/distilling.dm @@ -6,7 +6,7 @@ /obj/machinery/portable_atmospherics/powered/reagent_distillery name = "chemical distillery" desc = "A complex machine utilizing state-of-the-art components to mix chemicals at different temperatures." - use_power = 1 + use_power = USE_POWER_IDLE icon = 'icons/obj/machines/reagent.dmi' icon_state = "distiller" @@ -108,6 +108,57 @@ ..() +/obj/machinery/portable_atmospherics/powered/reagent_distillery/examine(mob/user) + ..() + if(get_dist(user, src) < 3) + to_chat(user, "\The [src] is powered [on ? "on" : "off"].") + + to_chat(user, "\The [src]'s gauges read:") + if(!use_atmos) + to_chat(user, "- Target Temperature: [target_temp]") + to_chat(user, "- Temperature: [current_temp]") + + if(InputBeaker) + if(InputBeaker.reagents.reagent_list.len) + to_chat(user, "\The [src]'s input beaker holds [InputBeaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s input beaker is empty!") + + if(Reservoir.reagents.reagent_list.len) + to_chat(user, "\The [src]'s internal buffer holds [Reservoir.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s internal buffer is empty!") + + if(OutputBeaker) + if(OutputBeaker.reagents.reagent_list.len) + to_chat(user, "\The [src]'s output beaker holds [OutputBeaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s output beaker is empty!") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/verb/toggle_power(mob/user = usr) + set name = "Toggle Distillery Heating" + set category = "Object" + set src in view(1) + + if(powered()) + on = !on + to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + else + to_chat(user, " Nothing happens.") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/verb/toggle_mixing(mob/user = usr) + set name = "Start Distillery Mixing" + set category = "Object" + set src in view(1) + + to_chat(user, "You press \the [src]'s chamber agitator button.") + if(on) + visible_message("\The [src] rattles to life.") + Reservoir.reagents.handle_reactions() + else + spawn(1 SECOND) + to_chat(user, "Nothing happens..") + /obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user) var/list/options = list() options["examine"] = radial_examine @@ -138,9 +189,7 @@ examine(user) if("use") - if(powered()) - on = !on - to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + toggle_power(user) if("inspect gauges") to_chat(user, "\The [src]'s gauges read:") @@ -149,13 +198,7 @@ to_chat(user, "- Temperature: [current_temp]") if("pulse agitator") - to_chat(user, "You press \the [src]'s chamber agitator button.") - if(on) - visible_message("\The [src] rattles to life.") - Reservoir.reagents.handle_reactions() - else - spawn(1 SECOND) - to_chat(user, "Nothing happens..") + toggle_mixing(user) if("eject input") if(InputBeaker) @@ -252,19 +295,36 @@ if(!powered()) on = FALSE - if(!on || (use_atmos && (!connected_port || avg_pressure < 1000))) + if(!on || (use_atmos && (!connected_port || (avg_pressure / avg_temp) < (1000 / T20C)))) // This mostly respects gas laws by ignoring volume but it should make it usable at low temps current_temp = round((current_temp + T20C) / 2) else if(on) if(!use_atmos) if(current_temp != round(target_temp)) - var/shift_mod = 0 - if(current_temp < target_temp) - shift_mod = 1 - else if(current_temp > target_temp) - shift_mod = -1 - current_temp = CLAMP(round((current_temp + 1 * shift_mod) + (rand(-5, 5) / 10)), min_temp, max_temp) + // Some horrible bastardized attempt at approximating the values of a logistic function, bounded by (max_temp, target_temp, min_temp) + // So we can attempt to estimate the change in temperature for this process() step + + // Apply inverse of the logistic function to fetch our x value + var/x = -1 * log((current_temp < target_temp ? (target_temp - min_temp) / (current_temp - min_temp) : (max_temp - target_temp) / (max_temp - current_temp)) - 1) + if(!x) + x = 0 // Keep null from propagating into the temp + + // Apply the derivative of the logistic function to get the slope + var/dy = (NUM_E ** (-1 * x)) / ((1 + (NUM_E ** (-1 * x))) ** 2) + + // Compute temperature diff, being farther from the target should result in larger steps + // IMPORTANT: If you want to tweak how quickly this changes, tweak this *10! + // As of initial testing, a *10 gives ~5-6 minutes to go from room temp to 500C (+/-0.5C) + var/temp_diff = (current_temp < target_temp ? dy * 10 * target_temp / current_temp : dy * -10 * current_temp / target_temp) + + current_temp = CLAMP(round((current_temp + temp_diff), 0.01), min_temp, max_temp) use_power(power_rating * CELLRATE) + + if(target_temp == round(current_temp, 1.0)) + current_temp = target_temp // Hard set it so we don't need to worry about exact decimals any more, after we've been keeping track of it all this time + playsound(src, 'sound/machines/ping.ogg', 50, 0) + src.visible_message("\The [src] pings as it reaches the target temperature.") + else if(connected_port && avg_pressure > 1000) current_temp = round((current_temp + avg_temp) / 2) else if(!run_pump) diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 8a2e9796cd..b8aba13939 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -359,7 +359,7 @@ // charge the gas reservoir and perform flush if ready /obj/machinery/disposal/process() if(!air_contents || (stat & BROKEN)) // nothing can happen if broken - update_use_power(0) + update_use_power(USE_POWER_OFF) return flush_count++ @@ -377,7 +377,7 @@ flush() if(mode != 1) //if off or ready, no need to charge - update_use_power(1) + update_use_power(USE_POWER_IDLE) else if(air_contents.return_pressure() >= SEND_PRESSURE) mode = 2 //if full enough, switch to ready mode update() @@ -386,7 +386,7 @@ /obj/machinery/disposal/proc/pressurize() if(stat & NOPOWER) // won't charge if no power - update_use_power(0) + update_use_power(USE_POWER_OFF) return var/atom/L = loc // recharging from loc turf diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 75d45652d9..f9ff3adb51 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -20,7 +20,7 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid). hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_GRAPHITE, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 2500 diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index f8d7fbc4d6..a17bf370bf 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -12,7 +12,7 @@ Note: Must be placed within 3 tiles of the R&D Console var/obj/item/weapon/loaded_item = null var/decon_mod = 0 circuit = /obj/item/weapon/circuitboard/destructive_analyzer - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 2500 diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index a61dc04e89..0b414ff868 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -54,7 +54,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() desc = "Facilitates both PDA messages and request console functions." density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 @@ -240,7 +240,7 @@ var/obj/machinery/blackbox_recorder/blackbox desc = "Records all radio communications, as well as various other information in case of the worst." density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 var/list/messages = list() //Stores messages of non-standard frequencies diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index d038736b96..da9cc05509 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -53,7 +53,7 @@ newspecies = prosfab.species var/mob/living/carbon/human/H = new(newloc,newspecies) - H.stat = DEAD + H.set_stat(DEAD) H.gender = gender for(var/obj/item/organ/external/EO in H.organs) if(EO.organ_tag == BP_TORSO || EO.organ_tag == BP_GROIN) diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index 3d65e35d63..3837e13aae 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -3,7 +3,7 @@ icon_state = "protolathe" flags = OPENCONTAINER circuit = /obj/item/weapon/circuitboard/protolathe - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 5000 diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index 9bcb130c00..a5f25beb08 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -7,7 +7,7 @@ icon = 'icons/obj/machines/research.dmi' density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE var/busy = 0 var/obj/machinery/computer/rdconsole/linked_console diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index e3202ccbec..707bd3ccd9 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -16,7 +16,7 @@ //1 = select event //2 = authenticate anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index e764e0b516..792d9d4657 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -134,7 +134,7 @@ var/is_open = 0 //Whether or not the wires are exposed var/locked = 0 var/check_delay = 60 //periodically recheck if we need to rebuild a shield - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 var/global/list/blockedturfs = list( /turf/space, @@ -156,7 +156,7 @@ idle_power_usage = 0 for(var/obj/machinery/shield/shield_tile in deployed_shields) idle_power_usage += shield_tile.shield_idle_power - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/shieldgen/proc/shields_down() if(!active) return 0 //If it's already off, how did this get called? @@ -166,7 +166,7 @@ collapse_shields() - update_use_power(0) + update_use_power(USE_POWER_OFF) /obj/machinery/shieldgen/proc/create_shields() for(var/turf/target_tile in range(2, src)) diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 5b7d7f2118..87d07cceaf 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -31,8 +31,14 @@ /obj/effect/energy_field/Destroy() update_nearby_tiles() - my_gen.field.Remove(src) - my_gen = null + if(my_gen) + if(istype(my_gen)) + my_gen.field.Remove(src) + my_gen = null + else if(istype(my_gen, /datum/artifact_effect/forcefield)) + var/datum/artifact_effect/forcefield/AE = my_gen + AE.created_field.Remove(src) + my_gen = null var/turf/current_loc = get_turf(src) . = ..() for(var/direction in cardinal) diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index 331f04549a..2a6a859c43 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -23,7 +23,7 @@ //There have to be at least two posts, so these are effectively doubled var/power_draw = 30000 //30 kW. How much power is drawn from powernet. Increase this to allow the generator to sustain longer shields, at the cost of more power draw. var/max_stored_power = 50000 //50 kW - use_power = 0 //Draws directly from power net. Does not use APC power. + use_power = USE_POWER_OFF //Draws directly from power net. Does not use APC power. /obj/machinery/shieldwallgen/attack_hand(mob/user as mob) if(state != 1) diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 4006a1cedf..a30dea1622 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -15,7 +15,7 @@ var/max_charge = 8e6 //8 MJ var/max_charge_rate = 400000 //400 kW var/locked = 0 - use_power = 0 //doesn't use APC power + use_power = USE_POWER_OFF //doesn't use APC power var/charge_rate = 100000 //100 kW var/obj/machinery/shield_gen/owned_gen diff --git a/code/modules/shieldgen/shield_diffuser.dm b/code/modules/shieldgen/shield_diffuser.dm index c30f8a7351..9c42e22b57 100644 --- a/code/modules/shieldgen/shield_diffuser.dm +++ b/code/modules/shieldgen/shield_diffuser.dm @@ -4,7 +4,7 @@ description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access." icon = 'icons/obj/machines/shielding.dmi' icon_state = "fdiffuser_on" - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 25 // Previously 100. active_power_usage = 500 // Previously 2000 anchored = 1 @@ -57,7 +57,7 @@ update_icon() return enabled = !enabled - use_power = enabled + 1 + update_use_power(enabled ? USE_POWER_ACTIVE : USE_POWER_IDLE) update_icon() to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index 253bc391f3..ef63f9d683 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -22,7 +22,7 @@ var/time_since_fail = 100 var/energy_conversion_rate = 0.0006 //how many renwicks per watt? Higher numbers equals more effiency. var/z_range = 0 // How far 'up and or down' to extend the shield to, in z-levels. Only works on MultiZ supported z-levels. - use_power = 0 //doesn't use APC power + use_power = USE_POWER_OFF //doesn't use APC power /obj/machinery/shield_gen/advanced name = "advanced bubble shield generator" diff --git a/code/modules/shuttles/_defines.dm b/code/modules/shuttles/_defines.dm index ad8c39466b..22d57fe9cb 100644 --- a/code/modules/shuttles/_defines.dm +++ b/code/modules/shuttles/_defines.dm @@ -1,4 +1,22 @@ -#define SHUTTLE_FLAGS_NONE 0 -#define SHUTTLE_FLAGS_PROCESS 1 -#define SHUTTLE_FLAGS_SUPPLY 2 -#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) \ No newline at end of file +// Shuttle flags +#define SHUTTLE_FLAGS_NONE 0 +#define SHUTTLE_FLAGS_PROCESS 1 // Should be processed by shuttle subsystem +#define SHUTTLE_FLAGS_SUPPLY 2 // This is the supply shuttle. Why is this a tag? +#define SHUTTLE_FLAGS_ZERO_G 4 // Shuttle has no internal gravity generation +#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) + +// shuttle_landmark flags +#define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at +#define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. + +// Ferry shuttle location constants +#define FERRY_LOCATION_STATION 0 +#define FERRY_LOCATION_OFFSITE 1 +#define FERRY_GOING_TO_STATION 0 +#define FERRY_GOING_TO_OFFSITE 1 + +#ifndef DEBUG_SHUTTLES + #define log_shuttle(M) +#else + #define log_shuttle(M) log_debug("[M]") +#endif diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm index 060dbff1ad..245b96e82e 100644 --- a/code/modules/shuttles/escape_pods.dm +++ b/code/modules/shuttles/escape_pods.dm @@ -1,50 +1,55 @@ -/datum/shuttle/ferry/escape_pod - var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller - category = /datum/shuttle/ferry/escape_pod +/datum/shuttle/autodock/ferry/escape_pod + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/arming_controller + category = /datum/shuttle/autodock/ferry/escape_pod -/datum/shuttle/ferry/escape_pod/New() +/datum/shuttle/autodock/ferry/escape_pod/New() move_time = move_time + rand(-30, 60) if(name in emergency_shuttle.escape_pods) CRASH("An escape pod with the name '[name]' has already been defined.") emergency_shuttle.escape_pods[name] = src + ..() -/datum/shuttle/ferry/escape_pod/init_docking_controllers() - ..() - arming_controller = locate(dock_target_station) + //find the arming controller (berth) - If not configured directly, try to read it from current location landmark + var/arming_controller_tag = arming_controller + if(!arming_controller && active_docking_controller) + arming_controller_tag = active_docking_controller.id_tag + arming_controller = SSshuttles.docking_registry[arming_controller_tag] if(!istype(arming_controller)) - warning("warning: escape pod with station dock tag [dock_target_station] could not find it's dock target!") + CRASH("Could not find arming controller for escape pod \"[name]\", tag was '[arming_controller_tag]'.") - if(docking_controller) - var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = docking_controller.master - if(!istype(controller_master)) - warning("warning: escape pod with docking tag [docking_controller_tag] could not find it's controller master!") - else - controller_master.pod = src + //find the pod's own controller + var/datum/computer/file/embedded_program/docking/simple/prog = SSshuttles.docking_registry[docking_controller_tag] + var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = prog.master + if(!istype(controller_master)) + CRASH("Escape pod \"[name]\" could not find it's controller master! docking_controller_tag=[docking_controller_tag]") + controller_master.pod = src -/datum/shuttle/ferry/escape_pod/can_launch() +/datum/shuttle/autodock/ferry/escape_pod/can_launch() if(arming_controller && !arming_controller.armed) //must be armed return 0 if(location) return 0 //it's a one-way trip. return ..() -/datum/shuttle/ferry/escape_pod/can_force() +/datum/shuttle/autodock/ferry/escape_pod/can_force() if (arming_controller.eject_time && world.time < arming_controller.eject_time + 50) return 0 //dont allow force launching until 5 seconds after the arming controller has reached it's countdown return ..() -/datum/shuttle/ferry/escape_pod/can_cancel() +/datum/shuttle/autodock/ferry/escape_pod/can_cancel() return 0 //This controller goes on the escape pod itself /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod name = "escape pod controller" - var/datum/shuttle/ferry/escape_pod/pod + program = /datum/computer/file/embedded_program/docking/simple + var/datum/shuttle/autodock/ferry/escape_pod/pod /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type data = list( "docking_status" = docking_program.get_docking_status(), @@ -64,17 +69,18 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return if("manual_arm") pod.arming_controller.arm() + return TOPIC_REFRESH if("force_launch") if (pod.can_force()) pod.force_launch(src) else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves pod.launch(src) - + return TOPIC_REFRESH return 0 @@ -82,18 +88,15 @@ //This controller is for the escape pod berth (station side) /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth name = "escape pod berth controller" - -/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/Initialize() - . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/simple/escape_pod(src) - program = docking_program + program = /datum/computer/file/embedded_program/docking/simple/escape_pod_berth /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type var/armed = null - if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) - var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = docking_program armed = P.armed data = list( @@ -114,44 +117,44 @@ if (!emagged) to_chat(user, "You emag the [src], arming the escape pod!") emagged = 1 - if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) - var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + if (istype(program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = program if (!P.armed) P.arm() return 1 //A docking controller program for a simple door based docking port -/datum/computer/file/embedded_program/docking/simple/escape_pod +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth var/armed = 0 var/eject_delay = 10 //give latecomers some time to get out of the way if they don't make it onto the pod var/eject_time = null var/closing = 0 -/datum/computer/file/embedded_program/docking/simple/escape_pod/proc/arm() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/proc/arm() if(!armed) armed = 1 open_door() -/datum/computer/file/embedded_program/docking/simple/escape_pod/receive_user_command(command) +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/receive_user_command(command) if (!armed) - return - ..(command) + return TRUE // Eat all commands. + return ..(command) -/datum/computer/file/embedded_program/docking/simple/escape_pod/process() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/process() ..() if (eject_time && world.time >= eject_time && !closing) close_door() closing = 1 -/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_docking() return -/datum/computer/file/embedded_program/docking/simple/escape_pod/ready_for_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/ready_for_docking() return 1 -/datum/computer/file/embedded_program/docking/simple/escape_pod/finish_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/finish_docking() return //don't do anything - the doors only open when the pod is armed. -/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_undocking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_undocking() eject_time = world.time + eject_delay*10 diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm new file mode 100644 index 0000000000..99761dfe25 --- /dev/null +++ b/code/modules/shuttles/landmarks.dm @@ -0,0 +1,194 @@ +//making this separate from /obj/effect/landmark until that mess can be dealt with +/obj/effect/shuttle_landmark + name = "Nav Point" + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + anchored = 1 + unacidable = 1 + simulated = 0 + invisibility = 101 + flags = SLANDMARK_FLAG_AUTOSET // We generally want to use current area/turf as base. + + //ID of the landmark + var/landmark_tag + //ID of the controller on the dock side (intialize to id_tag, becomes reference) + var/datum/computer/file/embedded_program/docking/docking_controller + //Map of shuttle names to ID of controller used for this landmark for shuttles with multiple ones. + var/list/special_dock_targets + + //When the shuttle leaves this landmark, it will leave behind the base area + //also used to determine if the shuttle can arrive here without obstruction + var/area/base_area + //Will also leave this type of turf behind if set. + var/turf/base_turf + //Name of the shuttle, null for generic waypoint + var/shuttle_restricted + +/obj/effect/shuttle_landmark/Initialize() + . = ..() + if(docking_controller) + . = INITIALIZE_HINT_LATELOAD + + // Even if this flag is set, hardcoded values take precedence. + if(flags & SLANDMARK_FLAG_AUTOSET) + if(ispath(base_area)) + var/area/A = locate(base_area) + if(!istype(A)) + CRASH("Shuttle landmark \"[landmark_tag]\" couldn't locate area [base_area].") + base_area = A + else + base_area = get_area(src) + var/turf/T = get_turf(src) + if(T && !base_turf) + base_turf = T.type + else + base_area = locate(base_area || world.area) + + name = (name + " ([x],[y])") + SSshuttles.register_landmark(landmark_tag, src) + +/obj/effect/shuttle_landmark/LateInitialize() + if(!docking_controller) + return + var/docking_tag = docking_controller + docking_controller = SSshuttles.docking_registry[docking_tag] + if(!istype(docking_controller)) + log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.") + if(using_map.use_overmap) + var/obj/effect/overmap/visitable/location = map_sectors["[z]"] + if(location && location.docking_codes) + docking_controller.docking_codes = location.docking_codes + +/obj/effect/shuttle_landmark/forceMove() + var/obj/effect/overmap/visitable/map_origin = map_sectors["[z]"] + . = ..() + var/obj/effect/overmap/visitable/map_destination = map_sectors["[z]"] + if(map_origin != map_destination) + if(map_origin) + map_origin.remove_landmark(src, shuttle_restricted) + if(map_destination) + map_destination.add_landmark(src, shuttle_restricted) + +//Called when the landmark is added to an overmap sector. +/obj/effect/shuttle_landmark/proc/sector_set(var/obj/effect/overmap/visitable/O, shuttle_name) + shuttle_restricted = shuttle_name + +/obj/effect/shuttle_landmark/proc/is_valid(var/datum/shuttle/shuttle) + if(shuttle.current_location == src) + return FALSE + for(var/area/A in shuttle.shuttle_area) + var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents) + if(check_collision(base_area, list_values(translation))) + return FALSE + var/conn = GetConnectedZlevels(z) + for(var/w in (z - shuttle.multiz) to z) + if(!(w in conn)) + return FALSE + return TRUE + +// This creates a graphical warning to where the shuttle is about to land in approximately five seconds. +/obj/effect/shuttle_landmark/proc/create_warning_effect(var/datum/shuttle/shuttle) + if(shuttle.current_location == src) + return // TOO LATE! + for(var/area/A in shuttle.shuttle_area) + var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents) + for(var/T in list_values(translation)) + new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed. + return + +// Should return a readable description of why not if it can't depart. +/obj/effect/shuttle_landmark/proc/cannot_depart(datum/shuttle/shuttle) + return FALSE + +/obj/effect/shuttle_landmark/proc/shuttle_departed(datum/shuttle/shuttle) + return + +/obj/effect/shuttle_landmark/proc/shuttle_arrived(datum/shuttle/shuttle) + return + +/proc/check_collision(area/target_area, list/target_turfs) + for(var/target_turf in target_turfs) + var/turf/target = target_turf + if(!target) + return TRUE //collides with edge of map + if(target.loc != target_area) + return TRUE //collides with another area + if(target.density) + return TRUE //dense turf + return FALSE + +// +//Self-naming/numbering ones. +// +/obj/effect/shuttle_landmark/automatic + name = "Navpoint" + landmark_tag = "navpoint" + flags = SLANDMARK_FLAG_AUTOSET + +/obj/effect/shuttle_landmark/automatic/Initialize() + landmark_tag += "-[x]-[y]-[z]-[random_id("landmarks",1,9999)]" + return ..() + +/obj/effect/shuttle_landmark/automatic/sector_set(var/obj/effect/overmap/visitable/O) + ..() + name = ("[O.name] - [initial(name)] ([x],[y])") + +//Subtype that calls explosion on init to clear space for shuttles +/obj/effect/shuttle_landmark/automatic/clearing + var/radius = 10 + +/obj/effect/shuttle_landmark/automatic/clearing/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/shuttle_landmark/automatic/clearing/LateInitialize() + ..() + for(var/turf/T in range(radius, src)) + if(T.density) + T.ChangeTurf(get_base_turf_by_area(T)) + + +// Subtype that also queues a shuttle datum (for shuttles starting on maps loaded at runtime) +/obj/effect/shuttle_landmark/shuttle_initializer + var/datum/shuttle/shuttle_type + +/obj/effect/shuttle_landmark/shuttle_initializer/Initialize() + . = ..() + LAZYADD(SSshuttles.shuttles_to_initialize, shuttle_type) // queue up for init. + +// +// Bluespace flare landmark beacon +// +/obj/item/device/spaceflare + name = "bluespace flare" + desc = "Burst transmitter used to broadcast all needed information for shuttle navigation systems. Has a flare attached for marking the spot where you probably shouldn't be standing." + icon_state = "bluflare" + light_color = "#3728ff" + var/active + +/obj/item/device/spaceflare/attack_self(var/mob/user) + if(!active) + visible_message("[user] pulls the cord, activating the [src].") + activate() + +/obj/item/device/spaceflare/proc/activate() + if(active) + return + var/turf/T = get_turf(src) + var/mob/M = loc + if(istype(M) && !M.unEquip(src, T)) + return + + active = 1 + anchored = 1 + + var/obj/effect/shuttle_landmark/automatic/mark = new(T) + mark.name = ("Beacon signal ([T.x],[T.y])") + T.hotspot_expose(1500, 5) + update_icon() + +/obj/item/device/spaceflare/update_icon() + . = ..() + if(active) + icon_state = "bluflare_on" + set_light(0.3, 0.1, 6, 2, "85d1ff") diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index 2bb550635c..2d277ab32a 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -1,6 +1,3 @@ -//These lists are populated in /datum/controller/subsystem/shuttles/proc/setup_shuttle_docks() -//Shuttle subsystem is instantiated in shuttles.dm. - //shuttle moving state defines are in setup.dm /datum/shuttle @@ -8,48 +5,78 @@ var/warmup_time = 0 var/moving_status = SHUTTLE_IDLE - var/docking_controller_tag //tag of the controller used to coordinate docking - var/datum/computer/file/embedded_program/docking/docking_controller //the controller itself. (micro-controller, not game controller) + var/list/shuttle_area // Initial value can be either a single area type or a list of area types + var/obj/effect/shuttle_landmark/current_location //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. - var/arrive_time = 0 //the time at which the shuttle arrives when long jumping - var/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping, to compare against arrive time. - var/flags = SHUTTLE_FLAGS_PROCESS + var/tmp/arrive_time = 0 //the time at which the shuttle arrives when long jumping + var/flags = SHUTTLE_FLAGS_NONE + var/process_state = IDLE_STATE // Used with SHUTTLE_FLAGS_PROCESS, as well as to store current state. var/category = /datum/shuttle + var/multiz = 0 //how many multiz levels, starts at 0 TODO Leshana - Are we porting this? - var/ceiling_type = /turf/unsimulated/floor/shuttle_ceiling + var/ceiling_type // Type path of turf to roof over the shuttle when at multi-z landmarks. Ignored if null. -/datum/shuttle/New() + var/sound_takeoff = 'sound/effects/shuttles/shuttle_takeoff.ogg' + var/sound_landing = 'sound/effects/shuttles/shuttle_landing.ogg' + + var/knockdown = 1 //whether shuttle downs non-buckled people when it moves + + var/defer_initialisation = FALSE //If this this shuttle should be initialised automatically. + //If set to true, you are responsible for initialzing the shuttle manually. + //Useful for shuttles that are initialized by map_template loading, or shuttles that are created in-game or not used. + + var/mothershuttle //tag of mothershuttle + var/motherdock //tag of mothershuttle landmark, defaults to starting location + + var/tmp/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping. Used for progress bars. + + // Future Thoughts: Baystation put "docking" stuff in a subtype, leaving base type pure and free of docking stuff. Is this best? + +/datum/shuttle/New(_name, var/obj/effect/shuttle_landmark/initial_location) ..() - if(src.name in shuttle_controller.shuttles) + if(_name) + src.name = _name + + var/list/areas = list() + if(!islist(shuttle_area)) + shuttle_area = list(shuttle_area) + for(var/T in shuttle_area) + var/area/A = locate(T) + if(!istype(A)) + CRASH("Shuttle \"[name]\" couldn't locate area [T].") + areas += A + shuttle_area = areas + + if(initial_location) + current_location = initial_location + else + current_location = SSshuttles.get_landmark(current_location) + if(!istype(current_location)) + log_debug("UM whoops, no initial? [src]") + CRASH("Shuttle '[name]' could not find its starting location landmark [current_location].") + + if(src.name in SSshuttles.shuttles) CRASH("A shuttle with the name '[name]' is already defined.") - shuttle_controller.shuttles[src.name] = src + SSshuttles.shuttles[src.name] = src if(flags & SHUTTLE_FLAGS_PROCESS) - shuttle_controller.process_shuttles += src + SSshuttles.process_shuttles += src if(flags & SHUTTLE_FLAGS_SUPPLY) if(supply_controller.shuttle) CRASH("A supply shuttle is already defined.") supply_controller.shuttle = src /datum/shuttle/Destroy() - shuttle_controller.shuttles -= src.name - shuttle_controller.process_shuttles -= src + current_location = null + SSshuttles.shuttles -= src.name + SSshuttles.process_shuttles -= src + SSshuttles.shuttle_logs -= src if(supply_controller.shuttle == src) supply_controller.shuttle = null . = ..() -/datum/shuttle/process() - return - -/datum/shuttle/proc/init_docking_controllers() - if(docking_controller_tag) - docking_controller = locate(docking_controller_tag) - if(!istype(docking_controller)) - to_world("warning: shuttle with docking tag [docking_controller_tag] could not find it's controller!") - // This creates a graphical warning to where the shuttle is about to land, in approximately five seconds. -/datum/shuttle/proc/create_warning_effect(area/landing_area) - for(var/turf/T in landing_area) - new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed. +/datum/shuttle/proc/create_warning_effect(var/obj/effect/shuttle_landmark/destination) + destination.create_warning_effect(src) // Return false to abort a jump, before the 'warmup' phase. /datum/shuttle/proc/pre_warmup_checks() @@ -60,194 +87,271 @@ return TRUE // If you need an event to occur when the shuttle jumps in short or long jump, override this. -/datum/shuttle/proc/on_shuttle_departure(var/area/origin) - origin.shuttle_departed() +// Keep in mind that destination is the intended destination, the shuttle may or may not actually reach it.s +/datum/shuttle/proc/on_shuttle_departure(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination) return // Similar to above, but when it finishes moving to the target. Short jump generally makes this occur immediately after the above proc. -/datum/shuttle/proc/on_shuttle_arrival(var/area/destination) - destination.shuttle_arrived() +// Keep in mind we might not actually have gotten to destination. Check current_location to be sure where we ended up. +/datum/shuttle/proc/on_shuttle_arrival(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination) return -/datum/shuttle/proc/short_jump(var/area/origin,var/area/destination) +/datum/shuttle/proc/short_jump(var/obj/effect/shuttle_landmark/destination) if(moving_status != SHUTTLE_IDLE) return if(!pre_warmup_checks()) return + var/obj/effect/shuttle_landmark/start_location = current_location + // TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no? moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(origin, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) create_warning_effect(destination) sleep(5 SECONDS) // so the sound finishes. if(!post_warmup_checks()) - moving_status = SHUTTLE_IDLE + cancel_launch(null) + + if(!fuel_check()) //fuel error (probably out of fuel) occured, so cancel the launch + cancel_launch(null) if (moving_status == SHUTTLE_IDLE) - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch - on_shuttle_departure(origin) - moving_status = SHUTTLE_INTRANSIT //shouldn't matter but just to be safe - move(origin, destination) + on_shuttle_departure(start_location, destination) + + attempt_move(destination) + moving_status = SHUTTLE_IDLE + on_shuttle_arrival(start_location, destination) - on_shuttle_arrival(destination) + make_sounds(HYPERSPACE_END) - make_sounds(destination, HYPERSPACE_END) - -/datum/shuttle/proc/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //to_world("shuttle/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]") +// TODO - Far Future - Would be great if this was driven by process too. +/datum/shuttle/proc/long_jump(var/obj/effect/shuttle_landmark/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time) + //to_world("shuttle/long_jump: current_location=[current_location], destination=[destination], interim=[interim], travel_time=[travel_time]") if(moving_status != SHUTTLE_IDLE) return if(!pre_warmup_checks()) return - //it would be cool to play a sound here + var/obj/effect/shuttle_landmark/start_location = current_location + // TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no? moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(departing, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) create_warning_effect(interim) // Really doubt someone is gonna get crushed in the interim area but for completeness's sake we'll make the warning. sleep(5 SECONDS) // so the sound finishes. if(!post_warmup_checks()) - moving_status = SHUTTLE_IDLE + cancel_launch(null) if (moving_status == SHUTTLE_IDLE) - make_sounds(departing, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch arrive_time = world.time + travel_time*10 - depart_time = world.time moving_status = SHUTTLE_INTRANSIT + on_shuttle_departure(start_location, destination) - on_shuttle_departure(departing) + if(attempt_move(interim, TRUE)) + interim.shuttle_arrived() - move(departing, interim, direction) - interim.shuttle_arrived() + var/last_progress_sound = 0 + var/made_warning = FALSE + while (world.time < arrive_time) + // Make the shuttle make sounds every four seconds, since the sound file is five seconds. + if(last_progress_sound + 4 SECONDS < world.time) + make_sounds(HYPERSPACE_PROGRESS) + last_progress_sound = world.time - var/last_progress_sound = 0 - var/made_warning = FALSE - while (world.time < arrive_time) - // Make the shuttle make sounds every four seconds, since the sound file is five seconds. - if(last_progress_sound + 4 SECONDS < world.time) - make_sounds(interim, HYPERSPACE_PROGRESS) - last_progress_sound = world.time + if(arrive_time - world.time <= 5 SECONDS && !made_warning) + made_warning = TRUE + create_warning_effect(destination) + sleep(5) - if(arrive_time - world.time <= 5 SECONDS && !made_warning) - made_warning = TRUE - create_warning_effect(destination) - sleep(5) + if(!attempt_move(destination)) + attempt_move(start_location) //try to go back to where we started. If that fails, I guess we're stuck in the interim location - interim.shuttle_departed() - move(interim, destination, direction) moving_status = SHUTTLE_IDLE + on_shuttle_arrival(start_location, destination) + make_sounds(HYPERSPACE_END) - on_shuttle_arrival(destination) - make_sounds(destination, HYPERSPACE_END) +////////////////////////////// +// Forward declarations of public procs. They do nothing because this is not auto-dock. +/datum/shuttle/proc/fuel_check() + return 1 //fuel check should always pass in non-overmap shuttles (they have magic engines) + +/datum/shuttle/proc/cancel_launch(var/user) + // If we are past warming up its too late to cancel. + if (moving_status == SHUTTLE_WARMUP) + moving_status = SHUTTLE_IDLE +/* + Docking stuff +*/ /datum/shuttle/proc/dock() - if (!docking_controller) - return - - var/dock_target = current_dock_target() - if (!dock_target) - return - - docking_controller.initiate_docking(dock_target) + return /datum/shuttle/proc/undock() - if (!docking_controller) - return - docking_controller.initiate_undocking() + return -/datum/shuttle/proc/current_dock_target() - return null +/datum/shuttle/proc/force_undock() + return -/datum/shuttle/proc/skip_docking_checks() - if (!docking_controller || !current_dock_target()) - return 1 //shuttles without docking controllers or at locations without docking ports act like old-style shuttles - return 0 +// Check if we are docked (or never dock) and thus have properly arrived. +/datum/shuttle/proc/check_docked() + return TRUE -//just moves the shuttle from A to B, if it can be moved -//A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle. +// Check if we are undocked and thus probably ready to depart. +/datum/shuttle/proc/check_undocked() + return TRUE + +/***************** +* Shuttle Moved Handling * (Observer Pattern Implementation: Shuttle Moved) +* Shuttle Pre Move Handling * (Observer Pattern Implementation: Shuttle Pre Move) +*****************/ + +// Move the shuttle to destination if possible. +// Returns TRUE if we actually moved, otherwise FALSE. +/datum/shuttle/proc/attempt_move(var/obj/effect/shuttle_landmark/destination, var/interim = FALSE) + if(current_location == destination) + log_shuttle("Shuttle [src] attempted to move to [destination] but is already there!") + return FALSE + + if(!destination.is_valid(src)) + log_shuttle("Shuttle [src] aborting attempt_move() because destination=[destination] is not valid") + return FALSE + if(current_location.cannot_depart(src)) + log_shuttle("Shuttle [src] aborting attempt_move() because current_location=[current_location] refuses.") + return FALSE + + log_shuttle("[src] moving to [destination]. Areas are [english_list(shuttle_area)]") + var/list/translation = list() + for(var/area/A in shuttle_area) + log_shuttle("Translating [A]") + translation += get_turf_translation(get_turf(current_location), get_turf(destination), A.contents) + var/old_location = current_location + + // Observer pattern pre-move + GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination) + current_location.shuttle_departed(src) + + // Actually do it! (This never fails) + perform_shuttle_move(destination, translation) + + // Observer pattern post-move + destination.shuttle_arrived(src) + GLOB.shuttle_moved_event.raise_event(src, old_location, destination) + + return TRUE + + +//just moves the shuttle from A to B +//A note to anyone overriding move in a subtype. perform_shuttle_move() must absolutely not, under any circumstances, fail to move the shuttle. //If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump() -/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null) - +/datum/shuttle/proc/perform_shuttle_move(var/obj/effect/shuttle_landmark/destination, var/list/turf_translation) + log_shuttle("perform_shuttle_move() current=[current_location] destination=[destination]") //to_world("move_shuttle() called for [name] leaving [origin] en route to [destination].") //to_world("area_coming_from: [origin]") //to_world("destination: [destination]") + ASSERT(current_location != destination) - if(origin == destination) - //to_world("cancelling move, shuttle will overlap.") - return + // If shuttle has no internal gravity, update our gravity with destination gravity + if((flags & SHUTTLE_FLAGS_ZERO_G)) + var/new_grav = 1 + if(destination.flags & SLANDMARK_FLAG_ZERO_G) + var/area/new_area = get_area(destination) + new_grav = new_area.has_gravity + for(var/area/our_area in shuttle_area) + if(our_area.has_gravity != new_grav) + our_area.gravitychange(new_grav) - if (docking_controller && !docking_controller.undocked()) - docking_controller.force_undock() + // TODO - Old code used to throw stuff out of the way instead of squashing. Should we? - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in destination) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - for(var/turf/T in dstturfs) - var/turf/D = locate(T.x, throwy - 1, T.z) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - - for(var/mob/living/carbon/bug in destination) - bug.gib() - - for(var/mob/living/simple_mob/pest in destination) - pest.gib() - - origin.move_contents_to(destination, direction=direction) - - for(var/mob/M in destination) - if(M.client) - spawn(0) - if(M.buckled) - to_chat(M, "Sudden acceleration presses you into \the [M.buckled]!") - shake_camera(M, 3, 1) + // Move, gib, or delete everything in our way! + for(var/turf/src_turf in turf_translation) + var/turf/dst_turf = turf_translation[src_turf] + if(src_turf.is_solid_structure()) // in case someone put a hole in the shuttle and you were lucky enough to be under it + for(var/atom/movable/AM in dst_turf) + //if(AM.movable_flags & MOVABLE_FLAG_DEL_SHUTTLE) + // qdel(AM) + // continue + if(!AM.simulated) + continue + if(isliving(AM)) + var/mob/living/bug = AM + bug.gib() else - to_chat(M, "The floor lurches beneath you!") - shake_camera(M, 10, 1) - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(3) + qdel(AM) //it just gets atomized I guess? TODO throw it into space somewhere, prevents people from using shuttles as an atom-smasher + + var/list/powernets = list() + for(var/area/A in shuttle_area) + // If there was a zlevel above our origin and we own the ceiling, erase our ceiling now we're leaving + if(ceiling_type && HasAbove(current_location.z)) + for(var/turf/TO in A.contents) + var/turf/TA = GetAbove(TO) + if(istype(TA, ceiling_type)) + TA.ChangeTurf(get_base_turf_by_area(TA), 1, 1) + if(knockdown) + for(var/mob/living/M in A) + spawn(0) + if(M.buckled) + to_chat(M, "Sudden acceleration presses you into \the [M.buckled]!") + shake_camera(M, 3, 1) + else + to_chat(M, "The floor lurches beneath you!") + shake_camera(M, 10, 1) + // TODO - tossing? + //M.visible_message("[M.name] is tossed around by the sudden acceleration!") + //M.throw_at_random(FALSE, 4, 1) + if(istype(M, /mob/living/carbon)) + M.Weaken(3) + // We only need to rebuild powernets for our cables. No need to check machines because they are on top of cables. + for(var/obj/structure/cable/C in A) + powernets |= C.powernet + + // Actually do the movement of everything - This replaces origin.move_contents_to(destination) + translate_turfs(turf_translation, current_location.base_area, current_location.base_turf) + current_location = destination + + // If there's a zlevel above our destination, paint in a ceiling on it so we retain our air + if(ceiling_type && HasAbove(current_location.z)) + for(var/area/A in shuttle_area) + for(var/turf/TD in A.contents) + var/turf/TA = GetAbove(TD) + if(istype(TA, get_base_turf_by_area(TA)) || isopenspace(TA)) + if(get_area(TA) in shuttle_area) + continue + TA.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE) // Power-related checks. If shuttle contains power related machinery, update powernets. - var/update_power = 0 - for(var/obj/machinery/power/P in destination) - update_power = 1 - break + // Note: Old way was to rebuild ALL powernets: if(powernets.len) SSmachines.makepowernets() + // New way only rebuilds the powernets we have to + var/list/cables = list() + for(var/datum/powernet/P in powernets) + cables |= P.cables + qdel(P) + SSmachines.setup_powernets_for_cables(cables) - for(var/obj/structure/cable/C in destination) - update_power = 1 - break - - if(update_power) - SSmachines.makepowernets() return //returns 1 if the shuttle has a valid arrive time /datum/shuttle/proc/has_arrive_time() return (moving_status == SHUTTLE_INTRANSIT) -/datum/shuttle/proc/make_sounds(var/area/A, var/sound_type) +/datum/shuttle/proc/make_sounds(var/sound_type) var/sound_to_play = null switch(sound_type) if(HYPERSPACE_WARMUP) @@ -256,9 +360,29 @@ sound_to_play = 'sound/effects/shuttles/hyperspace_progress.ogg' if(HYPERSPACE_END) sound_to_play = 'sound/effects/shuttles/hyperspace_end.ogg' - for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice - playsound(E, sound_to_play, 50, FALSE) + for(var/area/A in shuttle_area) + for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice + playsound(E, sound_to_play, 50, FALSE) -/datum/shuttle/proc/message_passengers(area/A, var/message) - for(var/mob/M in A) - M.show_message(message, 2) +/datum/shuttle/proc/message_passengers(var/message) + for(var/area/A in shuttle_area) + for(var/mob/M in A) + M.show_message(message, 2) + +/datum/shuttle/proc/find_children() + . = list() + for(var/shuttle_name in SSshuttles.shuttles) + var/datum/shuttle/shuttle = SSshuttles.shuttles[shuttle_name] + if(shuttle.mothershuttle == name) + . += shuttle + +//Returns the areas in shuttle_area that are not actually child shuttles. +/datum/shuttle/proc/find_childfree_areas() + . = shuttle_area.Copy() + for(var/datum/shuttle/child in find_children()) + . -= child.shuttle_area + +/datum/shuttle/proc/get_location_name() + if(moving_status == SHUTTLE_INTRANSIT) + return "In transit" + return current_location.name diff --git a/code/modules/shuttles/shuttle_arrivals.dm b/code/modules/shuttles/shuttle_arrivals.dm index 115d4476d2..a2d71c3fcc 100644 --- a/code/modules/shuttles/shuttle_arrivals.dm +++ b/code/modules/shuttles/shuttle_arrivals.dm @@ -1,15 +1,18 @@ // The new arrivals shuttle. -/datum/shuttle/ferry/arrivals +/datum/shuttle/autodock/ferry/arrivals + category = /datum/shuttle/autodock/ferry/arrivals + name = "Arrivals" - location = 1 + location = FERRY_LOCATION_OFFSITE warmup_time = 25 // Warmup takes 5 seconds, so 30 total. always_process = TRUE var/launch_delay = 3 - area_offsite = /area/shuttle/arrival/pre_game // not really 'pre game' but this area is already defined and unused - area_station = /area/shuttle/arrival/station - docking_controller_tag = "arrivals_shuttle" - dock_target_station = "arrivals_dock" + // Maps must implement their own subtype for their arrivals shuttle, and define at least: + // shuttle_area + // landmark_station (Which should define its dock target) + // landmark_offsite + // docking_controller_tag // For debugging. /obj/machinery/computer/shuttle_control/arrivals @@ -18,36 +21,42 @@ shuttle_tag = "Arrivals" // Unlike most shuttles, the arrivals shuttle is completely automated, so we need to put some additional code here. - +// Process the arrivals shuttle even when idle. +/obj/machinery/computer/shuttle_control/arrivals/process() + var/datum/shuttle/autodock/ferry/arrivals/shuttle = SSshuttles.shuttles[shuttle_tag] + if(shuttle && shuttle.process_state == IDLE_STATE) + shuttle.process() + ..() // This proc checks if anyone is on the shuttle. -/datum/shuttle/ferry/arrivals/proc/check_for_passengers(area/A) - for(var/mob/living/L in A) - return TRUE +/datum/shuttle/autodock/ferry/arrivals/proc/check_for_passengers() + for(var/area/A in shuttle_area) + for(var/mob/living/L in A) + return TRUE return FALSE // This is to stop the shuttle if someone tries to stow away when its leaving. -/datum/shuttle/ferry/arrivals/post_warmup_checks() +/datum/shuttle/autodock/ferry/arrivals/post_warmup_checks() if(!location) // If we're at station. - if(check_for_passengers(area_station)) + if(check_for_passengers()) return FALSE return TRUE -/datum/shuttle/ferry/arrivals/process() +/datum/shuttle/autodock/ferry/arrivals/process() if(process_state == IDLE_STATE) if(location) // If we're off-station (space). - if(check_for_passengers(area_offsite)) // No point arriving with an empty shuttle. + if(check_for_passengers()) // No point arriving with an empty shuttle. warmup_time = initial(warmup_time) launch() - message_passengers(area_offsite, "Arriving at [using_map.station_name] in thirty seconds...") + message_passengers("Arriving at [using_map.station_name] in thirty seconds...") spawn(10 SECONDS) - message_passengers(area_offsite, "Arriving at [using_map.station_name] in twenty seconds.") + message_passengers("Arriving at [using_map.station_name] in twenty seconds.") spawn(10 SECONDS) - message_passengers(area_offsite, "Arriving at [using_map.station_name] in ten seconds. Please buckle up.") + message_passengers("Arriving at [using_map.station_name] in ten seconds. Please buckle up.") else // We are at the station. - if(!check_for_passengers(area_station)) // Don't leave with anyone. + if(!check_for_passengers()) // Don't leave with anyone. if(launch_delay) // Give some time to get on the docks so people don't get trapped inbetween the dock airlocks. launch_delay-- else @@ -58,7 +67,7 @@ ..() // Do everything else /* -/datum/shuttle/ferry/arrivals/current_dock_target() +/datum/shuttle/autodock/ferry/arrivals/current_dock_target() if(location) // If we're off station. return null // Nothing to dock to in space. return ..() diff --git a/code/modules/shuttles/shuttle_autodock.dm b/code/modules/shuttles/shuttle_autodock.dm new file mode 100644 index 0000000000..b9b1801205 --- /dev/null +++ b/code/modules/shuttles/shuttle_autodock.dm @@ -0,0 +1,220 @@ +#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up. + +// Subtype of shuttle that handles docking with docking controllers +// Consists of code pulled down from the old /datum/shuttle and up from /datum/shuttle/ferry +// Note: Since all known shuttles extend this type, this really could just be built into /datum/shuttle +// Why isn't it you ask? Eh, baystation did it this way and its convenient to keep the files smaller I guess. +/datum/shuttle/autodock + var/in_use = null // Tells the controller whether this shuttle needs processing, also attempts to prevent double-use + var/last_dock_attempt_time = 0 + + var/docking_controller_tag = null // ID of the controller on the shuttle (If multiple, this is the default one) + var/datum/computer/file/embedded_program/docking/shuttle_docking_controller // Controller on the shuttle (the one in use) + var/docking_codes + + var/tmp/obj/effect/shuttle_landmark/next_location //This is only used internally. + var/datum/computer/file/embedded_program/docking/active_docking_controller // Controller we are docked with (or trying to) + + var/obj/effect/shuttle_landmark/landmark_transition //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. + var/move_time = 240 //the time spent in the transition area + + category = /datum/shuttle/autodock + flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G + +/datum/shuttle/autodock/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint) + ..(_name, start_waypoint) + + //Initial dock + active_docking_controller = current_location.docking_controller + update_docking_target(current_location) + if(active_docking_controller) + set_docking_codes(active_docking_controller.docking_codes) + else if(global.using_map.use_overmap) + var/obj/effect/overmap/visitable/location = map_sectors["[current_location.z]"] + if(location && location.docking_codes) + set_docking_codes(location.docking_codes) + dock() + + //Optional transition area + if(landmark_transition) + landmark_transition = SSshuttles.get_landmark(landmark_transition) + +/datum/shuttle/autodock/Destroy() + in_use = null + next_location = null + active_docking_controller = null + landmark_transition = null + + return ..() + +/datum/shuttle/autodock/proc/set_docking_codes(var/code) + docking_codes = code + if(shuttle_docking_controller) + shuttle_docking_controller.docking_codes = code + +/datum/shuttle/autodock/perform_shuttle_move() + force_undock() //bye! + ..() + +// Despite the name this actually updates the SHUTTLE docking conroller, not the active. +/datum/shuttle/autodock/proc/update_docking_target(var/obj/effect/shuttle_landmark/location) + var/current_dock_target + if(location && location.special_dock_targets && location.special_dock_targets[name]) + current_dock_target = location.special_dock_targets[name] + else + current_dock_target = docking_controller_tag + shuttle_docking_controller = SSshuttles.docking_registry[current_dock_target] + if(current_dock_target && !shuttle_docking_controller) + to_world("warning: shuttle [src] can't find its controller with tag [current_dock_target]!") +/* + Docking stuff +*/ +/datum/shuttle/autodock/dock() + if(active_docking_controller && shuttle_docking_controller) + shuttle_docking_controller.initiate_docking(active_docking_controller.id_tag) + last_dock_attempt_time = world.time + +/datum/shuttle/autodock/undock() + if(shuttle_docking_controller) + shuttle_docking_controller.initiate_undocking() + +/datum/shuttle/autodock/force_undock() + if(shuttle_docking_controller) + shuttle_docking_controller.force_undock() + +/datum/shuttle/autodock/check_docked() + if(shuttle_docking_controller) + return shuttle_docking_controller.docked() + return TRUE + +/datum/shuttle/autodock/check_undocked() + if(shuttle_docking_controller) + return shuttle_docking_controller.can_launch() + return TRUE + +// You also could just directly reference active_docking_controller +/datum/shuttle/autodock/proc/current_dock_target() + if(active_docking_controller) + return active_docking_controller.id_tag + return null + +// These checks are built into the check_docked() and check_undocked() procs +/datum/shuttle/autodock/proc/skip_docking_checks() + if (!shuttle_docking_controller || !current_dock_target()) + return TRUE //shuttles without docking controllers or at locations without docking ports act like old-style shuttles + return FALSE + + +/* + Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well. + Doing so will ensure that multiple jumps cannot be initiated in parallel. +*/ +/datum/shuttle/autodock/process() + switch(process_state) + if (WAIT_LAUNCH) + if(check_undocked()) + //*** ready to go + process_launch() + + if (FORCE_LAUNCH) + process_launch() + + if (WAIT_ARRIVE) + if (moving_status == SHUTTLE_IDLE) + //*** we made it to the destination, update stuff + process_arrived() + process_state = WAIT_FINISH + + if (WAIT_FINISH) + if (world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT || check_docked()) + //*** all done here + process_state = IDLE_STATE + arrived() + +//not to be confused with the arrived() proc +/datum/shuttle/autodock/proc/process_arrived() + active_docking_controller = next_location.docking_controller + update_docking_target(next_location) + dock() + + next_location = null + in_use = null //release lock + +/datum/shuttle/autodock/proc/get_travel_time() + return move_time + +/datum/shuttle/autodock/proc/process_launch() + if(!next_location || !next_location.is_valid(src) || current_location.cannot_depart(src)) + process_state = IDLE_STATE + in_use = null + return + if (get_travel_time() && landmark_transition) + . = long_jump(next_location, landmark_transition, get_travel_time()) + else + . = short_jump(next_location) + process_state = WAIT_ARRIVE + +/* + Guards - (These don't take docking status into account, just the state machine and move safety) +*/ +/datum/shuttle/autodock/proc/can_launch() + return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && !in_use) + +/datum/shuttle/autodock/proc/can_force() + return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH) + +/datum/shuttle/autodock/proc/can_cancel() + return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) + +/* + "Public" procs +*/ +// Queue shuttle for undock and launch by shuttle subsystem. +/datum/shuttle/autodock/proc/launch(var/user) + if (!can_launch()) return + + in_use = user //obtain an exclusive lock on the shuttle + + process_state = WAIT_LAUNCH + undock() + +// Queue shuttle for forced undock and launch by shuttle subsystem. +/datum/shuttle/autodock/proc/force_launch(var/user) + if (!can_force()) return + + in_use = user //obtain an exclusive lock on the shuttle + + process_state = FORCE_LAUNCH + +// Cancel queued launch. +/datum/shuttle/autodock/cancel_launch(var/user) + if (!can_cancel()) return + + moving_status = SHUTTLE_IDLE + process_state = WAIT_FINISH + in_use = null + + //whatever we were doing with docking: stop it, then redock + force_undock() + spawn(1 SECOND) + dock() + +//returns 1 if the shuttle is getting ready to move, but is not in transit yet +/datum/shuttle/autodock/proc/is_launching() + return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) + +// /datum/shuttle/autodock/get_location_name() defined in shuttle.dm + +/datum/shuttle/autodock/proc/get_destination_name() + if(!next_location) + return "None" + return next_location.name + +//This gets called when the shuttle finishes arriving at it's destination +//This can be used by subtypes to do things when the shuttle arrives. +//Note that this is called when the shuttle leaves the WAIT_FINISHED state, the proc name is a little misleading +/datum/shuttle/autodock/proc/arrived() + return //do nothing for now + +/obj/effect/shuttle_landmark/transit + flags = SLANDMARK_FLAG_ZERO_G|SLANDMARK_FLAG_AUTOSET diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index be9cec13af..80086b1b47 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -8,23 +8,20 @@ var/shuttle_tag // Used to coordinate data in shuttle controller. var/hacked = 0 // Has been emagged, no access restrictions. + var/ui_template = "shuttle_control_console.tmpl" + /obj/machinery/computer/shuttle_control/attack_hand(user as mob) if(..(user)) return //src.add_fingerprint(user) //shouldn't need fingerprints just for looking at it. if(!allowed(user)) - to_chat(user, "Access Denied.") + to_chat(user, "Access Denied.") return 1 ui_interact(user) -/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return - +/obj/machinery/computer/shuttle_control/proc/get_ui_data(var/datum/shuttle/autodock/shuttle) var/shuttle_state switch(shuttle.moving_status) if(SHUTTLE_IDLE) shuttle_state = "idle" @@ -34,55 +31,100 @@ var/shuttle_status switch (shuttle.process_state) if(IDLE_STATE) + var/cannot_depart = shuttle.current_location.cannot_depart(shuttle) if (shuttle.in_use) shuttle_status = "Busy." - else if (!shuttle.location) - shuttle_status = "Standing-by at station." + else if(cannot_depart) + shuttle_status = cannot_depart else - shuttle_status = "Standing-by at offsite location." + shuttle_status = "Standing-by at \the [shuttle.get_location_name()]." + if(WAIT_LAUNCH, FORCE_LAUNCH) shuttle_status = "Shuttle has received command and will depart shortly." if(WAIT_ARRIVE) - shuttle_status = "Proceeding to destination." + shuttle_status = "Proceeding to \the [shuttle.get_destination_name()]." if(WAIT_FINISH) shuttle_status = "Arriving at destination now." - data = list( + return list( "shuttle_status" = shuttle_status, "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "has_docking" = shuttle.shuttle_docking_controller ? 1 : 0, + "docking_status" = shuttle.shuttle_docking_controller?.get_docking_status(), + "docking_override" = shuttle.shuttle_docking_controller?.override_enabled, "can_launch" = shuttle.can_launch(), "can_cancel" = shuttle.can_cancel(), "can_force" = shuttle.can_force(), + "docking_codes" = shuttle.docking_codes ) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - ui = new(user, src, ui_key, "shuttle_control_console.tmpl", "[shuttle_tag] Shuttle Control", 470, 310) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) +// This is a subset of the actual checks; contains those that give messages to the user. +// This enables us to give nice error messages as well as not even bother proceeding if we can't. +/obj/machinery/computer/shuttle_control/proc/can_move(var/datum/shuttle/autodock/shuttle, var/user) + var/cannot_depart = shuttle.current_location.cannot_depart(shuttle) + if(cannot_depart) + to_chat(user, "[cannot_depart]") + log_shuttle("Shuttle [shuttle] cannot depart [shuttle.current_location] because: [cannot_depart].") + return FALSE + if(!shuttle.next_location.is_valid(shuttle)) + to_chat(user, "Destination zone is invalid or obstructed.") + log_shuttle("Shuttle [shuttle] destination [shuttle.next_location] is invalid.") + return FALSE + return TRUE /obj/machinery/computer/shuttle_control/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return usr.set_machine(src) src.add_fingerprint(usr) - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return + var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag] + if(!shuttle) + to_chat(usr, "Unable to establish link with the shuttle.") + return handle_topic_href(shuttle, href_list, usr) + +/obj/machinery/computer/shuttle_control/proc/handle_topic_href(var/datum/shuttle/autodock/shuttle, var/list/href_list, var/user) + if(!istype(shuttle)) + return TOPIC_NOACTION if(href_list["move"]) - shuttle.launch(src) + if(can_move(shuttle, user)) + shuttle.launch(src) + return TOPIC_REFRESH + return TOPIC_HANDLED + if(href_list["force"]) - shuttle.force_launch(src) - else if(href_list["cancel"]) + if(can_move(shuttle, user)) + shuttle.force_launch(src) + return TOPIC_REFRESH + return TOPIC_HANDLED + + if(href_list["cancel"]) shuttle.cancel_launch(src) + return TOPIC_REFRESH + + if(href_list["set_codes"]) + var/newcode = input("Input new docking codes", "Docking codes", shuttle.docking_codes) as text|null + if (newcode && CanInteract(usr, global.default_state)) + shuttle.set_docking_codes(uppertext(newcode)) + return TOPIC_REFRESH + +// We delegate populating data to another proc to make it easier for overriding types to add their data. +/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag] + if (!istype(shuttle)) + to_chat(user, "Unable to establish link with the shuttle.") + return + + var/list/data = get_ui_data(shuttle) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 310) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) /obj/machinery/computer/shuttle_control/emag_act(var/remaining_charges, var/mob/user) if (!hacked) diff --git a/code/modules/shuttles/shuttle_console_multi.dm b/code/modules/shuttles/shuttle_console_multi.dm new file mode 100644 index 0000000000..9d6dccad56 --- /dev/null +++ b/code/modules/shuttles/shuttle_console_multi.dm @@ -0,0 +1,34 @@ +/obj/machinery/computer/shuttle_control/multi + ui_template = "shuttle_control_console_multi.tmpl" + +/obj/machinery/computer/shuttle_control/multi/get_ui_data(var/datum/shuttle/autodock/multi/shuttle) + . = ..() + if(istype(shuttle)) + . += list( + "destination_name" = shuttle.next_location ? shuttle.next_location.name : "No destination set.", + "can_pick" = shuttle.moving_status == SHUTTLE_IDLE, + "can_cloak" = shuttle.can_cloak ? 1 : 0, + "cloaked" = shuttle.cloaked ? 1 : 0, + "legit" = shuttle.legit ? 1 : 0, + // "engines_charging" = ((shuttle.last_move + (shuttle.cooldown SECONDS)) > world.time), // Replaced by longer warmup_time + ) + +/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list) + if((. = ..()) != null) + return + + if(href_list["pick"]) + var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations() + if(dest_key && CanInteract(usr, global.default_state)) + shuttle.set_destination(dest_key, usr) + return TOPIC_REFRESH + + if(href_list["toggle_cloaked"]) + if(!shuttle.can_cloak) + return TOPIC_HANDLED + shuttle.cloaked = !shuttle.cloaked + if(shuttle.legit) + to_chat(usr, "Ship ATC inhibitor systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be notified of our arrival.") + else + to_chat(usr, "Ship stealth systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.") + return TOPIC_REFRESH diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm index 1722374a1d..4310b9e6ce 100644 --- a/code/modules/shuttles/shuttle_emergency.dm +++ b/code/modules/shuttles/shuttle_emergency.dm @@ -1,21 +1,22 @@ -/datum/shuttle/ferry/emergency - category = /datum/shuttle/ferry/emergency +// Formerly /datum/shuttle/ferry/emergency +/datum/shuttle/autodock/ferry/emergency + category = /datum/shuttle/autodock/ferry/emergency -/datum/shuttle/ferry/emergency/New() +/datum/shuttle/autodock/ferry/emergency/New() + ..() if(emergency_shuttle.shuttle) CRASH("An emergency shuttle has already been defined.") emergency_shuttle.shuttle = src - ..() -/datum/shuttle/ferry/emergency/arrived() +/datum/shuttle/autodock/ferry/emergency/arrived() + . = ..() if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = in_use C.reset_authorization() emergency_shuttle.shuttle_arrived() -/datum/shuttle/ferry/emergency/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //to_world("shuttle/ferry/emergency/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]") +/datum/shuttle/autodock/ferry/emergency/long_jump(var/destination, var/interim, var/travel_time) if (!location) travel_time = SHUTTLE_TRANSIT_DURATION_RETURN else @@ -25,28 +26,28 @@ move_time = travel_time emergency_shuttle.launch_time = world.time + ..(destination, interim, travel_time, direction) + +/datum/shuttle/autodock/ferry/emergency/perform_shuttle_move() + if (current_location == landmark_station) //leaving the station + spawn(0) + emergency_shuttle.departed = 1 + var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1) + + if (emergency_shuttle.evac) + priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) + else + priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) ..() -/datum/shuttle/ferry/emergency/move(var/area/origin,var/area/destination) - ..(origin, destination) - - if (origin == area_station) //leaving the station - emergency_shuttle.departed = 1 - var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1) - - if (emergency_shuttle.evac) - priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) - else - priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) - -/datum/shuttle/ferry/emergency/can_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/can_launch(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user if (!C.has_authorization()) return 0 return ..() -/datum/shuttle/ferry/emergency/can_force(var/user) +/datum/shuttle/autodock/ferry/emergency/can_force(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user @@ -56,14 +57,14 @@ return 0 return ..() -/datum/shuttle/ferry/emergency/can_cancel(var/user) +/datum/shuttle/autodock/ferry/emergency/can_cancel(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user if (!C.has_authorization()) return 0 return ..() -/datum/shuttle/ferry/emergency/launch(var/user) +/datum/shuttle/autodock/ferry/emergency/launch(var/user) if (!can_launch(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -77,7 +78,7 @@ ..(user) -/datum/shuttle/ferry/emergency/force_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/force_launch(var/user) if (!can_force(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -91,7 +92,7 @@ ..(user) -/datum/shuttle/ferry/emergency/cancel_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/cancel_launch(var/user) if (!can_cancel(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -177,7 +178,7 @@ /obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] - var/datum/shuttle/ferry/emergency/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/ferry/emergency/shuttle = SSshuttles.shuttles[shuttle_tag] if (!istype(shuttle)) return @@ -222,9 +223,9 @@ data = list( "shuttle_status" = shuttle_status, "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "has_docking" = shuttle.active_docking_controller? 1 : 0, + "docking_status" = shuttle.active_docking_controller? shuttle.active_docking_controller.get_docking_status() : null, + "docking_override" = shuttle.active_docking_controller? shuttle.active_docking_controller.override_enabled : null, "can_launch" = shuttle.can_launch(src), "can_cancel" = shuttle.can_cancel(src), "can_force" = shuttle.can_force(src), diff --git a/code/modules/shuttles/shuttle_ferry.dm b/code/modules/shuttles/shuttle_ferry.dm index d96051cc46..a5876f55dd 100644 --- a/code/modules/shuttles/shuttle_ferry.dm +++ b/code/modules/shuttles/shuttle_ferry.dm @@ -1,177 +1,50 @@ #define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up. -/datum/shuttle/ferry - var/location = 0 //0 = at area_station, 1 = at area_offsite - var/direction = 0 //0 = going to station, 1 = going to offsite. - var/process_state = IDLE_STATE - var/always_process = FALSE +/datum/shuttle/autodock/ferry + var/location = FERRY_LOCATION_STATION //0 = at area_station, 1 = at area_offsite + var/direction = FERRY_GOING_TO_STATION //0 = going to station, 1 = going to offsite. - var/in_use = null //tells the controller whether this shuttle needs processing + var/always_process = FALSE // TODO -why should this exist? - var/area_transition - var/move_time = 0 //the time spent in the transition area - var/transit_direction = null //needed for area/move_contents_to() to properly handle shuttle corners - not exactly sure how it works. + var/obj/effect/shuttle_landmark/landmark_station //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. + var/obj/effect/shuttle_landmark/landmark_offsite //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. - var/area/area_station - var/area/area_offsite - //TODO: change location to a string and use a mapping for area and dock targets. - var/dock_target_station - var/dock_target_offsite + category = /datum/shuttle/autodock/ferry - var/last_dock_attempt_time = 0 - category = /datum/shuttle/ferry +/datum/shuttle/autodock/ferry/New(var/_name) + if(landmark_station) + landmark_station = SSshuttles.get_landmark(landmark_station) + if(landmark_offsite) + landmark_offsite = SSshuttles.get_landmark(landmark_offsite) -/datum/shuttle/ferry/New() - area_offsite = locate(area_offsite) - area_station = locate(area_station) - if(area_transition) - area_transition = locate(area_transition) - ..() + ..(_name, get_location_waypoint(location)) -/datum/shuttle/ferry/short_jump(var/area/origin,var/area/destination) - if(isnull(location)) - return + next_location = get_location_waypoint(!location) - if(!destination) - destination = get_location_area(!location) - if(!origin) - origin = get_location_area(location) - direction = !location - ..(origin, destination) - -/datum/shuttle/ferry/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //to_world("shuttle/ferry/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]") - if(isnull(location)) - return - - if(!destination) - destination = get_location_area(!location) - if(!departing) - departing = get_location_area(location) - - direction = !location - ..(departing, destination, interim, travel_time, direction) - -/datum/shuttle/ferry/move(var/area/origin,var/area/destination) - ..(origin, destination) - - if (destination == area_station) location = 0 - if (destination == area_offsite) location = 1 - //if this is a long_jump retain the location we were last at until we get to the new one - -/datum/shuttle/ferry/dock() - ..() - last_dock_attempt_time = world.time - -/datum/shuttle/ferry/proc/get_location_area(location_id = null) +//Gets the shuttle landmark associated with the given location (defaults to current location) +/datum/shuttle/autodock/ferry/proc/get_location_waypoint(location_id = null) if (isnull(location_id)) location_id = location - if (!location_id) - return area_station - return area_offsite + if (location_id == FERRY_LOCATION_STATION) + return landmark_station + return landmark_offsite -/* - Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well. - Doing so will ensure that multiple jumps cannot be initiated in parallel. -*/ -/datum/shuttle/ferry/process() - switch(process_state) - if (WAIT_LAUNCH) - if (skip_docking_checks() || docking_controller.can_launch()) +/datum/shuttle/autodock/ferry/short_jump(var/destination) + direction = !location // Heading away from where we currently are + . = ..() - //to_world("shuttle/ferry/process: area_transition=[area_transition], travel_time=[travel_time]") - if (move_time && area_transition) - long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) - else - short_jump() +/datum/shuttle/autodock/ferry/long_jump(var/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time) + direction = !location // Heading away from where we currently are + . = ..() - process_state = WAIT_ARRIVE - - if (FORCE_LAUNCH) - if (move_time && area_transition) - long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) - else - short_jump() - - process_state = WAIT_ARRIVE - - if (WAIT_ARRIVE) - if (moving_status == SHUTTLE_IDLE) - dock() - in_use = null //release lock - process_state = WAIT_FINISH - - if (WAIT_FINISH) - if (skip_docking_checks() || docking_controller.docked() || world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT) - process_state = IDLE_STATE - arrived() - -/datum/shuttle/ferry/current_dock_target() - var/dock_target - if (!location) //station - dock_target = dock_target_station - else - dock_target = dock_target_offsite - return dock_target - - -/datum/shuttle/ferry/proc/launch(var/user) - if (!can_launch()) return - - in_use = user //obtain an exclusive lock on the shuttle - - process_state = WAIT_LAUNCH - undock() - -/datum/shuttle/ferry/proc/force_launch(var/user) - if (!can_force()) return - - in_use = user //obtain an exclusive lock on the shuttle - - process_state = FORCE_LAUNCH - -/datum/shuttle/ferry/proc/cancel_launch(var/user) - if (!can_cancel()) return - - moving_status = SHUTTLE_IDLE - process_state = WAIT_FINISH - in_use = null - - if (docking_controller && !docking_controller.undocked()) - docking_controller.force_undock() - - spawn(10) - dock() - - return - -/datum/shuttle/ferry/proc/can_launch() - if (moving_status != SHUTTLE_IDLE) - return 0 - - if (in_use) - return 0 - - return 1 - -/datum/shuttle/ferry/proc/can_force() - if (moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH) - return 1 - return 0 - -/datum/shuttle/ferry/proc/can_cancel() - if (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) - return 1 - return 0 - -//returns 1 if the shuttle is getting ready to move, but is not in transit yet -/datum/shuttle/ferry/proc/is_launching() - return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) - -//This gets called when the shuttle finishes arriving at it's destination -//This can be used by subtypes to do things when the shuttle arrives. -/datum/shuttle/ferry/proc/arrived() - return //do nothing for now +/datum/shuttle/autodock/ferry/perform_shuttle_move() + ..() + if (current_location == landmark_station) location = FERRY_LOCATION_STATION + if (current_location == landmark_offsite) location = FERRY_LOCATION_OFFSITE +// Once we have arrived where we are going, plot a course back! +/datum/shuttle/autodock/ferry/process_arrived() + ..() + next_location = get_location_waypoint(!location) diff --git a/code/modules/shuttles/shuttle_specops.dm b/code/modules/shuttles/shuttle_specops.dm index b702d7a78f..0dd88a58e8 100644 --- a/code/modules/shuttles/shuttle_specops.dm +++ b/code/modules/shuttles/shuttle_specops.dm @@ -4,39 +4,11 @@ req_access = list(access_cent_specops) /obj/machinery/computer/shuttle_control/specops/attack_ai(user as mob) - to_chat(user, "Access Denied.") + to_chat(user, "Access Denied.") return 1 -//for shuttles that may use a different docking port at each location -/datum/shuttle/ferry/multidock - var/docking_controller_tag_station - var/docking_controller_tag_offsite - var/datum/computer/file/embedded_program/docking/docking_controller_station - var/datum/computer/file/embedded_program/docking/docking_controller_offsite - category = /datum/shuttle/ferry/multidock - -/datum/shuttle/ferry/multidock/init_docking_controllers() - if(docking_controller_tag_station) - docking_controller_station = locate(docking_controller_tag_station) - if(!istype(docking_controller_station)) - warning("warning: shuttle with docking tag [docking_controller_station] could not find it's controller!") - if(docking_controller_tag_offsite) - docking_controller_offsite = locate(docking_controller_tag_offsite) - if(!istype(docking_controller_offsite)) - warning("warning: shuttle with docking tag [docking_controller_offsite] could not find it's controller!") - if (!location) - docking_controller = docking_controller_station - else - docking_controller = docking_controller_offsite - -/datum/shuttle/ferry/multidock/move(var/area/origin,var/area/destination) - ..(origin, destination) - if (!location) - docking_controller = docking_controller_station - else - docking_controller = docking_controller_offsite - -/datum/shuttle/ferry/multidock/specops +// Formerly /datum/shuttle/ferry/multidock/specops +/datum/shuttle/autodock/ferry/specops var/specops_return_delay = 6000 //After moving, the amount of time that must pass before the shuttle may move again var/specops_countdown_time = 600 //Length of the countdown when moving the shuttle @@ -44,19 +16,19 @@ var/reset_time = 0 //the world.time at which the shuttle will be ready to move again. var/launch_prep = 0 var/cancel_countdown = 0 - category = /datum/shuttle/ferry/multidock/specops + category = /datum/shuttle/autodock/ferry/specops -/datum/shuttle/ferry/multidock/specops/New() +/datum/shuttle/autodock/ferry/specops/New() ..() announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. announcer.config(list("Response Team" = 0)) -/datum/shuttle/ferry/multidock/specops/proc/radio_announce(var/message) +/datum/shuttle/autodock/ferry/specops/proc/radio_announce(var/message) if(announcer) announcer.autosay(message, "A.L.I.C.E.", "Response Team") -/datum/shuttle/ferry/multidock/specops/launch(var/user) +/datum/shuttle/autodock/ferry/specops/launch(var/user) if (!can_launch()) return @@ -64,14 +36,14 @@ var/obj/machinery/computer/C = user if(world.time <= reset_time) - C.visible_message("[using_map.boss_name] will not allow the Special Operations shuttle to launch yet.") + C.visible_message("[global.using_map.boss_name] will not allow the Special Operations shuttle to launch yet.") if (((world.time - reset_time)/10) > 60) - C.visible_message("[-((world.time - reset_time)/10)/60] minutes remain!") + C.visible_message("[-((world.time - reset_time)/10)/60] minutes remain!") else - C.visible_message("[-(world.time - reset_time)/10] seconds remain!") + C.visible_message("[-(world.time - reset_time)/10] seconds remain!") return - C.visible_message("The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.") + C.visible_message("The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.") if (location) //returning radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN") @@ -81,31 +53,31 @@ sleep_until_launch() if (location) - var/obj/machinery/light/small/readylight/light = locate() in get_location_area() + var/obj/machinery/light/small/readylight/light = locate() in shuttle_area if(light) light.set_state(0) //launch radio_announce("ALERT: INITIATING LAUNCH SEQUENCE") ..(user) -/datum/shuttle/ferry/multidock/specops/move(var/area/origin,var/area/destination) - ..(origin, destination) +/datum/shuttle/autodock/ferry/specops/perform_shuttle_move() + ..() - spawn(20) + spawn(2 SECONDS) if (!location) //just arrived home - for(var/turf/T in get_area_turfs(destination)) + for(var/turf/T in get_area_turfs(shuttle_area)) var/mob/M = locate(/mob) in T to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") else //just left for the station launch_mauraders() - for(var/turf/T in get_area_turfs(destination)) + for(var/turf/T in get_area_turfs(shuttle_area)) var/mob/M = locate(/mob) in T to_chat(M, "You have arrived at [station_name()]. Commence operation!") var/obj/machinery/light/small/readylight/light = locate() in T if(light) light.set_state(1) -/datum/shuttle/ferry/multidock/specops/cancel_launch() +/datum/shuttle/autodock/ferry/specops/cancel_launch() if (!can_cancel()) return @@ -113,27 +85,26 @@ radio_announce("ALERT: LAUNCH SEQUENCE ABORTED") if (istype(in_use, /obj/machinery/computer)) var/obj/machinery/computer/C = in_use - C.visible_message("Launch sequence aborted.") - + C.visible_message("Launch sequence aborted.") ..() -/datum/shuttle/ferry/multidock/specops/can_launch() +/datum/shuttle/autodock/ferry/specops/can_launch() if(launch_prep) return 0 return ..() //should be fine to allow forcing. process_state only becomes WAIT_LAUNCH after the countdown is over. -///datum/shuttle/ferry/multidock/specops/can_force() +///datum/shuttle/autodock/ferry/specops/can_force() // return 0 -/datum/shuttle/ferry/multidock/specops/can_cancel() +/datum/shuttle/autodock/ferry/specops/can_cancel() if(launch_prep) return 1 return ..() -/datum/shuttle/ferry/multidock/specops/proc/sleep_until_launch() +/datum/shuttle/autodock/ferry/specops/proc/sleep_until_launch() var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. var/launch_time = world.time + specops_countdown_time diff --git a/code/modules/shuttles/shuttle_supply.dm b/code/modules/shuttles/shuttle_supply.dm index 7565d7f466..53286ac949 100644 --- a/code/modules/shuttles/shuttle_supply.dm +++ b/code/modules/shuttles/shuttle_supply.dm @@ -1,82 +1,87 @@ -/datum/shuttle/ferry/supply - var/away_location = 1 //the location to hide at while pretending to be in-transit +// Formerly /datum/shuttle/ferry/supply +/datum/shuttle/autodock/ferry/supply + var/away_location = FERRY_LOCATION_OFFSITE //the location to hide at while pretending to be in-transit var/late_chance = 80 var/max_late_time = 300 - category = /datum/shuttle/ferry/supply + flags = SHUTTLE_FLAGS_PROCESS|SHUTTLE_FLAGS_SUPPLY + category = /datum/shuttle/autodock/ferry/supply -/datum/shuttle/ferry/supply/short_jump(var/area/origin,var/area/destination) +/datum/shuttle/autodock/ferry/supply/short_jump(var/obj/effect/shuttle_landmark/destination) if(moving_status != SHUTTLE_IDLE) return if(isnull(location)) return - if(!destination) - destination = get_location_area(!location) - if(!origin) - origin = get_location_area(location) - //it would be cool to play a sound here moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(origin, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) sleep(5 SECONDS) // so the sound finishes. if (moving_status == SHUTTLE_IDLE) - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch if (at_station() && forbidden_atoms_check()) //cancel the launch because of forbidden atoms. announce over supply channel? moving_status = SHUTTLE_IDLE - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return if (!at_station()) //at centcom supply_controller.buy() //We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period. - var/area/away_area = get_location_area(away_location) + var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location) moving_status = SHUTTLE_INTRANSIT - //If we are at the away_area then we are just pretending to move, otherwise actually do the move - if (origin != away_area) - move(origin, away_area) + //If we are at the away_landmark then we are just pretending to move, otherwise actually do the move + if (next_location == away_waypoint) + attempt_move(away_waypoint) //wait ETA here. arrive_time = world.time + supply_controller.movetime while (world.time <= arrive_time) sleep(5) - if (destination != away_area) + if (next_location != away_waypoint) //late if (prob(late_chance)) sleep(rand(0,max_late_time)) - move(away_area, destination) + attempt_move(destination) moving_status = SHUTTLE_IDLE - make_sounds(destination, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) if (!at_station()) //at centcom supply_controller.sell() // returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms -/datum/shuttle/ferry/supply/proc/forbidden_atoms_check() +/datum/shuttle/autodock/ferry/supply/proc/forbidden_atoms_check() if (!at_station()) return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care - return supply_controller.forbidden_atoms_check(get_location_area()) + for(var/area/A in shuttle_area) + if(supply_controller.forbidden_atoms_check(A)) + return 1 -/datum/shuttle/ferry/supply/proc/at_station() +/datum/shuttle/autodock/ferry/supply/proc/at_station() return (!location) //returns 1 if the shuttle is idle and we can still mess with the cargo shopping list -/datum/shuttle/ferry/supply/proc/idle() +/datum/shuttle/autodock/ferry/supply/proc/idle() return (moving_status == SHUTTLE_IDLE) //returns the ETA in minutes -/datum/shuttle/ferry/supply/proc/eta_minutes() +/datum/shuttle/autodock/ferry/supply/proc/eta_minutes() var/ticksleft = arrive_time - world.time return round(ticksleft/600,1) + +// Read the docking codes off of the target to make sure we can always dock. +/datum/shuttle/autodock/ferry/supply/update_docking_target(var/obj/effect/shuttle_landmark/location) + ..() + if(active_docking_controller && active_docking_controller.docking_codes) + set_docking_codes(active_docking_controller.docking_codes) diff --git a/code/modules/shuttles/shuttles_multi.dm b/code/modules/shuttles/shuttles_multi.dm index 8152677615..562a21f4d4 100644 --- a/code/modules/shuttles/shuttles_multi.dm +++ b/code/modules/shuttles/shuttles_multi.dm @@ -1,13 +1,15 @@ //This is a holder for things like the Skipjack and Nuke shuttle. -/datum/shuttle/multi_shuttle +// Formerly /datum/shuttle/multi_shuttle +/datum/shuttle/autodock/multi + var/list/destination_tags + var/list/destinations_cache = list() + var/last_cache_rebuild_time = 0 + category = /datum/shuttle/autodock/multi - flags = SHUTTLE_FLAGS_NONE var/cloaked = FALSE var/can_cloak = FALSE + var/at_origin = 1 - var/returned_home = 0 -// var/move_time = 240 - var/move_time = 60 var/cooldown = 20 var/last_move = 0 //the time at which we last moved @@ -15,238 +17,46 @@ var/arrival_message var/departure_message - var/area/interim - var/area/last_departed var/start_location var/last_location - var/list/destinations - var/list/destination_dock_controller_tags = list() //optional, in case the shuttle has multiple docking ports like the ERT shuttle (even though that isn't a multi_shuttle) - var/list/destination_dock_controllers = list() - var/list/destination_dock_targets = list() - var/area/origin var/return_warning = 0 - category = /datum/shuttle/multi_shuttle + var/legit = FALSE -/datum/shuttle/multi_shuttle/New() - origin = locate(origin) - interim = locate(interim) - for(var/destination in destinations) - destinations[destination] = locate(destinations[destination]) +/datum/shuttle/autodock/multi/New() ..() + start_location = current_location + last_location = current_location -/datum/shuttle/multi_shuttle/init_docking_controllers() - ..() - for(var/destination in destinations) - var/controller_tag = destination_dock_controller_tags[destination] - if(!controller_tag) - destination_dock_controllers[destination] = docking_controller - else - var/datum/computer/file/embedded_program/docking/C = locate(controller_tag) +/datum/shuttle/autodock/multi/proc/set_destination(var/destination_key, mob/user) + if(moving_status != SHUTTLE_IDLE) + return + next_location = destinations_cache[destination_key] + if(!next_location) + warning("Shuttle [src] set to destination we can't find: [destination_key]") - if(!istype(C)) - warning("warning: shuttle with docking tag [controller_tag] could not find it's controller!") - else - destination_dock_controllers[destination] = C +/datum/shuttle/autodock/multi/proc/get_destinations() + if (last_cache_rebuild_time < SSshuttles.last_landmark_registration_time) + build_destinations_cache() + return destinations_cache - //might as well set this up here. - if(origin) last_departed = origin - last_location = start_location +/datum/shuttle/autodock/multi/proc/build_destinations_cache() + last_cache_rebuild_time = world.time + destinations_cache.Cut() + for(var/destination_tag in destination_tags) + var/obj/effect/shuttle_landmark/landmark = SSshuttles.get_landmark(destination_tag) + if (istype(landmark)) + destinations_cache["[landmark.name]"] = landmark -/datum/shuttle/multi_shuttle/current_dock_target() - return destination_dock_targets[last_location] - -/datum/shuttle/multi_shuttle/move(var/area/origin, var/area/destination) +/datum/shuttle/autodock/multi/perform_shuttle_move() ..() last_move = world.time - if (destination == src.origin) - returned_home = 1 - docking_controller = destination_dock_controllers[last_location] - -/datum/shuttle/multi_shuttle/proc/announce_departure() +/datum/shuttle/autodock/multi/proc/announce_departure() if(cloaked || isnull(departure_message)) return + command_announcement.Announce(departure_message, (announcer ? announcer : "[using_map.boss_name]")) - command_announcement.Announce(departure_message,(announcer ? announcer : "[using_map.boss_name]")) - -/datum/shuttle/multi_shuttle/proc/announce_arrival() - +/datum/shuttle/autodock/multi/proc/announce_arrival() if(cloaked || isnull(arrival_message)) return - - command_announcement.Announce(arrival_message,(announcer ? announcer : "[using_map.boss_name]")) - - -/obj/machinery/computer/shuttle_control/multi - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - -/obj/machinery/computer/shuttle_control/multi/attack_hand(user as mob) - - if(..(user)) - return - src.add_fingerprint(user) - - var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] - if(!istype(MS)) return - - var/dat - dat = "
[shuttle_tag] Ship Control
" - - - if(MS.moving_status != SHUTTLE_IDLE) - dat += "Location: Moving
" - else - var/area/areacheck = get_area(src) - dat += "Location: [areacheck.name]
" - - if((MS.last_move + MS.cooldown*10) > world.time) - dat += "Engines charging.
" - else - dat += "Engines ready.
" - - if(MS.can_cloak) - dat += "
Toggle cloaking field
" - dat += "Move ship
" - dat += "Return to base
" - - //Docking - dat += "


" - if(MS.skip_docking_checks()) - dat += "Docking Status: Not in use." - else - var/override_en = MS.docking_controller.override_enabled - var/docking_status = MS.docking_controller.get_docking_status() - - dat += "Docking Status: " - switch(docking_status) - if("undocked") - dat += "Undocked" - if("docking") - dat += "Docking" - if("undocking") - dat += "Undocking" - if("docked") - dat += "Docked" - - if(override_en) dat += " (Override Enabled)" - - dat += ". \[Refresh\]

" - - switch(docking_status) - if("undocked") - dat += "Dock" - if("docked") - dat += "Undock" - dat += "
" - - user << browse("[dat]", "window=[shuttle_tag]shuttlecontrol;size=300x600") - -//check if we're undocked, give option to force launch -/obj/machinery/computer/shuttle_control/proc/check_docking(datum/shuttle/multi_shuttle/MS) - if(MS.skip_docking_checks() || MS.docking_controller.can_launch()) - return 1 - - var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch") - if(choice == "Cancel") - return 0 - - choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel") - if(choice == "Cancel") - return 0 - - return 1 - -/obj/machinery/computer/shuttle_control/multi/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) - - var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] - if(!istype(MS)) return - - //to_world("multi_shuttle: last_departed=[MS.last_departed], origin=[MS.origin], interim=[MS.interim], travel_time=[MS.move_time]") - - if(href_list["refresh"]) - updateUsrDialog() - return - - if (MS.moving_status != SHUTTLE_IDLE) - to_chat(usr, "[shuttle_tag] vessel is moving.") - return - - if(href_list["dock_command"]) - MS.dock() - return - - if(href_list["undock_command"]) - MS.undock() - return - - if(href_list["start"]) - if(MS.at_origin) - to_chat(usr, "You are already at the home base.") - return - - if((MS.last_move + MS.cooldown*10) > world.time) - to_chat(usr, "The ship's drive is inoperable while the engines are charging.") - return - - if(!check_docking(MS)) - updateUsrDialog() - return - - // No point giving a warning if it does literally nothing. -// if(!MS.return_warning) -// to_chat(usr, "Returning to your home base will end your mission. If you are sure, press the button again.") -// //TODO: Actually end the mission. -// MS.return_warning = 1 -// return - - MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time) - MS.last_departed = MS.origin - MS.last_location = MS.start_location - MS.at_origin = 1 - - if(href_list["toggle_cloak"]) - if(!MS.can_cloak) - return - MS.cloaked = !MS.cloaked - to_chat(usr, "Ship stealth systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.") - - if(href_list["move_multi"]) - if((MS.last_move + MS.cooldown*10) > world.time) - to_chat(usr, "The ship's drive is inoperable while the engines are charging.") - return - - if(!check_docking(MS)) - updateUsrDialog() - return - - var/choice = input("Select a destination.") as null|anything in MS.destinations - if(!choice) return - - to_chat(usr, "[shuttle_tag] main computer received message.") - - if(MS.at_origin) - MS.announce_arrival() - MS.last_departed = MS.origin - MS.at_origin = 0 - - - MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time) - MS.last_departed = MS.destinations[choice] - MS.last_location = choice - return - - else if(choice == MS.origin) - - MS.announce_departure() - - MS.short_jump(MS.last_departed, MS.destinations[choice]) - MS.last_departed = MS.destinations[choice] - MS.last_location = choice - - updateUsrDialog() + command_announcement.Announce(arrival_message, (announcer ? announcer : "[using_map.boss_name]")) diff --git a/code/modules/shuttles/shuttles_web.dm b/code/modules/shuttles/shuttles_web.dm index e0b016d2f6..968911ee5c 100644 --- a/code/modules/shuttles/shuttles_web.dm +++ b/code/modules/shuttles/shuttles_web.dm @@ -1,12 +1,11 @@ //This shuttle traverses a "web" of route_datums to have a wider range of places to go and make flying feel like movement is actually occuring. -/datum/shuttle/web_shuttle - flags = SHUTTLE_FLAGS_NONE +/datum/shuttle/autodock/web_shuttle + flags = SHUTTLE_FLAGS_ZERO_G var/visible_name = null // The pretty name shown to people in announcements, since the regular name var is used internally for other things. var/cloaked = FALSE var/can_cloak = FALSE var/cooldown = 0 var/last_move = 0 //the time at which we last moved - var/area/current_area = null var/datum/shuttle_web_master/web_master = null var/web_master_type = null var/flight_time_modifier = 1.0 @@ -15,15 +14,15 @@ var/autopilot_delay = 60 // How many ticks to not do anything when not following an autopath. Should equal two minutes. var/autopilot_first_delay = null // If your want your shuttle to stay for a different amount of time for the first time, set this. var/can_rename = TRUE // Lets the pilot rename the shuttle. Only available once. - category = /datum/shuttle/web_shuttle + category = /datum/shuttle/autodock/web_shuttle var/list/obj/item/clothing/head/pilot/helmets -/datum/shuttle/web_shuttle/New() - current_area = locate(current_area) +/datum/shuttle/autodock/web_shuttle/New() web_master = new web_master_type(src) build_destinations() if(autopilot) flags |= SHUTTLE_FLAGS_PROCESS + process_state = DO_AUTOPILOT if(autopilot_first_delay) autopilot_delay = autopilot_first_delay if(!visible_name) @@ -31,42 +30,45 @@ helmets = list() ..() -/datum/shuttle/web_shuttle/Destroy() - qdel(web_master) +/datum/shuttle/autodock/web_shuttle/Destroy() + QDEL_NULL(web_master) helmets.Cut() return ..() +/datum/shuttle/autodock/web_shuttle/current_dock_target() + // TODO - Probably don't even need to override this right? Debug testing code below will check! + . = web_master?.get_current_destination()?.my_landmark?.docking_controller?.id_tag + if (. != ..()) + warning("Web shuttle [src] had current_dock_target()=[.] but autodock.current_dock_target() = [..()]") -/datum/shuttle/web_shuttle/current_dock_target() - if(web_master) - return web_master.current_dock_target() - -/datum/shuttle/web_shuttle/move(var/area/origin, var/area/destination) +/datum/shuttle/autodock/web_shuttle/perform_shuttle_move() ..() last_move = world.time -/datum/shuttle/web_shuttle/short_jump() +/datum/shuttle/autodock/web_shuttle/short_jump() . = ..() update_helmets() -/datum/shuttle/web_shuttle/long_jump() +/datum/shuttle/autodock/web_shuttle/long_jump() . = ..() update_helmets() -/datum/shuttle/web_shuttle/on_shuttle_departure() +/datum/shuttle/autodock/web_shuttle/on_shuttle_departure() . = ..() web_master.on_shuttle_departure() update_helmets() -/datum/shuttle/web_shuttle/on_shuttle_arrival() +/datum/shuttle/autodock/web_shuttle/on_shuttle_arrival() . = ..() + active_docking_controller = current_location.docking_controller + update_docking_target(current_location) web_master.on_shuttle_arrival() update_helmets() -/datum/shuttle/web_shuttle/proc/build_destinations() +/datum/shuttle/autodock/web_shuttle/proc/build_destinations() return -/datum/shuttle/web_shuttle/process() +/datum/shuttle/autodock/web_shuttle/process() update_helmets() if(moving_status == SHUTTLE_IDLE) @@ -76,8 +78,8 @@ else // Otherwise we are about to start one or just finished one. if(autopilot_delay > 0) // Wait for awhile so people can get on and off. - if(docking_controller && !skip_docking_checks()) // Dock to the destination if possible. - var/docking_status = docking_controller.get_docking_status() + if(active_docking_controller && shuttle_docking_controller) // Dock to the destination if possible. + var/docking_status = shuttle_docking_controller.get_docking_status() if(docking_status == "undocked") dock() autopilot_say("Docking.") @@ -96,8 +98,8 @@ autopilot_delay-- else // Time to go. - if(docking_controller && !skip_docking_checks()) // Undock if possible. - var/docking_status = docking_controller.get_docking_status() + if(active_docking_controller && shuttle_docking_controller) // Undock if possible. + var/docking_status = shuttle_docking_controller.get_docking_status() if(docking_status == "docked") undock() autopilot_say("Undocking.") @@ -109,13 +111,13 @@ autopilot_say("Taking off.") web_master.process_autopath() -/datum/shuttle/web_shuttle/proc/update_helmets() +/datum/shuttle/autodock/web_shuttle/proc/update_helmets() for(var/helm in helmets) - if(!helm) - helmets -= helm - continue var/obj/item/clothing/head/pilot/H = helm - if(!H.shuttle_comp || get_area(H.shuttle_comp) != get_area(H)) + if(QDELETED(H)) + helmets -= H + continue + if(!H.shuttle_comp || !(get_area(H) in shuttle_area)) H.shuttle_comp = null H.audible_message("\The [H] pings as it loses it's connection with the ship.") H.update_hud("discon") @@ -123,24 +125,28 @@ else H.update_hud(moving_status) -/datum/shuttle/web_shuttle/proc/adjust_autopilot(on) +/datum/shuttle/autodock/web_shuttle/proc/adjust_autopilot(on) if(on) if(autopilot) return autopilot = TRUE autopilot_delay = initial(autopilot_delay) - shuttle_controller.process_shuttles += src + shuttle_controller.process_shuttles |= src + if(process_state == IDLE_STATE) + process_state = DO_AUTOPILOT else if(!autopilot) return autopilot = FALSE shuttle_controller.process_shuttles -= src + if (process_state == DO_AUTOPILOT) + process_state = initial(process_state) -/datum/shuttle/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers. +/datum/shuttle/autodock/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers. var/padded_message = "shuttle autopilot states, \"[message]\"" - message_passengers(current_area, padded_message) + message_passengers(padded_message) -/datum/shuttle/web_shuttle/proc/rename_shuttle(mob/user) +/datum/shuttle/autodock/web_shuttle/proc/rename_shuttle(mob/user) if(!can_rename) to_chat(user, "You can't rename this vessel.") return @@ -162,6 +168,8 @@ var/list/my_doors //Should be list("id_tag" = "Pretty Door Name", ...) var/list/my_sensors //Should be list("id_tag" = "Pretty Sensor Name", ...) +// Note - Searching own area for doors/sensors is fine for legacy web shuttles as they are single-area. +// However if this code is copied to future multi-area shuttles, should search in all shuttle areas /obj/machinery/computer/shuttle_control/web/Initialize() . = ..() var/area/my_area = get_area(src) @@ -186,7 +194,7 @@ log_debug("[my_area] shuttle computer couldn't find [lost] sensor!") /obj/machinery/computer/shuttle_control/web/attackby(obj/I, mob/user) - var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] if(shuttle && istype(I,/obj/item/clothing/head/pilot)) var/obj/item/clothing/head/pilot/H = I H.shuttle_comp = src @@ -208,7 +216,7 @@ /* // If nanoUI falls over and you want a non-nanoUI UI, feel free to uncomment this section. - var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] if(!istype(WS)) message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.") return @@ -272,8 +280,9 @@ /obj/machinery/computer/shuttle_control/web/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] var/list/routes[0] - var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/shuttle = SSshuttles.shuttles[shuttle_tag] if(!istype(shuttle)) + to_chat(user, "Unable to establish link with the shuttle.") return var/list/R = shuttle.web_master.get_available_routes() @@ -333,11 +342,11 @@ "future_location" = future_location, "shuttle_state" = shuttle_state, "routes" = routes, - "has_docking" = shuttle.docking_controller? 1 : 0, + "has_docking" = shuttle.shuttle_docking_controller? 1 : 0, "skip_docking" = shuttle.skip_docking_checks(), "is_moving" = shuttle.moving_status != SHUTTLE_IDLE, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "docking_status" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.get_docking_status() : null, + "docking_override" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.override_enabled : null, "is_in_transit" = shuttle.has_arrive_time(), "travel_progress" = between(0, percent_finished, 100), "time_left" = round( (total_time - elapsed_time) / 10), @@ -360,13 +369,13 @@ /obj/machinery/computer/shuttle_control/web/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return usr.set_machine(src) src.add_fingerprint(usr) - var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/WS = SSshuttles.shuttles[shuttle_tag] if(!istype(WS)) message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.") return @@ -436,19 +445,38 @@ message_admins("ERROR: Shuttle computer was asked to travel to a nonexistant destination.") return + WS.next_location = target_destination.my_landmark + if(!can_move(WS, usr)) + return + WS.web_master.future_destination = target_destination to_chat(usr, "[WS.visible_name] flight computer received command.") WS.web_master.reset_autopath() // Deviating from the path will almost certainly confuse the autopilot, so lets just reset its memory. var/travel_time = new_route.travel_time * WS.flight_time_modifier - + // TODO - Leshana - Change this to use proccess stuff of autodock! if(new_route.interim && new_route.travel_time) - WS.long_jump(WS.current_area, target_destination.my_area, new_route.interim, travel_time / 10) + WS.long_jump(target_destination.my_landmark, new_route.interim, travel_time / 10) else - WS.short_jump(WS.current_area, target_destination.my_area) + WS.short_jump(target_destination.my_landmark) ui_interact(usr) +//check if we're undocked, give option to force launch +/obj/machinery/computer/shuttle_control/web/proc/check_docking(datum/shuttle/autodock/MS) + if(MS.skip_docking_checks() || MS.check_undocked()) + return 1 + + var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch") + if(choice == "Cancel") + return 0 + + choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel") + if(choice == "Cancel") + return 0 + + return 1 + // Props, for now. /obj/structure/flight_left name = "flight computer meters" @@ -474,11 +502,18 @@ /obj/shuttle_connector/Initialize() . = ..() - SSshuttles.OnDocksInitialized(CALLBACK(src, .proc/setup_routes)) + GLOB.shuttle_added.register_global(src, .proc/setup_routes) -/obj/shuttle_connector/proc/setup_routes() - if(destinations && shuttle_name) - var/datum/shuttle/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name] +/obj/shuttle_connector/Destroy() + GLOB.shuttle_added.unregister_global(src, .proc/setup_routes) + . = ..() + +// This is called whenever a shuttle is initialized. If its our shuttle, do our thing! +/obj/shuttle_connector/proc/setup_routes(var/new_shuttle) + var/datum/shuttle/autodock/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name] + if(ES != new_shuttle) + return // Its not our shuttle! Ignore! + if(destinations && istype(ES)) var/datum/shuttle_web_master/WM = ES.web_master for(var/new_dest in destinations) @@ -487,7 +522,9 @@ for(var/type_to_link in D.routes_to_make) var/travel_delay = D.routes_to_make[type_to_link] - D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay) + D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay) + else + warning("[log_info_line()]'s shuttle [global.log_info_line(ES)] initialized but destinations:[destinations]") qdel(src) diff --git a/code/modules/shuttles/upgrade_guide.md b/code/modules/shuttles/upgrade_guide.md new file mode 100644 index 0000000000..7c2c73f422 --- /dev/null +++ b/code/modules/shuttles/upgrade_guide.md @@ -0,0 +1,124 @@ +# "Landmark" Shuttles Conversion +This guide helps with updating maps and shuttle datums from the old "area" based system to the "landmark" based system. + +## Summary +The old shuttle datum worked with areas (`/area`). You are probably familiar with every shuttle having a few cookie-cutter shaped areas it travels between. +When the shuttle "moved" it was translated from its current area to the destination area. The areas had to be _exactly_ the same shape so +that each turf in the origin area got translated to the equivalent place at the target.\ +Since _all possible_ destinations had to have a dedicated area (and areas in BYOND can't overlap) this means it is impossible for two shuttles to travel to the same spot, even at different times. + +In the new system shuttle destinations are represented by "landmark" objects (`/obj/effect/shuttle_landmark`). +When a shuttle is "moved" it is translated from its current landmark to the destination landmark, with each turf keeping its same position _relative_ to the landmarks. +In other words, whatever a turf's x/y/z offsets are from the origin landmark, it will be moved to the same x/y/z offset from the destination landmark. + +## Landmark Objects +Shuttle destinations are represented by `/obj/effect/shuttle_landmark` objects on the map. + +* `name` - Pretty name of the nav point, used on overmap and in messages and console UI. +* `landmark_tag` - Globally unique ID, used by everything else to refer to this landmark. +* `docking_controller` - ID of the controller on the dock side (initialize to id_tag, becomes reference). Leave null if not applicable. +* `base_area` - Type path of the `/area` that should be here when a shuttle is *not* present. +* `base_turf` - Type path of the `/turf` that should be here when a shuttle is *not* present. +* `shuttle_restricted` - If not null, only the named shuttle is allowed to use this landmark. (TODO: Overmap functionality) +* `flags` - Bitfield - defaults to `SLANDMARK_FLAG_AUTOSET`, can be any combination of: + * `SLANDMARK_FLAG_AUTOSET` (1) - If set, will initialize base_area and base_turf to same as where it was spawned at. + * `SLANDMARK_FLAG_ZERO_G` (2) - If set, Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. +* `special_dock_targets` - Used to configure shuttles with multiple docking controllers on the shuttle. Map of shuttle `name` -> `id_tag` of the docking controller it should use for this landmark. (Think of a shuttle with airlocks on both sides, each with their own controller. This would tell it which side to use.) + + +## Shuttle Types + + +### Ferry Shuttles +These shuttles go back and forth between two locations (normally called "station" and "offsite"). +Examples: Mining shuttle, Arrivals Shuttle, etc. + +Old Type Path: `/datum/shuttle/ferry`\ +New Type Path: `/datum/shuttle/autodock/ferry` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +area_station |landmark_station |Yes|Tag of the landmark for the "station" location. +area_offsite |landmark_offsite |Yes|Tag of the landmark for the "offsite" location. +area_transition |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump() +dock_target_station |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_station landmark obj. +dock_target_offsite |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_offsite landmark obj. + + + + +### Multi Shuttles +These shuttles go between a list of configured locations, one of which is its starting location. +Examples: Skipjack, Syndicate Shuttle + +Old Type Path: `/datum/shuttle/multi_shuttle`\ +New Type Path: `/datum/shuttle/autodock/multi` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +origin |current_location |Yes|Tag of the landmark where the shuttle is at startup. +interim |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump() +start_location |N/A |No|No longer necessary, automatically determined from the value of `origin` +destinations |destination_tags |Yes|List of destinations the shuttle can travel to. Used to be associative list of *name* -> *area typepath*, now is normal list of landmark tag ids. Name is now read from the landmark obj. +destination_dock_targets|On landmarks |No|Used to be associative list of *name* -> *id_tag* for which docking controller *on the dock* to use at each destination. This is now specified by the `docking_controller` var on each landmark obj. + + + +### Web Shuttles +These shuttles travel along a network of locations connected by routes. Instead of being able to travel to any of its destinations, it can only travel to destinations connected by a route to its current location. Added by Polaris as an upgrade to Multi Shuttles. +Note: While cool, it is likely that the upcoming "overmap" shuttles will be even cooler, and may eventually replace some web shuttles. +Examples: Southern Cross' Ninja Shuttle, Tether's Excursion Shuttle + +Old Type Path: `/datum/shuttle/web_shuttle`\ +New Type Path: `/datum/shuttle/autodock/web_shuttle` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +current_area |current_location |Yes|Tag of the landmark where the shuttle is at startup. + +#### Web Destination Configuration (`/datum/shuttle_destination`) +The network of routes for each web shuttle is configured by defining datums. These are mostly unchanged but use landmarks instead of areas now. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +my_area |my_landmark |Yes|Tag of the landmark associated with this destination. +preferred_interim_area |preferred_interim_tag |No|Tag of the landmark for the "transition" location used during long_jump() +dock_target |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the my_landmark landmark obj. + +### Misc Shuttle Types +Other shuttle types that are either unused or unchanged in particular. + +#### Escape Pods +Special case of ferry shuttles that use escape pod berth controllers. +Type path changed from `/datum/shuttle/ferry/escape_pod` to `/datum/shuttle/autodock/ferry/escape_pod` +Follow same instructions as for other ferry shuttles. + +#### Multidock Ferry Shuttles +`/datum/shuttle/ferry/multidock` was a variant of ferry shuttles that could use a different docking port at each location. +Obsolete since is now natively supported by all dockable shuttles. diff --git a/code/modules/shuttles/web_datums.dm b/code/modules/shuttles/web_datums.dm index f64573270c..3e2abf9443 100644 --- a/code/modules/shuttles/web_datums.dm +++ b/code/modules/shuttles/web_datums.dm @@ -8,7 +8,7 @@ /datum/shuttle_route var/datum/shuttle_destination/start = null // One of the two sides of this route. Start just means it was the creator of this route. var/datum/shuttle_destination/end = null // The second side. - var/area/interim = null // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen. + var/var/obj/effect/shuttle_landmark/interim // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen. var/travel_time = 0 // How long it takes to move from start to end, or end to start. Set to 0 for instant travel. var/one_way = FALSE // If true, you can't travel from end to start. @@ -16,7 +16,7 @@ start = _start end = _end if(_interim) - interim = locate(_interim) + interim = SSshuttles.get_landmark(_interim) travel_time = _time one_way = _oneway @@ -50,14 +50,12 @@ // This is the second datum, and contains information on all the potential destinations for a specific shuttle. /datum/shuttle_destination var/name = "a place" // Name of the destination, used for the flight computer. - var/area/my_area = null // Where the shuttle will move to when it actually arrives. + var/obj/effect/shuttle_landmark/my_landmark = null // Where the shuttle will move to when it actually arrives. var/datum/shuttle_web_master/master = null // The datum that does the coordination with the actual shuttle datum. var/list/routes = list() // Routes that are connected to this destination. - var/preferred_interim_area = null // When building a new route, use this interim area. + var/preferred_interim_tag = null // When building a new route, use interim landmark with this tag. var/skip_me = FALSE // We will not autocreate this one. Some map must be doing it. - var/dock_target = null // The tag_id that the shuttle will use to try to dock to the destination, if able. - var/radio_announce = 0 // Whether it will make a station announcement (0) or a radio announcement (1). var/announcer = null // The name of the 'announcer' that will say the arrival/departure messages. Defaults to the map's boss name if blank. // var/arrival_message = null // Message said if the ship enters this destination. Not announced if the ship is cloaked. @@ -72,7 +70,9 @@ var/list/routes_to_make = list() /datum/shuttle_destination/New(var/new_master) - my_area = locate(my_area) + my_landmark = SSshuttles.get_landmark(my_landmark) + if(!my_landmark) + log_debug("Web shuttle destination '[name]' could not find its landmark '[my_landmark]'.") master = new_master /datum/shuttle_destination/Destroy() @@ -99,7 +99,7 @@ // Now link our new destination to us. var/travel_delay = destinations_to_create[type_to_make] - link_destinations(new_dest, preferred_interim_area, travel_delay) + link_destinations(new_dest, preferred_interim_tag, travel_delay) to_world("SHUTTLES: [name] has linked themselves to [new_dest.name]") to_world("SHUTTLES: [name] has finished building destinations. already_made list is \[[english_list(already_made)]\].") @@ -135,14 +135,14 @@ else global_announcer.autosay(get_arrival_message(),(announcer ? announcer : "[using_map.boss_name]")) -/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/area/interim_area, var/travel_time = 0) +/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/interim_tag, var/travel_time = 0) // First, check to make sure this doesn't cause a duplicate route. for(var/datum/shuttle_route/R in routes) if(R.start == other_place || R.end == other_place) return // Now we can connect them. - var/datum/shuttle_route/new_route = new(src, other_place, interim_area, travel_time) + var/datum/shuttle_route/new_route = new(src, other_place, interim_tag, travel_time) routes += new_route other_place.routes += new_route @@ -166,7 +166,7 @@ // This is the third and final datum, which coordinates with the shuttle datum to tell it where it is, where it can go, and how long it will take. // It is also responsible for instancing all the destinations it has control over, and linking them together. /datum/shuttle_web_master - var/datum/shuttle/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with. + var/datum/shuttle/autodock/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with. var/datum/shuttle_destination/current_destination = null // Where the shuttle currently is. Bit of a misnomer. var/datum/shuttle_destination/future_destination = null // Where it will be in the near future. var/datum/shuttle_destination/starting_destination = null // Where the shuttle will start at, generally at the home base. @@ -204,7 +204,7 @@ for(var/datum/shuttle_destination/D in destinations) for(var/type_to_link in D.routes_to_make) var/travel_delay = D.routes_to_make[type_to_link] - D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay) + D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay) /datum/shuttle_web_master/proc/on_shuttle_departure() current_destination.exit() @@ -214,11 +214,6 @@ future_destination.enter() current_destination = future_destination future_destination = null - my_shuttle.current_area = current_destination.my_area - -/datum/shuttle_web_master/proc/current_dock_target() - if(current_destination) - return current_destination.dock_target /datum/shuttle_web_master/proc/get_available_routes() if(current_destination) @@ -254,10 +249,11 @@ future_destination = R.get_other_side(current_destination) var/travel_time = R.travel_time * my_shuttle.flight_time_modifier * 2 // Autopilot is less efficent than having someone flying manually. + // TODO - Leshana - Change this to use proccess stuff of autodock! if(R.interim && R.travel_time > 0) - my_shuttle.long_jump(my_shuttle.current_area, future_destination.my_area, R.interim, travel_time / 10) + my_shuttle.long_jump(future_destination.my_landmark, R.interim, travel_time / 10) else - my_shuttle.short_jump(my_shuttle.current_area, future_destination.my_area) + my_shuttle.short_jump(future_destination.my_landmark) return TRUE // Note this will return before the shuttle actually arrives. /datum/shuttle_web_master/proc/process_autopath() diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index 411ecf75d3..b6ec121adf 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -225,7 +225,7 @@ rig = target.belt if(!istype(rig)) return - rig.reset() + rig.cut_suit() user.visible_message("[user] has cut through the support systems of \the [rig] on [target] with \the [tool].", \ "You have cut through the support systems of \the [rig] on [target] with \the [tool].") diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 8c626a6b46..5d8abe9fbd 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -71,7 +71,7 @@ turn_off() src.visible_message("\The [src] putters before turning off.", "You hear something putter slowly.") -/obj/vehicle/bike/verb/kickstand() +/obj/vehicle/bike/verb/kickstand(var/mob/user as mob) //TFF 22/3/20 - Tweaking the visible_message output so it's not "You put kickstand down" to everyone. set name = "Toggle Kickstand" set category = "Vehicle" set src in view(0) @@ -82,12 +82,12 @@ if(usr.incapacitated()) return if(kickstand) - src.visible_message("You put up \the [src]'s kickstand.") + visible_message("[user] puts up \the [src]'s kickstand.") else if(istype(src.loc,/turf/space) || istype(src.loc, /turf/simulated/floor/water)) to_chat(usr, " You don't think kickstands work here...") return - src.visible_message("You put down \the [src]'s kickstand.") + visible_message("[user] puts down \the [src]'s kickstand.") if(pulledby) pulledby.stop_pulling() diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index 43ccbd9f3c..840c2096c5 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -271,7 +271,36 @@ warn = 1 if(warn) - to_chat(M, "You accidentally touch [src].") + to_chat(M, "You accidentally touch \the [src].") + ..() + +/obj/machinery/artifact/Bump(var/atom/bumped) + if(istype(bumped,/obj)) + if(bumped:throwforce >= 10) + if(my_effect.trigger == TRIGGER_FORCE) + my_effect.ToggleActivate() + if(secondary_effect && secondary_effect.trigger == TRIGGER_FORCE && prob(25)) + secondary_effect.ToggleActivate(0) + else if(ishuman(bumped) && GetAnomalySusceptibility(bumped) >= 0.5) + var/warn = 0 + + if (my_effect.trigger == TRIGGER_TOUCH && prob(50)) + my_effect.ToggleActivate() + warn = 1 + if(secondary_effect && secondary_effect.trigger == TRIGGER_TOUCH && prob(25)) + secondary_effect.ToggleActivate(0) + warn = 1 + + if (my_effect.effect == EFFECT_TOUCH && prob(50)) + my_effect.DoEffectTouch(bumped) + warn = 1 + if(secondary_effect && secondary_effect.effect == EFFECT_TOUCH && secondary_effect.activated && prob(50)) + secondary_effect.DoEffectTouch(bumped) + warn = 1 + + if(warn) + to_chat(bumped, "You accidentally touch \the [src] as it hits you.") + ..() /obj/machinery/artifact/bullet_act(var/obj/item/projectile/P) diff --git a/code/modules/xenoarcheaology/artifacts/autocloner.dm b/code/modules/xenoarcheaology/artifacts/autocloner.dm index e8ee5a16c1..16182acbc9 100644 --- a/code/modules/xenoarcheaology/artifacts/autocloner.dm +++ b/code/modules/xenoarcheaology/artifacts/autocloner.dm @@ -10,7 +10,7 @@ density = 1 var/previous_power_state = 0 - use_power = 1 + use_power = USE_POWER_IDLE active_power_usage = 2000 idle_power_usage = 1000 @@ -54,7 +54,7 @@ //if we've finished growing... if(time_spent_spawning >= time_per_spawn) time_spent_spawning = 0 - use_power = 1 + update_use_power(USE_POWER_IDLE) src.visible_message("\icon[src] [src] pings!") icon_state = "cellold1" desc = "It's full of a bubbling viscous liquid, and is lit by a mysterious glow." @@ -63,11 +63,11 @@ //if we're getting close to finished, kick into overdrive power usage if(time_spent_spawning / time_per_spawn > 0.75) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) icon_state = "cellold2" desc = "It's full of a bubbling viscous liquid, and is lit by a mysterious glow. A dark shape appears to be forming inside..." else - use_power = 1 + update_use_power(USE_POWER_IDLE) icon_state = "cellold1" desc = "It's full of a bubbling viscous liquid, and is lit by a mysterious glow." diff --git a/code/modules/xenoarcheaology/artifacts/replicator.dm b/code/modules/xenoarcheaology/artifacts/replicator.dm index 9301ab4784..2ebad9cd96 100644 --- a/code/modules/xenoarcheaology/artifacts/replicator.dm +++ b/code/modules/xenoarcheaology/artifacts/replicator.dm @@ -7,7 +7,7 @@ idle_power_usage = 100 active_power_usage = 1000 - use_power = 1 + use_power = USE_POWER_IDLE var/spawn_progress_time = 0 var/max_spawn_time = 50 @@ -104,7 +104,7 @@ max_spawn_time = rand(30,100) if(!spawning_types.len || !stored_materials.len) - use_power = 1 + update_use_power(USE_POWER_IDLE) icon_state = "borgcharger0(old)" else if(prob(5)) @@ -145,7 +145,7 @@ spawning_types.Add(construction[construction[index]]) spawn_progress_time = 0 - use_power = 2 + update_use_power(USE_POWER_ACTIVE) icon_state = "borgcharger1(old)" else src.visible_message(fail_message) diff --git a/code/modules/xenoarcheaology/effects/animate_anomaly.dm b/code/modules/xenoarcheaology/effects/animate_anomaly.dm new file mode 100644 index 0000000000..c0c17e4aaf --- /dev/null +++ b/code/modules/xenoarcheaology/effects/animate_anomaly.dm @@ -0,0 +1,60 @@ + +/datum/artifact_effect/animate_anomaly + name = "animate anomaly" + effect_type = EFFECT_PSIONIC + var/mob/living/target = null + +/datum/artifact_effect/animate_anomaly/ToggleActivate(var/reveal_toggle = 1) + ..() + find_target() + +/datum/artifact_effect/animate_anomaly/New() + ..() + effectrange = max(3, effectrange) + +/datum/artifact_effect/animate_anomaly/proc/find_target() + if(!target || target.z != holder.z || get_dist(target, holder) > effectrange) + var/mob/living/ClosestMob = null + for(var/mob/living/L in range(effectrange, holder)) + if(!L.mind) + continue + if(!ClosestMob) + ClosestMob = L + continue + if(!L.stat) + if(get_dist(holder, L) < get_dist(holder, ClosestMob)) + ClosestMob = L + + target = ClosestMob + +/datum/artifact_effect/animate_anomaly/DoEffectTouch(var/mob/living/user) + var/obj/O = holder + var/turf/T = get_step_away(O, user) + + if(target && istype(T) && istype(O.loc, /turf)) + O.Move(T) + O.visible_message("\The [holder] lurches away from [user]") + +/datum/artifact_effect/animate_anomaly/DoEffectAura() + var/obj/O = holder + if(!target || target.z != O.z || get_dist(target, O) > effectrange) + target = null + find_target() + var/turf/T = get_step_to(O, target) + + if(target && istype(T) && istype(O.loc, /turf)) + if(get_dist(O, T) > 1) + O.Move(T) + O.visible_message("\The [holder] lurches toward [target]") + +/datum/artifact_effect/animate_anomaly/DoEffectPulse() + var/obj/O = holder + if(!target || target.z != O.z || get_dist(target, O) > effectrange) + target = null + find_target() + var/turf/T = get_step_to(O, target) + + if(target && istype(T) && istype(O.loc, /turf)) + if(get_dist(O, T) > 1) + O.Move(T) + O.visible_message("\The [holder] lurches toward [target]") diff --git a/code/modules/xenoarcheaology/effects/cannibal.dm b/code/modules/xenoarcheaology/effects/cannibal.dm new file mode 100644 index 0000000000..04a0b5a9e8 --- /dev/null +++ b/code/modules/xenoarcheaology/effects/cannibal.dm @@ -0,0 +1,75 @@ +/datum/artifact_effect/cannibalfeeling + name = "cannibalfeeling" + effect_type = EFFECT_PSIONIC + var/list/messages = list("You feel peckish.", + "Something doesn't feel right.", + "You get a strange feeling in your gut.", + "You feel particularly hungry.", + "You taste blood.", + "There's a strange feeling in the air.", + "There's a strange smell in the air.", + "The tips of your fingers feel tingly.", + "You feel twitchy.", + "You feel empty.", + "You've got a good feeling about this.", + "Your tongue prickles.", + "Are they clean?", + "You feel weak.", + "The ground is getting closer.", + "Something is missing.") + + var/list/drastic_messages = list("They look delicious.", + "They'll take what's yours!", + "They're full of meat.", + "What's happening to you?", + "Butcher them!", + "Feast!") + +/datum/artifact_effect/cannibalfeeling/DoEffectTouch(var/mob/user) + if(user) + if (istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.is_sentient()) + if(prob(50)) + if(prob(75)) + to_chat(H, "[pick(drastic_messages)]") + else + to_chat(H, "[pick(messages)]") + + if(prob(50)) + H.dizziness += rand(3,5) + H.nutrition = H.nutrition / 1.5 + +/datum/artifact_effect/cannibalfeeling/DoEffectAura() + if(holder) + var/turf/T = get_turf(holder) + for (var/mob/living/carbon/human/H in range(src.effectrange,T)) + if(H.is_sentient()) + if(prob(5)) + if(prob(75)) + to_chat(H, "[pick(messages)]") + else + to_chat(H, "[pick(drastic_messages)]") + + if(prob(10)) + H.dizziness += rand(3,5) + H.nutrition = H.nutrition / 2 + return 1 + +/datum/artifact_effect/cannibalfeeling/DoEffectPulse() + if(holder) + var/turf/T = get_turf(holder) + for (var/mob/living/carbon/human/H in range(src.effectrange,T)) + if(H.is_sentient()) + if(prob(50)) + if(prob(95)) + to_chat(H, "[pick(drastic_messages)]") + else + to_chat(H, "[pick(messages)]") + + if(prob(50)) + H.dizziness += rand(3,5) + else if(prob(25)) + H.dizziness += rand(5,15) + H.nutrition = H.nutrition / 4 + return 1 diff --git a/code/modules/xenoarcheaology/effects/electric_field.dm b/code/modules/xenoarcheaology/effects/electric_field.dm new file mode 100644 index 0000000000..3b27e8fe42 --- /dev/null +++ b/code/modules/xenoarcheaology/effects/electric_field.dm @@ -0,0 +1,69 @@ + +/datum/artifact_effect/electric_field + name = "electric field" + effect_type = EFFECT_ENERGY + +/datum/artifact_effect/electric_field/DoEffectTouch(var/mob/user) + var/list/nearby_mobs = list() + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + if(L == user) // You're "grounded" when you contact the artifact. + continue + if(!L.stat) + nearby_mobs |= L + + for(var/obj/machinery/light/light in range(effectrange, get_turf(holder))) + light.flicker() + + for(var/mob/living/L in nearby_mobs) + if(L.isSynthetic()) + to_chat(L, "ERROR: Electrical fault detected!") + L.stuttering += 3 + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) + H.electrocute_act(rand(25, 40), holder, H.get_siemens_coefficient_organ(affected), affected) + else + L.electrocute_act(rand(25, 40), holder, 0.75, BP_TORSO) + +/datum/artifact_effect/electric_field/DoEffectAura() + var/list/nearby_mobs = list() + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + if(!L.stat) + nearby_mobs |= L + + for(var/obj/machinery/light/light in range(effectrange, get_turf(holder))) + light.flicker() + + for(var/mob/living/L in nearby_mobs) + if(L.isSynthetic()) + to_chat(L, "ERROR: Electrical fault detected!") + L.stuttering += 3 + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) + H.electrocute_act(rand(1, 10), holder, H.get_siemens_coefficient_organ(affected), affected) + else + L.electrocute_act(rand(1, 10), holder, 0.75, BP_TORSO) + +/datum/artifact_effect/electric_field/DoEffectPulse() + var/list/nearby_mobs = list() + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + if(!L.stat) + nearby_mobs |= L + + for(var/obj/machinery/light/light in range(effectrange, get_turf(holder))) + light.flicker() + + for(var/mob/living/L in nearby_mobs) + if(L.isSynthetic()) + to_chat(L, "ERROR: Electrical fault detected!") + L.stuttering += 3 + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) + H.electrocute_act(rand(10, 30), holder, H.get_siemens_coefficient_organ(affected), affected) + else + L.electrocute_act(rand(10, 30), holder, 0.75, BP_TORSO) diff --git a/code/modules/xenoarcheaology/effects/feysight.dm b/code/modules/xenoarcheaology/effects/feysight.dm new file mode 100644 index 0000000000..379dc0cc54 --- /dev/null +++ b/code/modules/xenoarcheaology/effects/feysight.dm @@ -0,0 +1,43 @@ +/datum/artifact_effect/feysight + name = "feysight" + effect_type = EFFECT_PSIONIC + +/datum/artifact_effect/feysight/proc/apply_modifier(var/mob/living/L) + if(!istype(L)) + return FALSE + + if(!L.is_sentient()) + return FALSE // Drons are presumably deaf to any psionic things. + + if(L.add_modifier(/datum/modifier/feysight, 30 SECONDS)) + to_chat(L, "An otherworldly feeling seems to enter your mind, and you feel at peace.") + L.adjustHalLoss(10) + to_chat(L, "The inside of your head hurts...") + return TRUE + else + if(L.has_modifier_of_type(/datum/modifier/feysight)) + to_chat(L, "An otherworldly feeling seems to enter your mind again, and it holds the visions in place.") + else + to_chat(L, "An otherworldly feeling seems to enter your mind, and you briefly feel peace, but \ + it quickly passes.") + return FALSE + +/datum/artifact_effect/feysight/DoEffectTouch(var/mob/toucher) + if(toucher && isliving(toucher)) + apply_modifier(toucher) + return TRUE + +/datum/artifact_effect/feysight/DoEffectAura() + if(holder) + var/turf/T = get_turf(holder) + for(var/mob/living/L in range(src.effectrange,T)) + if(prob(10)) + apply_modifier(L) + return TRUE + +/datum/artifact_effect/feysight/DoEffectPulse() + if(holder) + var/turf/T = get_turf(holder) + for(var/mob/living/L in range(src.effectrange,T)) + apply_modifier(L) + return TRUE \ No newline at end of file diff --git a/code/modules/xenoarcheaology/effects/gaia.dm b/code/modules/xenoarcheaology/effects/gaia.dm new file mode 100644 index 0000000000..1a89149818 --- /dev/null +++ b/code/modules/xenoarcheaology/effects/gaia.dm @@ -0,0 +1,81 @@ + +/datum/artifact_effect/gaia + name = "gaia" + effect_type = EFFECT_ORGANIC + + var/list/my_glitterflies = list() + +/datum/artifact_effect/gaia/proc/age_plantlife(var/obj/machinery/portable_atmospherics/hydroponics/Tray = null) + if(istype(Tray) && Tray.seed) + Tray.health += rand(1,3) * HYDRO_SPEED_MULTIPLIER + Tray.age += 1 + + if(Tray.health > 0 && Tray.dead) + Tray.dead = FALSE + + Tray.check_health() + + if(!Tray.dead) + if((Tray.age > Tray.seed.get_trait(TRAIT_MATURATION)) && \ + ((Tray.age - Tray.lastproduce) > Tray.seed.get_trait(TRAIT_PRODUCTION)) && \ + (!Tray.harvest && !Tray.dead)) + Tray.harvest = 1 + Tray.lastproduce = Tray.age + + else if(istype(Tray, /obj/effect/plant)) + var/obj/effect/plant/P = Tray + Tray = P.plant + if(Tray) + age_plantlife(Tray) + P.update_icon() + +/datum/artifact_effect/gaia/DoEffectTouch(var/mob/user) + to_chat(user, "You feel the presence of something long forgotten.") + for(var/obj/machinery/portable_atmospherics/hydroponics/Tray in view(world.view,get_turf(holder))) + age_plantlife(Tray) + if(prob(30)) + var/mob/living/simple_mob/animal/sif/glitterfly/G = new(get_turf(Tray)) + + my_glitterflies |= G + + G.ai_holder.returns_home = TRUE + + for(var/obj/effect/plant/P in view(world.view,get_turf(holder))) + age_plantlife(P) + +/datum/artifact_effect/gaia/DoEffectAura() + for(var/obj/machinery/portable_atmospherics/hydroponics/Tray in view(effectrange,holder)) + age_plantlife(Tray) + if(prob(2)) + var/mob/living/simple_mob/animal/sif/glitterfly/G = new(get_turf(Tray)) + + my_glitterflies |= G + + G.ai_holder.returns_home = TRUE + + for(var/obj/effect/plant/P in view(effectrange,get_turf(holder))) + age_plantlife(P) + +/datum/artifact_effect/gaia/DoEffectPulse() + for(var/obj/machinery/portable_atmospherics/hydroponics/Tray in view(effectrange,holder)) + age_plantlife(Tray) + if(prob(10)) + var/mob/living/simple_mob/animal/sif/glitterfly/G = new(get_turf(Tray)) + + my_glitterflies |= G + + G.ai_holder.returns_home = TRUE + + for(var/obj/effect/plant/P in view(effectrange,get_turf(holder))) + age_plantlife(P) + +/datum/artifact_effect/gaia/process() + ..() + + listclearnulls(my_glitterflies) + + for(var/mob/living/L in my_glitterflies) + if(L.stat == DEAD) + my_glitterflies -= L + + L.ai_holder.home_turf = get_turf(holder) diff --git a/code/modules/xenoarcheaology/effects/gravitational_waves.dm b/code/modules/xenoarcheaology/effects/gravitational_waves.dm new file mode 100644 index 0000000000..8483d23d4b --- /dev/null +++ b/code/modules/xenoarcheaology/effects/gravitational_waves.dm @@ -0,0 +1,25 @@ + +/datum/artifact_effect/gravity_wave + name = "gravity wave" + effect_type = EFFECT_ENERGY + + var/last_wave_pull = 0 + +/datum/artifact_effect/gravity_wave/DoEffectTouch(var/mob/user) + gravwave(user, effectrange, STAGE_TWO) + +/datum/artifact_effect/gravity_wave/DoEffectAura() + var/seconds_since_last_pull = max(0, round((last_wave_pull - world.time) / 10)) + + if(prob(10 + seconds_since_last_pull)) + holder.visible_message("\The [holder] distorts as local gravity intensifies, and shifts toward it.") + last_wave_pull = world.time + gravwave(get_turf(holder), effectrange, STAGE_TWO) + +/datum/artifact_effect/gravity_wave/DoEffectPulse() + holder.visible_message("\The [holder] distorts as local gravity intensifies, and shifts toward it.") + gravwave(get_turf(holder), effectrange, STAGE_TWO) + +proc/gravwave(var/atom/target, var/pull_range = 7, var/pull_power = STAGE_TWO) + for(var/atom/A in oview(pull_range, target)) + A.singularity_pull(target, pull_power) diff --git a/code/modules/xenoarcheaology/effects/poltergeist.dm b/code/modules/xenoarcheaology/effects/poltergeist.dm new file mode 100644 index 0000000000..189c0ea4cd --- /dev/null +++ b/code/modules/xenoarcheaology/effects/poltergeist.dm @@ -0,0 +1,47 @@ + +/datum/artifact_effect/poltergeist + name = "poltergeist" + effect_type = EFFECT_ENERGY + +/datum/artifact_effect/poltergeist/proc/throw_at_mob(var/mob/living/target, var/damage = 20) + var/list/valid_targets = list() + + for(var/obj/O in oview(world.view, target)) + if(!O.anchored && isturf(O.loc)) + valid_targets |= O + + if(valid_targets.len) + var/obj/obj_to_throw = pick(valid_targets) + obj_to_throw.visible_message("\The [obj_to_throw] levitates, befure hurtling toward [target]!") + obj_to_throw.throw_at(target, world.view, min(40, damage * GetAnomalySusceptibility(target))) + +/datum/artifact_effect/poltergeist/DoEffectTouch(var/mob/user) + throw_at_mob(user, rand(10, 30)) + +/datum/artifact_effect/poltergeist/DoEffectAura() + var/mob/living/target = null + for(var/mob/living/L in oview(get_turf(holder), effectrange)) + if(L.stat || !L.mind) + continue + + if(target && get_dist(get_turf(holder), L) > get_dist(get_turf(holder), target)) + continue + + target = L + + if(target) + throw_at_mob(target, rand(15, 30)) + +/datum/artifact_effect/poltergeist/DoEffectPulse() + var/mob/living/target = null + for(var/mob/living/L in oview(get_turf(holder), effectrange)) + if(L.stat || !L.mind) + continue + + if(target && get_dist(get_turf(holder), L) > get_dist(get_turf(holder), target)) + continue + + target = L + + if(target) + throw_at_mob(target, chargelevelmax) diff --git a/code/modules/xenoarcheaology/effects/resurrect.dm b/code/modules/xenoarcheaology/effects/resurrect.dm new file mode 100644 index 0000000000..e54ca276ea --- /dev/null +++ b/code/modules/xenoarcheaology/effects/resurrect.dm @@ -0,0 +1,100 @@ +/datum/artifact_effect/resurrect + name = "resurrect" + effect_type = EFFECT_ORGANIC + + var/stored_life = 0 + +/datum/artifact_effect/resurrect/proc/steal_life(var/mob/living/target = null) + if(!istype(target)) + return 0 + + if(target.stat != DEAD && stored_life < 200) + holder.Beam(target, icon_state = "drain_life", time = 1 SECOND) + target.apply_damage(5, SEARING, BP_TORSO) + return 5 + + return 0 + +/datum/artifact_effect/resurrect/proc/give_life(var/mob/living/target = null) + if(!istype(target)) + return + + if(target.stat == DEAD && stored_life) + holder.Beam(target, icon_state = "lichbeam", time = 1 SECOND) + target.adjustBruteLoss(-5) + target.adjustFireLoss(-5) + target.adjustCloneLoss(-5) + target.adjustOxyLoss(-5) + target.adjustHalLoss(-5) + target.adjustToxLoss(-5) + stored_life = max(0, stored_life - 5) + + if(target.health > (target.maxHealth / 4)) + attempt_revive(target) + stored_life = 0 + +/datum/artifact_effect/resurrect/proc/attempt_revive(var/mob/living/L = null) + spawn() + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = L + SM.adjustBruteLoss(-40) + SM.adjustFireLoss(-40) + SM.health = SM.getMaxHealth() / 3 + SM.stat = CONSCIOUS + dead_mob_list -= SM + living_mob_list += SM + SM.update_icon() + SM.revive() + holder.visible_message("\The [SM]'s eyes open in a flash of light!") + else if(ishuman(L)) + var/mob/living/carbon/human/H = L + + if(!H.client && H.mind) + for(var/mob/observer/dead/ghost in player_list) + if(ghost.mind == H.mind) + to_chat(ghost, "An artifact is trying to \ + revive you. Return to your body if you want to be resurrected! \ + (Verbs -> Ghost -> Re-enter corpse)") + break + + H.adjustBruteLoss(-40) + H.adjustFireLoss(-40) + + sleep(10 SECONDS) + if(H.client) + L.stat = CONSCIOUS + dead_mob_list -= H + living_mob_list += H + H.timeofdeath = null + + holder.visible_message("\The [H]'s eyes open in a flash of light!") + +/datum/artifact_effect/resurrect/DoEffectTouch(var/mob/user) + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + stored_life += 4 * steal_life(L) + + var/turf/T = get_turf(holder) + for(var/mob/living/L in T) + if(L.stat == DEAD) + give_life(L) + break + +/datum/artifact_effect/resurrect/DoEffectAura() + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + stored_life += steal_life(L) + + var/turf/T = get_turf(holder) + for(var/mob/living/L in T) + if(L.stat == DEAD) + give_life(L) + break + +/datum/artifact_effect/resurrect/DoEffectPulse() + for(var/mob/living/L in oview(effectrange, get_turf(holder))) + stored_life += 2 * steal_life(L) + + var/turf/T = get_turf(holder) + for(var/mob/living/L in T) + if(L.stat == DEAD) + give_life(L) + break diff --git a/code/modules/xenoarcheaology/effects/vampire.dm b/code/modules/xenoarcheaology/effects/vampire.dm new file mode 100644 index 0000000000..ee75ea5ba1 --- /dev/null +++ b/code/modules/xenoarcheaology/effects/vampire.dm @@ -0,0 +1,86 @@ + +/datum/artifact_effect/vampire + name = "vampire" + effect_type = EFFECT_ORGANIC + var/last_bloodcall = 0 + var/bloodcall_interval = 50 + var/last_eat = 0 + var/eat_interval = 100 + var/charges = 0 + var/list/nearby_mobs = list() + +/datum/artifact_effect/vampire/proc/bloodcall(var/mob/living/carbon/human/M) + last_bloodcall = world.time + if(istype(M)) + playsound(holder.loc, pick('sound/hallucinations/wail.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/far_noise.ogg'), 50, 1, -3) + + var/target = pick(M.organs_by_name) + M.apply_damage(rand(5, 10), SEARING, target) + to_chat(M, "The skin on your [parse_zone(target)] feels like it's ripping apart, and a stream of blood flies out.") + var/obj/effect/decal/cleanable/blood/splatter/animated/B = new(M.loc) + B.basecolor = M.species.get_blood_colour(M) + B.color = M.species.get_blood_colour(M) + B.target_turf = pick(range(1, get_turf(holder))) + B.blood_DNA = list() + B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type + M.vessel.remove_reagent("blood",rand(25,50)) + +/datum/artifact_effect/vampire/DoEffectTouch(var/mob/user) + bloodcall(user) + DoEffectAura() + +/datum/artifact_effect/vampire/DoEffectAura() + nearby_mobs.Cut() + + var/turf/T = get_turf(holder) + + for(var/mob/living/L in oview(effectrange, T)) + if(!L.stat && L.mind) + nearby_mobs |= L + + if(world.time - last_bloodcall > bloodcall_interval && nearby_mobs.len) + var/mob/living/carbon/human/M = pick(nearby_mobs) + if(M in view(effectrange,holder) && M.health > 20) + if(prob(50)) + bloodcall(M) + holder.Beam(M, icon_state = "drainbeam", time = 1 SECOND) + + if(world.time - last_eat > eat_interval) + var/obj/effect/decal/cleanable/blood/B = locate() in range(2,holder) + if(B) + last_eat = world.time + B.loc = null + if(istype(B, /obj/effect/decal/cleanable/blood/drip)) + charges += 0.25 + else + charges += 1 + playsound(holder.loc, 'sound/effects/splat.ogg', 50, 1, -3) + + qdel(B) + + if(charges >= 10) + charges -= 10 + var/manifestation = pick(/obj/item/device/soulstone, /mob/living/simple_mob/faithless/cult/strong, /mob/living/simple_mob/creature/cult/strong, /mob/living/simple_mob/animal/space/bats/cult/strong) + new manifestation(get_turf(pick(view(1,T)))) + + if(charges >= 3) + if(prob(5)) + charges -= 1 + var/spawn_type = pick(/mob/living/simple_mob/animal/space/bats, /mob/living/simple_mob/creature, /mob/living/simple_mob/faithless) + new spawn_type(get_turf(pick(view(1,T)))) + playsound(holder.loc, pick('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg'), 50, 1, -3) + + if(charges >= 1 && nearby_mobs.len && prob(15 * nearby_mobs.len)) + var/mob/living/L = pick(nearby_mobs) + + holder.Beam(L, icon_state = "drainbeam", time = 1 SECOND) + + L.add_modifier(/datum/modifier/agonize, 5 SECONDS) + + if(charges >= 0.1) + if(prob(5)) + holder.visible_message("\icon[holder] \The [holder] gleams a bloody red!") + charges -= 0.1 + +/datum/artifact_effect/vampire/DoEffectPulse() + DoEffectAura() diff --git a/code/modules/xenoarcheaology/finds/find_spawning.dm b/code/modules/xenoarcheaology/finds/find_spawning.dm index 4c091200ca..20f10e91da 100644 --- a/code/modules/xenoarcheaology/finds/find_spawning.dm +++ b/code/modules/xenoarcheaology/finds/find_spawning.dm @@ -21,7 +21,7 @@ var/apply_prefix = 1 if(prob(40)) - material_descriptor = pick("rusted ","dusty ","archaic ","fragile ") + material_descriptor = pick("rusted ","dusty ","archaic ","fragile ", "damaged", "pristine") source_material = pick("cordite","quadrinium",DEFAULT_WALL_MATERIAL,"titanium","aluminium","ferritic-alloy","plasteel","duranium") var/talkative = 0 @@ -32,7 +32,7 @@ //icon_state //item_state switch(find_type) - if(1) + if(ARCHAEO_BOWL) item_type = "bowl" if(prob(33)) new_item = new /obj/item/weapon/reagent_containers/glass/replenishing(src.loc) @@ -46,7 +46,7 @@ new_item.color = rgb(rand(0,255),rand(0,255),rand(0,255)) if(prob(20)) additional_desc = "There appear to be [pick("dark","faintly glowing","pungent","bright")] [pick("red","purple","green","blue")] stains inside." - if(2) + if(ARCHAEO_URN) item_type = "urn" if(prob(33)) new_item = new /obj/item/weapon/reagent_containers/glass/replenishing(src.loc) @@ -58,7 +58,7 @@ apply_image_decorations = 1 if(prob(20)) additional_desc = "It [pick("whispers faintly","makes a quiet roaring sound","whistles softly","thrums quietly","throbs")] if you put it to your ear." - if(3) + if(ARCHAEO_CUTLERY) item_type = "[pick("fork","spoon","knife")]" if(prob(25)) new_item = new /obj/item/weapon/material/kitchen/utensil/fork(src.loc) @@ -71,7 +71,7 @@ additional_desc = "[pick("It's like no [item_type] you've ever seen before",\ "It's a mystery how anyone is supposed to eat with this",\ "You wonder what the creator's mouth was shaped like")]." - if(4) + if(ARCHAEO_STATUETTE) name = "statuette" icon = 'icons/obj/xenoarchaeology.dmi' item_type = "statuette" @@ -82,7 +82,7 @@ if(prob(25)) new_item = new /obj/item/weapon/vampiric(src.loc) LAZYSET(new_item.origin_tech, TECH_ARCANE, 1) - if(5) + if(ARCHAEO_INSTRUMENT) name = "instrument" icon = 'icons/obj/xenoarchaeology.dmi' item_type = "instrument" @@ -93,13 +93,13 @@ "You wonder how many mouths the creator had",\ "You wonder what it sounds like",\ "You wonder what kind of music was made with it")]." - if(6) + if(ARCHAEO_KNIFE) item_type = "[pick("bladed knife","serrated blade","sharp cutting implement")]" new_item = new /obj/item/weapon/material/knife(src.loc) additional_desc = "[pick("It doesn't look safe.",\ "It looks wickedly jagged",\ "There appear to be [pick("dark red","dark purple","dark green","dark blue")] stains along the edges")]." - if(7) + if(ARCHAEO_COIN) //assuming there are 12 types of coins var/chance = 8 for(var/type in typesof(/obj/item/weapon/coin)) @@ -112,11 +112,11 @@ apply_prefix = 0 apply_material_decorations = 0 apply_image_decorations = 1 - if(8) + if(ARCHAEO_HANDCUFFS) item_type = "handcuffs" new_item = new /obj/item/weapon/handcuffs(src.loc) additional_desc = "[pick("They appear to be for securing two things together","Looks kinky","Doesn't seem like a children's toy")]." - if(9) + if(ARCHAEO_BEARTRAP) item_type = "[pick("wicked","evil","byzantine","dangerous")] looking [pick("device","contraption","thing","trap")]" apply_prefix = 0 new_item = new /obj/item/weapon/beartrap(src.loc) @@ -125,13 +125,13 @@ additional_desc = "[pick("It looks like it could take a limb off",\ "Could be some kind of animal trap",\ "There appear to be [pick("dark red","dark purple","dark green","dark blue")] stains along part of it")]." - if(10) + if(ARCHAEO_LIGHTER) item_type = "[pick("cylinder","tank","chamber")]" new_item = new /obj/item/weapon/flame/lighter(src.loc) additional_desc = "There is a tiny device attached." if(prob(30)) apply_image_decorations = 1 - if(11) + if(ARCHAEO_BOX) item_type = "box" new_item = new /obj/item/weapon/storage/box(src.loc) new_item.icon = 'icons/obj/xenoarchaeology.dmi' @@ -143,7 +143,7 @@ if(prob(30)) LAZYSET(new_item.origin_tech, TECH_ARCANE, 1) apply_image_decorations = 1 - if(12) + if(ARCHAEO_GASTANK) item_type = "[pick("cylinder","tank","chamber")]" if(prob(25)) new_item = new /obj/item/weapon/tank/air(src.loc) @@ -153,7 +153,7 @@ new_item = new /obj/item/weapon/tank/phoron(src.loc) icon_state = pick("oxygen","oxygen_fr","oxygen_f","phoron","anesthetic") additional_desc = "It [pick("gloops","sloshes")] slightly when you shake it." - if(13) + if(ARCHAEO_TOOL) item_type = "tool" if(prob(25)) new_item = new /obj/item/weapon/tool/wrench(src.loc) @@ -167,7 +167,7 @@ additional_desc = "[pick("It doesn't look safe.",\ "You wonder what it was used for",\ "There appear to be [pick("dark red","dark purple","dark green","dark blue")] stains on it")]." - if(14) + if(ARCHAEO_METAL) apply_material_decorations = 0 var/list/possible_spawns = list() possible_spawns += /obj/item/stack/material/steel @@ -184,7 +184,7 @@ var/new_type = pick(possible_spawns) new_item = new new_type(src.loc) new_item:amount = rand(5,45) - if(15) + if(ARCHAEO_PEN) if(prob(75)) new_item = new /obj/item/weapon/pen(src.loc) else @@ -194,7 +194,7 @@ icon_state = "pen1" LAZYSET(new_item.origin_tech, TECH_ARCANE, 1) apply_image_decorations = 1 - if(16) + if(ARCHAEO_CRYSTAL) apply_prefix = 0 if(prob(25)) icon = 'icons/obj/xenoarchaeology.dmi' @@ -218,27 +218,28 @@ new_item.icon = 'icons/obj/xenoarchaeology.dmi' new_item.icon_state = icon_state LAZYSET(new_item.origin_tech, TECH_ARCANE, 2) - if(17) + if(ARCHAEO_CULTBLADE) //cultblade apply_prefix = 0 new_item = new /obj/item/weapon/melee/cultblade(src.loc) apply_material_decorations = 0 apply_image_decorations = 0 - if(18) + if(ARCHAEO_TELEBEACON) new_item = new /obj/item/device/radio/beacon(src.loc) talkative = 0 new_item.icon = 'icons/obj/xenoarchaeology.dmi' new_item.icon_state = "unknown[rand(1,4)]" new_item.desc = "" - if(19) + if(ARCHAEO_CLAYMORE) apply_prefix = 0 new_item = new /obj/item/weapon/material/sword(src.loc) new_item.force = 10 + new_item.name = pick("great-sword","claymore","longsword","broadsword","shortsword","gladius") item_type = new_item.name if(prob(30)) new_item.icon = 'icons/obj/xenoarchaeology.dmi' new_item.icon_state = "blade1" - if(20) + if(ARCHAEO_CULTROBES) //arcane clothing apply_prefix = 0 var/list/possible_spawns = list(/obj/item/clothing/head/culthood, @@ -249,14 +250,14 @@ var/new_type = pick(possible_spawns) new_item = new new_type(src.loc) LAZYSET(new_item.origin_tech, TECH_ARCANE, 1) - if(21) + if(ARCHAEO_SOULSTONE) //soulstone apply_prefix = 0 new_item = new /obj/item/device/soulstone(src.loc) item_type = new_item.name apply_material_decorations = 0 LAZYSET(new_item.origin_tech, TECH_ARCANE, 2) - if(22) + if(ARCHAEO_SHARD) if(prob(50)) new_item = new /obj/item/weapon/material/shard(src.loc) else @@ -264,12 +265,12 @@ apply_prefix = 0 apply_image_decorations = 0 apply_material_decorations = 0 - if(23) + if(ARCHAEO_RODS) apply_prefix = 0 new_item = new /obj/item/stack/rods(src.loc) apply_image_decorations = 0 apply_material_decorations = 0 - if(24) + if(ARCHAEO_STOCKPARTS) var/list/possible_spawns = typesof(/obj/item/weapon/stock_parts) possible_spawns -= /obj/item/weapon/stock_parts possible_spawns -= /obj/item/weapon/stock_parts/subspace @@ -278,12 +279,13 @@ new_item = new new_type(src.loc) item_type = new_item.name apply_material_decorations = 0 - if(25) + if(ARCHAEO_KATANA) apply_prefix = 0 new_item = new /obj/item/weapon/material/sword/katana(src.loc) new_item.force = 10 + new_item.name = "katana" item_type = new_item.name - if(26) + if(ARCHAEO_LASER) //energy gun var/spawn_type = pick(\ /obj/item/weapon/gun/energy/laser/practice/xenoarch,\ @@ -311,7 +313,7 @@ new_gun.power_supply.charge = 0 item_type = "gun" - if(27) + if(ARCHAEO_GUN) //revolver var/obj/item/weapon/gun/projectile/new_gun = new /obj/item/weapon/gun/projectile/revolver(src.loc) new_item = new_gun @@ -346,11 +348,11 @@ I.loc = null item_type = "gun" - if(28) + if(ARCHAEO_UNKNOWN) //completely unknown alien device if(prob(50)) apply_image_decorations = 0 - if(29) + if(ARCHAEO_FOSSIL) //fossil bone/skull //new_item = new /obj/item/weapon/fossil/base(src.loc) @@ -364,7 +366,7 @@ additional_desc = "A fossilised part of an alien, long dead." apply_image_decorations = 0 apply_material_decorations = 0 - if(30) + if(ARCHAEO_SHELL) //fossil shell new_item = new /obj/item/weapon/fossil/shell(src.loc) apply_prefix = 0 @@ -373,7 +375,7 @@ apply_material_decorations = 0 if(prob(10)) apply_image_decorations = 1 - if(31) + if(ARCHAEO_PLANT) //fossil plant new_item = new /obj/item/weapon/fossil/plant(src.loc) item_type = new_item.name @@ -381,7 +383,7 @@ apply_image_decorations = 0 apply_material_decorations = 0 apply_prefix = 0 - if(32) + if(ARCHAEO_REMAINS_HUMANOID) //humanoid remains apply_prefix = 0 item_type = "humanoid [pick("remains","skeleton")]" @@ -396,7 +398,7 @@ "The mouth is wide open in a death rictus, the victim would appear to have died screaming.") apply_image_decorations = 0 apply_material_decorations = 0 - if(33) + if(ARCHAEO_REMAINS_ROBOT) //robot remains apply_prefix = 0 item_type = "[pick("mechanical","robotic","cyborg")] [pick("remains","chassis","debris")]" @@ -411,7 +413,7 @@ "A pile of wires and crap metal that looks vaguely robotic.") apply_image_decorations = 0 apply_material_decorations = 0 - if(34) + if(ARCHAEO_REMAINS_XENO) //xenos remains apply_prefix = 0 item_type = "alien [pick("remains","skeleton")]" @@ -427,7 +429,7 @@ "It doesn't look human.") apply_image_decorations = 0 apply_material_decorations = 0 - if(35) + if(ARCHAEO_GASMASK) //gas mask if(prob(25)) new_item = new /obj/item/clothing/mask/gas/poltergeist(src.loc) @@ -436,7 +438,7 @@ new_item = new /obj/item/clothing/mask/gas(src.loc) if(prob(40)) new_item.color = rgb(rand(0,255),rand(0,255),rand(0,255)) - if(36) + if(ARCHAEO_ALIEN_ITEM) // Alien stuff. apply_prefix = FALSE apply_material_decorations = FALSE @@ -469,7 +471,7 @@ LAZYSET(new_item.origin_tech, TECH_PRECURSOR, 1) item_type = new_item.name - if(37) + if(ARCHAEO_ALIEN_BOAT) // Alien boats. apply_prefix = FALSE var/new_boat_mat = pickweight(list( @@ -500,7 +502,7 @@ new_item = new new_type(src.loc, new_boat_mat) item_type = new_item.name - if(38) + if(ARCHAEO_IMPERION_CIRCUIT) // Imperion circuit. apply_prefix = FALSE apply_image_decorations = FALSE @@ -511,7 +513,7 @@ desc = new_item.desc item_type = new_item.name - if(39) + if(ARCHAEO_TELECUBE) // Telecube. if(prob(25)) apply_prefix = FALSE @@ -520,6 +522,62 @@ if(prob(25)) apply_material_decorations = FALSE new_item = new /obj/item/weapon/telecube/randomized(src.loc) + item_type = new_item.name + + if(ARCHAEO_BATTERY) + // Battery! + var/new_path = pick(subtypesof(/obj/item/weapon/cell)) + new_item = new new_path(src.loc) + new_item.name = pick("cell", "battery", "device") + + if(prob(30)) + apply_prefix = FALSE + if(prob(5)) + apply_image_decorations = TRUE + if(prob(15)) + apply_material_decorations = FALSE + + item_type = new_item.name + + if(ARCHAEO_SYRINGE) + // Syringe. + if(prob(25)) + apply_prefix = FALSE + if(prob(75)) + apply_image_decorations = TRUE + if(prob(25)) + apply_material_decorations = FALSE + new_item = new /obj/item/weapon/reagent_containers/syringe(src.loc) + var/obj/item/weapon/reagent_containers/syringe/S = new_item + + S.volume = 30 + S.reagents.maximum_volume = 30 + + item_type = new_item.name + + if(ARCHAEO_RING) + // Ring. + if(prob(15)) + apply_prefix = FALSE + if(prob(40)) + apply_image_decorations = TRUE + if(prob(25)) + apply_material_decorations = FALSE + new_item = new /obj/item/clothing/gloves/ring/material(src.loc) + item_type = new_item.name + + if(ARCHAEO_CLUB) + // Baseball Bat + if(prob(30)) + apply_prefix = FALSE + if(prob(80)) + apply_image_decorations = TRUE + if(prob(10)) + apply_material_decorations = FALSE + + new_item = new /obj/item/weapon/material/twohanded/baseballbat(src.loc) + new_item.name = pick("great-club","club","billyclub","mace","tenderizer","maul","bat") + item_type = new_item.name if(istype(new_item, /obj/item/weapon/material)) var/new_item_mat = pickweight(list( @@ -553,9 +611,13 @@ var/decorations = "" if(apply_material_decorations) source_material = pick("cordite","quadrinium",DEFAULT_WALL_MATERIAL,"titanium","aluminium","ferritic-alloy","plasteel","duranium") + if(istype(new_item, /obj/item/weapon/material)) var/obj/item/weapon/material/MW = new_item source_material = MW.material.display_name + if(istype(new_item, /obj/vehicle/boat)) + var/obj/vehicle/boat/B = new_item + source_material = B.material.display_name desc = "A [material_descriptor ? "[material_descriptor] " : ""][item_type] made of [source_material], all craftsmanship is of [pick("the lowest","low","average","high","the highest")] quality." var/list/descriptors = list() diff --git a/code/modules/xenoarcheaology/tools/ano_device_battery.dm b/code/modules/xenoarcheaology/tools/ano_device_battery.dm index 937f5f2927..5e0773e61e 100644 --- a/code/modules/xenoarcheaology/tools/ano_device_battery.dm +++ b/code/modules/xenoarcheaology/tools/ano_device_battery.dm @@ -3,12 +3,18 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "anobattery0" var/datum/artifact_effect/battery_effect - var/capacity = 300 + var/capacity = 500 var/stored_charge = 0 var/effect_id = "" +/obj/item/weapon/anobattery/advanced + name = "advanced anomaly battery" + capacity = 3000 + +/* /obj/item/weapon/anobattery/New() battery_effect = new() +*/ /obj/item/weapon/anobattery/proc/UpdateSprite() var/p = (stored_charge/capacity)*100 @@ -105,7 +111,6 @@ to_chat(holder, "the \icon[src] [src] held by [holder] shudders in your grasp.") else src.loc.visible_message("the \icon[src] [src] shudders.") - inserted_battery.battery_effect.DoEffectTouch(holder) //consume power inserted_battery.use_power(energy_consumed_on_touch) @@ -113,11 +118,13 @@ //consume power equal to time passed inserted_battery.use_power(world.time - last_process) + inserted_battery.battery_effect.DoEffectTouch(holder) + else if(inserted_battery.battery_effect.effect == EFFECT_PULSE) inserted_battery.battery_effect.chargelevel = inserted_battery.battery_effect.chargelevelmax //consume power relative to the time the artifact takes to charge and the effect range - inserted_battery.use_power(inserted_battery.battery_effect.effectrange * inserted_battery.battery_effect.effectrange * inserted_battery.battery_effect.chargelevelmax) + inserted_battery.use_power((inserted_battery.battery_effect.effectrange * inserted_battery.battery_effect.chargelevelmax) / 2) else //consume power equal to time passed @@ -167,6 +174,7 @@ if(!inserted_battery.battery_effect.activated) inserted_battery.battery_effect.ToggleActivate(1) time_end = world.time + duration + last_process = world.time if(href_list["shutdown"]) activated = 0 if(href_list["ejectbattery"]) diff --git a/code/modules/xenoarcheaology/tools/artifact_harvester.dm b/code/modules/xenoarcheaology/tools/artifact_harvester.dm index 480061f9bb..3d71aef155 100644 --- a/code/modules/xenoarcheaology/tools/artifact_harvester.dm +++ b/code/modules/xenoarcheaology/tools/artifact_harvester.dm @@ -6,7 +6,7 @@ density = 1 idle_power_usage = 50 active_power_usage = 750 - use_power = 1 + use_power = USE_POWER_IDLE var/harvesting = 0 var/obj/item/weapon/anobattery/inserted_battery var/obj/machinery/artifact/cur_artifact @@ -80,7 +80,7 @@ //check if we've finished if(inserted_battery.stored_charge >= inserted_battery.capacity) - use_power = 1 + update_use_power(USE_POWER_IDLE) harvesting = 0 cur_artifact.anchored = 0 cur_artifact.being_used = 0 @@ -105,7 +105,7 @@ //if there's no charge left, finish if(inserted_battery.stored_charge <= 0) - use_power = 1 + update_use_power(USE_POWER_IDLE) inserted_battery.stored_charge = 0 harvesting = 0 if(inserted_battery.battery_effect && inserted_battery.battery_effect.activated) @@ -156,6 +156,7 @@ //delete it when the ids match to account for duplicate ids having different effects if(inserted_battery.battery_effect && inserted_battery.stored_charge <= 0) qdel(inserted_battery.battery_effect) + inserted_battery.battery_effect = null // var/datum/artifact_effect/source_effect @@ -191,7 +192,7 @@ if(source_effect) harvesting = 1 - use_power = 2 + update_use_power(USE_POWER_ACTIVE) cur_artifact.anchored = 1 cur_artifact.being_used = 1 icon_state = "incubator_on" @@ -235,7 +236,7 @@ inserted_battery.battery_effect.ToggleActivate(1) last_process = world.time harvesting = -1 - use_power = 2 + update_use_power(USE_POWER_ACTIVE) icon_state = "incubator_on" var/message = "[src] states, \"Warning, battery charge dump commencing.\"" src.visible_message(message) diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm index 28cf64b46e..e8b44b7bf8 100644 --- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm @@ -6,7 +6,7 @@ icon = 'icons/obj/virology.dmi' icon_state = "analyser" - use_power = 1 //1 = idle, 2 = active + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 300 diff --git a/code/modules/xenobio2/machinery/gene_manipulators.dm b/code/modules/xenobio2/machinery/gene_manipulators.dm index 76bab91b59..1f81882f77 100644 --- a/code/modules/xenobio2/machinery/gene_manipulators.dm +++ b/code/modules/xenobio2/machinery/gene_manipulators.dm @@ -42,7 +42,7 @@ /obj/machinery/xenobio density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE var/obj/item/weapon/disk/xenobio/loaded_disk //Currently loaded data disk. diff --git a/code/modules/xenobio2/machinery/injector.dm b/code/modules/xenobio2/machinery/injector.dm index b69642daab..f90d9dbdb5 100644 --- a/code/modules/xenobio2/machinery/injector.dm +++ b/code/modules/xenobio2/machinery/injector.dm @@ -10,7 +10,7 @@ desc = "Injects biological organisms that are inserted with the contents of an inserted beaker at the command of a remote computer." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE icon = 'icons/obj/biogenerator.dmi' icon_state = "biogen-work" var/mob/living/occupant diff --git a/code/modules/xenobio2/machinery/injector_computer.dm b/code/modules/xenobio2/machinery/injector_computer.dm index 9742e72d8c..b2952b2a8c 100644 --- a/code/modules/xenobio2/machinery/injector_computer.dm +++ b/code/modules/xenobio2/machinery/injector_computer.dm @@ -11,7 +11,7 @@ icon_keyboard = "med_key" icon_screen = "dna" light_color = "#315ab4" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/xenobio2computer diff --git a/code/modules/xenobio2/mob/xeno.dm b/code/modules/xenobio2/mob/xeno.dm index 9a854e9d8e..760eeb2239 100644 --- a/code/modules/xenobio2/mob/xeno.dm +++ b/code/modules/xenobio2/mob/xeno.dm @@ -93,7 +93,7 @@ Also includes Life and New traitdat.source = name if(!health) - stat = DEAD + set_stat(DEAD) /mob/living/simple_animal/xeno/bullet_act(var/obj/item/projectile/Proj) if(istype(Proj, /obj/item/projectile/beam/stun/xeno)) diff --git a/code/stylesheet.dm b/code/stylesheet.dm index 754fd88901..011655df47 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -65,6 +65,7 @@ em {font-style: normal;font-weight: bold;} .say {} .alert {color: #ff0000;} h1.alert, h2.alert {color: #000000;} +.ghostalert {color: #5c00e6; font-style: italic; font-weight: bold;} .emote {font-style: italic;} diff --git a/code/unit_tests/language_tests.dm b/code/unit_tests/language_tests.dm new file mode 100644 index 0000000000..b5559eaab2 --- /dev/null +++ b/code/unit_tests/language_tests.dm @@ -0,0 +1,29 @@ +/datum/unit_test/language_test_shall_have_distinct_names + name = "LANGUAGES: Entries shall have distinct names" + +/datum/unit_test/language_test_shall_have_distinct_names/start_test() + if(length(GLOB.language_name_conflicts) != 0) + var/list/name_conflict_log = list() + for(var/conflicted_name in GLOB.language_name_conflicts) + name_conflict_log += "+[length(GLOB.language_name_conflicts[conflicted_name])] languages with name \"[conflicted_name]\"!" + for(var/datum/language/L in GLOB.language_name_conflicts[conflicted_name]) + name_conflict_log += "+-+[L.type]" + fail("Some names are used by more than one language:\n" + name_conflict_log.Join("\n")) + else + pass("All languages have distinct names") + return 1 + +/datum/unit_test/language_test_shall_have_distinct_keys + name = "LANGUAGES: Entries shall have distinct keys" + +/datum/unit_test/language_test_shall_have_distinct_keys/start_test() + if(length(GLOB.language_key_conflicts) != 0) + var/list/key_conflict_log = list() + for(var/conflicted_key in GLOB.language_key_conflicts) + key_conflict_log += "+[length(GLOB.language_key_conflicts[conflicted_key])] languages with key \"[conflicted_key]\"!" + for(var/datum/language/L in GLOB.language_key_conflicts[conflicted_key]) + key_conflict_log += "+-+[L]([L.type])" + fail("Some keys are used by more than one language:\n" + key_conflict_log.Join("\n")) + else + pass("All languages in GLOB.all_languages have distinct keys") + return 1 \ No newline at end of file diff --git a/code/unit_tests/unit_test.dm b/code/unit_tests/unit_test.dm index 7537f14022..e775f6f763 100644 --- a/code/unit_tests/unit_test.dm +++ b/code/unit_tests/unit_test.dm @@ -94,10 +94,10 @@ var/total_unit_tests = 0 if(all_unit_tests_passed) log_unit_test("[ASCII_GREEN]*** All Unit Tests Passed \[[total_unit_tests]\] ***[ASCII_RESET]") - world.Del() else log_unit_test("[ASCII_RED]!!! \[[failed_unit_tests]\\[total_unit_tests]\] Unit Tests Failed !!![ASCII_RESET]") - world.Del() + log_unit_test("Caught [GLOB.total_runtimes] Runtime\s.") + world.Del() /datum/unit_test/proc/get_standard_turf() return locate(20,20,1) diff --git a/html/changelog.html b/html/changelog.html index 12c8d59b85..d3183fc169 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,75 @@ -->
+

20 March 2020

+

Aronai/Arokha updated:

+ +

Cerebulon updated:

+ +

Mechoid updated:

+ +

Meghan-Rossi updated:

+ +

Neerti updated:

+ +

PrismaticGynoid updated:

+ +

TheFurryFeline updated:

+ +

schnayy updated:

+ +

11 March 2020

Cerebulon updated:

+
+
+ Emergency Lighting: +
+
+ {{if data.locked && !data.siliconUser}} + {{:data.emergencyLights ? "Enabled" : "Disabled"}} + {{else}} + {{:helper.link(data.emergencyLights ? 'Enabled' : 'Disabled', data.emergencyLights ? 'power' : 'close', {'emergency_lighting' : 1}, null)}} + {{/if}} +
+
+ {{if data.siliconUser}}

System Overrides

diff --git a/nano/templates/shuttle_control_console_multi.tmpl b/nano/templates/shuttle_control_console_multi.tmpl new file mode 100644 index 0000000000..df25132bc7 --- /dev/null +++ b/nano/templates/shuttle_control_console_multi.tmpl @@ -0,0 +1,84 @@ +

Shuttle Status

+
+
+ {{:data.shuttle_status}} +
+ {{if data.can_cloak}} +
+ {{:data.legit ? "ATC Inhibitor" : "Cloaking Field"}} is {{:data.cloaked ? "enabled" : "disabled"}}. {{:helper.link('Toggle', 'arrowreturn-1-s', {'toggle_cloaked' : '1'}) }} +
+ {{/if}} +
+
+
+
+ Bluespace Drive: +
+
+ {{if data.shuttle_state == "idle"}} + IDLE + {{else data.shuttle_state == "warmup"}} + SPINNING UP + {{else data.shuttle_state == "in_transit"}} + ENGAGED + {{else}} + ERROR + {{/if}} +
+
+
+{{if data.has_docking}} +
+
+
+ Docking Status: +
+
+ {{if data.docking_status == "docked"}} + DOCKED + {{else data.docking_status == "docking"}} + {{if !data.docking_override}} + DOCKING + {{else}} + DOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocking"}} + {{if !data.docking_override}} + UNDOCKING + {{else}} + UNDOCKING-MANUAL + {{/if}} + {{else data.docking_status == "undocked"}} + UNDOCKED + {{else}} + ERROR + {{/if}} +
+
+ Docking Codes: +
+
+ {{:helper.link(data.docking_codes ? data.docking_codes : 'Not set', null, {'set_codes' : '1'}, null , null)}} +
+
+
+{{/if}} +
+
+ Current Destination: +
+ {{:data.destination_name}} +
+ {{:helper.link('Choose Destination', 'arrowreturn-1-s', {'pick' : '1'}, data.can_pick ? null : 'disabled' , null)}} +
+
+

Shuttle Control

+
+
+
+ {{:helper.link('Launch Shuttle', 'arrowthickstop-1-e', {'move' : '1'}, data.can_launch ? null : 'disabled' , null)}} + {{:helper.link('Cancel Launch', 'cancel', {'cancel' : '1'}, data.can_cancel ? null : 'disabled' , null)}} + {{:helper.link('Force Launch', 'alert', {'force' : '1'}, data.can_force ? null : 'disabled' , data.can_force ? 'redButton' : null)}} +
+
+
diff --git a/polaris.dme b/polaris.dme index 84d2277943..616f97dde3 100644 --- a/polaris.dme +++ b/polaris.dme @@ -293,6 +293,9 @@ #include "code\datums\observation\logged_in.dm" #include "code\datums\observation\moved.dm" #include "code\datums\observation\observation.dm" +#include "code\datums\observation\shuttle_added.dm" +#include "code\datums\observation\shuttle_moved.dm" +#include "code\datums\observation\stat_set.dm" #include "code\datums\observation\turf_changed.dm" #include "code\datums\observation\unequipped.dm" #include "code\datums\observation\z_moved.dm" @@ -358,6 +361,7 @@ #include "code\datums\uplink\hardsuit_modules.dm" #include "code\datums\uplink\implants.dm" #include "code\datums\uplink\medical.dm" +#include "code\datums\uplink\resources.dm" #include "code\datums\uplink\stealth_items.dm" #include "code\datums\uplink\stealthy_weapons.dm" #include "code\datums\uplink\telecrystals.dm" @@ -385,7 +389,6 @@ #include "code\datums\wires\suit_storage_unit.dm" #include "code\datums\wires\tesla_coil.dm" #include "code\datums\wires\vending.dm" -#include "code\datums\wires\wire_hint.dm" #include "code\datums\wires\wires.dm" #include "code\defines\gases.dm" #include "code\defines\obj.dm" @@ -444,6 +447,7 @@ #include "code\game\area\areas.dm" #include "code\game\area\asteroid_areas.dm" #include "code\game\area\Space Station 13 areas.dm" +#include "code\game\area\ss13_deprecated_areas.dm" #include "code\game\dna\dna2.dm" #include "code\game\dna\dna2_domutcheck.dm" #include "code\game\dna\dna2_helpers.dm" @@ -996,6 +1000,7 @@ #include "code\game\objects\items\weapons\autopsy.dm" #include "code\game\objects\items\weapons\bones.dm" #include "code\game\objects\items\weapons\candle.dm" +#include "code\game\objects\items\weapons\canes.dm" #include "code\game\objects\items\weapons\cigs_lighters.dm" #include "code\game\objects\items\weapons\clown_items.dm" #include "code\game\objects\items\weapons\cosmetics.dm" @@ -1351,6 +1356,7 @@ #include "code\modules\admin\verbs\debug.dm" #include "code\modules\admin\verbs\diagnostics.dm" #include "code\modules\admin\verbs\dice.dm" +#include "code\modules\admin\verbs\fps.dm" #include "code\modules\admin\verbs\getlogs.dm" #include "code\modules\admin\verbs\grief_fixers.dm" #include "code\modules\admin\verbs\lightning_strike.dm" @@ -1363,7 +1369,6 @@ #include "code\modules\admin\verbs\randomverbs.dm" #include "code\modules\admin\verbs\smite.dm" #include "code\modules\admin\verbs\striketeam.dm" -#include "code\modules\admin\verbs\ticklag.dm" #include "code\modules\admin\verbs\tripAI.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" @@ -1492,6 +1497,7 @@ #include "code\modules\clothing\ears\ears.dm" #include "code\modules\clothing\glasses\glasses.dm" #include "code\modules\clothing\glasses\hud.dm" +#include "code\modules\clothing\gloves\antagonist.dm" #include "code\modules\clothing\gloves\arm_guards.dm" #include "code\modules\clothing\gloves\boxing.dm" #include "code\modules\clothing\gloves\color.dm" @@ -1941,6 +1947,7 @@ #include "code\modules\mob\update_icons.dm" #include "code\modules\mob\_modifiers\aura.dm" #include "code\modules\mob\_modifiers\cloning.dm" +#include "code\modules\mob\_modifiers\feysight.dm" #include "code\modules\mob\_modifiers\fire.dm" #include "code\modules\mob\_modifiers\medical.dm" #include "code\modules\mob\_modifiers\modifiers.dm" @@ -2387,13 +2394,9 @@ #include "code\modules\organs\subtypes\vox.dm" #include "code\modules\organs\subtypes\xenos.dm" #include "code\modules\overmap\_defines.dm" +#include "code\modules\overmap\overmap_object.dm" #include "code\modules\overmap\sectors.dm" -#include "code\modules\overmap\ships\ship.dm" -#include "code\modules\overmap\ships\computers\engine_control.dm" -#include "code\modules\overmap\ships\computers\helm.dm" -#include "code\modules\overmap\ships\computers\shuttle.dm" -#include "code\modules\overmap\ships\engines\engine.dm" -#include "code\modules\overmap\ships\engines\thermal.dm" +#include "code\modules\overmap\spacetravel.dm" #include "code\modules\paperwork\adminpaper.dm" #include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" @@ -2508,6 +2511,7 @@ #include "code\modules\projectiles\guns\launcher\rocket.dm" #include "code\modules\projectiles\guns\launcher\syringe_gun.dm" #include "code\modules\projectiles\guns\magnetic\bore.dm" +#include "code\modules\projectiles\guns\magnetic\gasthrower.dm" #include "code\modules\projectiles\guns\magnetic\magnetic.dm" #include "code\modules\projectiles\guns\magnetic\magnetic_construction.dm" #include "code\modules\projectiles\guns\magnetic\magnetic_railgun.dm" @@ -2671,9 +2675,12 @@ #include "code\modules\shuttles\antagonist.dm" #include "code\modules\shuttles\departmental.dm" #include "code\modules\shuttles\escape_pods.dm" +#include "code\modules\shuttles\landmarks.dm" #include "code\modules\shuttles\shuttle.dm" #include "code\modules\shuttles\shuttle_arrivals.dm" +#include "code\modules\shuttles\shuttle_autodock.dm" #include "code\modules\shuttles\shuttle_console.dm" +#include "code\modules\shuttles\shuttle_console_multi.dm" #include "code\modules\shuttles\shuttle_emergency.dm" #include "code\modules\shuttles\shuttle_ferry.dm" #include "code\modules\shuttles\shuttle_specops.dm" @@ -2780,29 +2787,38 @@ #include "code\modules\xenoarcheaology\artifacts\crystal.dm" #include "code\modules\xenoarcheaology\artifacts\gigadrill.dm" #include "code\modules\xenoarcheaology\artifacts\replicator.dm" +#include "code\modules\xenoarcheaology\effects\animate_anomaly.dm" #include "code\modules\xenoarcheaology\effects\badfeeling.dm" #include "code\modules\xenoarcheaology\effects\berserk.dm" +#include "code\modules\xenoarcheaology\effects\cannibal.dm" #include "code\modules\xenoarcheaology\effects\cellcharge.dm" #include "code\modules\xenoarcheaology\effects\celldrain.dm" #include "code\modules\xenoarcheaology\effects\cold.dm" #include "code\modules\xenoarcheaology\effects\dnaswitch.dm" +#include "code\modules\xenoarcheaology\effects\electric_field.dm" #include "code\modules\xenoarcheaology\effects\emp.dm" +#include "code\modules\xenoarcheaology\effects\feysight.dm" #include "code\modules\xenoarcheaology\effects\forcefield.dm" +#include "code\modules\xenoarcheaology\effects\gaia.dm" #include "code\modules\xenoarcheaology\effects\gasco2.dm" #include "code\modules\xenoarcheaology\effects\gasnitro.dm" #include "code\modules\xenoarcheaology\effects\gasoxy.dm" #include "code\modules\xenoarcheaology\effects\gasphoron.dm" #include "code\modules\xenoarcheaology\effects\gassleeping.dm" #include "code\modules\xenoarcheaology\effects\goodfeeling.dm" +#include "code\modules\xenoarcheaology\effects\gravitational_waves.dm" #include "code\modules\xenoarcheaology\effects\heal.dm" #include "code\modules\xenoarcheaology\effects\heat.dm" #include "code\modules\xenoarcheaology\effects\hurt.dm" +#include "code\modules\xenoarcheaology\effects\poltergeist.dm" #include "code\modules\xenoarcheaology\effects\radiate.dm" +#include "code\modules\xenoarcheaology\effects\resurrect.dm" #include "code\modules\xenoarcheaology\effects\roboheal.dm" #include "code\modules\xenoarcheaology\effects\robohurt.dm" #include "code\modules\xenoarcheaology\effects\sleepy.dm" #include "code\modules\xenoarcheaology\effects\stun.dm" #include "code\modules\xenoarcheaology\effects\teleport.dm" +#include "code\modules\xenoarcheaology\effects\vampire.dm" #include "code\modules\xenoarcheaology\finds\eguns.dm" #include "code\modules\xenoarcheaology\finds\find_spawning.dm" #include "code\modules\xenoarcheaology\finds\finds.dm" @@ -2828,6 +2844,7 @@ #include "code\modules\xenobio\machinery\processor.dm" #include "code\modules\xgm\xgm_gas_data.dm" #include "code\modules\xgm\xgm_gas_mixture.dm" +#include "code\unit_tests\language_tests.dm" #include "code\unit_tests\loadout_tests.dm" #include "code\unit_tests\map_tests.dm" #include "code\unit_tests\mob_tests.dm" diff --git a/sound/effects/antag_notice/cult_alert.ogg b/sound/effects/antag_notice/cult_alert.ogg new file mode 100644 index 0000000000..9fa22df51d Binary files /dev/null and b/sound/effects/antag_notice/cult_alert.ogg differ diff --git a/sound/effects/antag_notice/deathsquid_alert.ogg b/sound/effects/antag_notice/deathsquid_alert.ogg new file mode 100644 index 0000000000..7c2774f0a0 Binary files /dev/null and b/sound/effects/antag_notice/deathsquid_alert.ogg differ diff --git a/sound/effects/antag_notice/general_baddie_alert.ogg b/sound/effects/antag_notice/general_baddie_alert.ogg new file mode 100644 index 0000000000..6f0c0dd097 Binary files /dev/null and b/sound/effects/antag_notice/general_baddie_alert.ogg differ diff --git a/sound/effects/antag_notice/general_goodie_alert.ogg b/sound/effects/antag_notice/general_goodie_alert.ogg new file mode 100644 index 0000000000..59a4e3f26d Binary files /dev/null and b/sound/effects/antag_notice/general_goodie_alert.ogg differ diff --git a/sound/effects/antag_notice/ling_alert.ogg b/sound/effects/antag_notice/ling_alert.ogg new file mode 100644 index 0000000000..1132ccca29 Binary files /dev/null and b/sound/effects/antag_notice/ling_alert.ogg differ diff --git a/sound/effects/antag_notice/malf_alert.ogg b/sound/effects/antag_notice/malf_alert.ogg new file mode 100644 index 0000000000..feea5fbf19 Binary files /dev/null and b/sound/effects/antag_notice/malf_alert.ogg differ diff --git a/sound/effects/antag_notice/technomancer_alert.ogg b/sound/effects/antag_notice/technomancer_alert.ogg new file mode 100644 index 0000000000..dabc828557 Binary files /dev/null and b/sound/effects/antag_notice/technomancer_alert.ogg differ diff --git a/sound/effects/antag_notice/traitor_alert.ogg b/sound/effects/antag_notice/traitor_alert.ogg new file mode 100644 index 0000000000..ca0efa0ea0 Binary files /dev/null and b/sound/effects/antag_notice/traitor_alert.ogg differ diff --git a/sound/effects/genetics.ogg b/sound/effects/genetics.ogg new file mode 100644 index 0000000000..9b28be68b5 Binary files /dev/null and b/sound/effects/genetics.ogg differ diff --git a/sound/effects/shuttles/shuttle_landing.ogg b/sound/effects/shuttles/shuttle_landing.ogg new file mode 100644 index 0000000000..fcb723416b Binary files /dev/null and b/sound/effects/shuttles/shuttle_landing.ogg differ diff --git a/sound/effects/shuttles/shuttle_takeoff.ogg b/sound/effects/shuttles/shuttle_takeoff.ogg new file mode 100644 index 0000000000..06dac1c788 Binary files /dev/null and b/sound/effects/shuttles/shuttle_takeoff.ogg differ diff --git a/sound/weapons/attributions.txt b/sound/weapons/attributions.txt new file mode 100644 index 0000000000..fe2911c873 --- /dev/null +++ b/sound/weapons/attributions.txt @@ -0,0 +1,9 @@ +bulletflyby sounds are by kMoon on freesound.org: +bulletflyby:https://www.freesound.org/people/kMoon/sounds/90782/ +bulletflyby2:https://www.freesound.org/people/kMoon/sounds/90784/ +bulletflyby3:https://www.freesound.org/people/kMoon/sounds/90783/ +No changes were made to the sounds, and all credit goes to kMoon. + +batreflect sounds are by shadoWisp on freesound.org: +https://www.freesound.org/people/shadoWisp/sounds/252044/ +Small parts of the sound are cut out and used. \ No newline at end of file diff --git a/sound/weapons/bulletflyby.ogg b/sound/weapons/bulletflyby.ogg new file mode 100644 index 0000000000..ce9405a577 Binary files /dev/null and b/sound/weapons/bulletflyby.ogg differ diff --git a/sound/weapons/bulletflyby2.ogg b/sound/weapons/bulletflyby2.ogg new file mode 100644 index 0000000000..63956534ba Binary files /dev/null and b/sound/weapons/bulletflyby2.ogg differ diff --git a/sound/weapons/bulletflyby3.ogg b/sound/weapons/bulletflyby3.ogg new file mode 100644 index 0000000000..e8afd28b06 Binary files /dev/null and b/sound/weapons/bulletflyby3.ogg differ diff --git a/sound/weapons/effects/batreflect1.ogg b/sound/weapons/effects/batreflect1.ogg new file mode 100644 index 0000000000..43f19710c5 Binary files /dev/null and b/sound/weapons/effects/batreflect1.ogg differ diff --git a/sound/weapons/effects/batreflect2.ogg b/sound/weapons/effects/batreflect2.ogg new file mode 100644 index 0000000000..d58e4e906c Binary files /dev/null and b/sound/weapons/effects/batreflect2.ogg differ diff --git a/sound/weapons/effects/ric1.ogg b/sound/weapons/effects/ric1.ogg new file mode 100644 index 0000000000..9f22888722 Binary files /dev/null and b/sound/weapons/effects/ric1.ogg differ diff --git a/sound/weapons/effects/ric2.ogg b/sound/weapons/effects/ric2.ogg new file mode 100644 index 0000000000..03d02b8c45 Binary files /dev/null and b/sound/weapons/effects/ric2.ogg differ diff --git a/sound/weapons/effects/ric3.ogg b/sound/weapons/effects/ric3.ogg new file mode 100644 index 0000000000..2d7a3ce8a0 Binary files /dev/null and b/sound/weapons/effects/ric3.ogg differ diff --git a/sound/weapons/effects/ric4.ogg b/sound/weapons/effects/ric4.ogg new file mode 100644 index 0000000000..e0193a690b Binary files /dev/null and b/sound/weapons/effects/ric4.ogg differ diff --git a/sound/weapons/effects/ric5.ogg b/sound/weapons/effects/ric5.ogg new file mode 100644 index 0000000000..b1064eece1 Binary files /dev/null and b/sound/weapons/effects/ric5.ogg differ diff --git a/sound/weapons/effects/searwall.ogg b/sound/weapons/effects/searwall.ogg new file mode 100644 index 0000000000..6a29326f24 Binary files /dev/null and b/sound/weapons/effects/searwall.ogg differ diff --git a/sound/weapons/ionrifle.ogg b/sound/weapons/ionrifle.ogg new file mode 100644 index 0000000000..b808068e55 Binary files /dev/null and b/sound/weapons/ionrifle.ogg differ diff --git a/sound/weapons/sear.ogg b/sound/weapons/sear.ogg new file mode 100644 index 0000000000..c6d5f6846c Binary files /dev/null and b/sound/weapons/sear.ogg differ diff --git a/sound/weapons/zapbang.ogg b/sound/weapons/zapbang.ogg new file mode 100644 index 0000000000..4e14e30a11 Binary files /dev/null and b/sound/weapons/zapbang.ogg differ diff --git a/tools/travis/compile_and_run.sh b/tools/travis/compile_and_run.sh new file mode 100644 index 0000000000..b4f9e403b0 --- /dev/null +++ b/tools/travis/compile_and_run.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +RED='\033[0;31m' +NC='\033[0m' + +source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup + +# Copy example configs +cp config/example/* config/ + +# Define any unit test defines that need to run +echo "#define ${TEST_DEFINE} 1" > ${TEST_FILE} + +# Compile a copy of the codebase +DreamMaker $BASENAME.dme +exitVal=$? + +# Compile failed on map_test +if [ $exitVal -gt 0 ] && [ $TEST_DEFINE = "MAP_TEST" ]; then + echo "${RED}Some POIs appear to contain map-specific objects or code. Please isolate map-specific items/code from POIs.${NC}" + exit 1 +# Compile failed on away_mission_test +elif [ $exitVal -gt 0 ] && [ $TEST_DEFINE = "AWAY_MISSION_TEST" ]; then + echo "${RED}Some away missions failed to compile. Please check them for missing items/objects by trying to compile them in DreamMaker.${NC}" + exit 1 +# Compile failed on unit_test +elif [ $exitVal -gt 0 ] && [ $TEST_DEFINE = "UNIT_TEST" ]; then + echo "${RED}Compiling the codebase normally failed. Please review the compile errors and correct them, usually before making your PR.${NC}" + exit 1 +fi + +# If we're running, run +if [ $RUN -eq 1 ]; +then + DreamDaemon $BASENAME.dmb -invisible -trusted -core 2>&1 | tee log.txt; + grep "All Unit Tests Passed" log.txt + grep "Caught 0 Runtimes" log.txt +fi diff --git a/install-byond.sh b/tools/travis/install_byond.sh similarity index 100% rename from install-byond.sh rename to tools/travis/install_byond.sh diff --git a/tools/travis/validate_files.sh b/tools/travis/validate_files.sh new file mode 100644 index 0000000000..1b5a756903 --- /dev/null +++ b/tools/travis/validate_files.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +RED='\033[0;31m' +NC='\033[0m' +FAILED=0 + +#Checking for step_x/step_y defined in any maps anywhere. +(! grep 'step_[xy]' maps/**/*.dmm) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}The variables 'step_x' and 'step_y' are present on a map, and they 'break' movement ingame.${NC}" + FAILED=1 +fi + +#Checking for 'tag' set to something on maps +(! grep -Pn '( |\t|;|{)tag( ?)=' maps/**/*.dmm) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}A map has 'tag' set on an atom. It may cause problems and should be removed.${NC}" + FAILED=1 +fi + +#Checking for duplicate nanoui templates +(! find nano/templates/ -type f -exec md5sum {} + | sort | uniq -D -w 32 | grep nano) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}There are identical nanoui template files present.${NC}" + FAILED=1 +fi + +#Checking for broken HTML tags (didn't close the quote for class) +(! grep -En "<\s*span\s+class\s*=\s*('[^'>]+|[^'>]+')\s*>" **/*.dm) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}A broken span tag class is present (check quotes).${NC}" + FAILED=1 +fi + +#Checking for any 'checked' maps that include 'test' +(! grep 'maps\\.*test.*' *.dme) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}A map containing the word 'test' is included. This is not allowed to be committed.${NC}" + FAILED=1 +fi + +#Check for weird indentation in any .dm files +awk -f tools/indentation.awk **/*.dm +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}Indention testing failed. Please see results and fix indentation.${NC}" + FAILED=1 +fi + +#Checking for a change to html/changelogs/example.yml +md5sum -c - <<< "88490b460c26947f5ec1ab1bb9fa9f17 *html/changelogs/example.yml" +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}Do not modify the example.yml changelog file.${NC}" + FAILED=1 +fi + +#Checking for color macros +(num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ]) +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}Do not use any byond color macros (such as \blue), they are deprecated.${NC}" + FAILED=1 +fi + +#Checking for missed tags +python tools/TagMatcher/tag-matcher.py ../.. +retVal=$? +if [ $retVal -ne 0 ]; then + echo -e "${RED}Some HTML tags are missing their opening/closing partners. Please correct this.${NC}" + FAILED=1 +fi + +# Quit with our status code +exit $FAILED \ No newline at end of file