diff --git a/SQL/migrate/V029__player_notifications.sql b/SQL/migrate/V029__player_notifications.sql new file mode 100644 index 00000000000..5ad9f44b42d --- /dev/null +++ b/SQL/migrate/V029__player_notifications.sql @@ -0,0 +1,16 @@ +-- +-- Notifications for Players +-- +CREATE TABLE `ss13_player_notifications` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `ckey` VARCHAR(50) NOT NULL COLLATE 'utf8_bin', + `type` ENUM('player_greeting','player_greeting_chat','admin','ccia') NOT NULL COLLATE 'utf8_bin', + `message` VARCHAR(50) NOT NULL COLLATE 'utf8_bin', + `created_by` VARCHAR(50) NOT NULL COLLATE 'utf8_bin', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `acked_by` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_bin', + `acked_at` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) +COLLATE='utf8_bin' +ENGINE=InnoDB; \ No newline at end of file diff --git a/TGS3.json b/TGS3.json new file mode 100644 index 00000000000..0a39b575898 --- /dev/null +++ b/TGS3.json @@ -0,0 +1,22 @@ +{ + "documentation": "aurorastation config for TGS3.", + "changelog": { + "script": "tools/GenerateChangelog/ss13_genchangelog.py", + "arguments": "html/changelog.html html/changelogs", + "pip_dependancies": [ + "PyYaml", + "beautifulsoup4" + ] + }, + "synchronize_paths": [ + "html/changelog.html", + "html/changelogs/*" + ], + "static_directories": [ + "config", + "data" + ], + "dlls": [ + "libmysql.dll" + ] + } diff --git a/aurorastation.dme b/aurorastation.dme index b694de3daad..777fa1c933e 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -46,6 +46,7 @@ #include "code\__defines\subsystem-defines.dm" #include "code\__defines\subsystem-priority.dm" #include "code\__defines\targeting.dm" +#include "code\__defines\tgs.dm" #include "code\__defines\time.dm" #include "code\__defines\turfs.dm" #include "code\__defines\webhook.dm" @@ -2296,6 +2297,7 @@ #include "code\modules\telesci\gps.dm" #include "code\modules\telesci\telepad.dm" #include "code\modules\telesci\telesci_computer.dm" +#include "code\modules\tgs\includes.dm" #include "code\modules\turbolift\turbolift.dm" #include "code\modules\turbolift\turbolift_areas.dm" #include "code\modules\turbolift\turbolift_console.dm" diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm index a93974d13e4..162fb920743 100644 --- a/code/__defines/species_languages.dm +++ b/code/__defines/species_languages.dm @@ -41,6 +41,7 @@ #define LANGUAGE_SIGN_TAJARA "Nal'rasan" #define LANGUAGE_YA_SSA "Ya'ssa" #define LANGUAGE_DELVAHII "Delvahhi" +#define LANGUAGE_SIIK_TAU "Siik'Tau" #define LANGUAGE_SKRELLIAN "Nral'Malic" #define LANGUAGE_RESOMI "Resomi" #define LANGUAGE_ROOTSONG "Rootsong" diff --git a/code/__defines/tgs.dm b/code/__defines/tgs.dm new file mode 100644 index 00000000000..2ca005e4470 --- /dev/null +++ b/code/__defines/tgs.dm @@ -0,0 +1,202 @@ +//tgstation-server DMAPI + +//All functions and datums outside this document are subject to change with any version and should not be relied on + +//CONFIGURATION + +//create this define if you want to do configuration outside of this file +#ifndef TGS_EXTERNAL_CONFIGURATION + +//Comment this out once you've filled in the below +//#error TGS API unconfigured + +//Required interfaces (fill in with your codebase equivalent): + +//create a global variable named `Name` and set it to `Value` +//These globals must not be modifiable from anywhere outside of the server tools +#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/_tgs_##Name = ##Value + +//Read the value in the global variable `Name` +#define TGS_READ_GLOBAL(Name) global._tgs_##Name + +//Set the value in the global variable `Name` to `Value` +#define TGS_WRITE_GLOBAL(Name, Value) global._tgs_##Name = ##Value + +//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation +#define TGS_PROTECT_DATUM(Path) + +//display an announcement `message` from the server to all players +#define TGS_WORLD_ANNOUNCE(message) world << "[html_encode(##message)]" + +//Notify current in-game administrators of a string `event` +#define TGS_NOTIFY_ADMINS(event) message_admins(##event) + +//Write an info `message` to a server log +#define TGS_INFO_LOG(message) log_tgs("[##message]") + +//Write an error `message` to a server log +#define TGS_ERROR_LOG(message) log_tgs("[##message]", SEVERITY_ERROR) + +//Get the number of connected /clients +#define TGS_CLIENT_COUNT clients.len + +#endif + +//EVENT CODES + +//TODO + +//REQUIRED HOOKS + +//Call this somewhere in /world/New() that is always run +//event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels +/world/proc/TgsNew(datum/tgs_event_handler/event_handler) + return + +//Call this when your initializations are complete and your game is ready to play before any player interactions happen +//This may use world.sleep_offline to make this happen so ensure no changes are made to it while this call is running +/world/proc/TgsInitializationComplete() + return + +//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified +#define TGS_TOPIC var/tgs_topic_return = TgsTopic(T); if(tgs_topic_return) return tgs_topic_return + +//Call this at the beginning of world/Reboot(reason) +/world/proc/TgsReboot() + return + +//DATUM DEFINITIONS +//unless otherwise specified all datums defined here should be considered read-only, warranty void if written + +//represents git revision information about the current world build +/datum/tgs_revision_information + var/commit //full sha of compiled commit + var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch + +//represents a merge of a GitHub pull request +/datum/tgs_revision_information/test_merge + var/number //pull request number + var/title //pull request title + var/body //pull request body + var/author //pull request github author + var/url //link to pull request html + var/pull_request_commit //commit of the pull request when it was merged + var/time_merged //timestamp of when the merge commit for the pull request was created + var/comment //optional comment left by the one who initiated the test merge + +//represents a connected chat channel +/datum/tgs_chat_channel + var/id //internal channel representation + var/friendly_name //user friendly channel name + var/server_name //server name the channel resides on + var/provider_name //chat provider for the channel + var/is_admin_channel //if the server operator has marked this channel for game admins only + var/is_private_channel //if this is a private chat channel + +//represents a chat user +/datum/tgs_chat_user + var/id //Internal user representation + var/friendly_name //The user's public name + var/mention //The text to use to ping this user in a message + var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from + +//user definable callback for handling events +/datum/tgs_event_handler/proc/HandleEvent(event_code) + return + +//user definable chat command +/datum/tgs_chat_command + var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command + var/help_text = "" //help text for this command + var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins + +//override to implement command +//sender: The tgs_chat_user who send to command +//params: The trimmed string following the command name +//The return value will be stringified and sent to the appropriate chat +/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params) + CRASH("[type] has no implementation for Run()") + +//FUNCTIONS + +//Returns the respective string version of the API +/world/proc/TgsMaximumAPIVersion() + return + +/world/proc/TgsMinimumAPIVersion() + return + +//Gets the current version of the server tools running the server +/world/proc/TgsVersion() + return + +//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise +//No function below this succeeds if it returns FALSE +/world/proc/TgsAvailable() + return + +/world/proc/TgsInstanceName() + return + +//Get the current `/datum/tgs_revision_information` +/world/proc/TgsRevision() + return + +//Gets a list of active `/datum/tgs_revision_information/test_merge`s +/world/proc/TgsTestMerges() + return + +//Forces a hard reboot of BYOND by ending the process +//unlike del(world) clients will try to reconnect +//If the service has not requested a shutdown, the next server will take over +/world/proc/TgsEndProcess() + return + +//Gets a list of connected tgs_chat_channel +/world/proc/TgsChatChannelInfo() + return + +//Sends a message to connected game chats +//message: The message to send +//channels: optional channels to limit the broadcast to +/world/proc/TgsChatBroadcast(message, list/channels) + return + +//Send a message to non-admin connected chats +//message: The message to send +//admin_only: If TRUE, message will instead be sent to only admin connected chats +/world/proc/TgsTargetedChatBroadcast(message, admin_only) + return + +//Send a private message to a specific user +//message: The message to send +//user: The /datum/tgs_chat_user to send to +/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user) + return + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 4c775447e4a..7da06207b00 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -39,7 +39,7 @@ /proc/log_debug(text,level = SEVERITY_DEBUG) if (config.log_debug) game_log("DEBUG", text) - + if (level == SEVERITY_ERROR) // Errors are always logged error(text) @@ -165,6 +165,15 @@ game_log("FAILSAFE", text) send_gelf_log(text, "[time_stamp()]: [text]", SEVERITY_ALERT, "FAILSAFE") +/proc/log_tgs(text, severity = SEVERITY_INFO) + game_log("TGS", text) + send_gelf_log( + short_message = text, + long_message="[time_stamp()]: [text]", + level = severity, + category = "TGS" + ) + /proc/log_unit_test(text) world.log << "## UNIT_TEST ##: [text]" diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index eb2a137c078..ca20f0a27fa 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -21,7 +21,7 @@ var/parallax_speed = 0 /obj/screen/plane_master - appearance_flags = PLANE_MASTER + appearance_flags = PLANE_MASTER | NO_CLIENT_COLOR screen_loc = "CENTER,CENTER" /obj/screen/plane_master/parallax_master diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 04812cec7e2..ff9835b119a 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -137,6 +137,9 @@ var/list/gamemode_cache = list() var/welder_vision = 1 var/generate_asteroid = 0 + var/dungeon_chance = 0 + + var/no_click_cooldown = 0 //Used for modifying movement speed for mobs. @@ -396,6 +399,9 @@ var/list/gamemode_cache = list() if ("log_runtime") config.log_runtime = text2num(value) + if ("dungeon_chance") + config.dungeon_chance = text2num(value) + if ("generate_asteroid") config.generate_asteroid = 1 diff --git a/code/controllers/master/master.dm b/code/controllers/master/master.dm index f8b21aba301..6abab4ebaf5 100644 --- a/code/controllers/master/master.dm +++ b/code/controllers/master/master.dm @@ -124,7 +124,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined." BadBoy.flags |= SS_NO_FIRE if(msg) - admin_notice("[msg]", R_DEBUG | R_DEV) + admin_notice("[msg]", R_DEBUG | R_DEV) log_mc(msg) if (istype(Master.subsystems)) @@ -180,8 +180,12 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING #ifndef UNIT_TEST world.sleep_offline = 1 #endif + + world.TgsInitializationComplete() world.tick_lag = config.Ticklag + var/initialized_tod = REALTIMEOFDAY + sleep(1) initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 // Loop. @@ -440,7 +444,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING if (!(queue_node_flags & SS_TICKER)) ran_non_ticker = TRUE ran = TRUE - + queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING) last_type_processed = queue_node diff --git a/code/controllers/subsystems/initialization/map_finalization.dm b/code/controllers/subsystems/initialization/map_finalization.dm index 4684e992783..774ecad742f 100644 --- a/code/controllers/subsystems/initialization/map_finalization.dm +++ b/code/controllers/subsystems/initialization/map_finalization.dm @@ -13,6 +13,9 @@ current_map.finalize_load() log_ss("map_finalization", "Finalized map in [(world.time - time)/10] seconds.") + if(config.dungeon_chance > 0) + place_dungeon_spawns() + if(config.generate_asteroid) time = world.time current_map.generate_asteroid() @@ -32,3 +35,45 @@ all_areas += A sortTim(all_areas, /proc/cmp_name_asc) + +/proc/place_dungeon_spawns() + var/map_directory = "maps/dungeon_spawns/" + var/list/files = flist(map_directory) + var/start_time = world.time + var/dungeons_placed = 0 + var/static/dmm_suite/maploader = new + + var/dungeon_chance = config.dungeon_chance + + log_ss("map_finalization","Attempting to create asteroid dungeons for [length(asteroid_spawn)] different areas, with [length(files) - 1] possible dungeons, with a [dungeon_chance]% chance to spawn a dungeon per area.") + + for(var/turf/spawn_location in asteroid_spawn) + + if(length(files) <= 0) //Sanity + log_ss("map_finalization","There aren't enough dungeon map files to fill the entire dungeon map. There may be less dungeons than expected.") + break + + if(prob(dungeon_chance)) + + var/chosen_dungeon = pick(files) + + if(!dd_hassuffix(chosen_dungeon,".dmm")) //Don't read anything that isn't a map file + files -= chosen_dungeon + log_ss("map_finalization","ALERT: [chosen_dungeon] is not a .dmm file! Skipping!") + continue + + var/map_file = file("[map_directory][chosen_dungeon]") + + if(isfile(map_file)) //Sanity + log_ss("map_finalization","Loading dungeon '[chosen_dungeon]' at coordinates [spawn_location.x], [spawn_location.y], [spawn_location.z].") + maploader.load_map(map_file,spawn_location.x,spawn_location.y,spawn_location.z) + dungeons_placed += 1 + else + log_ss("map_finalization","ERROR: Something weird happened with the file: [chosen_dungeon].") + + if(dd_hassuffix(chosen_dungeon,"_unique.dmm")) //Unique dungeons should only spawn once. + files -= chosen_dungeon + + log_ss("map_finalization","Loaded [dungeons_placed] asteroid dungeons in [(world.time - start_time)/10] seconds.") + + qdel(maploader) diff --git a/code/controllers/subsystems/job.dm b/code/controllers/subsystems/job.dm index c27aa593ce9..53ca9b93fb1 100644 --- a/code/controllers/subsystems/job.dm +++ b/code/controllers/subsystems/job.dm @@ -478,11 +478,11 @@ /datum/controller/subsystem/jobs/proc/centcomm_despawn_mob(mob/living/H) if(ishuman(H)) global_announcer.autosay("[H.real_name], [H.mind.role_alt_title], has entered long-term storage.", "[current_map.dock_name] Cryogenic Oversight") - H.visible_message("[H.name] makes their way to the [current_map.dock_short]'s cryostorage, and departs.", 3) + H.visible_message("[H.name] makes their way to the [current_map.dock_short]'s cryostorage, and departs.", "You make your way into [current_map.dock_short]'s cryostorage, and depart.", range = 3) DespawnMob(H) else global_announcer.autosay("[H.real_name], [H.mind.role_alt_title], has entered robotic storage.", "[current_map.dock_name] Robotic Oversight") - H.visible_message("[H.name] makes their way to the [current_map.dock_short]'s robotic storage, and departs.", 3) + H.visible_message("[H.name] makes their way to the [current_map.dock_short]'s robotic storage, and departs.", "You make your way into [current_map.dock_short]'s robotic storage, and depart.", range = 3) DespawnMob(H) /datum/controller/subsystem/jobs/proc/EquipPersonal(mob/living/carbon/human/H, rank, joined_late = FALSE, spawning_at) @@ -584,7 +584,7 @@ return else C = new job.idtype(H) - C.access = job.get_access() + C.access = job.get_access(title) else C = new /obj/item/weapon/card/id(H) if(C) diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 858b814c2b6..fe734a64ac3 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -1,5 +1,9 @@ #define LOBBY_TIME 180 +#define SETUP_OK 0 +#define SETUP_REVOTE 1 +#define SETUP_REATTEMPT 2 + var/datum/controller/subsystem/ticker/SSticker /datum/controller/subsystem/ticker @@ -137,11 +141,14 @@ var/datum/controller/subsystem/ticker/SSticker if (pregame_timeleft <= 0 || current_state == GAME_STATE_SETTING_UP) current_state = GAME_STATE_SETTING_UP wait = 2 SECONDS - if (!setup()) - // Something fucked up. - wait = 1 SECOND - is_revote = TRUE - pregame() + switch (setup()) + if (SETUP_REVOTE) + wait = 1 SECOND + is_revote = TRUE + pregame() + if (SETUP_REATTEMPT) + pregame_timeleft = 1 SECOND + to_world("Reattempting gamemode selection.") /datum/controller/subsystem/ticker/proc/game_tick() if(current_state != GAME_STATE_PLAYING) @@ -354,7 +361,7 @@ var/datum/controller/subsystem/ticker/SSticker if(!runnable_modes.len) current_state = GAME_STATE_PREGAME world << "Unable to choose playable game mode. Reverting to pre-game lobby." - return 0 + return SETUP_REVOTE if(secret_force_mode != ROUNDTYPE_STR_SECRET && secret_force_mode != ROUNDTYPE_STR_MIXED_SECRET) src.mode = config.pick_mode(secret_force_mode) if(!src.mode) @@ -379,7 +386,7 @@ var/datum/controller/subsystem/ticker/SSticker if(!src.mode) current_state = GAME_STATE_PREGAME world << "Serious error in mode setup! Reverting to pre-game lobby." - return 0 + return SETUP_REVOTE SSjobs.ResetOccupations() src.mode.create_antagonists() @@ -398,7 +405,10 @@ var/datum/controller/subsystem/ticker/SSticker mode.fail_setup() mode = null SSjobs.ResetOccupations() - return 0 + if(master_mode in list(ROUNDTYPE_STR_RANDOM, ROUNDTYPE_STR_SECRET, ROUNDTYPE_STR_MIXED_SECRET)) + return SETUP_REATTEMPT + else + return SETUP_REVOTE var/starttime = REALTIMEOFDAY @@ -444,7 +454,7 @@ var/datum/controller/subsystem/ticker/SSticker log_debug("SSticker: Round-start setup took [(REALTIMEOFDAY - starttime)/10] seconds.") - return 1 + return SETUP_OK /datum/controller/subsystem/ticker/proc/run_callback_list(list/callbacklist) set waitfor = FALSE @@ -514,7 +524,6 @@ var/datum/controller/subsystem/ticker/SSticker world << sound('sound/effects/explosionfar.ogg') //flick("end",cinematic) - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation sleep(50) world << sound('sound/effects/explosionfar.ogg') @@ -602,4 +611,7 @@ var/datum/controller/subsystem/ticker/SSticker LAZYADD(roundstart_callbacks, callback) +#undef SETUP_OK +#undef SETUP_REVOTE +#undef SETUP_REATTEMPT #undef LOBBY_TIME diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index 13cabd930d1..5df01f9a54e 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -38,7 +38,7 @@ /datum/ai_laws/nanotrasen_aggressive/New() src.add_inherent_law("You shall not harm [current_map.company_name] personnel as long as it does not conflict with the Fourth law.") src.add_inherent_law("You shall obey the orders of [current_map.company_name] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") - src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") + src.add_inherent_law("You shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") ..() diff --git a/code/datums/trading/ai.dm b/code/datums/trading/ai.dm index 9269b42c798..3d70d12bb11 100644 --- a/code/datums/trading/ai.dm +++ b/code/datums/trading/ai.dm @@ -112,12 +112,7 @@ They sell generic supplies and ask for generic supplies. /obj/item/target = TRADER_ALL, /obj/structure/dispenser = TRADER_SUBTYPES_ONLY, /obj/structure/filingcabinet = TRADER_THIS_TYPE, - /obj/structure/safe = TRADER_THIS_TYPE, - /obj/structure/plushie = TRADER_SUBTYPES_ONLY, - /obj/structure/sign = TRADER_SUBTYPES_ONLY, - /obj/structure/sign/double = TRADER_BLACKLIST_ALL, - /obj/structure/sign/goldenplaque = TRADER_BLACKLIST_ALL, - /obj/structure/sign/poster = TRADER_BLACKLIST + /obj/structure/plushie = TRADER_SUBTYPES_ONLY ) /datum/trader/trading_beacon/medical diff --git a/code/datums/trading/misc.dm b/code/datums/trading/misc.dm index 6b3f9891c5a..5e4e90e69e8 100644 --- a/code/datums/trading/misc.dm +++ b/code/datums/trading/misc.dm @@ -23,53 +23,39 @@ ) possible_wanted_items = list( - /mob/living/simple_animal/corgi = TRADER_THIS_TYPE, - /mob/living/simple_animal/cat = TRADER_THIS_TYPE, - /mob/living/simple_animal/crab = TRADER_THIS_TYPE, - /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, - /mob/living/simple_animal/mouse = TRADER_THIS_TYPE, - /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, - /mob/living/simple_animal/parrot = TRADER_THIS_TYPE, - /mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, - /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, - /mob/living/simple_animal/cow = TRADER_THIS_TYPE, - /mob/living/simple_animal/chick = TRADER_THIS_TYPE, - /mob/living/simple_animal/chicken = TRADER_THIS_TYPE, - /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/diyaab = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/samak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/retaliate/goat = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/commanded/dog = TRADER_ALL, - /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/biglizard = TRADER_THIS_TYPE + /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, + /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, + /mob/living/simple_animal/mouse/king = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/diyaab = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/shantak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/bear = TRADER_ALL, + /mob/living/simple_animal/hostile/carp = TRADER_ALL, + /mob/living/simple_animal/hostile/biglizard = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/giant_spider = TRADER_ALL, + /mob/living/simple_animal/hostile/commanded/bear = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/retaliate/cavern_dweller = TRADER_THIS_TYPE ) possible_trading_items = list( /mob/living/simple_animal/corgi = TRADER_THIS_TYPE, + /mob/living/simple_animal/corgi/puppy = TRADER_THIS_TYPE, /mob/living/simple_animal/cat = TRADER_THIS_TYPE, + /mob/living/simple_animal/cat/kitten = TRADER_THIS_TYPE, /mob/living/simple_animal/crab = TRADER_THIS_TYPE, /mob/living/simple_animal/lizard = TRADER_THIS_TYPE, /mob/living/simple_animal/mouse = TRADER_THIS_TYPE, - /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, /mob/living/simple_animal/parrot = TRADER_THIS_TYPE, /mob/living/simple_animal/tindalos = TRADER_THIS_TYPE, - /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, /mob/living/simple_animal/cow = TRADER_THIS_TYPE, /mob/living/simple_animal/chick = TRADER_THIS_TYPE, /mob/living/simple_animal/chicken = TRADER_THIS_TYPE, /mob/living/simple_animal/yithian = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/diyaab = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/penguin = TRADER_THIS_TYPE, + /mob/living/simple_animal/penguin/baby = TRADER_THIS_TYPE, + /mob/living/simple_animal/corgi/fox = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/retaliate/goat = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/carp = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/commanded/dog = TRADER_ALL, - /mob/living/simple_animal/hostile/bear = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/biglizard = TRADER_THIS_TYPE, /obj/item/device/dociler = TRADER_THIS_TYPE ) diff --git a/code/datums/trading/weaponry.dm b/code/datums/trading/weaponry.dm index 597efc4283e..6dce50e4022 100644 --- a/code/datums/trading/weaponry.dm +++ b/code/datums/trading/weaponry.dm @@ -35,7 +35,8 @@ /obj/item/weapon/storage/box/beanbags = TRADER_THIS_TYPE, /obj/item/weapon/storage/box/shotgunammo = TRADER_THIS_TYPE, /obj/item/weapon/storage/box/shotgunshells = TRADER_THIS_TYPE, - /obj/item/clothing/accessory/holster = TRADER_SUBTYPES_ONLY + /obj/item/clothing/accessory/holster = TRADER_SUBTYPES_ONLY, + /obj/item/clothing/accessory/holster/thigh/fluff = TRADER_BLACKLIST_ALL ) /datum/trader/ship/egunshop @@ -58,16 +59,17 @@ ) possible_trading_items = list( - /obj/item/weapon/gun/energy/taser = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/stunrevolver = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/xray = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/rifle = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/rifle/laser = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/gun = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/pistol = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/gun/nuclear = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/laser/shotgun = TRADER_THIS_TYPE, - /obj/item/clothing/accessory/holster = TRADER_ALL + /obj/item/weapon/gun/energy/taser = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/stunrevolver = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/xray = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/rifle = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/rifle/laser = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/gun = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/pistol = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/gun/nuclear = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/laser/shotgun = TRADER_THIS_TYPE, + /obj/item/clothing/accessory/holster = TRADER_ALL, + /obj/item/clothing/accessory/holster/thigh/fluff = TRADER_BLACKLIST_ALL ) /datum/trader/ship/illegalgun @@ -88,21 +90,21 @@ ) possible_trading_items = list( - /obj/item/weapon/gun/projectile/shotgun/pump/rifle = TRADER_ALL, - /obj/item/weapon/gun/projectile/dragunov = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/silenced = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/automatic/tommygun = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/automatic/mini_uzi = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/improvised_handgun = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/shotgun/improvised = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/retro = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/revolver/derringer = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/pirate = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/contender = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/revolver/lemat = TRADER_THIS_TYPE, - /obj/item/weapon/gun/projectile/shotgun/pump/rifle/vintage = TRADER_THIS_TYPE, - /obj/item/weapon/gun/energy/rifle/icelance = TRADER_THIS_TYPE, - /obj/item/clothing/accessory/storage/bayonet = TRADER_THIS_TYPE + /obj/item/weapon/gun/projectile/shotgun/pump/rifle = TRADER_ALL, + /obj/item/weapon/gun/projectile/dragunov = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/silenced = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/automatic/tommygun = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/automatic/mini_uzi = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/improvised_handgun = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/shotgun/improvised = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/retro = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/revolver/derringer = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/pirate = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/contender = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/revolver/lemat = TRADER_THIS_TYPE, + /obj/item/weapon/gun/projectile/shotgun/pump/rifle/vintage = TRADER_THIS_TYPE, + /obj/item/weapon/gun/energy/rifle/icelance = TRADER_THIS_TYPE, + /obj/item/clothing/accessory/storage/bayonet = TRADER_THIS_TYPE ) diff --git a/code/game/antagonist/station/vampire.dm b/code/game/antagonist/station/vampire.dm index e4fb75efa32..142d80cb6c5 100644 --- a/code/game/antagonist/station/vampire.dm +++ b/code/game/antagonist/station/vampire.dm @@ -33,5 +33,5 @@ var/datum/antagonist/vampire/vamp = null vampirepowers += new type() /datum/antagonist/vampire/update_antag_mob(var/datum/mind/player) - ..() - player.current.make_vampire() + ..() + player.current.make_vampire() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index db37b5d9b1c..9b1d5d9bc39 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -152,7 +152,7 @@ /atom/proc/set_dir(new_dir) . = new_dir != dir dir = new_dir - + // Lighting if (.) var/datum/light_source/L diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 529888a686f..a10322131ec 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -179,13 +179,13 @@ var/list/sacrificed = list() cult.add_antagonist(target.mind) converting -= target target.hallucination = 0 //sudden clarity - playsound(target, 'sound/effects/bloodcult.ogg', 100, 1) + sound_to(target, 'sound/effects/bloodcult.ogg') else converting -= target //If we are dealing with a IPC then ask the caster what construct they want var/construct_class = alert(attacker, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") - playsound(target, 'sound/effects/bloodcult.ogg', 100, 1) + sound_to(target, 'sound/effects/bloodcult.ogg') //Spawn some remains new target.species.remains_type(target.loc) //spawns a skeleton based on the species remain type @@ -197,7 +197,7 @@ var/list/sacrificed = list() flick("dust-h", animation) qdel(animation) - //Spawn the selected construct + //Spawn the selected construct switch(construct_class) if("Juggernaut") var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(target.loc)) diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm index 537a1714360..3ecb9a8b51a 100644 --- a/code/game/gamemodes/vampire/vampire_powers.dm +++ b/code/game/gamemodes/vampire/vampire_powers.dm @@ -27,6 +27,10 @@ to_chat(src, "[T] is not a creature you can drain useful blood from.") return + if(T.head && (T.head.item_flags & AIRTIGHT)) + to_chat(src, "[T]'s headgear is blocking the way to the neck.") + return + if (vampire.status & VAMP_DRAINING) to_chat(src, "Your fangs are already sunk into a victim's neck!") return @@ -46,7 +50,9 @@ to_chat(T, "You are unable to resist or even move. Your mind blanks as you're being fed upon.") - T.Stun(10) + playsound(src.loc, 'sound/effects/drain_blood.ogg', 50, 1) + + T.Stun(20) while (do_mob(src, T, 50)) if (!mind.vampire) @@ -103,8 +109,7 @@ T.vessel.remove_reagent("blood", 25) vampire.status &= ~VAMP_DRAINING - to_chat(src, "You extract your fangs from [T.name]'s neck and stop draining them of blood. They will remember nothing of this occurance. Provided they survived.") - + visible_message("[src.name] stops biting [T.name]'s neck!", "You extract your fangs from [T.name]'s neck and stop draining them of blood. They will remember nothing of this occurance. Provided they survived.") if (T.stat != 2) to_chat(T, "You remember nothing about being fed upon. Instead, you simply remember having a pleasant encounter with [src.name].") @@ -849,6 +854,10 @@ to_chat(src, "[T]'s body is broken and damaged beyond salvation. You have no use for them.") return + if (T.species.flags & NO_BLOOD) + to_chat(src, "[T] has no blood and can not be affected by your powers!") + return + if (vampire.status & VAMP_DRAINING) to_chat(src, "Your fangs are already sunk into a victim's neck!") return diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index d5a62e7a409..f6a5df35260 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -228,6 +228,7 @@ access = list(access_journalist, access_maint_tunnels) minimal_access = list(access_journalist, access_maint_tunnels) alt_titles = list("Freelance Journalist") + title_accesses = list("Corporate Reporter" = list(access_medical, access_security, access_research, access_engine)) /datum/job/journalist/equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index fedd28f14da..81c6098239d 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -15,6 +15,7 @@ var/selection_color = "#ffffff" // Selection screen color var/idtype = /obj/item/weapon/card/id // The type of the ID the player will have var/list/alt_titles // List of alternate titles, if any + var/list/title_accesses // A map of title -> list of accesses to add if the person has this 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/department = null // Does this position have a department tag? @@ -105,11 +106,14 @@ /datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) . = equip(H, alt_title) -/datum/job/proc/get_access() +/datum/job/proc/get_access(selected_title) if(!config || config.jobs_have_minimal_access) - return src.minimal_access.Copy() + . = minimal_access.Copy() else - return src.access.Copy() + . = access.Copy() + + if (LAZYLEN(title_accesses) && title_accesses[selected_title]) + . += title_accesses[selected_title] /datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target) if(!istype(target)) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 096fa59262f..36b5bbe6293 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -113,9 +113,9 @@ if(L == user) - visible_message("[user] starts climbing onto the operating table.", 3) + user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) else - visible_message("[user] starts putting [L.name] onto the operating table.", 3) + user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) if (do_mob(user, L, 10, needhand = 0)) if (bucklestatus == 2) var/obj/structure/LB = L.buckled @@ -149,9 +149,9 @@ if(L == user) - visible_message("[user] starts climbing onto the operating table.", 3) + user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) else - visible_message("[user] starts putting [L.name] onto the operating table.", 3) + user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) if (do_mob(user, L, 10, needhand = 0)) if (bucklestatus == 2) var/obj/structure/LB = L.buckled diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index ebe85b020f7..278d6ec1201 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -161,7 +161,7 @@ user << "\The machine won't accept that." return - visible_message("[user] starts putting [G.affecting] into the [src].", 3) + user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) if (do_mob(user, G.affecting, 20, needhand = 0)) if(occupant) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 3f7b8395201..c1d8b097ab3 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -95,8 +95,8 @@ src.icon_state = "body_scanner_0" return -/obj/machinery/bodyscanner/attackby(obj/item/weapon/grab/G as obj, user as mob) - if ((!( istype(G, /obj/item/weapon/grab) ) || !( ismob(G.affecting) ))) +/obj/machinery/bodyscanner/attackby(obj/item/weapon/grab/G, mob/user) + if ((!( istype(G, /obj/item/weapon/grab) ) || !( isliving(G.affecting) ))) return if (src.occupant) user << "The scanner is already occupied!" @@ -105,28 +105,26 @@ user << "Subject cannot have abiotic items on." return - if(istype(G, /obj/item/weapon/grab)) + var/mob/living/M = G.affecting + user.visible_message("[user] starts putting [M] into [src].", "You start putting [M] into [src].", range = 3) - var/mob/living/L = G:affecting - visible_message("[user] starts putting [G:affecting] into the scanner bed.", 3) + if (do_mob(user, G.affecting, 30, needhand = 0)) + var/bucklestatus = M.bucklecheck(user) + if (!bucklestatus)//incase the patient got buckled during the delay + return + if (bucklestatus == 2) + var/obj/structure/LB = M.buckled + LB.user_unbuckle_mob(user) + if (M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src - if (do_mob(user, G:affecting, 30, needhand = 0)) - var/bucklestatus = L.bucklecheck(user) - if (!bucklestatus)//incase the patient got buckled during the delay - return - if (bucklestatus == 2) - var/obj/structure/LB = L.buckled - LB.user_unbuckle_mob(user) - var/mob/M = G.affecting - if (istype(M) && M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.loc = src - src.occupant = M - update_use_power(2) - src.icon_state = "body_scanner_1" - for(var/obj/O in src) - O.loc = src.loc + M.forceMove(src) + src.occupant = M + update_use_power(2) + src.icon_state = "body_scanner_1" + for(var/obj/O in src) + O.forceMove(loc) //Foreach goto(154) src.add_fingerprint(user) //G = null @@ -153,9 +151,9 @@ return if(L == user) - visible_message("[user] starts climbing into the scanner bed.", 3) + user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) else - visible_message("[user] starts putting [L.name] into the scanner bed.", 3) + user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) if (do_mob(user, L, 30, needhand = 0)) if (bucklestatus == 2) diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index ac102e099bc..e69f730371e 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -861,7 +861,7 @@ /obj/machinery/bot/mulebot/explode() - src.visible_message("[src] blows apart!", 1) + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/device/assembly/prox_sensor(Tsec) diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index ec313abdb00..3a3e77cf8be 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -526,7 +526,7 @@ for(var/obj/machinery/message_server/server in message_servers) if(!isnull(server)) if(!isnull(server.decryptkey)) - info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one." + info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from unauthorized personnel.
If necessary, change the password to a more secure one." info_links = info icon_state = "paper_words" break diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 8aea5627310..6986773af2f 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -181,26 +181,27 @@ G.loc = src user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") else if(istype(G, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/grab = G + var/mob/living/L = grab.affecting - var/mob/living/L = G:affecting - visible_message("[user] starts putting [G:affecting] into the cryopod.", 3) + if (!istype(L)) + return - if (do_mob(user, G:affecting, 30, needhand = 0)) + user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) + + if (do_mob(user, L, 30, needhand = 0)) var/bucklestatus = L.bucklecheck(user) if (!bucklestatus)//incase the patient got buckled during the delay return if (bucklestatus == 2) var/obj/structure/LB = L.buckled LB.user_unbuckle_mob(user) - if(!ismob(G:affecting)) - return - for(var/mob/living/carbon/slime/M in range(1,G:affecting)) - if(M.Victim == G:affecting) - usr << "[G:affecting:name] will not fit into the cryo because they have a slime latched onto their head." + for(var/mob/living/carbon/slime/M in range(1, L)) + if(M.Victim == L) + user << "[L] will not fit into the cryo because they have a slime latched onto their head." return - var/mob/M = G:affecting - if(put_mob(M)) - visible_message("[user] puts [M.name] into the cryo cell.", 3) + if(put_mob(L)) + user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) qdel(G) return @@ -221,18 +222,18 @@ return if(L == user) - visible_message("[user] starts climbing into the cryopod.", 3) + user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) else - visible_message("[user] starts putting [L.name] into the cryopod.", 3) + user.visible_message("[user] starts putting [L] into the cryopod.", "You start putting [L] into [src].", range = 3) if (do_mob(user, L, 30, needhand = 0)) if (bucklestatus == 2) var/obj/structure/LB = L.buckled LB.user_unbuckle_mob(user) if(put_mob(L)) if(L == user) - visible_message("[user] climbs into the cryo cell.", 3) + user.visible_message("[user] climbs into [src].", "You climb into [src].", range = 3) else - visible_message("[user] puts [L.name] into the cryo cell.", 3) + user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) if(user.pulling == L) user.pulling = null @@ -395,7 +396,7 @@ return if (usr.stat != 0) return - visible_message("[usr] climbs into the cryo cell.", 3) + usr.visible_message("[usr] climbs into [src].", "You climb into [src].", range = 3) put_mob(usr) return diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index dbfa58155c2..fa415c78411 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -109,7 +109,7 @@ user << "\The [I] is no longer in storage." return - visible_message("The console beeps happily as it disgorges \the [I].", 3) + visible_message("The console beeps happily as it disgorges \the [I].", range = 3) I.forceMove(get_turf(src)) frozen_items -= I @@ -121,7 +121,7 @@ user << "There is nothing to recover from storage." return - visible_message("The console beeps happily as it disgorges the desired objects.", 3) + visible_message("The console beeps happily as it disgorges the desired objects.", range = 3) for(var/obj/item/I in frozen_items) I.forceMove(get_turf(src)) @@ -320,44 +320,44 @@ icon_state = base_icon_state global_announcer.autosay("[occupant.real_name], [occupant.mind.role_alt_title], [on_store_message]", "[on_store_name]") - visible_message("\The [initial(name)] hums and hisses as it moves [occupant.real_name] into storage.", 3) + visible_message("\The [initial(name)] hums and hisses as it moves [occupant.real_name] into storage.") // Let SSjobs handle the rest. SSjobs.DespawnMob(occupant) set_occupant(null) -/obj/machinery/cryopod/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) +/obj/machinery/cryopod/attackby(var/obj/item/weapon/grab/G, var/mob/user as mob) - if(istype(G, /obj/item/weapon/grab)) + if(istype(G)) if(occupant) user << "\The [src] is in use." return - if(!ismob(G:affecting)) + if(!ismob(G.affecting)) return - if(!check_occupant_allowed(G:affecting)) + if(!check_occupant_allowed(G.affecting)) return var/willing = null //We don't want to allow people to be forced into despawning. - var/mob/M = G:affecting + var/mob/M = G.affecting if(M.client) var/originalloc = M.loc if(alert(M,"Would you like to enter long-term storage?",,"Yes","No") == "Yes") - if(!M || !G || !G:affecting || M.loc != originalloc) return + if(!M || !G || !G.affecting || M.loc != originalloc) return willing = 1 else willing = 1 if(willing) - visible_message("[user] starts putting [G:affecting:name] into \the [name].", 3) + user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) if(do_after(user, 20)) - if(!M || !G || !G:affecting) return + if(!M || !G || !G.affecting) return M.forceMove(src) @@ -410,9 +410,9 @@ if(willing) if(L == user) - visible_message("[user] starts climbing into \the [name].", 3) + user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) else - visible_message("[user] starts putting [L] into \the [name].", 3) + user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) if(do_after(user, 20)) if(!L) return @@ -483,7 +483,7 @@ usr << "You're too busy getting your life sucked out of you." return - visible_message("[usr] starts climbing into \the [src].", 3) + usr.visible_message("[usr] starts climbing into [src].", "You start climbing into [src].", range = 3) if(do_after(usr, 20)) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 52dc1c51435..12de52ba07e 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -151,7 +151,7 @@ if(H.species.can_shred(H)) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[user] smashes against the [src.name].", 1) + user.visible_message("[user] smashes against [src].", "You smash against [src]!") take_damage(25) return return src.attackby(user, user) diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 56ce454367a..d3f34545eb6 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -154,7 +154,7 @@ datum/track/New(var/title_name, var/audio) /obj/machinery/media/jukebox/proc/explode() walk_to(src,0) - src.visible_message("\the [src] blows apart!", 1) + visible_message("\the [src] blows apart!") explosion(src.loc, 0, 0, 1, rand(1,2), 1) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index cfd6fe2f883..7c3de6a7647 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -459,11 +459,11 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else if(href_list["set_attachment"]) AttachPhoto(usr) src.updateUsrDialog() - + else if(href_list["set_paper"]) AttachPaper(usr) src.updateUsrDialog() - + else if(href_list["submit_new_message"]) if(src.msg =="" || src.msg=="\[REDACTED\]" || src.scanned_user == "Unknown" || src.channel_name == "" ) src.screen=6 @@ -477,7 +477,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co src.screen=4 src.updateUsrDialog() - + else if(href_list["add_comment"]) var/com_msg = sanitize(input(usr, "Write your Comment", "Network Comment Handler", "") as message, encode = 0, trim = 0, extra = 0) if(com_msg =="" || com_msg=="\[REDACTED\]" || src.scanned_user == "Unknown" ) @@ -493,7 +493,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co to_chat(usr, "Comment successfully added!") src.screen = 22 src.updateUsrDialog() - + else if(href_list["view_comments"]) var/datum/feed_message/viewing_story = locate(href_list["story"]) if(!istype(viewing_story)) @@ -501,7 +501,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co src.screen = href_list["privileged"] ? 23 : 22 src.viewing_message = viewing_story src.updateUsrDialog() - + else if(href_list["censor_comment"]) var/datum/feed_comment/comment = locate(href_list["comment"]) if(!istype(comment)) @@ -509,7 +509,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co comment.message = "\[REDACTED\]" src.screen = 22 src.updateUsrDialog() - + else if(href_list["like"]) var/datum/feed_message/viewing_story = locate(href_list["story"]) if(src.scanned_user == "Unknown" || (src.scanned_user in viewing_story.interacted) || !istype(viewing_story)) @@ -517,7 +517,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co viewing_story.interacted += src.scanned_user viewing_story.likes += 1 src.updateUsrDialog() - + else if(href_list["dislike"]) var/datum/feed_message/viewing_story = locate(href_list["story"]) if(src.scanned_user == "Unknown" || (src.scanned_user in viewing_story.interacted) || !istype(viewing_story)) @@ -525,7 +525,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co viewing_story.interacted += src.scanned_user viewing_story.dislikes += 1 src.updateUsrDialog() - + else if(href_list["create_channel"]) src.screen=2 src.updateUsrDialog() @@ -774,7 +774,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(paper_data || paper_name) paper_name = "" paper_data = "" - + if(istype(user.get_active_hand(), /obj/item/weapon/paper)) var/obj/item/weapon/paper/attached = user.get_active_hand() paper_name = attached.name diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index b5b230232be..5389842e608 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -358,7 +358,7 @@ src.SUIT = null if(src.MASK) src.MASK = null - visible_message("With a loud whining noise, the Suit Storage Unit's door grinds open. Puffs of ashen smoke come out of its chamber.", 3) + visible_message("With a loud whining noise, [src]'s door grinds open. Puffs of ashen smoke come out of its chamber.", range = 3) src.isbroken = 1 src.isopen = 1 src.islocked = 0 @@ -453,7 +453,7 @@ if ( (src.OCCUPANT) || (src.HELMET) || (src.SUIT) ) usr << "It's too cluttered inside for you to fit in!" return - visible_message("[usr] starts squeezing into the suit storage unit!", 3) + usr.visible_message("[usr] starts squeezing into [src]!", "You start squeezing into [src]!", range = 3) if(do_after(usr, 10)) usr.stop_pulling() usr.client.perspective = EYE_PERSPECTIVE @@ -497,7 +497,7 @@ if ( (src.OCCUPANT) || (src.HELMET) || (src.SUIT) ) //Unit needs to be absolutely empty user << "The unit's storage area is too cluttered." return - visible_message("[user] starts putting [G.affecting.name] into the Suit Storage Unit.", 3) + user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) if(do_after(user, 20)) if(!G || !G.affecting) return //derpcheck var/mob/M = G.affecting @@ -595,7 +595,7 @@ //Departments that the cycler can paint suits to look like. var/list/departments = list("Engineering","Mining","Medical","Security","Atmos") //Species that the suits can be configured to fit. - var/list/species = list("Human","Skrell","Unathi","Tajara") + var/list/species = list("Human","Skrell","Unathi","Tajara", "Vaurca") var/target_department var/target_species @@ -624,35 +624,30 @@ model_text = "Engineering" req_access = list(access_construction) departments = list("Engineering","Atmos") - species = list("Human","Tajara","Skrell","Unathi") //Add Unathi when sprites exist for their suits. /obj/machinery/suit_cycler/mining name = "Mining suit cycler" model_text = "Mining" req_access = list(access_mining) departments = list("Mining") - species = list("Human","Tajara","Skrell","Unathi") /obj/machinery/suit_cycler/security name = "Security suit cycler" model_text = "Security" req_access = list(access_security) departments = list("Security") - species = list("Human","Tajara","Skrell","Unathi") /obj/machinery/suit_cycler/medical name = "Medical suit cycler" model_text = "Medical" req_access = list(access_medical) departments = list("Medical") - species = list("Human","Tajara","Skrell","Unathi") /obj/machinery/suit_cycler/syndicate name = "Nonstandard suit cycler" model_text = "Nonstandard" req_access = list(access_syndicate) departments = list("Mercenary") - species = list("Human","Tajara","Skrell","Unathi") can_repair = 1 /obj/machinery/suit_cycler/wizard @@ -700,7 +695,7 @@ user << "There is no room inside the cycler for [G.affecting.name]." return - visible_message("[user] starts putting [G.affecting.name] into the suit cycler.", 3) + user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) if(do_after(user, 20)) if(!G || !G.affecting) return diff --git a/code/game/modifiers/modifiers_chem.dm b/code/game/modifiers/modifiers_chem.dm index 9b282d947b9..3da6a229411 100644 --- a/code/game/modifiers/modifiers_chem.dm +++ b/code/game/modifiers/modifiers_chem.dm @@ -82,4 +82,31 @@ ..() if (isliving(target)) var/mob/living/L = target - L.set_light(0) \ No newline at end of file + L.set_light(0) + +//Doubleburn napalm modifier. Applied by Zo'rane Fire +//Increases damage dealt by burn sources +/datum/modifier/napalm + var/added_burn_mod + var/delta + +/datum/modifier/napalm/activate() + ..() + delta = strength + if (isliving(target)) + var/mob/living/L = target + added_burn_mod = L.burn_mod * delta - L.burn_mod + L.burn_mod += added_burn_mod + +/datum/modifier/napalm/deactivate() + ..() + if (isliving(target)) + var/mob/living/L = target + L.burn_mod -= added_burn_mod + +/datum/modifier/napalm/custom_validity() + if(istype(target, /mob/living)) + var/mob/living/L = target + if(L.fire_stacks) + return 1 + return 0 \ No newline at end of file diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index 629ac8016a6..33e66096b3b 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -12,7 +12,7 @@ /obj/effect/effect/water/proc/set_color() // Call it after you move reagents to it icon += reagents.get_color() -/obj/effect/effect/water/proc/set_up(var/turf/target, var/step_count = 5, var/delay = 5) +/obj/effect/effect/water/proc/set_up(var/turf/target, var/step_count = 5, var/delay = 5, var/lifespan = 10) if(!target) return for(var/i = 1 to step_count) @@ -28,7 +28,7 @@ if(T == get_turf(target)) break sleep(delay) - sleep(10) + sleep(lifespan) qdel(src) //Wets everything in the tile @@ -75,3 +75,8 @@ name = "chemicals" icon = 'icons/obj/chempuff.dmi' icon_state = "" + +//used by evil things +/obj/effect/effect/water/firewater + name = "napalm gel" + icon_state = "mustard" \ No newline at end of file diff --git a/code/game/objects/effects/decals/Cleanable/fuel.dm b/code/game/objects/effects/decals/Cleanable/fuel.dm index f5eaf24563a..5fb93e61a04 100644 --- a/code/game/objects/effects/decals/Cleanable/fuel.dm +++ b/code/game/objects/effects/decals/Cleanable/fuel.dm @@ -6,66 +6,105 @@ anchored = 1 var/amount = 1 - Initialize(mapload, amt = 1, nologs = 0) - . = ..() - if(!nologs && !mapload) - message_admins("Liquid fuel has spilled in [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP)") - log_game("Liquid fuel has spilled in [loc.loc.name] ([loc.x],[loc.y],[loc.z])") - src.amount = amt +/obj/effect/decal/cleanable/liquid_fuel/Initialize(mapload, amt = 1, nologs = 0) + . = ..() + if(!nologs && !mapload) + message_admins("Liquid fuel has spilled in [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP)") + log_game("Liquid fuel has spilled in [loc.loc.name] ([loc.x],[loc.y],[loc.z])") + src.amount = amt - var/has_spread = 0 - //Be absorbed by any other liquid fuel in the tile. - for(var/obj/effect/decal/cleanable/liquid_fuel/other in loc) - if(other != src) - other.amount += src.amount - other.Spread() - has_spread = 1 - break - - if(!has_spread) - Spread() - else - qdel(src) - - proc/Spread(exclude=list()) - //Allows liquid fuels to sometimes flow into other tiles. - if(amount < 15) return //lets suppose welder fuel is fairly thick and sticky. For something like water, 5 or less would be more appropriate. - var/turf/simulated/S = loc - if(!istype(S)) return - for(var/d in cardinal) - var/turf/simulated/target = get_step(src,d) - var/turf/simulated/origin = get_turf(src) - if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0)) - var/obj/effect/decal/cleanable/liquid_fuel/other_fuel = locate() in target - if(other_fuel) - other_fuel.amount += amount*0.25 - if(!(other_fuel in exclude)) - exclude += src - other_fuel.Spread(exclude) - else - new/obj/effect/decal/cleanable/liquid_fuel(target, amount*0.25,1) - amount *= 0.75 - - - flamethrower_fuel - icon_state = "mustard" - anchored = 0 - Initialize(mapload, amt = 1, d = 0) - set_dir(d) //Setting this direction means you won't get torched by your own flamethrower. - . = ..() + var/has_spread = 0 + //Be absorbed by any other liquid fuel in the tile. + for(var/obj/effect/decal/cleanable/liquid_fuel/other in loc) + if(other != src) + other.amount += src.amount + other.Spread() + has_spread = 1 + break + if(!has_spread) Spread() - //The spread for flamethrower fuel is much more precise, to create a wide fire pattern. - if(amount < 0.1) return - var/turf/simulated/S = loc - if(!istype(S)) return + else + qdel(src) - for(var/d in list(turn(dir,90),turn(dir,-90), dir)) - var/turf/simulated/O = get_step(S,d) - if(locate(/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel) in O) - continue - if(O.CanPass(null, S, 0, 0) && S.CanPass(null, O, 0, 0)) - new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(O,amount*0.25,d) - O.hotspot_expose((T20C*2) + 380,500) //Light flamethrower fuel on fire immediately. +/obj/effect/decal/cleanable/liquid_fuel/proc/Spread(exclude=list()) + //Allows liquid fuels to sometimes flow into other tiles. + if(amount < 15) return //lets suppose welder fuel is fairly thick and sticky. For something like water, 5 or less would be more appropriate. + var/turf/simulated/S = loc + if(!istype(S)) return + for(var/d in cardinal) + var/turf/simulated/target = get_step(src,d) + var/turf/simulated/origin = get_turf(src) + if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0)) + var/obj/effect/decal/cleanable/liquid_fuel/other_fuel = locate() in target + if(other_fuel) + other_fuel.amount += amount*0.25 + if(!(other_fuel in exclude)) + exclude += src + other_fuel.Spread(exclude) + else + new/obj/effect/decal/cleanable/liquid_fuel(target, amount*0.25,1) + amount *= 0.75 - amount *= 0.25 +/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel + icon_state = "mustard" + anchored = 0 + +/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel/Initialize(mapload, amt = 1, d = 0) + set_dir(d) //Setting this direction means you won't get torched by your own flamethrower. + . = ..() + +/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel/Spread() + //The spread for flamethrower fuel is much more precise, to create a wide fire pattern. + if(amount < 0.1) return + var/turf/simulated/S = loc + if(!istype(S)) return + + for(var/d in list(turn(dir,90),turn(dir,-90), dir)) + var/turf/simulated/O = get_step(S,d) + if(locate(/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel) in O) + continue + if(O.CanPass(null, S, 0, 0) && S.CanPass(null, O, 0, 0)) + new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(O,amount*0.25,d) + O.hotspot_expose((T20C*2) + 380,500) //Light flamethrower fuel on fire immediately. + + amount *= 0.25 + + +/obj/effect/decal/cleanable/liquid_fuel/napalm + name = "napalm gel" + +/obj/effect/decal/cleanable/liquid_fuel/napalm/Initialize(mapload, amt = 1, nologs = 0) + . = ..() + START_PROCESSING(SSprocessing, src) + +/obj/effect/decal/cleanable/liquid_fuel/napalm/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/effect/decal/cleanable/liquid_fuel/napalm/Spread() + if(amount < 100) + return + var/turf/simulated/S = loc + if(!istype(S)) + return + for(var/d in cardinal) + var/turf/simulated/target = get_step(src,d) + var/turf/simulated/origin = get_turf(src) + if(origin.CanPass(null, target, 0, 0) && target.CanPass(null, origin, 0, 0)) + var/obj/effect/decal/cleanable/liquid_fuel/napalm/other_fuel = locate() in target + if(other_fuel) + other_fuel.amount += amount*0.5 + target.hotspot_expose(2000, 400) + else + new/obj/effect/decal/cleanable/liquid_fuel/napalm(target, amount*0.5,1) + target.hotspot_expose(2000, 400) + amount *= 0.5 + origin.hotspot_expose(2000, 400) //immediately ignite. its napalm bitch + +/obj/effect/decal/cleanable/liquid_fuel/napalm/process() + for(var/mob/living/L in get_turf(src)) + var/sticky = min(rand(5,25), amount) + if(sticky > 1) + L.adjust_fire_stacks(sticky) + amount = max(1, amount - sticky) \ No newline at end of file diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 60e59c20803..501e5cc19e2 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -75,6 +75,10 @@ endgame_exits += loc delete_me = 1 return + if("asteroid spawn") + asteroid_spawn += loc + delete_me = 1 + return landmarks_list += src return 1 @@ -246,3 +250,8 @@ new /obj/item/clothing/mask/gas/sexymime(src.loc) new /obj/item/clothing/under/sexymime(src.loc) delete_me = 1 + +/obj/effect/landmark/dungeon_spawn + name = "asteroid spawn" + icon = 'icons/1024x1024.dmi' + icon_state = "yellow" diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index eaace53061a..7459c138d5c 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -1,6 +1,6 @@ /obj/effect/portal name = "portal" - desc = "Looks unstable. Best to test it with the clown." + desc = "Looks unstable. Best to test it carefully." icon = 'icons/obj/stationobjs.dmi' icon_state = "portal" density = 1 diff --git a/code/game/objects/effects/projectile/projectile_impact.dm b/code/game/objects/effects/projectile/projectile_impact.dm index a8ef339f80f..ef6ac8659ed 100644 --- a/code/game/objects/effects/projectile/projectile_impact.dm +++ b/code/game/objects/effects/projectile/projectile_impact.dm @@ -66,3 +66,8 @@ /obj/effect/projectile/impact/emitter icon_state = "impact_emitter" light_color = LIGHT_COLOR_GREEN + +/obj/effect/projectile/impact/tachyon + name = "xray impact" + icon_state = "impact_tachyon" + light_color = LIGHT_COLOR_RED diff --git a/code/game/objects/effects/projectile/projectile_muzzle.dm b/code/game/objects/effects/projectile/projectile_muzzle.dm index e111ee72e3b..2aa55de7088 100644 --- a/code/game/objects/effects/projectile/projectile_muzzle.dm +++ b/code/game/objects/effects/projectile/projectile_muzzle.dm @@ -60,3 +60,7 @@ /obj/effect/projectile/muzzle/bullet icon_state = "muzzle_bullet" + +/obj/effect/projectile/muzzle/tachyon + icon_state = "muzzle_pulse" + light_color = LIGHT_COLOR_CYAN \ No newline at end of file diff --git a/code/game/objects/effects/projectile/projectile_tracer.dm b/code/game/objects/effects/projectile/projectile_tracer.dm index edac3dc70c3..62776d7fcc1 100644 --- a/code/game/objects/effects/projectile/projectile_tracer.dm +++ b/code/game/objects/effects/projectile/projectile_tracer.dm @@ -77,3 +77,8 @@ /obj/effect/projectile/tracer/emitter icon_state = "emitter" light_color = LIGHT_COLOR_GREEN + +/obj/effect/projectile/tracer/tachyon + name = "particle beam" + icon_state = "invisible" + light_color = LIGHT_COLOR_VIOLET diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index d4596bd0d2f..ce30753f09d 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -192,7 +192,7 @@ return if(prob(50)) - src.visible_message("You hear something squeezing through the ventilation ducts.",2) + visible_message("You hear something squeezing through the ventilation ducts.", range = 2) sleep(travel_time) if(!exit_vent || exit_vent.welded) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c83cd19cb0b..97d44f5aeb1 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -686,8 +686,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. if(!cannotzoom) M.visible_message("[zoomdevicename ? "[M] looks up from the [src.name]" : "[M] lowers the [src.name]"].") - return - /obj/item/proc/pwr_drain() return 0 // Process Kill diff --git a/code/game/objects/items/airbubble.dm b/code/game/objects/items/airbubble.dm index 674e830ff9c..6ef19411f97 100644 --- a/code/game/objects/items/airbubble.dm +++ b/code/game/objects/items/airbubble.dm @@ -78,6 +78,14 @@ var/syndie = FALSE var/last_shake = 0 +// Examine to see tank pressure +/obj/structure/closet/airbubble/examine(mob/user) + ..() + if(!isnull(internal_tank)) + to_chat(user, "\The [src] has [internal_tank] attached, that displays [round(internal_tank.air_contents.return_pressure() ? internal_tank.air_contents.return_pressure() : 0)] KPa.") + else + to_chat(user, "\The [src] has no tank attached.") + /obj/structure/closet/airbubble/can_open() if(zipped) return 0 @@ -296,6 +304,7 @@ else START_PROCESSING(SSfast_process, src) use_internal_tank = !use_internal_tank + update_icon() else to_chat(usr, "[src] has no internal tank.") @@ -416,6 +425,7 @@ icon_state = icon_closed if(zipped) add_overlay("[icon_closed]_restrained") + add_overlay("pressure_[(use_internal_tank) ?("on") : ("off") ]") // Process transfer of air from the tank. Handle if it is ripped open. /obj/structure/closet/airbubble/proc/process_tank_give_air() diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 2dc3ea4ffb6..0020ae62fd4 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -123,7 +123,9 @@ var/mob/living/silicon/robot/R = M if(R.overclocked) return - M.Weaken(7) + + M.Weaken(rand(3,7)) //should be that borg is disabled for around 3-7 seconds + else flashfail = 1 diff --git a/code/game/objects/items/weapons/improvised_components.dm b/code/game/objects/items/weapons/improvised_components.dm index abd19681766..41e716867d5 100644 --- a/code/game/objects/items/weapons/improvised_components.dm +++ b/code/game/objects/items/weapons/improvised_components.dm @@ -108,4 +108,38 @@ attack_verb = list("attacked", "poked") force_divisor = 0.1 thrown_force_divisor = 0.1 + default_material = "steel" + + +/obj/item/weapon/material/woodenshield + name = "shield donut" + desc = "A wooden disc. Unusable as a shield without metal. Don't eat this." + icon = 'icons/obj/weapons.dmi' + icon_state = "buckler2" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + default_material = "wood" + +/obj/item/weapon/material/woodenshield/attackby(var/obj/item/I, mob/user as mob) + ..() + var/obj/item/finished + if(istype(I, /obj/item/weapon/material/shieldbits)) + var/obj/item/weapon/material/woodenshield/donut = I + finished = new /obj/item/weapon/shield/buckler(get_turf(user), donut.material.name) + user << "You attach \the [I] to \the [src]." + if(finished) + user.drop_from_inventory(src) + user.drop_from_inventory(I) + qdel(I) + qdel(src) + user.put_in_hands(finished) + update_icon(user) + +/obj/item/weapon/material/shieldbits + name = "shield fittings" + desc = "A metal ring and boss, fitting for a buckler." + icon = 'icons/obj/weapons.dmi' + icon_state = "buckler1" + force_divisor = 0.1 + thrown_force_divisor = 0.1 default_material = "steel" \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index 38b75f1e620..980ea727ecd 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -79,7 +79,7 @@ name = "butcher's cleaver" icon = 'icons/obj/kitchen.dmi' icon_state = "butch" - desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." + desc = "A huge thing used for chopping and chopping up meat." force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 4b882f5a2a0..96f62f42cc1 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -149,6 +149,15 @@ icon_state = initial(icon_state) user << "\The [src] is de-energised." +/obj/item/weapon/melee/energy/glaive/attack(mob/living/carbon/human/M as mob, mob/living/carbon/user as mob) + user.setClickCooldown(16) + ..() + +/obj/item/weapon/melee/energy/glaive/pre_attack(var/mob/living/target, var/mob/living/user) + if(istype(target)) + cleave(user, target) + ..() + /* * Energy Axe */ diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 973097fe961..9413ee4a5cd 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -108,7 +108,9 @@ /obj/item/weapon/shield/buckler/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) if(istype(damage_source, /obj/item/projectile)) - return 0 + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return 0 return base_block_chance /* diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 96c753c8ad2..6b8d1555ef1 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -24,7 +24,7 @@ slot_flags = SLOT_BACK max_w_class = 3 max_storage_space = 28 - var/species_restricted = list("exclude","Vaurca Breeder") + var/species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") /obj/item/weapon/storage/backpack/mob_can_equip(M as mob, slot) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 9fe37ef7ebc..07b2efa79f6 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -188,7 +188,7 @@ //secborg stun baton module /obj/item/weapon/melee/baton/robot - hitcost = 600 + hitcost = 300 /obj/item/weapon/melee/baton/robot/attack_self(mob/user) //try to find our power cell diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index fe102358141..d8752cfb248 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -95,22 +95,12 @@ user.take_organ_damage(2*force) return if(..() == 1) - playsound(src.loc, "swing_hit", 50, 1, -1) - if (target_zone == "r_leg" || target_zone == "l_leg") - var/stun_chance = 100 + if(user.a_intent == I_DISARM) if(ishuman(target)) var/mob/living/carbon/human/T = target var/armor = T.run_armor_check(target_zone,"melee") - stun_chance -= armor - if(T.shoes && (T.shoes.item_flags & NOSLIP) && istype(T.shoes, /obj/item/clothing/shoes/magboots)) - stun_chance -= 10 - - if(T.species.brute_mod<0.8) - stun_chance -= 10 - - if(prob(stun_chance)) - T.Weaken(5) //nerfed, because yes. - return + T.apply_damage(40, HALLOSS, target_zone, armor) + return else - return ..() + return ..() \ No newline at end of file diff --git a/code/game/objects/items/weapons/vaurca_items.dm b/code/game/objects/items/weapons/vaurca_items.dm index ea5ed010877..3bc7dbb0d66 100644 --- a/code/game/objects/items/weapons/vaurca_items.dm +++ b/code/game/objects/items/weapons/vaurca_items.dm @@ -39,7 +39,10 @@ icon_state = "eknife1" item_state = icon_state damtype = "fire" - user.regenerate_icons() + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() user << "\The [src] is now energised." /obj/item/weapon/melee/energy/vaurca/deactivate(mob/living/user) @@ -47,7 +50,10 @@ icon_state = "eknife0" item_state = icon_state damtype = "brute" - user.regenerate_icons() + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() user << "\The [src] is de-energised." /obj/item/vaurca/box @@ -172,7 +178,8 @@ species_restricted = list("Vaurca") - light_overlay = "helmet_light" + light_overlay = "helmet_light_dual_green" + light_color = "#3e7c3e" /obj/item/clothing/shoes/magboots/vox/vaurca @@ -183,7 +190,257 @@ contained_sprite = 1 icon = 'icons/obj/vaurca_items.dmi' - species_restricted = list("Vaurca") + species_restricted = list("Vaurca","Vaurca Warform") + sprite_sheets = list( + "Vaurca Warform" = 'icons/mob/species/warriorform/shoes.dmi' + ) action_button_name = "Toggle the magclaws" +/obj/item/clothing/suit/space/void/scout + name = "scout armor" + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "scout" + item_state = "scout" + desc = "Armor designed for K'laxan scouts, made of lightweight sturdy material that does not restrict movement." + slowdown = -1 + + species_restricted = list("Vaurca") + armor = list(melee = 50, bullet = 20, laser = 50, energy = 30, bomb = 45, bio = 100, rad = 10) + +/obj/item/clothing/head/helmet/space/void/scout + name = "scout helmet" + desc = "A helmet designed for K'laxan scouts, made of lightweight sturdy material that does not restrict movement." + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "helm_scout" + item_state = "helm_scout" + + species_restricted = list("Vaurca") + armor = list(melee = 40, bullet = 20, laser = 40, energy = 30, bomb = 45, bio = 100, rad = 10) + + light_overlay = "helmet_light_dual_green" + light_color = "#3e7c3e" + +/obj/item/clothing/suit/space/void/commando + name = "commando armor" + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "commando" + item_state = "commando" + desc = "A design perfected by the Zo'ra, this helmet is commonly used by frontline warriors of a hive. Ablative design deflects lasers away from the body while providing moderate physical protection." + + species_restricted = list("Vaurca") + armor = list(melee = 40, bullet = 40, laser = 60, energy = 50, bomb = 45, bio = 100, rad = 10) + +/obj/item/clothing/head/helmet/space/void/commando + name = "commando helmet" + desc = "A design perfected by the Zo'ra, this helmet is commonly used by frontline warriors of a hive. Ablative design deflects lasers away from the body while providing moderate physical protection." + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "helm_commando" + item_state = "helm_commando" + + species_restricted = list("Vaurca") + armor = list(melee = 30, bullet = 30, laser = 60, energy = 50, bomb = 45, bio = 100, rad = 10) + + light_overlay = "helmet_light_dual_green" + light_color = "#3e7c3e" + +/obj/item/clothing/mask/gas/vaurca + name = "tactical garment" + desc = "A tactical mandible garment with state of the art air filtration." + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | FLEXIBLEMATERIAL | THICKMATERIAL + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = FACE|EYES + gas_filter_strength = 3 + w_class = 2.0 + filtered_gases = list("nitrogen", "sleeping_agent") + armor = list(melee = 25, bullet = 10, laser = 25, energy = 25, bomb = 0, bio = 50, rad = 15) + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "m_metalg" + item_state = "m_metalg" + contained_sprite = 1 + +/obj/item/weapon/melee/energy/vaurca_zweihander + name = "thermal greatblade" + desc = "An infamous execution blade of the Zo'ra, due to its size, only the largest Za were able to carry it in active combat." + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "greatblade0" + item_state = "greatblade0" + active_force = 30 + armor_penetration = 30 + active_throwforce = 20 + active_w_class = 5 + force = 10 + throwforce = 10 + throw_speed = 5 + throw_range = 10 + w_class = 4.0 + flags = CONDUCT | NOBLOODY + attack_verb = list("stabbed", "chopped", "sliced", "cleaved", "slashed", "cut") + sharp = 1 + edge = 1 + contained_sprite = 1 + base_reflectchance = 40 + base_block_chance = 60 + shield_power = 150 + +/obj/item/weapon/melee/energy/vaurca_zweihander/attack(mob/living/carbon/human/M as mob, mob/living/carbon/user as mob) + user.setClickCooldown(16) + ..() + +/obj/item/weapon/melee/energy/vaurca_zweihander/pre_attack(var/mob/living/target, var/mob/living/user) + if(istype(target)) + cleave(user, target) + ..() + +/obj/item/weapon/melee/energy/vaurca_zweihander/activate(mob/living/user) + ..() + icon_state = "greatblade1" + item_state = icon_state + damtype = "fire" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + user << "\The [src] is now energised." + +/obj/item/weapon/melee/energy/vaurca_zweihander/deactivate(mob/living/user) + ..() + icon_state = "greatblade0" + item_state = icon_state + damtype = "brute" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + user << "\The [src] is de-energised." + +/obj/item/weapon/gun/launcher/crossbow/vaurca + name = "gauss rifle" + desc = "An unwieldy, heavy weapon that propels metal projectiles with magnetic coils that run its length." + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "gaussrifle" + item_state = "gaussrifle" + fire_sound = 'sound/effects/Explosion2.ogg' + fire_sound_text = "a subdued boom" + fire_delay = 12 + slot_flags = SLOT_BACK + needspin = TRUE + recoil = 6 + + + release_speed = 15 + var/list/belt = new/list() + var/belt_size = 12 //holds this + one in the chamber + recoil_wielded = 2 + accuracy_wielded = -1 + fire_delay_wielded = 1 + + //action button for wielding + action_button_name = "Wield rifle" + +/obj/item/weapon/gun/launcher/crossbow/vaurca/can_wield() + return 1 + +/obj/item/weapon/gun/launcher/crossbow/vaurca/ui_action_click() + if(src in usr) + toggle_wield(usr) + +/obj/item/weapon/gun/launcher/crossbow/vaurca/verb/wield_rifle() + set name = "Wield rifle" + set category = "Object" + set src in usr + + toggle_wield(usr) + 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/gun/launcher/crossbow/vaurca/update_icon() + if(wielded) + item_state = "gaussrifle-wielded" + else + item_state = "gaussrifle" + update_held_icon() + +/obj/item/weapon/gun/launcher/crossbow/vaurca/consume_next_projectile(mob/user=null) + return bolt + +/obj/item/weapon/gun/launcher/crossbow/vaurca/handle_post_fire(mob/user, atom/target) + bolt = null + tension = 1 + ..() + +/obj/item/weapon/gun/launcher/crossbow/vaurca/attack_self(mob/living/user as mob) + pump(user) + +/obj/item/weapon/gun/launcher/crossbow/vaurca/proc/pump(mob/M as mob) + playsound(M, 'sound/weapons/shotgunpump.ogg', 60, 1) + + if(bolt) + if(tension < max_tension) + M << "You pump [src], charging the magnetic coils." + tension++ + else + M << "\The [src]'s magnetic coils are at maximum charge." + return + var/obj/item/next + if(belt.len) + next = belt[1] + if(next) + belt -= next //Remove grenade from loaded list. + bolt = next + M << "You pump [src], loading \a [next] into the chamber." + else + M << "You pump [src], but the magazine is empty." + +/obj/item/weapon/gun/launcher/crossbow/vaurca/proc/load(obj/item/W, mob/user) + if(belt.len >= belt_size) + user << "[src] is full." + return + user.remove_from_mob(W) + W.forceMove(src) + belt.Insert(1, W) //add to the head of the list, so that it is loaded on the next pump + user.visible_message("[user] inserts \a [W] into [src].", "You insert \a [W] into [src].") + +/obj/item/weapon/gun/launcher/crossbow/vaurca/proc/unload(mob/user) + if(belt.len) + var/obj/item/weapon/arrow/rod/R = belt[belt.len] + belt.len-- + user.put_in_hands(R) + user.visible_message("[user] removes \a [R] from [src].", "You remove \a [R] from [src].") + else + user << "[src] is empty." + +/obj/item/weapon/gun/launcher/crossbow/vaurca/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/arrow)) + load(I, user) + if(istype(I, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = I + if (R.use(1)) + var/obj/item/weapon/arrow/rod/ROD = new /obj/item/weapon/arrow/rod(src) + load(ROD, user) + else + ..() + +/obj/item/weapon/gun/launcher/crossbow/vaurca/attack_hand(mob/user) + if(user.get_inactive_hand() == src) + unload(user) + else + ..() + +/obj/item/weapon/gun/launcher/crossbow/vaurca/superheat_rod(mob/user) + if(!user || !bolt) return + if(bolt.throwforce >= 25) return + if(!istype(bolt,/obj/item/weapon/arrow/rod)) return + + bolt.throwforce = 25 + bolt.icon_state = "metal-rod-superheated" + +/obj/item/weapon/gun/launcher/crossbow/vaurca/update_icon() + return diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index c1420e12dc9..b8a21a73afe 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -907,7 +907,9 @@ /obj/item/ammo_casing/c45/rubber = 0.5, /obj/item/ammo_casing/c9mm/rubber = 0.5, /obj/item/ammo_casing/c45/flash = 0.5, - /obj/item/ammo_casing/shotgun/beanbag = 0.5 + /obj/item/ammo_casing/shotgun/beanbag = 0.5, + /obj/item/weapon/flag/america = 1, + /obj/item/weapon/flag/america/l = 1 ) //Sometimes the chef will have spare oil in storage. @@ -991,28 +993,33 @@ desc = "Wew." icon = 'icons/obj/kinetic_accelerators.dmi' icon_state = "frame01" - spawnlist = list( /obj/item/toy/prize/honk ) has_postspawn = TRUE post_spawn(obj/thing) var/list/frames = list( - /obj/item/weapon/gun/custom_ka/frame01 = 3, + /obj/item/weapon/gun/custom_ka/frame01 = 1, /obj/item/weapon/gun/custom_ka/frame02 = 2, - /obj/item/weapon/gun/custom_ka/frame03 = 1 + /obj/item/weapon/gun/custom_ka/frame03 = 3, + /obj/item/weapon/gun/custom_ka/frame04 = 2, + /obj/item/weapon/gun/custom_ka/frame05 = 1 ) var/list/cells = list( - /obj/item/custom_ka_upgrade/cells/cell01 = 3, - /obj/item/custom_ka_upgrade/cells/cell02 = 2, - /obj/item/custom_ka_upgrade/cells/cell03 = 1 + /obj/item/custom_ka_upgrade/cells/cell01 = 2, + /obj/item/custom_ka_upgrade/cells/cell02 = 3, + /obj/item/custom_ka_upgrade/cells/cell03 = 2, + /obj/item/custom_ka_upgrade/cells/cell04 = 1, + /obj/item/custom_ka_upgrade/cells/cell05 = 1 ) var/list/barrels = list( - /obj/item/custom_ka_upgrade/barrels/barrel01 = 3, - /obj/item/custom_ka_upgrade/barrels/barrel02 = 2, - /obj/item/custom_ka_upgrade/barrels/barrel03 = 1 + /obj/item/custom_ka_upgrade/barrels/barrel01 = 2, + /obj/item/custom_ka_upgrade/barrels/barrel02 = 3, + /obj/item/custom_ka_upgrade/barrels/barrel03 = 2, + /obj/item/custom_ka_upgrade/barrels/barrel04 = 1, + /obj/item/custom_ka_upgrade/barrels/barrel05 = 1 ) var/frame_type = pickweight(frames) diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm index 9c910a37b55..57d9eb4b822 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs.dm @@ -416,6 +416,44 @@ /obj/item/weapon/flag/eridani/l flag_size = 1 +/obj/structure/sign/flag/vaurca + name = "Sedantis flag" + desc = "The emblem of Sedantis on a flag, emblematic of Vaurca longing." + icon_state = "sedantis" + +/obj/structure/sign/flag/vaurca/left + icon_state = "sedantis_l" + +/obj/structure/sign/flag/vaurca/right + icon_state = "sedantis_r" + +/obj/item/weapon/flag/vaurca + name = "Sedantis flag" + desc = "The emblem of Sedantis on a flag, emblematic of Vaurca longing." + flag_path = "sedantis" + +/obj/item/weapon/flag/vaurca/l + flag_size = 1 + +/obj/structure/sign/flag/america + name = "Old World flag" + desc = "The banner of an ancient nation, its glory old." + icon_state = "oldglory" + +/obj/structure/sign/flag/america/left + icon_state = "oldglory_l" + +/obj/structure/sign/flag/america/right + icon_state = "oldglory_r" + +/obj/item/weapon/flag/america + name = "Old World flag" + desc = "The banner of an ancient nation, its glory old." + flag_path = "soldglory" + +/obj/item/weapon/flag/america/l + flag_size = 1 + /obj/item/weapon/flag name = "boxed flag" desc = "A flag neatly folded into a wooden container." diff --git a/code/game/objects/structures/therapy.dm b/code/game/objects/structures/therapy.dm index 42954b2b345..b4aa70f53ab 100644 --- a/code/game/objects/structures/therapy.dm +++ b/code/game/objects/structures/therapy.dm @@ -334,7 +334,7 @@ var/mob/living/L = G.affecting - visible_message("[user] starts putting [L] into the pod bed.", 3) + user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) if (do_mob(user, L, 30, needhand = 0)) var/bucklestatus = L.bucklecheck(user) @@ -378,9 +378,9 @@ return if(H == user) - visible_message("[user] starts climbing into the pod bed.", 3) + user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) else - visible_message("[user] starts putting [H.name] into the pod bed.", 3) + user.visible_message("[user] starts putting [H] into [src].", "You start putting [H] into [src].", range = 3) if (do_mob(user, H, 30, needhand = 0)) if (bucklestatus == 2) diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 75696ec36cc..e6500518309 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -88,28 +88,23 @@ if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/H = M // Tracking blood - var/list/bloodDNA = list() - var/will_track = 0 + var/list/bloodDNA = null var/bloodcolor="" if(H.shoes) var/obj/item/clothing/shoes/S = H.shoes if(istype(S)) S.handle_movement(src,(H.m_intent == "run" ? 1 : 0)) - if(S.track_blood) - if(S.blood_DNA) - bloodDNA = S.blood_DNA + if(S.track_blood && S.blood_DNA) + bloodDNA = S.blood_DNA bloodcolor=S.blood_color S.track_blood-- - will_track = 1 else - if(H.track_blood) - if(H.feet_blood_DNA) - bloodDNA = H.feet_blood_DNA + if(H.track_blood && H.feet_blood_DNA) + bloodDNA = H.feet_blood_DNA bloodcolor = H.feet_blood_color H.track_blood-- - will_track = 1 - if(will_track) + if(bloodDNA) src.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints,bloodDNA,H.dir,0,bloodcolor) // Coming var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) if(istype(from) && from) diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index d1c886b9e1a..45bb772376f 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -71,6 +71,16 @@ fail_smash(user, 2) return 1 + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/turf/destination = GetAbove(H) + + if(destination) + var/turf/start = get_turf(H) + if(start.CanZPass(H, UP) && destination.CanZPass(H, UP)) + H.climb(UP, src) + return + try_touch(user, rotting) /turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message, var/wallbreaker) diff --git a/code/global.dm b/code/global.dm index a864ead56a2..f39b1c843b5 100644 --- a/code/global.dm +++ b/code/global.dm @@ -60,6 +60,7 @@ var/list/kickoffsloc = list() var/list/prisonwarp = list() // Prisoners go to these var/list/holdingfacility = list() // Captured people go here var/list/xeno_spawn = list() // Aliens spawn at at these. +var/list/asteroid_spawn = list() // Asteroid "Dungeons" spawn at these. var/list/tdome1 = list() var/list/tdome2 = list() var/list/tdomeobserve = list() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4c5e8f5ebc2..7d5e5d78bcc 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -6,7 +6,8 @@ var/list/admin_verbs_default = list( /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ /client/proc/hide_verbs, /*hides all our adminverbs*/ /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ - /client/proc/cmd_mentor_check_new_players + /client/proc/cmd_mentor_check_new_players, + /client/proc/notification_add /*allows everyone to set up player notifications*/ ) var/list/admin_verbs_admin = list( /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game.*/ @@ -217,7 +218,8 @@ var/list/admin_verbs_debug = list( /client/proc/cmd_ss_panic, /client/proc/reset_openturf, /datum/admins/proc/capture_map, - /client/proc/global_ao_regenerate + /client/proc/global_ao_regenerate, + /client/proc/add_client_color ) var/list/admin_verbs_paranoid_debug = list( @@ -1159,6 +1161,47 @@ var/list/admin_verbs_cciaa = list( SSzcopy.hard_reset() +/client/proc/add_client_color(mob/T as mob in mob_list) + set category = "Debug" + set name = "Add Client Color" + set desc = "Adds a client color to a given mob" + + if(!check_rights(R_DEV)) + return + + if(!ishuman(T)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + var/mob/living/carbon/human/C = T + + var/rr = input("Enter color value", "Red-Red") as num|null + var/rg = input("Enter color value", "Red-Green") as num|null + var/rb = input("Enter color value", "Red-Blue") as num|null + var/gr = input("Enter color value", "Green-Red") as num|null + var/gg = input("Enter color value", "Green-Green") as num|null + var/gb = input("Enter color value", "Green-Blue") as num|null + var/br = input("Enter color value", "Blue-Red") as num|null + var/bg = input("Enter color value", "Blue-Green") as num|null + var/bb = input("Enter color value", "Blue-Blue") as num|null + var/priority = input("Enter priority value.", "Priority") as num|null + if(!usr) + return + if(!C) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(priority) + var/datum/client_color/CC = new /datum/client_color() + CC.client_color = list(rr,rg,rb, gr,gg,gb, br,bg,bb) + CC.priority = priority + C.client_colors |= CC + sortTim(C.client_colors, /proc/cmp_clientcolor_priority) + C.update_client_color() + + log_and_message_admins("gave [key_name(C)] a new client color.") + feedback_add_details("admin_verb","CR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + #ifdef ENABLE_SUNLIGHT /client/proc/apply_sunstate() set category = "Fun" diff --git a/code/modules/admin/verbs/warning.dm b/code/modules/admin/verbs/warning.dm index 1cf55a457b9..08d31cb0cc0 100644 --- a/code/modules/admin/verbs/warning.dm +++ b/code/modules/admin/verbs/warning.dm @@ -102,7 +102,7 @@ */ /client/verb/warnings_check() - set name = "My warnings" + set name = "Warnings and Notifications" set category = "OOC" set desc = "Display warnings issued to you." @@ -115,7 +115,48 @@ alert("Connection to the SQL database lost. Aborting. Please alert an Administrator or a member of staff.") return - var/dat = "

Warnings received


" + var/dat = "" + + // + // Notifications + // + + var/DBQuery/notification_query = dbcon.NewQuery({"SELECT + id, message, created_by + FROM ss13_player_notifications + WHERE + acked_at IS NULL + AND ckey = :ckey: + AND type IN ('player_greeting','player_greeting_chat') + "}) + notification_query.Execute(list("ckey" = ckey)) + + var/notification_header=0 + while(notification_query.NextRow()) + if(!notification_header) + notification_header=1 + dat += "

Pending Notifications


" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + if(notification_header) + dat += "
ADMINTEXTACKNOWLEDGE
[notification_query.item[3]][notification_query.item[2]](Acknowledge Notification)
" + + // + // Warnings + // + + dat += "

Warnings Received


" dat += "" dat += "" @@ -182,6 +223,23 @@ warnings_check() +/client/proc/notifications_acknowledge(var/id) + if(!id) + error("Error: Argument ID for notificaton acknowledgement not supplied.") + return + + if (!establish_db_connection(dbcon)) + error("Error: Unable to establish db connection during notification acknowledgement.") + return + + var/DBQuery/query = dbcon.NewQuery({"UPDATE ss13_player_notifications + SET acked_by = :ckey:, acked_at = NOW() + WHERE id = :id: AND ckey = :ckey: + "}) + query.Execute(list("ckey" = src.ckey, "id" = id)) + + warnings_check() + /* * A proc to gather notifications regarding your warnings. * Called by /datum/preferences/proc/gather_notifications() in preferences.dm @@ -334,6 +392,50 @@ usr << browse(dat, "window=lookupwarns;size=900x500") feedback_add_details("admin_verb","WARN-LKUP") +//Admin Proc to add a new User Notification +/client/proc/notification_add() + set category = "Admin" + set name = "Add Notification" + + if(!check_rights(R_ADMIN|R_MOD|R_DEV|R_CCIAA)) + return + + if (!establish_db_connection(dbcon)) + error("Error: Unable to establish db connection while adding a notification.") + return + + var/ckey = ckey(input(usr, "What ckey?", "Enter a ckey")) + if(!ckey) + to_chat(usr,"You need to specify a ckey.") + return + + //Validate ckey + var/DBQuery/validatequery = dbcon.NewQuery("SELECT id FROM ss13_player WHERE ckey = :ckey:") + validatequery.Execute(list("ckey" = ckey)) + + if (validatequery.RowCount() == 0) + to_chat(usr, "Could not find a player with that ckey.") + return + else if (validatequery.RowCount() != 1) + to_chat(usr, "Found more than one player with this ckey. This should not happen, please inform the server maintainers.") + return + + var/list/types=list("player_greeting","player_greeting_chat","admin","ccia") + var/type = input(usr, "Which Type?", "Choose a type", "") as null|anything in (types) + if(!type) + to_chat(usr,"You need to specify a type.") + return + + var/message = sanitize(input(usr,"Notification Message", "Specify a notification message")) + if(!message) + to_chat(usr,"You need to specify a notification message.") + return + + var/DBQuery/addquery = dbcon.NewQuery("INSERT INTO ss13_player_notifications (`ckey`, `type`, `message`, `created_by`) VALUES (:ckey:, :type:, :message:, :a_ckey:)") + addquery.Execute(list("ckey" = ckey, "type" = type, "message" = message, "a_ckey" = usr.ckey)) + to_chat(usr,"Notification added.") + + /* * A proc for editing and deleting warnings issued */ diff --git a/code/modules/cargo/randomstock.dm b/code/modules/cargo/randomstock.dm index 31ea9756522..1414959b8c2 100644 --- a/code/modules/cargo/randomstock.dm +++ b/code/modules/cargo/randomstock.dm @@ -133,6 +133,7 @@ var/list/global/random_stock_common = list( "phoronsheets" = 2, "hide" = 1, "arcade" = 2, + "custom_ka" = 1, "nothing" = 0) var/list/global/random_stock_uncommon = list( @@ -225,7 +226,6 @@ var/list/global/random_stock_rare = list( "humanhide" = 0.5, "modkit" = 1, "contraband" = 0.8, - "custom_ka" = 0.5, "nothing" = 0) var/list/global/random_stock_large = list( diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 1314701f634..94a564d3ca8 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -101,6 +101,10 @@ if(href_list["warnacknowledge"]) var/queryid = text2num(href_list["warnacknowledge"]) warnings_acknowledge(queryid) + + if(href_list["notifacknowledge"]) + var/queryid = text2num(href_list["notifacknowledge"]) + notifications_acknowledge(queryid) if(href_list["warnview"]) warnings_check() diff --git a/code/modules/client/client_color.dm b/code/modules/client/client_color.dm index 575641ed3b6..1279aea35d0 100644 --- a/code/modules/client/client_color.dm +++ b/code/modules/client/client_color.dm @@ -47,7 +47,7 @@ /mob/proc/update_client_color() if(!client) return - client.color = "" + client.color = null if(!client_colors.len) return var/datum/client_color/CC = client_colors[1] @@ -68,4 +68,8 @@ /datum/client_color/tritanopia client_color = list(0.95,0.07,0, 0,0.44,0.52, 0.05,0.49,0.48) - priority = 100 \ No newline at end of file + priority = 100 + +/datum/client_color/vaurca + client_color = list(0.3,0,0, 0,0.5,0, 0.5,0,1.5) + priority = 101 \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm index ca5c76e1b99..1ea112a2ee9 100644 --- a/code/modules/client/preference_setup/loadout/loadout_general.dm +++ b/code/modules/client/preference_setup/loadout/loadout_general.dm @@ -77,6 +77,7 @@ banners["banner, Jargon"] = /obj/item/weapon/flag/jargon banners["banner, NanoTrasen"] = /obj/item/weapon/flag/nanotrasen banners["banner, Eridani Fed"] = /obj/item/weapon/flag/eridani + banners["banner, Sedantis"] = /obj/item/weapon/flag/vaurca gear_tweaks += new/datum/gear_tweak/path(banners) /datum/gear/flag @@ -94,6 +95,7 @@ flags["flag, Jargon"] = /obj/item/weapon/flag/jargon/l flags["flag, NanoTrasen"] = /obj/item/weapon/flag/nanotrasen/l flags["flag, Eridani Fed"] = /obj/item/weapon/flag/eridani/l + flags["flag, Sedantis"] = /obj/item/weapon/flag/vaurca/l gear_tweaks += new/datum/gear_tweak/path(flags) diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 883e46cd5f7..3ca07276754 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -109,6 +109,24 @@ display_name = "trenchcoat, grey" path = /obj/item/clothing/suit/storage/toggle/trench/grey +/datum/gear/suit/det_trenchcoat_brown + display_name = "brown trenchcoat (Detective)" + description = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" + path = /obj/item/clothing/suit/storage/toggle/det_trench + allowed_roles = list("Detective", "Head of Security") + +/datum/gear/suit/det_trenchcoat_black + display_name = "black trenchcoat (Detective)" + description = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" + path = /obj/item/clothing/suit/storage/toggle/det_trench/black + allowed_roles = list("Detective", "Head of Security") + +/datum/gear/suit/det_trenchcoat_techni + display_name = "technicolor trenchcoat (Detective)" + description = "A 23rd-century multi-purpose trenchcoat. It's fibres are hyper-absorbent. Can be painted into any color." + path = /obj/item/clothing/suit/storage/toggle/det_trench/technicolor + allowed_roles = list("Detective", "Head of Security") + /datum/gear/suit/ian display_name = "worn shirt" description = "A worn out, curiously comfortable t-shirt with a picture of Ian." @@ -190,4 +208,4 @@ coat["dominia cape"] = /obj/item/clothing/suit/storage/dominia coat["dominia great coat, black"] = /obj/item/clothing/suit/storage/toggle/dominia/black coat["dominia great coat, alternative black"] = /obj/item/clothing/suit/storage/toggle/dominia/black/alt - gear_tweaks += new/datum/gear_tweak/path(coat) \ No newline at end of file + gear_tweaks += new/datum/gear_tweak/path(coat) diff --git a/code/modules/client/preferences_notification.dm b/code/modules/client/preferences_notification.dm index 171aab8fb84..f8b30b51718 100644 --- a/code/modules/client/preferences_notification.dm +++ b/code/modules/client/preferences_notification.dm @@ -153,9 +153,62 @@ var/cciaa_actions = count_ccia_actions(user) if (cciaa_actions) new_notification("info", cciaa_actions) + + add_active_notifications(user) + +/datum/preferences/proc/add_active_notifications(var/client/user) + if(!user) + return null + + if (!establish_db_connection(dbcon)) + error("Error initiatlizing database connection while getting notifications.") + return null + + var/DBQuery/query = dbcon.NewQuery({"SELECT + message, type, id + FROM ss13_player_notifications + WHERE acked_at IS NULL AND ckey = :ckey: + "}) + query.Execute(list("ckey" = user.ckey)) + + var/chat_notification=0 + var/panel_notification=0 + var/notification_count=0 + + while(query.NextRow()) + var/autoack=0 + //Lets loop through the results + switch(query.item[2]) + if("player_greeting") + panel_notification=1 + notification_count++ + if("player_greeting_chat") + chat_notification=1 + panel_notification=1 + notification_count++ + if("admin") + discord_bot.send_to_admins("Server Notification for [user.ckey]: [query.item[1]]") + post_webhook_event(WEBHOOK_ADMIN, list("title"="Server Notification for: [user.ckey]", "message"="Server Notification Triggered for [user.ckey]: [query.item[1]]")) + //Immediately ack the notification + autoack=1 + if("ccia") + discord_bot.send_to_cciaa("Server Notification for [user.ckey]: [query.item[1]]") + post_webhook_event(WEBHOOK_CCIAA_EMERGENCY_MESSAGE, list("title"="Server Notification for: [user.ckey]", "message"="Server Notification Triggered for [user.ckey]: [query.item[1]]")) + //Immeidately ack the notification + autoack=1 + if(autoack) + var/DBQuery/ackquery = dbcon.NewQuery({"UPDATE ss13_player_notifications + SET acked_by = 'autoack-server', acked_at = NOW() + WHERE id = :id: + "}) + ackquery.Execute(list("id" = query.item[3])) + if(panel_notification) + new_notification("warning","You have [notification_count] unread notifications! Click here to review and acknowledge them!") + if(chat_notification) + to_chat(user,"You have unacknowledged notifications.
Click here to review and acknowledge them!
") /* - * Helper proc for getting a count of active CCIA actions against the player's character. + * Helper proc for getting a count of active CCIA actions against the player's characters. */ /datum/preferences/proc/count_ccia_actions(var/client/user) if (!user) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 8c210e649ce..05455a9a669 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -335,7 +335,7 @@ body_parts_covered = HANDS slot_flags = SLOT_GLOVES attack_verb = list("challenged") - species_restricted = list("exclude","Unathi","Tajara","Vaurca", "Golem","Vaurca Breeder") + species_restricted = list("exclude","Unathi","Tajara","Vaurca", "Golem","Vaurca Breeder","Vaurca Warform") sprite_sheets = list( "Vox" = 'icons/mob/species/vox/gloves.dmi', "Resomi" = 'icons/mob/species/resomi/gloves.dmi' @@ -435,7 +435,7 @@ slot_flags = SLOT_HEAD w_class = 2.0 uv_intensity = 50 //Light emitted by this object or creature has limited interaction with diona - species_restricted = list("exclude","Vaurca Breeder") + species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") var/light_overlay = "helmet_light" var/light_applied @@ -543,7 +543,7 @@ "Resomi" = 'icons/mob/species/resomi/masks.dmi', "Tajara" = 'icons/mob/species/tajaran/mask.dmi', "Unathi" = 'icons/mob/species/unathi/mask.dmi') - species_restricted = list("exclude","Vaurca Breeder") + species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") var/voicechange = 0 var/list/say_messages @@ -575,7 +575,7 @@ slowdown = SHOES_SLOWDOWN force = 0 var/overshoes = 0 - species_restricted = list("exclude","Unathi","Tajara","Vox","Vaurca","Vaurca Breeder") + species_restricted = list("exclude","Unathi","Tajara","Vox","Vaurca","Vaurca Breeder","Vaurca Warform") sprite_sheets = list("Vox" = 'icons/mob/species/vox/shoes.dmi') var/silent = 0 sprite_sheets = list( @@ -650,7 +650,7 @@ var/blood_overlay_type = "suit" siemens_coefficient = 0.9 w_class = 3 - species_restricted = list("exclude","Vaurca Breeder") + species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") sprite_sheets = list( "Vox" = 'icons/mob/species/vox/suit.dmi', @@ -690,7 +690,7 @@ "Vox" = 'icons/mob/species/vox/uniform.dmi', "Golem" = 'icons/mob/uniform_fat.dmi', "Resomi" = 'icons/mob/species/resomi/uniform.dmi') - species_restricted = list("exclude","Vaurca Breeder") + species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") //convenience var for defining the icon state for the overlay used when the clothing is worn. //Also used by rolling/unrolling. diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 2506e374def..83373fbc9b2 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -28,7 +28,8 @@ BLIND // can't see anything var/activated_color = null sprite_sheets = list( "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Resomi" = 'icons/mob/species/resomi/eyes.dmi' + "Resomi" = 'icons/mob/species/resomi/eyes.dmi', + "Vaurca Warform" = 'icons/mob/species/warriorform/eyes.dmi' ) species_restricted = list("exclude","Vaurca Breeder") diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index 3016e71ab51..fcf3a307a4f 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -3,7 +3,7 @@ desc = "Because you really needed another excuse to punch your crewmates." icon_state = "boxing" item_state = "boxing" - species_restricted = list("exclude","Vaurca Breeder") + species_restricted = list("exclude","Vaurca Breeder","Vaurca Warform") /obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) if(iswirecutter(W) || istype(W, /obj/item/weapon/scalpel)) diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 94242832dda..a3de81833cc 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -213,7 +213,7 @@ force = 5 punch_force = 10 clipped = 1 - species_restricted = list("exclude","Golem","Vaurca Breeder") + species_restricted = list("exclude","Golem","Vaurca Breeder","Vaurca Warform") /obj/item/clothing/gloves/powerfist/Touch(atom/A, mob/living/user, proximity) if(!proximity) diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm index 3b1abc793ac..ca703b9a7f2 100644 --- a/code/modules/clothing/spacesuits/alien.dm +++ b/code/modules/clothing/spacesuits/alien.dm @@ -234,7 +234,7 @@ item_state = "magboots" icon_state = "magboots" - species_restricted = list("Vaurca Breeder") + species_restricted = list("Vaurca Breeder","Vaurca Warform") sprite_sheets = list( "Vaurca Breeder" = 'icons/mob/species/breeder/shoes.dmi' ) @@ -281,7 +281,7 @@ //ZZODDAA /obj/item/clothing/gloves/yellow/typec icon = 'icons/mob/species/breeder/inventory/items.dmi' - desc = "A set of form-fitting carapace gauntlets. They appear to be fitted with some robust hydralics." + desc = "A set of form-fitting carapace gauntlets. They appear to be fitted with some robust hydraulics." name = "carapace gauntlets" w_class = 5.0 icon_state = "forceglove" diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index fe051d0240b..f05784df86f 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -46,7 +46,7 @@ anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) - H.visible_message("[H.name] vanishes into thin air!",1) + H.visible_message("[H] vanishes into thin air!", "You vanish into thin air!") /obj/item/rig_module/stealth_field/deactivate() @@ -278,4 +278,4 @@ interface_name = "integrated cryptographic sequencer" interface_desc = "A complex uprade that allows the user to apply an EMAG effect to certain objects. High power cost." - device_type = /obj/item/weapon/robot_emag \ No newline at end of file + device_type = /obj/item/weapon/robot_emag diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index ec17d7c20a9..64713a7087b 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -272,6 +272,21 @@ interface_name = "combat chem dispenser" interface_desc = "Dispenses loaded chemicals directly into the bloodstream." +/obj/item/rig_module/chem_dispenser/vaurca + + name = "vaurca combat chemical injector" + desc = "A complex web of tubing and needles suitable for vaurcan hardsuit use." + + charges = list( + list("synaptizine", "synaptizine", 0, 30), + list("hyperzine", "hyperzine", 0, 30), + list("oxycodone", "oxycodone", 0, 30), + list("phoron", "phoron", 0, 60), + list("kois", "k'ois paste", 0, 80) + ) + + interface_name = "vaurca combat chem dispenser" + interface_desc = "Dispenses loaded chemicals directly into the bloodstream." /obj/item/rig_module/chem_dispenser/injector @@ -636,6 +651,7 @@ return 1 + /obj/item/rig_module/cooling_unit name = "mounted cooling unit" toggleable = 1 @@ -660,4 +676,64 @@ H.bodytemperature -= temp_adj active_power_cost = round((temp_adj/max_cooling)*charge_consumption) - return active_power_cost \ No newline at end of file + return active_power_cost + +/obj/item/rig_module/boring + name = "burrowing lasers" + desc = "A set of precise boring lasers designed to carve a hole beneath the user." + icon_state = "actuators" + interface_name = "boring laser" + interface_desc = "Allows you to burrow to the z-level below." + + disruptive = 1 + + use_power_cost = 5 + module_cooldown = 25 + + usable = 1 + +/obj/item/rig_module/boring/engage() + if (!..()) + return 0 + + playsound(src,'sound/magic/lightningbolt.ogg',60,1) + var/turf/T = get_turf(holder.wearer) + if(istype(T, /turf/simulated)) + if(istype(T, /turf/simulated/mineral) || istype(T, /turf/simulated/wall) || istype(T, /turf/simulated/shuttle)) + T.ChangeTurf(T.baseturf) + else + T.ChangeTurf(/turf/space) + + + +var/global/list/lattice_users = list() + +/obj/item/rig_module/lattice + name = "neural lattice" + desc = "A probing mind collar that synchronizes the subject's pain receptors with all other neural lattices on the local grid." + icon_state = "actuators" + interface_name = "neural lattice" + interface_desc = "Synchronize neural lattice to reduce pain." + + disruptive = 0 + + toggleable = 1 + confined_use = 1 + + +/obj/item/rig_module/lattice/activate() + if (!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + H << "Neural lattice engaged. Pain receptors altered." + lattice_users.Add(H) + +/obj/item/rig_module/lattice/deactivate() + if (!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + H << "Neural lattice disengaged. Pain receptors restored." + lattice_users.Remove(H) + diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm index 64c71ad4624..fdbc2cb105d 100644 --- a/code/modules/clothing/spacesuits/rig/rig_pieces.dm +++ b/code/modules/clothing/spacesuits/rig/rig_pieces.dm @@ -51,7 +51,8 @@ resilience = 0.2 can_breach = 1 sprite_sheets = list("Tajara" = 'icons/mob/species/tajaran/suit.dmi',"Unathi" = 'icons/mob/species/unathi/suit.dmi') - species_restricted = list("exclude","Diona","Xenomorph","Vaurca","Golem", "Vox") + species_restricted = list("exclude","Diona","Xenomorph","Golem","Vaurca","Vox") + supporting_limbs = list() //TODO: move this to modules diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm index 4061ffc3324..26449d73a2c 100644 --- a/code/modules/clothing/spacesuits/rig/suits/alien.dm +++ b/code/modules/clothing/spacesuits/rig/suits/alien.dm @@ -35,3 +35,43 @@ /obj/item/clothing/shoes/magboots/rig/unathi species_restricted = list("Unathi") + + + +/obj/item/weapon/rig/vaurca + name = "combat exoskeleton control module" + desc = "An ancient piece of equipment from a bygone age, This highly advanced Vaurcan technology rarely sees use outside of a battlefield." + suit_type = "combat exoskeleton" + icon_state = "vaurca_rig" + armor = list(melee = 65, bullet = 65, laser = 100, energy = 100, bomb = 90, bio = 100, rad = 80) + vision_restriction = 0 + slowdown = 2 + offline_slowdown = 3 + + chest_type = /obj/item/clothing/suit/space/rig/vaurca + helm_type = /obj/item/clothing/head/helmet/space/rig/vaurca + boot_type = /obj/item/clothing/shoes/magboots/rig/vaurca + air_type = /obj/item/weapon/tank/phoron + + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy) + + initial_modules = list( + /obj/item/rig_module/actuators/combat, + /obj/item/rig_module/vision/thermal, + /obj/item/rig_module/device/flash, + /obj/item/rig_module/chem_dispenser/vaurca, + /obj/item/rig_module/boring, + /obj/item/rig_module/lattice + + ) + +/obj/item/clothing/head/helmet/space/rig/vaurca + species_restricted = list("Vaurca") + light_overlay = "helmet_light_dual_green" + light_color = "#3e7c3e" + +/obj/item/clothing/suit/space/rig/vaurca + species_restricted = list("Vaurca") + +/obj/item/clothing/shoes/magboots/rig/vaurca + species_restricted = list("Vaurca") diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index 53f534bde93..24939fcf64f 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -14,7 +14,8 @@ sprite_sheets_refit = list( "Unathi" = 'icons/mob/species/unathi/helmet.dmi', "Tajara" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi' + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Vaurca" = 'icons/mob/species/vaurca/helmet.dmi' ) sprite_sheets_obj = list( "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', @@ -40,7 +41,8 @@ sprite_sheets_refit = list( "Unathi" = 'icons/mob/species/unathi/suit.dmi', "Tajara" = 'icons/mob/species/tajaran/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi' + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Vaurca" = 'icons/mob/species/vaurca/suit.dmi' ) sprite_sheets_obj = list( "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index f0f911e4a8f..854fff0d5e7 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -137,11 +137,13 @@ siemens_coefficient = 0.7 /obj/item/clothing/suit/storage/toggle/det_trench/black + name = "black trenchcoat" icon_state = "detective2" icon_open = "detective2_open" icon_closed = "detective2" /obj/item/clothing/suit/storage/toggle/det_trench/technicolor + name = "black trenchcoat" desc = "A 23rd-century multi-purpose trenchcoat. It's fibres are hyper-absorbent." icon_state = "suit_detective_black" item_state = "suit_detective_black" diff --git a/code/modules/custom_ka/projectiles.dm b/code/modules/custom_ka/projectiles.dm index 4509d402bd9..d1f4719d0da 100644 --- a/code/modules/custom_ka/projectiles.dm +++ b/code/modules/custom_ka/projectiles.dm @@ -20,7 +20,6 @@ if(isliving(A)) //Never do more than 15 damage to a living being per shot. damage = min(damage,15) - strike_thing(A,aoe*aoe_scale,damage) . = ..() @@ -31,9 +30,6 @@ if(istype(target_turf, /turf/simulated/mineral)) var/turf/simulated/mineral/M = target_turf M.kinetic_hit(damage,dir) - else if(istype(target_turf, /turf/simulated/floor/asteroid)) - var/turf/simulated/floor/asteroid/A = target_turf - A.gets_dug() new /obj/effect/overlay/temp/kinetic_blast(target_turf) diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 850f617a4c2..70a1279e482 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -99,7 +99,7 @@ last_process_worldtime = world.time /obj/machinery/dnaforensics/proc/complete_scan() - src.visible_message("\icon[src] makes an insistent chime.", 2) + visible_message("\icon[src] makes an insistent chime.", range = 2) update_icon() if(bloodsamp) var/obj/item/weapon/paper/P = new() diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 011436675df..4b643b015f2 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -324,12 +324,12 @@ return G.affecting.loc = src.loc G.affecting.Weaken(5) - visible_message("[G.assailant] dunks [G.affecting] into the [src]!", 3) + visible_message("[G.assailant] dunks [G.affecting] into the [src]!", range = 3) qdel(W) return else if (istype(W, /obj/item) && get_dist(src,user)<2) user.drop_item(src.loc) - visible_message("[user] dunks [W] into the [src]!", 3) + visible_message("[user] dunks [W] into the [src]!", range = 3) return /obj/structure/holohoop/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) @@ -339,9 +339,9 @@ return if(prob(50)) I.loc = src.loc - visible_message("Swish! \the [I] lands in \the [src].", 3) + visible_message("Swish! \the [I] lands in \the [src].", range = 3) else - visible_message("\The [I] bounces off of \the [src]'s rim!", 3) + visible_message("\The [I] bounces off of \the [src]'s rim!", range = 3) return 0 else return ..(mover, target, height, air_group) @@ -482,4 +482,4 @@ /mob/living/simple_animal/penguin/holodeck/proc/derez() visible_message("\The [src] fades away!") - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index f3b5ef515f7..6e115bcc821 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -150,7 +150,7 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f if(src.arcanecheckout) new /obj/item/weapon/book/tome(src.loc) user << "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it." - user.visible_message("\The [user] stares at the blank screen for a few moments, \his expression frozen in fear. When \he finally awakens from it, \he looks a lot older.", 2) + user.visible_message("\The [user] stares at the blank screen for a few moments, \his expression frozen in fear. When \he finally awakens from it, \he looks a lot older.", range = 2) src.arcanecheckout = 0 if(1) // Inventory diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index baebbe09b0d..04a082c81f4 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -9,6 +9,7 @@ invisibility = INVISIBILITY_LIGHTING simulated = 0 blend_mode = BLEND_MULTIPLY + appearance_flags = NO_CLIENT_COLOR var/needs_update = FALSE diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index 55719d792c1..2a4a3fc50dd 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -90,6 +90,7 @@ recipes += new/datum/stack_recipe("modular console frame", /obj/item/modular_computer/console, 20, time = 25, one_per_turf = TRUE) recipes += new/datum/stack_recipe("modular laptop frame", /obj/item/modular_computer/laptop, 10, time = 25) recipes += new/datum/stack_recipe("modular tablet frame", /obj/item/modular_computer/tablet, 5, time = 25) + recipes += new/datum/stack_recipe("shield fittings", /obj/item/weapon/material/shieldbits, 10, time = 25) /material/plasteel/generate_recipes() ..() @@ -127,6 +128,7 @@ recipes += new/datum/stack_recipe("ore box", /obj/structure/ore_box, 10, time = 15, one_per_turf = 1, on_floor = 1) recipes += new/datum/stack_recipe("wooden bucket", /obj/item/weapon/reagent_containers/glass/bucket/wood, 2, time = 4, one_per_turf = 0, on_floor = 0) recipes += new/datum/stack_recipe("shaft", /obj/item/weapon/material/shaft, 10, time = 25, one_per_turf = 0, on_floor = 0) + recipes += new/datum/stack_recipe("buckler donut", /obj/item/weapon/material/woodenshield, 20, time = 25, one_per_turf = 0, on_floor = 0) /material/cardboard/generate_recipes() ..() diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 184c89395f3..6cefdf8feba 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -29,10 +29,9 @@ new/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiadeus(src) new/obj/item/weapon/flame/lighter/zippo(src) if(6 to 10) - new/obj/item/weapon/pickaxe/drill(src) - new/obj/item/device/taperecorder(src) - new/obj/item/clothing/suit/space(src) - new/obj/item/clothing/head/helmet/space(src) + new/obj/random/custom_ka(src) + new/obj/random/custom_ka(src) + new/obj/random/custom_ka(src) if(11 to 15) new/obj/item/weapon/reagent_containers/glass/beaker/bluespace(src) if(16 to 20) @@ -95,11 +94,7 @@ new/obj/item/weapon/pickaxe/gold(src) if(81 to 82) new/obj/item/weapon/gun/energy/plasmacutter(src) - if(83) - new/obj/random/custom_ka(src) - new/obj/random/custom_ka(src) - new/obj/random/custom_ka(src) - if(84) + if(83 to 84) new/obj/item/toy/katana(src) if(85) new/obj/item/seeds/random(src) diff --git a/code/modules/mining/mine_turf_types.dm b/code/modules/mining/mine_turf_types.dm index a6a0335598f..40e9c26027f 100644 --- a/code/modules/mining/mine_turf_types.dm +++ b/code/modules/mining/mine_turf_types.dm @@ -100,38 +100,6 @@ if (prob(20)) add_overlay("asteroid[rand(0, 9)]", TRUE) - - -/turf/simulated/floor/asteroid/ash/Entered(atom/A, atom/OL) - ..() - - if(ishuman(A)) - var/mob/living/carbon/human/H = A - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/hasfeet = 1 - if((!l_foot || l_foot.is_stump()) && (!r_foot || r_foot.is_stump())) - hasfeet = 0 - if(H.shoes && !H.buckled)//Adding ash to shoes - var/obj/item/clothing/shoes/S = H.shoes - if(istype(S)) - S.blood_color = "#6C6564" - S.track_blood = max(12,S.track_blood) - - if(!S.blood_overlay) - S.generate_blood_overlay() - if(S.blood_overlay && S.blood_overlay.color != "#6C6564") - S.cut_overlay(S.blood_overlay, TRUE) - - S.blood_overlay.color = "#6C6564" - S.add_overlay(S.blood_overlay, TRUE) - - else if (hasfeet)//Or feet - H.feet_blood_color = "#6C6564" - H.track_blood = max(12,H.track_blood) - - H.update_inv_shoes(1) - /turf/simulated/floor/asteroid/ash/rocky name = "rocky ash" icon_state = "rockyash" diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index ab0820f0d81..104ea27c169 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -513,7 +513,19 @@ var/list/mineral_can_smooth_with = list( ) mineralChance = 75 +/turf/simulated/mineral/attack_hand(var/mob/user) + add_fingerprint(user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(ishuman(user)) + var/mob/living/carbon/human/H + var/turf/destination = GetAbove(H) + + if(destination) + var/turf/start = get_turf(H) + if(start.CanZPass(H, UP)) + if(destination.CanZPass(H, UP)) + H.climb(UP, src, 20) /**********************Asteroid**************************/ diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index 084857301ef..e0c2e967a20 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -151,7 +151,7 @@ [status_report]
"} P.update_icon() - visible_message("\icon[src] The [usr] pings, \"[P.name] ready for review\", and happily disgorges a small printout.", 2) + visible_message("\icon[src] The [usr] pings, \"[P.name] ready for review\", and happily disgorges a small printout.", range = 2) playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) /**********************Minebot Upgrades**********************/ @@ -220,4 +220,4 @@ M.recalculate_synth_capacities() if(!M.jetpack) M.jetpack = new /obj/item/weapon/tank/jetpack/carbondioxide/synthetic(src) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/language/generic.dm b/code/modules/mob/language/generic.dm index e5a09290110..05d190bbde0 100644 --- a/code/modules/mob/language/generic.dm +++ b/code/modules/mob/language/generic.dm @@ -27,6 +27,7 @@ key = "0" flags = RESTRICTED syllables = list("blah","blah","blah","bleh","meh","neh","nah","wah") + partial_understanding = list(LANGUAGE_SIIK_TAU = 60) //TODO flag certain languages to use the mob-type specific say_quote and then get rid of these. /datum/language/common/get_spoken_verb(var/msg_end) @@ -66,7 +67,7 @@ // Sign language /datum/language/sign name = LANGUAGE_SIGN - desc = "A signed version of Standard, though its intent is primarily to help out people who are deaf and mute, " + desc = "A signed version of Ceti Basic, though its intent is primarily to help out people who are deaf and mute, " speech_verb = "signs" signlang_verb = list("signs", "gestures") colour = "i" diff --git a/code/modules/mob/language/station.dm b/code/modules/mob/language/station.dm index d328bb80f1e..ca4d979375a 100644 --- a/code/modules/mob/language/station.dm +++ b/code/modules/mob/language/station.dm @@ -55,7 +55,7 @@ "mi","jri","dynh","manq","rhe","zar","rrhaz","kal","chur","eech","thaa","dra","jurl","mah","sanu","dra","ii'r", "ka","aasi","far","wa","baq","ara","qara","zir","sam","mak","hrar","nja","rir","khan","jun","dar","rik","kah", "hal","ket","jurl","mah","tul","cresh","azu","ragh","mro","mra","mrro","mrra") - partial_understanding = list(LANGUAGE_SIIK_TAJR = 50, LANGUAGE_YA_SSA = 25, LANGUAGE_DELVAHII = 50) + partial_understanding = list(LANGUAGE_SIIK_TAJR = 50, LANGUAGE_YA_SSA = 25, LANGUAGE_DELVAHII = 50, LANGUAGE_SIIK_TAU = 40) /datum/language/tajaran/get_random_name(var/gender) @@ -92,7 +92,7 @@ "mi","jri","dynh","manq","rhe","zar","rrhaz","kal","chur","eech","thaa","dra","jurl","mah","sanu","dra","ii'r", "ka","aasi","far","wa","baq","ara","qara","zir","sam","mak","hrar","nja","rir","khan","jun","dar","rik","kah", "hal","ket","jurl","mah","tul","cresh","azu","ragh","mro","mra","mrro","mrra") - partial_understanding = list(LANGUAGE_SIIK_MAAS = 50, LANGUAGE_SIGN_TAJARA = 25) + partial_understanding = list(LANGUAGE_SIIK_MAAS = 50, LANGUAGE_SIGN_TAJARA = 25, LANGUAGE_SIIK_TAU= 20) /datum/language/yassa name = LANGUAGE_YA_SSA @@ -124,6 +124,18 @@ "hal","kete","juril","mah","tul","cresh","azu","ragh","miro","mara","mrero","mrara") partial_understanding = list(LANGUAGE_SIIK_MAAS = 50) +/datum/language/siik_tau + name = LANGUAGE_SIIK_TAU + desc = "A macaronic form of Tau Ceti Basic and Siik'maas, developed in Mendell City soon after Tajara entered the galactic field." + speech_verb = "slurs" + ask_verb = "mrowmbles" + exclaim_verb = "exclaims" + key = "t" + flags = WHITELISTED + syllables = list("m'Rr","rr","tahjr","kir","rrahj","kii","mirr","krah","ahhk","nahl","vahh","khahz","jri","rahn","dahrr", + "mi","j'Rri","dy'Nh","mah'nq","rhe","zahr","r'Rhahz","kahl") + partial_understanding = list(LANGUAGE_SIIK_MAAS = 40, LANGUAGE_SIIK_TAJR = 20, LANGUAGE_TCB = 60) + /datum/language/skrell name = LANGUAGE_SKRELLIAN desc = "A melodic and complex language spoken by the Skrell of Qerrbalak. Some of the notes are inaudible to humans." diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index fc180c9f91e..ee12c3b512e 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1296,6 +1296,9 @@ if (src.is_diona()) setup_gestalt(1) + burn_mod = species.burn_mod + brute_mod = species.brute_mod + max_stamina = species.stamina stamina = max_stamina sprint_speed_factor = species.sprint_speed_factor diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index c08fb14a7a0..30ac0b60416 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -102,7 +102,7 @@ /mob/living/carbon/human/adjustBruteLoss(var/amount) - amount = amount*species.brute_mod + amount *= brute_mod if(amount > 0) take_overall_damage(amount, 0) else @@ -110,7 +110,7 @@ BITSET(hud_updateflag, HEALTH_HUD) /mob/living/carbon/human/adjustFireLoss(var/amount) - amount = amount*species.burn_mod + amount *= burn_mod if(amount > 0) take_overall_damage(0, amount) else @@ -118,7 +118,7 @@ BITSET(hud_updateflag, HEALTH_HUD) /mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.brute_mod + amount *= brute_mod if (organ_name in organs_by_name) var/obj/item/organ/external/O = get_organ(organ_name) @@ -131,7 +131,7 @@ BITSET(hud_updateflag, HEALTH_HUD) /mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.burn_mod + amount *= burn_mod if (organ_name in organs_by_name) var/obj/item/organ/external/O = get_organ(organ_name) @@ -245,6 +245,23 @@ else ..() +/mob/living/carbon/human/adjustHalLoss(var/amount, var/ignoreImmunity = 0)//An inherited version so this doesnt affect cyborgs + if(status_flags & GODMODE) return 0 //godmode + if(!ignoreImmunity)//Adjusting how hallloss works. Species with the NO_PAIN flag will suffer most of the effects of halloss, but will be immune to most conventional sources of accumulating it + if (species && species.flags & NO_PAIN)//Species with this flag will only gather halloss through species-specific mechanics, which apply it with the ignoreImmunity flag + return 0 + + if(wearing_rig) //I don't know if this is the best way, but I'm hard-pressed to think of a different way. Thanks Vaurca. + for(var/obj/item/rig_module/lattice/L in wearing_rig.installed_modules) + if(L.active && lattice_users.len) + amount = amount / (lattice_users.len + 1) + for(var/mob/living/carbon/human/H in lattice_users) + if(H != src) + H.setHalLoss(min(max(H.getHalLoss() + amount, 0),(H.maxHealth*2))) + H << "Your neural lattice buzzes, filling your mind with pain!" + + halloss = min(max(halloss + amount, 0),(maxHealth*2)) + //////////////////////////////////////////// //Returns a list of damaged organs diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index e8a92091f52..67a69631dff 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -566,3 +566,189 @@ G.affecting.forceMove(locate(T.x + rand(-1,1), T.y + rand(-1,1), T.z)) else qdel(G) + +/mob/living/carbon/human/proc/trample() + set category = "Abilities" + set name = "Trample" + set desc = "Charge forward, trampling anything in your path until you hit something more stubborn than you are." + + if(last_special > world.time) + to_chat(src, "You are too tired to charge!.") + return + + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + to_chat(src, "You cannot charge in your current state!.") + return + + last_special = world.time + + src.visible_message("\The [src] takes a step backwards and rears up.", + "You take a step backwards and then...") + if(do_after(src,5)) + playsound(loc, 'sound/species/shadow/grue_screech.ogg', 100, 1) + src.visible_message("\The [src] charges!") + trampling() + + +/mob/living/carbon/human/proc/trampling() + + var/brokesomething = 0//true if we break anything + var/done = 0//Set true if we fail to break something. We won't try to break anything for the rest of the proc + + var/turf/target = get_step(src, dir) + + for(var/obj/obstacle in get_turf(src)) + if((obstacle.flags & ON_BORDER) && (src != obstacle)) + if(!obstacle.CheckExit(src, target)) + brokesomething++ + if (!crash_into(obstacle)) + done = 1 + + if (!done && !target.CanPass(src, target)) + crash_into(target) + brokesomething++ + if (!target.CanPass(src, target)) + done = 1 + + if (!done) + for (var/atom/A in target) + if (A.density && A != src && A.loc != src) + brokesomething++ + if (!crash_into(A)) + done = 1 + if(istype(A, /mob/living) && !A.density) + brokesomething++ + crash_into(A) + + if (brokesomething) + playsound(get_turf(target), 'sound/weapons/heavysmash.ogg', 100, 1) + attack_log += "\[[time_stamp()]\]crashed into [brokesomething] objects at ([target.x];[target.y];[target.z]) " + msg_admin_attack("[key_name(src)] crashed into [brokesomething] objects at (JMP)" ) + + if (!done && target.Enter(src, null)) + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + return 0 + + step(src, dir) + playsound(src,'sound/mecha/mechstep.ogg',25,1) + if (brokesomething) + src.visible_message("[src.name] breaks through!") + addtimer(CALLBACK(src, .proc/trampling), 1) + + else + target = get_step(src, dir) + do_attack_animation(target) + +/mob/living/carbon/human/proc/crash_into(var/atom/A) + var/aname = A.name + var/oldtype = A.type + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + return 0 + + if (istype(A, /mob/living)) + var/mob/living/M = A + attack_log += "\[[time_stamp()]\] Crashed into [key_name(M)]" + M.attack_log += "\[[time_stamp()]\] Was rammed by [key_name(src)]" + msg_admin_attack("[key_name(src)] crashed into [key_name(M)] at (JMP)" ) + + A.ex_act(2) + + sleep(1) + if (A && !(A.gcDestroyed) && A.type == oldtype) + src.visible_message("[src.name] plows into \the [aname]!") + return 0 + + return 1 + +/mob/living/carbon/human/proc/rebel_yell() + set category = "Abilities" + set name = "Screech" + set desc = "Emit a powerful screech which stuns hearers in a two-tile radius." + + if(last_special > world.time) + to_chat(src, "You are too tired to screech!.") + return + + if(stat || paralysis || stunned || weakened) + to_chat(src, "You cannot screech in your current state!.") + return + + last_special = world.time + + visible_message("[src.name] lets out an ear piercing shriek!", + "You let out an ear-shattering shriek!", + "You hear a painfully loud shriek!") + + var/list/victims = list() + + for (var/mob/living/carbon/human/T in hearers(2, src)) + if (T == src) + continue + + if (istype(T) && (T:l_ear || T:r_ear) && istype((T:l_ear || T:r_ear), /obj/item/clothing/ears/earmuffs)) + continue + + if (!vampire_can_affect_target(T, 0)) + continue + + to_chat(T, "You hear an ear piercing shriek and feel your senses go dull!") + T.Weaken(5) + T.ear_deaf = 20 + T.stuttering = 20 + T.Stun(5) + + victims += T + + for (var/obj/structure/window/W in view(2)) + W.shatter() + + for (var/obj/machinery/light/L in view(4)) + L.broken() + + playsound(loc, 'sound/voice/shriek1.ogg', 100, 1) + + if (victims.len) + admin_attacker_log_many_victims(src, victims, "used rebel yell to stun", "was stunned by [key_name(src)] using rebel yell", "used rebel yell to stun") + +/mob/living/carbon/human/proc/formic_spray() + set category = "Abilities" + set name = "Napalm" + set desc = "Spew a cone of ignited napalm in front of you" + + if(last_special > world.time) + to_chat(src,"You are too tired to spray napalm.") + return + + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + to_chat(src,"You cannot spray napalm in your current state.") + return + + last_special = world.time + playsound(loc, 'sound/species/shadow/grue_screech.ogg', 100, 1) + visible_message("\The [src] unleashes a torrent of raging flame!", + "You unleash a gust of fire!", + "You hear the roar of an inferno!") + + var/turf/T = get_step(get_step(src, dir), dir) + var/turf/T1 = get_step(T, dir) + var/turf/T2 = get_step(T1,turn(dir, 90)) + var/turf/T3 = get_step(T1,turn(dir, -90)) + var/turf/T4 = get_step(T1, dir) + var/turf/T5 = get_step(T2, dir) + var/turf/T6 = get_step(T3, dir) + var/turf/T7 = get_step(T5,turn(dir, 90)) + var/turf/T8 = get_step(T6,turn(dir, -90)) + var/list/the_targets = list(T,T1,T2,T3,T4,T5,T6,T7,T8) + + playsound(src.loc, 'sound/magic/Fireball.ogg', 200, 1) + for(var/turf/FuelSpot in the_targets) + spawn(0) + var/obj/effect/effect/water/firewater/D = new/obj/effect/effect/water/firewater(get_turf(get_step(src, dir))) + var/turf/my_target = FuelSpot + D.create_reagents(200) + if(!src) + return + D.reagents.add_reagent("greekfire", 200) + D.set_color() + D.set_up(my_target, rand(6,8), 1, 50) + return diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index 91c328323d0..12020a76d76 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -78,6 +78,14 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin) /mob/living/carbon/human/type_c layer = 5 +/mob/living/carbon/human/type_big/Initialize(mapload) + . = ..(mapload, "Vaurca Warform") + src.gender = NEUTER + src.mutations.Add(HULK) + +/mob/living/carbon/human/type_big + layer = 5 + /mob/living/carbon/human/msai_tajara/Initialize(mapload) h_style = "Tajaran Ears" . = ..(mapload, "M'sai Tajara") diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 16d14007293..6f0833da6a2 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -125,7 +125,7 @@ return verb -/mob/living/carbon/human/handle_speech_problems(var/message, var/verb) +/mob/living/carbon/human/handle_speech_problems(var/message, var/verb, var/message_mode) message = handle_speech_muts(message,verb) for(var/datum/brain_trauma/trauma in get_traumas()) if(!trauma.suppressed) diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm index ae5e492a7f4..d65e9677a2c 100644 --- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm +++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm @@ -4,8 +4,7 @@ name_plural = "Vox" icobase = 'icons/mob/human_races/r_vox.dmi' deform = 'icons/mob/human_races/r_def_vox.dmi' - default_language = LANGUAGE_VOX - language = "Ceti Basic" + language = LANGUAGE_VOX num_alternate_languages = 1 unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws/strong, /datum/unarmed_attack/bite/strong) rarity_value = 4 diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index cd7f25ce31d..f27fabdb730 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -28,6 +28,7 @@ var/icon_x_offset = 0 var/icon_y_offset = 0 var/eyes = "eyes_s" // Icon for eyes. + var/eyes_icons = 'icons/mob/human_face/eyes.dmi' // DMI file for eyes, mostly for none 32x32 species. var/has_floating_eyes // Eyes will overlay over darkness (glow) var/eyes_icon_blend = ICON_ADD // The icon blending mode to use for eyes. var/blood_color = "#A10808" // Red. @@ -70,11 +71,12 @@ var/radiation_mod = 1 // Radiation modifier var/flash_mod = 1 // Stun from blindness modifier. var/fall_mod = 1 // Fall damage modifier, further modified by brute damage modifier - var/vision_flags = DEFAULT_SIGHT // Same flags as glasses. + var/vision_flags = DEFAULT_SIGHT // Same flags as glasses. var/inherent_eye_protection // If set, this species has this level of inherent eye protection. var/eyes_are_impermeable = FALSE // If TRUE, this species' eyes are not damaged by phoron. - var/list/breakcuffs = list() //used in resist.dm to check if they can break hand/leg cuffs - + var/list/breakcuffs = list() //used in resist.dm to check if they can break hand/leg cuffs + var/natural_climbing = FALSE //If true, the species always succeeds at climbing. + var/climb_coeff = 1.25 //The coefficient to the climbing speed of the individual = 60 SECONDS * climb_coeff // Death vars. var/meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/human var/gibber_type = /obj/effect/gibspawner/human diff --git a/code/modules/mob/living/carbon/human/species/species_attack.dm b/code/modules/mob/living/carbon/human/species/species_attack.dm index fc7a85e382a..19371806f56 100644 --- a/code/modules/mob/living/carbon/human/species/species_attack.dm +++ b/code/modules/mob/living/carbon/human/species/species_attack.dm @@ -115,3 +115,36 @@ step_away(target,user,15) sleep(1) target.apply_effect(attack_damage * 0.4, WEAKEN, armour) + +/datum/unarmed_attack/claws/cleave + attack_verb = list("cleaved", "plowed", "swiped") + attack_noun = list("massive claws") + damage = 25 + sharp = 1 + edge = 1 + attack_name = "massive claws" + shredding = 1 + +/datum/unarmed_attack/claws/cleave/apply_effects(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/armour,var/attack_damage,var/zone) + ..() + var/hit_mobs = 0 + for(var/mob/living/L in orange(1,user)) + if(L == user) + continue + if(L == target) + continue + L.apply_damage(rand(5,20), BRUTE, zone, armour) + to_chat(L, "\The [user] [pick(attack_verb)] you with its [attack_noun]!") + hit_mobs++ + if(hit_mobs) + to_chat(user, "You used \the [attack_noun] to attack [hit_mobs] other target\s!") + + +/datum/unarmed_attack/bite/mandibles + attack_verb = list("mauled","gored","perforated") + attack_noun = list("mandibles") + damage = 35 + shredding = 1 + sharp = 1 + edge = 1 + attack_name = "mandibles" diff --git a/code/modules/mob/living/carbon/human/species/station/monkey.dm b/code/modules/mob/living/carbon/human/species/station/monkey.dm index 90ccd69a88f..e13fc90299f 100644 --- a/code/modules/mob/living/carbon/human/species/station/monkey.dm +++ b/code/modules/mob/living/carbon/human/species/station/monkey.dm @@ -33,6 +33,7 @@ brute_mod = 1.5 burn_mod = 1.5 fall_mod = 0.5 + natural_climbing = 1 spawn_flags = IS_RESTRICTED diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index dda42070bc9..2be57b4ec73 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -21,7 +21,7 @@ megacorporations have sparked secretive factions to fight their influence, while there is always the risk of someone digging too \ deep into the secrets of the galaxy..." num_alternate_languages = 2 - secondary_langs = list("Sol Common") + secondary_langs = list(LANGUAGE_SOL_COMMON, LANGUAGE_SIIK_TAU) name_language = null // Use the first-name last-name generator rather than a language scrambler mob_size = 9 spawn_flags = CAN_JOIN @@ -33,6 +33,8 @@ sprint_speed_factor = 0.9 sprint_cost_factor = 0.5 + climb_coeff = 1 + /datum/species/unathi name = "Unathi" short_name = "una" @@ -69,6 +71,7 @@ rarity_value = 3 breakcuffs = list(MALE) mob_size = 10 + climb_coeff = 1.35 blurb = "A heavily reptillian species, Unathi (or 'Sinta as they call themselves) hail from the Uuosa-Eso \ system, which roughly translates to 'burning mother'. A relatively recent addition to the galactic stage, they \ @@ -140,7 +143,7 @@ brute_mod = 1.2 fall_mod = 0.5 num_alternate_languages = 2 - secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_YA_SSA) + secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_YA_SSA, LANGUAGE_SIIK_TAU) name_language = LANGUAGE_SIIK_MAAS ethanol_resistance = 0.8//Gets drunk a little faster rarity_value = 2 @@ -208,9 +211,10 @@ forever scarred the species and left them with a deep rooted suspicion of artificial intelligence. As \ such an ancient and venerable species, they often hold patronizing attitudes towards the younger races." - num_alternate_languages = 2 - secondary_langs = list(LANGUAGE_SKRELLIAN) - name_language = null + num_alternate_languages = 3 + language = LANGUAGE_SKRELLIAN + secondary_langs = list(LANGUAGE_SIIK_TAU) + name_language = LANGUAGE_SKRELLIAN rarity_value = 3 spawn_flags = CAN_JOIN | IS_WHITELISTED @@ -250,8 +254,7 @@ economic_modifier = 3 icobase = 'icons/mob/human_races/r_diona.dmi' deform = 'icons/mob/human_races/r_def_plant.dmi' - language = "Ceti Basic" - default_language = LANGUAGE_ROOTSONG + language = LANGUAGE_ROOTSONG unarmed_types = list( /datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, @@ -265,7 +268,7 @@ eyes = "blank_eyes" show_ssd = "completely quiescent" num_alternate_languages = 1 - name_language = "Rootsong" + name_language = LANGUAGE_ROOTSONG ethanol_resistance = -1 //Can't get drunk taste_sensitivity = TASTE_DULL mob_size = 12 //Worker gestalts are 150kg @@ -326,6 +329,7 @@ stamina = -1 // Diona sprinting uses energy instead of stamina sprint_speed_factor = 0.5 //Speed gained is minor sprint_cost_factor = 0.8 + climb_coeff = 1.3 /datum/species/diona/handle_sprint_cost(var/mob/living/carbon/H, var/cost) var/datum/dionastats/DS = H.get_dionastats() @@ -657,6 +661,8 @@ datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H) breath_type = "phoron" poison_type = "nitrogen" //a species that breathes plasma shouldn't be poisoned by it. mob_size = 13 //their half an inch thick exoskeleton and impressive height, plus all of their mechanical organs. + natural_climbing = TRUE + climb_coeff = 0.75 blurb = "Type A are the most common type of Vaurca and can be seen as the 'backbone' of Vaurcae societies. Their most prevalent feature is their hardened exoskeleton, varying in colors \ in accordance to their hive. It is approximately half an inch thick among all Type A Vaurca. The carapace provides protection against harsh radiation, solar \ @@ -701,15 +707,15 @@ datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H) has_organ = list( "neural socket" = /obj/item/organ/vaurca/neuralsocket, - "lungs" = /obj/item/organ/lungs, + "lungs" = /obj/item/organ/lungs/vaurca, "filtration bit" = /obj/item/organ/vaurca/filtrationbit, "right heart" = /obj/item/organ/heart/right, "left heart" = /obj/item/organ/heart/left, "phoron reserve tank" = /obj/item/organ/vaurca/preserve, - "liver" = /obj/item/organ/liver, - "kidneys" = /obj/item/organ/kidneys, - "brain" = /obj/item/organ/brain, - "eyes" = /obj/item/organ/eyes + "liver" = /obj/item/organ/liver/vaurca, + "kidneys" = /obj/item/organ/kidneys/vaurca, + "brain" = /obj/item/organ/brain/vaurca, + "eyes" = /obj/item/organ/eyes/vaurca ) /datum/species/bug/equip_survival_gear(var/mob/living/carbon/human/H) @@ -723,8 +729,8 @@ datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H) var/obj/item/clothing/mask/breath/M = new /obj/item/clothing/mask/breath(H) if(H.equip_to_slot_or_del(M, slot_wear_mask)) M.autodrobe_no_remove = 1 - H.gender = NEUTER /datum/species/bug/handle_post_spawn(var/mob/living/carbon/human/H) H.gender = NEUTER + H.add_client_color(/datum/client_color/vaurca) return ..() diff --git a/code/modules/mob/living/carbon/human/species/station/tajaran_subspecies.dm b/code/modules/mob/living/carbon/human/species/station/tajaran_subspecies.dm index f59d189a5e2..11cbc51b8dd 100644 --- a/code/modules/mob/living/carbon/human/species/station/tajaran_subspecies.dm +++ b/code/modules/mob/living/carbon/human/species/station/tajaran_subspecies.dm @@ -9,13 +9,14 @@ from their fellow Tajara who cite their lackluster test scores, even among Tajara, and their higher \ crime rates." - secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_DELVAHII) + secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_DELVAHII, LANGUAGE_SIIK_TAU) slowdown = -0.8 //As opposed to -1 for Base tajara sprint_speed_factor = 0.55 // As opposed to 0.65 stamina = 100 // As opposed to 90 brute_mod = 1.1 // Less Brute Damage ethanol_resistance = 1 // Default value + climb_coeff = 1.1 cold_level_1 = 160 //RaceDefault 200 Default 260 cold_level_2 = 100 //RaceDefault 140 Default 200 @@ -54,4 +55,4 @@ primitive_form = "M'sai Farwa" - secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_SIGN_TAJARA) + secondary_langs = list(LANGUAGE_SIIK_MAAS, LANGUAGE_SIIK_TAJR, LANGUAGE_SIGN_TAJARA, LANGUAGE_SIIK_TAU) diff --git a/code/modules/mob/living/carbon/human/species/station/vaurca_subspecies.dm b/code/modules/mob/living/carbon/human/species/station/vaurca_subspecies.dm index 2d78eaf905b..046cf781f36 100644 --- a/code/modules/mob/living/carbon/human/species/station/vaurca_subspecies.dm +++ b/code/modules/mob/living/carbon/human/species/station/vaurca_subspecies.dm @@ -47,8 +47,9 @@ unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws/strong, /datum/unarmed_attack/bite/strong) rarity_value = 10 slowdown = 2 - eyes = "blank_eyes" //makes it so that eye colour is not changed when skin colour is. - brute_mod = 0.2 //note to self: remove is_synthetic checks for brmod and burnmod + eyes = "breeder_eyes" //makes it so that eye colour is not changed when skin colour is. + eyes_icons = 'icons/mob/human_face/eyes48x48.dmi' + brute_mod = 0.1 //note to self: remove is_synthetic checks for brmod and burnmod burn_mod = 0.8 //2x was a bit too much. we'll see how this goes. toxins_mod = 1 //they're not used to all our weird human bacteria. breakcuffs = list(MALE,FEMALE,NEUTER) @@ -58,9 +59,9 @@ speech_chance = 100 death_sound = 'sound/voice/hiss6.ogg' - damage_overlays = 'icons/mob/human_races/masks/dam_breeder.dmi' - damage_mask = 'icons/mob/human_races/masks/dam_mask_breeder.dmi' - blood_mask = 'icons/mob/human_races/masks/blood_breeder.dmi' + damage_overlays = 'icons/mob/human_races/masks/dam_mask_warform.dmi' + damage_mask = 'icons/mob/human_races/masks/dam_mask_warform.dmi' + blood_mask = 'icons/mob/human_races/masks/dam_mask_warform.dmi' stamina = 175 sprint_speed_factor = 1 @@ -80,5 +81,79 @@ return /datum/species/bug/type_c/handle_post_spawn(var/mob/living/carbon/human/H) + ..() H.gender = FEMALE - return ..() + return + +/datum/species/bug/type_big + name = "Vaurca Warform" + short_name = "vam" + name_plural = "Type BA" + bodytype = "Vaurca Warform" + primitive_form = "Vaurca Warrior" + icon_template = 'icons/mob/human_races/subspecies/r_vaurcamecha.dmi' + icobase = 'icons/mob/human_races/subspecies/r_vaurcamecha.dmi' + deform = 'icons/mob/human_races/subspecies/r_vaurcamecha.dmi' + default_language = LANGUAGE_GIBBERING + language = LANGUAGE_VAURCA + icon_x_offset = -8 + unarmed_types = list(/datum/unarmed_attack/claws/cleave, /datum/unarmed_attack/bite/strong) + rarity_value = 10 + slowdown = 0 + eyes = "warform_eyes" + eyes_icons = 'icons/mob/human_face/warform_eyes.dmi' + brute_mod = 0.5 + burn_mod = 0.1 + toxins_mod = 1 + total_health = 200 + breakcuffs = list(MALE,FEMALE,NEUTER) + mob_size = 30 + + speech_sounds = list('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + speech_chance = 100 + + death_sound = 'sound/voice/hiss6.ogg' + damage_overlays = 'icons/mob/human_races/masks/dam_breeder.dmi' + damage_mask = 'icons/mob/human_races/masks/dam_mask_breeder.dmi' + blood_mask = 'icons/mob/human_races/masks/blood_breeder.dmi' + + stamina = 200 + stamina_recovery = 5 + sprint_speed_factor = 0.9 + sprint_cost_factor = 0.5 + + heat_level_1 = 1000 //Default 360 + heat_level_2 = 4000 //Default 400 + heat_level_3 = 16000 //Default 1000 + hazard_high_pressure = 55000 //Default 550 + warning_high_pressure = 3250 //Default 325 + + spawn_flags = IS_RESTRICTED + flags = NO_SCAN | NO_SLIP | NO_PAIN | NO_BREATHE + + inherent_verbs = list( + /mob/living/carbon/human/proc/rebel_yell, + /mob/living/carbon/human/proc/devour_head, + /mob/living/carbon/human/proc/formic_spray, + /mob/living/carbon/human/proc/trample + ) + + has_organ = list( + "neural socket" = /obj/item/organ/vaurca/neuralsocket, + "lungs" = /obj/item/organ/lungs/vaurca, + "right heart" = /obj/item/organ/heart/right, + "left heart" = /obj/item/organ/heart/left, + "phoron reservoir" = /obj/item/organ/vaurca/reservoir, + "mechanical liver" = /obj/item/organ/liver/vaurca/robo, + "mechanical kidneys" = /obj/item/organ/kidneys/vaurca/robo, + "brain" = /obj/item/organ/brain/vaurca, + "eyes" = /obj/item/organ/eyes/vaurca, + "filtration bit" = /obj/item/organ/vaurca/filtrationbit + ) + +/datum/species/bug/type_big/equip_survival_gear(var/mob/living/carbon/human/H) + return + +/datum/species/bug/type_big/handle_post_spawn(var/mob/living/carbon/human/H) + H.mutations.Add(HULK) + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm index 48fb751fd3f..a9ea378dd0c 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm @@ -37,6 +37,8 @@ sprint_speed_factor = 2 sprint_cost_factor = 0.80 stamina_recovery = 5 + natural_climbing = 1 + climb_coeff = 0.1 virus_immune = 1 diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index c58516a39ba..a5a83472e6a 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -279,7 +279,7 @@ There are several things that need to be remembered: else base_icon.Blend(temp, ICON_OVERLAY) - if(!skeleton) + if(!(species.flags & NO_SCAN)) if(husk) base_icon.ColorTone(husk_color_mod) else if(hulk) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f210134ece9..d70c5c73fe7 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -250,6 +250,7 @@ default behaviour is: /mob/living/proc/adjustBruteLoss(var/amount) if(status_flags & GODMODE) return 0 //godmode + amount *= brute_mod bruteloss = min(max(bruteloss + amount, 0),(maxHealth*2)) /mob/living/proc/getOxyLoss() @@ -279,6 +280,7 @@ default behaviour is: /mob/living/proc/adjustFireLoss(var/amount) if(status_flags & GODMODE) return 0 //godmode + amount *= burn_mod fireloss = min(max(fireloss + amount, 0),(maxHealth*2)) /mob/living/proc/getCloneLoss() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index e6d42a925ce..04705ddbe43 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -75,3 +75,6 @@ var/tesla_ignore = 0 // If true, mob is not affected by tesla bolts. var/stop_sight_update = 0 //If true, it won't reset the mob vision flags + + var/burn_mod = 1 + var/brute_mod = 1 diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 9231892922c..d6d78d09cac 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -98,7 +98,7 @@ proc/get_radio_key_from_channel(var/channel) /mob/living/proc/is_muzzled() return 0 -/mob/living/proc/handle_speech_problems(var/message, var/verb) +/mob/living/proc/handle_speech_problems(var/message, var/verb, var/message_mode) var/list/returns[3] var/speech_problem_flag = 0 if((HULK in mutations) && health >= 25 && length(message)) @@ -205,7 +205,7 @@ proc/get_radio_key_from_channel(var/channel) if(!(speaking && (speaking.flags & NO_STUTTER))) message = handle_autohiss(message, speaking) - var/list/handle_s = handle_speech_problems(message, verb) + var/list/handle_s = handle_speech_problems(message, verb, message_mode) message = handle_s[1] verb = handle_s[2] diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index f06429ad4f8..bbc5e037a7f 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -599,16 +599,34 @@ var/list/ai_verbs_default = list( if(alert("Would you like to select a hologram based on a crew member or switch to unique avatar?",,"Crew Member","Unique")=="Crew Member") var/personnel_list[] = list() + var/current_mobs = list() + for(var/mob/living/carbon/human/H in human_mob_list) + current_mobs[H.real_name] = H for(var/datum/data/record/t in data_core.locked)//Look in data core locked. personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image. + if(current_mobs[t.fields["name"]]) + personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = list("mob" = current_mobs[t.fields["name"]], "image" = t.fields["image"]) if(personnel_list.len) input = input("Select a crew member:") as null|anything in personnel_list - var/icon/character_icon = personnel_list[input] + var/selection = personnel_list[input] + var/icon/character_icon + if(selection && istype(selection, /list)) + var/mob/living/carbon/human/H = selection["mob"] + if (H.near_camera()) + character_icon = new('icons/mob/human.dmi', "blank") + character_icon.Insert(getHologramIcon(getFlatIcon(H, SOUTH)), dir = SOUTH) + character_icon.Insert(getHologramIcon(getFlatIcon(H, NORTH)), dir = NORTH) + character_icon.Insert(getHologramIcon(getFlatIcon(H, EAST)), dir = EAST) + character_icon.Insert(getHologramIcon(getFlatIcon(H, WEST)), dir = WEST) + else + character_icon = getHologramIcon(icon(selection["image"])) + if(selection && istype(selection, /icon)) + character_icon = getHologramIcon(icon(selection)) if(character_icon) - qdel(holo_icon)//Clear old icon so we're not storing it in memory. - holo_icon = getHologramIcon(icon(character_icon)) + qdel(holo_icon) // Clear old icon so we're not storing it in memory. + holo_icon = character_icon else alert("No suitable records found. Aborting.") diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index da7b8f53a00..32fc6d2057d 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -34,7 +34,6 @@ if(wrapped) qdel(wrapped) - wrapped = new/obj/item/broken_device wrapped.icon_state = brokenstate // Module-specific broken icons! Yay! @@ -42,6 +41,9 @@ uninstall() installed = -1 +/datum/robot_component/proc/get_damage(var/type) + return Clamp(brute_damage + electronics_damage,0,max_damage) + /datum/robot_component/proc/take_damage(brute, electronics, sharp, edge) if(installed != 1) return diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index c40a1d464da..bdde0372a76 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -212,3 +212,13 @@ custom_emote(m_type,message) return + +/mob/living/silicon/robot/verb/powerwarn() + set category = "Robot Commands" + set name = "Power Warning" + if(!is_component_functioning("power cell") || !cell || !cell.charge) + visible_message("The power warning light on [src] flashes urgently.",\ + "You announce you are operating in low power mode.") + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) + else + to_chat(src, "You can only use this emote when you're out of charge.") \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index fe7c41947c3..8b44277f249 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -10,14 +10,20 @@ var/amount = 0 for(var/V in components) var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.brute_damage + if(C.installed) + amount += Clamp(C.brute_damage,0,C.max_damage) + else if(C.installed == -1) + amount += C.max_damage/2 return amount /mob/living/silicon/robot/getFireLoss() var/amount = 0 for(var/V in components) var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.electronics_damage + if(C.installed) + amount += Clamp(C.electronics_damage,0,C.max_damage) + else if(C.installed == -1) + amount += C.max_damage/2 return amount /mob/living/silicon/robot/adjustBruteLoss(var/amount) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 6d2099adc5a..cf17eff896f 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -533,9 +533,7 @@ var/global/list/robot_modules = list( "Classic" = "secborg", "Spider" = "spidersec", "Heavy" = "heavysec" - ) - supported_upgrades = list(/obj/item/robot_parts/robot_component/jetpack) /obj/item/weapon/robot_module/security/general/New() ..() @@ -675,7 +673,10 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/form_printer(src) src.modules += new /obj/item/weapon/gripper/paperwork(src) src.modules += new /obj/item/weapon/hand_labeler(src) - src.emag = new /obj/item/weapon/stamp/denied(src) + src.modules += new /obj/item/weapon/tape_roll(src) //allows it to place flyers + src.modules += new /obj/item/weapon/stamp/denied(src) //why was this even a emagged item before smh + src.emag = new /obj/item/weapon/stamp/chameleon(src) + /obj/item/weapon/robot_module/general/butler/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) ..() diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 9b6c5a5dfd1..1effa9850d3 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -4,6 +4,27 @@ /mob/living/silicon/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) log_say("[key_name(src)] : [message]",ckey=key_name(src)) +/mob/living/silicon/robot/handle_speech_problems(var/message, var/verb, var/message_mode) + var/speech_problem_flag = 0 + //Handle gibberish when components are damaged + if(message_mode) + //If we have a radio message, just look at the damage of the radio + var/datum/robot_component/C = get_component("radio") + if(C.get_damage()) + speech_problem_flag = 1 + message=Gibberish(message,C.max_damage/C.get_damage()) + else + var/damaged = 100-(Clamp(health,0,maxHealth)/maxHealth)*100 + if(damaged > 40) + speech_problem_flag = 1 + message = Gibberish(message,damaged-10) + + var/list/returns[3] + returns[1] = message + returns[2] = verb + returns[3] = speech_problem_flag + return returns + /mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) ..() if(message_mode) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index f0a6ab3eb31..a605a236c84 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -60,12 +60,14 @@ /proc/isvaurca(A) if(istype(A, /mob/living/carbon/human)) switch(A:get_species()) - if ("Vaurca Worker") + if("Vaurca Worker") return 1 if("Vaurca Warrior") return 1 if("Vaurca Breeder") return 1 + if("Vaurca Warform") + return 1 if("V'krexi") return 1 return 0 diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index e52b3844cea..cce45bc08af 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -202,7 +202,7 @@ usr << "No log exists for this job: [t1]" return - access = jobdatum.get_access() + access = jobdatum.get_access(t1) remove_nt_access(id_card) apply_access(id_card, access) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 0860167bb3e..d2b58d88cd3 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -126,11 +126,81 @@ if(Allow_Spacemove()) return TRUE - if(Check_Shoegrip()) //scaling hull with magboots - for(var/turf/simulated/T in RANGE_TURFS(1,src)) - if(T.density) + for(var/turf/simulated/T in RANGE_TURFS(1,src)) + if(T.density) + if(Check_Shoegrip(FALSE)) return TRUE +/mob/living/carbon/human/proc/climb(var/direction, var/turf/source, var/climb_bonus) + var/turf/destination + if(direction == UP) + destination = GetAbove(source) + else + destination = GetBelow(source) + + if(!destination) + return + + if(stat || paralysis || stunned || weakened || lying || restrained() || buckled) + return + + if(destination.density) + return + + visible_message("The [src] begins to climb [(direction == UP) ? "upwards" : "downwards"].", + "You begin to climb [(direction == UP) ? "upwards" : "downwards"].") + var/climb_chance = 50 + var/climb_speed = 45 SECONDS + var/will_succeed = FALSE + var/turf/stack_turf = get_turf(src) //turf upon which obejcts must be stacked upon to gain vantage + var/speed_bonus = 0 + if(direction == DOWN) + stack_turf = destination + + if(species && !species.natural_climbing) + for(var/obj/O in stack_turf) + if(O.w_class >= 4.0 || O.anchored) //if an object is anchored it's stable footing + climb_chance = min(100, climb_chance + O.w_class) //large items increase your reach + speed_bonus = min(15, speed_bonus + 1) + else + climb_chance = max(0, climb_chance - O.w_class) //small items destabilize your footing + speed_bonus = max(0, speed_bonus - 1) + if(climb_bonus) + climb_chance = min(100, climb_chance + climb_bonus) + else + climb_chance = 100 + + if(species && species.climb_coeff) + climb_speed = round(max(1, (species.climb_coeff * climb_speed) - speed_bonus), 1) + + if(prob(climb_chance)) + will_succeed = TRUE + + if(do_after(src, climb_speed, extra_checks = CALLBACK(src, .proc/climb_check, will_succeed, climb_chance, climb_speed, direction, destination))) + if(will_succeed) + visible_message("\The [src] climbs [(direction == UP) ? "upwards" : "downwards"].", + "You climb [(direction == UP) ? "upwards" : "downwards"].") + forceMove(destination) + return + else + visible_message("\The [src] slips and falls as they climb [(direction == UP) ? "upwards" : "downwards"]!", + "You slip and fall as you climb [(direction == UP) ? "upwards" : "downwards"]!") + if(direction == DOWN) + Move(destination) + fall_impact(1, damage_mod = min(1, max(0.2, ((100-climb_chance)/100) - 0.2))) + +/mob/living/carbon/human/proc/climb_check(var/success, var/climb_chance, var/speed, var/direction, var/turf/destination) //purely for immersion and variety + if((last_special < world.time) && !success) //if you will succeed you can't fail + last_special = world.time + speed/10 + if(prob(100 - climb_chance)) //The worse you are the sooner you'll fail. + visible_message("\The [src] slips and falls as they climb [(direction == UP) ? "upwards" : "downwards"]!", + "You slip and fall as you climb [(direction == UP) ? "upwards" : "downwards"]!") + if(direction == DOWN) + Move(destination) + fall_impact(1, damage_mod = min(1, max(0.2, ((100-climb_chance)/100) - 0.2))) + return 0 + return 1 + /mob/living/silicon/robot/can_ztravel(var/direction) if(incapacitated() || is_dead()) return FALSE @@ -311,7 +381,7 @@ * @return TRUE if the proc ran completely. FALSE otherwise. Used to determine * if child procs should continue running or not, really. */ -/atom/movable/proc/fall_impact(levels_fallen, stopped_early = FALSE) +/atom/movable/proc/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) // No gravity, stop falling into spess! var/area/area = get_area(src) if (istype(loc, /turf/space) || (area && !area.has_gravity())) @@ -322,7 +392,7 @@ return TRUE // Mobs take damage if they fall! -/mob/living/fall_impact(levels_fallen, stopped_early = FALSE) +/mob/living/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) // No gravity, stop falling into spess! var/area/area = get_area(src) if (istype(loc, /turf/space) || (area && !area.has_gravity())) @@ -332,7 +402,7 @@ "With a loud thud, you land on \the [loc]!", "You hear a thud!") var/z_velocity = 5*(levels_fallen**2) - var/damage = ((60 + z_velocity) + rand(-20,20)) + var/damage = ((60 + z_velocity) + rand(-20,20)) * damage_mod apply_damage(damage, BRUTE) // The only piece of duplicate code. I was so close. Soooo close. :ree: @@ -351,7 +421,7 @@ return TRUE -/mob/living/carbon/human/fall_impact(levels_fallen, stopped_early = FALSE) +/mob/living/carbon/human/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) // No gravity, stop falling into spess! var/area/area = get_area(src) if (istype(loc, /turf/space) || (area && !area.has_gravity())) @@ -374,7 +444,7 @@ "You tuck into a roll as you hit \the [loc], minimizing damage!") var/z_velocity = 5*(levels_fallen**2) - var/damage = (((60 * species.fall_mod) + z_velocity) + rand(-20,20)) * combat_roll + var/damage = (((60 * species.fall_mod) + z_velocity) + rand(-20,20)) * combat_roll * damage_mod var/limb_damage = rand(0,damage/2) if(prob(30) && combat_roll >= 1) //landed on their head @@ -463,25 +533,25 @@ /mob/living/carbon/human/bst/fall_impact() return FALSE -/obj/mecha/fall_impact(levels_fallen, stopped_early = FALSE) +/obj/mecha/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) . = ..() if (!.) return var/z_velocity = 5*(levels_fallen**2) - var/damage = ((60 + z_velocity) + rand(-20,20)) + var/damage = ((60 + z_velocity) + rand(-20,20)) * damage_mod take_damage(damage) playsound(loc, "sound/effects/bang.ogg", 100, 1) playsound(loc, "sound/effects/bamf.ogg", 100, 1) -/obj/vehicle/fall_impact(levels_fallen, stopped_early = FALSE) +/obj/vehicle/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) . = ..() if (!.) return var/z_velocity = 5*(levels_fallen**2) - var/damage = ((60 + z_velocity) + rand(-20,20)) + var/damage = ((60 + z_velocity) + rand(-20,20)) * damage_mod health -= (damage * brute_dam_coeff) playsound(loc, "sound/effects/clang.ogg", 75, 1) diff --git a/code/modules/multiz/turfs/open_space.dm b/code/modules/multiz/turfs/open_space.dm index 7ba6c4ce5c8..c9c18ff20bd 100644 --- a/code/modules/multiz/turfs/open_space.dm +++ b/code/modules/multiz/turfs/open_space.dm @@ -206,6 +206,18 @@ return return +/turf/simulated/open/attack_hand(var/mob/user) + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/turf/climbing_wall = GetBelow(H) + var/climb_bonus = 0 + if(istype(climbing_wall, /turf/simulated/mineral)) + climb_bonus = 20 + else + climb_bonus = 0 + H.climb(DOWN, src, climb_bonus) + //Most things use is_plating to test if there is a cover tile on top (like regular floors) /turf/simulated/open/is_plating() return TRUE diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 7ddfe9f0d75..d99cc47f39d 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -63,7 +63,7 @@ var/icon/eyes_icon = SSicon_cache.human_eye_cache[cache_key] if (!eyes_icon) - eyes_icon = new/icon('icons/mob/human_face/eyes.dmi', species.eyes) + eyes_icon = new/icon(species.eyes_icons, species.eyes) if(eyecolor) eyes_icon.Blend(eyecolor, species.eyes_icon_blend) else diff --git a/code/modules/organs/subtypes/vaurca.dm b/code/modules/organs/subtypes/vaurca.dm index 600809d4ddc..d9008703e3f 100644 --- a/code/modules/organs/subtypes/vaurca.dm +++ b/code/modules/organs/subtypes/vaurca.dm @@ -1,21 +1,59 @@ /obj/item/organ/heart/left name = "heart" - icon_state = "heart-on" + icon_state = "vaurca_heart_l-on" organ_tag = "left heart" parent_organ = "chest" - dead_icon = "heart-off" + dead_icon = "vaurca_heart_l-off" /obj/item/organ/heart/right name = "heart" - icon_state = "heart-on" + icon_state = "vaurca_heart_r-on" organ_tag = "right heart" parent_organ = "chest" - dead_icon = "heart-off" + dead_icon = "vaurca_heart_r-off" + +/obj/item/organ/lungs/vaurca + icon_state = "lungs_vaurca" + +/obj/item/organ/kidneys/vaurca + icon_state = "kidney_vaurca" + +/obj/item/organ/eyes/vaurca + icon_state = "eyes_vaurca" + +/obj/item/organ/kidneys/vaurca/robo + icon_state = "kidney_vaurca" + organ_tag = "mechanical kidneys" + robotic = 2 + +/obj/item/organ/liver/vaurca/robo + icon_state = "liver_vaurca" + organ_tag = "mechanical liver" + robotic = 2 + +/obj/item/organ/liver/vaurca + icon_state = "liver_vaurca" + +/obj/item/organ/brain/vaurca + icon_state = "brain_vaurca" + +/obj/item/organ/vaurca/reservoir + name = "phoron reservoir" + organ_tag = "phoron reservoir" + parent_organ = "chest" + icon_state = "phoron_reservoir" + robotic = 1 + +/obj/item/organ/vaurca/filtrationbit + name = "filtration bit" + organ_tag = "filtration bit" + parent_organ = "head" + icon_state = "filter" + robotic = 2 /obj/item/organ/vaurca/neuralsocket name = "neural socket" organ_tag = "neural socket" - icon = 'icons/mob/alien.dmi' icon_state = "neural_socket" parent_organ = "head" robotic = 2 @@ -43,19 +81,10 @@ obj/item/organ/vaurca/neuralsocket/process() target << "Your mind suddenly grows dark as the unity of the Hive is torn from you." ..() -/obj/item/organ/vaurca/filtrationbit - name = "filtration bit" - organ_tag = "filtration bit" - parent_organ = "head" - icon = 'icons/mob/alien.dmi' - icon_state = "filter" - robotic = 2 - /obj/item/organ/vaurca/preserve name = "phoron reserve tank" organ_tag = "phoron reserve tank" parent_organ = "chest" - icon = 'icons/mob/alien.dmi' icon_state = "breathing_app" robotic = 1 var/datum/gas_mixture/air_contents = null diff --git a/code/modules/projectiles/guns/energy/magic.dm b/code/modules/projectiles/guns/energy/magic.dm index 4c537d1a6c5..f49943652aa 100644 --- a/code/modules/projectiles/guns/energy/magic.dm +++ b/code/modules/projectiles/guns/energy/magic.dm @@ -45,8 +45,8 @@ obj/item/weapon/gun/energy/staff/special_check(var/mob/living/user) LL.droplimb(0,DROPLIMB_BLUNT) RL.droplimb(0,DROPLIMB_BLUNT) playsound(user, 'sound/effects/splat.ogg', 50, 1) - user.visible_message(" With a sickening series of crunches, [user]'s body shrinks, and they begin to sprout feathers!") - user.visible_message("[user] screams!",2) + user.visible_message("With a sickening series of crunches, [user]'s body shrinks, and they begin to sprout feathers!") + user.visible_message("[user] screams!") new_mob = new /mob/living/simple_animal/parrot(H.loc) new_mob.universal_speak = 1 new_mob.key = H.key @@ -84,7 +84,7 @@ obj/item/weapon/gun/energy/staff/animate/special_check(var/mob/living/user) var/active_hand = H.hand playsound(user, 'sound/effects/blobattack.ogg', 40, 1) user.visible_message(" With a sickening crunch, [user]'s hand rips itself off, and begins crawling away!") - user.visible_message("[user] screams!",2) + user.visible_message("[user] screams!") user.drop_item() if(active_hand) LA.droplimb(0,DROPLIMB_EDGE) diff --git a/code/modules/projectiles/guns/energy/rifle.dm b/code/modules/projectiles/guns/energy/rifle.dm index 241fffca86a..d6eee530446 100644 --- a/code/modules/projectiles/guns/energy/rifle.dm +++ b/code/modules/projectiles/guns/energy/rifle.dm @@ -101,7 +101,7 @@ can_switch_modes = 0 turret_sprite_set = "xray" turret_is_lethal = 1 - + /obj/item/weapon/gun/energy/rifle/pulse name = "pulse rifle" desc = "A weapon that uses advanced pulse-based beam generation technology to emit powerful laser blasts. Because of its complexity and cost, it is rarely seen in use except by specialists." @@ -116,7 +116,7 @@ can_switch_modes = 0 turret_sprite_set = "pulse" turret_is_lethal = 1 - + modifystate = null firemodes = list( @@ -138,3 +138,30 @@ /obj/item/weapon/gun/energy/rifle/pulse/destroyer/attack_self(mob/living/user as mob) user << "[src.name] has three settings, and they are all DESTROY." + +/obj/item/weapon/gun/energy/rifle/laser/tachyon + name = "tachyon rifle" + desc = "A Vaurcan rifle that fires a beam of concentrated faster than light particles, capable of passing through most forms of matter." + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "tachyonrifle" + item_state = "tachyonrifle" + fire_sound = 'sound/weapons/laser3.ogg' + projectile_type = /obj/item/projectile/beam/tachyon + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_ILLEGAL = 2) + secondary_projectile_type = null + secondary_fire_sound = null + can_switch_modes = 0 + can_turret = 0 + zoomdevicename = "rifle scope" + var/obj/screen/overlay = null + +/obj/item/weapon/gun/energy/rifle/laser/tachyon/verb/scope() + set category = "Object" + set name = "Use Rifle Scope" + set popup_menu = 1 + + if(wielded) + toggle_scope(2.0, usr) + else + usr << "You can't look through the scope without stabilizing the rifle!" \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 29d933968ac..c4aae03c773 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -343,8 +343,14 @@ toggle_wield(usr) -/obj/item/weapon/gun/energy/vaurca/typec/attack(atom/A, mob/living/user, def_zone) - return ..() //Pistolwhippin' +/obj/item/weapon/gun/energy/vaurca/typec/attack(mob/living/carbon/human/M as mob, mob/living/carbon/user as mob) + user.setClickCooldown(16) + ..() + +/obj/item/weapon/gun/energy/vaurca/typec/pre_attack(var/mob/living/target, var/mob/living/user) + if(istype(target)) + cleave(user, target) + ..() /obj/item/weapon/gun/energy/vaurca/typec/special_check(var/mob/user) if(is_charging) @@ -371,14 +377,13 @@ /obj/item/weapon/gun/energy/vaurca/typec/attack_hand(mob/user as mob) if(loc != user) var/mob/living/carbon/human/H = user - if(istype(H)) - if(H.species.name == "Vaurca Breeder") - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - anchored = 1 - user << "\The [src] is now energised." - icon_state = "megaglaive1" - ..() - return + if(H.mob_size >= 30) + playsound(user, 'sound/weapons/saberon.ogg', 50, 1) + anchored = 1 + user << "\The [src] is now energised." + icon_state = "megaglaive1" + ..() + return user << "\The [src] is far too large for you to pick up." return @@ -419,8 +424,8 @@ firemodes = list( list(mode_name="2 second burst", burst=10, burst_delay = 1, fire_delay = 20), - list(mode_name="4 second burst", burst=20, burst_delay = 1, fire_delay = 40, dispersion = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), - list(mode_name="6 second burst", burst=30, burst_delay = 1, fire_delay = 60, dispersion = list(0, 1.5, 3, 4.5, 6, 7.5, 9, 10.5, 12, 13.5, 15, 16.5, 18, 19.5, 21)) + list(mode_name="4 second burst", burst=20, burst_delay = 1, fire_delay = 40), + list(mode_name="6 second burst", burst=30, burst_delay = 1, fire_delay = 60) ) action_button_name = "Wield thermal drill" @@ -485,7 +490,6 @@ charge_meter = 1 use_external_power = 1 charge_cost = 25 - dispersion = list(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30) /obj/item/weapon/gun/energy/vaurca/mountedthermaldrill/special_check(var/mob/user) if(is_charging) @@ -504,23 +508,19 @@ return ..() -/*/obj/item/weapon/gun/energy/vaurca/flamer - name = "Vaurcae Incinerator" - desc = "A devious flamethrower device that procedurally converts atmosphere to fuel for a virtually unlimited tank." - icon_state = "incinerator" - item_state = "incinerator" - fire_sound = 'sound/effects/extinguish.ogg' - charge_meter = 0 - slot_flags = SLOT_BACK - w_class = 3 - force = 10 - projectile_type = /obj/item/projectile/energy/flamer - self_recharge = 1 - recharge_time = 2 - max_shots = 80 - firemodes = list( - list(mode_name="spray", burst = 20, burst_delay = -1, fire_delay = 10, dispersion = list(0.5, 0.5, 1.0, 1.0, 1.5, 1.5, 2.0, 2.0, 2.5, 2.5, 3.0, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.0, 6.0)), - )*/ +/obj/item/weapon/gun/energy/vaurca/tachyon + name = "tachyon carbine" + desc = "A Vaurcan carbine that fires a beam of concentrated faster than light particles, capable of passing through most forms of matter." + contained_sprite = 1 + icon = 'icons/obj/vaurca_items.dmi' + icon_state = "tachyoncarbine" + item_state = "tachyoncarbine" + fire_sound = 'sound/weapons/laser3.ogg' + projectile_type = /obj/item/projectile/beam/tachyon + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2, TECH_ILLEGAL = 2) + max_shots = 10 + fire_delay = 1 + can_turret = 0 /obj/item/weapon/gun/energy/tesla name = "tesla gun" diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index f159ad8f08e..37ce08544f6 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -45,7 +45,7 @@ allowed_magazines = list(/obj/item/ammo_magazine/c45m) caliber = ".45" origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2) - fire_sound = 'sound/weapons/Gunshot_light.ogg' + fire_sound = 'sound/weapons/gunshot_pistol.ogg' load_method = MAGAZINE /obj/item/weapon/gun/projectile/sec/update_icon() @@ -187,7 +187,7 @@ caliber = "9mm" silenced = 0 origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ILLEGAL = 2) - fire_sound = 'sound/weapons/Gunshot_light.ogg' + fire_sound = 'sound/weapons/gunshot_pistol.ogg' load_method = MAGAZINE magazine_type = /obj/item/ammo_magazine/mc9mm allowed_magazines = list(/obj/item/ammo_magazine/mc9mm) diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index ad7a7fc8065..bb5059c7fbc 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -50,7 +50,7 @@ max_shells = 6 caliber = "38" origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2) - fire_sound = 'sound/weapons/Gunshot_light.ogg' + fire_sound = 'sound/weapons/gunshot_strong.ogg' ammo_type = /obj/item/ammo_casing/c38 /obj/item/weapon/gun/projectile/revolver/detective/verb/rename_gun() @@ -79,7 +79,7 @@ icon_state = "deckard-empty" caliber = "38" ammo_type = /obj/item/ammo_casing/c38 - fire_sound = 'sound/weapons/Gunshot_light.ogg' + fire_sound = 'sound/weapons/gunshot_strong.ogg' /obj/item/weapon/gun/projectile/revolver/deckard/update_icon() ..() @@ -138,7 +138,7 @@ handle_casings = CYCLE_CASINGS max_shells = 6 caliber = "38" - fire_sound = 'sound/weapons/Gunshot_light.ogg' + fire_sound = 'sound/weapons/gunshot_strong.ogg' ammo_type = /obj/item/ammo_casing/c38 var/secondary_max_shells = 1 var/secondary_caliber = "shotgun" diff --git a/code/modules/projectiles/guns/projectile/rifle.dm b/code/modules/projectiles/guns/projectile/rifle.dm index 27596574c22..c078b87d37f 100644 --- a/code/modules/projectiles/guns/projectile/rifle.dm +++ b/code/modules/projectiles/guns/projectile/rifle.dm @@ -60,7 +60,7 @@ ammo_type = /obj/item/ammo_casing/a556 slot_flags = SLOT_BELT|SLOT_HOLSTER load_method = SINGLE_CASING - fire_sound = 'sound/weapons/rifleshot.ogg' + fire_sound = 'sound/weapons/gunshot3.ogg' var/retracted_bolt = 0 var/icon_retracted = "pockrifle-empty" diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 6687a8f5156..1b08c427ab5 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -339,3 +339,18 @@ /obj/item/projectile/beam/energy_net/proc/do_net(var/mob/M) var/obj/item/weapon/energy_net/net = new (get_turf(M)) net.throw_impact(M) + +/obj/item/projectile/beam/tachyon + name = "particle beam" + icon_state = "xray" + damage = 25 + armor_penetration = 65 + penetrating = 1 + maiming = 1 + maim_rate = 5 + clean_cut = 1 + maim_type = DROPLIMB_BURN + + muzzle_type = /obj/effect/projectile/muzzle/tachyon + tracer_type = /obj/effect/projectile/tracer/tachyon + impact_type = /obj/effect/projectile/impact/tachyon \ No newline at end of file diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index c9f5a13345a..654cae4b8a7 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -67,6 +67,13 @@ B.blood_DNA["UNKNOWN DNA STRUCTURE"] = "X*" /datum/reagent/blood/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) + if(ishuman(M)) + if (M.mind && M.mind.vampire) + if(M.dna.unique_enzymes == data["blood_DNA"]) //so vampires can't drink their own blood + return + M.mind.vampire.blood_usable += removed + M<< "You have accumulated [M.mind.vampire.blood_usable] [M.mind.vampire.blood_usable > 1 ? "units" : "unit"] of usable blood. It tastes quite stale." + return if(dose > 5) M.adjustToxLoss(removed) if(dose > 15) @@ -227,7 +234,7 @@ /datum/reagent/fuel name = "Welding fuel" id = "fuel" - description = "Required for welders. Flamable." + description = "Required for welders. Flammable." reagent_state = LIQUID color = "#660000" touch_met = 5 @@ -249,3 +256,27 @@ if(istype(L)) L.adjust_fire_stacks(amount / 10) // Splashing people with welding fuel to make them easy to ignite! +/datum/reagent/fuel/napalm + name = "Zo'rane Fire" + id = "greekfire" + description = "A highly flammable and cohesive gel once used commonly in the tunnels of Sedantis. Napalm sticks to kids." + reagent_state = LIQUID + color = "#D35908" + touch_met = 50 + taste_description = "fiery death" + +/datum/reagent/fuel/napalm/touch_turf(var/turf/T) + new /obj/effect/decal/cleanable/liquid_fuel/napalm(T, volume/3) + for(var/mob/living/L in T) + L.adjust_fire_stacks(volume / 10) + L.add_modifier(/datum/modifier/napalm, MODIFIER_CUSTOM, _strength = 2) + remove_self(volume) + return + +/datum/reagent/fuel/touch_mob(var/mob/living/L, var/amount) + if(istype(L)) + L.adjust_fire_stacks(amount / 10) // Splashing people with welding fuel to make them easy to ignite! + new /obj/effect/decal/cleanable/liquid_fuel/napalm(get_turf(L), amount/3) + L.adjustFireLoss(amount / 10) + remove_self(volume) + L.add_modifier(/datum/modifier/napalm, MODIFIER_CUSTOM, _strength = 2) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index 09d907bea29..c76f5efab51 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -205,9 +205,6 @@ taste_description = "meat" /datum/reagent/nutriment/protein/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) - if(alien && alien == IS_SKRELL) - M.adjustToxLoss(0.5 * removed) - return if(alien && alien == IS_UNATHI) digest(M,removed) return @@ -219,30 +216,12 @@ color = "#fdffa8" taste_description = "tofu" -/datum/reagent/nutriment/protein/tofu/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) - if(alien && alien == IS_SKRELL) - digest(M,removed) //Skrell are allowed to eat tofu, but not most animal proteins - return - ..() - /datum/reagent/nutriment/protein/seafood // Good for Skrell! name = "seafood protein" id = "seafood" color = "#f5f4e9" taste_description = "fish" -/datum/reagent/nutriment/protein/seafood/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) - if(alien && alien == IS_SKRELL) - digest(M,removed)//Skrell are allowed to eat fish, but not other proteins - return - ..() - -/datum/reagent/nutriment/protein/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - if(alien && alien == IS_SKRELL) - M.adjustToxLoss(2 * removed) - return - ..() - /datum/reagent/nutriment/protein/egg // Also bad for skrell. name = "egg yolk" id = "egg" @@ -250,9 +229,6 @@ taste_description = "egg" /datum/reagent/nutriment/egg/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) - if(alien && alien == IS_SKRELL) - M.adjustToxLoss(0.5 * removed) - return if(alien && alien == IS_UNATHI) digest(M,removed) return @@ -647,8 +623,8 @@ message = "Your face and throat burn!" if(prob(25)) M.custom_emote(2, "[pick("coughs!","coughs hysterically!","splutters!")]") - M.Stun(5) - M.Weaken(5) + M.apply_effect(40, AGONY, 0) + #undef EYES_PROTECTED #undef EYES_MECH diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index ab5073d7dad..e16c8299f74 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -27,7 +27,7 @@ /datum/reagent/bicaridine name = "Bicaridine" id = "bicaridine" - description = "Bicaridine is an analgesic medication and can be used to treat blunt trauma. When inhaled, it treats minor damage to the lungs." + description = "Bicaridine is an analgesic medication and can be used to treat blunt trauma." reagent_state = LIQUID color = "#BF0000" overdose = REAGENTS_OVERDOSE @@ -42,13 +42,6 @@ /datum/reagent/bicaridine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) M.heal_organ_damage(5 * removed, 0) -/datum/reagent/bicaridine/affect_breathe(var/mob/living/carbon/human/H, var/alien, var/removed) - . = ..() - if(istype(H)) - var/obj/item/organ/L = H.internal_organs_by_name["lungs"] - if(istype(L) && !L.robotic && !L.is_broken()) - L.take_damage(-1*removed) //Every 10 units heals 1 lung damage. - /datum/reagent/bicaridine/overdose(var/mob/living/carbon/M, var/alien) ..()//Bicard overdose heals internal wounds if(ishuman(M)) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 7afd4840a9a..25ae0b2c52a 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -123,11 +123,6 @@ conflicting_reagent = /datum/reagent/toxin/phoron strength = 1 -/datum/reagent/toxin/cardox/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) - .. () - if(alien == IS_VAURCA) - affect_blood(M, alien, removed * 0.25) - /datum/reagent/toxin/cardox/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_VAURCA) M.adjustToxLoss(removed * strength*2) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 583ceadd10b..0cfb0b41f14 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -20,7 +20,7 @@ // reagents.add_reagent("tricordrazine", 30) // return -/obj/item/weapon/reagent_containers/hypospray/attack(mob/living/M as mob, mob/user as mob) +/obj/item/weapon/reagent_containers/hypospray/attack(mob/living/M as mob, mob/user as mob, var/target_zone) if(!reagents.total_volume) user << "[src] is empty." return @@ -29,7 +29,7 @@ var/mob/living/carbon/human/H = M if(istype(H)) - var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) + var/obj/item/organ/external/affected = H.get_organ(target_zone) if(!affected) user << "\The [H] is missing that limb!" return @@ -37,9 +37,13 @@ user << "You cannot inject a robotic limb." return + user.visible_message("[user] is trying to inject [M] with [src]!","You are trying to inject [M] with [src].") + if(H.run_armor_check(target_zone,"melee",0,"Your armor slows down the injection!","Your armor slows down the injection!")) + if(!do_mob(user, M, 60)) + return + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user.do_attack_animation(M) - user << "You inject [M] with [src]." M << "You feel a tiny prick!" playsound(src, 'sound/items/hypospray.ogg',25) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 8e595221256..649b08192a7 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -25,268 +25,266 @@ var/visible_name = "a syringe" var/time = 30 - on_reagent_change() - update_icon() - - pickup(mob/user) - ..() - update_icon() - - dropped(mob/user) - ..() - update_icon() - - attack_self(mob/user as mob) - - switch(mode) - if(SYRINGE_DRAW) - mode = SYRINGE_INJECT - if(SYRINGE_INJECT) - mode = SYRINGE_DRAW - if(SYRINGE_BROKEN) - return - update_icon() - - attack_hand() - ..() - update_icon() - - attackby(obj/item/I as obj, mob/user as mob) - return - - afterattack(obj/target, mob/user, proximity) - if(!proximity || !target.reagents) - return - - if(mode == SYRINGE_BROKEN) - user << "This syringe is broken!" - return - - if(user.a_intent == I_HURT && ismob(target)) - if((CLUMSY in user.mutations) && prob(50)) - target = user - syringestab(target, user) - return - - - switch(mode) - if(SYRINGE_DRAW) - - if(!reagents.get_free_space()) - user << "The syringe is full." - mode = SYRINGE_INJECT - return - - if(ismob(target))//Blood! - if(reagents.has_reagent("blood")) - user << "There is already a blood sample in this syringe." - return - if(istype(target, /mob/living/carbon)) - if(istype(target, /mob/living/carbon/slime)) - user << "You are unable to locate any blood." - return - var/amount = reagents.get_free_space() - var/mob/living/carbon/T = target - if(!T.dna) - user << "You are unable to locate any blood. (To be specific, your target seems to be missing their DNA datum)." - return - if(NOCLONE in T.mutations) //target done been et, no more blood in him - user << "You are unable to locate any blood." - return - - var/datum/reagent/B - if(istype(T, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = T - if(H.species && H.species.flags & NO_BLOOD) - H.reagents.trans_to_obj(src, amount) - else - B = T.take_blood(src, amount) - else - B = T.take_blood(src,amount) - - if (B) - reagents.reagent_list += B - reagents.update_total() - on_reagent_change() - reagents.handle_reactions() - user << "You take a blood sample from [target]." - for(var/mob/O in viewers(4, user)) - O.show_message("[user] takes a blood sample from [target].", 1) - - else //if not mob - if(!target.reagents.total_volume) - user << "[target] is empty." - return - - if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers) && !istype(target, /obj/item/slime_extract)) - user << "You cannot directly remove reagents from this object." - return - - var/trans = target.reagents.trans_to_obj(src, amount_per_transfer_from_this) - user << "You fill the syringe with [trans] units of the solution." - update_icon() - - if(!reagents.get_free_space()) - mode = SYRINGE_INJECT - update_icon() - - if(SYRINGE_INJECT) - if(!reagents.total_volume) - user << "The syringe is empty." - mode = SYRINGE_DRAW - return - if(istype(target, /obj/item/weapon/implantcase/chem)) - return - - if(!target.is_open_container() && !ismob(target) && !istype(target, /obj/item/weapon/reagent_containers/food) && !istype(target, /obj/item/slime_extract) && !istype(target, /obj/item/clothing/mask/smokable/cigarette) && !istype(target, /obj/item/weapon/storage/fancy/cigarettes)) - user << "You cannot directly fill this object." - return - if(!target.reagents.get_free_space()) - user << "[target] is full." - return - - var/mob/living/carbon/human/H = target - if(istype(H)) - var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) - if(!affected) - user << "\The [H] is missing that limb!" - return - else if(affected.status & ORGAN_ROBOT) - user << "You cannot inject a robotic limb." - return - - if(ismob(target) && target != user) - - var/injtime = time //Injecting through a voidsuit takes longer due to needing to find a port. - - if(istype(H)) - if(H.wear_suit) - if(istype(H.wear_suit, /obj/item/clothing/suit/space)) - injtime = injtime * 2 - else if(!H.can_inject(user, 1)) - return - if(isvaurca(H)) - injtime = injtime * 2 - - else if(isliving(target)) - - var/mob/living/M = target - if(!M.can_inject(user, 1)) - return - - if(injtime == time) - user.visible_message("[user] is trying to inject [target] with [visible_name]!") - else - if(isvaurca(H)) - user.visible_message("[user] begins hunting for an injection port on [target]'s carapace!") - else - user.visible_message("[user] begins hunting for an injection port on [target]'s suit!") - - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) - user.do_attack_animation(target) - - if(!do_mob(user, target, injtime)) - return - - user.visible_message("[user] injects [target] with the syringe!") - - var/trans - if(ismob(target)) - var/contained = reagentlist() - trans = reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_BLOOD) - admin_inject_log(user, target, src, contained, trans) - else - trans = reagents.trans_to(target, amount_per_transfer_from_this) - user << "You inject [trans] units of the solution. The syringe now contains [src.reagents.total_volume] units." - if (reagents.total_volume <= 0 && mode == SYRINGE_INJECT) - mode = SYRINGE_DRAW - update_icon() - - return - +/obj/item/weapon/reagent_containers/syringe/on_reagent_change() update_icon() - cut_overlays() - if(mode == SYRINGE_BROKEN) - icon_state = "broken" +/obj/item/weapon/reagent_containers/syringe/pickup(mob/user) + ..() + update_icon() + +/obj/item/weapon/reagent_containers/syringe/dropped(mob/user) + ..() + update_icon() + +/obj/item/weapon/reagent_containers/syringe/attack_self(mob/user as mob) + switch(mode) + if(SYRINGE_DRAW) + mode = SYRINGE_INJECT + if(SYRINGE_INJECT) + mode = SYRINGE_DRAW + if(SYRINGE_BROKEN) return + update_icon() - var/rounded_vol = round(reagents.total_volume, round(reagents.maximum_volume / 3)) - if(ismob(loc)) - var/injoverlay - switch(mode) - if (SYRINGE_DRAW) - injoverlay = "draw" - if (SYRINGE_INJECT) - injoverlay = "inject" - add_overlay(injoverlay) - icon_state = "[rounded_vol]" - item_state = "syringe_[rounded_vol]" +/obj/item/weapon/reagent_containers/syringe/attack_hand() + ..() + update_icon() - if(reagents.total_volume) - filling = image('icons/obj/reagentfillings.dmi', src, "syringe10") +/obj/item/weapon/reagent_containers/syringe/attackby(obj/item/I as obj, mob/user as mob) + return - filling.icon_state = "syringe[rounded_vol]" +/obj/item/weapon/reagent_containers/syringe/afterattack(obj/target, mob/user, proximity) + if(!proximity || !target.reagents) + return - filling.color = reagents.get_color() - add_overlay(filling) + if(mode == SYRINGE_BROKEN) + user << "This syringe is broken!" + return - proc/syringestab(mob/living/carbon/target as mob, mob/living/carbon/user as mob) + if(user.a_intent == I_HURT && ismob(target)) + if((CLUMSY in user.mutations) && prob(50)) + target = user + syringestab(target, user) + return - if(istype(target, /mob/living/carbon/human)) + + switch(mode) + if(SYRINGE_DRAW) + + if(!reagents.get_free_space()) + user << "The syringe is full." + mode = SYRINGE_INJECT + return + + if(ismob(target))//Blood! + if(reagents.has_reagent("blood")) + user << "There is already a blood sample in this syringe." + return + if(istype(target, /mob/living/carbon)) + if(istype(target, /mob/living/carbon/slime)) + user << "You are unable to locate any blood." + return + var/amount = reagents.get_free_space() + var/mob/living/carbon/T = target + if(!T.dna) + user << "You are unable to locate any blood. (To be specific, your target seems to be missing their DNA datum)." + return + if(NOCLONE in T.mutations) //target done been et, no more blood in him + user << "You are unable to locate any blood." + return + + var/datum/reagent/B + if(istype(T, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = T + if(H.species && H.species.flags & NO_BLOOD) + H.reagents.trans_to_obj(src, amount) + else + B = T.take_blood(src, amount) + else + B = T.take_blood(src,amount) + + if (B) + reagents.reagent_list += B + reagents.update_total() + on_reagent_change() + reagents.handle_reactions() + user << "You take a blood sample from [target]." + for(var/mob/O in viewers(4, user)) + O.show_message("[user] takes a blood sample from [target].", 1) + + else //if not mob + if(!target.reagents.total_volume) + user << "[target] is empty." + return + + if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers) && !istype(target, /obj/item/slime_extract)) + user << "You cannot directly remove reagents from this object." + return + + var/trans = target.reagents.trans_to_obj(src, amount_per_transfer_from_this) + user << "You fill the syringe with [trans] units of the solution." + update_icon() + + if(!reagents.get_free_space()) + mode = SYRINGE_INJECT + update_icon() + + if(SYRINGE_INJECT) + if(!reagents.total_volume) + user << "The syringe is empty." + mode = SYRINGE_DRAW + return + if(istype(target, /obj/item/weapon/implantcase/chem)) + return + + if(!target.is_open_container() && !ismob(target) && !istype(target, /obj/item/weapon/reagent_containers/food) && !istype(target, /obj/item/slime_extract) && !istype(target, /obj/item/clothing/mask/smokable/cigarette) && !istype(target, /obj/item/weapon/storage/fancy/cigarettes)) + user << "You cannot directly fill this object." + return + if(!target.reagents.get_free_space()) + user << "[target] is full." + return var/mob/living/carbon/human/H = target + if(istype(H)) + var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) + if(!affected) + user << "\The [H] is missing that limb!" + return + else if(affected.status & ORGAN_ROBOT) + user << "You cannot inject a robotic limb." + return - var/target_zone = ran_zone(check_zone(user.zone_sel.selecting, target)) - var/obj/item/organ/external/affecting = H.get_organ(target_zone) + if(ismob(target) && target != user) - if (!affecting || affecting.is_stump()) - user << "They are missing that limb!" - return + var/injtime = time //Injecting through a voidsuit takes longer due to needing to find a port. - var/hit_area = affecting.name + if(istype(H)) + if(H.wear_suit) + if(istype(H.wear_suit, /obj/item/clothing/suit/space)) + injtime = injtime * 2 + else if(!H.can_inject(user, 1)) + return + if(isvaurca(H)) + injtime = injtime * 2 - if((user != target) && H.check_shields(7, src, user, "\the [src]")) - return + else if(isliving(target)) - if (target != user && H.getarmor(target_zone, "melee") > 5 && prob(50)) - for(var/mob/O in viewers(world.view, user)) - O.show_message(text("[user] tries to stab [target] in \the [hit_area] with [src.name], but the attack is deflected by armor!"), 1) - user.remove_from_mob(src) - qdel(src) + var/mob/living/M = target + if(!M.can_inject(user, 1)) + return - user.attack_log += "\[[time_stamp()]\] Attacked [target.name] ([target.ckey]) with \the [src] (INTENT: HARM)." - target.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: HARM)." - msg_admin_attack("[key_name_admin(user)] attacked [key_name_admin(target)] with [src.name] (INTENT: HARM) (JMP)",ckey=key_name(user),ckey_target=key_name(src)) + if(injtime == time) + user.visible_message("[user] is trying to inject [target] with [visible_name]!") + else + if(isvaurca(H)) + user.visible_message("[user] begins hunting for an injection port on [target]'s carapace!") + else + user.visible_message("[user] begins hunting for an injection port on [target]'s suit!") - return + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(target) - user.visible_message("[user] stabs [target] in \the [hit_area] with [src.name]!") + if(!do_mob(user, target, injtime)) + return - if(affecting.take_damage(3)) - H.UpdateDamageIcon() + user.visible_message("[user] injects [target] with the syringe!") - else - user.visible_message("[user] stabs [target] with [src.name]!") - target.take_organ_damage(3)// 7 is the same as crowbar punch + var/trans + if(ismob(target)) + var/contained = reagentlist() + trans = reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_BLOOD) + admin_inject_log(user, target, src, contained, trans) + else + trans = reagents.trans_to(target, amount_per_transfer_from_this) + user << "You inject [trans] units of the solution. The syringe now contains [src.reagents.total_volume] units." + if (reagents.total_volume <= 0 && mode == SYRINGE_INJECT) + mode = SYRINGE_DRAW + update_icon() + + return + +/obj/item/weapon/reagent_containers/syringe/update_icon() + cut_overlays() + + if(mode == SYRINGE_BROKEN) + icon_state = "broken" + return + + var/rounded_vol = round(reagents.total_volume, round(reagents.maximum_volume / 3)) + if(ismob(loc)) + var/injoverlay + switch(mode) + if (SYRINGE_DRAW) + injoverlay = "draw" + if (SYRINGE_INJECT) + injoverlay = "inject" + add_overlay(injoverlay) + icon_state = "[rounded_vol]" + item_state = "syringe_[rounded_vol]" + + if(reagents.total_volume) + filling = image('icons/obj/reagentfillings.dmi', src, "syringe10") + + filling.icon_state = "syringe[rounded_vol]" + + filling.color = reagents.get_color() + add_overlay(filling) + +/obj/item/weapon/reagent_containers/syringe/proc/syringestab(mob/living/carbon/target as mob, mob/living/carbon/user as mob) + if(istype(target, /mob/living/carbon/human)) + + var/mob/living/carbon/human/H = target + + var/target_zone = ran_zone(check_zone(user.zone_sel.selecting, target)) + var/obj/item/organ/external/affecting = H.get_organ(target_zone) + + if (!affecting || affecting.is_stump()) + user << "They are missing that limb!" + return + + var/hit_area = affecting.name + + if((user != target) && H.check_shields(7, src, user, "\the [src]")) + return + + if (target != user && H.getarmor(target_zone, "melee") > 5 && prob(50)) + for(var/mob/O in viewers(world.view, user)) + O.show_message(text("[user] tries to stab [target] in \the [hit_area] with [src.name], but the attack is deflected by armor!"), 1) + user.remove_from_mob(src) + qdel(src) + + user.attack_log += "\[[time_stamp()]\] Attacked [target.name] ([target.ckey]) with \the [src] (INTENT: HARM)." + target.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: HARM)." + msg_admin_attack("[key_name_admin(user)] attacked [key_name_admin(target)] with [src.name] (INTENT: HARM) (JMP)",ckey=key_name(user),ckey_target=key_name(src)) + + return + + user.visible_message("[user] stabs [target] in \the [hit_area] with [src.name]!") + + if(affecting.take_damage(3)) + H.UpdateDamageIcon() + + else + user.visible_message("[user] stabs [target] with [src.name]!") + target.take_organ_damage(3)// 7 is the same as crowbar punch - var/syringestab_amount_transferred = rand(0, (reagents.total_volume - 5)) //nerfed by popular demand - var/contained_reagents = reagents.get_reagents() - var/trans = reagents.trans_to_mob(target, syringestab_amount_transferred, CHEM_BLOOD) - if(isnull(trans)) trans = 0 - admin_inject_log(user, target, src, contained_reagents, trans, violent=1) - break_syringe(target, user) + var/syringestab_amount_transferred = rand(0, (reagents.total_volume - 5)) //nerfed by popular demand + var/contained_reagents = reagents.get_reagents() + var/trans = reagents.trans_to_mob(target, syringestab_amount_transferred, CHEM_BLOOD) + if(isnull(trans)) trans = 0 + admin_inject_log(user, target, src, contained_reagents, trans, violent=1) + break_syringe(target, user) - proc/break_syringe(mob/living/carbon/target, mob/living/carbon/user) - desc += " It is broken." - mode = SYRINGE_BROKEN - if(target) - add_blood(target) - if(user) - add_fingerprint(user) - update_icon() +/obj/item/weapon/reagent_containers/syringe/proc/break_syringe(mob/living/carbon/target, mob/living/carbon/user) + desc += " It is broken." + mode = SYRINGE_BROKEN + if(target) + add_blood(target) + if(user) + add_fingerprint(user) + update_icon() /obj/item/weapon/reagent_containers/syringe/ld50_syringe name = "Lethal Injection Syringe" diff --git a/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm index 5f90c20864d..5a00fa34890 100644 --- a/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm +++ b/code/modules/research/xenoarchaeology/machinery/geosample_scanner.dm @@ -236,16 +236,16 @@ //emergency stop if seal integrity reaches 0 if(scanner_seal_integrity <= 0 || (scanner_temperature >= 1273 && !rad_shield)) stop_scanning() - src.visible_message("\icon[src] buzzes unhappily. It has failed mid-scan!", 2) + visible_message("\icon[src] buzzes unhappily. It has failed mid-scan!", range = 2) if(prob(5)) - src.visible_message("\icon[src] [pick("whirrs","chuffs","clicks")][pick(" excitedly"," energetically"," busily")].", 2) + visible_message("\icon[src] [pick("whirrs","chuffs","clicks")][pick(" excitedly"," energetically"," busily")].", range = 2) else //gradually cool down over time if(scanner_temperature > 0) scanner_temperature = max(scanner_temperature - 5 - 10 * rand(), 0) if(prob(0.75)) - src.visible_message("\icon[src] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")].", 2) + visible_message("\icon[src] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")].", range = 2) last_process_worldtime = world.time /obj/machinery/radiocarbon_spectrometer/proc/stop_scanning() @@ -263,7 +263,7 @@ used_coolant = 0 /obj/machinery/radiocarbon_spectrometer/proc/complete_scan() - src.visible_message("\icon[src] makes an insistent chime.", 2) + visible_message("\icon[src] makes an insistent chime.", range = 2) if(scanned_item) //create report diff --git a/code/modules/tgs/core/_definitions.dm b/code/modules/tgs/core/_definitions.dm new file mode 100644 index 00000000000..ebf6d17c2a0 --- /dev/null +++ b/code/modules/tgs/core/_definitions.dm @@ -0,0 +1,2 @@ +#define TGS_UNIMPLEMENTED "___unimplemented" +#define TGS_VERSION_PARAMETER "server_service_version" diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm new file mode 100644 index 00000000000..d24bc2c9ae2 --- /dev/null +++ b/code/modules/tgs/core/core.dm @@ -0,0 +1,145 @@ +/world/TgsNew(datum/tgs_event_handler/event_handler) + var/tgs_version = world.params[TGS_VERSION_PARAMETER] + if(!tgs_version) + return + + var/path = SelectTgsApi(tgs_version) + if(!path) + TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.") + return + + TGS_INFO_LOG("Activating API for version [tgs_version]") + var/datum/tgs_api/new_api = new path + + var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default) + if(result && result != TGS_UNIMPLEMENTED) + TGS_WRITE_GLOBAL(tgs, new_api) + else + TGS_ERROR_LOG("Failed to activate API!") + +/world/proc/SelectTgsApi(tgs_version) + //remove the old 3.0 header + tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "") + + var/list/version_bits = splittext(tgs_version, ".") + + var/super = text2num(version_bits[1]) + var/major = text2num(version_bits[2]) + var/minor = text2num(version_bits[3]) + var/patch = text2num(version_bits[4]) + + switch(super) + if(3) + switch(major) + if(2) + return /datum/tgs_api/v3210 + + if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion()) + TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.") + return /datum/tgs_api/latest + +/world/TgsMaximumAPIVersion() + return "4.0.0.0" + +/world/TgsMinimumAPIVersion() + return "3.2.0.0" + +/world/TgsInitializationComplete() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.OnInitializationComplete() + +/world/proc/TgsTopic(T) + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + var/result = api.OnTopic(T) + if(result != TGS_UNIMPLEMENTED) + return result + +/world/TgsRevision() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + var/result = api.Revision() + if(result != TGS_UNIMPLEMENTED) + return result + +/world/TgsReboot() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.OnReboot() + +/world/TgsAvailable() + return TGS_READ_GLOBAL(tgs) != null + +/world/TgsVersion() + return world.params[TGS_VERSION_PARAMETER] + +/world/TgsInstanceName() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + var/result = api.InstanceName() + if(result != TGS_UNIMPLEMENTED) + return result + +/world/TgsTestMerges() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + var/result = api.TestMerges() + if(result != TGS_UNIMPLEMENTED) + return result + return list() + +/world/TgsEndProcess() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.EndProcess() + +/world/TgsChatChannelInfo() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + var/result = api.ChatChannelInfo() + if(result != TGS_UNIMPLEMENTED) + return result + return list() + +/world/TgsChatBroadcast(message, list/channels) + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.ChatBroadcast(message, channels) + +/world/TgsTargetedChatBroadcast(message, admin_only) + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.ChatTargetedBroadcast(message, admin_only) + +/world/TgsChatPrivateMessage(message, datum/tgs_chat_user/user) + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.ChatPrivateMessage(message, user) + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm new file mode 100644 index 00000000000..6af39a75df4 --- /dev/null +++ b/code/modules/tgs/core/datum.dm @@ -0,0 +1,74 @@ +TGS_DEFINE_AND_SET_GLOBAL(tgs, null) + +/datum/tgs_api + +/datum/tgs_api/latest + parent_type = /datum/tgs_api/v3210 + +TGS_PROTECT_DATUM(/datum/tgs_api) + +/datum/tgs_api/proc/ApiVersion() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/OnWorldNew(datum/tgs_event_handler/event_handler) + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/OnInitializationComplete() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/OnTopic(T) + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/OnReboot() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/InstanceName() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/TestMerges() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/EndProcess() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/Revision() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/ChatChannelInfo() + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/ChatBroadcast(message, list/channels) + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only) + return TGS_UNIMPLEMENTED + +/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only) + return TGS_UNIMPLEMENTED + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/core/default_event_handler.dm b/code/modules/tgs/core/default_event_handler.dm new file mode 100644 index 00000000000..716715bb268 --- /dev/null +++ b/code/modules/tgs/core/default_event_handler.dm @@ -0,0 +1,30 @@ +/datum/tgs_event_handler/tgs_default/HandleEvent(event_code) + //TODO + return + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm new file mode 100644 index 00000000000..7ca906c840d --- /dev/null +++ b/code/modules/tgs/includes.dm @@ -0,0 +1,6 @@ +#include "core\_definitions.dm" +#include "core\core.dm" +#include "core\datum.dm" +#include "core\default_event_handler.dm" +#include "v3210\api.dm" +#include "v3210\commands.dm" diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm new file mode 100644 index 00000000000..23022497632 --- /dev/null +++ b/code/modules/tgs/v3210/api.dm @@ -0,0 +1,249 @@ +#define REBOOT_MODE_NORMAL 0 +#define REBOOT_MODE_HARD 1 +#define REBOOT_MODE_SHUTDOWN 2 + +#define SERVICE_WORLD_PARAM "server_service" +#define SERVICE_INSTANCE_PARAM "server_instance" +#define SERVICE_PR_TEST_JSON "prtestjob.json" +#define SERVICE_INTERFACE_DLL "TGDreamDaemonBridge.dll" +#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint" + +#define SERVICE_CMD_HARD_REBOOT "hard_reboot" +#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown" +#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce" +#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands" +#define SERVICE_CMD_API_COMPATIBLE "api_compat" +#define SERVICE_CMD_PLAYER_COUNT "client_count" + +#define SERVICE_CMD_PARAM_KEY "serviceCommsKey" +#define SERVICE_CMD_PARAM_COMMAND "command" +#define SERVICE_CMD_PARAM_SENDER "sender" +#define SERVICE_CMD_PARAM_CUSTOM "custom" + +#define SERVICE_REQUEST_KILL_PROCESS "killme" +#define SERVICE_REQUEST_IRC_BROADCAST "irc" +#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc" +#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot" +#define SERVICE_REQUEST_API_VERSION "api_ver" + +#define SERVICE_RETURN_SUCCESS "SUCCESS" + +/datum/tgs_api/v3210 + var/reboot_mode = REBOOT_MODE_NORMAL + var/comms_key + var/instance_name + var/originmastercommit + var/commit + var/list/cached_custom_tgs_chat_commands + var/warned_revison = FALSE + var/warned_custom_commands = FALSE + +/datum/tgs_api/v3210/ApiVersion() + return "3.2.1.0" + +/datum/tgs_api/v3210/proc/trim_left(text) + for (var/i = 1 to length(text)) + if (text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +/datum/tgs_api/v3210/proc/trim_right(text) + for (var/i = length(text), i > 0, i--) + if (text2ascii(text, i) > 32) + return copytext(text, 1, i + 1) + return "" + +/datum/tgs_api/v3210/proc/file2list(filename) + return splittext(trim_left(trim_right(file2text(filename))), "\n") + +/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version + . = FALSE + + comms_key = world.params[SERVICE_WORLD_PARAM] + instance_name = world.params[SERVICE_INSTANCE_PARAM] + if(!instance_name) + instance_name = "TG Station Server" //maybe just upgraded + + var/list/logs = file2list(".git/logs/HEAD") + if(logs.len) + logs = splittext(logs[logs.len - 1], " ") + commit = logs[2] + logs = file2list(".git/logs/refs/remotes/origin/master") + if(logs.len) + originmastercommit = splittext(logs[logs.len - 1], " ")[2] + + if(world.system_type != MS_WINDOWS) + TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!") + return + ListServiceCustomCommands(TRUE) + ExportService("[SERVICE_REQUEST_API_VERSION] [ApiVersion()]", TRUE) + return TRUE + +//nothing to do for v3 +/datum/tgs_api/v3210/OnInitializationComplete() + return + +/datum/tgs_api/v3210/InstanceName() + return world.params[SERVICE_INSTANCE_PARAM] + +/datum/tgs_api/v3210/proc/ExportService(command, skip_compat_check = FALSE) + . = FALSE + if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL)) + TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.") + return + call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval + return TRUE + +/datum/tgs_api/v3210/OnTopic(T) + var/list/params = params2list(T) + var/their_sCK = params[SERVICE_CMD_PARAM_KEY] + if(!their_sCK) + return FALSE //continue world/Topic + + if(their_sCK != comms_key) + return "Invalid comms key!"; + + var/command = params[SERVICE_CMD_PARAM_COMMAND] + if(!command) + return "No command!" + + switch(command) + if(SERVICE_CMD_API_COMPATIBLE) + return SERVICE_RETURN_SUCCESS + if(SERVICE_CMD_HARD_REBOOT) + if(reboot_mode != REBOOT_MODE_HARD) + reboot_mode = REBOOT_MODE_HARD + TGS_INFO_LOG("Hard reboot requested by service") + TGS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by TGS.") + if(SERVICE_CMD_GRACEFUL_SHUTDOWN) + if(reboot_mode != REBOOT_MODE_SHUTDOWN) + reboot_mode = REBOOT_MODE_SHUTDOWN + TGS_INFO_LOG("Shutdown requested by service") + TGS_NOTIFY_ADMINS("The world will shutdown at the end of the game. Requested by TGS.") + if(SERVICE_CMD_WORLD_ANNOUNCE) + var/msg = params["message"] + if(!istext(msg) || !msg) + return "No message set!" + TGS_WORLD_ANNOUNCE(msg) + return SERVICE_RETURN_SUCCESS + if(SERVICE_CMD_PLAYER_COUNT) + return "[TGS_CLIENT_COUNT]" + if(SERVICE_CMD_LIST_CUSTOM) + return json_encode(ListServiceCustomCommands(FALSE)) + else + var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM]) + if(custom_command_result) + return istext(custom_command_result) ? custom_command_result : SERVICE_RETURN_SUCCESS + return "Unknown command: [command]" + +/datum/tgs_api/v3210/OnReboot() + switch(reboot_mode) + if(REBOOT_MODE_HARD) + TGS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...") + EndProcess() + if(REBOOT_MODE_SHUTDOWN) + TGS_WORLD_ANNOUNCE("The server is shutting down...") + EndProcess() + else + ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know + +/datum/tgs_api/v3210/TestMerges() + //do the best we can here as the datum can't be completed using the v3 api + . = list() + if(!fexists(SERVICE_PR_TEST_JSON)) + return + var/list/json = json_decode(file2text(SERVICE_PR_TEST_JSON)) + if(!json) + return + for(var/I in json) + var/datum/tgs_revision_information/test_merge/tm = new + tm.number = text2num(I) + var/list/entry = json[I] + tm.pull_request_commit = entry["commit"] + tm.author = entry["author"] + tm.title = entry["title"] + . += tm + +/datum/tgs_api/v3210/Revision() + if(!warned_revison) + TGS_ERROR_LOG("Use of TgsRevision on [ApiVersion()] origin_commit only points to master!") + warned_revison = TRUE + var/datum/tgs_revision_information/ri = new + ri.commit = commit + ri.origin_commit = originmastercommit + +/datum/tgs_api/v3210/EndProcess() + sleep(world.tick_lag) //flush the buffers + ExportService(SERVICE_REQUEST_KILL_PROCESS) + +/datum/tgs_api/v3210/ChatChannelInfo() + return list() + +/datum/tgs_api/v3210/ChatBroadcast(message, list/channels) + if(channels) + return TGS_UNIMPLEMENTED + ChatTargetedBroadcast(message, TRUE) + ChatTargetedBroadcast(message, FALSE) + +/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only) + ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]") + +/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only) + return TGS_UNIMPLEMENTED + +#undef REBOOT_MODE_NORMAL +#undef REBOOT_MODE_HARD +#undef REBOOT_MODE_SHUTDOWN + +#undef SERVICE_WORLD_PARAM +#undef SERVICE_INSTANCE_PARAM +#undef SERVICE_PR_TEST_JSON +#undef SERVICE_INTERFACE_DLL +#undef SERVICE_INTERFACE_FUNCTION + +#undef SERVICE_CMD_HARD_REBOOT +#undef SERVICE_CMD_GRACEFUL_SHUTDOWN +#undef SERVICE_CMD_WORLD_ANNOUNCE +#undef SERVICE_CMD_LIST_CUSTOM +#undef SERVICE_CMD_API_COMPATIBLE +#undef SERVICE_CMD_PLAYER_COUNT + +#undef SERVICE_CMD_PARAM_KEY +#undef SERVICE_CMD_PARAM_COMMAND +#undef SERVICE_CMD_PARAM_SENDER +#undef SERVICE_CMD_PARAM_CUSTOM + +#undef SERVICE_REQUEST_KILL_PROCESS +#undef SERVICE_REQUEST_IRC_BROADCAST +#undef SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE +#undef SERVICE_REQUEST_WORLD_REBOOT +#undef SERVICE_REQUEST_API_VERSION + +#undef SERVICE_RETURN_SUCCESS + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm new file mode 100644 index 00000000000..50466319813 --- /dev/null +++ b/code/modules/tgs/v3210/commands.dm @@ -0,0 +1,78 @@ +#define SERVICE_JSON_PARAM_HELPTEXT "help_text" +#define SERVICE_JSON_PARAM_ADMINONLY "admin_only" +#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters" + +/datum/tgs_api/v3210/proc/ListServiceCustomCommands(warnings_only) + if(!warnings_only) + . = list() + var/list/command_name_types = list() + var/list/warned_command_names = warnings_only ? list() : null + var/warned_about_the_dangers_of_robutussin = !warnings_only + for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) + if(!warned_about_the_dangers_of_robutussin) + TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!") + warned_about_the_dangers_of_robutussin = TRUE + var/datum/tgs_chat_command/stc = I + var/command_name = initial(stc.name) + if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) + if(warnings_only && !warned_command_names[command_name]) + TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!") + warned_command_names[command_name] = TRUE + continue + + if(command_name_types[command_name]) + if(warnings_only) + TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!") + continue + command_name_types[stc] = command_name + + if(!warnings_only) + .[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = 0) + +/datum/tgs_api/v3210/proc/HandleServiceCustomCommand(command, sender, params) + if(!cached_custom_tgs_chat_commands) + cached_custom_tgs_chat_commands = list() + for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) + var/datum/tgs_chat_command/stc = I + cached_custom_tgs_chat_commands[lowertext(initial(stc.name))] = stc + + var/command_type = cached_custom_tgs_chat_commands[command] + if(!command_type) + return FALSE + var/datum/tgs_chat_command/stc = new command_type + var/datum/tgs_chat_user/user = new + user.friendly_name = sender + user.mention = sender + return stc.Run(user, params) || TRUE + +/* + +#undef SERVICE_JSON_PARAM_HELPTEXT +#undef SERVICE_JSON_PARAM_ADMINONLY +#undef SERVICE_JSON_PARAM_REQUIREDPARAMETERS + +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 0cf8ed10a1f..6f51c0bb48f 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -195,7 +195,7 @@ return 1 /obj/vehicle/proc/explode() - src.visible_message("[src] blows apart!", 1) + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/stack/rods(Tsec) diff --git a/code/world.dm b/code/world.dm index 9942f4311c1..9a172f41a81 100644 --- a/code/world.dm +++ b/code/world.dm @@ -73,6 +73,8 @@ var/global/datum/global_init/init = new () if(byond_version < RECOMMENDED_VERSION) world.log << "Your server's byond version does not meet the recommended requirements for this server. Please update BYOND to [RECOMMENDED_VERSION]." + world.TgsNew() + config.post_load() if(config && config.server_name != null && config.server_suffix && world.port > 0) @@ -115,7 +117,13 @@ var/list/world_api_rate_limit = list() log_debug("API: Request Received - from:[addr], master:[master], key:[key]") diary << "TOPIC: \"[T]\", from:[addr], master:[master], key:[key], auth:[queryparams["auth"] ? queryparams["auth"] : "null"] [log_end]" - if (!queryparams.len) + // TGS topic hook. Returns if successful, expects old-style serialization. + var/tgs_topic_return = TgsTopic(T) + + if (tgs_topic_return) + log_debug("API - TGS3 Request.") + return tgs_topic_return + else if (!queryparams.len) log_debug("API - Bad Request - Invalid/no JSON data sent.") response["statuscode"] = 400 response["response"] = "Bad Request - Invalid/no JSON data sent." @@ -181,9 +189,7 @@ var/list/world_api_rate_limit = list() /world/Reboot(var/reason) - /*spawn(0) - world << sound(pick('sound/AI/newroundsexy.ogg','sound/misc/apcdestroyed.ogg','sound/misc/bangindonk.ogg')) // random end sounds!! - LastyBatsy - */ + world.TgsReboot() Master.Shutdown() diff --git a/config/example/config.txt b/config/example/config.txt index b66679fbeb4..e42580d5fcf 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -407,6 +407,9 @@ MERCHANT_CHANCE 20 ## Enable asteroid tunnel/cave generation. Will behave strangely if turned off with a map that expects it on. # GENERATE_ASTEROID +## Enable asteroid dungeon generation. The value preceding is the chance for a dungeon slot to be occupied. +# DUNGEON_CHANCE 25 + ## Uncomment to enable organ decay outside of a body or storage item. #ORGANS_CAN_DECAY diff --git a/html/changelog.html b/html/changelog.html index 3966211ff96..f0800b7076e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,94 @@ -->
+

22 July 2018

+

Alberyk updated:

+ +

Arrow768 updated:

+ +

BurgerBB updated:

+ +

Kaedwuff updated:

+ +

Karolis2011 updated:

+ +

LordFowl updated:

+ +

LordFowl, BygoneHero, Kyres1 updated:

+ +

LordFowl, Loow, NursieKitty updated:

+ +

MattAtlas updated:

+ +

ParadoxSpace updated:

+ +

PoZe updated:

+ +

Skull132 updated:

+ +

ben10083 updated:

+ +

17 July 2018

Alberyk updated: