diff --git a/baystation12.dme b/baystation12.dme index e49f506a2f4e7..13ca0278f8dc1 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -492,6 +492,7 @@ #include "code\datums\wires\fabricator.dm" #include "code\datums\wires\nuclearbomb.dm" #include "code\datums\wires\particle_accelerator.dm" +#include "code\datums\wires\quantum_pads.dm" #include "code\datums\wires\radio.dm" #include "code\datums\wires\robot.dm" #include "code\datums\wires\shield_generator.dm" @@ -1744,6 +1745,7 @@ #include "code\modules\error_handler\error_handler.dm" #include "code\modules\error_handler\error_reporting.dm" #include "code\modules\error_handler\error_viewer.dm" +#include "code\modules\events\abominable_infestation.dm" #include "code\modules\events\apc_damage.dm" #include "code\modules\events\blob.dm" #include "code\modules\events\brain_expansion.dm" @@ -1980,6 +1982,7 @@ #include "code\modules\mining\mine_turfs.dm" #include "code\modules\mining\satchel_ore_boxdm.dm" #include "code\modules\mining\drilling\drill.dm" +#include "code\modules\mining\drilling\drill_upgrades.dm" #include "code\modules\mining\machinery\_mineral.dm" #include "code\modules\mining\machinery\mineral_console.dm" #include "code\modules\mining\machinery\mineral_processor.dm" @@ -2006,6 +2009,10 @@ #include "code\modules\mob\grab\grab_datum.dm" #include "code\modules\mob\grab\grab_object.dm" #include "code\modules\mob\grab\grab_readme.dm" +#include "code\modules\mob\grab\abnomination\abom_agressive.dm" +#include "code\modules\mob\grab\abnomination\abom_kill.dm" +#include "code\modules\mob\grab\abnomination\abom_passive.dm" +#include "code\modules\mob\grab\abnomination\grab_abom.dm" #include "code\modules\mob\grab\nab\grab_nab.dm" #include "code\modules\mob\grab\nab\nab_aggressive.dm" #include "code\modules\mob\grab\nab\nab_kill.dm" @@ -2298,6 +2305,14 @@ #include "code\modules\mob\living\simple_animal\hostile\hivebot\hivebot.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebot\ranged.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebot\tank.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\_infestation.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\assembler.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\broodling.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\eviscerator.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\floatfly.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\larva.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\rhino.dm" +#include "code\modules\mob\living\simple_animal\hostile\infestation\spitter.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\exoplanet.dm" @@ -2527,6 +2542,7 @@ #include "code\modules\organs\internal\lungs.dm" #include "code\modules\organs\internal\stomach.dm" #include "code\modules\organs\internal\voice.dm" +#include "code\modules\organs\internal\species\abomination.dm" #include "code\modules\organs\internal\species\adherent.dm" #include "code\modules\organs\internal\species\blueforged.dm" #include "code\modules\organs\internal\species\borer.dm" @@ -2570,6 +2586,7 @@ #include "code\modules\overmap\exoplanets\planet_types\chlorine.dm" #include "code\modules\overmap\exoplanets\planet_types\desert.dm" #include "code\modules\overmap\exoplanets\planet_types\grass.dm" +#include "code\modules\overmap\exoplanets\planet_types\infested.dm" #include "code\modules\overmap\exoplanets\planet_types\shrouded.dm" #include "code\modules\overmap\exoplanets\planet_types\snow.dm" #include "code\modules\overmap\exoplanets\planet_types\volcanic.dm" @@ -2927,6 +2944,7 @@ #include "code\modules\species\species_hud.dm" #include "code\modules\species\species_random.dm" #include "code\modules\species\species_shapeshift.dm" +#include "code\modules\species\outsider\abomination.dm" #include "code\modules\species\outsider\random.dm" #include "code\modules\species\outsider\shadow.dm" #include "code\modules\species\outsider\starlight.dm" @@ -3263,10 +3281,13 @@ #include "proxima\code\game\machinery\remote_weapon.dm" #include "proxima\code\game\machinery\factory\blueprint.dm" #include "proxima\code\game\machinery\factory\factory.dm" +#include "proxima\code\game\machinery\factory\quantum_pads.dm" #include "proxima\code\game\machinery\factory\blueprints\generic.dm" #include "proxima\code\game\objects\effects\particles\snow.dm" +#include "proxima\code\game\objects\items\bouquet.dm" #include "proxima\code\game\objects\items\circuits_remote.dm" #include "proxima\code\game\objects\items\containers.dm" +#include "proxima\code\game\objects\items\mgsbox.dm" #include "proxima\code\game\objects\items\posters.dm" #include "proxima\code\game\objects\items\remote_weapon_ammo.dm" #include "proxima\code\game\objects\items\toys.dm" diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 3eaa128a76887..2aa913e3b7ccb 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -57,3 +57,9 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define TANK_FLAG_FORCED FLAG(1) #define TANK_FLAG_LEAKING FLAG(2) #define TANK_FLAG_WIRED FLAG(3) + +// Sector Flags. +#define OVERMAP_SECTOR_BASE 0x0001 // Whether or not this sector is a starting sector. Z levels contained in this sector are added to station_levels +#define OVERMAP_SECTOR_KNOWN 0x0002 // Makes the sector show up on nav computers +#define OVERMAP_SECTOR_IN_SPACE 0x0004 // If the sector can be accessed by drifting off the map edge +#define OVERMAP_SECTOR_UNTARGETABLE 0x0008 // If the sector is untargetable by missiles diff --git a/code/__defines/guns.dm b/code/__defines/guns.dm index b58e3cc83ce0a..9b49748ec5550 100644 --- a/code/__defines/guns.dm +++ b/code/__defines/guns.dm @@ -7,6 +7,7 @@ #define CALIBER_PISTOL_FAST "6mmR" #define CALIBER_RIFLE "5mmR" +#define CALIBER_CARABINE "6.5mmR" #define CALIBER_RIFLE_MILITARY "7mmR" #define CALIBER_ANTIMATERIAL "15mmR" #define CALIBER_ANTIMATERIAL_SMALL "12mmR" //Порт с инфинити. Калибр КорпоАМР винтовки diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 34c594c5afbe7..1d2498a962195 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -19,6 +19,7 @@ #define GRAB_NORMAL "normal" #define GRAB_NAB "nab" #define GRAB_NAB_SPECIAL "special nab" +#define GRAB_ABOMINATION "terrifying grab" // Grab levels. #define NORM_PASSIVE "normal passive" @@ -31,6 +32,10 @@ #define NAB_AGGRESSIVE "nab aggressive" #define NAB_KILL "nab kill" +#define GRAB_ABOMINATION_PASSIVE "terrifying grab passive" +#define GRAB_ABOMINATION_AGGRESSIVE "terrifying grab aggressive" +#define GRAB_ABOMINATION_KILL "terrifying grab kill" + #define BORGMESON FLAG(0) #define BORGTHERM FLAG(1) #define BORGXRAY FLAG(2) @@ -222,6 +227,7 @@ #define BP_ANCHOR "anchoring ligament" #define BP_PHORON "phoron filter" #define BP_ACETONE "acetone reactor" +#define BP_LARVA "larvae storage" // Vox bits. #define BP_HINDTONGUE "hindtongue" @@ -378,10 +384,11 @@ #define SPECIES_MONKEY "Monkey" #define SPECIES_NABBER "giant armoured serpentid" #define SPECIES_FBP "Full Body Prosthesis" //just for xeno-panel //proxima +#define SPECIES_ABOMINATION "Abomination" #define UNRESTRICTED_SPECIES list(SPECIES_HUMAN, SPECIES_DIONA, SPECIES_IPC, SPECIES_UNATHI, SPECIES_YEOSA, SPECIES_SKRELL, SPECIES_TRITONIAN, SPECIES_SPACER, SPECIES_VATGROWN, SPECIES_GRAVWORLDER, SPECIES_MULE, SPECIES_SHELL) -#define RESTRICTED_SPECIES list(SPECIES_VOX, SPECIES_ALIEN, SPECIES_GOLEM) -#define HUMAN_SPECIES list(SPECIES_HUMAN, SPECIES_VATGROWN, SPECIES_SPACER, SPECIES_GRAVWORLDER, SPECIES_MULE) +#define RESTRICTED_SPECIES list(SPECIES_VOX, SPECIES_ALIEN, SPECIES_GOLEM, SPECIES_ABOMINATION) +#define HUMAN_SPECIES list(SPECIES_HUMAN, SPECIES_VATGROWN, SPECIES_SPACER, SPECIES_GRAVWORLDER, SPECIES_MULE, SPECIES_ABOMINATION) #define SOUNDED_SPECIES list(SPECIES_HUMAN, SPECIES_VATGROWN, SPECIES_SPACER, SPECIES_TRITONIAN, SPECIES_GRAVWORLDER, SPECIES_MULE, SPECIES_UNATHI, SPECIES_YEOSA, SPECIES_SKRELL, SPECIES_SHELL) #define SURGERY_CLOSED 0 @@ -486,3 +493,6 @@ #define DO_INCAPACITATED (-3) #define FAKE_INVIS_ALPHA_THRESHOLD 127 // If something's alpha var is at or below this number, certain things will pretend it is invisible. + + +#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; diff --git a/code/__defines/overmap.dm b/code/__defines/overmap.dm index b2dabb5f93a0d..6f0b673da2c86 100644 --- a/code/__defines/overmap.dm +++ b/code/__defines/overmap.dm @@ -12,3 +12,10 @@ #define OVERMAP_WEAKNESS_MINING 4 #define OVERMAP_WEAKNESS_EXPLOSIVE 8 #define OVERMAP_WEAKNESS_DROPPOD 16 +#define OVERMAP_WEAKNESS_ODST 32 + +#define TARGET_SHIP 0 +#define TARGET_MISSILE 1 +#define TARGET_PLANET 2 +#define TARGET_PLANETCOORD 4 +#define TARGET_POINT 8 diff --git a/code/datums/wires/quantum_pads.dm b/code/datums/wires/quantum_pads.dm new file mode 100644 index 0000000000000..0db57bed5c258 --- /dev/null +++ b/code/datums/wires/quantum_pads.dm @@ -0,0 +1,24 @@ +/datum/wires/quantumpad + holder_type = /obj/machinery/quantumpad + wire_count = 1 + descriptions = list( + new /datum/wire_description(WIRE_TRIGGER, "This wire seems to be activating the teleportation mechanism.") + ) + +var/const/WIRE_TRIGGER = 1 + +/datum/wires/quantumpad/proc/trigger() + var/obj/machinery/quantumpad/QP = holder + QP.physical_attack_hand() + return + +/datum/wires/quantumpad/UpdatePulsed(index) + switch(index) + if(WIRE_TRIGGER) + trigger() + +/datum/wires/quantumpad/CanUse(mob/living/L) + var/obj/machinery/quantumpad/QP = holder + if(QP.panel_open) + return TRUE + return FALSE diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 5e79b22653d85..e20639ae92d7d 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -241,3 +241,7 @@ /atom/movable/proc/get_bullet_impact_effect_type() return BULLET_IMPACT_NONE + +/// Handles special effects of item being removed from "implants" of a mob +/atom/movable/proc/ImplantRemoval(mob/user) + return diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index c6e7bd4bdc0e4..5f0943c77d6e9 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -69,13 +69,6 @@ return TRUE -/obj/item/implant/proc/removed() - imp_in = null - if(part) - part.implants -= src - part = null - implanted = 0 - //Called in surgery when incision is retracted open / ribs are opened - basically before you can take implant out /obj/item/implant/proc/exposed() return diff --git a/code/game/objects/items/weapons/implants/implants/death_alarm.dm b/code/game/objects/items/weapons/implants/implants/death_alarm.dm index 8abf8f0dc7201..7b78e8933c6e8 100644 --- a/code/game/objects/items/weapons/implants/implants/death_alarm.dm +++ b/code/game/objects/items/weapons/implants/implants/death_alarm.dm @@ -66,7 +66,7 @@ START_PROCESSING(SSobj, src) return TRUE -/obj/item/implant/death_alarm/removed() +/obj/item/implant/death_alarm/ImplantRemoval() ..() STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/weapons/implants/implants/explosive.dm b/code/game/objects/items/weapons/implants/implants/explosive.dm index 31e47db1fc065..639dfdcf3ab9b 100644 --- a/code/game/objects/items/weapons/implants/implants/explosive.dm +++ b/code/game/objects/items/weapons/implants/implants/explosive.dm @@ -160,7 +160,7 @@ return TRUE /obj/item/implant/explosive/Destroy() - removed() + ImplantRemoval() GLOB.listening_objects -= src return ..() diff --git a/code/game/objects/items/weapons/implants/implants/imprinting.dm b/code/game/objects/items/weapons/implants/implants/imprinting.dm index 9607ddc2b367e..372b33b64ee7c 100644 --- a/code/game/objects/items/weapons/implants/implants/imprinting.dm +++ b/code/game/objects/items/weapons/implants/implants/imprinting.dm @@ -85,7 +85,7 @@ to_chat(imp_in, instruction) addtimer(CALLBACK(src,.proc/activate),3000,(TIMER_UNIQUE|TIMER_OVERRIDE)) -/obj/item/implant/imprinting/removed() +/obj/item/implant/imprinting/ImplantRemoval() if(brainwashing && !malfunction) to_chat(imp_in,"A wave of nausea comes over you.
You are no longer so sure of those beliefs you've had...") ..() diff --git a/code/game/objects/items/weapons/implants/implants/translator.dm b/code/game/objects/items/weapons/implants/implants/translator.dm index 764fe79944076..d3fa6eb743aa3 100644 --- a/code/game/objects/items/weapons/implants/implants/translator.dm +++ b/code/game/objects/items/weapons/implants/implants/translator.dm @@ -34,7 +34,7 @@ return TRUE /obj/item/implant/translator/Destroy() - removed() + ImplantRemoval() GLOB.listening_objects -= src return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1145888db929f..f807f667364a2 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -204,3 +204,6 @@ */ /obj/proc/is_safe_to_step(mob/living/L) return TRUE + +/obj/proc/get_additional_speed_decrease() + return between(0, src.w_class, ITEM_SIZE_GARGANTUAN) / 5 diff --git a/code/game/sound.dm b/code/game/sound.dm index d422515de3c05..fb7ed0cc15109 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -171,6 +171,22 @@ var/global/const/FALLOFF_SOUNDS = 0.5 sound_to(src, S) +/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, falloff, is_global = FALSE, ignore_pressure = FALSE) + for(var/m in GLOB.player_list) + if(ismob(m) && !isnewplayer(m)) + var/mob/M = m + M.playsound_local(get_turf(M), soundin, volume, vary, frequency, falloff, is_global, ignore_pressure) + +/proc/sound_to_playing_players_on_level(soundin, volume = 100, vary = FALSE, frequency = 0, falloff, is_global = FALSE, ignore_pressure = FALSE, zlevel) + for(var/m in GLOB.player_list) + if(ismob(m) && !isnewplayer(m)) + var/mob/M = m + if(!islist(zlevel)) + zlevel = list(zlevel) + if((M.loc.z in zlevel) && M.client) + M.playsound_local(get_turf(M), soundin, volume, vary, frequency, falloff, is_global, ignore_pressure) + + /client/proc/playtitlemusic() if (get_preference_value(/datum/client_preference/play_lobby_music) == GLOB.PREF_YES) sound_to(src, GLOB.using_map.lobby_track.get_sound()) diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm index c6f14f4d0ccf1..d2967ce9c368e 100644 --- a/code/game/turfs/flooring/flooring.dm +++ b/code/game/turfs/flooring/flooring.dm @@ -402,3 +402,8 @@ wall_smooth = SMOOTH_NONE space_smooth = SMOOTH_NONE height = -FLUID_OVER_MOB_HEAD * 2 + +/decl/flooring/flesh/infested + name = "pulsating flesh" + desc = "Disgusting flooring made out of flesh, bone, eyes, and various other human bits and pieces." + color = "#94404e" diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm index 688231e5568e7..5ab074502b97d 100644 --- a/code/modules/ai/ai_holder_communication.dm +++ b/code/modules/ai/ai_holder_communication.dm @@ -109,15 +109,25 @@ switch(pick(comm_types)) if (COMM_SAY) holder.ISay(pick(holder.say_list.speak)) + PlayMobSound(holder.say_list.speak_sounds) if (COMM_AUDIBLE_EMOTE) holder.audible_emote(pick(holder.say_list.emote_hear)) + PlayMobSound(holder.say_list.emote_hear_sounds) if (COMM_VISUAL_EMOTE) holder.visible_emote(pick(holder.say_list.emote_see)) + PlayMobSound(holder.say_list.emote_see_sounds) #undef COMM_SAY #undef COMM_AUDIBLE_EMOTE #undef COMM_VISUAL_EMOTE +// Simply plays a sound chosen out of a list +/datum/ai_holder/proc/PlayMobSound(list/potential_sounds) + if(LAZYLEN(potential_sounds)) + var/result = pickweight(holder.say_list.emote_see_sounds) + if(result != null) + playsound(holder, result, 50, 1) + // Handles the holder hearing a mob's say() // Does nothing by default, override this proc for special behavior. /datum/ai_holder/proc/on_hear_say(mob/living/speaker, message) diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index 5ac387dad6d9e..1ea2b9bb54177 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -10,7 +10,7 @@ var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options. var/turf/target_last_seen_turf = null // Where the mob last observed the target being, used if the target disappears but the mob wants to keep fighting. - var/vision_range = 7 // How far the targeting system will look for things to kill. Note that values higher than 7 are 'offscreen' and might be unsporting. + var/vision_range = 8 // How far the targeting system will look for things to kill. Note that values higher than 7 are 'offscreen' and might be unsporting. var/respect_alpha = TRUE // If true, mobs with a sufficently low alpha will be treated as invisible. var/alpha_vision_threshold = FAKE_INVIS_ALPHA_THRESHOLD // Targets with an alpha less or equal to this will be considered invisible. Requires above var to be true. diff --git a/code/modules/ai/say_list.dm b/code/modules/ai/say_list.dm index 6e4a43ee6b9c4..bc90fe0c98d07 100644 --- a/code/modules/ai/say_list.dm +++ b/code/modules/ai/say_list.dm @@ -32,6 +32,11 @@ var/list/say_stand_down = list() // When the threatened thing goes away. var/list/say_escalate = list() // When the threatened thing doesn't go away. +// Lists belows are ASSOCIATIVE lists! Sound = Chance. If null is in there - it will not play sound when prompted + var/list/speak_sounds = list() // Sounds that can be played when anything from speak list is said + var/list/emote_hear_sounds = list() // Sounds that can be played when anything from emote_hear is performed + var/list/emote_see_sounds = list() // Sounds that can be played when anything from emote_see is performed + var/threaten_sound = null // Sound file played when the mob's AI calls threaten_target() for the first time. var/stand_down_sound = null // Sound file played when the mob's AI loses sight of the threatened target. diff --git a/code/modules/events/abominable_infestation.dm b/code/modules/events/abominable_infestation.dm new file mode 100644 index 0000000000000..d103794090134 --- /dev/null +++ b/code/modules/events/abominable_infestation.dm @@ -0,0 +1,26 @@ +/datum/event/abominable_infestation + announceWhen = 30 + var/spawncount = 1 + +/datum/event/abominable_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 30) + spawncount = rand(2 * severity, 4 * severity) + +/datum/event/abominable_infestation/announce() + GLOB.using_map.unidentified_lifesigns_announcement() + +/datum/event/abominable_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in SSmachines.machinery) + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in affecting_z)) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + + while((spawncount >= 1) && LAZYLEN(vents)) + var/obj/vent = pick(vents) + if(prob(33)) + new /mob/living/simple_animal/hostile/infestation/larva/implant/implanter(get_turf(vent)) + else + new /mob/living/simple_animal/hostile/infestation/larva(get_turf(vent)) + vents -= vent + spawncount-- diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index c0f46fdafc303..db8857455cb64 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -7,6 +7,7 @@ #define ASSIGNMENT_MEDICAL "Medical" #define ASSIGNMENT_SCIENTIST "Scientist" #define ASSIGNMENT_SECURITY "Security" +#define ASSIGNMENT_EXPLORATION "Exploration" #define ASSIGNMENT_PSYCHIATRIST "Psychiatrist" var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major", EVENT_LEVEL_EXO = "Exoplanet") @@ -182,7 +183,8 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT severity = EVENT_LEVEL_MAJOR available_events = list( new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 1320), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 40), 1), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 20, ASSIGNMENT_MEDICAL = 10, ASSIGNMENT_SECURITY = 20, ASSIGNMENT_EXPLORATION = 10), 1), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Abominable Infestation", /datum/event/abominable_infestation,0, list(ASSIGNMENT_MEDICAL = 10, ASSIGNMENT_SECURITY = 20, ASSIGNMENT_EXPLORATION = 15), 1), new /datum/event_meta/no_overmap(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 5), 1), new /datum/event_meta/no_overmap(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10), 1), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Space Vines", /datum/event/spacevine, 0, list(ASSIGNMENT_ENGINEER = 15), 1), @@ -208,4 +210,5 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT #undef ASSIGNMENT_MEDICAL #undef ASSIGNMENT_SCIENTIST #undef ASSIGNMENT_SECURITY +#undef ASSIGNMENT_EXPLORATION #undef ASSIGNMENT_PSYCHIATRIST diff --git a/code/modules/fabrication/designs/general/designs_arms_ammo.dm b/code/modules/fabrication/designs/general/designs_arms_ammo.dm index 9ffccef77ddb0..06233109454d2 100644 --- a/code/modules/fabrication/designs/general/designs_arms_ammo.dm +++ b/code/modules/fabrication/designs/general/designs_arms_ammo.dm @@ -138,3 +138,7 @@ /datum/fabricator_recipe/arms_ammo/hidden/rifleinternalclip name = "ammunition (rifle internal clip)" path = /obj/item/ammo_magazine/iclipr + +/datum/fabricator_recipe/arms_ammo/hidden/carabinemagazine + name = "ammunition (spec.ops carabine magazine)" + path = /obj/item/ammo_magazine/carabine_rifle diff --git a/code/modules/materials/recipes_storage.dm b/code/modules/materials/recipes_storage.dm index a9f8293f6e540..a00afb64a058d 100644 --- a/code/modules/materials/recipes_storage.dm +++ b/code/modules/materials/recipes_storage.dm @@ -8,6 +8,26 @@ result_type = /obj/item/storage/box/large req_amount = 2 +/datum/stack_recipe/box/larger + title = "cardboard box" + result_type = /obj/item/storage/mgsbox + req_amount = 5 + +/datum/stack_recipe/box/larger/med + title = "cardboard box with medical symbols" + result_type = /obj/item/storage/mgsbox/med + req_amount = 5 + +/datum/stack_recipe/box/larger/lpa + title = "cardboard box with strange symbols" + result_type = /obj/item/storage/mgsbox/lpa + req_amount = 5 + +/datum/stack_recipe/box/larger/clear + title = "cardboard box without symbols" + result_type = /obj/item/storage/mgsbox/clear + req_amount = 5 + /datum/stack_recipe/box/donut title = "donut box" result_type = /obj/item/storage/box/donut/empty diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index 48a542305cf4a..4db875e3ef5cc 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -1,3 +1,9 @@ +/// Maximum amount of upgrades that can be installed +#define MAX_DRILL_UPGRADES 3 +// Upgrade flags +#define DRILL_AUTOMATIC (1<1) + + /obj/machinery/mining icon = 'icons/obj/mining_drill.dmi' anchored = FALSE @@ -19,9 +25,20 @@ machine_desc = "A cell-powered industrial drill, used to crack through dirt and rock to harvest minerals beneath the surface. Requires two adjacent braces to operate." var/braces_needed = 2 var/list/supports = list() - var/supported = 0 + var/supported = FALSE var/active = FALSE var/list/resource_field = list() + /// List of ores currently stored + var/list/stored_ores = list() + /// Range around drill from where we intake ores + var/resource_area = 3 + /// Upgrades installed + var/list/drill_upgrades = list() + /// Upgrade flags + var/upgrade_flags = 0 + /// Direction of automatic ore unloading + var/dispense_dir = NORTH + var/ore_types = list( MATERIAL_IRON = /obj/item/ore/iron, @@ -38,13 +55,13 @@ MATERIAL_RUTILE = /obj/item/ore/rutile ) - //Upgrades + // Component upgrades var/harvest_speed var/capacity - //Flags - var/need_update_field = 0 - var/need_player_check = 0 + // Booleans + var/need_update_field = FALSE + var/need_player_check = FALSE /obj/machinery/mining/drill/Process() if(need_player_check) @@ -80,7 +97,7 @@ var/turf/simulated/floor/exoplanet/T = get_turf(src) if(T.diggable) new /obj/structure/pit(T) - T.diggable = 0 + T.diggable = FALSE else if(istype(get_turf(src), /turf/simulated/floor)) var/turf/simulated/floor/T = get_turf(src) T.ex_act(EX_ACT_HEAVY) @@ -90,7 +107,7 @@ var/turf/simulated/harvesting = pick(resource_field) while(resource_field.len && !harvesting.resources) - harvesting.has_resources = 0 + harvesting.has_resources = FALSE harvesting.resources = null resource_field -= harvesting if(resource_field.len) @@ -100,24 +117,32 @@ return var/total_harvest = harvest_speed //Ore harvest-per-tick. - var/found_resource = 0 //If this doesn't get set, the area is depleted and the drill errors out. + var/found_resource = FALSE //If this doesn't get set, the area is depleted and the drill errors out. for(var/metal in ore_types) - if(contents.len >= capacity) - system_error("insufficient storage space") - set_active(FALSE) - need_player_check = 1 - update_icon() - return - - if(contents.len + total_harvest >= capacity) - total_harvest = capacity - contents.len - - if(total_harvest <= 0) break + if(stored_ores.len >= capacity) + if(!(upgrade_flags & DRILL_AUTOMATIC)) + system_error("INSUFFICIENT STORAGE SPACE") + set_active(FALSE) + need_player_check = TRUE + on_update_icon() + return + // Full auto drill! + var/turf/unload_turf = get_step(src, dispense_dir) + if(!istype(unload_turf) || unload_turf.density) + unload_turf = get_turf(src) + UnloadTo(unload_turf, null) + playsound(loc,'sound/machines/ping.ogg', 50, TRUE) + + if(stored_ores.len + total_harvest >= capacity) + total_harvest = capacity - stored_ores.len + + if(total_harvest <= 0) + break if(harvesting.resources[metal]) - found_resource = 1 + found_resource = TRUE var/create_ore = 0 if(harvesting.resources[metal] >= total_harvest) @@ -131,7 +156,7 @@ for(var/i=1, i <= create_ore, i++) var/oretype = ore_types[metal] - new oretype(src) + stored_ores += new oretype(src) if(!found_resource) harvesting.has_resources = 0 @@ -139,13 +164,59 @@ resource_field -= harvesting else set_active(FALSE) - need_player_check = 1 + need_player_check = TRUE + on_update_icon() + +/obj/machinery/mining/drill/examine(mob/user) + . = ..() + for(var/obj/item/drill_upgrade/U in drill_upgrades) + if(!U.ExamineMessage(user, src)) + continue + to_chat(user, U.ExamineMessage(user, src)) + to_chat(user, "\The [src] is mining in a range of [resource_area].") + return + +/obj/machinery/mining/drill/attackby(obj/item/I, mob/user) + if(panel_open || user.a_intent == I_HURT) + return ..() + + if(istype(I, /obj/item/drill_upgrade)) + var/obj/item/drill_upgrade/upgrade = I + if(active) + to_chat(user, SPAN_WARNING("[src] cannot be modified while it is running.")) + return FALSE + AttemptUpgrade(upgrade, user) + return TRUE + + if(istype(I, /obj/item/device/multitool)) + if(upgrade_flags & DRILL_AUTOMATIC) + dispense_dir = turn(dispense_dir, 45) + playsound(loc, 'sound/machines/click.ogg', 25, TRUE) + to_chat(user, SPAN_NOTICE("[src] is now dispensing ores to the [dir2text(dispense_dir)].")) + return TRUE + + if(istype(I, /obj/item/crowbar)) + if(!LAZYLEN(drill_upgrades)) + to_chat() + return TRUE + var/obj/item/drill_upgrade/U = input(user, "Which modification would you like to uninstall from \the [src]?", "Modification Removal") as null|anything in drill_upgrades + if(!istype(U) || QDELETED(src) || !Adjacent(user) || user.incapacitated()) + return TRUE + user.visible_message(SPAN_NOTICE("[user] begins removing [U] from [src]'s sockets."), + SPAN_NOTICE("You begin removing [U] from the [src].")) + playsound(loc, 'sound/items/Crowbar.ogg', 25, TRUE) + if(!do_after(user, 10 SECONDS, src)) + return TRUE + if(!(U in drill_upgrades) || QDELETED(src) || !Adjacent(user)) + return TRUE + user.visible_message(SPAN_NOTICE("[user] sucessfuly removes [U] from [src]."), + SPAN_NOTICE("You successfuly uninstall [U] from the [src].")) + playsound(loc, 'sound/items/Deconstruct.ogg', 50, TRUE) + U.Uninstall(src, user) update_icon() + return TRUE -/obj/machinery/mining/drill/proc/set_active(var/new_active) - if(active != new_active) - active = new_active - update_use_power(active ? POWER_USE_ACTIVE : POWER_USE_OFF) + return ..() /obj/machinery/mining/drill/cannot_transition_to(state_path) if(active) @@ -163,7 +234,7 @@ else if(anchored) get_resource_field() to_chat(user, "You hit the manual override and reset the drill's error checking.") - need_player_check = 0 + need_player_check = TRUE update_icon() return TRUE if(supported && !panel_open) @@ -171,7 +242,7 @@ set_active(!active) if(active) visible_message("\The [src] lurches downwards, grinding noisily.") - need_update_field = 1 + need_update_field = TRUE else visible_message("\The [src] shudders to a grinding halt.") else @@ -186,12 +257,16 @@ if(need_player_check) icon_state = "mining_drill_error" else if(active) - var/status = clamp(round( (contents.len / capacity) * 4 ), 0, 3) + var/status = clamp(round((stored_ores.len / capacity) * 4 ), 0, 3) icon_state = "mining_drill_active[status]" else if(supported) icon_state = "mining_drill_braced" else icon_state = "mining_drill" + + overlays.Cut() + for(var/obj/item/upgrade in drill_upgrades) + overlays += image(icon, upgrade.icon_state) return /obj/machinery/mining/drill/RefreshParts() @@ -201,9 +276,13 @@ var/charge_multiplier = clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 0.1, 10) change_power_consumption(initial(active_power_usage) / charge_multiplier, POWER_USE_ACTIVE) -/obj/machinery/mining/drill/proc/check_supports() +/obj/machinery/mining/drill/proc/set_active(new_active) + if(active != new_active) + active = new_active + update_use_power(active ? POWER_USE_ACTIVE : POWER_USE_OFF) - supported = 0 +/obj/machinery/mining/drill/proc/check_supports() + supported = FALSE if((!supports || !supports.len) && initial(anchored) == 0) anchored = FALSE @@ -212,28 +291,53 @@ anchored = TRUE if(supports && supports.len >= braces_needed) - supported = 1 + supported = TRUE update_icon() -/obj/machinery/mining/drill/proc/system_error(var/error) +/obj/machinery/mining/drill/proc/system_error(error = "UNKNOWN ERROR") if(error) src.visible_message("\The [src] flashes a '[error]' warning.") - need_player_check = 1 + need_player_check = TRUE set_active(FALSE) update_icon() /obj/machinery/mining/drill/proc/get_resource_field() resource_field = list() - need_update_field = 0 + need_update_field = FALSE - for (var/turf/simulated/T in range(2, src)) - if (T.has_resources) + for(var/turf/simulated/T in range(resource_area, src)) + if(T.has_resources) resource_field += T - if (!length(resource_field)) - system_error("resources depleted") + if(!length(resource_field)) + system_error("RESOURCES DEPLETED") + +/obj/machinery/mining/drill/proc/AttemptUpgrade(obj/item/drill_upgrade/upgrade, mob/user) + if(drill_upgrades.len >= MAX_DRILL_UPGRADES) + to_chat(user, SPAN_WARNING("The [src] has maximum amount of modifications installed already!")) + return FALSE + if((locate(upgrade.type) in drill_upgrades) && !upgrade.allow_multiple) + to_chat(user, SPAN_WARNING("The [src] has same kind of modification installed already!")) + return FALSE + if(!user.skill_check(SKILL_ELECTRICAL, SKILL_ADEPT) || !user.skill_check(SKILL_DEVICES, SKILL_ADEPT)) + user.visible_message(SPAN_NOTICE("[user] fumbles around figuring out how to install the modification module on [src]."), + SPAN_NOTICE("You fumble around figuring out how to install the module on [src].")) + var/fumbling_time = 15 SECONDS - 2 SECONDS * (user.get_skill_value(SKILL_ELECTRICAL) + user.get_skill_value(SKILL_DEVICES)) + if(!do_after(user, fumbling_time, src)) + return FALSE + user.visible_message(SPAN_NOTICE("[user] begins attaching a module to [src]'s sockets."), + SPAN_NOTICE("You begin installing the [upgrade] into the [src].")) + if(!do_after(user, 10 SECONDS, src)) + return FALSE + if(QDELETED(upgrade)) // It could have somehow happened, idk + return FALSE + user.visible_message(SPAN_NOTICE("[user] installs [upgrade] into the [src]!")) + upgrade.Install(src, user) // Handles the effects of it and puts into the list + playsound(loc,'sound/items/screwdriver.ogg', 25, TRUE) + update_icon() + /obj/machinery/mining/drill/verb/unload() set name = "Unload Drill" @@ -242,13 +346,24 @@ if(usr.stat) return + UnloadTo(get_turf(usr), usr) + +/obj/machinery/mining/drill/proc/UnloadTo(turf/T, mob/living/user) var/obj/structure/ore_box/B = locate() in orange(1) - if(B) - for(var/obj/item/ore/O in contents) + if(B && istype(user)) + for(var/obj/item/ore/O in stored_ores) O.forceMove(B) + stored_ores = list() to_chat(usr, "You unload the drill's storage cache into the ore box.") - else - to_chat(usr, "You must move an ore box up to the drill before you can unload it.") + return + + if(!istype(T)) + T = get_turf(src) + for(var/obj/item/ore/O in stored_ores) + O.forceMove(T) + stored_ores = list() + update_icon() + return /obj/machinery/mining/brace @@ -269,6 +384,8 @@ return ..() /obj/machinery/mining/brace/attackby(obj/item/W as obj, mob/user as mob) + if(!isWrench(W)) + return ..() if(connected && connected.active) to_chat(user, "You can't work with the brace of a running drill!") return TRUE @@ -289,8 +406,9 @@ else disconnect() -/obj/machinery/mining/brace/proc/connect() + return +/obj/machinery/mining/brace/proc/connect() var/turf/T = get_step(get_turf(src), src.dir) for(var/thing in T.contents) diff --git a/code/modules/mining/drilling/drill_upgrades.dm b/code/modules/mining/drilling/drill_upgrades.dm new file mode 100644 index 0000000000000..55c6116ba89aa --- /dev/null +++ b/code/modules/mining/drilling/drill_upgrades.dm @@ -0,0 +1,51 @@ +/obj/item/drill_upgrade + name = "drill modification kit" + desc = "Does nothing. Yell at coders." + icon = 'icons/obj/mining.dmi' + icon_state = "upgrade_automatic" + w_class = ITEM_SIZE_NORMAL + matter = list(MATERIAL_STEEL = 1000, MATERIAL_PLASTEEL = 750, MATERIAL_GLASS = 500, MATERIAL_ALUMINIUM = 250) + /// If more than one can be installed on the same drill + var/allow_multiple = FALSE + +/obj/item/drill_upgrade/proc/Install(obj/machinery/mining/drill/drill, mob/user) + drill.drill_upgrades += src + if(user) + user.unEquip(src, drill) + return + +/obj/item/drill_upgrade/proc/Uninstall(obj/machinery/mining/drill/drill, mob/user) + drill.drill_upgrades -= src + forceMove(get_turf(drill)) + return + +/obj/item/drill_upgrade/proc/ExamineMessage(mob/user, obj/machinery/mining/drill/drill) + return null + +/obj/item/drill_upgrade/auto_dispense + name = "automatic dispenser modification kit" + desc = "A modification kit for the mining drills. This one forces drills to dispense its stored ores whenever at full capacity automatically." + +/obj/item/drill_upgrade/auto_dispense/Install(obj/machinery/mining/drill/drill) + . = ..() + drill.upgrade_flags |= DRILL_AUTOMATIC + +/obj/item/drill_upgrade/auto_dispense/Uninstall(obj/machinery/mining/drill/drill) + . = ..() + drill.upgrade_flags &= ~DRILL_AUTOMATIC + +/obj/item/drill_upgrade/auto_dispense/ExamineMessage(mob/user, obj/machinery/mining/drill/drill) + return SPAN_NOTICE("The drill is fully automated and will dispense ores to the [dir2text(drill.dispense_dir)].") + +/obj/item/drill_upgrade/range_increase + name = "deep mining modification kit" + desc = "A modification kit for the mining drills. This one increases range of drilling by 1." + allow_multiple = TRUE + +/obj/item/drill_upgrade/range_increase/Install(obj/machinery/mining/drill/drill) + . = ..() + drill.resource_area += 1 + +/obj/item/drill_upgrade/range_increase/Uninstall(obj/machinery/mining/drill/drill) + . = ..() + drill.resource_area -= 1 diff --git a/code/modules/mob/grab/abnomination/abom_agressive.dm b/code/modules/mob/grab/abnomination/abom_agressive.dm new file mode 100644 index 0000000000000..a877392b599ef --- /dev/null +++ b/code/modules/mob/grab/abnomination/abom_agressive.dm @@ -0,0 +1,24 @@ +/datum/grab/abomination/aggressive + state_name = GRAB_ABOMINATION_AGGRESSIVE + upgrab_name = GRAB_ABOMINATION_KILL + + shift = 12 + + point_blank_mult = 1.5 + can_throw = 1 + stop_move = 1 + + icon_state = "reinforce1" + + break_chance_table = list(5, 10, 25, 50, 75, 100) + +/datum/grab/abomination/aggressive/process_effect(obj/item/grab/G) + var/mob/living/carbon/human/target = G.affecting + + if(G.target_zone in list(BP_L_HAND, BP_R_HAND)) + target.drop_l_hand() + target.drop_r_hand() + + // Keeps those who are on the ground down + if(target.lying) + target.Weaken(4) diff --git a/code/modules/mob/grab/abnomination/abom_kill.dm b/code/modules/mob/grab/abnomination/abom_kill.dm new file mode 100644 index 0000000000000..8da119fb4fc93 --- /dev/null +++ b/code/modules/mob/grab/abnomination/abom_kill.dm @@ -0,0 +1,30 @@ +/datum/grab/abomination/kill + state_name = GRAB_ABOMINATION_KILL + downgrab_name = GRAB_ABOMINATION_AGGRESSIVE + + shift = 0 + same_tile = 1 + + icon_state = "kill1" + + downgrade_on_action = 1 + downgrade_on_move = 1 + stop_move = 1 + restrains = 1 + + break_chance_table = list(1, 3, 5, 10, 20, 35, 50) + +/datum/grab/abomination/kill/upgrade_effect(obj/item/grab/G) + process_effect(G) + +/datum/grab/abomination/kill/process_effect(obj/item/grab/G) + var/mob/living/carbon/human/user = G.assailant + var/mob/living/carbon/human/target = G.affecting + + target.Stun(3) + + switch(user.a_intent) + if(I_GRAB) + on_hit_grab(G) + if(I_HURT) + on_hit_harm(G) diff --git a/code/modules/mob/grab/abnomination/abom_passive.dm b/code/modules/mob/grab/abnomination/abom_passive.dm new file mode 100644 index 0000000000000..400201925e43f --- /dev/null +++ b/code/modules/mob/grab/abnomination/abom_passive.dm @@ -0,0 +1,21 @@ +/datum/grab/abomination/passive + state_name = GRAB_ABOMINATION_PASSIVE + fancy_desc = "holding" + + upgrab_name = GRAB_ABOMINATION_AGGRESSIVE + + shift = 8 + + point_blank_mult = 1.1 + + icon_state = "reinforce" + + break_chance_table = list(15, 30, 50, 75, 100) + +/datum/grab/abomination/passive/on_hit_grab(obj/item/grab/normal/G) + to_chat(G.assailant, SPAN_WARNING("Your grip isn't strong enough to crush.")) + return FALSE + +/datum/grab/abomination/passive/on_hit_harm(obj/item/grab/normal/G) + to_chat(G.assailant, SPAN_WARNING("Your grip isn't strong enough to devour.")) + return FALSE diff --git a/code/modules/mob/grab/abnomination/grab_abom.dm b/code/modules/mob/grab/abnomination/grab_abom.dm new file mode 100644 index 0000000000000..a03137c2b43cc --- /dev/null +++ b/code/modules/mob/grab/abnomination/grab_abom.dm @@ -0,0 +1,115 @@ +/obj/item/grab/abomination + type_name = GRAB_ABOMINATION + start_grab_name = GRAB_ABOMINATION_PASSIVE + +/obj/item/grab/abomination/init() + . = ..() + if(!.) + return + visible_message("[assailant] has grabbed [affecting] passively!") + +/datum/grab/abomination + type_name = GRAB_ABOMINATION + icon = 'icons/mob/screen1.dmi' + + can_absorb = 1 + point_blank_mult = 1 + ladder_carry = 1 + force_danger = 1 + can_grab_self = 0 + +/datum/grab/abomination/on_hit_grab(obj/item/grab/G) + var/mob/living/carbon/human/user = G.assailant + var/mob/living/carbon/human/target = G.affecting + + target.visible_message( + SPAN_DANGER("[user] begins crushing [target]!"), + SPAN_DANGER("[user] is trying to crush your bones!"), + ) + G.attacking = TRUE + if(do_after(user, action_cooldown * 0.5, target)) + G.attacking = FALSE + G.action_used() + Crush(G) + return TRUE + else + G.attacking = FALSE + target.visible_message(SPAN_WARNING("[user] stops crushing [target]!")) + return FALSE + +/datum/grab/abomination/on_hit_harm(obj/item/grab/G) + var/mob/living/carbon/human/user = G.assailant + var/mob/living/carbon/human/target = G.affecting + var/obj/item/organ/external/damaging = target.get_organ(user.zone_sel.selecting) + + if(!istype(damaging)) + to_chat(user, SPAN_WARNING("This limb is missing!")) + return FALSE + + target.visible_message( + SPAN_DANGER("[user] begins eating [target]'s [damaging.name]!"), + SPAN_DANGER("[user] is eating your [damaging.name]!"), + ) + G.attacking = TRUE + + if(do_after(user, action_cooldown, target)) + G.attacking = FALSE + G.action_used() + Devour(G) + return TRUE + else + G.attacking = FALSE + target.visible_message(SPAN_WARNING("[user] stops eating [target]'s limbs.")) + return FALSE + +// This causes the user to temporarily stun and weaken the target, dealing pain and slight brute damage +/datum/grab/abomination/proc/Crush(obj/item/grab/G) + var/mob/living/carbon/human/user = G.assailant + var/mob/living/carbon/human/target = G.affecting + var/hit_zone = user.zone_sel.selecting + var/obj/item/organ/external/damaging = target.get_organ(hit_zone) + + target.visible_message(SPAN_DANGER("[user] crushes [target]'s [damaging.name]!")) + target.custom_pain("You feel your bones painfuly compress in [damaging.name]!", 50, affecting = damaging) + target.apply_damage(4, DAMAGE_BRUTE, hit_zone, used_weapon = "crushing") + target.Stun(10) + target.Weaken(15) + playsound(get_turf(target), 'sound/weapons/pierce.ogg', 25, TRUE, -3) + + admin_attack_log(user, target, "Crushed their victim.", "Was crushed.", "crushed") + +// This causes the user to heavily damage targeted limb while also restoring its own nutrition +/datum/grab/abomination/proc/Devour(obj/item/grab/G) + var/mob/living/carbon/human/user = G.assailant + var/mob/living/carbon/human/target = G.affecting + var/hit_zone = user.zone_sel.selecting + var/obj/item/organ/external/damaging = target.get_organ(hit_zone) + + if(!istype(damaging)) + to_chat(user, SPAN_WARNING("This limb is missing!")) + return + + if(!damaging.how_open()) + damaging.createwound(INJURY_TYPE_CUT, damaging.min_broken_damage*0.75, 1) + playsound(get_turf(target), 'sound/weapons/alien_tail_attack.ogg', 50, TRUE, 5) + target.visible_message( + SPAN_DANGER("[user] cuts [target]'s [damaging.name] open!"), + SPAN_DANGER("[user] cuts your [damaging.name] open!"), + ) + admin_attack_log(user, target, "Cuts open their victim.", "Has been cut.", "cut") + return + + if(user.get_fullness() > 380) // Just slash them + target.apply_damage(rand(16, 24), DAMAGE_BRUTE, hit_zone, used_weapon = "claws") + target.visible_message(SPAN_DANGER("[user] slashes [target]'s [damaging.name]!")) + playsound(get_turf(target), 'sound/weapons/alien_claw_flesh3.ogg', 25, TRUE) + else // Food! + var/datum/reagents/R = new /datum/reagents(3, GLOB.temp_reagents_holder) + R.add_reagent(/datum/reagent/nutriment, 3) + R.trans_to_mob(user, 3, CHEM_INGEST) + qdel(R) + target.apply_damage(rand(8, 14), DAMAGE_BRUTE, hit_zone, used_weapon = "fangs") + target.visible_message(SPAN_DANGER("[user] eats [target]'s [damaging.name]!")) + playsound(get_turf(target), 'sound/weapons/alien_claw_flesh1.ogg', 25, TRUE) + + admin_attack_log(user, target, "Devours their victim.", "Was chewed.", "chewed") diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm index ec7c2de465599..2538ab0ca6dfc 100644 --- a/code/modules/mob/living/simple_animal/borer/borer.dm +++ b/code/modules/mob/living/simple_animal/borer/borer.dm @@ -289,3 +289,9 @@ /mob/living/simple_animal/borer/flash_eyes(intensity, override_blindness_check, affect_silicon, visual, type) intensity *= 1.5 . = ..() + +/mob/living/simple_animal/borer/ImplantRemoval() + if(controlling) + host.release_control() + detatch() + leave_host() diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/_infestation.dm b/code/modules/mob/living/simple_animal/hostile/infestation/_infestation.dm new file mode 100644 index 0000000000000..bb9ac7010f6d8 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/_infestation.dm @@ -0,0 +1,99 @@ +/mob/living/simple_animal/hostile/infestation + name = "abominable infestation" + desc = "AUGH! A BUG! A LITERAL ONE! REPORT TO CODERS BEFORE IT SPREADS!" + icon = 'icons/mob/simple_animal/abominable_infestation/32x32.dmi' + // Icon states for egg + var/icon_egg = "egg" + var/icon_egg_dead = "egg_dead" + faction = "abominable_infestation" + color = "#c84444" + bleed_colour = COLOR_MAROON + see_in_dark = 5 + min_gas = null + max_gas = null + minbodytemp = 0 + status_flags = CANPUSH // Cannot be stunned or otherwise incapacitated + melee_attack_delay = 0 + /// Reference to structure that effectively controls the colony + var/overmind = null + /// World time at which we will begin transforming into another mob + var/transformation_time + /// Assoc list of potential transformations if none are set by outer forces + // Type = Time + var/list/transformation_types = list() + /// Which mob we will be transformed into + var/transformation_target_type = null + /// If TRUE - will evolve despite having a target mob + var/ignore_combat = FALSE + +/mob/living/simple_animal/hostile/infestation/Life() + . = ..() + if(!.) + return + if(isnull(transformation_time)) + return + if(isnull(transformation_target_type) && !LAZYLEN(transformation_types)) + return + if(world.time >= transformation_time) + if(istype(loc, /obj/item/organ/external)) + var/obj/item/organ/external/O = loc + if(O.owner) + to_chat(O.owner, SPAN_DANGER("Something has wriggled out of your body!")) + O.createwound(INJURY_TYPE_PIERCE, O.min_broken_damage, FALSE) + O.owner.Weaken(5) // Gives some time for larva to turn into egg + O.owner.apply_damage(15, DAMAGE_BRUTE, O.organ_tag) + O.implants -= src + forceMove(get_turf(O)) + playsound(src, 'sound/effects/splat.ogg', 50, TRUE) + return + if(icon_state != icon_egg) // Become egg + // We're in combat, forget evolving for a moment + if(!ignore_combat && ai_holder.target) + transformation_time = world.time + 10 SECONDS + return + BecomeEgg() + return + Evolve() + +/mob/living/simple_animal/hostile/infestation/death() + if(icon_state == icon_egg) + animate(src, alpha = 0, time = (5 SECONDS)) + QDEL_IN(src, (5 SECONDS)) + return ..() + + +/mob/living/simple_animal/hostile/infestation/proc/BecomeEgg() + name = "egg" + desc = "A weird egg..?" + icon_state = icon_egg + icon_living = icon_egg + icon_dead = icon_egg_dead + anchored = TRUE + QDEL_NULL(ai_holder) + QDEL_NULL(say_list) + ai_holder = null + say_list = null + death_sounds = list() + maxHealth = max(200, maxHealth * 2) + health = maxHealth + if(isnull(transformation_target_type) && LAZYLEN(transformation_types)) + transformation_target_type = pick(transformation_types) + if(transformation_target_type in transformation_types) + transformation_time = world.time + transformation_types[transformation_target_type] + return + transformation_time = world.time + rand(45 SECONDS, 60 SECONDS) + +/mob/living/simple_animal/hostile/infestation/proc/Evolve() + var/mob/living/simple_animal/broodling = new transformation_target_type(get_turf(src)) + broodling.color = color + broodling.faction = faction + if(ckey) // We're player controlled + broodling.ckey = ckey + gib() + +// Shared AI behavior +/datum/ai_holder/simple_animal/infestation + hostile = TRUE + retaliate = TRUE + cooperative = TRUE + respect_confusion = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/assembler.dm b/code/modules/mob/living/simple_animal/hostile/infestation/assembler.dm new file mode 100644 index 0000000000000..f2187d90d78e4 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/assembler.dm @@ -0,0 +1,208 @@ +/mob/living/simple_animal/hostile/infestation/assembler + name = "assembler" + desc = "A large monstrosity with many appendages that it uses to 'assemble' things." + icon = 'icons/mob/simple_animal/abominable_infestation/48x48.dmi' + icon_state = "assembler" + icon_living = "assembler" + icon_dead = "assembler_dead" + mob_size = MOB_MEDIUM + move_to_delay = 3.5 + default_pixel_x = -8 + pixel_x = -8 + + natural_weapon = /obj/item/natural_weapon/assembler + + health = 200 + maxHealth = 200 + + move_to_delay = 5 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 8 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 2 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 2 + + ai_holder = /datum/ai_holder/simple_animal/infestation/assembler + //say_list_type = /datum/say_list/infestation_assembler + death_sounds = list( + 'sound/simple_mob/abominable_infestation/eviscerator/death_1.ogg', + 'sound/simple_mob/abominable_infestation/eviscerator/death_2.ogg', + ) + + // Organs add 1; Non-human mobs add 5. + var/nutrient_stored = 0 + /// Assoc list Type = Nutrient Required; This is responsible for forcing larva's evolution target type + var/larva_types = list( + /mob/living/simple_animal/hostile/infestation/broodling = 4, + /mob/living/simple_animal/hostile/infestation/floatfly = 6, + /mob/living/simple_animal/hostile/infestation/spitter = 8, + /mob/living/simple_animal/hostile/infestation/eviscerator = 12, + /mob/living/simple_animal/hostile/infestation/assembler = 16, + ) + /// Not actually the limit, but the point where it will forcefully spawn larva without waiting for time + var/nutrient_max = 25 + /// World time by which we will spawn an larva if we have no target + var/larva_time + +/obj/item/natural_weapon/assembler + name = "sharp tentacle" + attack_verb = list("stabbed", "pierced") + force = 20 + armor_penetration = 10 + hitsound = 'sound/weapons/rapidslice.ogg' + sharp = TRUE + edge = TRUE + + +/datum/ai_holder/simple_animal/infestation/assembler + cooperative = FALSE + mauling = TRUE + handle_corpse = TRUE + returns_home = FALSE + home_low_priority = TRUE + speak_chance = 1 + wander = TRUE + base_wander_delay = 20 + +/datum/ai_holder/simple_animal/infestation/assembler/list_targets() + . = ..() + + var/static/alternative_targets = typecacheof(list(/obj/item/organ)) + for(var/obj/item/organ/O in typecache_filter_list(range(vision_range, holder), alternative_targets)) + if(can_see(holder, O, vision_range) && !BP_IS_ROBOTIC(O)) + . += O + +/datum/ai_holder/simple_animal/infestation/assembler/pick_target(list/targets) + var/mobs_only = locate(/mob/living) in targets // If a mob is in the list of targets, then ignore objects. + if(mobs_only) + for(var/A in targets) + if(!isliving(A)) + targets -= A + + return ..(targets) + +/mob/living/simple_animal/hostile/infestation/assembler/Initialize() + . = ..() + larva_time = world.time + 10 SECONDS + +/mob/living/simple_animal/hostile/infestation/assembler/Life() + . = ..() + if(!.) + return + if(nutrient_stored >= nutrient_max || world.time >= larva_time) + AttemptLarva() + return + +/mob/living/simple_animal/hostile/infestation/assembler/attack_target(atom/A) + return UnarmedAttack(A) + +/mob/living/simple_animal/hostile/infestation/assembler/UnarmedAttack(atom/A) + + if(istype(A, /obj/item/organ)) + ConsumeOrgan(A) + ai_holder.target = null + return + + if(isliving(A)) + var/mob/living/L = A + if(L.stat) + ConsumeDead(L) + return + + return ..() + +/mob/living/simple_animal/hostile/infestation/assembler/proc/ConsumeOrgan(obj/item/organ/O) + visible_message(SPAN_WARNING("[src] consumes \the [O].")) + qdel(O) + nutrient_stored += 1 + larva_time = world.time + 5 SECONDS + +/mob/living/simple_animal/hostile/infestation/assembler/proc/ConsumeDead(mob/living/L) + if(ishuman(L)) + ConsumeHuman(L) + return + + visible_message(SPAN_DANGER("[src] starts to consume \the [L]!")) + playsound(src, 'sound/simple_mob/abominable_infestation/assembler/ambient_1.ogg', 50, TRUE) + set_AI_busy(TRUE) + + if(!do_after(src, min(10, L.mob_size) SECONDS, L)) + set_AI_busy(FALSE) + return FALSE + + set_AI_busy(FALSE) + visible_message(SPAN_DANGER("[src] consumes \the [L]!")) + nutrient_stored += min(10, L.mob_size) + larva_time = world.time + 10 SECONDS + L.gib() + +/mob/living/simple_animal/hostile/infestation/assembler/proc/ConsumeHuman(mob/living/carbon/human/H) + visible_message(SPAN_DANGER("[src] starts to tear [H] apart!")) + playsound(src, 'sound/simple_mob/abominable_infestation/assembler/ambient_1.ogg', 50, TRUE) + set_AI_busy(TRUE) + + if(!do_after(src, 3 SECONDS, H)) + set_AI_busy(FALSE) + return FALSE + + if(QDELETED(H)) + set_AI_busy(FALSE) + return FALSE + + var/list/potential_organs = list() + for(var/obj/item/organ/external/O in H.organs) + if(istype(O, /obj/item/organ/external/stump)) + continue + if(O.organ_tag in (BP_LEGS_FEET | BP_ARMS_HANDS)) + potential_organs += O + continue + if(!LAZYLEN(potential_organs) && (O.organ_tag in list(BP_HEAD, BP_GROIN))) + potential_organs += O + continue + + if(!LAZYLEN(potential_organs)) // Most likely only chest left + for(var/obj/item/organ/internal/I in H.internal_organs) + I.removed() + I.forceMove(get_turf(H)) + if(!QDELETED(I) && isturf(loc)) + I.throw_at(get_edge_target_turf(get_turf(H), pick(GLOB.alldirs)), rand(1,2), 5) + H.gib() + set_AI_busy(FALSE) + return + + var/obj/item/organ/external/target_organ = pick(potential_organs) + target_organ.droplimb(FALSE, DROPLIMB_EDGE, FALSE, FALSE) + for(var/obj/item/organ/I in target_organ.internal_organs) + I.removed() + I.forceMove(get_turf(target_organ)) + if(!QDELETED(I) && isturf(loc)) + I.throw_at(get_edge_target_turf(target_organ, pick(GLOB.alldirs)), rand(1,2), 5) + + larva_time = world.time + 5 SECONDS + set_AI_busy(FALSE) + +/mob/living/simple_animal/hostile/infestation/assembler/proc/AttemptLarva(forced_type = null) + if(ai_holder.target) + return + + larva_time = world.time + 5 SECONDS + var/target_type = forced_type + if(isnull(target_type)) + for(var/thing_type in larva_types) + if(nutrient_stored < larva_types[thing_type]) + break + target_type = thing_type + + if(isnull(target_type)) + return + + var/mob/living/simple_animal/hostile/infestation/larva/L = new(get_turf(src)) + L.transformation_target_type = target_type + L.transformation_time = world.time + 30 SECONDS // Assembled larvas hatch faster + L.color = color + L.faction = faction + playsound(L, 'sound/simple_mob/abominable_infestation/larva/spawn.ogg', rand(35, 50), TRUE) + visible_message(SPAN_WARNING("[src] assembles a new [L.name]!")) + nutrient_stored -= larva_types[target_type] diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/broodling.dm b/code/modules/mob/living/simple_animal/hostile/infestation/broodling.dm new file mode 100644 index 0000000000000..7985b0a918428 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/broodling.dm @@ -0,0 +1,56 @@ +/mob/living/simple_animal/hostile/infestation/broodling + name = "broodling" + desc = "An abominable creature, fast and vicious." + icon_state = "broodling" + icon_living = "broodling" + icon_dead = "broodling_dead" + mob_size = MOB_SMALL + move_to_delay = 2 + + natural_weapon = /obj/item/natural_weapon/claws/broodling + melee_attack_delay = 0 + + health = 100 + maxHealth = 100 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 2 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 1 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 1 + + ai_holder = /datum/ai_holder/simple_animal/infestation/broodling + say_list_type = /datum/say_list/infestation_broodling + death_sounds = list('sound/simple_mob/abominable_infestation/broodling/death.ogg') + +/obj/item/natural_weapon/claws/broodling + force = 7 + armor_penetration = 10 + hitsound = 'sound/weapons/slashmiss.ogg' + attack_cooldown = 2 + +/datum/say_list/infestation_broodling + emote_hear = list("hisses", "attempts to bark", "breathes heavily", "gurgles") + emote_see = list("jumps from place to place") + + emote_hear_sounds = list( + 'sound/simple_mob/abominable_infestation/broodling/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/broodling/ambient_2.ogg', + ) + emote_see_sounds = list( + 'sound/simple_mob/abominable_infestation/broodling/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/broodling/ambient_2.ogg', + ) + +/datum/ai_holder/simple_animal/infestation/broodling + returns_home = FALSE + home_low_priority = TRUE + speak_chance = 3 + wander = TRUE + base_wander_delay = 2 + +/datum/ai_holder/simple_animal/infestation/broodling/post_melee_attack(atom/A) + if(holder.Adjacent(A)) + holder.IMove(get_step(holder, pick(GLOB.alldirs))) + holder.face_atom(A) diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/eviscerator.dm b/code/modules/mob/living/simple_animal/hostile/infestation/eviscerator.dm new file mode 100644 index 0000000000000..9aa45f59c0d52 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/eviscerator.dm @@ -0,0 +1,63 @@ +/mob/living/simple_animal/hostile/infestation/eviscerator + name = "eviscerator" + desc = "A large monster with very sharp spear-like limbs. You better start running..." + icon = 'icons/mob/simple_animal/abominable_infestation/48x48.dmi' + icon_state = "eviscerator" + icon_living = "eviscerator" + icon_dead = "eviscerator_dead" + mob_size = MOB_MEDIUM + move_to_delay = 3.5 + default_pixel_x = -8 + pixel_x = -8 + + natural_weapon = /obj/item/natural_weapon/claws/strong/eviscerator + + health = 300 + maxHealth = 300 + + move_to_delay = 3.5 + movement_sound = 'sound/simple_mob/abominable_infestation/eviscerator/step.ogg' + movement_shake_radius = 2 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 6 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 4 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 4 + + ai_holder = /datum/ai_holder/simple_animal/infestation/eviscerator + //say_list_type = /datum/say_list/infestation_eviscerator + death_sounds = list( + 'sound/simple_mob/abominable_infestation/eviscerator/death_1.ogg', + 'sound/simple_mob/abominable_infestation/eviscerator/death_2.ogg', + ) + + transformation_types = list( + /mob/living/simple_animal/hostile/infestation/rhino = 150 SECONDS, + ) + +/obj/item/natural_weapon/claws/strong/eviscerator + armor_penetration = 10 + hitsound = 'sound/simple_mob/abominable_infestation/eviscerator/attack.ogg' + +/datum/ai_holder/simple_animal/infestation/eviscerator + returns_home = FALSE + home_low_priority = TRUE + speak_chance = 1 + wander = TRUE + base_wander_delay = 20 + var/list/aggro_sounds = list( + 'sound/simple_mob/abominable_infestation/eviscerator/aggro_1.ogg', + 'sound/simple_mob/abominable_infestation/eviscerator/aggro_2.ogg', + 'sound/simple_mob/abominable_infestation/eviscerator/aggro_3.ogg', + ) + +/datum/ai_holder/simple_animal/infestation/eviscerator/give_target(new_target, urgent = FALSE) + . = ..() + if(. && prob(30)) + playsound(holder, pick(aggro_sounds), rand(35,75), TRUE) + +/mob/living/simple_animal/hostile/infestation/eviscerator/Initialize() + . = ..() + transformation_time = world.time + rand(120 SECONDS, 240 SECONDS) diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/floatfly.dm b/code/modules/mob/living/simple_animal/hostile/infestation/floatfly.dm new file mode 100644 index 0000000000000..13edb5b3233f3 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/floatfly.dm @@ -0,0 +1,86 @@ +// Medium damage, Low health, Very high mobility +// Ignores gravity and can "fly", temporarily turning non-dense +// When attacked, can change its pixel position slightly, making it more difficult to hit + +/mob/living/simple_animal/hostile/infestation/floatfly + name = "floatfly" + desc = "A flesh creature resembling a big fly." + icon_state = "fly" + icon_living = "fly" + icon_dead = "fly_dead" + mob_size = MOB_SMALL + movement_cooldown = 2.5 + + natural_weapon = /obj/item/natural_weapon/claws/floatfly + + health = 80 + maxHealth = 80 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 3 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 1 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 1 + + ai_holder = /datum/ai_holder/simple_animal/infestation/floatfly + say_list_type = /datum/say_list/infestation_floatfly + death_sounds = list('sound/simple_mob/abominable_infestation/floatfly/death.ogg') + + var/fly_cooldown + var/fly_cooldown_time = 5 SECONDS + var/fly_duration = 5 SECONDS + +/obj/item/natural_weapon/claws/floatfly + force = 14 + armor_penetration = 10 + hitsound = 'sound/weapons/alien_claw_flesh1.ogg' + +/datum/say_list/infestation_floatfly + emote_hear = list("buzzes", "hisses") + emote_see = list("flies all over the place") + + emote_hear_sounds = list() + emote_see_sounds = list() + +/mob/living/simple_animal/hostile/infestation/floatfly/death() + animate(src, pixel_z = 0, time = 3) + return ..() + +/mob/living/simple_animal/hostile/infestation/floatfly/adjustBruteLoss(amount) + . = ..() + if(world.time > fly_cooldown && prob(amount * 5)) + animate(src, pixel_x = default_pixel_x + rand(-10, 10), pixel_y = default_pixel_y + rand(-10, 10), time = 2) + + +/mob/living/simple_animal/hostile/infestation/floatfly/proc/StartFlight() + if(!density || fly_cooldown >= world.time) + return FALSE + density = FALSE + playsound(src, 'sound/simple_mob/abominable_infestation/floatfly/fly.ogg', 75, TRUE, 6) + visible_message(SPAN_DANGER("\The [src] flies upwards!")) + animate(src, pixel_z = 16, time = 5) + default_pixel_z = 16 + addtimer(CALLBACK(src, .proc/EndFlight), fly_duration) + +/mob/living/simple_animal/hostile/infestation/floatfly/proc/EndFlight() + if(QDELETED(src) || stat == DEAD) + return FALSE + fly_cooldown = world.time + fly_cooldown_time + density = TRUE + visible_message(SPAN_WARNING("\The [src] stops its flight.")) + animate(src, pixel_z = 0, time = 5) + default_pixel_z = 0 + +/datum/ai_holder/simple_animal/infestation/floatfly + returns_home = FALSE + home_low_priority = TRUE + speak_chance = 3 + wander = TRUE + base_wander_delay = 1 + +/datum/ai_holder/simple_animal/infestation/floatfly/give_target(new_target, urgent = FALSE) + . = ..() + var/mob/living/simple_animal/hostile/infestation/floatfly/F = holder + if(. && prob(25) && world.time > F.fly_cooldown) + F.StartFlight() diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/larva.dm b/code/modules/mob/living/simple_animal/hostile/infestation/larva.dm new file mode 100644 index 0000000000000..6ff6480133b3a --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/larva.dm @@ -0,0 +1,133 @@ +/mob/living/simple_animal/hostile/infestation/larva + name = "larva" + desc = "A weird insect-like creature." + icon_state = "larva" + icon_living = "larva" + icon_dead = "larva_dead" + response_help = "pets" + response_disarm = "pushes aside" + response_harm = "stomps on" + destroy_surroundings = FALSE + mob_size = MOB_MINISCULE + density = FALSE + layer = LYING_MOB_LAYER + speak_emote = list("gurgles") + pass_flags = PASS_FLAG_TABLE | PASS_FLAG_GRILLE + move_to_delay = 1.5 + + health = 20 + maxHealth = 20 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 1 + + ai_holder = /datum/ai_holder/simple_animal/infestation/larva + say_list_type = /datum/say_list/infestation_larva + death_sounds = list( + 'sound/simple_mob/abominable_infestation/larva/death_1.ogg', + 'sound/simple_mob/abominable_infestation/larva/death_2.ogg', + ) + + transformation_types = list( + /mob/living/simple_animal/hostile/infestation/broodling = 30 SECONDS, + /mob/living/simple_animal/hostile/infestation/spitter = 45 SECONDS, + /mob/living/simple_animal/hostile/infestation/eviscerator = 60 SECONDS, + /mob/living/simple_animal/hostile/infestation/assembler = 75 SECONDS, + ) + ignore_combat = TRUE + +/datum/say_list/infestation_larva + emote_hear = list("gurgles", "hisses", "attempts to make a sound") + emote_see = list("wriggles around") + + emote_hear_sounds = list( + 'sound/simple_mob/abominable_infestation/larva/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/larva/ambient_2.ogg', + 'sound/simple_mob/abominable_infestation/larva/ambient_3.ogg', + ) + emote_see_sounds = list( + 'sound/simple_mob/abominable_infestation/larva/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/larva/ambient_2.ogg', + 'sound/simple_mob/abominable_infestation/larva/ambient_3.ogg', + ) + +/datum/ai_holder/simple_animal/infestation/larva + hostile = FALSE + retaliate = FALSE + returns_home = FALSE + can_flee = TRUE + speak_chance = 4 + wander = TRUE + base_wander_delay = 4 + +/mob/living/simple_animal/hostile/infestation/larva/Initialize() + . = ..() + transformation_time = world.time + rand(60 SECONDS, 90 SECONDS) + + +// Neutral faction and slightly different color +/mob/living/simple_animal/hostile/infestation/larva/friendly + faction = "neutral" + color = "#c87d44" + transformation_types = list( + /mob/living/simple_animal/hostile/infestation/broodling = 60 SECONDS, + /mob/living/simple_animal/hostile/infestation/eviscerator = 90 SECONDS, + /mob/living/simple_animal/hostile/infestation/assembler = 120 SECONDS, + ) + +// Implanted subtype of larva transforms much faster +/mob/living/simple_animal/hostile/infestation/larva/implant/BecomeEgg() + . = ..() + transformation_time = world.time + rand(5 SECONDS, 10 SECONDS) + +/mob/living/simple_animal/hostile/infestation/larva/implant/ImplantRemoval() + playsound(src, pick(say_list.emote_hear_sounds), 50, TRUE) + transformation_time = world.time + rand(10 SECONDS, 15 SECONDS) + +// Aggressive larva that will rush to humans and implant into them +/mob/living/simple_animal/hostile/infestation/larva/implant/implanter + icon_state = "larva_implanter" + icon_living = "larva_implanter" + ai_holder = /datum/ai_holder/simple_animal/infestation/larva/implanter + +/datum/ai_holder/simple_animal/infestation/larva/implanter + hostile = TRUE + retaliate = TRUE + can_flee = FALSE + +/datum/ai_holder/simple_animal/infestation/larva/implanter/list_targets() + var/mob/living/simple_animal/hostile/infestation/larva/implant/implanter/L = holder + if(L.transformation_time != null) // Already implanted once + return + + var/list/humans = list() + for(var/mob/living/carbon/human/H in view(vision_range, holder)) + humans += H + + return humans + +/mob/living/simple_animal/hostile/infestation/larva/implant/implanter/Initialize() + . = ..() + transformation_time = null // We only evolve after implanting ourselves + +/mob/living/simple_animal/hostile/infestation/larva/implant/implanter/attack_target(atom/A) + var/mob/living/carbon/human/H = A + var/list/valid_organs = list() + for(var/obj/item/organ/external/O in H.organs) + if(istype(O, /obj/item/organ/external/stump)) + continue + if(locate(/mob/living/simple_animal/hostile/infestation/larva) in O.implants) + continue + valid_organs += O + + if(!LAZYLEN(valid_organs)) + return + + visible_message(SPAN_DANGER("[src] bites through [H]'s clothes and skin and wriggles inside!")) + playsound(src, 'sound/simple_mob/abominable_infestation/larva/implant.ogg', 50, TRUE) + var/obj/item/organ/external/target_organ = pick(valid_organs) + target_organ.owner.apply_damage(15, DAMAGE_BRUTE, target_organ.organ_tag) + forceMove(target_organ) + target_organ.implants += src + transformation_time = world.time + rand(120 SECONDS, 240 SECONDS) + ai_holder.speak_chance = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/rhino.dm b/code/modules/mob/living/simple_animal/hostile/infestation/rhino.dm new file mode 100644 index 0000000000000..74386f0fcfeb2 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/rhino.dm @@ -0,0 +1,71 @@ +/mob/living/simple_animal/hostile/infestation/rhino + name = "rhino" + desc = "A large heavily-armored monster." + icon = 'icons/mob/simple_animal/abominable_infestation/48x48.dmi' + icon_state = "rhino" + icon_living = "rhino" + icon_dead = "rhino_dead" + mob_size = MOB_MEDIUM + default_pixel_x = -8 + pixel_x = -8 + + natural_weapon = /obj/item/natural_weapon/hooves/rhino + + health = 1000 + maxHealth = 1000 + resistance = 15 + + movement_cooldown = 5 + movement_sound = 'sound/simple_mob/abominable_infestation/rhino/step.ogg' + movement_shake_radius = 3 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 6 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 8 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 8 + + ai_holder = /datum/ai_holder/simple_animal/infestation/rhino + death_sounds = list('sound/simple_mob/abominable_infestation/rhino/death.ogg') + + /// Grants increased movement speed + var/enraged = FALSE + var/enraged_end_time + var/enraged_cooldown + var/enraged_cooldown_time = 20 SECONDS + var/enraged_movement_sound = 'sound/simple_mob/abominable_infestation/rhino/step_angry.ogg' + +/obj/item/natural_weapon/hooves/rhino + force = 50 + armor_penetration = 10 + hitsound = 'sound/weapons/heavysmash.ogg' + +/mob/living/simple_animal/hostile/infestation/rhino/Life() + . = ..() + if(!.) + return + if(enraged && world.time > enraged_end_time) + enraged = FALSE + visible_message(SPAN_WARNING("\The [src] calms down.")) + +/mob/living/simple_animal/hostile/infestation/rhino/movement_delay() + . = ..() + . -= enraged * 2 + + +/datum/ai_holder/simple_animal/infestation/rhino + returns_home = TRUE + home_low_priority = TRUE + wander = TRUE + base_wander_delay = 10 + +/datum/ai_holder/simple_animal/infestation/rhino/give_target(new_target, urgent = FALSE) + . = ..() + var/mob/living/simple_animal/hostile/infestation/rhino/R = holder + if(. && prob(25) && world.time > R.enraged_cooldown) + R.enraged_cooldown = world.time + R.enraged_cooldown_time + R.enraged_end_time = world.time + 10 SECONDS + R.enraged = TRUE + playsound(holder, 'sound/simple_mob/abominable_infestation/rhino/roar.ogg', 75, TRUE, 6) + holder.visible_message(SPAN_DANGER("\The [holder] charges at [new_target]!")) diff --git a/code/modules/mob/living/simple_animal/hostile/infestation/spitter.dm b/code/modules/mob/living/simple_animal/hostile/infestation/spitter.dm new file mode 100644 index 0000000000000..a6d630120d8c7 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/infestation/spitter.dm @@ -0,0 +1,54 @@ +/mob/living/simple_animal/hostile/infestation/spitter + name = "spitter" + desc = "A weird wriggling creature. Some sort of corrosive substance is dripping from its maw." + icon_state = "spitter" + icon_living = "spitter" + icon_dead = "spitter_dead" + mob_size = MOB_SMALL + move_to_delay = 3 + + natural_weapon = /obj/item/natural_weapon/bite/abomination_spitter + + base_attack_cooldown = 1 SECONDS + ranged = TRUE + projectiletype = /obj/item/projectile/energy/acid_spit + projectilesound = 'sound/weapons/alien_spit.ogg' + fire_desc = "spits acid" + ranged_range = 7 + + health = 150 + maxHealth = 150 + + meat_type = /obj/item/reagent_containers/food/snacks/abominationmeat + meat_amount = 4 + skin_material = MATERIAL_SKIN_CHITIN + skin_amount = 2 + bone_material = MATERIAL_BONE_CARTILAGE + bone_amount = 2 + + ai_holder = /datum/ai_holder/simple_animal/infestation/spitter + say_list_type = /datum/say_list/infestation_spitter + death_sounds = list('sound/simple_mob/abominable_infestation/spitter/death.ogg') + +/obj/item/natural_weapon/bite/abomination_spitter + hitsound = 'sound/simple_mob/abominable_infestation/spitter/attack.ogg' + +/datum/say_list/infestation_spitter + emote_hear = list("gurgles") + emote_see = list("looks around", "wriggles around") + + emote_hear_sounds = list( + 'sound/simple_mob/abominable_infestation/spitter/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/spitter/ambient_2.ogg', + ) + emote_see_sounds = list( + 'sound/simple_mob/abominable_infestation/spitter/ambient_1.ogg', + 'sound/simple_mob/abominable_infestation/spitter/ambient_2.ogg', + ) + +/datum/ai_holder/simple_animal/infestation/spitter + returns_home = FALSE + home_low_priority = TRUE + speak_chance = 2 + wander = TRUE + base_wander_delay = 15 diff --git a/code/modules/mob/living/simple_animal/life.dm b/code/modules/mob/living/simple_animal/life.dm index 0411270a7f39f..9500fc7121200 100644 --- a/code/modules/mob/living/simple_animal/life.dm +++ b/code/modules/mob/living/simple_animal/life.dm @@ -8,7 +8,7 @@ icon_state = icon_living switch_from_dead_to_living_mob_list() set_stat(CONSCIOUS) - set_density(1) + set_density(initial(density)) return 0 handle_atmos() diff --git a/code/modules/mob/living/simple_animal/natural_weapons.dm b/code/modules/mob/living/simple_animal/natural_weapons.dm index a5b2c6e4967d3..262217d31270b 100644 --- a/code/modules/mob/living/simple_animal/natural_weapons.dm +++ b/code/modules/mob/living/simple_animal/natural_weapons.dm @@ -30,6 +30,9 @@ attack_verb = list("nibbled") hitsound = null +/obj/item/natural_weapon/bite/abomination_spitter + hitsound = 'sound/simple_mob/abominable_infestation/spitter/attack.ogg' + /obj/item/natural_weapon/bite/strong force = 25 @@ -40,9 +43,17 @@ sharp = TRUE edge = TRUE +/obj/item/natural_weapon/claws/broodling + force = 4 + hitsound = 'sound/weapons/slashmiss.ogg' + attack_cooldown = 2 + /obj/item/natural_weapon/claws/strong force = 25 +/obj/item/natural_weapon/claws/strong/eviscerator + hitsound = 'sound/simple_mob/abominable_infestation/eviscerator/attack.ogg' + /obj/item/natural_weapon/claws/weak force = 5 attack_verb = list("clawed", "scratched") diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 999533259b5f0..8e56de0eb6e13 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -152,6 +152,9 @@ var/return_damage_min var/return_damage_max +// List of potential death sounds, if any + var/list/death_sounds = list() + /mob/living/simple_animal/Initialize() . = ..() if(LAZYLEN(natural_armor)) @@ -175,8 +178,11 @@ density = FALSE adjustBruteLoss(maxHealth) //Make sure dey dead. walk_to(src,0) + if(LAZYLEN(death_sounds)) + playsound(src, pick(death_sounds), 50, TRUE) return ..(gibbed,deathmessage,show_dead_message) + /mob/living/simple_animal/ex_act(severity) if (status_flags & GODMODE) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e97b9e7f003a3..c3924a1296706 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -210,7 +210,7 @@ if(pulling) if(istype(pulling, /obj)) var/obj/O = pulling - . += clamp(O.w_class, 0, ITEM_SIZE_GARGANTUAN) / 5 + . += O.get_additional_speed_decrease() else if(istype(pulling, /mob)) var/mob/M = pulling . += max(0, M.mob_size) / MOB_MEDIUM @@ -887,7 +887,7 @@ implant.update_icon() if(istype(implant,/obj/item/implant)) var/obj/item/implant/imp = implant - imp.removed() + imp.ImplantRemoval() if (istype(implant, /obj/item/holder/voxslug)) var/obj/item/holder/voxslug/holder = implant var/mob/living/simple_animal/hostile/voxslug/V = holder.contents[1] diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 1d14c63d814da..e00f92b1b4182 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -1252,7 +1252,7 @@ Note that amputating the affected organ does in fact remove the infection from t // let actual implants still inside know they're no longer implanted if(istype(I, /obj/item/implant)) var/obj/item/implant/imp_device = I - imp_device.removed() + imp_device.ImplantRemoval() else implants.Remove(implant) implant.forceMove(get_turf(src)) diff --git a/code/modules/organs/internal/species/abomination.dm b/code/modules/organs/internal/species/abomination.dm new file mode 100644 index 0000000000000..932fe7c613300 --- /dev/null +++ b/code/modules/organs/internal/species/abomination.dm @@ -0,0 +1,57 @@ +/obj/item/organ/internal/heart/abomination + name = "blood pump" + +/obj/item/organ/internal/stomach/abomination + name = "nutrient refinery" + +/obj/item/organ/internal/lungs/abomination + name = "gas pump" + +/obj/item/organ/internal/liver/abomination + name = "toxin filter" + +/obj/item/organ/internal/kidneys/abomination + name = "secondary filter" + +/obj/item/organ/internal/brain/abomination + name = "swarm link" + +/obj/item/organ/internal/eyes/abomination + name = "eyes" // Eyes, yes. + +// Actual stuff +/obj/item/organ/internal/larva_producer + name = "larvae storage" + icon_state = "stomach" + color = COLOR_MAROON + organ_tag = BP_LARVA + parent_organ = BP_CHEST + can_be_printed = FALSE + var/larva_cooldown = 0 + var/larva_cooldown_time = 300 + +/obj/item/organ/internal/larva_producer/Initialize() + . = ..() + larva_cooldown = larva_cooldown_time * rand(2, 3) + +/obj/item/organ/internal/larva_producer/Process() + if(owner) + larva_cooldown = larva_cooldown <= 0 ? larva_cooldown_time : larva_cooldown - 1 + if(larva_cooldown == round(larva_cooldown_time * 0.3)) + to_chat(owner, SPAN_WARNING("Your [parent_organ] is vibrating ever so slighty...")) + else if(larva_cooldown == round(larva_cooldown_time * 0.2)) + to_chat(owner, SPAN_DANGER("Something inside of your [parent_organ] is moving!")) + else if(larva_cooldown == round(larva_cooldown_time * 0.1)) + to_chat(owner, SPAN_DANGER("\The [src] is about to produce a new larva!")) + else if(larva_cooldown <= 0) + var/mob/living/simple_animal/hostile/infestation/larva/L = new(get_turf(owner)) + owner.Stun(2) + playsound(L, 'sound/effects/splat.ogg', 50, TRUE) + var/obj/item/organ/internal/stomach/stomach = owner.internal_organs_by_name[BP_STOMACH] + var/obj/effect/decal/cleanable/vomit/splat = new /obj/effect/decal/cleanable/vomit(get_turf(owner)) + splat.color = COLOR_MAROON + if(stomach.ingested.total_volume) + stomach.ingested.trans_to_obj(splat, min(15, stomach.ingested.total_volume)) + visible_message(SPAN_DANGER("\The [owner] throws up, as something crawls out!"), + SPAN_DANGER("You throw up, leading \the [L] outside.")) + return ..() diff --git a/code/modules/overmap/contacts/_contact.dm b/code/modules/overmap/contacts/_contact.dm new file mode 100644 index 0000000000000..37add6643e8d2 --- /dev/null +++ b/code/modules/overmap/contacts/_contact.dm @@ -0,0 +1,140 @@ +var/list/phonetic_alphabet_prefix = list("ALPHA", "BRAVO", "CHARLIE", "DELTA", "ECHO", "FOXTROT", "GOLF", "HOTEL", "INDIA", + "JULIETT", "KILO", "LIMA", "MIKE", "NOVEMBER", "OSCAR", "PAPA", "QUEBEC", "ROMEO", "SIERRA", "TANGO", + "UNIFORM", "VICTOR", "WHISKEY", "XRAY", "YANKEE", "ZULU") +var/list/phonetic_alphabet_suffix = list("ALPHA", "BETA", "GAMMA", "DELTA", "EPSILON", "ZETA", "ETA", "THETA", "IOTA", "KAPPA", + "LAMBDA", "MU", "NU", "XI", "OMICRON", "PI", "RHO", "SIGMA", "TAU", "UPSILON", "PHI", "CHI", "PSI", "OMEGA") +#define SENSOR_TIME_DELAY 0.2 SECONDS +/datum/overmap_contact + + var/name = "Unknown" // Contact name. + var/temp_designation = "Unknown" // Temporary designation given by the computer to this object. + var/class = "UNKWN" // Class of the ship + var/class_long = "Unknown" // Human-readable name of the class. + var/information = "Unknown" // Any information we have on the contact. + var/identified = FALSE // Have we identified it? + var/identification_progress // Progress towards identification, increased by identification_amount of the ship effect. + var/seen // Have we seen this recently? + var/image/marker // Image overlay attached to the contact. + var/is_overmap_event = FALSE // Pretty much used to skip a bunch of stuff so we can ues the same system for overmap effects. + var/pinged = FALSE // Used to animate overmap effects. + var/list/images = list() // Our list of images to cast to users. + + // The sensor console holding this data. + var/obj/machinery/computer/ship/sensors/owner + + // The actual overmap effect associated with this. + var/obj/effect/overmap/visitable/ship/effect + +/datum/overmap_contact/proc/handle_being_identified() + if(!identified) + hide() + identified = TRUE + identification_progress = effect.identification_difficulty + marker.icon = effect.icon + marker.appearance_flags |= RESET_COLOR + marker.icon_state = effect.contact_icon_state || "ship" + marker.color = effect.color + show() + +/datum/overmap_contact/New(var/obj/machinery/computer/ship/sensors/creator, var/obj/effect/overmap/source, var/is_event = FALSE) + is_overmap_event = is_event + // Update local tracking information. + owner = creator + effect = source + name = effect.scanner_name + owner.contact_datums[effect] = src + + if(!is_overmap_event) + var/obj/effect/overmap/visitable/ship/source_ship = source + information = source_ship.desc + temp_designation = "[pick(global.phonetic_alphabet_prefix)]-[pick(global.phonetic_alphabet_suffix)]-[random_id(type, 1, 999)]" + marker = image(loc = source_ship, icon = 'icons/obj/overmap.dmi', icon_state = "unidentified_ship") + + if(source_ship.transponder_active) + handle_being_identified() + else + identified = TRUE + var/obj/effect/overmap/event/source_event = source + marker = image(loc = source_event, icon = 'icons/obj/overmap.dmi', icon_state = source_event.overmap_effect_state) + marker.color = source_event.color + marker.filters = filter(type="drop_shadow", color = marker.color + "F0", size = 2, offset = 1,x = 0, y = 0) + marker.alpha = 0 + + images += marker + +/datum/overmap_contact/proc/update_marker_icon(var/range = 0) + if(identified) + marker.icon_state = effect.contact_icon_state + + marker.overlays.Cut() + + if(check_effect_shield()) + var/image/shield_image = image(icon = 'icons/obj/overmap.dmi', icon_state = "shield") + shield_image.pixel_x = 8 + marker.overlays += shield_image + + if(range > 1) + var/image/radar + + for(var/image/I in images) + if(I.tag == "radar") + radar = I + if(radar) + return + + radar = image(loc = effect, icon = 'icons/obj/overmap.dmi', icon_state = "sensor_range") + radar.pixel_x = -2 + radar.tag = "radar" + radar.filters = filter(type="blur", size = 0.5) + images += radar + + var/matrix/M = matrix() + M.Scale(range*2.6) + animate(radar, transform = M, alpha = 0, time = (0.25 SECONDS*range), 1, SINE_EASING) + addtimer(CALLBACK(src, .proc/reset_radar, radar), (0.25 SECONDS *range+0.1)) + QDEL_IN(radar, (0.25 SECONDS *range+0.3)) + +/datum/overmap_contact/proc/reset_radar(var/image/radar) + images -= radar + +/datum/overmap_contact/proc/show() + for(var/weakref/W in owner?.viewers) + var/mob/M = W.resolve() + if(istype(M)) + M.client?.images |= images + +/datum/overmap_contact/proc/hide() + for(var/weakref/W in owner?.viewers) + var/mob/M = W.resolve() + if(istype(M)) + M.client?.images -= images + +/datum/overmap_contact/proc/ping() + if(pinged) + return + pinged = TRUE + show() + animate(marker, alpha=255, 0.5 SECOND, 1, LINEAR_EASING) + addtimer(CALLBACK(src, .proc/unping), 1 SECOND) + + +/datum/overmap_contact/proc/unping() + animate(marker, alpha=230, 2 SECOND, 1, LINEAR_EASING) + +/datum/overmap_contact/proc/check_effect_shield() + var/shield_active = FALSE + for(var/obj/machinery/power/shield_generator/S in SSmachines.machinery) + if(S.z in effect.map_z) + if(S.running == SHIELD_RUNNING) + shield_active = TRUE + return shield_active + +/datum/overmap_contact/Destroy() + if(owner) + hide() + if(effect) + owner.contact_datums[effect] = null + owner.contact_datums -= null + owner = null + effect = null + . = ..() diff --git a/code/modules/overmap/contacts/_contacts.dm b/code/modules/overmap/contacts/_contacts.dm new file mode 100644 index 0000000000000..f9e22ee42875f --- /dev/null +++ b/code/modules/overmap/contacts/_contacts.dm @@ -0,0 +1,141 @@ +var/list/phonetic_alphabet_prefix = list("ALPHA", "BRAVO", "CHARLIE", "DELTA", "ECHO", "FOXTROT", "GOLF", "HOTEL", "INDIA", + "JULIETT", "KILO", "LIMA", "MIKE", "NOVEMBER", "OSCAR", "PAPA", "QUEBEC", "ROMEO", "SIERRA", "TANGO", + "UNIFORM", "VICTOR", "WHISKEY", "XRAY", "YANKEE", "ZULU") +var/list/phonetic_alphabet_suffix = list("ALPHA", "BETA", "GAMMA", "DELTA", "EPSILON", "ZETA", "ETA", "THETA", "IOTA", "KAPPA", + "LAMBDA", "MU", "NU", "XI", "OMICRON", "PI", "RHO", "SIGMA", "TAU", "UPSILON", "PHI", "CHI", "PSI", "OMEGA") +#define SENSOR_TIME_DELAY 0.2 SECONDS + +/datum/overmap_contact + + var/name = "Unknown" // Contact name. + var/temp_designation = "Unknown" // Temporary designation given by the computer to this object. + var/class = "UNKWN" // Class of the ship + var/class_long = "Unknown" // Human-readable name of the class. + var/information = "Unknown" // Any information we have on the contact. + var/identified = FALSE // Have we identified it? + var/identification_progress // Progress towards identification, increased by identification_amount of the ship effect. + var/seen // Have we seen this recently? + var/image/marker // Image overlay attached to the contact. + var/is_overmap_event = FALSE // Pretty much used to skip a bunch of stuff so we can ues the same system for overmap effects. + var/pinged = FALSE // Used to animate overmap effects. + var/list/images = list() // Our list of images to cast to users. + + // The sensor console holding this data. + var/obj/machinery/computer/ship/sensors/owner + + // The actual overmap effect associated with this. + var/obj/effect/overmap/visitable/ship/effect + +/datum/overmap_contact/proc/handle_being_identified() + if(!identified) + hide() + identified = TRUE + identification_progress = effect.identification_difficulty + marker.icon = effect.icon + marker.appearance_flags |= RESET_COLOR + marker.icon_state = effect.contact_icon_state || "ship" + marker.color = effect.color + show() + +/datum/overmap_contact/New(var/obj/machinery/computer/ship/sensors/creator, var/obj/effect/overmap/source, var/is_event = FALSE) + is_overmap_event = is_event + // Update local tracking information. + owner = creator + effect = source + name = effect.scanner_name + owner.contact_datums[effect] = src + + if(!is_overmap_event) + var/obj/effect/overmap/visitable/ship/source_ship = source + information = source_ship.desc + temp_designation = "[pick(global.phonetic_alphabet_prefix)]-[pick(global.phonetic_alphabet_suffix)]-[random_id(type, 1, 999)]" + marker = image(loc = source_ship, icon = 'icons/obj/overmap.dmi', icon_state = "unidentified_ship") + + if(source_ship.transponder_active) + handle_being_identified() + else + identified = TRUE + var/obj/effect/overmap/event/source_event = source + marker = image(loc = source_event, icon = 'icons/obj/overmap.dmi', icon_state = source_event.overmap_effect_state) + marker.color = source_event.color + marker.filters = filter(type="drop_shadow", color = marker.color + "F0", size = 2, offset = 1,x = 0, y = 0) + marker.alpha = 0 + + images += marker + +/datum/overmap_contact/proc/update_marker_icon(var/range = 0) + if(identified) + marker.icon_state = effect.contact_icon_state + + marker.overlays.Cut() + + if(check_effect_shield()) + var/image/shield_image = image(icon = 'icons/obj/overmap.dmi', icon_state = "shield") + shield_image.pixel_x = 8 + marker.overlays += shield_image + + if(range > 1) + var/image/radar + + for(var/image/I in images) + if(I.tag == "radar") + radar = I + if(radar) + return + + radar = image(loc = effect, icon = 'icons/obj/overmap.dmi', icon_state = "sensor_range") + radar.pixel_x = -2 + radar.tag = "radar" + radar.filters = filter(type="blur", size = 0.5) + images += radar + + var/matrix/M = matrix() + M.Scale(range*2.6) + animate(radar, transform = M, alpha = 0, time = (0.25 SECONDS*range), 1, SINE_EASING) + addtimer(CALLBACK(src, .proc/reset_radar, radar), (0.25 SECONDS *range+0.1)) + QDEL_IN(radar, (0.25 SECONDS *range+0.3)) + +/datum/overmap_contact/proc/reset_radar(var/image/radar) + images -= radar + +/datum/overmap_contact/proc/show() + for(var/weakref/W in owner?.viewers) + var/mob/M = W.resolve() + if(istype(M)) + M.client?.images |= images + +/datum/overmap_contact/proc/hide() + for(var/weakref/W in owner?.viewers) + var/mob/M = W.resolve() + if(istype(M)) + M.client?.images -= images + +/datum/overmap_contact/proc/ping() + if(pinged) + return + pinged = TRUE + show() + animate(marker, alpha=255, 0.5 SECOND, 1, LINEAR_EASING) + addtimer(CALLBACK(src, .proc/unping), 1 SECOND) + + +/datum/overmap_contact/proc/unping() + animate(marker, alpha=230, 2 SECOND, 1, LINEAR_EASING) + +/datum/overmap_contact/proc/check_effect_shield() + var/shield_active = FALSE + for(var/obj/machinery/power/shield_generator/S in SSmachines.machinery) + if(S.z in effect.map_z) + if(S.running == SHIELD_RUNNING) + shield_active = TRUE + return shield_active + +/datum/overmap_contact/Destroy() + if(owner) + hide() + if(effect) + owner.contact_datums[effect] = null + owner.contact_datums -= null + owner = null + effect = null + . = ..() diff --git a/code/modules/overmap/contacts/contact_class.dm b/code/modules/overmap/contacts/contact_class.dm new file mode 100644 index 0000000000000..2079bb94e29c5 --- /dev/null +++ b/code/modules/overmap/contacts/contact_class.dm @@ -0,0 +1,41 @@ +/obj/effect/overmap/visitable/ship + var/contact_class = /decl/ship_contact_class + +/decl/ship_contact_class + var/class_short = "Ship" + var/class_long = "Unknown Ship Class" + var/min_ship_mass = 0 + var/max_ship_mass = INFINITY + +/decl/ship_contact_class/ship + class_short = "SH" + class_long = "Ship" + max_ship_mass = 10000 + +/decl/ship_contact_class/shuttle + class_short = "SHUTTLE" + class_long = "Shuttle" + max_ship_mass = 10000 + +/decl/ship_contact_class/destroyer_escort + class_short = "DE" + class_long = "Destroyer Escort" + min_ship_mass = 10000 + max_ship_mass = 50000 + +/decl/ship_contact_class/destroyer + class_short = "DD" + class_long = "Destroyer" + min_ship_mass = 50000 + max_ship_mass = 100000 + +/decl/ship_contact_class/cruiser + class_short = "CA" + class_long = "Cruiser" + min_ship_mass = 100000 + max_ship_mass = 250000 + +/decl/ship_contact_class/capital_ship + class_short = "CAP" + class_long = "Capital Ship" + min_ship_mass = 250000 diff --git a/code/modules/overmap/contacts/contact_sensors.dm b/code/modules/overmap/contacts/contact_sensors.dm new file mode 100644 index 0000000000000..0c3d59d5e12a0 --- /dev/null +++ b/code/modules/overmap/contacts/contact_sensors.dm @@ -0,0 +1,143 @@ +/obj/machinery/computer/ship/sensors + var/list/objects_in_view = list() + var/list/contact_datums = list() + +/obj/machinery/computer/ship/sensors/Destroy() + objects_in_view.Cut() + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + qdel(record) + contact_datums.Cut() + . = ..() + +/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + . = ..() + if(. && linked && !contact_datums[linked]) + var/datum/overmap_contact/record = new(src, linked) + record.handle_being_identified() + +/obj/machinery/computer/ship/sensors/proc/reveal_contacts(var/mob/user) + if(user && user.client) + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record) + user.client.images |= record.marker + +/obj/machinery/computer/ship/sensors/proc/hide_contacts(var/mob/user) + if(user && user.client) + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record) + user.client.images -= record.marker + +/obj/machinery/computer/ship/sensors/Process() + ..() + update_sound() + if(!linked) + return + + // Update our 'sensor range' (ie. overmap lighting) + if(!sensors || !sensors.use_power || !sensors.powered()) + linked.set_light(0) + // TODO move to on_power_change()? + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record.effect == linked) + continue + animate(record.marker, alpha=0, 1 SECOND, 1, LINEAR_EASING) + record.hide() + + objects_in_view.Cut() + return + + var/sensor_range = round(sensors.range,1) + linked.set_light(1, sensor_range, sensor_range+1) + + // What can we see? + var/list/new_objects_in_view = list() + var/list/objects_in_current_view = list() + for(var/obj/effect/overmap/contact in view(sensor_range, linked)-linked) + objects_in_current_view[contact] = TRUE + if(!objects_in_view[contact]) + new_objects_in_view[contact] = TRUE + + // Fade out and remove anything that is out of range. + for(var/obj/effect/overmap/contact in objects_in_view) + if(!QDELETED(contact) && objects_in_current_view[contact]) + continue + var/datum/overmap_contact/record = contact_datums[contact] + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(record) + animate(record.marker, alpha=0, 2 SECOND, 1, LINEAR_EASING) + addtimer(CALLBACK(record, /datum/overmap_contact/proc/hide), 2 SECOND) + if(record && !record.is_overmap_event) + if(record.identified) + visible_message(SPAN_NOTICE("[src] states, 'Contact lost with [record.name], bearing [bearing].'")) + else + visible_message(SPAN_NOTICE("[src] states, 'Contact lost with [record.temp_designation], bearing [bearing].'")) + playsound(loc, "sound/machines/sensors/contact_lost.ogg", 30, 1) + objects_in_view -= contact + // Refresh or update contacts and markers for anything new. + objects_in_view |= new_objects_in_view + + for(var/obj/effect/overmap/contact in new_objects_in_view) + var/datum/overmap_contact/record = contact_datums[contact] + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(record) + if(!record.is_overmap_event) + if(record.identified) + visible_message(SPAN_NOTICE("[src] states, 'Contact regained with [record.name], bearing [bearing].'")) + else + visible_message(SPAN_NOTICE("[src] states, 'Contact regained with [record.temp_designation], bearing [bearing].'")) + playsound(loc, "sound/machines/sensors/contact_regained.ogg", 30, 1) + record.show() + if(record.is_overmap_event) + animate(record.marker, alpha=255, 2 SECOND, 1, LINEAR_EASING) + continue + animate(record.marker, alpha=255, 2 SECOND, 1, LINEAR_EASING) + + for(var/obj/effect/overmap/visitable/ship/contact in objects_in_view) //Update everything. + // Have we seen this ship before? + var/datum/overmap_contact/record = contact_datums[contact] + // Generate contact information for this overmap object. + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(!record) + record = new /datum/overmap_contact(src, contact) + if(record.effect.type in linked.known_ships) + record.handle_being_identified() + visible_message(SPAN_NOTICE("\The [src] states, \"Known contact registered, [record.name].\"")) + else + playsound(loc, "sound/machines/sensors/newcontact.ogg", 30, 1) + visible_message(SPAN_NOTICE("\The [src] states, \"New contact detected, temporary designation [record.temp_designation], bearing [bearing]. Identification in progress.\"")) + + // Update identification information for this record. + if(prob(record.effect.sensor_visiblity)) + record.update_marker_icon() + if(!record.identified) + if(record.identification_progress < record.effect.identification_difficulty) + record.identification_progress += 5 + if(record.identification_progress == record.effect.identification_difficulty) + record.handle_being_identified() + playsound(loc, "sound/machines/sensors/contact_identified.ogg", 30, 1) + var/decl/ship_contact_class/class = decls_repository.get_decl(record.effect.contact_class) + visible_message(SPAN_NOTICE("[src] states, 'Contact [record.temp_designation] identified as [record.name], [class.class_long], bearing [bearing].'")) + + for(var/obj/effect/overmap/event in objects_in_view) + var/datum/overmap_contact/record = contact_datums[event] + if(!record) + record = new /datum/overmap_contact(src, event, TRUE) + var/time_delay = max((SENSOR_TIME_DELAY * get_dist(linked, event)),1) + if(!record.pinged) + addtimer(CALLBACK(record, .proc/ping), time_delay) + + + //Update our own marker icon. + var/datum/overmap_contact/self_record = contact_datums[linked] + self_record.update_marker_icon(sensor_range) + self_record.show() diff --git a/code/modules/overmap/contacts/contacts_class.dm b/code/modules/overmap/contacts/contacts_class.dm new file mode 100644 index 0000000000000..e9f5d67359b1c --- /dev/null +++ b/code/modules/overmap/contacts/contacts_class.dm @@ -0,0 +1,41 @@ +/obj/effect/overmap/visitable/ship + var/contact_class = /decl/ship_contact_class + +/decl/ship_contact_class + var/class_short = "Ship" + var/class_long = "Unknown Ship Class" + var/min_ship_mass = 0 + var/max_ship_mass = INFINITY + +/decl/ship_contact_class/ship + class_short = "SH" + class_long = "Ship" + max_ship_mass = 10000 + +/decl/ship_contact_class/shuttle + class_short = "SHUTTLE" + class_long = "Shuttle" + max_ship_mass = 10000 + +/decl/ship_contact_class/destroyer_escort + class_short = "DE" + class_long = "Destroyer Escort" + min_ship_mass = 10000 + max_ship_mass = 50000 + +/decl/ship_contact_class/destroyer + class_short = "DD" + class_long = "Destroyer" + min_ship_mass = 50000 + max_ship_mass = 100000 + +/decl/ship_contact_class/cruiser + class_short = "CA" + class_long = "Cruiser" + min_ship_mass = 100000 + max_ship_mass = 250000 + +/decl/ship_contact_class/capital_ship + class_short = "CAP" + class_long = "Capital Ship" + min_ship_mass = 250000 diff --git a/code/modules/overmap/contacts/contacts_sensors.dm b/code/modules/overmap/contacts/contacts_sensors.dm new file mode 100644 index 0000000000000..0c3d59d5e12a0 --- /dev/null +++ b/code/modules/overmap/contacts/contacts_sensors.dm @@ -0,0 +1,143 @@ +/obj/machinery/computer/ship/sensors + var/list/objects_in_view = list() + var/list/contact_datums = list() + +/obj/machinery/computer/ship/sensors/Destroy() + objects_in_view.Cut() + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + qdel(record) + contact_datums.Cut() + . = ..() + +/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + . = ..() + if(. && linked && !contact_datums[linked]) + var/datum/overmap_contact/record = new(src, linked) + record.handle_being_identified() + +/obj/machinery/computer/ship/sensors/proc/reveal_contacts(var/mob/user) + if(user && user.client) + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record) + user.client.images |= record.marker + +/obj/machinery/computer/ship/sensors/proc/hide_contacts(var/mob/user) + if(user && user.client) + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record) + user.client.images -= record.marker + +/obj/machinery/computer/ship/sensors/Process() + ..() + update_sound() + if(!linked) + return + + // Update our 'sensor range' (ie. overmap lighting) + if(!sensors || !sensors.use_power || !sensors.powered()) + linked.set_light(0) + // TODO move to on_power_change()? + for(var/key in contact_datums) + var/datum/overmap_contact/record = contact_datums[key] + if(record.effect == linked) + continue + animate(record.marker, alpha=0, 1 SECOND, 1, LINEAR_EASING) + record.hide() + + objects_in_view.Cut() + return + + var/sensor_range = round(sensors.range,1) + linked.set_light(1, sensor_range, sensor_range+1) + + // What can we see? + var/list/new_objects_in_view = list() + var/list/objects_in_current_view = list() + for(var/obj/effect/overmap/contact in view(sensor_range, linked)-linked) + objects_in_current_view[contact] = TRUE + if(!objects_in_view[contact]) + new_objects_in_view[contact] = TRUE + + // Fade out and remove anything that is out of range. + for(var/obj/effect/overmap/contact in objects_in_view) + if(!QDELETED(contact) && objects_in_current_view[contact]) + continue + var/datum/overmap_contact/record = contact_datums[contact] + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(record) + animate(record.marker, alpha=0, 2 SECOND, 1, LINEAR_EASING) + addtimer(CALLBACK(record, /datum/overmap_contact/proc/hide), 2 SECOND) + if(record && !record.is_overmap_event) + if(record.identified) + visible_message(SPAN_NOTICE("[src] states, 'Contact lost with [record.name], bearing [bearing].'")) + else + visible_message(SPAN_NOTICE("[src] states, 'Contact lost with [record.temp_designation], bearing [bearing].'")) + playsound(loc, "sound/machines/sensors/contact_lost.ogg", 30, 1) + objects_in_view -= contact + // Refresh or update contacts and markers for anything new. + objects_in_view |= new_objects_in_view + + for(var/obj/effect/overmap/contact in new_objects_in_view) + var/datum/overmap_contact/record = contact_datums[contact] + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(record) + if(!record.is_overmap_event) + if(record.identified) + visible_message(SPAN_NOTICE("[src] states, 'Contact regained with [record.name], bearing [bearing].'")) + else + visible_message(SPAN_NOTICE("[src] states, 'Contact regained with [record.temp_designation], bearing [bearing].'")) + playsound(loc, "sound/machines/sensors/contact_regained.ogg", 30, 1) + record.show() + if(record.is_overmap_event) + animate(record.marker, alpha=255, 2 SECOND, 1, LINEAR_EASING) + continue + animate(record.marker, alpha=255, 2 SECOND, 1, LINEAR_EASING) + + for(var/obj/effect/overmap/visitable/ship/contact in objects_in_view) //Update everything. + // Have we seen this ship before? + var/datum/overmap_contact/record = contact_datums[contact] + // Generate contact information for this overmap object. + var/bearing = round(90 - Atan2(contact.x - linked.x, contact.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(!record) + record = new /datum/overmap_contact(src, contact) + if(record.effect.type in linked.known_ships) + record.handle_being_identified() + visible_message(SPAN_NOTICE("\The [src] states, \"Known contact registered, [record.name].\"")) + else + playsound(loc, "sound/machines/sensors/newcontact.ogg", 30, 1) + visible_message(SPAN_NOTICE("\The [src] states, \"New contact detected, temporary designation [record.temp_designation], bearing [bearing]. Identification in progress.\"")) + + // Update identification information for this record. + if(prob(record.effect.sensor_visiblity)) + record.update_marker_icon() + if(!record.identified) + if(record.identification_progress < record.effect.identification_difficulty) + record.identification_progress += 5 + if(record.identification_progress == record.effect.identification_difficulty) + record.handle_being_identified() + playsound(loc, "sound/machines/sensors/contact_identified.ogg", 30, 1) + var/decl/ship_contact_class/class = decls_repository.get_decl(record.effect.contact_class) + visible_message(SPAN_NOTICE("[src] states, 'Contact [record.temp_designation] identified as [record.name], [class.class_long], bearing [bearing].'")) + + for(var/obj/effect/overmap/event in objects_in_view) + var/datum/overmap_contact/record = contact_datums[event] + if(!record) + record = new /datum/overmap_contact(src, event, TRUE) + var/time_delay = max((SENSOR_TIME_DELAY * get_dist(linked, event)),1) + if(!record.pinged) + addtimer(CALLBACK(record, .proc/ping), time_delay) + + + //Update our own marker icon. + var/datum/overmap_contact/self_record = contact_datums[linked] + self_record.update_marker_icon(sensor_range) + self_record.show() diff --git a/code/modules/overmap/exoplanets/planet_types/infested.dm b/code/modules/overmap/exoplanets/planet_types/infested.dm new file mode 100644 index 0000000000000..9e1188f8539dd --- /dev/null +++ b/code/modules/overmap/exoplanets/planet_types/infested.dm @@ -0,0 +1,84 @@ +/obj/effect/overmap/visitable/sector/exoplanet/infested + name = "infested exoplanet" + icon_state = "globe_infested" + desc = "Dangerous plante, requeired infiltration." + + color = "#9c3d51" + water_color = null + surface_color = "#94404e" + planetary_area = /area/exoplanet/infested_forest + rock_colors = list(COLOR_ASTEROID_ROCK, COLOR_GRAY80, COLOR_BROWN) + plant_colors = list("#f5426f","#c72a51","#cc1d48","#99156f","#8c268b", "RANDOM") + map_generators = list(/datum/random_map/noise/exoplanet/infested, /datum/random_map/noise/ore/rich) + habitability_distribution = list(HABITABILITY_IDEAL = 10, HABITABILITY_OKAY = 80, HABITABILITY_BAD = 10) + flora_diversity = 15 + fauna_types = list( + /mob/living/simple_animal/hostile/infestation/broodling = 7, + /mob/living/simple_animal/hostile/infestation/eviscerator = 5, + /mob/living/simple_animal/hostile/infestation/floatfly = 4, + /mob/living/simple_animal/hostile/infestation/spitter = 3, + /mob/living/simple_animal/hostile/infestation/assembler = 3, + /mob/living/simple_animal/hostile/infestation/rhino = 2, + /mob/living/simple_animal/hostile/infestation/larva/implant/implanter = 1, + ) + +/obj/effect/overmap/visitable/sector/exoplanet/infested/generate_map() + if(prob(50)) + lightlevel = pick(0.05, 0.1, 0.15) + return ..() + +/obj/effect/overmap/visitable/sector/exoplanet/infested/generate_atmosphere() + ..() + if(atmosphere) + atmosphere.temperature = T20C + rand(-40, 40) + atmosphere.update_values() + +/obj/effect/overmap/visitable/sector/exoplanet/infested/get_surface_color() + return "#9c3d51" + +/obj/effect/overmap/visitable/sector/exoplanet/infested/adapt_animal(mob/living/simple_animal/A, setname = TRUE) + if(!istype(A, /mob/living/simple_animal/hostile/infestation)) + return ..() + // Planet-spawned abominations do not evolve normally + var/mob/living/simple_animal/hostile/infestation/abom = A + abom.transformation_time = null + +/obj/effect/overmap/visitable/sector/exoplanet/infested/adapt_seed(datum/seed/S) + ..() + var/carnivore_prob = rand(100) + if(carnivore_prob < 30) + S.set_trait(TRAIT_CARNIVOROUS,2) + if(prob(75)) + S.get_trait(TRAIT_STINGS, 1) + else if(carnivore_prob < 60) + S.set_trait(TRAIT_CARNIVOROUS,1) + if(prob(50)) + S.get_trait(TRAIT_STINGS) + if(prob(15) || (S.get_trait(TRAIT_CARNIVOROUS) && prob(40))) + S.set_trait(TRAIT_BIOLUM,1) + S.set_trait(TRAIT_BIOLUM_COLOUR,get_random_colour(0,75,190)) + + if(prob(30)) + S.set_trait(TRAIT_PARASITE,1) + if(!S.get_trait(TRAIT_LARGE)) + var/vine_prob = rand(100) + if(vine_prob < 15) + S.set_trait(TRAIT_SPREAD,2) + else if(vine_prob < 30) + S.set_trait(TRAIT_SPREAD,1) + +/area/exoplanet/infested_forest + base_turf = /turf/simulated/floor/exoplanet/grass + +/area/exoplanet/infested_forest/play_ambience(mob/living/L) + ..() + if(!L.ear_deaf && L.client) + L.playsound_local(get_turf(L),sound('sound/ambience/infested_forest.ogg', repeat = 1, wait = 0, volume = 25)) + +/datum/random_map/noise/exoplanet/infested + descriptor = "infested exoplanet" + smoothing_iterations = 2 + land_type = /turf/simulated/floor/exoplanet/flesh + + flora_prob = 10 + fauna_prob = 20 diff --git a/code/modules/overmap/exoplanets/turfs.dm b/code/modules/overmap/exoplanets/turfs.dm index 257bd4f9fff9d..8bd74ebc5431b 100644 --- a/code/modules/overmap/exoplanets/turfs.dm +++ b/code/modules/overmap/exoplanets/turfs.dm @@ -184,6 +184,27 @@ icon_state = "sandglass" diggable = 0 +// Flesh +/turf/simulated/floor/exoplanet/flesh + name = "flesh" + icon = 'icons/turf/flooring/flesh.dmi' + icon_state = "flesh0" + color = "#94404e" + footstep_type = /decl/footsteps/blank + +/turf/simulated/floor/exoplanet/flesh/Initialize() + . = ..() + icon_state = "flesh[pick(0,1,2,3)]" + +/turf/simulated/floor/exoplanet/flesh/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if((temperature > T0C + 200 && prob(5)) || temperature > T0C + 1000) + melt() + +/turf/simulated/floor/exoplanet/flesh/melt() + SetName("scorched flesh") + footstep_type = /decl/footsteps/asteroid + color = "#70353a" + //Concrete /turf/simulated/floor/exoplanet/concrete name = "concrete" diff --git a/code/modules/overmap/radio_beacon.dm b/code/modules/overmap/radio_beacon.dm new file mode 100644 index 0000000000000..d00ca713a6ba2 --- /dev/null +++ b/code/modules/overmap/radio_beacon.dm @@ -0,0 +1,187 @@ +/obj/effect/overmap/radio + name = "radio signal" + scanner_name = "radio signal" + icon_state = "radio" + scannable = TRUE + color = COLOR_AMBER + var/message + var/obj/effect/overmap/source + +/obj/effect/overmap/radio/get_scan_data(mob/user) + return "Радиосигнал, передаваемый [source].

\ + ---НАЧАЛО ПЕРЕДАЧИ---

\ + [message] \ +

---КОНЕЦ ПЕРЕДАЧИ---" + +/obj/effect/overmap/radio/proc/set_origin(obj/effect/overmap/origin) + GLOB.moved_event.register(origin, src, /obj/effect/overmap/radio/proc/follow) + GLOB.destroyed_event.register(origin, src, /datum/proc/qdel_self) + forceMove(origin.loc) + source = origin + pixel_x = -(origin.bound_width - 6) + pixel_y = origin.bound_height - 6 + +/obj/effect/overmap/radio/proc/follow(var/atom/movable/am, var/old_loc, var/new_loc) + forceMove(new_loc) + +/obj/effect/overmap/radio/Destroy() + GLOB.destroyed_event.unregister(source, src) + GLOB.moved_event.unregister(source, src) + source = null + . = ..() + +/obj/item/radio_beacon + name = "radio beacon" + desc = "Device capable of continuously broadcasting a signal that can be picked up by ship sensors." + icon = 'icons/obj/radio.dmi' + icon_state = "walkietalkie" + var/obj/effect/overmap/radio/signal + +/obj/item/radio_beacon/attack_self(mob/user) + var/obj/effect/overmap/visitable/O = map_sectors["[get_z(src)]"] + if(!O) + to_chat(user, SPAN_WARNING("Вы не можете развернуть [src] здесь.")) + return + var/message = sanitize(input("Что следует передать?") as message|null) + + if(!signal) + signal = new() + + signal.message = message + signal.set_origin(O) + + + + + +/obj/item/device/subspaceradio + name = "subspace radio" + desc = "This long range communications device has the ability to send and recieve transmissions from anywhere." + icon = 'icons/obj/structures/decor.dmi' + icon_state = "random_radio" + w_class = ITEM_SIZE_LARGE + action_button_name = "Remove/Replace Handset" + var/obj/item/subspacehandset/handset + +/obj/item/device/subspaceradio/Initialize() + . = ..() + if(ispath(handset)) + handset = new handset(src, src) + else + handset = new(src, src) + +/obj/item/device/subspaceradio/Destroy() + . = ..() + QDEL_NULL(handset) + +/obj/item/device/subspaceradio/ui_action_click() + toggle_handset() + +/obj/item/device/subspaceradio/attack_hand(mob/user) + if(loc == user) + toggle_handset() + else + ..() + +/obj/item/device/subspaceradio/MouseDrop() + if(ismob(loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = loc + if(!M.unEquip(src)) + return + src.add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) + +/obj/item/device/subspaceradio/attackby(obj/item/W, mob/user, params) + if(W == handset) + reattach_handset(user) + else + return ..() + +/obj/item/device/subspaceradio/AltClick(mob/user as mob) + toggle_handset() + +/obj/item/device/subspaceradio/verb/toggle_handset() + + var/mob/living/carbon/human/user = usr + if(!handset) + to_chat(user, SPAN_WARNING("The handset is missing!")) + return + + if(handset.loc != src) + reattach_handset(user) //Remove from their hands and back onto the defib unit + return + + else + if(!usr.put_in_hands(handset)) //Detach the handset into the user's hands + to_chat(user, SPAN_WARNING("You need a free hand to hold the handset!")) + update_icon() //success + +//checks that the base unit is in the correct slot to be used +/obj/item/device/subspaceradio/proc/slot_check() + var/mob/M = loc + if(!istype(M)) + return FALSE //not equipped + + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) + return TRUE + if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_belt) == src) + return TRUE + + return FALSE + +/obj/item/device/subspaceradio/dropped(mob/user) + ..() + reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/device/subspaceradio/proc/reattach_handset(mob/user) + if(!handset) return + + if(ismob(handset.loc)) + var/mob/M = handset.loc + if(M.drop_from_inventory(handset, src)) + to_chat(user, SPAN_NOTICE("\The [handset] snaps back into the main unit.")) + else + handset.forceMove(src) + +//Subspace Radio Handset +/obj/item/subspacehandset + name = "radio beacon" + desc = "Device capable of continuously broadcasting a signal that can be picked up by ship sensors." + icon = 'icons/obj/radio.dmi' + icon_state = "walkietalkie" + var/obj/effect/overmap/radio/signal + +/obj/item/subspacehandset/attack_self(mob/user) + var/obj/effect/overmap/visitable/O = map_sectors["[get_z(src)]"] + if(!O) + to_chat(user, SPAN_WARNING("Вы не можете развернуть [src] здесь.")) + return + var/message = sanitize(input("Что следует передать?") as message|null) + + if(!signal) + signal = new() + + signal.message = message + signal.set_origin(O) + +/obj/item/subspacehandset/linked + var/obj/item/device/subspaceradio/base_unit + +/obj/item/subspacehandset/linked/New(newloc, obj/item/device/subspaceradio/radio) + base_unit = radio + ..(newloc) + +/obj/item/subspacehandset/linked/Destroy() + if(base_unit) + //ensure the base unit's icon updates + if(base_unit.handset == src) + base_unit.handset = null + base_unit = null + return ..() + +/obj/item/subspacehandset/linked/dropped(mob/user) + ..() //update twohanding + if(base_unit) + base_unit.reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit diff --git a/code/modules/overmap/ships/computers/target_control.dm b/code/modules/overmap/ships/computers/target_control.dm new file mode 100644 index 0000000000000..24d9b1e0ebb85 --- /dev/null +++ b/code/modules/overmap/ships/computers/target_control.dm @@ -0,0 +1,166 @@ +//Engine control and monitoring console + +/obj/machinery/computer/ship/missiles + name = "missile control console" + icon_keyboard = "tech_key" + icon_screen = "mass_driver" + var/display_state = "status" + var/handled_sensors_range = 0 + + +/obj/machinery/computer/ship/missiles/proc/handle_sensors_range() + for(var/obj/machinery/computer/ship/sensors/sensor in linked.linked_computers) + handled_sensors_range = sensor.get_scanner_range() + //message_admins(handled_sensors_range) + + +/obj/machinery/computer/ship/missiles/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "sensors") + return + + handle_sensors_range() + var/data[0] + var/list/contacts_ships = list() + var/list/contacts_planets = list() + var/list/contacts_missiles = list() + var/obj/target_temp + for(var/obj/effect/overmap/O in view(src.handled_sensors_range, linked)) + if(linked == O) + continue + if(!O.scannable) + continue + var/bearing = round(90 - Atan2(O.x - linked.x, O.y - linked.y),5) + if(bearing < 0) + bearing += 360 + + if(istype(O, /obj/effect/overmap/visitable/ship)) + contacts_ships.Add(list(list("name"=O.scanner_name, "ref"="\ref[O]", "bearing"=bearing))) + + else if(istype(O, /obj/effect/overmap/visitable/sector/exoplanet)) + contacts_planets.Add(list(list("name"=O.scanner_name, "ref"="\ref[O]", "bearing"=bearing))) + + else if(istype(O, /obj/effect/overmap/projectile)) + contacts_missiles.Add(list(list("name"=O.scanner_name, "ref"="\ref[O]", "bearing"=bearing))) + + if(contacts_ships.len) + data["contacts_ships"] = contacts_ships + + if(contacts_planets.len) + data["contacts_planets"] = contacts_planets + + if(contacts_missiles.len) + data["contacts_missiles"] = contacts_missiles + + data["planet_x"] = linked.get_target(TARGET_PLANETCOORD)[1] + data["planet_y"] = linked.get_target(TARGET_PLANETCOORD)[2] + data["point_x"] = linked.get_target(TARGET_POINT)[1] + data["point_y"] = linked.get_target(TARGET_POINT)[2] + data["target_ship"] = null + if(linked.get_target(TARGET_PLANET)[1]) + target_temp = linked.get_target(TARGET_PLANET)[1] + data["target_planet"] = target_temp.name + if(linked.get_target(TARGET_SHIP)) + target_temp = linked.get_target(TARGET_SHIP) + data["target_ship"] = target_temp.name + if(linked.get_target(TARGET_MISSILE)) + target_temp = linked.get_target(TARGET_MISSILE) + data["target_missile"] = target_temp.name + + ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "target.tmpl", "[linked.scanner_name] Target Control", 700, 545) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + +/obj/machinery/computer/ship/missiles/OnTopic(var/mob/user, var/list/href_list, state) //TODO: FIX THIS + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["ship_lock"]) + var/obj/effect/overmap/O = locate(href_list["ship_lock"]) + if(istype(O) && !QDELETED(O) && (O in view(handled_sensors_range, linked))) + if(linked.set_target(TARGET_SHIP, O)) + visible_message(SPAN_NOTICE("[src] states, 'TARGET LOCKED: [O.scanner_name]'")) + playsound(loc, "sound/machines/sensors/target_lock.ogg", 30, 1) + else + visible_message(SPAN_NOTICE("[src] states, 'TARGET LOCK FAILED'")) + return TOPIC_HANDLED + + if (href_list["missile_lock"]) + var/obj/effect/overmap/O = locate(href_list["missile_lock"]) + if(istype(O) && !QDELETED(O) && (O in view(handled_sensors_range, linked))) + if(linked.set_target(TARGET_MISSILE, O)) + visible_message(SPAN_NOTICE("[src] states, 'MISSILE LOCKED: [O.scanner_name]'")) + playsound(loc, "sound/machines/sensors/target_lock.ogg", 30, 1) + else + visible_message(SPAN_NOTICE("[src] states, 'MISSILE LOCK FAILED'")) + return TOPIC_HANDLED + + if (href_list["planet_lock"]) + var/obj/effect/overmap/O = locate(href_list["planet_lock"]) + if(istype(O) && !QDELETED(O) && (O in view(handled_sensors_range, linked))) + if(linked.set_target(TARGET_PLANET, O, linked.get_target(TARGET_PLANET)[2], linked.get_target(TARGET_PLANET)[3])) + visible_message(SPAN_NOTICE("[src] states, 'PLANET LOCKED: [O.scanner_name]'")) + playsound(loc, "sound/machines/sensors/target_lock.ogg", 30, 1) + else + visible_message(SPAN_NOTICE("[src] states, 'PLANET LOCK FAILED'")) + return TOPIC_HANDLED + + if (href_list["set_planetx"]) + var/input = input("Set new planet X target", "Planet X", linked.get_target(TARGET_PLANETCOORD)[1]) as num|null + if(!CanInteract(user,state)) + return TOPIC_NOACTION + if (input) + linked.set_target(TARGET_PLANET, linked.get_target(TARGET_PLANET)[1], Clamp(input,1, world.maxx-8), linked.get_target(TARGET_PLANET)[3]) + return TOPIC_REFRESH + + if (href_list["set_planety"]) + var/input = input("Set new planet Y target", "Planet Y", linked.get_target(TARGET_PLANETCOORD)[2]) as num|null + if(!CanInteract(user,state)) + return TOPIC_NOACTION + if (input) + linked.set_target(TARGET_PLANET, linked.get_target(TARGET_PLANET)[1], linked.get_target(TARGET_PLANET)[2], Clamp(input,1, world.maxy-8)) + return TOPIC_REFRESH + + + if (href_list["set_pointx"]) + var/input = input("Set new point X target", "Planet X", linked.get_target(TARGET_POINT)[1]) as num|null + if(!CanInteract(user,state)) + return TOPIC_NOACTION + if (input) + linked.set_target(TARGET_POINT, null, Clamp(input, 1, 40), linked.get_target(TARGET_POINT)[2]) + return TOPIC_REFRESH + + if (href_list["set_pointy"]) + var/input = input("Set new point Y target", "Planet Y", linked.get_target(TARGET_POINT)[2]) as num|null + if(!CanInteract(user,state)) + return TOPIC_NOACTION + if (input) + linked.set_target(TARGET_POINT, null, linked.get_target(TARGET_POINT)[1], Clamp(input, 1, 40)) + return TOPIC_REFRESH + +/obj/machinery/computer/ship/missiles/proc/announce() + if(prob(80)) + for(var/obj/effect/overmap/O in view(10,linked)) + if(linked == O) + continue + if(!O.scannable) + continue + var/bearing = round(90 - Atan2(O.x - linked.x, O.y - linked.y),5) + if(bearing < 0) + bearing += 360 + if(istype(O, /obj/effect/overmap/projectile)) + if(bearing == 0) + return + if(bearing <= 179) + src.visible_message(SPAN_WARNING("[name] states, обнаружен пуск ракеты, передняя полусфера")) + playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 100, 1) + if(bearing >= 180) + src.visible_message(SPAN_DANGER("[name] states, обнаружен пуск ракеты, задняя полусфера")) + playsound(src.loc, 'sound/machines/defib_safetyOn.ogg', 100, 1) diff --git a/code/modules/overmap/ships/engines/electric.dm b/code/modules/overmap/ships/engines/electric.dm new file mode 100644 index 0000000000000..26d4ac5097429 --- /dev/null +++ b/code/modules/overmap/ships/engines/electric.dm @@ -0,0 +1,108 @@ +//Thermal nozzle engine +/datum/ship_engine/electric + name = "electric engine" + var/obj/machinery/power/engine/ion/nozzle + +/datum/ship_engine/electric/New(var/obj/machinery/_holder) + ..() + nozzle = _holder + +/datum/ship_engine/electric/Destroy() + nozzle = null + . = ..() + +/datum/ship_engine/electric/get_status() + return nozzle.get_status() + +/datum/ship_engine/electric/get_thrust() + return nozzle.get_thrust() + +/datum/ship_engine/electric/burn() + return nozzle.burn() + +/datum/ship_engine/electric/set_thrust_limit(var/new_limit) + nozzle.thrust_limit = new_limit + +/datum/ship_engine/electric/get_thrust_limit() + return nozzle.thrust_limit + +/datum/ship_engine/electric/is_on() + return nozzle.is_on() + +/datum/ship_engine/electric/toggle() + nozzle.on = !nozzle.on + +/datum/ship_engine/electric/can_burn() + return nozzle.is_on() && nozzle.check_power() + +/obj/machinery/power/engine/ion + name = "ion thruster nozzle" + desc = "Simple electrical propulsion nozzle, uses ion magic to propel the ship." + icon = 'icons/obj/ship_engine.dmi' + icon_state = "nozzle" + use_power = 1 + idle_power_usage = 1500 // internal circuitry, friction losses and stuff + opacity = TRUE + density = TRUE + + var/on = 1 + var/datum/ship_engine/electric/controller + var/thrust_limit = 0.3 // Value between 1 and 0 to limit the resulting thrust + + var/use_power_per_thrust = 250000 //INF, WAS 10000 + var/max_draw_per_tick = 100000 + + // Todo: upgrades, use cells and capacitors + var/stored_power + var/max_stored_power = 1000000 // ONE MILLION KILLER WATS + +/obj/machinery/power/engine/ion/Process() + ..() + if(powered()) + var/draw_amount = min(max_draw_per_tick, max_stored_power-stored_power) + if(surplus() < draw_amount) + draw_amount = surplus() + stored_power += draw_power(draw_amount) + +/obj/machinery/power/engine/ion/Initialize() + . = ..() + controller = new(src) + connect_to_network() + +/obj/machinery/power/engine/ion/Destroy() + ..() + if(controller) + qdel(controller) + controller = null + +/obj/machinery/power/engine/ion/proc/get_status() + . = list() + .+= "Location: [get_area(src)]." + if(!powered()) + .+= "Insufficient power to operate." + if(!check_power()) + .+= "Insufficient power for thrust (below [round(use_power_per_thrust * thrust_limit)] kW)." + .+= "Available power: [stored_power] kW." + . = jointext(.,"
") + +/obj/machinery/power/engine/ion/proc/is_on() + return on && powered() && check_power() + +/obj/machinery/power/engine/ion/proc/check_power() + return stored_power >= round(use_power_per_thrust * thrust_limit) + +/obj/machinery/power/engine/ion/proc/get_thrust() + if(!is_on() || !check_power()) + return 0 + return (use_power_per_thrust / 1000) * thrust_limit + +/obj/machinery/power/engine/ion/proc/burn() + if (!is_on()) + return 0 + if(!check_power()) + audible_message(src,"[src] sparks once or twice, then goes dark!") + on = !on + return 0 + . = get_thrust() + stored_power = max(0, stored_power - round(use_power_per_thrust * thrust_limit)) +//TODO: ion trail \ No newline at end of file diff --git a/code/modules/overmap/ships/panicbutton.dm b/code/modules/overmap/ships/panicbutton.dm new file mode 100644 index 0000000000000..e89f8b457f7ba --- /dev/null +++ b/code/modules/overmap/ships/panicbutton.dm @@ -0,0 +1,58 @@ +/obj/structure/panic_button + name = "distress beacon trigger" + desc = "WARNING: Will deploy ship's distress beacon and request help. Misuse may result in fines and jail time." + icon = 'icons/obj/objects.dmi' + icon_state = "panicbutton" + anchored = TRUE + + var/glass = TRUE + var/launched = FALSE + + +/obj/structure/panic_button/on_update_icon() + if(launched) + icon_state = "[initial(icon_state)]_launched" + else if(!glass) + icon_state = "[initial(icon_state)]_open" + else + icon_state = "[initial(icon_state)]" + +/obj/structure/panic_button/attack_hand(mob/living/user) + if(!istype(user)) + return ..() + + if(user.incapacitated()) + return + + // Already launched + if(launched) + to_chat(user, "The button is already depressed; the beacon has been launched already.") + // Glass present + else if(glass) + if(user.a_intent == I_HURT) + user.custom_emote(VISIBLE_MESSAGE, "smashes the glass on [src]!") + glass = FALSE + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg') + update_icon() + else + user.custom_emote(VISIBLE_MESSAGE, "pats [src] in a friendly manner.") + to_chat(user, "If you're trying to break the glass, you'll have to hit it harder than that...") + // Must be !glass and !launched + else if(!glass && !launched) + user.custom_emote(VISIBLE_MESSAGE, "pushes the button on [src]!") + playsound(src, get_sfx("button")) + update_icon() + launch(usr) + +/obj/structure/panic_button/proc/launch(mob/living/user) + var/sound/SND = sound('sound/misc/emergency_beacon_launched.ogg') // Inside the loop because playsound_local modifies it for each person, so, need separate instances + + if(launched) + return + launched = TRUE + var/obj/effect/overmap/visitable/S = get_overmap_sector(z) + if(!S) + error("Distress button hit on z[z] but that's not an overmap sector...") + return + S.distress(user) + playsound(src, SND, 25) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 5b94acb6537a7..9bec66fbd906a 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -136,6 +136,38 @@ SetName("[name] ([english_list(labels, and_text = ", ")])") update_icon() +/obj/item/ammo_magazine/afterattack(atom/target, mob/living/user, proximity_flag) + if(!proximity_flag || (!istype(target, /turf) && !istype(target, /obj/item/ammo_casing))) + return ..() + + var/turf/T = istype(target, /turf) ? target : get_turf(target) + if(istype(target, /turf)) + if(!locate(/obj/item/ammo_casing) in T) + return ..() + + var/curr_ammo = length(stored_ammo) + if(curr_ammo >= max_ammo) + to_chat(user, "[src] is full!") + return + + to_chat(user, SPAN_NOTICE("You begin inserting casings into \the [src]...")) + if(!do_after(user, (max_ammo - curr_ammo) * 2, src)) + return + + for(var/obj/item/ammo_casing/C in T) + if(stored_ammo.len >= max_ammo) + break + if(C.caliber != caliber) + continue + stored_ammo.Add(C) + C.forceMove(src) + + if(length(stored_ammo) - curr_ammo) + to_chat(user, SPAN_NOTICE("You insert [length(stored_ammo) - curr_ammo] casings into \the [src].")) + update_icon() + else + to_chat(user, SPAN_WARNING("You fail to collect any casings!")) + /obj/item/ammo_magazine/attackby(obj/item/W as obj, mob/user as mob) if(istype(W, /obj/item/ammo_casing)) var/obj/item/ammo_casing/C = W @@ -155,10 +187,17 @@ if(!stored_ammo.len) to_chat(user, "[src] is already empty!") return + if(!do_after(user, 10, src)) + return to_chat(user, "You empty [src].") + var/curr_sounds = 0 + var/max_sounds = clamp(round(length(stored_ammo) * 0.2), 1, 10) for(var/obj/item/ammo_casing/C in stored_ammo) C.forceMove(user.loc) C.set_dir(pick(GLOB.alldirs)) + if(LAZYLEN(C.fall_sounds) && curr_sounds < max_sounds) + playsound(user.loc, pick(C.fall_sounds), 20, TRUE) + curr_sounds += 1 stored_ammo.Cut() update_icon() diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index dcfe54753d42b..a827ca83f3770 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -222,3 +222,11 @@ damage = 10 armor_penetration = 35 damage_type = DAMAGE_BRUTE + +/obj/item/projectile/energy/acid_spit + name = "acid bolt" + icon_state = "toxin" + damage = 18 + damage_type = DAMAGE_BURN + fire_sound = 'sound/weapons/alien_spit.ogg' + pass_flags = PASS_FLAG_TABLE diff --git a/code/modules/psionics/equipment/implant.dm b/code/modules/psionics/equipment/implant.dm index 723a71517daca..e2cb64d200f2b 100644 --- a/code/modules/psionics/equipment/implant.dm +++ b/code/modules/psionics/equipment/implant.dm @@ -30,7 +30,8 @@ var/use_psi_mode = get_psi_mode() return (!malfunction && (use_psi_mode == PSI_IMPLANT_SHOCK || use_psi_mode == PSI_IMPLANT_WARN)) ? src : FALSE -/obj/item/implant/psi_control/removed() +/obj/item/implant/psi_control/ImplantRemoval(mob/user) + . = ..() var/mob/living/M = imp_in if(disrupts_psionics() && istype(M) && M.psi) to_chat(M, SPAN_NOTICE("Вы чувствуете, как исчезают холодные оковы, сковывающие ваши псионические способности.")) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm index 47a4cee484ee9..d38d2a77450c2 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm @@ -578,3 +578,119 @@ /datum/reagent/colored_hair_dye/chaos/affect_touch(mob/living/carbon/human/H, alien, removed) apply_dye_color(H, Frand(1, 254), Frand(1, 254), Frand(1, 254)) + + +/* Abominable Infestation reagents */ + +// Grauel - Basically a healing chem that can implant a larva into its user. +/datum/reagent/grauel + name = "Grauel" + description = "Reagent responsible for incubation of unknown lifeforms within its host. Has healing properties, but using it as medicine is a concerning idea." + taste_description = "vomit" + taste_mult = 1.4 + reagent_state = LIQUID + color = COLOR_MAROON + value = 4 + heating_products = list(/datum/reagent/nutriment/protein, /datum/reagent/laich) + heating_point = 120 CELSIUS + heating_message = "turns into a fleshy mess." + +/datum/reagent/grauel/affect_blood(mob/living/carbon/M, alien, removed) + if(alien == IS_DIONA) + return + var/heal_rate = 4 + if(M.chem_doses[/datum/reagent/laich] >= 1) + heal_rate *= 2.5 + M.heal_organ_damage(heal_rate * removed, 0) // Effectively weaker bicaridine, just with a tiny issue... + M.add_chemical_effect(CE_PAINKILLER, 5) + + var/dosage = M.chem_doses[type] + if(dosage < 5) // Only implant larvas on somewhat "high" dose + return + if(!prob(max(10, dosage*0.5))) + return + + var/list/valid_organs = list() + for(var/obj/item/organ/external/O in M.organs) + if(istype(O, /obj/item/organ/external/stump)) + continue + if(locate(/mob/living/simple_animal/hostile/infestation/larva) in O.implants) // One larva per limb + continue + valid_organs += O + if(!LAZYLEN(valid_organs)) + return + var/obj/item/organ/external/target_organ = pick(valid_organs) + var/mob/living/simple_animal/hostile/infestation/larva/implant/L = new(target_organ, TRUE) + target_organ.implants += L + L.transformation_time = world.time + rand(180 SECONDS, 360 SECONDS) + L.ai_holder.speak_chance = 0 + +// Laich - Reagent needed for creating of larva cube. +/datum/reagent/laich + name = "Laich" + description = "An important ingridient in creation of life. Causes mild suffocation. When consumed/injected alongside with Grauel - the later's healing properties were increased." + taste_description = "disgusting mess" + color = COLOR_ORANGE + +/datum/reagent/laich/affect_blood(mob/living/carbon/M, alien, removed) + if(alien == IS_DIONA) + return + if(prob(20)) + M.adjustOxyLoss(8) + if(ishuman(M) && prob(2)) + var/mob/living/carbon/human/H = M + H.vomit(2, 2, rand(2 SECONDS, 4 SECONDS)) + + +/datum/reagent/gottheit + name = "Gottheit" + description = "An impossibly powerful medicine, which is just as impossibly addictive." + taste_description = "pleasantly burning acid" + taste_mult = 5 + reagent_state = LIQUID + color = COLOR_YELLOW + value = 50 + +/datum/reagent/gottheit/affect_blood(mob/living/carbon/M, alien, removed) + if(alien == IS_DIONA) + return + + // Heal all conventional damage types + M.adjustCloneLoss(-40 * removed) + M.adjustOxyLoss(-10 * removed) + M.heal_organ_damage(40 * removed, 40 * removed) + M.adjustToxLoss(-40 * removed) + + // Some useful chem effects, including painkilling + M.add_chemical_effect(CE_PAINKILLER, 200) + M.add_chemical_effect(CE_SPEEDBOOST, 1) + + // Reduce bad effects + M.drowsyness = max(M.drowsyness - 50, 0) + M.adjust_hallucination(-50) + M.AdjustParalysis(-10) + M.AdjustStunned(-10) + M.AdjustWeakened(-10) + + // Super low doses won't do cool stuff + var/dosage = M.chem_doses[type] + if(dosage < 2) + return + + // Heal organs + if(ishuman(M)) + var/mob/living/carbon/human/H = M + for(var/obj/item/organ/internal/I in H.internal_organs) + if(!BP_IS_ROBOTIC(I)) + I.heal_damage(20 * removed) + for(var/obj/item/organ/external/E in H.organs) + if(E.status & ORGAN_ARTERY_CUT) + E.status &= ~ORGAN_ARTERY_CUT + if(E.status & ORGAN_TENDON_CUT) + E.status &= ~ORGAN_TENDON_CUT + if(E.status & ORGAN_BLEEDING) + E.status &= ~ORGAN_BLEEDING + if(E.status & ORGAN_BROKEN) + E.status &= ~ORGAN_BROKEN + if(E.status & ORGAN_DEAD) + E.status &= ~ORGAN_DEAD diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index d75b75e78e027..a96431fe1c913 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -3185,3 +3185,34 @@ catalysts = list( /datum/reagent/enzyme = 1 ) + + +/datum/chemical_reaction/bicaridine_alt + name = "Grauel Decomposition into Bicaridine" + result = /datum/reagent/bicaridine + required_reagents = list(/datum/reagent/grauel = 1, /datum/reagent/phosphorus = 1) + result_amount = 2 + +/datum/chemical_reaction/abomination_larva + name = "Abominable Larva" + result = null + required_reagents = list(/datum/reagent/laich = 10, /datum/reagent/phosphorus = 20) + +/datum/chemical_reaction/abomination_larva/on_reaction(datum/reagents/holder) + . = ..() + if(prob(66)) + new /obj/item/reagent_containers/food/snacks/monkeycube/abominationcube(get_turf(holder.my_atom)) + else + new /obj/item/reagent_containers/food/snacks/monkeycube/abominationcube/friendly(get_turf(holder.my_atom)) + + +/datum/chemical_reaction/gottheit + name = "Gottheit" + result = /datum/reagent/gottheit + result_amount = 2 + required_reagents = list( + /datum/reagent/grauel = 1, + /datum/reagent/rezadone = 1, + /datum/reagent/tramadol/oxycodone = 1, + /datum/reagent/peridaxon = 1, + ) diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index 6205956edc90e..f2954a2ad12fe 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -3907,3 +3907,14 @@ name = "taco" desc = "Interestingly, the shell has gone soft and the contents have gone stale." icon_state = "ancient_taco" + + +/obj/item/reagent_containers/food/snacks/monkeycube/abominationcube + name = "larva cube" + desc = "Requires blood to expand." + monkey_type = /mob/living/simple_animal/hostile/infestation/larva + color = COLOR_MAROON + filling_color = COLOR_MAROON + +/obj/item/reagent_containers/food/snacks/monkeycube/abominationcube/friendly + monkey_type = /mob/living/simple_animal/hostile/infestation/larva/friendly diff --git a/code/modules/reagents/reagent_containers/food/snacks/meat.dm b/code/modules/reagents/reagent_containers/food/snacks/meat.dm index 73a4cda1f63f0..38cfc26d683f6 100644 --- a/code/modules/reagents/reagent_containers/food/snacks/meat.dm +++ b/code/modules/reagents/reagent_containers/food/snacks/meat.dm @@ -46,3 +46,17 @@ /obj/item/reagent_containers/food/snacks/meat/chicken/game name = "game bird piece" desc = "Fresh game meat, harvested from some wild bird." + + +/obj/item/reagent_containers/food/snacks/abominationmeat + name = "meat" + desc = "A slab of red-ish meat. Smells terribly." + icon_state = "rottenmeat" + filling_color = COLOR_MAROON + center_of_mass = "x=16;y=10" + bitesize = 5 + +/obj/item/reagent_containers/food/snacks/abominationmeat/New() + ..() + reagents.add_reagent(/datum/reagent/nutriment/protein, 4) + reagents.add_reagent(/datum/reagent/grauel, 6) diff --git a/code/modules/research/designs/designs_circuits.dm b/code/modules/research/designs/designs_circuits.dm index 1c9e35004248a..963e2da8aeaa2 100644 --- a/code/modules/research/designs/designs_circuits.dm +++ b/code/modules/research/designs/designs_circuits.dm @@ -861,6 +861,19 @@ req_tech = list(TECH_ENGINEERING = 6, TECH_COMBAT = 7, TECH_POWER = 6, TECH_BLUESPACE = 6) build_path = /obj/item/stock_parts/circuitboard/remote_weapon/loadable/bs sort_string = "YAAAH" + +/datum/design/circuit/quantumpad + name = "quantum pad" + id = "quantumpad" + req_tech = list(TECH_BLUESPACE = 5, TECH_ENGINEERING = 5) + build_path = /obj/item/stock_parts/circuitboard/quantumpad + sort_string = "YAAAI" + +/datum/design/circuit/mining_quantumpad + name = "mining quantum pad" + id = "mining_quantumpad" + req_tech = list(TECH_BLUESPACE = 4, TECH_ENGINEERING = 4) + build_path = /obj/item/stock_parts/circuitboard/quantumpad/mining /datum/design/circuit/factory name = "automated production factory" diff --git a/code/modules/research/designs/designs_mining.dm b/code/modules/research/designs/designs_mining.dm index d41160d862d41..4da8a2bb1b640 100644 --- a/code/modules/research/designs/designs_mining.dm +++ b/code/modules/research/designs/designs_mining.dm @@ -68,3 +68,19 @@ materials = list(MATERIAL_STEEL = 1700, MATERIAL_GLASS = 1500, MATERIAL_PLASTIC = 500, MATERIAL_PHORON = 500) build_path = /obj/item/pickaxe/xeno/drill/plasma sort_string = "KAAAI" + +/datum/design/item/mining/drill_upgrade_automatic + desc = "Modifies mining drill to automatically dispense stored ores." + id = "drill_upgrade_automatic" + req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 3) + materials = list(MATERIAL_STEEL = 1000, MATERIAL_PLASTEEL = 750, MATERIAL_GLASS = 500, MATERIAL_ALUMINIUM = 250) + build_path = /obj/item/drill_upgrade/auto_dispense + sort_string = "KAAAJ" + +/datum/design/item/mining/drill_upgrade_range + desc = "Modifies mining drill to increase mining range." + id = "drill_upgrade_range" + req_tech = list(TECH_ENGINEERING = 4, TECH_POWER = 3) + materials = list(MATERIAL_STEEL = 1000, MATERIAL_PLASTEEL = 750, MATERIAL_GLASS = 500, MATERIAL_ALUMINIUM = 250) + build_path = /obj/item/drill_upgrade/range_increase + sort_string = "KAAAK" diff --git a/code/modules/species/outsider/abomination.dm b/code/modules/species/outsider/abomination.dm new file mode 100644 index 0000000000000..ecc45b73a990c --- /dev/null +++ b/code/modules/species/outsider/abomination.dm @@ -0,0 +1,88 @@ +// Human subspecies that were essentially produced(assimilated?) by the infestation. +// Very resistant to most damage sources except for burn. +// Very incomplete at the moment and not really used anywhere + +/datum/species/human/abomination + name = SPECIES_ABOMINATION + name_plural = "Abominations" + description = "A sub-product of bio-engineered species known as the \"Infestation\" or \"Abominations\". \ + Very dangerous and resistant to everything outside of burning things and high temperatures. \ + In the complex caste system of the Swarm, they are known to be among the highest-ranking leaders, \ + usually employed for more sophisticated tasks, such as infiltration and operating captured worlds." + show_ssd = "hybernating" + blood_color = COLOR_MAROON + flesh_color = COLOR_MAROON + + speech_chance = 50 + speech_sounds = list( + 'sound/voice/abomination1.ogg', + 'sound/voice/abomination2.ogg', + 'sound/voice/abomination3.ogg', + 'sound/voice/abomination4.ogg', + 'sound/voice/abomination5.ogg', + 'sound/voice/abomination6.ogg', + 'sound/voice/abomination7.ogg', + 'sound/voice/abomination8.ogg', + ) + + unarmed_types = list( + /datum/unarmed_attack/claws/strong/abomination, + /datum/unarmed_attack/bite/sharp, + ) + + darksight_range = 7 + breath_pressure = 5 + max_pressure_diff = 500 + + cold_level_1 = 60 + cold_level_2 = 30 + cold_level_3 = 0 + heat_level_1 = 330 + heat_level_2 = 360 + heat_level_3 = 600 + body_temperature = 290 + heat_discomfort_level = 305 + cold_discomfort_level = 75 + + natural_armour_values = list( + melee = ARMOR_MELEE_RESISTANT, + bullet = ARMOR_BALLISTIC_PISTOL, + laser = ARMOR_LASER_MAJOR, + energy = ARMOR_ENERGY_RESISTANT, + bomb = ARMOR_BOMB_PADDED, + bio = ARMOR_BIO_SHIELDED, + rad = ARMOR_RAD_SHIELDED + ) + + flash_mod = 0.5 + oxy_mod = 0.1 + toxins_mod = 0.2 + burn_mod = 2 + + hunger_factor = DEFAULT_HUNGER_FACTOR * 20 // Very hungry + thirst_factor = DEFAULT_THIRST_FACTOR + taste_sensitivity = TASTE_DULL + rarity_value = 10 + gluttonous = 2 + strength = STR_VHIGH + sexybits_location = null + vision_flags = SEE_SELF | SEE_MOBS + + has_organ = list( + BP_HEART = /obj/item/organ/internal/heart, + BP_STOMACH = /obj/item/organ/internal/stomach, + BP_LUNGS = /obj/item/organ/internal/lungs, + BP_LIVER = /obj/item/organ/internal/liver, + BP_KIDNEYS = /obj/item/organ/internal/kidneys, + BP_BRAIN = /obj/item/organ/internal/brain, + BP_APPENDIX = /obj/item/organ/internal/appendix, + BP_EYES = /obj/item/organ/internal/eyes, + BP_LARVA = /obj/item/organ/internal/larva_producer + ) + + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE_SPCR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + species_flags = SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_SLIP | SPECIES_FLAG_NO_MINOR_CUT + spawn_flags = SPECIES_IS_RESTRICTED | SPECIES_NO_FBP_CONSTRUCTION | SPECIES_NO_FBP_CHARGEN + +/datum/species/human/abomination/attempt_grab(mob/living/carbon/human/user, mob/living/target) + return ..(user, target, GRAB_ABOMINATION) diff --git a/code/modules/species/species_attack.dm b/code/modules/species/species_attack.dm index 638ed0b2eaa98..6a127d54fd0bf 100644 --- a/code/modules/species/species_attack.dm +++ b/code/modules/species/species_attack.dm @@ -83,6 +83,13 @@ /datum/unarmed_attack/claws/strong/gloves blocked_by_gloves = FALSE +/datum/unarmed_attack/claws/strong/abomination + attack_verb = list("eviscerated", "gored") + damage = 8 + attack_name = "terrifying claws" + attack_sound = 'sound/weapons/alien_claw_flesh2.ogg' + + /datum/unarmed_attack/bite/strong attack_verb = list("mauled") damage = 8 diff --git a/customs/code/krabinator.dm b/customs/code/krabinator.dm index a7732f060a1f9..82bcdbfda0683 100644 --- a/customs/code/krabinator.dm +++ b/customs/code/krabinator.dm @@ -11,4 +11,4 @@ desc = "Nice uniform sis." icon_state = "janimaid_alt" icon = 'customs/icons/obj/custom_items_obj.dmi' - item_icons = list(slot_head_str = 'customs/icons/mob/custom_items_mob.dmi') + item_icons = list(slot_wear_suit_str = 'customs/icons/mob/custom_items_mob.dmi') diff --git a/icons/mob/simple_animal/abominable_infestation/32x32.dmi b/icons/mob/simple_animal/abominable_infestation/32x32.dmi new file mode 100644 index 0000000000000..6170a78f2471b Binary files /dev/null and b/icons/mob/simple_animal/abominable_infestation/32x32.dmi differ diff --git a/icons/mob/simple_animal/abominable_infestation/48x48.dmi b/icons/mob/simple_animal/abominable_infestation/48x48.dmi new file mode 100644 index 0000000000000..afb17a37ee6b4 Binary files /dev/null and b/icons/mob/simple_animal/abominable_infestation/48x48.dmi differ diff --git a/icons/mob/simple_animal/abominable_infestation/64x64.dmi b/icons/mob/simple_animal/abominable_infestation/64x64.dmi new file mode 100644 index 0000000000000..252b601b75d69 Binary files /dev/null and b/icons/mob/simple_animal/abominable_infestation/64x64.dmi differ diff --git a/icons/obj/closets/bases/odst.dmi b/icons/obj/closets/bases/odst.dmi new file mode 100644 index 0000000000000..fbd18f090c103 Binary files /dev/null and b/icons/obj/closets/bases/odst.dmi differ diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi index 25e53c58d8898..f437baf1982fe 100644 Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ diff --git a/icons/obj/mining_drill.dmi b/icons/obj/mining_drill.dmi index 95aab5f5bea37..d8094234a0c79 100644 Binary files a/icons/obj/mining_drill.dmi and b/icons/obj/mining_drill.dmi differ diff --git a/icons/obj/missile_equipment.dmi b/icons/obj/missile_equipment.dmi new file mode 100644 index 0000000000000..c6f54ca70901b Binary files /dev/null and b/icons/obj/missile_equipment.dmi differ diff --git a/icons/obj/munitions.dmi b/icons/obj/munitions.dmi index 4631b35ba4b62..7b5362baf397c 100644 Binary files a/icons/obj/munitions.dmi and b/icons/obj/munitions.dmi differ diff --git a/icons/obj/overmap.dmi b/icons/obj/overmap.dmi index 3cda65da5e1c3..a36821fc688a8 100644 Binary files a/icons/obj/overmap.dmi and b/icons/obj/overmap.dmi differ diff --git a/icons/obj/structures/decor.dmi b/icons/obj/structures/decor.dmi new file mode 100644 index 0000000000000..f5b5ee54609d9 Binary files /dev/null and b/icons/obj/structures/decor.dmi differ diff --git a/icons/obj/structures/decor32x64.dmi b/icons/obj/structures/decor32x64.dmi new file mode 100644 index 0000000000000..ceb577bb2c45b Binary files /dev/null and b/icons/obj/structures/decor32x64.dmi differ diff --git a/icons/obj/structures/decor64x64.dmi b/icons/obj/structures/decor64x64.dmi new file mode 100644 index 0000000000000..e9ac905d19867 Binary files /dev/null and b/icons/obj/structures/decor64x64.dmi differ diff --git a/icons/obj/structures/decor96x96.dmi b/icons/obj/structures/decor96x96.dmi new file mode 100644 index 0000000000000..7229e30f238e6 Binary files /dev/null and b/icons/obj/structures/decor96x96.dmi differ diff --git a/icons/obj/structures/decor_fences.dmi b/icons/obj/structures/decor_fences.dmi new file mode 100644 index 0000000000000..155d5bef68c69 Binary files /dev/null and b/icons/obj/structures/decor_fences.dmi differ diff --git a/icons/obj/telescience.dmi b/icons/obj/telescience.dmi index dcd7f39d73846..20c9011e95e11 100644 Binary files a/icons/obj/telescience.dmi and b/icons/obj/telescience.dmi differ diff --git a/icons/turf/flooring/flesh.dmi b/icons/turf/flooring/flesh.dmi index 0653e6807395a..5cedd5abf01a7 100644 Binary files a/icons/turf/flooring/flesh.dmi and b/icons/turf/flooring/flesh.dmi differ diff --git a/maps/antag_spawn/mercenary/mercenary.dm b/maps/antag_spawn/mercenary/mercenary.dm index 4d5809b4d98a0..3efb4b7bf39ee 100644 --- a/maps/antag_spawn/mercenary/mercenary.dm +++ b/maps/antag_spawn/mercenary/mercenary.dm @@ -6,7 +6,6 @@ /obj/effect/overmap/visitable/sector/merc_base name = "Tiny Asteroid" desc = "Sensor array detects an small, insignificant asteroid. The core appears to be reflecting scans." - in_space = TRUE known = FALSE place_near_main = list(2, 4) icon_state = "meteor4" diff --git a/maps/antag_spawn/vox/voxraider.dm b/maps/antag_spawn/vox/voxraider.dm index 49baa5de1bbe7..97a6d826f23cb 100644 --- a/maps/antag_spawn/vox/voxraider.dm +++ b/maps/antag_spawn/vox/voxraider.dm @@ -6,7 +6,6 @@ /obj/effect/overmap/visitable/sector/vox_start name = "Empty Space" desc = "Just some empty space, with an irregular sensor echo." - in_space = TRUE known = FALSE place_near_main = list(2, 4) icon_state = "event" diff --git a/maps/away/blueriver/blueriver.dm b/maps/away/blueriver/blueriver.dm index b1373c79a0cca..521063ed9e37e 100644 --- a/maps/away/blueriver/blueriver.dm +++ b/maps/away/blueriver/blueriver.dm @@ -3,7 +3,6 @@ /obj/effect/overmap/visitable/sector/arcticplanet name = "arctic planetoid" desc = "Sensor array detects an arctic planet with a small vessel on the planet's surface. Scans further indicate strange energy emissions from below the planet's surface." - in_space = FALSE icon_state = "globe" initial_generic_waypoints = list( "nav_blueriv_1", diff --git a/maps/away/skrellscoutship/skrellscoutship.dm b/maps/away/skrellscoutship/skrellscoutship.dm index 831fb95761129..02dae80af8bce 100644 --- a/maps/away/skrellscoutship/skrellscoutship.dm +++ b/maps/away/skrellscoutship/skrellscoutship.dm @@ -22,7 +22,6 @@ /obj/effect/overmap/visitable/sector/skrellscoutspace name = "Empty Sector" desc = "Slight traces of a cloaking device are present. Unable to determine exact location." - in_space = TRUE icon_state = "event" hide_from_reports = TRUE diff --git a/maps/away/voxship/voxship.dm b/maps/away/voxship/voxship.dm index d69000f26f6e9..6e6d2e90cfa44 100644 --- a/maps/away/voxship/voxship.dm +++ b/maps/away/voxship/voxship.dm @@ -20,7 +20,6 @@ /obj/effect/overmap/visitable/sector/vox_scav_ship name = "small asteroid cluster" desc = "Sensor array detects a small asteroid cluster." - in_space = TRUE icon_state = "meteor4" hide_from_reports = TRUE initial_generic_waypoints = list( diff --git a/maps/torch/torch5_deck1.dmm b/maps/torch/torch5_deck1.dmm index 9f5952993bb2c..2f59a1b161dc6 100644 --- a/maps/torch/torch5_deck1.dmm +++ b/maps/torch/torch5_deck1.dmm @@ -9115,14 +9115,11 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftport) "aEG" = ( -/obj/effect/floor_decal/industrial/outline/grey, /obj/structure/table/rack, -/obj/item/clothing/shoes/dutyboots, -/obj/item/clothing/accessory/armband/bluegold, -/obj/item/clothing/accessory/storage/holster/thigh, -/obj/item/clothing/glasses/tacgoggles, -/obj/item/clothing/suit/armor/pcarrier/medium/sol, -/obj/item/clothing/head/helmet, +/obj/item/gun/projectile/automatic/carabine_tactical, +/obj/item/ammo_magazine/carabine_rifle, +/obj/item/ammo_magazine/carabine_rifle, +/obj/item/ammo_magazine/carabine_rifle, /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury/tactical) "aEH" = ( @@ -18360,9 +18357,6 @@ /obj/item/gun/energy/stunrevolver/rifle, /obj/item/gun/energy/taser/carbine, /obj/item/gun/energy/taser/carbine, -/obj/machinery/light{ - dir = 1 - }, /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury/tactical) "hub" = ( @@ -18711,17 +18705,6 @@ /turf/simulated/floor/tiled/white, /area/rnd/development) "hLf" = ( -/obj/effect/floor_decal/industrial/outline/grey, -/obj/structure/table/rack, -/obj/item/clothing/shoes/dutyboots, -/obj/item/clothing/accessory/armband/bluegold, -/obj/item/clothing/accessory/storage/holster/thigh, -/obj/item/clothing/glasses/tacgoggles, -/obj/item/clothing/suit/armor/pcarrier/medium/sol, -/obj/item/clothing/head/helmet, -/obj/machinery/recharger/wallcharger{ - pixel_y = 24 - }, /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury/tactical) "hLD" = ( @@ -18801,6 +18784,19 @@ /obj/effect/floor_decal/industrial/hatch/yellow, /turf/simulated/floor/tiled/dark, /area/rnd/rnd_sec) +"hQl" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/item/clothing/mask/gas/half, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "hUb" = ( /obj/machinery/door/blast/shutters{ density = 0; @@ -21058,6 +21054,22 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/centralstarboard) +"kML" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/mask/gas/half, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "kOb" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 @@ -25841,9 +25853,6 @@ /obj/item/shield/riot/metal, /obj/item/shield/riot/metal, /obj/item/shield/riot/metal, -/obj/machinery/light{ - dir = 1 - }, /turf/simulated/floor/tiled/techfloor/grid, /area/command/armoury/tactical) "qLb" = ( @@ -26258,6 +26267,22 @@ }, /turf/simulated/floor/tiled/freezer, /area/rnd/xenobiology/xenoflora) +"rkZ" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/item/clothing/mask/gas/half, +/obj/machinery/recharger/wallcharger{ + pixel_y = 24 + }, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "rlb" = ( /obj/machinery/atmospherics/unary/freezer{ dir = 1; @@ -27446,6 +27471,28 @@ }, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/aft) +"ssN" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/machinery/light{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/mask/gas/half, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "stb" = ( /obj/effect/floor_decal/corner/research/mono, /obj/effect/floor_decal/industrial/outline/yellow, @@ -27457,6 +27504,22 @@ }, /turf/simulated/floor/tiled/white/monotile, /area/rnd/xenobiology/xenoflora) +"stv" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/mask/gas/half, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "sub" = ( /obj/structure/closet/secure_closet/hydroponics_torch, /obj/effect/floor_decal/corner/research/mono, @@ -29192,6 +29255,28 @@ /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/plating, /area/maintenance/firstdeck/centralstarboard) +"uWS" = ( +/obj/effect/floor_decal/industrial/outline/grey, +/obj/structure/table/rack, +/obj/item/clothing/shoes/dutyboots, +/obj/item/clothing/accessory/armband/bluegold, +/obj/item/clothing/accessory/storage/holster/thigh, +/obj/item/clothing/suit/armor/pcarrier/medium/sol, +/obj/item/clothing/head/helmet, +/obj/item/clothing/accessory/glassesmod/nvg, +/obj/item/clothing/glasses/ballistic/security, +/obj/machinery/light{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/mask/gas/half, +/turf/simulated/floor/tiled/techfloor/grid, +/area/command/armoury/tactical) "uXe" = ( /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/aftstarboard) @@ -57982,10 +58067,10 @@ dUZ aaa acd acd -acd -acd dVw dVw +ssN +stv htK hFu vjY @@ -58184,13 +58269,13 @@ bhb aaa aaa aaa -acd -acd dVw dVw wSf svH svH +svH +svH swz aBc iBb @@ -58386,10 +58471,10 @@ abN aaa aaa aaa -acd -acd dVw dVw +rkZ +hQl hLf aEG aEG @@ -58588,13 +58673,13 @@ aiI ajJ aaa aaa -acd -acd dVw dVw ryt svH svH +svH +svH njm ksK nZZ @@ -58790,10 +58875,10 @@ abN aaa aaa acd -acd -acd dVw dVw +rkZ +hQl hLf aEG aEG @@ -58992,13 +59077,13 @@ abN aaa aaa acd -acd -acd dVw dVw sET svH svH +svH +svH nWP aBc cZd @@ -59194,10 +59279,10 @@ aaa aaa aaa acd -acd -acd dVw dVw +uWS +kML qKQ iAS bCX @@ -59396,8 +59481,8 @@ aaa aaa aaa acd -acd -acd +dVw +dVw dVw dVw dVw @@ -59600,7 +59685,7 @@ aaa aaa acd acd -acd +dVw dVw dVw dVw diff --git a/maps/torch/torch_overmap.dm b/maps/torch/torch_overmap.dm index 675bc24a5d04f..d639076078248 100644 --- a/maps/torch/torch_overmap.dm +++ b/maps/torch/torch_overmap.dm @@ -4,7 +4,6 @@ fore_dir = WEST vessel_mass = 100000 burn_delay = 2 SECONDS - base = TRUE initial_restricted_waypoints = list( "Charon" = list("nav_hangar_charon"), //can't have random shuttles popping inside the ship diff --git a/proxima/code/game/items/weapons/gun/projectiles/bosnia.dm b/proxima/code/game/items/weapons/gun/projectiles/bosnia.dm index b4678d34acf65..c56bf0da180fc 100644 --- a/proxima/code/game/items/weapons/gun/projectiles/bosnia.dm +++ b/proxima/code/game/items/weapons/gun/projectiles/bosnia.dm @@ -192,3 +192,63 @@ /obj/item/gun/projectile/automatic/bandit/on_update_icon() ..() icon_state = (ammo_magazine)? "mpistolen" : "mpistolen-empty" + + +/obj/item/gun/projectile/automatic/carabine_tactical + name = "Special forces carabine" + desc = "SC-42 carabine - These are weapons specially designed to resolve conflicts on the streets without attracting unnecessary attention. By integrating a silencer, it is mainly used by the military police of the SCG." + icon = 'proxima/icons/obj/guns/carabine.dmi' + icon_state = "arg75" + item_state = "arifle" + wielded_item_state = "arifle-wielded" + item_icons = list( + slot_r_hand_str = 'proxima/icons/mob/onmob/items/guns_r_default.dmi', + slot_l_hand_str = 'proxima/icons/mob/onmob/items/guns_l_default.dmi', + ) + fire_delay = 4 + caliber = CALIBER_CARABINE + ammo_type = /obj/item/ammo_casing/rifle/carabine + magazine_type = /obj/item/ammo_magazine/carabine_rifle + load_method = MAGAZINE + allowed_magazines = /obj/item/ammo_magazine/carabine_rifle + silenced = TRUE + multi_aim = 1 + can_special_reload = TRUE + firemodes = list( + list(mode_name = "semiauto", mode_desc = "Fire as fast as you can pull the trigger", burst=1, fire_delay=0, move_delay=null), + list(mode_name="2-round bursts", mode_desc = "Short, controlled bursts", burst=2, fire_delay=null, move_delay=2, one_hand_penalty=2), + list(mode_name="3-round bursts", mode_desc = "Short, controlled bursts", burst=3, fire_delay=null, move_delay=4, one_hand_penalty=3) + ) + +/obj/item/gun/projectile/automatic/carabine_tactical/on_update_icon() + ..() + icon_state = "arg75" + if(ammo_magazine) + overlays += image(icon, "arg75_mag") + + +/obj/item/ammo_magazine/carabine_rifle + name = "carabine rifle magazine" + icon = 'proxima/icons/obj/ammo.dmi' + icon_state = "carabine" + origin_tech = list(TECH_COMBAT = 2) + mag_type = MAGAZINE + caliber = CALIBER_CARABINE + matter = list(MATERIAL_STEEL = 1400) + ammo_type = /obj/item/ammo_casing/rifle/carabine + max_ammo = 20 + multiple_sprites = 1 + +/obj/item/ammo_casing/rifle/carabine + desc = "A spec.ops carabine casing." + caliber = CALIBER_CARABINE + projectile_type = /obj/item/projectile/bullet/rifle/carabine + icon_state = "rifle_mil" + spent_icon = "rifle_mil-spent" + +/obj/item/projectile/bullet/rifle/carabine + fire_sound = 'sound/weapons/gunshot/gunshot3.ogg' + damage = 35 + armor_penetration = 30 + penetration_modifier = 1 + penetrating = 0 //INF, WAS 1 diff --git a/proxima/code/game/machinery/factory/quantum_pads.dm b/proxima/code/game/machinery/factory/quantum_pads.dm new file mode 100644 index 0000000000000..ff4dd93e11141 --- /dev/null +++ b/proxima/code/game/machinery/factory/quantum_pads.dm @@ -0,0 +1,248 @@ +/obj/machinery/quantumpad + name = "quantum pad" + desc = "A bluespace quantum-linked telepad used for teleporting objects to other quantum pads." + icon = 'icons/obj/telescience.dmi' + icon_state = "qpad-idle" + idle_power_usage = 10 KILOWATTS + construct_state = /decl/machine_construction/default/panel_closed + wires = /datum/wires/quantumpad + maximum_component_parts = list(/obj/item/stock_parts = 14) + var/icon_state_default = "qpad" + var/teleport_delay = 2 SECONDS + var/teleport_cooldown_time = 30 SECONDS + var/teleport_cooldown + var/teleporting = FALSE //if it's in the process of teleporting + /// List of whitelisted types; If set - only these things can pass through + var/list/teleport_only = list() + var/teleport_power_cost = 100 KILOWATTS + var/power_efficiency = 1 + var/obj/machinery/quantumpad/linked_pad + + // Mapping + var/static/list/mapped_quantum_pads = list() + var/map_pad_id = "" + var/map_pad_link_id = "" + +/obj/machinery/quantumpad/Initialize() + . = ..() + if(map_pad_id) + mapped_quantum_pads[map_pad_id] = src + +/obj/machinery/quantumpad/Destroy() + mapped_quantum_pads -= map_pad_id + return ..() + +/obj/machinery/quantumpad/examine(mob/user) + . = ..() + to_chat(user, SPAN_NOTICE("It is [ linked_pad ? "currently" : "not"] linked to another pad.")) + if(!panel_open) + to_chat(user, SPAN_NOTICE("The panel is screwed in, obstructing the linking device.")) + else + to_chat(user, SPAN_NOTICE("The linking device is now able to be scanned with a multitool.")) + + if(world.time < teleport_cooldown) + to_chat(user, SPAN_NOTICE("[src] will be operational in [round((teleport_cooldown - world.time) / 10)] seconds.")) + +/obj/machinery/quantumpad/on_update_icon() + if(panel_open || (stat & NOPOWER)) + icon_state = "[icon_state_default]-o" + else + icon_state = "[icon_state_default]-idle" + return + +/obj/machinery/quantumpad/attackby(obj/item/I, mob/user, params) + if(user.a_intent == I_HURT) + return ..() + + if(istype(I, /obj/item/device/assembly/signaler) && panel_open) + return wires.Interact(user) + + if(istype(I, /obj/item/device/multitool)) + var/obj/item/device/multitool/M = I + if(panel_open) + M.set_buffer(src) + to_chat(user, SPAN_NOTICE("You save the data in [M]'s buffer. It can now be linked to pads with closed panels.")) + return TRUE + + else + var/obj/machinery/quantumpad/buffer = M.get_buffer(/obj/machinery/quantumpad) + if(!istype(buffer)) + return TRUE + if(buffer == src) + to_chat(user, SPAN_WARNING("You cannot link a pad to itself!")) + return TRUE + else + linked_pad = buffer + to_chat(user, SPAN_NOTICE("You link [src] to the one in [I]'s buffer.")) + return TRUE + + if(isWrench(I) && !panel_open) + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + to_chat(user, SPAN_NOTICE("You [anchored ? "un" : ""]anchor \the [src].")) + anchored = !anchored + return TRUE + + return ..() + +/obj/machinery/quantumpad/physical_attack_hand(mob/user, obj/machinery/quantumpad/target_pad = linked_pad) + if(!target_pad || QDELETED(target_pad)) + if(!map_pad_link_id || !initMappedLink()) + to_chat(user, SPAN_WARNING("Target pad not found!")) + return + + if(panel_open) + to_chat(user, SPAN_WARNING("[src]'s panel is open!")) + return + + if(stat & NOPOWER) + to_chat(user, SPAN_WARNING("[src] is unpowered!")) + return + + if(teleporting) + to_chat(user, SPAN_WARNING("[src] is charging up. Please wait.")) + return + + if(!anchored) + to_chat(user, SPAN_WARNING("[src] is not anchored.")) + return + + if(world.time < teleport_cooldown) + to_chat(user, SPAN_WARNING("[src] is recharging. Please wait for [round((teleport_cooldown - world.time) / 10)] seconds.")) + return + + if(target_pad.teleporting) + to_chat(user, SPAN_WARNING("Target pad is busy. Please wait.")) + return + + if(target_pad.stat & NOPOWER) + to_chat(user, SPAN_WARNING("Target pad is not responding to ping.")) + return + + if(!target_pad.anchored) + to_chat(user, SPAN_WARNING("Target pad is not anchored.")) + return + + add_fingerprint(user) + AttemptTeleport(user, target_pad) + return TRUE + +/obj/machinery/quantumpad/attack_ghost(mob/observer/ghost) + . = ..() + if(.) + return + if(!linked_pad && map_pad_link_id) + initMappedLink() + if(linked_pad) + ghost.forceMove(get_turf(linked_pad)) + +/obj/machinery/quantumpad/RefreshParts() + ..() + teleport_delay = 15 SECONDS / clamp(total_component_rating_of_type(/obj/item/stock_parts/micro_laser), 1, 30) + teleport_cooldown_time = 150 SECONDS / clamp(total_component_rating_of_type(/obj/item/stock_parts/micro_laser), 1, 30) + power_efficiency = 0.2 * clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 1, 30) + +/obj/machinery/quantumpad/proc/sparks() + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, get_turf(src)) + s.start() + +/obj/machinery/quantumpad/proc/AttemptTeleport(mob/user, obj/machinery/quantumpad/target_pad = linked_pad) + if(!target_pad) + return + + playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE) + teleporting = TRUE + addtimer(CALLBACK(src, .proc/DoTeleport, user, target_pad), teleport_delay) + +/obj/machinery/quantumpad/proc/DoTeleport(mob/user, obj/machinery/quantumpad/target_pad = linked_pad) + if(!src || QDELETED(src)) + teleporting = FALSE + return + if(stat & NOPOWER) + to_chat(user, SPAN_WARNING("[src] is unpowered!")) + teleporting = FALSE + return + if(!target_pad || QDELETED(target_pad) || target_pad.stat & NOPOWER) + to_chat(user, SPAN_WARNING("Linked pad is not responding to ping. Teleport aborted.")) + teleporting = FALSE + return + if(can_use_power_oneoff(teleport_power_cost / power_efficiency, powered() ? power_channel : LOCAL)) + to_chat(user, SPAN_WARNING("Not enough power to perform the jump. Teleport aborted.")) + teleporting = FALSE + return + + use_power_oneoff(teleport_power_cost / power_efficiency, powered() ? power_channel : LOCAL) + teleporting = FALSE + teleport_cooldown = world.time + teleport_cooldown_time + + sparks() + target_pad.sparks() + + flick("[icon_state_default]-beam", src) + playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, TRUE) + flick("[target_pad.icon_state_default]-beam", target_pad) + playsound(get_turf(target_pad), 'sound/weapons/emitter2.ogg', 25, TRUE) + for(var/atom/movable/ROI in get_turf(src)) + if(QDELETED(ROI)) + continue // Sleeps in CHECK_TICK + if(LAZYLEN(teleport_only) && !(ROI.type in teleport_only)) + continue // Cannot teleport these + + // if is anchored, don't let through + if(ROI.anchored) + if(isliving(ROI)) + var/mob/living/L = ROI + //only TP living mobs buckled to non anchored items + if(!L.buckled || L.buckled.anchored) + continue + else + continue + + // Don't TP ghosts + if(isobserver(ROI)) + continue + + do_teleport(ROI, get_turf(target_pad), type = /decl/teleport) + CHECK_TICK + +/obj/machinery/quantumpad/proc/initMappedLink() + . = FALSE + var/obj/machinery/quantumpad/link = mapped_quantum_pads[map_pad_link_id] + if(link) + linked_pad = link + . = TRUE + +/obj/machinery/quantumpad/mining + name = "mining quantum pad" + desc = "A bluespace quantum-linked telepad used for teleporting ores to other quantum pads." + icon_state = "pad_idle" + icon_state_default = "pad" + idle_power_usage = 0 + teleport_power_cost = 20 KILOWATTS // It only teleports ores, after all + maximum_component_parts = list(/obj/item/stock_parts = 12) + +/obj/machinery/quantumpad/mining/Initialize() + . = ..() + teleport_only = subtypesof(/obj/item/ore) + + +/obj/item/stock_parts/circuitboard/quantumpad + name = T_BOARD("quantum pad") + build_path = /obj/machinery/quantumpad + board_type = "machine" + origin_tech = list(TECH_BLUESPACE = 5, TECH_ENGINEERING = 5) + req_components = list( + /obj/item/stock_parts/capacitor = 5, + /obj/item/stock_parts/micro_laser = 5) + additional_spawn_components = list( + /obj/item/stock_parts/power/battery/buildable/stock, + /obj/item/cell/standard = 1 + ) + +/obj/item/stock_parts/circuitboard/quantumpad/mining + name = T_BOARD("mining quantum pad") + build_path = /obj/machinery/quantumpad/mining + origin_tech = list(TECH_BLUESPACE = 4, TECH_ENGINEERING = 4) + req_components = list( + /obj/item/stock_parts/capacitor = 3, + /obj/item/stock_parts/micro_laser = 5) diff --git a/proxima/code/game/objects/items/bouquet.dm b/proxima/code/game/objects/items/bouquet.dm new file mode 100644 index 0000000000000..ac1422497074d --- /dev/null +++ b/proxima/code/game/objects/items/bouquet.dm @@ -0,0 +1,77 @@ +/obj/item/bouquet + name = "Glass bouquet" + desc = "Beautiful bouquet with flowers. It smells indescribably delicious!" + icon = 'proxima/icons/obj/bouquet.dmi' + icon_state = "bouquet_base" + item_state = "bouquet_base" + var/list/allowedplants = list( + "harebells", + "poppies", + "lavender", + "sunflowers" + ) + var/list/contentflovers = list() + var/filled = FALSE + +/obj/item/bouquet/on_update_icon() + for(var/obj/item/reagent_containers/food/snacks/grown/flower as anything in contentflovers) + switch (flower.plantname) + if ("harebells") overlays += image(icon, "flower_1") + if ("poppies") overlays += image(icon, "flower_2") + if ("lavender") overlays += image(icon, "flower_3") + if ("sunflowers") overlays += image(icon, "flower_4") + . = ..() + +/obj/item/bouquet/attackby(obj/item/weapon, mob/user) + if(!istype(weapon, /obj/item/reagent_containers/food/snacks/grown) || filled) + return ..() + + var/obj/item/reagent_containers/food/snacks/grown/flower = weapon + + if(flower in contentflovers) + to_chat(user, SPAN_WARNING("There is already this flower here.")) + return + + if(!(flower.plantname in allowedplants)) + to_chat(user, SPAN_WARNING("This flower will look to strange in bouquet...")) + return + + contentflovers.Add(flower) + qdel(flower) + update_icon() + visible_message(SPAN_INFO("[user] put's [flower.plantname] in the bouquet.")) + if(length(contentflovers) >= 4) filled = TRUE + +/obj/item/bouquet/throw_impact(atom/hit_atom) + if(QDELETED(src)) + return + + visible_message( + SPAN_DANGER("\The [src] shatters from the impact!"), + SPAN_DANGER("You hear the sound of glass shattering!") + ) + playsound(src.loc, pick(GLOB.shatter_sound), 100) + new /obj/item/material/shard(src.loc) + qdel(src) + +/obj/item/bouquet/premaded + icon_state = "bouquet_preview" + item_state = "bouquet_preview" + filled = TRUE + +/obj/item/reagent_containers/food/drinks/glass2/carafe/attackby(obj/item/I, mob/user) + if(!istype(I, /obj/item/reagent_containers/food/snacks/grown)) + return ..() + + if(reagents.total_volume) + to_chat(user, SPAN_WARNING("The pitcher must be empty!")) + return + + var/obj/item/reagent_containers/food/snacks/grown/flower = I + if(flower.plantname != "grass") + return + qdel(flower) + var/obj/item/bouquet/B = new(get_turf(src)) + qdel(src) + user.put_in_any_hand_if_possible(B) + to_chat(user, SPAN_INFO("You placed grass into the pitcher.")) diff --git a/proxima/code/game/objects/items/mgsbox.dm b/proxima/code/game/objects/items/mgsbox.dm new file mode 100644 index 0000000000000..05df17eaf500b --- /dev/null +++ b/proxima/code/game/objects/items/mgsbox.dm @@ -0,0 +1,189 @@ +//snake? snake! SNAKE?!!!!!!!!!! +/obj/item/storage/mgsbox + name = "cardboard box" + icon = 'proxima/icons/obj/box.dmi' + icon_state = "box" + desc = "Just ordinary and absolutely unsuspicious box." + w_class = ITEM_SIZE_NO_CONTAINER + max_w_class = ITEM_SIZE_GARGANTUAN + max_storage_space = 40 + allow_quick_empty = 1 + allow_quick_gather = 1 + randpixel = 2 + + var/open = 0 + var/stealth_mode = 0 + var/sprite = "box" + var/sprite_open = "box-open" + var/taping_level = 0 + var/obj/effect/dummy/box/active_dummy = null + var/image/overlay + +/obj/item/storage/mgsbox/AltClick(mob/usr) + if(!(src in view(1, usr))) + return + + if(stealth_mode == 1) + to_chat(usr, SPAN_WARNING("You can't do it while in box!")) + return + + if(taping_level == 1) + to_chat(usr, SPAN_NOTICE("You begin to remove duct tape from the box...")) + if(do_after(usr, 6 SECONDS, src, DO_SHOW_PROGRESS | DO_PUBLIC_PROGRESS | DO_BAR_OVER_USER)) + to_chat(usr, SPAN_NOTICE("You're done removing the duct tape, now you can open the box.")) + taping_level = 0 + src.overlays -= overlay + return + else if(taping_level == 2) + to_chat(usr, SPAN_WARNING("There so much duct tape - you even can't open the box! Maybe just break it?..")) + return + + if(open == 0) + to_chat(usr, SPAN_NOTICE("You open the box.")) + open = 1 + icon_state = sprite_open + playsound(src, 'sound/effects/storage/box.ogg', 50, 1) + else + to_chat(usr, SPAN_NOTICE("You close the box.")) + open = 0 + icon_state = sprite + +/obj/item/storage/mgsbox/proc/is_open() + if(stealth_mode == 1) + to_chat(usr, SPAN_WARNING("You can't do it while in box!")) + return 0 + + if(open == 0) + to_chat(usr, SPAN_NOTICE("You need to open the box.")) + return 0 + else + return 1 + +/obj/item/storage/mgsbox/proc/taping() + if(taping_level == 0) + to_chat(usr, SPAN_NOTICE("You begin to taping the box...")) + playsound(src, 'sound/effects/tape.ogg', 25) + if(do_after(usr, 5 SECONDS, src, DO_SHOW_PROGRESS | DO_PUBLIC_PROGRESS | DO_BAR_OVER_USER)) + to_chat(usr, SPAN_NOTICE("You finish taping of the box. You can tape once more, but then it will be impossible to open this box.")) + taping_level = 1 + overlay = image(icon, icon_state = "taping_level1") + src.overlays += overlay + else if(taping_level == 1) + to_chat(usr, SPAN_NOTICE("You begin to taping the box...")) + playsound(src, 'sound/effects/tape.ogg', 25) + if(do_after(usr, 7 SECONDS, src, DO_SHOW_PROGRESS | DO_PUBLIC_PROGRESS | DO_BAR_OVER_USER)) + to_chat(usr, SPAN_NOTICE("This box is now fully secured by duct tape.")) + taping_level = 2 + src.overlays -= overlay + overlay = image(icon, icon_state = "taping_level2") + src.overlays += overlay + else + to_chat(usr, SPAN_WARNING("You don't know where else possible to stick the duct tape here.")) + +/obj/item/storage/mgsbox/attackby(obj/item/W, mob/user) + if((istype(W, /obj/item/tape_roll)) && (open == 0) && (stealth_mode == 0)) + taping() + return + + if((istype(W, /obj/item/crowbar)) || (istype(W, /obj/item/melee)) || (istype(W, /obj/item/material/twohanded)) || (istype(W, /obj/item/material/hatchet)) || (istype(W, /obj/item/material/sword))) + if(usr.a_intent == I_HURT) + playsound(src, 'sound/weapons/pierce.ogg', 75) + quick_empty() + visible_message(SPAN_WARNING("[usr] breaks the box with [W]!")) + new /obj/item/stack/material/cardboard(src.loc) + new /obj/item/stack/material/cardboard(src.loc) + qdel(src) + return + + if(is_open()) + . = ..() //When open work like ordinary storage. Same with commands below + else + return + +/obj/item/storage/mgsbox/MouseDrop(obj/over_object as obj) + if(is_open()) + . = ..() + else + return + +/obj/item/storage/mgsbox/attack_hand(mob/user as mob) + if(is_open()) + . = ..() + else + usr.put_in_active_hand(src) + +/obj/item/storage/mgsbox/quick_empty() + if(is_open()) + . = ..() + else + return + +/obj/item/storage/mgsbox/toggle_gathering_mode() + if(is_open()) + . = ..() + else + return + +/obj/item/storage/mgsbox/examine(mob/user, distance) + . = ..(user) + if(distance <= 2) + if(taping_level == 1) + to_chat(usr, SPAN_NOTICE("You notice that this box is secured with duct tape.")) + else if(taping_level == 2) + to_chat(usr, SPAN_NOTICE("You notice that this box is taped in every possible place.")) +/* + + Here's to you, Nicola and Bart + Rest Forever here in our hearts + The last and final moment is yours + That agony is your triumph +*/ + +/************ +* Other boxes +*************/ +/obj/item/storage/mgsbox/lpa + name = "cardboard box" + icon = 'proxima/icons/obj/box.dmi' + icon_state = "boxlpa" + desc = "Not ordinary and prety suspicious box." + + sprite = "boxlpa" + sprite_open = "boxlpa-open" + +/obj/item/storage/mgsbox/med + name = "cardboard box" + icon = 'proxima/icons/obj/box.dmi' + icon_state = "boxmed" + desc = "A blue cardboard box with medical symbols. Destruction of this box probably violate the Moon Convention." + + sprite = "boxmed" + sprite_open = "boxmed-open" + +/obj/item/storage/mgsbox/clear + name = "cardboard box" + icon = 'proxima/icons/obj/box.dmi' + icon_state = "boxclear" + desc = "A cardboard box without any symbols. Probably hand-made." + + sprite = "boxclear" + sprite_open = "boxclear-open" + + +/**************** +* Boxes with loot +****************/ +/obj/item/storage/mgsbox/med/loot_medicaments + startswith = list(/obj/random/firstaid, /obj/random/medical, /obj/random/medical) + +/obj/item/storage/mgsbox/loot_material + startswith = list(/obj/random/material, /obj/random/tool, /obj/random/maintenance, /obj/random/maintenance) + +/obj/item/storage/mgsbox/loot_engineering + startswith = list(/obj/random/toolbox, /obj/item/stack/cable_coil/random, /obj/item/stack/cable_coil/random, /obj/random/maintenance) + +/obj/item/storage/mgsbox/loot_accesory + startswith = list(/obj/random/clothing, /obj/random/accessory, /obj/random/accessory, /obj/random/masks, /obj/random/hat) + +/obj/item/storage/mgsbox/loot_meal + startswith = list(/obj/item/storage/mre/random, /obj/random/drinkbottle, /obj/random/snack, /obj/random/single/cola) diff --git a/proxima/icons/obj/ammo.dmi b/proxima/icons/obj/ammo.dmi new file mode 100644 index 0000000000000..e74b672d00d71 Binary files /dev/null and b/proxima/icons/obj/ammo.dmi differ diff --git a/proxima/icons/obj/bouquet.dmi b/proxima/icons/obj/bouquet.dmi new file mode 100644 index 0000000000000..110cf3c58c1eb Binary files /dev/null and b/proxima/icons/obj/bouquet.dmi differ diff --git a/proxima/icons/obj/box.dmi b/proxima/icons/obj/box.dmi new file mode 100644 index 0000000000000..20c915ace7f04 Binary files /dev/null and b/proxima/icons/obj/box.dmi differ diff --git a/proxima/icons/obj/cart.dmi b/proxima/icons/obj/cart.dmi new file mode 100644 index 0000000000000..6b7b80483e988 Binary files /dev/null and b/proxima/icons/obj/cart.dmi differ diff --git a/proxima/icons/obj/guns/carabine.dmi b/proxima/icons/obj/guns/carabine.dmi new file mode 100644 index 0000000000000..6f760cffe925b Binary files /dev/null and b/proxima/icons/obj/guns/carabine.dmi differ diff --git a/sound/AI/sos.ogg b/sound/AI/sos.ogg new file mode 100644 index 0000000000000..ded835c4784c6 Binary files /dev/null and b/sound/AI/sos.ogg differ diff --git a/sound/ambience/infested_forest.ogg b/sound/ambience/infested_forest.ogg new file mode 100644 index 0000000000000..4bd2321dedec7 Binary files /dev/null and b/sound/ambience/infested_forest.ogg differ diff --git a/sound/effects/combatroll.ogg b/sound/effects/combatroll.ogg new file mode 100644 index 0000000000000..744bcc6cf7ec8 Binary files /dev/null and b/sound/effects/combatroll.ogg differ diff --git a/sound/effects/heartbeat_low.ogg b/sound/effects/heartbeat_low.ogg new file mode 100644 index 0000000000000..ddac31ca51ea3 Binary files /dev/null and b/sound/effects/heartbeat_low.ogg differ diff --git a/sound/effects/orbital_bombardment.ogg b/sound/effects/orbital_bombardment.ogg new file mode 100644 index 0000000000000..d58e6c318fc0e Binary files /dev/null and b/sound/effects/orbital_bombardment.ogg differ diff --git a/sound/machines/sensors/alarm1.ogg b/sound/machines/sensors/alarm1.ogg new file mode 100644 index 0000000000000..15e6cf5bb8fc1 Binary files /dev/null and b/sound/machines/sensors/alarm1.ogg differ diff --git a/sound/machines/sensors/contact_identified.ogg b/sound/machines/sensors/contact_identified.ogg new file mode 100644 index 0000000000000..531411c39ed14 Binary files /dev/null and b/sound/machines/sensors/contact_identified.ogg differ diff --git a/sound/machines/sensors/contact_lost.ogg b/sound/machines/sensors/contact_lost.ogg new file mode 100644 index 0000000000000..4a1b7753658a4 Binary files /dev/null and b/sound/machines/sensors/contact_lost.ogg differ diff --git a/sound/machines/sensors/contact_regained.ogg b/sound/machines/sensors/contact_regained.ogg new file mode 100644 index 0000000000000..9e35c81b31267 Binary files /dev/null and b/sound/machines/sensors/contact_regained.ogg differ diff --git a/sound/machines/sensors/contactgeneric.ogg b/sound/machines/sensors/contactgeneric.ogg new file mode 100644 index 0000000000000..ef4e04b982574 Binary files /dev/null and b/sound/machines/sensors/contactgeneric.ogg differ diff --git a/sound/machines/sensors/dradis.ogg b/sound/machines/sensors/dradis.ogg new file mode 100644 index 0000000000000..aa53495a6fd6d Binary files /dev/null and b/sound/machines/sensors/dradis.ogg differ diff --git a/sound/machines/sensors/newcontact.ogg b/sound/machines/sensors/newcontact.ogg new file mode 100644 index 0000000000000..dd52c2d34dc91 Binary files /dev/null and b/sound/machines/sensors/newcontact.ogg differ diff --git a/sound/machines/sensors/target_lock.ogg b/sound/machines/sensors/target_lock.ogg new file mode 100644 index 0000000000000..c074147743a4c Binary files /dev/null and b/sound/machines/sensors/target_lock.ogg differ diff --git a/sound/simple_mob/abominable_infestation/alien_04.ogg b/sound/simple_mob/abominable_infestation/alien_04.ogg new file mode 100644 index 0000000000000..f012ad7797933 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/alien_04.ogg differ diff --git a/sound/simple_mob/abominable_infestation/alien_05.ogg b/sound/simple_mob/abominable_infestation/alien_05.ogg new file mode 100644 index 0000000000000..a762400655260 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/alien_05.ogg differ diff --git a/sound/simple_mob/abominable_infestation/alien_06.ogg b/sound/simple_mob/abominable_infestation/alien_06.ogg new file mode 100644 index 0000000000000..512512bbf68fe Binary files /dev/null and b/sound/simple_mob/abominable_infestation/alien_06.ogg differ diff --git a/sound/simple_mob/abominable_infestation/alien_09.ogg b/sound/simple_mob/abominable_infestation/alien_09.ogg new file mode 100644 index 0000000000000..765df11a70605 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/alien_09.ogg differ diff --git a/sound/simple_mob/abominable_infestation/assembler/ambient_1.ogg b/sound/simple_mob/abominable_infestation/assembler/ambient_1.ogg new file mode 100644 index 0000000000000..25745c672af7c Binary files /dev/null and b/sound/simple_mob/abominable_infestation/assembler/ambient_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/broodling/ambient_1.ogg b/sound/simple_mob/abominable_infestation/broodling/ambient_1.ogg new file mode 100644 index 0000000000000..e3662c4f30754 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/broodling/ambient_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/broodling/ambient_2.ogg b/sound/simple_mob/abominable_infestation/broodling/ambient_2.ogg new file mode 100644 index 0000000000000..cfa36cc33c24c Binary files /dev/null and b/sound/simple_mob/abominable_infestation/broodling/ambient_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/broodling/death.ogg b/sound/simple_mob/abominable_infestation/broodling/death.ogg new file mode 100644 index 0000000000000..d0184ffc1477f Binary files /dev/null and b/sound/simple_mob/abominable_infestation/broodling/death.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_03.ogg b/sound/simple_mob/abominable_infestation/bug_03.ogg new file mode 100644 index 0000000000000..1b09e54631743 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_03.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_05.ogg b/sound/simple_mob/abominable_infestation/bug_05.ogg new file mode 100644 index 0000000000000..7188ba4083c7a Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_05.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_06.ogg b/sound/simple_mob/abominable_infestation/bug_06.ogg new file mode 100644 index 0000000000000..a1fafd2cfc813 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_06.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_08.ogg b/sound/simple_mob/abominable_infestation/bug_08.ogg new file mode 100644 index 0000000000000..5520b4a4327e1 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_08.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_09.ogg b/sound/simple_mob/abominable_infestation/bug_09.ogg new file mode 100644 index 0000000000000..f4e2d95ad9cd4 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_09.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_10.ogg b/sound/simple_mob/abominable_infestation/bug_10.ogg new file mode 100644 index 0000000000000..2b08cf872636d Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_10.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_11.ogg b/sound/simple_mob/abominable_infestation/bug_11.ogg new file mode 100644 index 0000000000000..f46c6894dab41 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_11.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_12.ogg b/sound/simple_mob/abominable_infestation/bug_12.ogg new file mode 100644 index 0000000000000..be89ae926c0a9 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_12.ogg differ diff --git a/sound/simple_mob/abominable_infestation/bug_13.ogg b/sound/simple_mob/abominable_infestation/bug_13.ogg new file mode 100644 index 0000000000000..6a9f56dddb654 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/bug_13.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/aggro_1.ogg b/sound/simple_mob/abominable_infestation/eviscerator/aggro_1.ogg new file mode 100644 index 0000000000000..87a5c6535b5d7 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/aggro_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/aggro_2.ogg b/sound/simple_mob/abominable_infestation/eviscerator/aggro_2.ogg new file mode 100644 index 0000000000000..291895ef20a95 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/aggro_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/aggro_3.ogg b/sound/simple_mob/abominable_infestation/eviscerator/aggro_3.ogg new file mode 100644 index 0000000000000..3d1945dd02a70 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/aggro_3.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/attack.ogg b/sound/simple_mob/abominable_infestation/eviscerator/attack.ogg new file mode 100644 index 0000000000000..ceddc7b1e3874 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/attack.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/death_1.ogg b/sound/simple_mob/abominable_infestation/eviscerator/death_1.ogg new file mode 100644 index 0000000000000..4ca0361af09f3 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/death_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/death_2.ogg b/sound/simple_mob/abominable_infestation/eviscerator/death_2.ogg new file mode 100644 index 0000000000000..6e213a349e950 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/death_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/eviscerator/step.ogg b/sound/simple_mob/abominable_infestation/eviscerator/step.ogg new file mode 100644 index 0000000000000..6190eb6d108dd Binary files /dev/null and b/sound/simple_mob/abominable_infestation/eviscerator/step.ogg differ diff --git a/sound/simple_mob/abominable_infestation/floatfly/death.ogg b/sound/simple_mob/abominable_infestation/floatfly/death.ogg new file mode 100644 index 0000000000000..2e981a561fd8f Binary files /dev/null and b/sound/simple_mob/abominable_infestation/floatfly/death.ogg differ diff --git a/sound/simple_mob/abominable_infestation/floatfly/fly.ogg b/sound/simple_mob/abominable_infestation/floatfly/fly.ogg new file mode 100644 index 0000000000000..733b8190e33a1 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/floatfly/fly.ogg differ diff --git a/sound/simple_mob/abominable_infestation/heart_death.ogg b/sound/simple_mob/abominable_infestation/heart_death.ogg new file mode 100644 index 0000000000000..24e2a3b4743f1 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/heart_death.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/ambient_1.ogg b/sound/simple_mob/abominable_infestation/larva/ambient_1.ogg new file mode 100644 index 0000000000000..3e7a4dbb8ed11 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/ambient_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/ambient_2.ogg b/sound/simple_mob/abominable_infestation/larva/ambient_2.ogg new file mode 100644 index 0000000000000..41d87e12de265 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/ambient_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/ambient_3.ogg b/sound/simple_mob/abominable_infestation/larva/ambient_3.ogg new file mode 100644 index 0000000000000..299fc01fc49ac Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/ambient_3.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/death_1.ogg b/sound/simple_mob/abominable_infestation/larva/death_1.ogg new file mode 100644 index 0000000000000..738fd8f7c9ef2 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/death_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/death_2.ogg b/sound/simple_mob/abominable_infestation/larva/death_2.ogg new file mode 100644 index 0000000000000..afa9580d3eea6 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/death_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/implant.ogg b/sound/simple_mob/abominable_infestation/larva/implant.ogg new file mode 100644 index 0000000000000..0c7ba431bfd0e Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/implant.ogg differ diff --git a/sound/simple_mob/abominable_infestation/larva/spawn.ogg b/sound/simple_mob/abominable_infestation/larva/spawn.ogg new file mode 100644 index 0000000000000..820e500b33ad6 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/larva/spawn.ogg differ diff --git a/sound/simple_mob/abominable_infestation/rhino/death.ogg b/sound/simple_mob/abominable_infestation/rhino/death.ogg new file mode 100644 index 0000000000000..3222a77c21e3c Binary files /dev/null and b/sound/simple_mob/abominable_infestation/rhino/death.ogg differ diff --git a/sound/simple_mob/abominable_infestation/rhino/roar.ogg b/sound/simple_mob/abominable_infestation/rhino/roar.ogg new file mode 100644 index 0000000000000..39845a8867b08 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/rhino/roar.ogg differ diff --git a/sound/simple_mob/abominable_infestation/rhino/step.ogg b/sound/simple_mob/abominable_infestation/rhino/step.ogg new file mode 100644 index 0000000000000..dd68658f8aef9 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/rhino/step.ogg differ diff --git a/sound/simple_mob/abominable_infestation/rhino/step_angry.ogg b/sound/simple_mob/abominable_infestation/rhino/step_angry.ogg new file mode 100644 index 0000000000000..c5ff7040a26a0 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/rhino/step_angry.ogg differ diff --git a/sound/simple_mob/abominable_infestation/spitter/ambient_1.ogg b/sound/simple_mob/abominable_infestation/spitter/ambient_1.ogg new file mode 100644 index 0000000000000..41d87e12de265 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/spitter/ambient_1.ogg differ diff --git a/sound/simple_mob/abominable_infestation/spitter/ambient_2.ogg b/sound/simple_mob/abominable_infestation/spitter/ambient_2.ogg new file mode 100644 index 0000000000000..299fc01fc49ac Binary files /dev/null and b/sound/simple_mob/abominable_infestation/spitter/ambient_2.ogg differ diff --git a/sound/simple_mob/abominable_infestation/spitter/attack.ogg b/sound/simple_mob/abominable_infestation/spitter/attack.ogg new file mode 100644 index 0000000000000..3b6ff6f1b0bd1 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/spitter/attack.ogg differ diff --git a/sound/simple_mob/abominable_infestation/spitter/death.ogg b/sound/simple_mob/abominable_infestation/spitter/death.ogg new file mode 100644 index 0000000000000..8dcbeb4e55ce0 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/spitter/death.ogg differ diff --git a/sound/simple_mob/abominable_infestation/stand_down.ogg b/sound/simple_mob/abominable_infestation/stand_down.ogg new file mode 100644 index 0000000000000..63db2c0fb7eb0 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/stand_down.ogg differ diff --git a/sound/simple_mob/abominable_infestation/threat.ogg b/sound/simple_mob/abominable_infestation/threat.ogg new file mode 100644 index 0000000000000..79e720f3e1883 Binary files /dev/null and b/sound/simple_mob/abominable_infestation/threat.ogg differ diff --git a/sound/voice/abomination1.ogg b/sound/voice/abomination1.ogg new file mode 100644 index 0000000000000..f012ad7797933 Binary files /dev/null and b/sound/voice/abomination1.ogg differ diff --git a/sound/voice/abomination2.ogg b/sound/voice/abomination2.ogg new file mode 100644 index 0000000000000..a762400655260 Binary files /dev/null and b/sound/voice/abomination2.ogg differ diff --git a/sound/voice/abomination3.ogg b/sound/voice/abomination3.ogg new file mode 100644 index 0000000000000..512512bbf68fe Binary files /dev/null and b/sound/voice/abomination3.ogg differ diff --git a/sound/voice/abomination4.ogg b/sound/voice/abomination4.ogg new file mode 100644 index 0000000000000..765df11a70605 Binary files /dev/null and b/sound/voice/abomination4.ogg differ diff --git a/sound/voice/abomination5.ogg b/sound/voice/abomination5.ogg new file mode 100644 index 0000000000000..a1fafd2cfc813 Binary files /dev/null and b/sound/voice/abomination5.ogg differ diff --git a/sound/voice/abomination6.ogg b/sound/voice/abomination6.ogg new file mode 100644 index 0000000000000..5520b4a4327e1 Binary files /dev/null and b/sound/voice/abomination6.ogg differ diff --git a/sound/voice/abomination7.ogg b/sound/voice/abomination7.ogg new file mode 100644 index 0000000000000..7188ba4083c7a Binary files /dev/null and b/sound/voice/abomination7.ogg differ diff --git a/sound/voice/abomination8.ogg b/sound/voice/abomination8.ogg new file mode 100644 index 0000000000000..f4e2d95ad9cd4 Binary files /dev/null and b/sound/voice/abomination8.ogg differ diff --git a/sound/weapons/alien_bite1.ogg b/sound/weapons/alien_bite1.ogg new file mode 100644 index 0000000000000..3e90b90e3ba3b Binary files /dev/null and b/sound/weapons/alien_bite1.ogg differ diff --git a/sound/weapons/alien_bite2.ogg b/sound/weapons/alien_bite2.ogg new file mode 100644 index 0000000000000..0f0361a7c678a Binary files /dev/null and b/sound/weapons/alien_bite2.ogg differ diff --git a/sound/weapons/alien_claw_block.ogg b/sound/weapons/alien_claw_block.ogg new file mode 100644 index 0000000000000..a7c16ef022afb Binary files /dev/null and b/sound/weapons/alien_claw_block.ogg differ diff --git a/sound/weapons/alien_claw_flesh1.ogg b/sound/weapons/alien_claw_flesh1.ogg new file mode 100644 index 0000000000000..7853503c8f1c8 Binary files /dev/null and b/sound/weapons/alien_claw_flesh1.ogg differ diff --git a/sound/weapons/alien_claw_flesh2.ogg b/sound/weapons/alien_claw_flesh2.ogg new file mode 100644 index 0000000000000..2a1ba20dc471f Binary files /dev/null and b/sound/weapons/alien_claw_flesh2.ogg differ diff --git a/sound/weapons/alien_claw_flesh3.ogg b/sound/weapons/alien_claw_flesh3.ogg new file mode 100644 index 0000000000000..fe866ca7478bd Binary files /dev/null and b/sound/weapons/alien_claw_flesh3.ogg differ diff --git a/sound/weapons/alien_claw_metal1.ogg b/sound/weapons/alien_claw_metal1.ogg new file mode 100644 index 0000000000000..0316d97376d6e Binary files /dev/null and b/sound/weapons/alien_claw_metal1.ogg differ diff --git a/sound/weapons/alien_claw_metal2.ogg b/sound/weapons/alien_claw_metal2.ogg new file mode 100644 index 0000000000000..19d7027c65e91 Binary files /dev/null and b/sound/weapons/alien_claw_metal2.ogg differ diff --git a/sound/weapons/alien_claw_metal3.ogg b/sound/weapons/alien_claw_metal3.ogg new file mode 100644 index 0000000000000..47990ed0eb265 Binary files /dev/null and b/sound/weapons/alien_claw_metal3.ogg differ diff --git a/sound/weapons/alien_claw_swipe.ogg b/sound/weapons/alien_claw_swipe.ogg new file mode 100644 index 0000000000000..d37977f66e1a0 Binary files /dev/null and b/sound/weapons/alien_claw_swipe.ogg differ diff --git a/sound/weapons/alien_knockdown.ogg b/sound/weapons/alien_knockdown.ogg new file mode 100644 index 0000000000000..073f7e97f2a8e Binary files /dev/null and b/sound/weapons/alien_knockdown.ogg differ diff --git a/sound/weapons/alien_spit.ogg b/sound/weapons/alien_spit.ogg new file mode 100644 index 0000000000000..243d1aa1744c3 Binary files /dev/null and b/sound/weapons/alien_spit.ogg differ diff --git a/sound/weapons/alien_tail_attack.ogg b/sound/weapons/alien_tail_attack.ogg new file mode 100644 index 0000000000000..1c67cbb3f2e26 Binary files /dev/null and b/sound/weapons/alien_tail_attack.ogg differ