diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 74a6796bd2..41b5deb302 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -9,13 +9,12 @@ #define META_GAS_FUSION_POWER 7 //ATMOS //stuff you should probably leave well alone! -#define R_IDEAL_GAS_EQUATION 8.31446261815324 //kPa*L/(K*mol) -#define ONE_ATMOSPHERE 101.325 //kPa -#define TCMB 2.7 // -270.3degC -#define TCRYO 225 // -48.15degC -#define T0C 273.15 // 0degC -#define T20C 293.15 // 20degC -#define STEFANBOLTZMANN (5.670373*10e-8) // W/(m^2*K^4) +#define R_IDEAL_GAS_EQUATION 8.31 //kPa*L/(K*mol) +#define ONE_ATMOSPHERE 101.325 //kPa +#define TCMB 2.7 // -270.3degC +#define TCRYO 225 // -48.15degC +#define T0C 273.15 // 0degC +#define T20C 293.15 // 20degC #define MOLES_CELLSTANDARD (ONE_ATMOSPHERE*CELL_VOLUME/(T20C*R_IDEAL_GAS_EQUATION)) //moles in a 2.5 m^3 cell at 101.325 Pa and 20 degC #define M_CELL_WITH_RATIO (MOLES_CELLSTANDARD * 0.005) //compared against for superconductivity @@ -150,9 +149,9 @@ //OPEN TURF ATMOS #define OPENTURF_DEFAULT_ATMOS "o2=22;n2=82;TEMP=293.15" //the default air mix that open turfs spawn -#define TCOMMS_ATMOS "n2=100;TEMP=80" //-193,15°C telecommunications. also used for xenobiology slime killrooms +#define TCOMMS_ATMOS "n2=100;TEMP=80" //-193,15°C telecommunications. also used for xenobiology slime killrooms #define AIRLESS_ATMOS "TEMP=2.7" //space -#define FROZEN_ATMOS "o2=22;n2=82;TEMP=180" //-93.15°C snow and ice turfs +#define FROZEN_ATMOS "o2=22;n2=82;TEMP=180" //-93.15°C snow and ice turfs #define BURNMIX_ATMOS "o2=2500;plasma=5000;TEMP=370" //used in the holodeck burn test program //ATMOSPHERICS DEPARTMENT GAS TANK TURFS diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index 47b5c2f602..e29be375e6 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -109,8 +109,16 @@ #define MEDIHOUND_SLEEPER (1<<0) #define EATING_NOISES (1<<1) #define DIGESTION_NOISES (1<<2) +#define BREAST_ENLARGEMENT (1<<3) +#define PENIS_ENLARGEMENT (1<<4) +#define FORCED_FEM (1<<5) +#define FORCED_MASC (1<<6) +#define HYPNO (1<<7) +#define NEVER_HYPNO (1<<8) +#define NO_APHRO (1<<9) +#define NO_ASS_SLAP (1<<10) -#define TOGGLES_CITADEL (EATING_NOISES|DIGESTION_NOISES) +#define TOGGLES_CITADEL (EATING_NOISES|DIGESTION_NOISES|BREAST_ENLARGEMENT|PENIS_ENLARGEMENT) //component stuff #define COMSIG_COMBAT_TOGGLED "combatmode_toggled" //called by combat mode toggle on all equipped items. args: (mob/user, combatmode) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 29ff0a8fae..d89743d2d2 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -528,3 +528,5 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define FOURSPACES "    " #define CRYOMOBS 'icons/obj/cryo_mobs.dmi' + +#define CANT_REENTER_ROUND -1 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 439b3d1493..4f66482f51 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -134,6 +134,7 @@ #define TRAIT_NORUNNING "norunning" // You walk! #define TRAIT_NOMARROW "nomarrow" // You don't make blood, with chemicals or nanites. #define TRAIT_NOPULSE "nopulse" // Your heart doesn't beat. +#define TRAIT_EXEMPT_HEALTH_EVENTS "exempt-health-events" //non-mob traits @@ -160,17 +161,16 @@ #define TRAIT_TAGGER "tagger" #define TRAIT_PHOTOGRAPHER "photographer" #define TRAIT_MUSICIAN "musician" -#define TRAIT_CROCRIN_IMMUNE "crocin_immune" #define TRAIT_NYMPHO "nymphomania" #define TRAIT_MASO "masochism" #define TRAIT_EXHIBITIONIST "exhibitionist" #define TRAIT_HIGH_BLOOD "high_blood" -#define TRAIT_PHARMA "hepatic_pharmacokinesis" #define TRAIT_PARA "paraplegic" #define TRAIT_EMPATH "empath" #define TRAIT_FRIENDLY "friendly" -#define TRAIT_ASSBLASTUSA "assblastusa" #define TRAIT_CULT_EYES "cult_eyes" +#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item" +#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman. #define TRAIT_FREESPRINT "free_sprinting" @@ -193,6 +193,7 @@ #define ABSTRACT_ITEM_TRAIT "abstract-item" #define STATUS_EFFECT_TRAIT "status-effect" #define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention +#define GHOSTROLE_TRAIT "ghostrole" // unique trait sources, still defines #define STATUE_MUTE "statue" @@ -228,6 +229,7 @@ #define SLEEPING_CARP_TRAIT "sleeping_carp" #define RISING_BASS_TRAIT "rising_bass" #define ABDUCTOR_ANTAGONIST "abductor-antagonist" +#define NUKEOP_ANTAGONIST "nukeop-antagonist" #define MADE_UNCLONEABLE "made-uncloneable" #define NUKEOP_TRAIT "nuke-op" #define DEATHSQUAD_TRAIT "deathsquad" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index d76fc7731a..222002f512 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -447,7 +447,7 @@ var/list/candidates = list() for(var/mob/dead/observer/G in GLOB.player_list) - if(G.reenter_round_timeout < world.realtime) + if(G.can_reenter_round(TRUE)) candidates += G return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index ce8b2e4cf0..cdf0f604b8 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -20,8 +20,6 @@ else return "000" -#define UNDIE_COLORABLE(U) (U?.has_color) - /proc/random_underwear(gender) if(!GLOB.underwear_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/bottom, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 27f1a81fef..49825bcb50 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -19,6 +19,9 @@ var/list/mob_data = list() if(isnewplayer(m)) continue + if (m.client && m.client.prefs && m.client.prefs.auto_ooc) + if (!(m.client.prefs.chat_toggles & CHAT_OOC)) + m.client.prefs.chat_toggles ^= CHAT_OOC if(m.mind) if(m.stat != DEAD && !isbrain(m) && !iscameramob(m)) num_survivors++ diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 29f5331fd3..74c32dd52f 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -758,16 +758,6 @@ GLOBAL_LIST_INIT(can_embed_types, typecacheof(list( /obj/item/stack/rods, /obj/item/pipe))) -/proc/can_embed(obj/item/W) - if(W.get_sharpness()) - return 1 - if(is_pointed(W)) - return 1 - - if(is_type_in_typecache(W, GLOB.can_embed_types)) - return 1 - - /* Checks if that loc and dir has an item on the wall */ diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index c25ebf5b0a..914e45add5 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -115,5 +115,6 @@ GLOBAL_LIST_INIT(maintenance_loot, list( /obj/item/clothing/shoes/kindleKicks = 1, /obj/item/autosurgeon/penis = 1, /obj/item/autosurgeon/testicles = 1, + /obj/item/storage/box/marshmallow = 2, "" = 3 )) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f082f2ad16..e6b2a63673 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -98,17 +98,19 @@ take_damage(I.force, I.damtype, "melee", 1) /mob/living/attacked_by(obj/item/I, mob/living/user) + //CIT CHANGES START HERE - combatmode and resting checks + var/totitemdamage = I.force + if(iscarbon(user)) + var/mob/living/carbon/tempcarb = user + if(!tempcarb.combatmode) + totitemdamage *= 0.5 + if(user.resting) + totitemdamage *= 0.5 + //CIT CHANGES END HERE + if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + return FALSE send_item_attack_message(I, user) if(I.force) - //CIT CHANGES START HERE - combatmode and resting checks - var/totitemdamage = I.force - if(iscarbon(user)) - var/mob/living/carbon/tempcarb = user - if(!tempcarb.combatmode) - totitemdamage *= 0.5 - if(user.resting) - totitemdamage *= 0.5 - //CIT CHANGES END HERE apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW)) if(prob(33)) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 30e6883d24..6ee7d51091 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -91,6 +91,8 @@ var/obj/item/clothing/gloves/G = gloves if(istype(G) && G.Touch(A,0)) // for magic gloves return + if (istype(glasses) && glasses.ranged_attack(src,A,mouseparams)) + return for(var/datum/mutation/human/HM in dna.mutations) HM.on_ranged_attack(src, A, mouseparams) diff --git a/code/controllers/subsystem/processing/nanites.dm b/code/controllers/subsystem/processing/nanites.dm index 69bd5781de..5b53f9f884 100644 --- a/code/controllers/subsystem/processing/nanites.dm +++ b/code/controllers/subsystem/processing/nanites.dm @@ -6,6 +6,7 @@ PROCESSING_SUBSYSTEM_DEF(nanites) var/list/datum/nanite_cloud_backup/cloud_backups = list() var/list/mob/living/nanite_monitored_mobs = list() var/list/datum/nanite_program/relay/nanite_relays = list() + var/neural_network_count = 0 /datum/controller/subsystem/processing/nanites/proc/check_hardware(datum/nanite_cloud_backup/backup) if(QDELETED(backup.storage) || (backup.storage.stat & (NOPOWER|BROKEN))) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 890725fbb1..974d870a8a 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -401,6 +401,9 @@ SUBSYSTEM_DEF(ticker) qdel(player) living.notransform = TRUE if(living.client) + if (living.client.prefs && living.client.prefs.auto_ooc) + if (living.client.prefs.chat_toggles & CHAT_OOC) + living.client.prefs.chat_toggles ^= CHAT_OOC var/obj/screen/splash/S = new(living.client, TRUE) S.Fade(TRUE) livings += living diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index d5af47ea1f..06169f64bf 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -32,7 +32,7 @@ if(ishuman(M) && source.force && source.get_sharpness()) var/mob/living/carbon/human/H = M - if((H.health <= H.crit_threshold || (user.pulling == H && user.grab_state >= GRAB_NECK) || H.IsSleeping()) && user.zone_selected == BODY_ZONE_HEAD) // Only sleeping, neck grabbed, or crit, can be sliced. + if((H.health <= H.crit_threshold || (user.pulling == H && user.grab_state >= GRAB_NECK) || H.IsSleeping()) && user.zone_selected == BODY_ZONE_PRECISE_MOUTH) // Only sleeping, neck grabbed, or crit, can be sliced. if(H.has_status_effect(/datum/status_effect/neck_slice)) user.show_message("[H]'s neck has already been already cut, you can't make the bleeding any worse!", 1, \ "Their neck has already been already cut, you can't make the bleeding any worse!") @@ -120,4 +120,4 @@ if(eater.safety_mode || (eater.stat & (BROKEN|NOPOWER))) //I'm so sorry. return if(L.stat == DEAD && (L.butcher_results || L.guaranteed_butcher_results)) - Butcher(parent, L) \ No newline at end of file + Butcher(parent, L) diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm index 0ef13b514b..89c4deb2e9 100644 --- a/code/datums/components/nanites.dm +++ b/code/datums/components/nanites.dm @@ -11,8 +11,9 @@ var/list/datum/nanite_program/programs = list() var/max_programs = NANITE_PROGRAM_LIMIT - var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans, and does not display the program list on nanite scans - + var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans + var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners + /datum/component/nanites/Initialize(amount = 100, cloud = 0) if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup)) return COMPONENT_INCOMPATIBLE @@ -252,8 +253,8 @@ to_chat(user, "Cloud ID: [cloud_id ? cloud_id : "Disabled"]") to_chat(user, "================") to_chat(user, "Program List:") - if(stealth) - to_chat(user, "%#$ENCRYPTED&^@") + if(!diagnostics) + to_chat(user, "Diagnostics Disabled") else for(var/X in programs) var/datum/nanite_program/NP = X diff --git a/code/datums/martial/rising_bass.dm b/code/datums/martial/rising_bass.dm index a3b71c6784..9e00c90a92 100644 --- a/code/datums/martial/rising_bass.dm +++ b/code/datums/martial/rising_bass.dm @@ -150,7 +150,9 @@ if(!.) return ADD_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT) + ADD_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT) /datum/martial_art/the_rising_bass/on_remove(mob/living/carbon/human/H) . = ..() - REMOVE_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT) \ No newline at end of file + REMOVE_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT) + REMOVE_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT) \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index d1b4e51a7d..a0aa729a8d 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -135,6 +135,9 @@ if(L.client && L.client.prefs) L.canbearoused = L.client.prefs.arousable //Technically this should make taking over a character mean the body gain the new minds setting... L.update_arousal_hud() //Removes the old icon + if (L.client.prefs.auto_ooc) + if (L.client.prefs.chat_toggles & CHAT_OOC) + L.client.prefs.chat_toggles ^= CHAT_OOC SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character) diff --git a/code/datums/radiation_wave.dm b/code/datums/radiation_wave.dm index 5dce5791c2..4795f8da60 100644 --- a/code/datums/radiation_wave.dm +++ b/code/datums/radiation_wave.dm @@ -117,7 +117,7 @@ continue contam_atoms += thing var/did_contam = 0 - if(can_contam) + if(length(can_contam)) var/rad_strength = ((strength-RAD_MINIMUM_CONTAMINATION) * RAD_CONTAMINATION_STR_COEFFICIENT)/contam_atoms.len for(var/k in 1 to contam_atoms.len) var/atom/thing = contam_atoms[k] diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 5c4fc61827..5dab98d5ca 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -292,6 +292,7 @@ var/mob/living/carbon/C = owner for(var/X in C.bodyparts) var/obj/item/bodypart/BP = X + BP.max_damage *= 10 BP.brute_dam *= 10 BP.burn_dam *= 10 owner.toxloss *= 10 @@ -377,6 +378,7 @@ var/obj/item/bodypart/BP = X BP.brute_dam *= 0.1 BP.burn_dam *= 0.1 + BP.max_damage /= 10 owner.toxloss *= 0.1 owner.oxyloss *= 0.1 owner.cloneloss *= 0.1 diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm index eae2db6a5f..911929896b 100644 --- a/code/datums/traits/neutral.dm +++ b/code/datums/traits/neutral.dm @@ -88,15 +88,6 @@ if(quirk_holder) quirk_holder.remove_client_colour(/datum/client_colour/monochrome) -/datum/quirk/crocrin_immunity - name = "Crocin Immunity" - desc = "You're one of the few people in the galaxy who are genetically immune to Crocin and Hexacrocin products and their addictive properties! However, you can still get brain damage from Hexacrocin addiction." - mob_trait = TRAIT_CROCRIN_IMMUNE - value = 0 - gain_text = "You feel more prudish." - lose_text = "You don't feel as prudish as before." - medical_record_text = "Patient exhibits a special gene that makes them immune to aphrodisiacs." - /datum/quirk/libido name = "Nymphomania" desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual." @@ -130,23 +121,3 @@ mob_trait = TRAIT_EXHIBITIONIST gain_text = "You feel like exposing yourself to the world." lose_text = "Indecent exposure doesn't sound as charming to you anymore." - -/datum/quirk/pharmacokinesis //Prevents unwanted organ additions. - name = "Acute hepatic pharmacokinesis" - desc = "You've a rare genetic disorder that causes Incubus draft and Sucubus milk to be absorbed by your liver instead." - value = 0 - mob_trait = TRAIT_PHARMA - lose_text = "Your liver feels different." - medical_record_text = "Non-invasive tests report that the patient's metabolism is indeed incompatible with a certain \"stimulants\"." - var/active = FALSE - var/power = 0 - var/cachedmoveCalc = 1 - -/datum/quirk/assblastusa - name = "Buns of Steel" - desc = "You've never skipped ass day. With this trait, you are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand." - mob_trait = TRAIT_ASSBLASTUSA - value = 0 - medical_record_text = "Patient never skipped ass day." - gain_text = "Your ass rivals those of golems." - lose_text = "Your butt feels more squishy and slappable." \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 66570205e3..773c12b6af 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -166,7 +166,7 @@ return FALSE -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) if(does_attack_animation) user.changeNext_move(CLICK_CD_MELEE) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index ef20b6e35e..7c57de74a4 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -515,10 +515,10 @@ step(src, AM.dir) ..() -/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback) - return throw_at(target, range, speed, thrower, spin, diagonals_first, callback) +/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback, messy_throw = TRUE) + return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, messy_throw) -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback) //If this returns FALSE then callback will not be called. +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback, messy_throw = TRUE) //If this returns FALSE then callback will not be called. . = FALSE if (!target || speed <= 0) return diff --git a/code/game/gamemodes/clown_ops/clown_ops.dm b/code/game/gamemodes/clown_ops/clown_ops.dm index 49a336e16a..11898701fa 100644 --- a/code/game/gamemodes/clown_ops/clown_ops.dm +++ b/code/game/gamemodes/clown_ops/clown_ops.dm @@ -57,7 +57,6 @@ ..() if(visualsOnly) return - H.dna.add_mutation(CLOWNMUT) H.dna.add_mutation(SMILE) /datum/outfit/syndicate/clownop/leader diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index aee1cf4f69..6d19776d86 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -222,5 +222,21 @@ . += "\t[attached ? attached : "No one"] is attached." +/obj/machinery/iv_drip/telescopic + name = "telescopic IV drip" + desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down." + icon_state = "iv_drip" + +/obj/machinery/iv_drip/telescopic/update_icon() + ..() + icon_state = icon_state + "_tele" + +/obj/machinery/iv_drip/telescopic/AltClick(mob/user) + if (attached || beaker || !user.canUseTopic(src, BE_CLOSE)) + return ..() + new /obj/item/tele_iv(get_turf(src)) + qdel(src) + return TRUE + #undef IV_TAKING -#undef IV_INJECTING \ No newline at end of file +#undef IV_INJECTING diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index c5d198c05f..a410827347 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -578,21 +578,22 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) itempush = 0 //too light to push anything return A.hitby(src, 0, itempush) -/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) +/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, messy_throw = TRUE) thrownby = thrower - callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own + callback = CALLBACK(src, .proc/after_throw, callback, (spin && messy_throw)) //replace their callback with our own . = ..(target, range, speed, thrower, spin, diagonals_first, callback) -/obj/item/proc/after_throw(datum/callback/callback) +/obj/item/proc/after_throw(datum/callback/callback, messy_throw) if (callback) //call the original callback . = callback.Invoke() throw_speed = initial(throw_speed) //explosions change this. item_flags &= ~IN_INVENTORY - var/matrix/M = matrix(transform) - M.Turn(rand(-170, 170)) - transform = M - pixel_x = rand(-8, 8) - pixel_y = rand(-8, 8) + if(messy_throw) + var/matrix/M = matrix(transform) + M.Turn(rand(-170, 170)) + transform = M + pixel_x = rand(-8, 8) + pixel_y = rand(-8, 8) /obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage if(!newLoc) diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index cf8d38d9c9..884f5c4400 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -863,3 +863,205 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(reagents && reagents.total_volume) hand_reagents() + +/////////////// +/////BONGS///// +/////////////// + +/obj/item/bong + name = "bong" + desc = "A water bong used for smoking dried plants." + icon = 'icons/obj/bongs.dmi' + icon_state = null + item_state = null + w_class = WEIGHT_CLASS_NORMAL + light_color = "#FFCC66" + var/icon_off = "bong" + var/icon_on = "bong_lit" + var/chem_volume = 100 + var/last_used_time //for cooldown + var/firecharges = 0 //used for counting how many hits can be taken before the flame goes out + var/list/list_reagents = list() //For the base reagents bongs could get + + +/obj/item/bong/Initialize() + . = ..() + create_reagents(chem_volume, NO_REACT) // so it doesn't react until you light it + reagents.add_reagent_list(list_reagents) + icon_state = icon_off + +/obj/item/bong/attackby(obj/item/O, mob/user, params) + . = ..() + //If we're using a dried plant.. + if(istype(O,/obj/item/reagent_containers/food/snacks)) + var/obj/item/reagent_containers/food/snacks/DP = O + if (DP.dry) + //Nothing if our bong is full + if (reagents.holder_full()) + user.show_message("The bowl is full!", MSG_VISUAL) + return + + //Transfer reagents and remove the plant + user.show_message("You stuff the [DP] into the [src]'s bowl.", MSG_VISUAL) + DP.reagents.trans_to(src, 100) + qdel(DP) + return + else + user.show_message("[DP] must be dried first!", MSG_VISUAL) + return + + if (O.get_temperature() <= 500) + return + if (reagents && reagents.total_volume) //if there's stuff in the bong + var/lighting_text = O.ignition_effect(src, user) + if(lighting_text) + //Logic regarding igniting it on + if (firecharges == 0) + user.show_message("You light the [src] with the [O]!", MSG_VISUAL) + bongturnon() + else + user.show_message("You rekindle [src]'s flame with the [O]!", MSG_VISUAL) + + firecharges = 1 + return + else + user.show_message("There's nothing to light up in the bowl.", MSG_VISUAL) + return + +/obj/item/bong/CtrlShiftClick(mob/user) //empty reagents on alt click + ..() + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + + if (reagents && reagents.total_volume) + user.show_message("You empty the [src].", MSG_VISUAL) + reagents.clear_reagents() + if(firecharges) + firecharges = 0 + bongturnoff() + else + user.show_message("The [src] is already empty.", MSG_VISUAL) + +/obj/item/bong/AltClick(mob/user) + ..() + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + + if(firecharges) + firecharges = 0 + bongturnoff() + user.show_message("You quench the flame.", MSG_VISUAL) + return TRUE + +/obj/item/bong/examine(mob/user) + . = ..() + if(!reagents.total_volume) + . += "The bowl is empty." + else if (reagents.total_volume > 80) + . += "The bowl is filled to the brim." + else if (reagents.total_volume > 40) + . += "The bowl has plenty weed in it." + else + . += "The bowl has some weed in it." + + . += "Ctrl+Shift-click to empty." + . += "Alt-click to extinguish." + +/obj/item/bong/ignition_effect(atom/A, mob/user) + if(firecharges) + . = "[user] lights [A] off of the [src]." + else + . = "" + +/obj/item/bong/attack(mob/living/carbon/M, mob/living/carbon/user, obj/target) + //if it's lit up, some stuff in the bowl and the user is a target, and we're not on cooldown + + if (M != user) + return ..() + + if(user.is_mouth_covered(head_only = 1)) + to_chat(user, "Remove your headgear first.") + return ..() + + if(user.is_mouth_covered(mask_only = 1)) + to_chat(user, "Remove your mask first.") + return ..() + + if (!reagents.total_volume) + to_chat(user, "There's nothing in the bowl.") + return ..() + + if (!firecharges) + to_chat(user, "You have to light it up first.") + return ..() + + if (last_used_time + 30 >= world.time) + return ..() + var/hit_strength + var/noise + var/hittext = "" + //if the intent is help then you take a small hit, else a big one + if (user.a_intent == INTENT_HARM) + hit_strength = 2 + noise = 100 + hittext = "big hit" + else + hit_strength = 1 + noise = 70 + hittext = "hit" + //bubbling sound + playsound(user.loc,'sound/effects/bonghit.ogg', noise, 1) + + last_used_time = world.time + + //message + user.visible_message("[user] begins to take a [hittext] from the [src]!", \ + "You begin to take a [hittext] from [src].") + + //we take a hit here, after an uninterrupted delay + if(!do_after(user, 25, target = user)) + return + if (!(reagents && reagents.total_volume)) + return + + var/fraction = 12 * hit_strength + + var/datum/effect_system/smoke_spread/chem/smoke_machine/s = new + s.set_up(reagents, hit_strength, 18, user.loc) + s.start() + + reagents.reaction(user, INGEST, fraction) + if(!reagents.trans_to(user, fraction)) + reagents.remove_any(fraction) + + if (hit_strength == 2 && prob(15)) + user.emote("cough") + user.adjustOxyLoss(15) + + user.visible_message("[user] takes a [hittext] from the [src]!", \ + "You take a [hittext] from [src].") + + firecharges = firecharges - 1 + if (!firecharges) + bongturnoff() + if (!reagents.total_volume) + firecharges = 0 + bongturnoff() + + + +/obj/item/bong/proc/bongturnon() + icon_state = icon_on + set_light(3, 0.8) + +/obj/item/bong/proc/bongturnoff() + icon_state = icon_off + set_light(0, 0.0) + + + +/obj/item/bong/coconut + name = "coconut bong" + icon_off = "coconut_bong" + icon_on = "coconut_bong_lit" + desc = "A water bong used for smoking dried plants. This one's made out of a coconut and some bamboo." \ No newline at end of file diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm index 0c73d829ff..bf9c5daccd 100644 --- a/code/game/objects/items/devices/forcefieldprojector.dm +++ b/code/game/objects/items/devices/forcefieldprojector.dm @@ -2,7 +2,7 @@ name = "forcefield projector" desc = "An experimental device that can create several forcefields at a distance." icon = 'icons/obj/device.dmi' - icon_state = "signmaker_engi" + icon_state = "signmaker_forcefield" slot_flags = ITEM_SLOT_BELT w_class = WEIGHT_CLASS_SMALL item_flags = NOBLUDGEON diff --git a/code/game/objects/items/holosign_creator.dm b/code/game/objects/items/holosign_creator.dm index 78881d901e..eb58089293 100644 --- a/code/game/objects/items/holosign_creator.dm +++ b/code/game/objects/items/holosign_creator.dm @@ -79,7 +79,7 @@ /obj/item/holosign_creator/atmos name = "ATMOS holofan projector" desc = "A holographic projector that creates holographic barriers that prevent changes in atmosphere conditions." - icon_state = "signmaker_engi" + icon_state = "signmaker_atmos" holosign_type = /obj/structure/holosign/barrier/atmos creation_time = 0 max_signs = 3 diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 16262d61c3..4549dc73b6 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -374,7 +374,7 @@ It appears to have a wooden grip and a shaved down guard." icon_state = "cxsword_hilt_traitor" force_on = 30 - armour_penetration = 50 + armour_penetration = 35 embedding = list("embedded_pain_multiplier" = 10, "embed_chance" = 75, "embedded_fall_chance" = 0, "embedded_impact_pain_multiplier" = 10) block_chance = 50 hitsound_on = 'sound/weapons/blade1.ogg' diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index d8abff70a6..4242fb6c4b 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -216,10 +216,11 @@ return else if(last_hit < world.time) + if(target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + playsound(target, 'sound/weapons/genhit.ogg', 50, 1) + return if(ishuman(target)) var/mob/living/carbon/human/H = target - if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) - return if(check_martial_counter(H, user)) return playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index b35c07bcea..bf3043d4b5 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -11,11 +11,9 @@ var/charge_cost = 30 /obj/item/borg/stun/attack(mob/living/M, mob/living/user) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK)) - playsound(M, 'sound/weapons/genhit.ogg', 50, 1) - return FALSE + if(M.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK)) + playsound(M, 'sound/weapons/genhit.ogg', 50, 1) + return FALSE if(iscyborg(user)) var/mob/living/silicon/robot/R = user if(!R.cell.use(charge_cost)) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 3a4579d3f8..6c250029a6 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -61,6 +61,7 @@ /obj/item/radio, /obj/item/clothing/gloves, /obj/item/holosign_creator, + /obj/item/forcefield_projector, /obj/item/assembly/signaler )) STR.can_hold = can_hold @@ -458,8 +459,7 @@ /obj/item/extinguisher/mini, /obj/item/radio, /obj/item/clothing/gloves, - /obj/item/holosign_creator/atmos, - /obj/item/holosign_creator/engineering, + /obj/item/holosign_creator, /obj/item/forcefield_projector, /obj/item/assembly/signaler, /obj/item/lightreplacer, @@ -571,6 +571,7 @@ /obj/item/reagent_containers/spray, /obj/item/soap, /obj/item/holosign_creator, + /obj/item/forcefield_projector, /obj/item/key/janitor, /obj/item/clothing/gloves, /obj/item/melee/flyswatter, @@ -582,7 +583,7 @@ /obj/item/storage/belt/bandolier name = "bandolier" - desc = "A bandolier for holding shotgun ammunition." + desc = "A bandolier for holding ammunition." icon_state = "bandolier" item_state = "bandolier" @@ -592,7 +593,7 @@ STR.max_items = 18 STR.display_numerical_stacking = TRUE STR.can_hold = typecacheof(list( - /obj/item/ammo_casing/shotgun + /obj/item/ammo_casing )) /obj/item/storage/belt/bandolier/durathread diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index e214db3c71..4b2b3e2203 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -1259,3 +1259,12 @@ var/obj/item/stack/sheet/cardboard/I = new(user.drop_location()) qdel(src) user.put_in_hands(I) + +/obj/item/storage/box/marshmallow + name = "box of marshmallows" + desc = "A box of marshmallows." + illustration = "marshmallow" + +/obj/item/storage/box/marshmallow/PopulateContents() + for (var/i in 1 to 5) + new /obj/item/reagent_containers/food/snacks/marshmallow(src) \ No newline at end of file diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 484b9862ff..b5c8f6b631 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -168,11 +168,9 @@ /obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that - playsound(L, 'sound/weapons/genhit.ogg', 50, 1) - return FALSE + if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that + playsound(L, 'sound/weapons/genhit.ogg', 50, 1) + return FALSE var/stunpwr = stunforce var/obj/item/stock_parts/cell/our_cell = get_cell() if(!our_cell) diff --git a/code/game/objects/items/telescopic_iv.dm b/code/game/objects/items/telescopic_iv.dm new file mode 100644 index 0000000000..9b0b714ea5 --- /dev/null +++ b/code/game/objects/items/telescopic_iv.dm @@ -0,0 +1,17 @@ +/obj/item/tele_iv + name = "telescopic IV drip" + desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down." + icon = 'icons/obj/iv_drip.dmi' + icon_state = "tele_iv" + +/obj/item/tele_iv/attack_self(mob/user) + deploy_iv(user, user.loc) + +/obj/item/tele_iv/afterattack(atom/target, mob/user, proximity) + . = ..() + if(proximity && isopenturf(target) && user.CanReach(target)) + deploy_iv(user, target) + +/obj/item/tele_iv/proc/deploy_iv(mob/user, atom/location) + new /obj/machinery/iv_drip/telescopic(location) + qdel(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index f5dfa690db..14ac2f037e 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -80,13 +80,12 @@ SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue) anchored = anchorvalue -/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) - ..() +/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, messy_throw) + . = ..() if(obj_flags & FROZEN) visible_message("[src] shatters into a million pieces!") qdel(src) - /obj/assume_air(datum/gas_mixture/giver) if(loc) return loc.assume_air(giver) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 8da50d488f..579b1444a5 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -12,18 +12,12 @@ new /obj/item/clothing/head/hardhat/white(src) new /obj/item/clothing/head/hardhat/weldhat/white(src) new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/clothing/shoes/sneakers/brown(src) new /obj/item/tank/jetpack/suit(src) new /obj/item/cartridge/ce(src) new /obj/item/radio/headset/heads/ce(src) - new /obj/item/storage/toolbox/mechanical(src) - new /obj/item/clothing/suit/hazardvest(src) new /obj/item/megaphone/command(src) new /obj/item/areaeditor/blueprints(src) - new /obj/item/airlock_painter(src) new /obj/item/holosign_creator/engineering(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/multitool(src) new /obj/item/assembly/flash/handheld(src) new /obj/item/clothing/glasses/meson/engine(src) new /obj/item/door_remote/chief_engineer(src) @@ -67,7 +61,7 @@ for(var/i in 1 to 3) new /obj/item/clothing/head/welding(src) for(var/i in 1 to 3) - new /obj/item/weldingtool(src) + new /obj/item/weldingtool/largetank(src) /obj/structure/closet/secure_closet/engineering_personal name = "engineer's locker" @@ -84,7 +78,6 @@ new /obj/item/clothing/glasses/meson/engine(src) new /obj/item/storage/box/emptysandbags(src) - /obj/structure/closet/secure_closet/atmospherics name = "\proper atmospheric technician's locker" req_access = list(ACCESS_ATMOSPHERICS) @@ -103,3 +96,33 @@ new /obj/item/clothing/head/hardhat/atmos(src) new /obj/item/clothing/glasses/meson/engine/tray(src) new /obj/item/extinguisher/advanced(src) + +/* + * Empty lockers + * Some of the lockers are filled with junk, and sometimes its nice to just fill it with your own set-up for your own map gimmicks. + */ + +/obj/structure/closet/secure_closet/engineering_chief/empty + +/obj/structure/closet/secure_closet/engineering_chief/empty/PopulateContents() + return + +/obj/structure/closet/secure_closet/engineering_electrical/empty + +/obj/structure/closet/secure_closet/engineering_electrical/empty/PopulateContents() + return + +/obj/structure/closet/secure_closet/engineering_welding/empty + +/obj/structure/closet/secure_closet/engineering_welding/empty/PopulateContents() + return + +/obj/structure/closet/secure_closet/engineering_personal/empty + +/obj/structure/closet/secure_closet/engineering_personal/empty/PopulateContents() + return + +/obj/structure/closet/secure_closet/atmospherics/empty + +/obj/structure/closet/secure_closet/atmospherics/empty/PopulateContents() + return diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index 05e62c196f..7f0c3c6eb8 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -30,11 +30,11 @@ return var/list/undergarment_choices = list("Underwear", "Underwear Color", "Undershirt", "Undershirt Color", "Socks", "Socks Color") - if(!UNDIE_COLORABLE(GLOB.underwear_list[H.underwear])) + if(!(GLOB.underwear_list[H.underwear]?.has_color)) undergarment_choices -= "Underwear Color" - if(!UNDIE_COLORABLE(GLOB.undershirt_list[H.undershirt])) + if(!(GLOB.undershirt_list[H.undershirt]?.has_color)) undergarment_choices -= "Undershirt Color" - if(!UNDIE_COLORABLE(GLOB.socks_list[H.socks])) + if(!(GLOB.socks_list[H.socks]?.has_color)) undergarment_choices -= "Socks Color" var/choice = input(H, "Underwear, Undershirt, or Socks?", "Changing") as null|anything in undergarment_choices diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index fdc85475fb..fd6030453c 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -30,6 +30,8 @@ new/obj/structure/fluff/empty_terrarium(get_turf(src)) return ..() +/obj/effect/mob_spawn/human/seed_vault/special(mob/living/carbon/human/new_spawn) + ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) //Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers. /obj/effect/mob_spawn/human/ash_walker @@ -251,6 +253,9 @@ new/obj/structure/fluff/empty_cryostasis_sleeper(get_turf(src)) return ..() +/obj/effect/mob_spawn/human/hermit/special(mob/living/carbon/human/new_spawn) + ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) + //Broken rejuvenation pod: Spawns in animal hospitals in lavaland. Ghosts become disoriented interns and are advised to search for help. /obj/effect/mob_spawn/human/doctor/alive/lavaland name = "broken rejuvenation pod" @@ -353,6 +358,9 @@ new/obj/structure/fluff/empty_sleeper/syndicate(get_turf(src)) ..() +/obj/effect/mob_spawn/human/hotel_staff/special(mob/living/carbon/human/new_spawn) + ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) + /obj/effect/mob_spawn/human/demonic_friend name = "Essence of friendship" desc = "Oh boy! Oh boy! A friend!" @@ -597,7 +605,7 @@ rank = "Gunner" /obj/effect/mob_spawn/human/ghostcafe - name = "ghost cafe sleeper" + name = "Ghost Cafe Sleeper" uses = -1 icon = 'icons/obj/machines/sleeper.dmi' icon_state = "sleeper" @@ -618,12 +626,14 @@ O.equip(new_spawn, FALSE, new_spawn.client) SSjob.equip_loadout(null, new_spawn, FALSE) SSquirks.AssignQuirks(new_spawn, new_spawn.client, TRUE, TRUE, null, FALSE, new_spawn) + ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT) /datum/outfit/ghostcafe name = "ID, jumpsuit and shoes" uniform = /obj/item/clothing/under/color/random shoes = /obj/item/clothing/shoes/sneakers/black id = /obj/item/card/id + r_hand = /obj/item/storage/box/syndie_kit/chameleon/ghostcafe /datum/outfit/ghostcafe/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source) @@ -639,4 +649,18 @@ uniform = /obj/item/clothing/under/color/random else uniform = /obj/item/clothing/under/skirt/color/random - + +/obj/item/storage/box/syndie_kit/chameleon/ghostcafe + name = "ghost cafe costuming kit" + desc = "Look just the way you did in life - or better!" + +/obj/item/storage/box/syndie_kit/chameleon/ghostcafe/PopulateContents() // Doesn't contain a PDA, for isolation reasons. + new /obj/item/clothing/under/chameleon(src) + new /obj/item/clothing/suit/chameleon(src) + new /obj/item/clothing/gloves/chameleon(src) + new /obj/item/clothing/shoes/chameleon(src) + new /obj/item/clothing/glasses/chameleon(src) + new /obj/item/clothing/head/chameleon(src) + new /obj/item/clothing/mask/chameleon(src) + new /obj/item/storage/backpack/chameleon(src) + new /obj/item/clothing/neck/cloak/chameleon(src) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 53fdb315b5..f08112939a 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -492,7 +492,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) message_admins("[key_name_admin(usr)] assumed direct control of [M].") log_admin("[key_name(usr)] assumed direct control of [M].") var/mob/adminmob = src.mob - M.ckey = src.ckey + adminmob.transfer_ckey(M, send_signal = FALSE) if( isobserver(adminmob) ) qdel(adminmob) SSblackbox.record_feedback("tally", "admin_verb", 1, "Assume Direct Control") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index 433f52306b..fce45810d1 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -484,11 +484,9 @@ user.do_attack_animation(L) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) - playsound(H, 'sound/weapons/genhit.ogg', 50, TRUE) - return FALSE + if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE) + return FALSE switch (mode) if(BATON_STUN) diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index 33e7e4392f..da165adef2 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -3,6 +3,7 @@ icon = 'icons/mob/blob.dmi' icon_state = "blob_shield" desc = "A solid wall of slightly twitching tendrils." + var/damaged_desc = "A wall of twitching tendrils." max_integrity = 150 brute_resist = 0.25 explosion_block = 3 @@ -21,10 +22,10 @@ /obj/structure/blob/shield/update_icon() ..() - if(obj_integrity <= 70) - icon_state = "blob_shield_damaged" - name = "weakened strong blob" - desc = "A wall of twitching tendrils." + if(obj_integrity < max_integrity * 0.5) + icon_state = "[initial(icon_state)]_damaged" + name = "weakened [initial(name)]" + desc = "[damaged_desc]" atmosblock = FALSE if(!weakened) armor = armor.setRating("melee" = 15, "bullet" = 15, "laser" = 5, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) @@ -37,4 +38,27 @@ if(weakened) armor = armor.setRating("melee" = 25, "bullet" = 25, "laser" = 15, "energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) weakened = FALSE - air_update_turf(1) \ No newline at end of file + air_update_turf(1) + +/obj/structure/blob/shield/reflective + name = "reflective blob" + desc = "A solid wall of slightly twitching tendrils with a reflective glow." + damaged_desc = "A wall of twitching tendrils with a reflective glow." + icon_state = "blob_glow" + flags_1 = CHECK_RICOCHET_1 + point_return = 8 + max_integrity = 50 + brute_resist = 1 + explosion_block = 2 + +/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + visible_message("[P] reflects off [src]!") + return TRUE \ No newline at end of file diff --git a/code/modules/antagonists/blob/blob/powers.dm b/code/modules/antagonists/blob/blob/powers.dm index e49d186362..73c0776ad7 100644 --- a/code/modules/antagonists/blob/blob/powers.dm +++ b/code/modules/antagonists/blob/blob/powers.dm @@ -113,12 +113,22 @@ /mob/camera/blob/verb/create_shield_power() set category = "Blob" - set name = "Create Shield Blob (15)" - set desc = "Create a shield blob, which will block fire and is hard to kill." + set name = "Create/Upgrade Shield Blob (15)" + set desc = "Create a shield blob, which will block fire and is hard to kill. Using this on an existing shield blob turns it into a reflective blob, capable of reflecting most projectiles but making it much weaker than usual to brute attacks." create_shield() /mob/camera/blob/proc/create_shield(turf/T) - createSpecial(15, /obj/structure/blob/shield, 0, 0, T) + var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T + if(S) + if(!can_buy(15)) + return + if(S.obj_integrity < S.max_integrity * 0.5) + to_chat(src, "This shield blob is too damaged to be modified properly!") + return + to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced intregrity.") + S.change_to(/obj/structure/blob/shield/reflective, src) + else + createSpecial(15, /obj/structure/blob/shield, 0, 0, T) /mob/camera/blob/verb/create_resource() set category = "Blob" @@ -359,7 +369,7 @@ to_chat(src, "You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.") to_chat(src, "Normal Blobs will expand your reach and can be upgraded into special blobs that perform certain functions.") to_chat(src, "You can upgrade normal blobs into the following types of blob:") - to_chat(src, "Shield Blobs are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires.") + to_chat(src, "Shield Blobs are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires. Upgrading them again will result in a reflective blob, capable of reflecting most projectiles at the cost of the strong blob's extra health.") to_chat(src, "Resource Blobs are blobs which produce more resources for you, build as many of these as possible to consume the station. This type of blob must be placed near node blobs or your core to work.") to_chat(src, "Factory Blobs are blobs that spawn blob spores which will attack nearby enemies. This type of blob must be placed near node blobs or your core to work.") to_chat(src, "Blobbernauts can be produced from factories for a cost, and are hard to kill, powerful, and moderately smart. The factory used to create one will become fragile and briefly unable to produce spores.") diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm index febf53c324..97b4437298 100644 --- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm +++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm @@ -183,7 +183,7 @@ BuyPower(new /datum/action/bloodsucker/masquerade) BuyPower(new /datum/action/bloodsucker/veil) // Traits - for (var/T in defaultTraits) + for(var/T in defaultTraits) ADD_TRAIT(owner.current, T, "bloodsucker") if(HAS_TRAIT(owner.current, TRAIT_TOXINLOVER)) //No slime bonuses here, no thank you had_toxlover = TRUE @@ -200,10 +200,10 @@ var/mob/living/carbon/human/H = owner.current var/datum/species/S = H.dna.species // Make Changes - S.brutemod *= 0.5 // <-------------------- Start small, but burn mod increases based on rank! - S.coldmod = 0 - S.stunmod *= 0.25 - S.siemens_coeff *= 0.75 //base electrocution coefficient 1 + H.physiology.brute_mod *= 0.8 // <-------------------- Start small, but burn mod increases based on rank! + H.physiology.cold_mod = 0 + H.physiology.stun_mod *= 0.35 + H.physiology.siemens_coeff *= 0.75 //base electrocution coefficient 1 //S.heatmod += 0.5 // Heat shouldn't affect. Only Fire. //S.punchstunthreshold = 8 //damage at which punches from this race will stun 9 S.punchdamagelow += 1 //lowest possible punch damage 0 @@ -319,12 +319,10 @@ datum/antagonist/bloodsucker/proc/SpendRank() if(ishuman(owner.current)) var/mob/living/carbon/human/H = owner.current var/datum/species/S = H.dna.species - S.burnmod *= 0.025 // Slightly more burn damage - S.stunmod *= 0.95 // Slightly less stun time. S.punchdamagelow += 0.5 S.punchdamagehigh += 0.5 // NOTE: This affects the hitting power of Brawn. // More Health - owner.current.setMaxHealth(owner.current.maxHealth + 5) + owner.current.setMaxHealth(owner.current.maxHealth + 10) // Vamp Stats regenRate += 0.05 // Points of brute healed (starts at 0.3) feedAmount += 2 // Increase how quickly I munch down vics (15) diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm index f73912d0f5..3493622945 100644 --- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm +++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm @@ -275,7 +275,7 @@ // All done! if(convert_progress <= 0) // FAIL: Can't be Vassal - if(!SSticker.mode.can_make_vassal(target, user, display_warning=FALSE) && HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY + if(!SSticker.mode.can_make_vassal(target, user, display_warning=FALSE) || HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY to_chat(user, "[target] doesn't respond to your persuasion. It doesn't appear they can be converted to follow you, they either have a mindshield or their external loyalties are too difficult for you to break.\[ALT+click to release\]") convert_progress ++ // Pop it back up some. Avoids wasting Blood on a lost cause. // SUCCESS: All done! diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/nukeop/clownop.dm index 43e62c4638..58b492b578 100644 --- a/code/modules/antagonists/nukeop/clownop.dm +++ b/code/modules/antagonists/nukeop/clownop.dm @@ -5,6 +5,14 @@ antagpanel_category = "ClownOp" nukeop_outfit = /datum/outfit/syndicate/clownop +/datum/antagonist/nukeop/clownop/on_gain() + . = ..() + ADD_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST) + +/datum/antagonist/nukeop/clownop/on_removal() + REMOVE_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST) + return ..() + /datum/antagonist/nukeop/leader/clownop name = "Clown Operative Leader" roundend_category = "clown operatives" diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index 19f3f58708..38178a4339 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -154,11 +154,6 @@ var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) var/target_temperature var/target_heat_capacity - // first calculate heat from radiation. there's an implied "* 1 tick" here. - // 0.05 magic multiplicand is, first, 0.1 deciseconds; second, half of the radiation's going right back into the gas. - var/share_constant = STEFANBOLTZMANN*(share_volume**(2/3))*0.05 - // Minimizing temp to 4 billion is mostly to prevent -infinity temperatures. - var/heat = share_constant*(min(air.temperature,4000000000)**4) if(isopenturf(target)) @@ -170,8 +165,8 @@ if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) var/delta_temperature = air.temperature - target_temperature - heat -= share_constant*(min(target_temperature,4000000000)**4) - heat += thermal_conductivity*delta_temperature* \ + + var/heat = thermal_conductivity*delta_temperature* \ (partial_heat_capacity*target_heat_capacity/(partial_heat_capacity+target_heat_capacity)) air.temperature -= heat/total_heat_capacity @@ -188,8 +183,7 @@ var/sharer_temperature_delta = 0 if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - heat -= share_constant*(min(target_temperature,4000000000)**4) - heat += thermal_conductivity*delta_temperature* \ + var/heat = thermal_conductivity*delta_temperature* \ (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) self_temperature_delta = -heat/total_heat_capacity @@ -205,12 +199,10 @@ if((target.heat_capacity>0) && (partial_heat_capacity>0)) var/delta_temperature = air.temperature - target.temperature - heat -= share_constant*(min(target.temperature,4000000000)**4) - heat += thermal_conductivity*delta_temperature* \ + var/heat = thermal_conductivity*delta_temperature* \ (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) air.temperature -= heat/total_heat_capacity - air.temperature = CLAMP(air.temperature,TCMB,INFINITY) // i have no idea why TCMB needs to be the min but i ain't changing it update = TRUE /datum/pipeline/proc/return_air() diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm index 79aa957d97..b087859a47 100644 --- a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm @@ -26,7 +26,9 @@ var/turf/T = loc if(istype(T)) - if(T.blocks_air) + if(islava(T)) + environment_temperature = 5000 + else if(T.blocks_air) environment_temperature = T.temperature else var/turf/open/OT = T diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm index aa680e1b1e..29133b78d2 100644 --- a/code/modules/cargo/packs/misc.dm +++ b/code/modules/cargo/packs/misc.dm @@ -13,17 +13,16 @@ //////////////////// Paperwork and Writing Supplies ////////////////////////// ////////////////////////////////////////////////////////////////////////////// -/* I did it Kevin /datum/supply_pack/misc/abandonedcrate - name = "Abandoned Crate" - desc = "Someone keeps finding these locked crates out in the boonies. How about you take a crack at it, we've had our fill. WARNING: EXPLOSIVE" + name = "Loot Box" + desc = "Try your luck with these highly secure loot boxes! Solve the lock, win great prizes! WARNING: EXPLOSIVE FAILURE." contraband = TRUE - cost = 12800 + cost = 15000 contains = list(/obj/structure/closet/crate/secure/loot) crate_name = "abandoned crate" crate_type = /obj/structure/closet/crate/large dangerous = TRUE -*/ + /datum/supply_pack/misc/artsupply name = "Art Supplies" desc = "Make some happy little accidents with six canvasses, two easels, two boxes of crayons, and a rainbow crayon!" diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index a54584d6cc..ae9932e96f 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -79,4 +79,8 @@ var/client_keysend_amount = 0 var/next_keysend_reset = 0 var/next_keysend_trip_reset = 0 - var/keysend_tripped = FALSE \ No newline at end of file + var/keysend_tripped = FALSE + + // stops players from coming back through ghost/midround roles after suicide/cryo + // for a duration set by CONFIG_GET(number/suicide_reenter_round_timer) and CONFIG_GET(number/roundstart_suicide_time_limit) + var/reenter_round_timeout = 0 diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 00cc01105d..96a32d50e6 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -195,6 +195,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/auto_fit_viewport = TRUE var/uplink_spawn_loc = UPLINK_PDA + + var/sprint_spacebar = FALSE + var/sprint_toggle = FALSE var/list/exp = list() var/list/menuoptions @@ -245,6 +248,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Character Appearance" dat += "Loadout" dat += "Game Preferences" + dat += "Content Preferences" if(!path) dat += "
Please create an account to save your preferences
" @@ -721,13 +725,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" dat += "
" dat += "

Clothing & Equipment

" dat += "Underwear:[underwear]" - if(UNDIE_COLORABLE(GLOB.underwear_list[underwear])) + if(GLOB.underwear_list[underwear]?.has_color) dat += "Underwear Color:     Change
" dat += "Undershirt:[undershirt]" - if(UNDIE_COLORABLE(GLOB.undershirt_list[undershirt])) + if(GLOB.undershirt_list[undershirt]?.has_color) dat += "Undershirt Color:     Change
" dat += "Socks:[socks]" - if(UNDIE_COLORABLE(GLOB.socks_list[socks])) + if(GLOB.socks_list[socks]?.has_color) dat += "Socks Color:     Change
" dat += "Backpack:[backbag]" dat += "Jumpsuit:
[jumpsuit_style]
" @@ -834,13 +838,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat +="
" dat += "

Citadel Preferences

" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. - dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" - dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" - dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" - dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]
" - dat += "Lewdchem:[lewdchem == TRUE ? "Enabled" : "Disabled"]
" dat += "Widescreen: [widescreenpref ? "Enabled ([CONFIG_GET(string/default_view)])" : "Disabled (15x15)"]
" dat += "Auto stand: [autostand ? "Enabled" : "Disabled"]
" + dat += "Auto OOC: [auto_ooc ? "Enabled" : "Disabled"]
" dat += "Screen Shake: [(screenshake==100) ? "Full" : ((screenshake==0) ? "None" : "[screenshake]")]
" if (user && user.client && !user.client.prefs.screenshake==0) dat += "Damage Screen Shake: [(damagescreenshake==1) ? "On" : ((damagescreenshake==0) ? "Off" : "Only when down")]
" @@ -892,6 +892,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" dat += "Ambient Occlusion: [ambientocclusion ? "Enabled" : "Disabled"]
" dat += "Fit Viewport: [auto_fit_viewport ? "Auto" : "Manual"]
" + dat += "Sprint Key: [sprint_spacebar ? "Space" : "Shift"]
" + dat += "Toggle Sprint: [sprint_toggle ? "Enabled" : "Disabled"]
" if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation)) var/p_map = preferred_map @@ -991,6 +993,26 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" dat += "
[gear.description]
" + if(4) // Content preferences + dat += "" + dat +="
" + dat += "

Fetish content prefs

" + dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" + dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" + dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" + dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]
" + dat += "Forced Feminization: [(cit_toggles & FORCED_FEM) ? "Allowed" : "Disallowed"]
" + dat += "Forced Masculinization: [(cit_toggles & FORCED_MASC) ? "Allowed" : "Disallowed"]
" + dat += "Lewd Hypno: [(cit_toggles & HYPNO) ? "Allowed" : "Disallowed"]
" + dat += "
" + dat += "

Other content prefs

" + dat += "Breast Enlargement: [(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" + dat += "Penis Enlargement: [(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" + dat += "Hypno: [(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]
" + dat += "Aphrodisiacs: [(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]
" + dat += "Ass Slapping: [(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]
" + dat += "
" + dat += "
" @@ -2036,8 +2058,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["genitals_use_skintone"] = !features["genitals_use_skintone"] if("arousable") arousable = !arousable - if("lewdchem") - lewdchem = !lewdchem if("has_cock") features["has_cock"] = !features["has_cock"] if(features["has_cock"] == FALSE) @@ -2069,6 +2089,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) user.client.change_view(CONFIG_GET(string/default_view)) if("autostand") autostand = !autostand + if("auto_ooc") + auto_ooc = !auto_ooc if ("screenshake") var/desiredshake = input(user, "Set the amount of screenshake you want. \n(0 = disabled, 100 = full, 200 = maximum.)", "Character Preference", screenshake) as null|num if (!isnull(desiredshake)) @@ -2184,6 +2206,31 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("toggledigestionnoise") cit_toggles ^= DIGESTION_NOISES + + if("breast_enlargement") + cit_toggles ^= BREAST_ENLARGEMENT + + if("penis_enlargement") + cit_toggles ^= PENIS_ENLARGEMENT + + if("feminization") + cit_toggles ^= FORCED_FEM + + if("masculinization") + cit_toggles ^= FORCED_MASC + + if("hypno") + cit_toggles ^= HYPNO + + if("never_hypno") + cit_toggles ^= NEVER_HYPNO + + if("aphro") + cit_toggles ^= NO_APHRO + + if("ass_slap") + cit_toggles ^= NO_ASS_SLAP + //END CITADEL EDIT if("ambientocclusion") @@ -2197,6 +2244,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(auto_fit_viewport && parent) parent.fit_viewport() + if("sprint_key") + sprint_spacebar = !sprint_spacebar + + if("sprint_toggle") + sprint_toggle = !sprint_toggle + if("save") save_preferences() save_character() diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index c88d246b96..d78b9fe6fd 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -167,6 +167,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["parallax"] >> parallax S["ambientocclusion"] >> ambientocclusion S["auto_fit_viewport"] >> auto_fit_viewport + S["sprint_spacebar"] >> sprint_spacebar + S["sprint_toggle"] >> sprint_toggle S["menuoptions"] >> menuoptions S["enable_tips"] >> enable_tips S["tip_delay"] >> tip_delay @@ -181,8 +183,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["widescreenpref"] >> widescreenpref S["autostand"] >> autostand S["cit_toggles"] >> cit_toggles - S["lewdchem"] >> lewdchem S["preferred_chaos"] >> preferred_chaos + S["auto_ooc"] >> auto_ooc //try to fix any outdated data if necessary @@ -204,6 +206,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null) ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion)) auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport)) + sprint_spacebar = sanitize_integer(sprint_spacebar, 0, 1, initial(sprint_spacebar)) + sprint_toggle = sanitize_integer(sprint_toggle, 0, 1, initial(sprint_toggle)) ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form)) ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) @@ -219,7 +223,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref)) autostand = sanitize_integer(autostand, 0, 1, initial(autostand)) cit_toggles = sanitize_integer(cit_toggles, 0, 65535, initial(cit_toggles)) - + auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc)) return 1 @@ -264,6 +268,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["parallax"], parallax) WRITE_FILE(S["ambientocclusion"], ambientocclusion) WRITE_FILE(S["auto_fit_viewport"], auto_fit_viewport) + WRITE_FILE(S["sprint_spacebar"], sprint_spacebar) + WRITE_FILE(S["sprint_toggle"], sprint_toggle) WRITE_FILE(S["menuoptions"], menuoptions) WRITE_FILE(S["enable_tips"], enable_tips) WRITE_FILE(S["tip_delay"], tip_delay) @@ -278,8 +284,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["widescreenpref"], widescreenpref) WRITE_FILE(S["autostand"], autostand) WRITE_FILE(S["cit_toggles"], cit_toggles) - WRITE_FILE(S["lewdchem"], lewdchem) WRITE_FILE(S["preferred_chaos"], preferred_chaos) + WRITE_FILE(S["auto_ooc"], auto_ooc) return 1 @@ -517,6 +523,21 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car all_quirks = SANITIZE_LIST(all_quirks) + for(var/V in all_quirks) // quirk migration + switch(V) + if("Acute hepatic pharmacokinesis") + DISABLE_BITFIELD(cit_toggles, PENIS_ENLARGEMENT) + DISABLE_BITFIELD(cit_toggles, BREAST_ENLARGEMENT) + ENABLE_BITFIELD(cit_toggles,FORCED_FEM) + ENABLE_BITFIELD(cit_toggles,FORCED_MASC) + all_quirks -= V + if("Crocin Immunity") + ENABLE_BITFIELD(cit_toggles,NO_APHRO) + all_quirks -= V + if("Buns of Steel") + ENABLE_BITFIELD(cit_toggles,NO_ASS_SLAP) + all_quirks -= V + cit_character_pref_load(S) return 1 diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 60b8e1565e..d9e967e54d 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -298,6 +298,33 @@ BLIND // can't see anything user.regenerate_icons() return TRUE +/obj/item/clothing/neck/AltClick(mob/user) + . = ..() + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + // Polychrome stuff: + if(hasprimary | hassecondary | hastertiary) + var/choice = input(user,"polychromic thread options", "Clothing Recolor") as null|anything in list("[hasprimary ? "Primary Color" : ""]", "[hassecondary ? "Secondary Color" : ""]", "[hastertiary ? "Tertiary Color" : ""]") //generates a list depending on the enabled overlays + switch(choice) //Lets the list's options actually lead to something + if("Primary Color") + var/primary_color_input = input(usr,"","Choose Primary Color",primary_color) as color|null //color input menu, the "|null" adds a cancel button to it. + if(primary_color_input) //Checks if the color selected is NULL, rejects it if it is NULL. + primary_color = sanitize_hexcolor(primary_color_input, desired_format=6, include_crunch=1) //formats the selected color properly + update_icon() //updates the item icon + user.regenerate_icons() //updates the worn icon. Probably a bad idea, but it works. + if("Secondary Color") + var/secondary_color_input = input(usr,"","Choose Secondary Color",secondary_color) as color|null + if(secondary_color_input) + secondary_color = sanitize_hexcolor(secondary_color_input, desired_format=6, include_crunch=1) + update_icon() + user.regenerate_icons() + if("Tertiary Color") + var/tertiary_color_input = input(usr,"","Choose Tertiary Color",tertiary_color) as color|null + if(tertiary_color_input) + tertiary_color = sanitize_hexcolor(tertiary_color_input, desired_format=6, include_crunch=1) + update_icon() + user.regenerate_icons() + return TRUE /obj/item/clothing/under/verb/jumpsuit_adjust() set name = "Adjust Jumpsuit Style" diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 1a178158d8..a80cf6fba3 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -54,6 +54,9 @@ H.blur_eyes(5) eyes.applyOrganDamage(5) +/obj/item/clothing/glasses/proc/ranged_attack(mob/living/carbon/human/user,atom/A, params) + return FALSE + /obj/item/clothing/glasses/meson name = "optical meson scanner" desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting conditions." diff --git a/code/modules/clothing/glasses/disablerglasses.dm b/code/modules/clothing/glasses/disablerglasses.dm new file mode 100644 index 0000000000..a46e4c8339 --- /dev/null +++ b/code/modules/clothing/glasses/disablerglasses.dm @@ -0,0 +1,15 @@ +/obj/item/clothing/glasses/hud/security/sunglasses/disablers + name = "true stunglasses" + desc = "Made for only the best of shitsec. Wear 'em like you're gonna robust all of those fuckers." + var/beamtype = /obj/item/projectile/beam/disabler //change for adminbus + +/obj/item/clothing/glasses/hud/security/sunglasses/disablers/ranged_attack(mob/living/carbon/human/user,atom/A, params) + user.changeNext_move(CLICK_CD_RANGE) + var/obj/item/projectile/beam/disabler/LE = new beamtype( loc ) + playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) + LE.firer = src + LE.def_zone = user.get_organ_target() + LE.preparePixelProjectile(A, src, params) + LE.fire() + return TRUE + //shamelessly copied \ No newline at end of file diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index 47706a3e1e..0132fa8596 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -90,7 +90,7 @@ for(var/i in rad_places) var/turf/place = i - if(get_dist(user, place) >= range*2) //Rads are easier to see than wires under the floor + if(get_dist(user, place) >= range*8) //Rads are easier to see than wires under the floor continue var/strength = round(rad_places[i] / 1000, 0.1) var/image/pic = new(loc = place) @@ -139,7 +139,6 @@ item_state = "trayson-t-ray" desc = "Used by engineering staff to see underfloor objects such as cables and pipes." range = 2 - modes = list(MODE_NONE = MODE_TRAY, MODE_TRAY = MODE_NONE) /obj/item/clothing/glasses/meson/engine/tray/prescription @@ -152,7 +151,6 @@ icon_state = "trayson-shuttle" item_state = "trayson-shuttle" desc = "Used to see the boundaries of shuttle regions." - modes = list(MODE_NONE = MODE_SHUTTLE, MODE_SHUTTLE = MODE_NONE) #undef MODE_NONE diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 3d318db761..43c99fe7fd 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -89,12 +89,12 @@ /obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) . = ..() - if(user.mind && user.mind.assigned_role == "Clown") + if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)) SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "noshoes") /obj/item/clothing/shoes/clown_shoes/dropped(mob/user) . = ..() - if(user.mind && user.mind.assigned_role == "Clown") + if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)) SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "noshoes", /datum/mood_event/noshoes) /obj/item/clothing/shoes/clown_shoes/jester diff --git a/code/modules/clothing/shoes/taeclowndo.dm b/code/modules/clothing/shoes/taeclowndo.dm index f2bbdf0ceb..7c891dbde4 100644 --- a/code/modules/clothing/shoes/taeclowndo.dm +++ b/code/modules/clothing/shoes/taeclowndo.dm @@ -13,7 +13,7 @@ if(!ishuman(user)) return var/mob/living/carbon/human/H = user - if(!(HAS_TRAIT(H, TRAIT_CLUMSY)) && !(H.mind && H.mind.assigned_role == "Clown")) + if(!(HAS_TRAIT(H, TRAIT_CLUMSY)) && !(H.mind && HAS_TRAIT(H.mind, TRAIT_CLOWN_MENTALITY))) return if(slot == SLOT_SHOES) spells = new diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 9a5c919720..bf52b26ae9 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -590,8 +590,7 @@ /obj/item/clothing/suit/space/hardsuit/clown/mob_can_equip(mob/M, slot) if(!..() || !ishuman(M)) return FALSE - var/mob/living/carbon/human/H = M - if(H.mind.assigned_role == "Clown") + if(M.mind && HAS_TRAIT(M.mind, TRAIT_CLOWN_MENTALITY)) return TRUE else return FALSE diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 58df911412..177bce672f 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -467,6 +467,33 @@ fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE +/obj/item/clothing/under/sundresswhite + name = "white sundress" + desc = "Makes you want to frolic in a field of lillies." + icon_state = "sundress_white" + item_color = "sundress_white" + body_parts_covered = CHEST|GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/greendress + name = "green dress" + desc = "A tight green dress" + icon_state = "dress_green" + item_color = "dress_green" + body_parts_covered = CHEST|GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/pinkdress + name = "pink dress" + desc = "A tight pink dress" + icon_state = "dress_pink" + item_color = "dress_pink" + body_parts_covered = CHEST|GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + /obj/item/clothing/under/captainparade name = "captain's parade uniform" desc = "A captain's luxury-wear, for special occasions." @@ -509,6 +536,24 @@ fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE +/obj/item/clothing/under/westernbustle + name = "western bustle dress" + desc = "Filled with Western fire." + icon_state = "western_bustle" + item_state = "wcoat" + item_color = "western_bustle" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/flamenco + name = "flamenco dress" + desc = "Filled with Latin fire." + icon_state = "flamenco" + item_state = "wcoat" + item_color = "flamenco" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + /obj/item/clothing/under/stripeddress name = "striped dress" desc = "Fashion in space." @@ -529,6 +574,44 @@ fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE +/obj/item/clothing/under/flowerdress + name = "flower dress" + desc = "Lovely dress" + icon_state = "flower_dress" + item_state = "sailordress" + item_color = "flower_dress" + body_parts_covered = CHEST|GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/sweptskirt + name = "swept skirt" + desc = "Formal skirt" + icon_state = "skirt_swept" + item_color = "skirt_swept" + body_parts_covered = GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/corset + name = "black corset" + desc = "Nanotrasen is not resposible for any organ damage" + icon_state = "corset" + item_color = "corset" + body_parts_covered = CHEST|GROIN + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/croptop + name = "crop top" + desc = "We've saved money by giving you half a shirt!" + icon_state = "sailor_dress" + item_state = "sailordress" + item_color = "sailor_dress" + body_parts_covered = CHEST|GROIN|ARMS + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + /obj/item/clothing/under/redeveninggown name = "red evening gown" desc = "Fancy dress for space bar singers." diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index 05616337db..97bb4c48bc 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -75,3 +75,34 @@ desc = "A pair of woodland camouflage pants. Probably not the best choice for a space station." icon_state = "camopants" item_color = "camopants" + +/obj/item/clothing/under/pants/jeanripped + name = "ripped jeans" + desc = "If you're wearing this you're poor or a rebel" + icon_state = "jean_ripped" + item_color = "jean_ripped" + +/obj/item/clothing/under/pants/jeanshort + name = "jean shorts" + desc = "These are really just jeans cut in half" + icon_state = "jean_shorts" + item_color = "jean_shorts" + +/obj/item/clothing/under/pants/denimskirt + name = "denim skirt" + desc = "These are really just a jean leg hole cut from a pair" + icon_state = "denim_skirt" + item_color = "denim_skirt" + +/obj/item/clothing/under/pants/chaps + name = "black chaps" + body_parts_covered = LEGS + desc = "Yeehaw" + icon_state = "chaps" + item_color = "chaps" + +/obj/item/clothing/under/pants/yoga + name = "yoga pants" + desc = "Comfy!" + icon_state = "yoga_pants" + item_color = "yoga_pants" diff --git a/code/modules/crafting/recipes/recipes_misc.dm b/code/modules/crafting/recipes/recipes_misc.dm index 70dd4d1370..38ae3560e5 100644 --- a/code/modules/crafting/recipes/recipes_misc.dm +++ b/code/modules/crafting/recipes/recipes_misc.dm @@ -358,3 +358,11 @@ time = 100 category = CAT_MISC always_availible = FALSE // Disabled til learned + +/datum/crafting_recipe/coconut_bong + name = "Coconut Bong" + result = /obj/item/bong/coconut + reqs = list(/obj/item/stack/sheet/mineral/bamboo = 2, + /obj/item/reagent_containers/food/snacks/grown/coconut = 1) + time = 70 + category = CAT_MISC \ No newline at end of file diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 07a399a1b6..00e3698e1e 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -37,6 +37,8 @@ continue if(!H.client) continue + if(HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS)) + continue if(H.stat == DEAD) continue if(HAS_TRAIT(H, TRAIT_VIRUSIMMUNE)) //Don't pick someone who's virus immune, only for it to not do anything. diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm index 8db2d98bf0..a47a8b81b4 100644 --- a/code/modules/events/heart_attack.dm +++ b/code/modules/events/heart_attack.dm @@ -9,7 +9,7 @@ /datum/round_event/heart_attack/start() var/list/heart_attack_contestants = list() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || H.stat == DEAD || H.InCritical() || !H.can_heartattack() || H.has_status_effect(STATUS_EFFECT_EXERCISED) || (/datum/disease/heart_failure in H.diseases) || H.undergoing_cardiac_arrest()) + if(!H.client || H.stat == DEAD || H.InCritical() || !H.can_heartattack() || H.has_status_effect(STATUS_EFFECT_EXERCISED) || (/datum/disease/heart_failure in H.diseases) || H.undergoing_cardiac_arrest() || HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS)) continue if(H.satiety <= -60) //Multiple junk food items recently heart_attack_contestants[H] = 3 diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm index 2b0c16ebfc..0553f69b5b 100644 --- a/code/modules/events/mass_hallucination.dm +++ b/code/modules/events/mass_hallucination.dm @@ -35,4 +35,6 @@ /datum/hallucination/delusion, /datum/hallucination/oh_yeah) for(var/mob/living/carbon/C in GLOB.alive_mob_list) + if (HAS_TRAIT(C,TRAIT_EXEMPT_HEALTH_EVENTS)) + continue new picked_hallucination(C, TRUE) \ No newline at end of file diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index 901337cd52..8ee943beb9 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -15,6 +15,8 @@ continue if(H.stat == DEAD) continue + if (HAS_TRAIT(H,TRAIT_EXEMPT_HEALTH_EVENTS)) + continue if(!H.getorgan(/obj/item/organ/appendix)) //Don't give the disease to some who lacks it, only for it to be auto-cured continue if(!(MOB_ORGANIC in H.mob_biotypes)) //biotype sleeper bugs strike again, once again making appendicitis pick a target that can't take it diff --git a/code/modules/food_and_drinks/food/snacks_frozen.dm b/code/modules/food_and_drinks/food/snacks_frozen.dm index 4a4c5ddb88..fb7bcf33b4 100644 --- a/code/modules/food_and_drinks/food/snacks_frozen.dm +++ b/code/modules/food_and_drinks/food/snacks_frozen.dm @@ -219,7 +219,7 @@ name = "space cola snowcone" desc = "Space Cola drizzled over a snowball in a paper cup." icon_state = "soda_sc" - list_reagents = list("nutriment" = 1, "space_cola" = 5) + list_reagents = list("nutriment" = 1, "cola" = 5) tastes = list("ice" = 1, "water" = 1, "cola" = 5) /obj/item/reagent_containers/food/snacks/snowcones/spacemountainwind @@ -248,4 +248,4 @@ desc = "A very colorful snowball in a paper cup." icon_state = "rainbow_sc" list_reagents = list("nutriment" = 5, "laughter" = 25) - tastes = list("ice" = 1, "water" = 1, "sunlight" = 5, "light" = 5, "slime" = 5, "paint" = 3, "clouds" = 3) \ No newline at end of file + tastes = list("ice" = 1, "water" = 1, "sunlight" = 5, "light" = 5, "slime" = 5, "paint" = 3, "clouds" = 3) diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index 58a0ab4382..de22537063 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -609,4 +609,68 @@ filling_color = "#ECA735" tastes = list("fried corn" = 1) foodtype = JUNKFOOD | FRIED - dunkable = TRUE \ No newline at end of file + dunkable = TRUE + +/obj/item/reagent_containers/food/snacks/marshmallow + name = "marshmallow" + desc = "A marshmallow filled with fluffy marshmallow fluff." + icon_state = "marshmallow" + list_reagents = list("sugar" = 5, "nutriment" = 2) + filling_color = "#fafafa" + w_class = WEIGHT_CLASS_TINY + tastes = list("marshmallow" = 2) + foodtype = SUGAR | JUNKFOOD + var/burned = 0 + +/obj/item/reagent_containers/food/snacks/marshmallow/attackby(obj/item/I, mob/user) + switch (I.get_temperature()) + if (355 to 1500) + if (prob(30)) + burnmallow() + if (1500 to 2000) + if (prob(50)) + burnmallow() + else + burnmallow(TRUE) + if (2000 to 3000) + if (prob(10)) + burnmallow() + else + burnmallow(TRUE) + if (3000 to INFINITY) + burnmallow(TRUE) + return ..() + +/obj/item/reagent_containers/food/snacks/marshmallow/proc/burnmallow(reallyburned = FALSE) + if (reallyburned && burned == 1) + icon_state = "marshmallowrburned" + else if (burned == 0) + icon_state = "marshmallowburned" + +/obj/item/reagent_containers/food/snacks/marshmallow/examine(mob/user) + . = ..() + if (burned == 2) + . += "It looks very burned." + if (burned == 1) + . += "It looks just right for eating!" + +/obj/item/reagent_containers/food/snacks/marshmallow/fire_act(temp,volume) + switch (temp) + if (355 to 1500) + if (prob(30)) + burnmallow() + if (1500 to 2000) + if (prob(50)) + burnmallow() + else + burnmallow(TRUE) + if (2000 to 3000) + if (prob(10)) + burnmallow() + else + burnmallow(TRUE) + if (3000 to 7000) + burnmallow(TRUE) + if (7000 to INFINITY) + burn() + ..() diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index 3376bac1cc..77199b3496 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -240,7 +240,7 @@ dat += "
" else dat += "No trait-related genes detected in sample.
" - if(can_insert && istype(disk.gene, /datum/plant_gene/trait)) + if(can_insert && istype(disk.gene, /datum/plant_gene/trait) && !seed.is_gene_forbidden(disk.gene.type)) dat += "Insert: [disk.gene.get_name()]" dat += "" else diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index 8fc141e152..02b36d6a3f 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -27,7 +27,7 @@ bitesize_mod = 2 var/stacktype = /obj/item/stack/tile/grass var/tile_coefficient = 0.02 // 1/50 - wine_power = 15 + distill_reagent = /datum/reagent/consumable/ethanol/beer/light /obj/item/reagent_containers/food/snacks/grown/grass/attack_self(mob/user) to_chat(user, "You prepare the astroturf.") diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index b37e494233..3af284974e 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -232,7 +232,7 @@ /obj/item/reagent_containers/food/snacks/grown/cherry_bomb/proc/detonate() reagents.chem_temp = 1000 //Sets off the black powder reagents.handle_reactions() - + // Lavaland cactus /obj/item/seeds/lavaland/cactus @@ -244,3 +244,279 @@ product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' growthstages = 2 + + +// Coconut +/obj/item/seeds/coconut + name = "pack of coconut seeds" + desc = "They're seeds that grow into coconut palm trees." + icon_state = "seed-coconut" + species = "coconut" + plantname = "Coconut Palm Tree" + product = /obj/item/reagent_containers/food/snacks/grown/coconut + lifespan = 50 + endurance = 30 + potency = 35 + growing_icon = 'icons/obj/hydroponics/growing.dmi' + icon_dead = "coconut-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + forbiddengenes = list(/datum/plant_gene/trait/squash, /datum/plant_gene/trait/stinging) + reagents_add = list("coconutmilk" = 0.3) + +/obj/item/reagent_containers/food/snacks/grown/coconut + seed = /obj/item/seeds/coconut + name = "coconut" + desc = "Hard shell of a nut containing delicious milk inside. Perhaps try using something sharp?" + icon_state = "coconut" + item_state = "coconut" + possible_transfer_amounts = list(5, 10, 15, 20, 25, 30, 50) + spillable = FALSE + resistance_flags = ACID_PROOF + volume = 150 //so it won't cut reagents despite having the capacity for them + w_class = WEIGHT_CLASS_SMALL + force = 5 + throwforce = 5 + hitsound = 'sound/weapons/klonk.ogg' + attack_verb = list("klonked", "donked", "bonked") + var/opened = FALSE + var/carved = FALSE + var/chopped = FALSE + var/straw = FALSE + var/fused = FALSE + var/fusedactive = FALSE + var/defused = FALSE + +/obj/item/reagent_containers/food/snacks/grown/coconut/Initialize(mapload, obj/item/seeds/new_seed) + . = ..() + var/newvolume = 50 + round(seed.potency,10) + if (seed.get_gene(/datum/plant_gene/trait/maxchem)) + newvolume = newvolume + 50 + volume = newvolume + reagents.maximum_volume = newvolume + reagents.update_total() + + transform *= TRANSFORM_USING_VARIABLE(40, 100) + 0.5 //temporary fix for size? + +/obj/item/reagent_containers/food/snacks/grown/coconut/attack_self(mob/user) + if (!opened) + return + + if(!possible_transfer_amounts.len) + return + var/i=0 + for(var/A in possible_transfer_amounts) + i++ + if(A != amount_per_transfer_from_this) + continue + if(i[src]'s transfer amount is now [amount_per_transfer_from_this] units.") + return + +/obj/item/reagent_containers/food/snacks/grown/coconut/attackby(obj/item/W, mob/user, params) + //DEFUSING NADE LOGIC + if (W.tool_behaviour == TOOL_WIRECUTTER && fused) + user.show_message("You cut the fuse!", MSG_VISUAL) + playsound(user, W.hitsound, 50, 1, -1) + icon_state = "coconut_carved" + desc = "A coconut. This one's got a hole in it." + name = "coconut" + defused = TRUE + fused = FALSE + fusedactive = FALSE + if(!seed.get_gene(/datum/plant_gene/trait/glow)) + set_light(0, 0.0) + return + //IGNITING NADE LOGIC + if(!fusedactive && fused) + var/lighting_text = W.ignition_effect(src, user) + if(lighting_text) + user.visible_message("[user] ignites [src]'s fuse!", "You ignite the [src]'s fuse!") + fusedactive = TRUE + defused = FALSE + playsound(src, 'sound/effects/fuse.ogg', 100, 0) + message_admins("[ADMIN_LOOKUPFLW(user)] ignited a coconut bomb for detonation at [ADMIN_VERBOSEJMP(user)] [pretty_string_from_reagent_list(reagents.reagent_list)]") + log_game("[key_name(user)] primed a coconut grenade for detonation at [AREACOORD(user)].") + addtimer(CALLBACK(src, .proc/prime), 5 SECONDS) + icon_state = "coconut_grenade_active" + desc = "RUN!" + if(!seed.get_gene(/datum/plant_gene/trait/glow)) + light_color = "#FFCC66" //for the fuse + set_light(3, 0.8) + return + + //ADDING A FUSE, NADE LOGIC + if (istype(W,/obj/item/stack/sheet/cloth) || istype(W,/obj/item/stack/sheet/durathread)) + if (carved && !straw && !fused) + user.show_message("You add a fuse to the coconut!", 1) + W.use(1) + fused = TRUE + icon_state = "coconut_grenade" + desc = "A makeshift bomb made out of a coconut. You estimate the fuse is long enough for 5 seconds." + name = "coconut bomb" + return + //ADDING STRAW LOGIC + if (istype(W,/obj/item/stack/sheet/mineral/bamboo) && opened && !straw && fused) + user.show_message("You add a bamboo straw to the coconut!", 1) + straw = TRUE + W.use(1) + icon_state += "_straw" + desc = "You can already feel like you're on a tropical vacation." + return + //OPENING THE NUT LOGIC + if (!carved && !chopped) + var/screwdrivered = W.tool_behaviour == TOOL_SCREWDRIVER + if(screwdrivered || W.sharpness) + user.show_message("You [screwdrivered ? "make a hole in the coconut" : "slice the coconut open"]!", 1) + carved = TRUE + opened = TRUE + spillable = !screwdrivered + reagent_flags = OPENCONTAINER + ENABLE_BITFIELD(reagents.reagents_holder_flags, OPENCONTAINER) + icon_state = screwdrivered ? "coconut_carved" : "coconut_chopped" + desc = "A coconut. [screwdrivered ? "This one's got a hole in it" : "This one's sliced open, with all its delicious contents for your eyes to savour"]." + playsound(user, W.hitsound, 50, 1, -1) + return + return ..() + +/obj/item/reagent_containers/food/snacks/grown/coconut/attack(mob/living/M, mob/user, obj/target) + if(M && user.a_intent == INTENT_HARM && !spillable) + var/obj/item/bodypart/affecting = user.zone_selected //Find what the player is aiming at + if (affecting == BODY_ZONE_HEAD && prob(15)) + //smash the nut open + var/armor_block = min(90, M.run_armor_check(affecting, "melee", null, null,armour_penetration)) // For normal attack damage + M.apply_damage(force, BRUTE, affecting, armor_block) + + //Sound + playsound(user, hitsound, 100, 1, -1) + + //Attack logs + log_combat(user, M, "attacked", src) + + //Display an attack message. + if(M != user) + M.visible_message("[user] has cracked open a [name] on [M]'s head!", \ + "[user] has cracked open a [name] on [M]'s head!") + else + user.visible_message("[M] cracks open a [name] on their [M.p_them()] head!", \ + "[M] cracks open a [name] on [M.p_their()] head!") + + //The coconut breaks open so splash its reagents + spillable = TRUE + SplashReagents(M) + + //Lastly we remove the nut + qdel(src) + else + . = ..() + return + + if(fusedactive) + return + + if(!opened) + return + + if(!canconsume(M, user)) + return + + if(!reagents || !reagents.total_volume) + to_chat(user, "[src] is empty!") + return + + if(user.a_intent == INTENT_HARM && spillable) + var/R + M.visible_message("[user] splashes the contents of [src] onto [M]!", \ + "[user] splashes the contents of [src] onto [M]!") + if(reagents) + for(var/datum/reagent/A in reagents.reagent_list) + R += A.id + " (" + R += num2text(A.volume) + ")," + if(isturf(target) && reagents.reagent_list.len && thrownby) + log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]") + message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] at [ADMIN_VERBOSEJMP(target)].") + reagents.reaction(M, TOUCH) + log_combat(user, M, "splashed", R) + reagents.clear_reagents() + else + if(M != user) + M.visible_message("[user] attempts to feed something to [M].", \ + "[user] attempts to feed something to you.") + if(!do_mob(user, M)) + return + if(!reagents || !reagents.total_volume) + return // The drink might be empty after the delay, such as by spam-feeding + M.visible_message("[user] feeds something to [M].", "[user] feeds something to you.") + log_combat(user, M, "fed", reagents.log_list()) + else + to_chat(user, "You swallow a gulp of [src].") + var/fraction = min(5/reagents.total_volume, 1) + reagents.reaction(M, INGEST, fraction) + addtimer(CALLBACK(reagents, /datum/reagents.proc/trans_to, M, 5), 5) + playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1) + +/obj/item/reagent_containers/food/snacks/grown/coconut/afterattack(obj/target, mob/user, proximity) + . = ..() + if(fusedactive) + return + + if((!proximity) || !check_allowed_items(target,target_self=1)) + return + + if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it. + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + + if(target.reagents.holder_full()) + to_chat(user, "[target] is full.") + return + + var/trans = reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You transfer [trans] unit\s of the solution to [target].") + + else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us. + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty and can't be refilled!") + return + + if(reagents.holder_full()) + to_chat(user, "[src] is full.") + return + + var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) + to_chat(user, "You fill [src] with [trans] unit\s of the contents of [target].") + + else if(reagents.total_volume) + if(user.a_intent == INTENT_HARM && spillable == TRUE) + user.visible_message("[user] splashes the contents of [src] onto [target]!", \ + "You splash the contents of [src] onto [target].") + reagents.reaction(target, TOUCH) + reagents.clear_reagents() + +/obj/item/reagent_containers/food/snacks/grown/coconut/dropped(mob/user) + . = ..() + transform *= TRANSFORM_USING_VARIABLE(40, 100) + 0.5 //temporary fix for size? + +/obj/item/reagent_containers/food/snacks/grown/coconut/proc/prime() + if (defused) + return + var/turf/T = get_turf(src) + reagents.chem_temp = 1000 + //Disable seperated contents when the grenade primes + if (seed.get_gene(/datum/plant_gene/trait/noreact)) + DISABLE_BITFIELD(reagents.reagents_holder_flags, NO_REACT) + reagents.handle_reactions() + log_game("Coconut bomb detonation at [AREACOORD(T)], location [loc]") + qdel(src) + +/obj/item/reagent_containers/food/snacks/grown/coconut/ex_act(severity) + qdel(src) + +/obj/item/reagent_containers/food/snacks/grown/coconut/deconstruct(disassembled = TRUE) + if(!disassembled && fused) + prime() + if(!QDELETED(src)) + qdel(src) diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 4132719389..39e97e3801 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -27,6 +27,7 @@ var/rarity = 0 // How rare the plant is. Used for giving points to cargo when shipping off to CentCom. var/list/mutatelist = list() // The type of plants that this plant can mutate into. var/list/genes = list() // Plant genes are stored here, see plant_genes.dm for more info. + var/list/forbiddengenes = list() var/list/reagents_add = list() // A list of reagents to add to product. // Format: "reagent_id" = potency multiplier @@ -96,6 +97,10 @@ S.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes. return S +obj/item/seeds/proc/is_gene_forbidden(typepath) + return (typepath in forbiddengenes) + + /obj/item/seeds/proc/get_gene(typepath) return (locate(typepath) in genes) @@ -448,7 +453,7 @@ for(var/i in 1 to amount_random_traits) var/random_trait = pick((subtypesof(/datum/plant_gene/trait)-typesof(/datum/plant_gene/trait/plant_type))) var/datum/plant_gene/trait/T = new random_trait - if(T.can_add(src)) + if(T.can_add(src) && !is_gene_forbidden(random_trait)) genes += T else qdel(T) diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm index 922b335a39..f8a7e70473 100644 --- a/code/modules/jobs/job_types/clown.dm +++ b/code/modules/jobs/job_types/clown.dm @@ -14,6 +14,8 @@ access = list(ACCESS_THEATRE) minimal_access = list(ACCESS_THEATRE) + mind_traits = list(TRAIT_CLOWN_MENTALITY) + display_order = JOB_DISPLAY_ORDER_CLOWN /datum/outfit/job/clown diff --git a/code/modules/keybindings/bindings_human.dm b/code/modules/keybindings/bindings_human.dm index a9eafacef6..ed033935b2 100644 --- a/code/modules/keybindings/bindings_human.dm +++ b/code/modules/keybindings/bindings_human.dm @@ -58,13 +58,23 @@ return switch(_key) if("Shift") - sprint_hotkey(TRUE) + if(!user.prefs.sprint_spacebar) + user.prefs.sprint_toggle ? togglesprint() : sprint_hotkey(TRUE) //Yes, this looks hacky. Yes, this works. + return + if("Space") + if(user.prefs.sprint_spacebar) + user.prefs.sprint_toggle ? togglesprint() : sprint_hotkey(TRUE) return return ..() /mob/living/carbon/human/key_up(_key, client/user) switch(_key) if("Shift") - sprint_hotkey(FALSE) + if(!user.prefs.sprint_spacebar && !user.prefs.sprint_toggle) + sprint_hotkey(FALSE) + return + if("Space") + if(user.prefs.sprint_spacebar && !user.prefs.sprint_toggle) + sprint_hotkey(FALSE) return return ..() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 79da476719..b5f7bbf8ab 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -50,7 +50,7 @@ /obj/machinery/mineral/ore_redemption/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Smelting [sheet_per_ore] sheet(s) per piece of ore.
Ore pickup speed at [ore_pickup_rate].
" + . += "The status display reads: Smelting [sheet_per_ore] sheet(s) per piece of ore.
Reward point generation at [point_upgrade*100]%.
Ore pickup speed at [ore_pickup_rate].
" /obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O) var/datum/component/material_container/mat_container = materials.mat_container @@ -63,7 +63,7 @@ ore_buffer -= O if(O && O.refined_type) - points += O.points * O.amount + points += O.points * point_upgrade * O.amount var/material_amount = mat_container.get_item_material_amount(O) diff --git a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm index dd66f68e5d..241ec8118e 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/_sprite_accessories.dm @@ -74,4 +74,5 @@ /datum/sprite_accessory/underwear icon = 'icons/mob/underwear.dmi' - var/has_color = FALSE \ No newline at end of file + var/has_color = FALSE + var/has_digitigrade = FALSE \ No newline at end of file diff --git a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm index 0a35f0cd26..19ec677a72 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/socks.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/socks.dm @@ -2,6 +2,9 @@ // Socks Definitions // /////////////////////// +/datum/sprite_accessory/underwear/socks + has_digitigrade = TRUE + /datum/sprite_accessory/underwear/socks/nude name = "Nude" icon_state = null diff --git a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm index 3356804cb3..9441b5120a 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/underwear.dm @@ -28,41 +28,49 @@ name = "Boxers" icon_state = "boxers" has_color = TRUE + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_bee name = "Boxers - Bee" icon_state = "bee_shorts" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_hearts name = "Boxers - Heart" icon_state = "boxers_heart" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_stripe name = "Boxers - Striped" icon_state = "boxers_striped" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_commie name = "Boxers - Striped Communist" icon_state = "boxers_commie" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_usastripe name = "Boxers - Striped Freedom" icon_state = "boxers_assblastusa" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/male_uk name = "Boxers - Striped UK" icon_state = "boxers_uk" + has_digitigrade = TRUE gender = MALE /datum/sprite_accessory/underwear/bottom/boxer_briefs name = "Boxer Briefs" icon_state = "boxer_briefs" + has_digitigrade = TRUE has_color = TRUE /datum/sprite_accessory/underwear/bottom/panties @@ -140,6 +148,7 @@ /datum/sprite_accessory/underwear/bottom/longjon name = "Long John Bottoms" icon_state = "ljonb" + has_digitigrade = TRUE has_color = TRUE /datum/sprite_accessory/underwear/bottom/swimsuit_red diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 1b328dbc69..bf86a97574 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -18,3 +18,6 @@ update_icon(preferred_form) updateghostimages() + + client.reenter_round_timeout = max(client.reenter_round_timeout, clientless_round_timeout) + clientless_round_timeout = client.reenter_round_timeout diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 2b9b01dc18..0846cf1ea1 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -3,8 +3,6 @@ GLOBAL_LIST_EMPTY(ghost_images_simple) //this is a list of all ghost images as t GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) -#define CANT_REENTER_ROUND -1 - /mob/dead/observer name = "ghost" desc = "It's a g-g-g-g-ghooooost!" //jinkies! @@ -21,7 +19,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) hud_type = /datum/hud/ghost movement_type = GROUND | FLYING var/can_reenter_corpse - var/reenter_round_timeout = 0 // used to prevent people from coming back through ghost roles/midround antags as they suicide/cryo for a duration set by CONFIG_GET(number/suicide_reenter_round_timer) and CONFIG_GET(number/roundstart_suicide_time_limit) + var/clientless_round_timeout = 0 //mobs will lack a client as long as their player is disconnected. See client_defines.dm "reenter_round_timeout" var/datum/hud/living/carbon/hud = null // hud var/bootime = 0 var/started_as_observer //This variable is set to 1 when you enter the game as an observer. @@ -278,9 +276,16 @@ Works together with spawning an observer, noted above. if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed. penalty += roundstart_quit_limit - world.time if(penalty) - ghost.reenter_round_timeout = world.realtime + penalty - if(ghost.reenter_round_timeout - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime) - ghost.reenter_round_timeout = CANT_REENTER_ROUND + penalty += world.realtime + if(penalty - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime) + penalty = CANT_REENTER_ROUND + if(client) + client.reenter_round_timeout = penalty + else //A disconnected player (quite likely for cryopods) + ghost.clientless_round_timeout = penalty + if (client && client.prefs && client.prefs.auto_ooc) + if (!(client.prefs.chat_toggles & CHAT_OOC)) + client.prefs.chat_toggles ^= CHAT_OOC transfer_ckey(ghost, FALSE) return ghost @@ -339,10 +344,13 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghostize(0, penalize = TRUE) /mob/dead/observer/proc/can_reenter_round(silent = FALSE) - if(reenter_round_timeout != CANT_REENTER_ROUND && reenter_round_timeout <= world.realtime) + var/timeout = clientless_round_timeout + if(client) + timeout = client.reenter_round_timeout + if(timeout != CANT_REENTER_ROUND && timeout <= world.realtime) return TRUE - if(!silent) - to_chat(src, "You are unable to reenter the round[reenter_round_timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(reenter_round_timeout - world.realtime)]" : ""].") + if(!silent && client) + to_chat(src, "You are unable to reenter the round[timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(timeout - world.realtime)]" : ""].") return FALSE /mob/dead/observer/Move(NewLoc, direct) @@ -898,5 +906,3 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp spawners_menu = new(src) spawners_menu.ui_interact(src) - -#undef CANT_REENTER_ROUND \ No newline at end of file diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index b9e27c0637..bdc691ce49 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -6,21 +6,21 @@ return 2 //no ears /mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush) - ..(AM, skipcatch = TRUE, hitpush = FALSE) + return ..(AM, skipcatch = TRUE, hitpush = FALSE) +/mob/living/carbon/alien/can_embed(obj/item/I) + return FALSE /*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other. As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble. In all, this is a lot like the monkey code. /N */ /mob/living/carbon/alien/attack_alien(mob/living/carbon/alien/M) - if(isturf(loc) && istype(loc.loc, /area/start)) - to_chat(M, "No attacking people at spawn, you jackass.") + . = ..() + if(!.) // the attack was blocked or was help/grab intent return - switch(M.a_intent) - - if ("help") + if (INTENT_HELP) if(!recoveringstam) resting = 0 AdjustStun(-60) @@ -28,11 +28,7 @@ In all, this is a lot like the monkey code. /N AdjustUnconscious(-60) AdjustSleeping(-100) visible_message("[M.name] nuzzles [src] trying to wake [p_them()] up!") - - if ("grab") - grabbedby(M) - - else + if(INTENT_DISARM, INTENT_HARM) if(health > 0) M.do_attack_animation(src, ATTACK_EFFECT_BITE) playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) @@ -50,28 +46,31 @@ In all, this is a lot like the monkey code. /N /mob/living/carbon/alien/attack_hand(mob/living/carbon/human/M) - if(..()) //to allow surgery to return properly. - return 0 - + . = ..() + if(.) //To allow surgery to return properly. + return switch(M.a_intent) - if("help") + if(INTENT_HELP) help_shake_act(M) - if("grab") + if(INTENT_GRAB) grabbedby(M) - if ("harm") + if (INTENT_HARM) + if(HAS_TRAIT(M, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt [src]!") + return TRUE M.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - return 1 - if("disarm") + if(INTENT_DISARM) + if(HAS_TRAIT(M, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt [src]!") + return TRUE M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - return 1 - return 0 /mob/living/carbon/alien/attack_paw(mob/living/carbon/monkey/M) - if(..()) - if (stat != DEAD) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - apply_damage(rand(1, 3), BRUTE, affecting) + . = ..() + if(.) //successful monkey bite. + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) + apply_damage(rand(1, 3), BRUTE, affecting) /mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M) @@ -93,13 +92,15 @@ In all, this is a lot like the monkey code. /N adjustStaminaLoss(damage) /mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - var/damage = rand(5, 35) - if(M.is_adult) - damage = rand(10, 40) - adjustBruteLoss(damage) - log_combat(M, src, "attacked") - updatehealth() + . = ..() + if(!.) //unsuccessful slime attack + return + var/damage = rand(5, 35) + if(M.is_adult) + damage = rand(10, 40) + adjustBruteLoss(damage) + log_combat(M, src, "attacked") + updatehealth() /mob/living/carbon/alien/ex_act(severity, target, origin) if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src)) diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index fe682b5c99..d1ed09665b 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -63,12 +63,7 @@ if(A) if(isliving(A)) var/mob/living/L = A - var/blocked = FALSE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) - blocked = TRUE - if(!blocked) + if(!L.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") L.Knockdown(100) sleep(2)//Runtime prevention (infinite bump() calls on hulks) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm index b3839a6033..1d613db07a 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm @@ -5,9 +5,11 @@ else ..() -/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) - ..(user, 1) + . = ..(user, TRUE) + if(.) + return adjustBruteLoss(15) var/hitverb = "punched" if(mob_size < MOB_SIZE_LARGE) @@ -21,46 +23,46 @@ return 1 /mob/living/carbon/alien/humanoid/attack_hand(mob/living/carbon/human/M) - if(..()) - switch(M.a_intent) - if ("harm") - var/damage = rand(1, 9) - if (prob(90)) - playsound(loc, "punch", 25, 1, -1) - visible_message("[M] has punched [src]!", \ - "[M] has punched [src]!", null, COMBAT_MESSAGE_RANGE) - if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down. - Unconscious(40) - visible_message("[M] has knocked [src] down!", \ - "[M] has knocked [src] down!") - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - apply_damage(damage, BRUTE, affecting) - log_combat(M, src, "attacked") + . = ..() + if(.) //To allow surgery to return properly. + return + switch(M.a_intent) + if (INTENT_HARM) + var/damage = rand(1, 9) + if (prob(90)) + playsound(loc, "punch", 25, 1, -1) + visible_message("[M] has punched [src]!", \ + "[M] has punched [src]!", null, COMBAT_MESSAGE_RANGE) + if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down. + Unconscious(40) + visible_message("[M] has knocked [src] down!", \ + "[M] has knocked [src] down!") + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) + apply_damage(damage, BRUTE, affecting) + log_combat(M, src, "attacked") + else + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[M] has attempted to punch [src]!", \ + "[M] has attempted to punch [src]!", null, COMBAT_MESSAGE_RANGE) + + if (INTENT_DISARM) + if (!lying) + if (prob(5)) + Unconscious(40) + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + log_combat(M, src, "pushed") + visible_message("[M] has pushed down [src]!", \ + "[M] has pushed down [src]!") else - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M] has attempted to punch [src]!", \ - "[M] has attempted to punch [src]!", null, COMBAT_MESSAGE_RANGE) - - if ("disarm") - if (!lying) - if (prob(5)) - Unconscious(40) + if (prob(50)) + dropItemToGround(get_active_held_item()) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - log_combat(M, src, "pushed") - visible_message("[M] has pushed down [src]!", \ - "[M] has pushed down [src]!") + visible_message("[M] has disarmed [src]!", \ + "[M] has disarmed [src]!", null, COMBAT_MESSAGE_RANGE) else - if (prob(50)) - dropItemToGround(get_active_held_item()) - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - visible_message("[M] has disarmed [src]!", \ - "[M] has disarmed [src]!", null, COMBAT_MESSAGE_RANGE) - else - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M] has attempted to disarm [src]!",\ - "[M] has attempted to disarm [src]!", null, COMBAT_MESSAGE_RANGE) - - + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[M] has attempted to disarm [src]!",\ + "[M] has attempted to disarm [src]!", null, COMBAT_MESSAGE_RANGE) /mob/living/carbon/alien/humanoid/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) if(!no_effect && !visual_effect_icon) diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm index 69c1be707d..7dabcf5abf 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm @@ -1,26 +1,33 @@ /mob/living/carbon/alien/larva/attack_hand(mob/living/carbon/human/M) - if(..()) - var/damage = rand(1, 9) - if (prob(90)) - playsound(loc, "punch", 25, 1, -1) - log_combat(M, src, "attacked") - visible_message("[M] has kicked [src]!", \ - "[M] has kicked [src]!", null, COMBAT_MESSAGE_RANGE) - if ((stat != DEAD) && (damage > 4.9)) - Unconscious(rand(100,200)) + . = ..() + if(. || M.a_intent == INTENT_HELP || M.a_intent == INTENT_GRAB) + return + var/damage = rand(1, 9) + if (prob(90)) + playsound(loc, "punch", 25, 1, -1) + log_combat(M, src, "attacked") + visible_message("[M] has kicked [src]!", \ + "[M] has kicked [src]!", null, COMBAT_MESSAGE_RANGE) + if ((stat != DEAD) && (damage > 4.9)) + Unconscious(rand(100,200)) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - apply_damage(damage, BRUTE, affecting) - else - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M] has attempted to kick [src]!", \ - "[M] has attempted to kick [src]!", null, COMBAT_MESSAGE_RANGE) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) + apply_damage(damage, BRUTE, affecting) + else + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[M] has attempted to kick [src]!", \ + "[M] has attempted to kick [src]!", null, COMBAT_MESSAGE_RANGE) -/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) - ..(user, 1) + . = ..(user, TRUE) + if(.) + return + playsound(loc, "punch", 25, 1, -1) + visible_message("[user] has pummeled [src]!", \ + "[user] has pummeled [src]!", null, COMBAT_MESSAGE_RANGE) adjustBruteLoss(5 + rand(1,9)) new /datum/forced_movement(src, get_step_away(user,src, 30), 1) return 1 diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 0bfd23cbff..c08fe4f582 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -103,15 +103,17 @@ new_xeno.notransform = 0 new_xeno.invisibility = 0 + var/mob/living/carbon/old_owner = owner if(kill_on_sucess) //ITS TOO LATE new_xeno.visible_message("[new_xeno] bursts out of [owner]!", "You exit [owner], your previous host.", "You hear organic matter ripping and tearing!") owner.apply_damage(rand(100,300),BRUTE,zone,FALSE) //Random high damage to torso so health sensors don't metagame. - owner.spill_organs(TRUE,FALSE,TRUE) //Lets still make the death gruesome and impossible to just simply defib someone. + var/obj/item/bodypart/B = owner.get_bodypart(zone) + B.drop_organs(owner) //Lets still make the death gruesome and impossible to just simply defib someone. owner.death(FALSE) //Just in case some freak occurance occurs where you somehow survive all your organs being removed from you and the 100-300 brute damage. else //When it is removed via surgery at a late stage, rather than forced. new_xeno.visible_message("[new_xeno] wriggles out of [owner]!", "You exit [owner], your previous host.") owner.adjustBruteLoss(40) - owner.cut_overlay(overlay) + old_owner.cut_overlay(overlay) qdel(src) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 102bc6fc3d..32d09d8853 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD /mob/living/carbon blood_volume = BLOOD_VOLUME_NORMAL @@ -989,3 +990,996 @@ return TRUE if(isclothing(wear_mask) && (wear_mask.clothing_flags & SCAN_REAGENTS)) return TRUE +======= +/mob/living/carbon + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/carbon/Initialize() + . = ..() + create_reagents(1000) + update_body_parts() //to update the carbon's new bodyparts appearance + GLOB.carbon_list += src + blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio) + +/mob/living/carbon/Destroy() + //This must be done first, so the mob ghosts correctly before DNA etc is nulled + . = ..() + + QDEL_LIST(internal_organs) + QDEL_LIST(stomach_contents) + QDEL_LIST(bodyparts) + QDEL_LIST(implants) + remove_from_all_data_huds() + QDEL_NULL(dna) + GLOB.carbon_list -= src + +/mob/living/carbon/initialize_footstep() + AddComponent(/datum/component/footstep, 0.6, 2) + +/mob/living/carbon/relaymove(mob/user, direction) + if(user in src.stomach_contents) + if(prob(40)) + if(prob(25)) + audible_message("You hear something rumbling inside [src]'s stomach...", \ + "You hear something rumbling.", 4,\ + "Something is rumbling inside your stomach!") + var/obj/item/I = user.get_active_held_item() + if(I && I.force) + var/d = rand(round(I.force / 4), I.force) + var/obj/item/bodypart/BP = get_bodypart(BODY_ZONE_CHEST) + if(BP.receive_damage(d, 0)) + update_damage_overlays() + visible_message("[user] attacks [src]'s stomach wall with the [I.name]!", \ + "[user] attacks your stomach wall with the [I.name]!") + playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1) + + if(prob(src.getBruteLoss() - 50)) + for(var/atom/movable/A in stomach_contents) + A.forceMove(drop_location()) + stomach_contents.Remove(A) + src.gib() + + +/mob/living/carbon/swap_hand(held_index) + if(!held_index) + held_index = (active_hand_index % held_items.len)+1 + + var/obj/item/item_in_hand = src.get_active_held_item() + if(item_in_hand) //this segment checks if the item in your hand is twohanded. + var/obj/item/twohanded/TH = item_in_hand + if(istype(TH)) + if(TH.wielded == 1) + to_chat(usr, "Your other hand is too busy holding [TH]") + return + var/oindex = active_hand_index + active_hand_index = held_index + if(hud_used) + var/obj/screen/inventory/hand/H + H = hud_used.hand_slots["[oindex]"] + if(H) + H.update_icon() + H = hud_used.hand_slots["[held_index]"] + if(H) + H.update_icon() + + +/mob/living/carbon/activate_hand(selhand) //l/r OR 1-held_items.len + if(!selhand) + selhand = (active_hand_index % held_items.len)+1 + + if(istext(selhand)) + selhand = lowertext(selhand) + if(selhand == "right" || selhand == "r") + selhand = 2 + if(selhand == "left" || selhand == "l") + selhand = 1 + + if(selhand != active_hand_index) + swap_hand(selhand) + else + mode() // Activate held item + +/mob/living/carbon/attackby(obj/item/I, mob/user, params) + if(lying && surgeries.len) + if(user != src && (user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)) + for(var/datum/surgery/S in surgeries) + if(S.next_step(user,user.a_intent)) + return 1 + return ..() + +/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum) + . = ..() + var/hurt = TRUE + if(istype(throwingdatum, /datum/thrownthing)) + var/datum/thrownthing/D = throwingdatum + if(iscyborg(D.thrower)) + var/mob/living/silicon/robot/R = D.thrower + if(!R.emagged) + hurt = FALSE + if(hit_atom.density && isturf(hit_atom)) + if(hurt) + Knockdown(20) + take_bodypart_damage(10) + if(iscarbon(hit_atom) && hit_atom != src) + var/mob/living/carbon/victim = hit_atom + if(victim.movement_type & FLYING) + return + if(hurt) + victim.take_bodypart_damage(10) + take_bodypart_damage(10) + victim.Knockdown(20) + Knockdown(20) + visible_message("[src] crashes into [victim], knocking them both over!",\ + "You violently crash into [victim]!") + playsound(src,'sound/weapons/punch1.ogg',50,1) + + +//Throwing stuff +/mob/living/carbon/proc/toggle_throw_mode() + if(stat) + return + if(in_throw_mode) + throw_mode_off() + else + throw_mode_on() + + +/mob/living/carbon/proc/throw_mode_off() + in_throw_mode = 0 + if(client && hud_used) + hud_used.throw_icon.icon_state = "act_throw_off" + + +/mob/living/carbon/proc/throw_mode_on() + in_throw_mode = 1 + if(client && hud_used) + hud_used.throw_icon.icon_state = "act_throw_on" + +/mob/proc/throw_item(atom/target) + SEND_SIGNAL(src, COMSIG_MOB_THROW, target) + return + +/mob/living/carbon/throw_item(atom/target) + throw_mode_off() + if(!target || !isturf(loc)) + return + if(istype(target, /obj/screen)) + return + + //CIT CHANGES - makes it impossible to throw while in stamina softcrit + if(getStaminaLoss() >= STAMINA_SOFTCRIT) + to_chat(src, "You're too exhausted.") + return + var/random_turn = a_intent == INTENT_HARM + //END OF CIT CHANGES + + var/obj/item/I = src.get_active_held_item() + + var/atom/movable/thrown_thing + var/mob/living/throwable_mob + + if(istype(I, /obj/item/clothing/head/mob_holder)) + var/obj/item/clothing/head/mob_holder/holder = I + if(holder.held_mob) + throwable_mob = holder.held_mob + holder.release() + + if(!I || throwable_mob) + if(!throwable_mob && pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE) + throwable_mob = pulling + + if(throwable_mob && !throwable_mob.buckled) + thrown_thing = throwable_mob + if(pulling) + stop_pulling() + if(HAS_TRAIT(src, TRAIT_PACIFISM)) + to_chat(src, "You gently let go of [throwable_mob].") + return + + adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring + var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors + var/turf/end_T = get_turf(target) + if(start_T && end_T) + log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") + + else if(!CHECK_BITFIELD(I.item_flags, ABSTRACT) && !HAS_TRAIT(I, TRAIT_NODROP)) + thrown_thing = I + dropItemToGround(I) + + if(HAS_TRAIT(src, TRAIT_PACIFISM) && I.throwforce) + to_chat(src, "You set [I] down gently on the ground.") + return + + adjustStaminaLossBuffered(I.getweight()*2)//CIT CHANGE - throwing items shall be more tiring than swinging em. Doubly so. + + if(thrown_thing) + visible_message("[src] has thrown [thrown_thing].") + src.log_message("has thrown [thrown_thing]", LOG_ATTACK) + do_attack_animation(target, no_effect = 1) + playsound(loc, 'sound/weapons/punchmiss.ogg', 50, 1, -1) + newtonian_move(get_dir(target, src)) + thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src, null, null, null, random_turn) + + + +/mob/living/carbon/restrained(ignore_grab) + . = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE)) + +/mob/living/carbon/proc/canBeHandcuffed() + return 0 + + +/mob/living/carbon/show_inv(mob/user) + user.set_machine(src) + var/dat = {" +
+ [name] +
+
Head: [(head && !(head.item_flags & ABSTRACT)) ? head : "Nothing"] +
Mask: [(wear_mask && !(wear_mask.item_flags & ABSTRACT)) ? wear_mask : "Nothing"] +
Neck: [(wear_neck && !(wear_neck.item_flags & ABSTRACT)) ? wear_neck : "Nothing"]"} + + for(var/i in 1 to held_items.len) + var/obj/item/I = get_item_for_held_index(i) + dat += "
[get_held_index_name(i)]:[(I && !(I.item_flags & ABSTRACT)) ? I : "Nothing"]" + + dat += "
Back: [back ? back : "Nothing"]" + + if(istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank)) + dat += "
[internal ? "Disable Internals" : "Set Internals"]" + + if(handcuffed) + dat += "
Handcuffed" + if(legcuffed) + dat += "
Legcuffed" + + dat += {" +
+
Close + "} + user << browse(dat, "window=mob[REF(src)];size=325x500") + onclose(user, "mob[REF(src)]") + +/mob/living/carbon/Topic(href, href_list) + ..() + //strip panel + if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) + if(href_list["internal"]) + var/slot = text2num(href_list["internal"]) + var/obj/item/ITEM = get_item_by_slot(slot) + if(ITEM && istype(ITEM, /obj/item/tank) && wear_mask && (wear_mask.clothing_flags & ALLOWINTERNALS)) + visible_message("[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].", \ + "[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].") + if(do_mob(usr, src, POCKET_STRIP_DELAY)) + if(internal) + internal = null + update_internals_hud_icon(0) + else if(ITEM && istype(ITEM, /obj/item/tank)) + if((wear_mask && (wear_mask.clothing_flags & ALLOWINTERNALS)) || getorganslot(ORGAN_SLOT_BREATHING_TUBE)) + internal = ITEM + update_internals_hud_icon(1) + + visible_message("[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].", \ + "[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].") + + +/mob/living/carbon/fall(forced) + loc.handle_fall(src, forced)//it's loc so it doesn't call the mob's handle_fall which does nothing + +/mob/living/carbon/is_muzzled() + return(istype(src.wear_mask, /obj/item/clothing/mask/muzzle)) + +/mob/living/carbon/hallucinating() + if(hallucination) + return TRUE + else + return FALSE + +/mob/living/carbon/resist_buckle() + if(restrained()) + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + var/buckle_cd = 600 + if(handcuffed) + var/obj/item/restraints/O = src.get_item_by_slot(SLOT_HANDCUFFED) + buckle_cd = O.breakouttime + visible_message("[src] attempts to unbuckle [p_them()]self!", \ + "You attempt to unbuckle yourself... (This will take around [round(buckle_cd/600,1)] minute\s, and you need to stay still.)") + if(do_after(src, buckle_cd, 0, target = src)) + if(!buckled) + return + buckled.user_unbuckle_mob(src,src) + else + if(src && buckled) + to_chat(src, "You fail to unbuckle yourself!") + else + buckled.user_unbuckle_mob(src,src) + +/mob/living/carbon/resist_fire() + fire_stacks -= 5 + Knockdown(60, TRUE, TRUE) + spin(32,2) + visible_message("[src] rolls on the floor, trying to put [p_them()]self out!", \ + "You stop, drop, and roll!") + sleep(30) + if(fire_stacks <= 0) + visible_message("[src] has successfully extinguished [p_them()]self!", \ + "You extinguish yourself.") + ExtinguishMob() + return + +/mob/living/carbon/resist_restraints() + var/obj/item/I = null + var/type = 0 + if(handcuffed) + I = handcuffed + type = 1 + else if(legcuffed) + I = legcuffed + type = 2 + if(I) + if(type == 1) + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + if(type == 2) + changeNext_move(CLICK_CD_RANGE) + last_special = world.time + CLICK_CD_RANGE + cuff_resist(I) + + +/mob/living/carbon/proc/cuff_resist(obj/item/I, breakouttime = 600, cuff_break = 0) + if(I.item_flags & BEING_REMOVED) + to_chat(src, "You're already attempting to remove [I]!") + return + I.item_flags |= BEING_REMOVED + breakouttime = I.breakouttime + if(!cuff_break) + visible_message("[src] attempts to remove [I]!") + to_chat(src, "You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)") + if(do_after(src, breakouttime, 0, target = src)) + clear_cuffs(I, cuff_break) + else + to_chat(src, "You fail to remove [I]!") + + else if(cuff_break == FAST_CUFFBREAK) + breakouttime = 50 + visible_message("[src] is trying to break [I]!") + to_chat(src, "You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)") + if(do_after(src, breakouttime, 0, target = src)) + clear_cuffs(I, cuff_break) + else + to_chat(src, "You fail to break [I]!") + + else if(cuff_break == INSTANT_CUFFBREAK) + clear_cuffs(I, cuff_break) + I.item_flags &= ~BEING_REMOVED + +/mob/living/carbon/proc/uncuff() + if (handcuffed) + var/obj/item/W = handcuffed + handcuffed = null + if (buckled && buckled.buckle_requires_restraints) + buckled.unbuckle_mob(src) + update_handcuffed() + if (client) + client.screen -= W + if (W) + W.forceMove(drop_location()) + W.dropped(src) + if (W) + W.layer = initial(W.layer) + W.plane = initial(W.plane) + changeNext_move(0) + if (legcuffed) + var/obj/item/W = legcuffed + legcuffed = null + update_inv_legcuffed() + if (client) + client.screen -= W + if (W) + W.forceMove(drop_location()) + W.dropped(src) + if (W) + W.layer = initial(W.layer) + W.plane = initial(W.plane) + changeNext_move(0) + +/mob/living/carbon/proc/clear_cuffs(obj/item/I, cuff_break) + if(!I.loc || buckled) + return + visible_message("[src] manages to [cuff_break ? "break" : "remove"] [I]!") + to_chat(src, "You successfully [cuff_break ? "break" : "remove"] [I].") + + if(cuff_break) + . = !((I == handcuffed) || (I == legcuffed)) + qdel(I) + return + + else + if(I == handcuffed) + handcuffed.forceMove(drop_location()) + handcuffed.dropped(src) + handcuffed = null + if(buckled && buckled.buckle_requires_restraints) + buckled.unbuckle_mob(src) + update_handcuffed() + return + if(I == legcuffed) + legcuffed.forceMove(drop_location()) + legcuffed.dropped() + legcuffed = null + update_inv_legcuffed() + return + else + dropItemToGround(I) + return + return TRUE + +/mob/living/carbon/get_standard_pixel_y_offset(lying = 0) + if(lying) + return -6 + else + return initial(pixel_y) + +/mob/living/carbon/proc/accident(obj/item/I) + if(!I || (I.item_flags & ABSTRACT) || HAS_TRAIT(I, TRAIT_NODROP)) + return + + //dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100 + + var/modifier = 0 + + if(HAS_TRAIT(src, TRAIT_CLUMSY)) + modifier -= 40 //Clumsy people are more likely to hit themselves -Honk! + + //CIT CHANGES START HERE + else if(combatmode) + modifier += 50 + + if(modifier < 100) + dropItemToGround(I) + //END OF CIT CHANGES + + switch(rand(1,100)+modifier) //91-100=Nothing special happens + if(-INFINITY to 0) //attack yourself + I.attack(src,src) + if(1 to 30) //throw it at yourself + I.throw_impact(src) + if(31 to 60) //Throw object in facing direction + var/turf/target = get_turf(loc) + var/range = rand(2,I.throw_range) + for(var/i = 1; i < range; i++) + var/turf/new_turf = get_step(target, dir) + target = new_turf + if(new_turf.density) + break + I.throw_at(target,I.throw_range,I.throw_speed,src) + if(61 to 90) //throw it down to the floor + var/turf/target = get_turf(loc) + I.throw_at(target,I.throw_range,I.throw_speed,src) + +/mob/living/carbon/Stat() + ..() + if(statpanel("Status")) + var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel) + if(vessel) + stat(null, "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]") + if(locate(/obj/item/assembly/health) in src) + stat(null, "Health: [health]") + + add_abilities_to_panel() + +/mob/living/carbon/attack_ui(slot) + if(!has_hand_for_held_index(active_hand_index)) + return 0 + return ..() + +/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE) + if(HAS_TRAIT(src, TRAIT_NOHUNGER)) + return 1 + + if(nutrition < 100 && !blood) + if(message) + visible_message("[src] dry heaves!", \ + "You try to throw up, but there's nothing in your stomach!") + if(stun) + Knockdown(200) + return 1 + + if(is_mouth_covered()) //make this add a blood/vomit overlay later it'll be hilarious + if(message) + visible_message("[src] throws up all over [p_them()]self!", \ + "You throw up all over yourself!") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomitself) + distance = 0 + else + if(message) + visible_message("[src] throws up!", "You throw up!") + if(!isflyperson(src)) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomit) + if(stun) + Stun(80) + + playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1) + var/turf/T = get_turf(src) + if(!blood) + nutrition -= lost_nutrition + adjustToxLoss(-3) + for(var/i=0 to distance) + if(blood) + if(T) + add_splatter_floor(T) + if(stun) + adjustBruteLoss(3) + else if(src.reagents.has_reagent("blazaam")) + if(T) + T.add_vomit_floor(src, VOMIT_PURPLE) + else + if(T) + T.add_vomit_floor(src, VOMIT_TOXIC)//toxic barf looks different + T = get_step(T, dir) + if (is_blocked_turf(T)) + break + return 1 + +/mob/living/carbon/proc/spew_organ(power = 5, amt = 1) + for(var/i in 1 to amt) + if(!internal_organs.len) + break //Guess we're out of organs! + var/obj/item/organ/guts = pick(internal_organs) + var/turf/T = get_turf(src) + guts.Remove(src) + guts.forceMove(T) + var/atom/throw_target = get_edge_target_turf(guts, dir) + guts.throw_at(throw_target, power, 4, src) + + +/mob/living/carbon/fully_replace_character_name(oldname,newname) + ..() + if(dna) + dna.real_name = real_name + +//Updates the mob's health from bodyparts and mob damage variables +/mob/living/carbon/updatehealth() + if(status_flags & GODMODE) + return + var/total_burn = 0 + var/total_brute = 0 + var/total_stamina = 0 + for(var/X in bodyparts) //hardcoded to streamline things a bit + var/obj/item/bodypart/BP = X + total_brute += (BP.brute_dam * BP.body_damage_coeff) + total_burn += (BP.burn_dam * BP.body_damage_coeff) + total_stamina += (BP.stamina_dam * BP.stam_damage_coeff) + health = round(maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute, DAMAGE_PRECISION) + staminaloss = round(total_stamina, DAMAGE_PRECISION) + update_stat() + if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD ) + become_husk("burn") + med_hud_set_health() + if(stat == SOFT_CRIT) + add_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE, multiplicative_slowdown = SOFTCRIT_ADD_SLOWDOWN) + else + remove_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE) + +/mob/living/carbon/update_stamina() + var/stam = getStaminaLoss() + if(stam > DAMAGE_PRECISION) + var/total_health = (health - stam) + if(total_health <= crit_threshold && !stat) + if(!IsKnockdown()) + to_chat(src, "You're too exhausted to keep going...") + Knockdown(100) + update_health_hud() + +/mob/living/carbon/update_sight() + if(!client) + return + if(stat == DEAD) + sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_OBSERVER + return + + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES) + if(!E) + update_tint() + else + see_invisible = E.see_invisible + see_in_dark = E.see_in_dark + sight |= E.sight_flags + if(!isnull(E.lighting_alpha)) + lighting_alpha = E.lighting_alpha + if(HAS_TRAIT(src, TRAIT_NIGHT_VISION)) + lighting_alpha = min(LIGHTING_PLANE_ALPHA_NV_TRAIT, lighting_alpha) + see_in_dark = max(NIGHT_VISION_DARKSIGHT_RANGE, see_in_dark) + + if(client.eye && client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + if(glasses) + var/obj/item/clothing/glasses/G = glasses + sight |= G.vision_flags + see_in_dark = max(G.darkness_view, see_in_dark) + if(G.invis_override) + see_invisible = G.invis_override + else + see_invisible = min(G.invis_view, see_invisible) + if(!isnull(G.lighting_alpha)) + lighting_alpha = min(lighting_alpha, G.lighting_alpha) + if(dna) + for(var/X in dna.mutations) + var/datum/mutation/M = X + if(M.name == XRAY) + sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_in_dark = max(see_in_dark, 8) + + if(see_override) + see_invisible = see_override + . = ..() + + +//to recalculate and update the mob's total tint from tinted equipment it's wearing. +/mob/living/carbon/proc/update_tint() + if(!GLOB.tinted_weldhelh) + return + tinttotal = get_total_tint() + if(tinttotal >= TINT_BLIND) + become_blind(EYES_COVERED) + else if(tinttotal >= TINT_DARKENED) + cure_blind(EYES_COVERED) + overlay_fullscreen("tint", /obj/screen/fullscreen/impaired, 2) + else + cure_blind(EYES_COVERED) + clear_fullscreen("tint", 0) + +/mob/living/carbon/proc/get_total_tint() + . = 0 + if(istype(head, /obj/item/clothing/head)) + var/obj/item/clothing/head/HT = head + . += HT.tint + if(wear_mask) + . += wear_mask.tint + + var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES) + if(E) + . += E.tint + + else + . += INFINITY + +/mob/living/carbon/get_permeability_protection(list/target_zones = list(HANDS,CHEST,GROIN,LEGS,FEET,ARMS,HEAD)) + var/list/tally = list() + for(var/obj/item/I in get_equipped_items()) + for(var/zone in target_zones) + if(I.body_parts_covered & zone) + tally["[zone]"] = max(1 - I.permeability_coefficient, target_zones["[zone]"]) + var/protection = 0 + for(var/key in tally) + protection += tally[key] + protection *= INVERSE(target_zones.len) + return protection + +//this handles hud updates +/mob/living/carbon/update_damage_hud() + + if(!client) + return + + if(health <= crit_threshold) + var/severity = 0 + switch(health) + if(-20 to -10) + severity = 1 + if(-30 to -20) + severity = 2 + if(-40 to -30) + severity = 3 + if(-50 to -40) + severity = 4 + if(-50 to -40) + severity = 5 + if(-60 to -50) + severity = 6 + if(-70 to -60) + severity = 7 + if(-90 to -70) + severity = 8 + if(-95 to -90) + severity = 9 + if(-INFINITY to -95) + severity = 10 + if(!InFullCritical()) + var/visionseverity = 4 + switch(health) + if(-8 to -4) + visionseverity = 5 + if(-12 to -8) + visionseverity = 6 + if(-16 to -12) + visionseverity = 7 + if(-20 to -16) + visionseverity = 8 + if(-24 to -20) + visionseverity = 9 + if(-INFINITY to -24) + visionseverity = 10 + overlay_fullscreen("critvision", /obj/screen/fullscreen/crit/vision, visionseverity) + else + clear_fullscreen("critvision") + overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) + else + clear_fullscreen("crit") + clear_fullscreen("critvision") + + //Oxygen damage overlay + var/windedup = getOxyLoss() + getStaminaLoss() * 0.2 + if(windedup) + var/severity = 0 + switch(windedup) + if(10 to 20) + severity = 1 + if(20 to 25) + severity = 2 + if(25 to 30) + severity = 3 + if(30 to 35) + severity = 4 + if(35 to 40) + severity = 5 + if(40 to 45) + severity = 6 + if(45 to INFINITY) + severity = 7 + overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) + else + clear_fullscreen("oxy") + + //Fire and Brute damage overlay (BSSR) + var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp + if(hurtdamage) + var/severity = 0 + switch(hurtdamage) + if(5 to 15) + severity = 1 + if(15 to 30) + severity = 2 + if(30 to 45) + severity = 3 + if(45 to 70) + severity = 4 + if(70 to 85) + severity = 5 + if(85 to INFINITY) + severity = 6 + overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) + else + clear_fullscreen("brute") + +/mob/living/carbon/update_health_hud(shown_health_amount) + if(!client || !hud_used) + return + if(hud_used.healths) + if(stat != DEAD) + . = 1 + if(!shown_health_amount) + shown_health_amount = health + if(shown_health_amount >= maxHealth) + hud_used.healths.icon_state = "health0" + else if(shown_health_amount > maxHealth*0.8) + hud_used.healths.icon_state = "health1" + else if(shown_health_amount > maxHealth*0.6) + hud_used.healths.icon_state = "health2" + else if(shown_health_amount > maxHealth*0.4) + hud_used.healths.icon_state = "health3" + else if(shown_health_amount > maxHealth*0.2) + hud_used.healths.icon_state = "health4" + else if(shown_health_amount > 0) + hud_used.healths.icon_state = "health5" + else + hud_used.healths.icon_state = "health6" + else + hud_used.healths.icon_state = "health7" + +/mob/living/carbon/proc/update_internals_hud_icon(internal_state = 0) + if(hud_used && hud_used.internals) + hud_used.internals.icon_state = "internal[internal_state]" + +/mob/living/carbon/update_stat() + if(status_flags & GODMODE) + return + if(stat != DEAD) + if(health <= HEALTH_THRESHOLD_DEAD && !HAS_TRAIT(src, TRAIT_NODEATH)) + death() + return + if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !HAS_TRAIT(src, TRAIT_NOHARDCRIT))) + stat = UNCONSCIOUS + blind_eyes(1) + if(combatmode) + toggle_combat_mode(TRUE, TRUE) + else + if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT)) + stat = SOFT_CRIT + if(combatmode) + toggle_combat_mode(TRUE, TRUE) + else + stat = CONSCIOUS + adjust_blindness(-1) + update_canmove() + update_damage_hud() + update_health_hud() + med_hud_set_status() + +//called when we get cuffed/uncuffed +/mob/living/carbon/proc/update_handcuffed() + if(handcuffed) + drop_all_held_items() + stop_pulling() + throw_alert("handcuffed", /obj/screen/alert/restrained/handcuffed, new_master = src.handcuffed) + if(handcuffed.demoralize_criminals) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "handcuffed", /datum/mood_event/handcuffed) + else + clear_alert("handcuffed") + SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "handcuffed") + update_action_buttons_icon() //some of our action buttons might be unusable when we're handcuffed. + update_inv_handcuffed() + update_hud_handcuffed() + +/mob/living/carbon/fully_heal(admin_revive = FALSE) + if(reagents) + reagents.clear_reagents() + var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain) + if(B) + B.brain_death = FALSE + for(var/thing in diseases) + var/datum/disease/D = thing + if(D.severity != DISEASE_SEVERITY_POSITIVE) + D.cure(FALSE) + if(admin_revive) + regenerate_limbs() + regenerate_organs() + handcuffed = initial(handcuffed) + for(var/obj/item/restraints/R in contents) //actually remove cuffs from inventory + qdel(R) + update_handcuffed() + if(reagents) + reagents.addiction_list = list() + cure_all_traumas(TRAUMA_RESILIENCE_MAGIC) + ..() + // heal ears after healing traits, since ears check TRAIT_DEAF trait + // when healing. + restoreEars() + +/mob/living/carbon/can_be_revived() + . = ..() + if(!getorgan(/obj/item/organ/brain) && (!mind || !mind.has_antag_datum(/datum/antagonist/changeling))) + return 0 + +/mob/living/carbon/harvest(mob/living/user) + if(QDELETED(src)) + return + var/organs_amt = 0 + for(var/X in internal_organs) + var/obj/item/organ/O = X + if(prob(50)) + organs_amt++ + O.Remove(src) + O.forceMove(drop_location()) + if(organs_amt) + to_chat(user, "You retrieve some of [src]\'s internal organs!") + +/mob/living/carbon/ExtinguishMob() + for(var/X in get_equipped_items()) + var/obj/item/I = X + I.acid_level = 0 //washes off the acid on our clothes + I.extinguish() //extinguishes our clothes + ..() + +/mob/living/carbon/fakefire(var/fire_icon = "Generic_mob_burning") + var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER) + new_fire_overlay.appearance_flags = RESET_COLOR + overlays_standing[FIRE_LAYER] = new_fire_overlay + apply_overlay(FIRE_LAYER) + +/mob/living/carbon/fakefireextinguish() + remove_overlay(FIRE_LAYER) + + +/mob/living/carbon/proc/devour_mob(mob/living/carbon/C, devour_time = 130) + C.visible_message("[src] is attempting to devour [C]!", \ + "[src] is attempting to devour you!") + if(!do_mob(src, C, devour_time)) + return + if(pulling && pulling == C && grab_state >= GRAB_AGGRESSIVE && a_intent == INTENT_GRAB) + C.visible_message("[src] devours [C]!", \ + "[src] devours you!") + C.forceMove(src) + stomach_contents.Add(C) + log_combat(src, C, "devoured") + +/mob/living/carbon/proc/create_bodyparts() + var/l_arm_index_next = -1 + var/r_arm_index_next = 0 + for(var/X in bodyparts) + var/obj/item/bodypart/O = new X() + O.owner = src + bodyparts.Remove(X) + bodyparts.Add(O) + if(O.body_part == ARM_LEFT) + l_arm_index_next += 2 + O.held_index = l_arm_index_next //1, 3, 5, 7... + hand_bodyparts += O + else if(O.body_part == ARM_RIGHT) + r_arm_index_next += 2 + O.held_index = r_arm_index_next //2, 4, 6, 8... + hand_bodyparts += O + +/mob/living/carbon/do_after_coefficent() + . = ..() + var/datum/component/mood/mood = src.GetComponent(/datum/component/mood) //Currently, only carbons or higher use mood, move this once that changes. + if(mood) + switch(mood.sanity) //Alters do_after delay based on how sane you are + if(SANITY_INSANE to SANITY_DISTURBED) + . *= 1.25 + if(SANITY_NEUTRAL to SANITY_GREAT) + . *= 0.90 + + +/mob/living/carbon/proc/create_internal_organs() + for(var/X in internal_organs) + var/obj/item/organ/I = X + I.Insert(src) + +/mob/living/carbon/proc/update_disabled_bodyparts() + for(var/B in bodyparts) + var/obj/item/bodypart/BP = B + BP.update_disabled() + +/mob/living/carbon/vv_get_dropdown() + . = ..() + . += "---" + .["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]" + .["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]" + .["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]" + .["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]" + .["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]" + .["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]" + .["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]" + +/mob/living/carbon/can_resist() + return bodyparts.len > 2 && ..() + +/mob/living/carbon/proc/hypnosis_vulnerable()//unused atm, but added in case + if(HAS_TRAIT(src, TRAIT_MINDSHIELD)) + return FALSE + if(hallucinating()) + return TRUE + if(IsSleeping()) + return TRUE + if(HAS_TRAIT(src, TRAIT_DUMB)) + return TRUE + var/datum/component/mood/mood = src.GetComponent(/datum/component/mood) + if(mood) + if(mood.sanity < SANITY_UNSTABLE) + return TRUE + +/mob/living/carbon/transfer_ckey(mob/new_mob, send_signal = TRUE) + if(combatmode) + toggle_combat_mode(TRUE, TRUE) + return ..() + +/mob/living/carbon/can_see_reagents() + . = ..() + if(.) //No need to run through all of this if it's already true. + return + if(isclothing(head)) + var/obj/item/clothing/H = head + if(H.clothing_flags & SCAN_REAGENTS) + return TRUE + if(isclothing(wear_mask) && (wear_mask.clothing_flags & SCAN_REAGENTS)) + return TRUE +>>>>>>> parent of aabb39a364... Revert "Merge branch 'master' into Yeehaw" diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 06575aa802..ba40ae8e5f 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -48,41 +48,42 @@ if(affecting && affecting.dismemberable && affecting.get_damage() >= (affecting.max_damage - P.dismemberment)) affecting.dismember(P.damtype) -/mob/living/carbon/proc/can_catch_item(skip_throw_mode_check) - . = FALSE - if(mind) - if(mind.martial_art && mind.martial_art.dodge_chance == 100) - return TRUE - if(!skip_throw_mode_check && !in_throw_mode) +/mob/living/carbon/catch_item(obj/item/I, skip_throw_mode_check = FALSE) + . = ..() + if(!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) && !skip_throw_mode_check && !in_throw_mode) return - if(get_active_held_item()) + if(get_active_held_item() || restrained()) return - if(restrained()) - return - return TRUE - -/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE) - if(!skipcatch) //ugly, but easy - if(can_catch_item()) - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if (mind) - if (mind.martial_art && mind.martial_art.dodge_chance == 100) //autocatch for rising bass - if (get_active_held_item()) - visible_message("[I] falls to the ground as [src] chops it out of the air!") - return 1 - if(!in_throw_mode) - throw_mode_on() - if(isturf(I.loc)) - I.attack_hand(src) - if(get_active_held_item() == I) //if our attack_hand() picks up the item... - visible_message("[src] catches [I]!") //catch that sucker! - throw_mode_off() - return 1 - ..() + I.attack_hand(src) + if(get_active_held_item() == I) //if our attack_hand() picks up the item... + visible_message("[src] catches [I]!") //catch that sucker! + throw_mode_off() + return TRUE +/mob/living/carbon/embed_item(obj/item/I) + throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) + var/obj/item/bodypart/L = pick(bodyparts) + L.embedded_objects |= I + I.add_mob_blood(src)//it embedded itself in you, of course it's bloody! + I.forceMove(src) + L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier) + visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) /mob/living/carbon/attacked_by(obj/item/I, mob/living/user) + //CIT CHANGES START HERE - combatmode and resting checks + var/totitemdamage = I.force + if(iscarbon(user)) + var/mob/living/carbon/tempcarb = user + if(!tempcarb.combatmode) + totitemdamage *= 0.5 + if(user.resting) + totitemdamage *= 0.5 + if(!combatmode) + totitemdamage *= 1.5 + //CIT CHANGES END HERE + if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + return FALSE var/obj/item/bodypart/affecting if(user == src) affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay! @@ -93,17 +94,6 @@ SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) send_item_attack_message(I, user, affecting.name) if(I.force) - //CIT CHANGES START HERE - combatmode and resting checks - var/totitemdamage = I.force - if(iscarbon(user)) - var/mob/living/carbon/tempcarb = user - if(!tempcarb.combatmode) - totitemdamage *= 0.5 - if(user.resting) - totitemdamage *= 0.5 - if(!combatmode) - totitemdamage *= 1.5 - //CIT CHANGES END HERE apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) var/basebloodychance = affecting.brute_dam + totitemdamage @@ -137,7 +127,9 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /mob/living/carbon/attack_hand(mob/living/carbon/human/user) - + . = ..() + if(.) //was the attack blocked? + return for(var/thing in diseases) var/datum/disease/D = thing if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) @@ -152,8 +144,7 @@ if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM) for(var/datum/surgery/S in surgeries) if(S.next_step(user, user.a_intent)) - return 1 - return 0 + return TRUE /mob/living/carbon/attack_paw(mob/living/carbon/monkey/M) @@ -173,7 +164,8 @@ help_shake_act(M) return 0 - if(..()) //successful monkey bite. + . = ..() + if(.) //successful monkey bite. for(var/thing in M.diseases) var/datum/disease/D = thing ForceContractDisease(D) @@ -181,26 +173,27 @@ /mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - if(M.powerlevel > 0) - var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10 - if(prob(stunprob)) - M.powerlevel -= 3 - if(M.powerlevel < 0) - M.powerlevel = 0 + . = ..() + if(!.) + return + if(M.powerlevel > 0) + var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10 + if(prob(stunprob)) + M.powerlevel -= 3 + if(M.powerlevel < 0) + M.powerlevel = 0 - visible_message("The [M.name] has shocked [src]!", \ - "The [M.name] has shocked [src]!") + visible_message("The [M.name] has shocked [src]!", \ + "The [M.name] has shocked [src]!") - do_sparks(5, TRUE, src) - var/power = M.powerlevel + rand(0,3) - Knockdown(power*20) - if(stuttering < power) - stuttering = power - if (prob(stunprob) && M.powerlevel >= 8) - adjustFireLoss(M.powerlevel * rand(6,10)) - updatehealth() - return 1 + do_sparks(5, TRUE, src) + var/power = M.powerlevel + rand(0,3) + Knockdown(power*20) + if(stuttering < power) + stuttering = power + if (prob(stunprob) && M.powerlevel >= 8) + adjustFireLoss(M.powerlevel * rand(6,10)) + updatehealth() /mob/living/carbon/proc/dismembering_strike(mob/living/attacker, dam_zone) if(!attacker.limb_destroyer) @@ -332,12 +325,12 @@ else return - + else if(check_zone(M.zone_selected) == "r_arm" || check_zone(M.zone_selected) == "l_arm") M.visible_message( \ "[M] shakes [src]'s hand.", \ "You shake [src]'s hand.", ) - + else M.visible_message("[M] hugs [src] to make [p_them()] feel better!", \ "You hug [src] to make [p_them()] feel better!") diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 310ab6beeb..3a6f0e6724 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -308,7 +308,7 @@ var/obj/item/organ/vocal_cords/Vc = user.getorganslot(ORGAN_SLOT_VOICE) if(Vc) if(istype(Vc, /obj/item/organ/vocal_cords/velvet)) - if(client?.prefs.lewdchem) + if(client.prefs.cit_toggles & HYPNO) msg += "You feel your chords resonate looking at them.\n" diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 93f33c3a3d..2b1d6afc4f 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -67,66 +67,36 @@ P.setAngle(rand(0, 360))//SHING return FALSE - if(!(P.original == src && P.firer == src)) //can't block or reflect when shooting yourself - if(P.is_reflectable) - if(check_reflect(def_zone)) // Checks if you've passed a reflection% check - visible_message("The [P.name] gets reflected by [src]!", \ - "The [P.name] gets reflected by [src]!") - // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(src) + return ..() - // redirect the projectile - P.original = locate(new_x, new_y, P.z) - P.starting = curloc - P.firer = src - P.yo = new_y - curloc.y - P.xo = new_x - curloc.x - var/new_angle_s = P.Angle + rand(120,240) - while(new_angle_s > 180) // Translate to regular projectile degrees - new_angle_s -= 360 - P.setAngle(new_angle_s) +/mob/living/carbon/human/check_reflect(def_zone) + if(wear_suit?.IsReflect(def_zone)) + return TRUE + return ..() - return -1 // complete projectile permutation - - if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) - P.on_hit(src, 100, def_zone) - return 2 - - return (..(P , def_zone)) - -/mob/living/carbon/human/proc/check_reflect(def_zone) //Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance of the object - if(wear_suit) - if(wear_suit.IsReflect(def_zone) == 1) - return 1 - for(var/obj/item/I in held_items) - if(I.IsReflect(def_zone) == 1) - return 1 - return 0 - -/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) +/mob/living/carbon/human/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) + . = ..() + if(.) + return var/block_chance_modifier = round(damage / -3) - - for(var/obj/item/I in held_items) - if(!istype(I, /obj/item/clothing)) - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example - if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 if(wear_suit) var/final_block_chance = wear_suit.block_chance - (CLAMP((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(w_uniform) var/final_block_chance = w_uniform.block_chance - (CLAMP((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 + return TRUE if(wear_neck) var/final_block_chance = wear_neck.block_chance - (CLAMP((armour_penetration-wear_neck.armour_penetration)/2,0,100)) + block_chance_modifier if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - return 0 + return TRUE + return FALSE + +/mob/living/carbon/human/can_embed(obj/item/I) + if(I.get_sharpness() || is_pointed(I) || is_type_in_typecache(I, GLOB.can_embed_types)) + return TRUE + return FALSE /mob/living/carbon/human/proc/check_block() if(mind) @@ -135,39 +105,7 @@ return FALSE /mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE) - if(dna && dna.species) - var/spec_return = dna.species.spec_hitby(AM, src) - if(spec_return) - return spec_return - var/obj/item/I - var/throwpower = 30 - if(istype(AM, /obj/item)) - I = AM - throwpower = I.throwforce - if(I.thrownby == src) //No throwing stuff at yourself to trigger hit reactions - return ..() - if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK)) - hitpush = FALSE - skipcatch = TRUE - blocked = TRUE - else if(I) - if(I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && !(mind.martial_art && mind.martial_art.dodge_chance == 100)) - if(can_embed(I)) - if(prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE)) - throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) - var/obj/item/bodypart/L = pick(bodyparts) - L.embedded_objects |= I - I.add_mob_blood(src)//it embedded itself in you, of course it's bloody! - I.forceMove(src) - L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier) - visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) - hitpush = FALSE - skipcatch = TRUE //can't catch the now embedded item - if (mind) - if (mind.martial_art && mind.martial_art.dodge_chance == 100) - skipcatch = FALSE - return ..() + return dna?.species?.spec_hitby(AM, src) || ..() /mob/living/carbon/human/grabbedby(mob/living/carbon/user, supress_message = 0) if(user == src && pulling && !pulling.anchored && grab_state >= GRAB_AGGRESSIVE && (HAS_TRAIT(src, TRAIT_FAT)) && ismonkey(pulling)) @@ -201,12 +139,12 @@ return dna.species.spec_attacked_by(I, user, affecting, a_intent, src) -/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) - var/hulk_verb = pick("smash","pummel") - if(check_shields(user, 15, "the [hulk_verb]ing")) + . = ..(user, TRUE) + if(.) return - ..(user, 1) + var/hulk_verb = pick("smash","pummel") playsound(loc, user.dna.species.attack_sound, 25, 1, -1) var/message = "[user] has [hulk_verb]ed [src]!" visible_message("[message]", \ @@ -215,7 +153,8 @@ return 1 /mob/living/carbon/human/attack_hand(mob/user) - if(..()) //to allow surgery to return properly. + . = ..() + if(.) //To allow surgery to return properly. return if(ishuman(user)) var/mob/living/carbon/human/H = user @@ -227,8 +166,7 @@ if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) if(M.a_intent == INTENT_HELP) - ..() //shaking - return 0 + return ..() //shaking if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stunned instead. var/obj/item/I = get_active_held_item() @@ -249,78 +187,69 @@ if(can_inject(M, 1, affecting))//Thick suits can stop monkey bites. if(..()) //successful monkey bite, this handles disease contraction. var/damage = rand(1, 3) - if(check_shields(M, damage, "the [M.name]")) - return 0 - if(stat != DEAD) - apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, "melee")) + apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, "melee")) return 1 /mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M) - if(check_shields(M, 0, "the M.name")) - visible_message("[M] attempted to touch [src]!") - return 0 + . = ..() + if(!.) + return + if(M.a_intent == INTENT_HARM) + if (w_uniform) + w_uniform.add_fingerprint(M) + var/damage = prob(90) ? 20 : 0 + if(!damage) + playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) + visible_message("[M] has lunged at [src]!", \ + "[M] has lunged at [src]!") + return 0 + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + var/armor_block = run_armor_check(affecting, "melee", null, null,10) - if(..()) - if(M.a_intent == INTENT_HARM) - if (w_uniform) - w_uniform.add_fingerprint(M) - var/damage = prob(90) ? 20 : 0 - if(!damage) - playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) - visible_message("[M] has lunged at [src]!", \ - "[M] has lunged at [src]!") - return 0 - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - var/armor_block = run_armor_check(affecting, "melee", null, null,10) + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + visible_message("[M] has slashed at [src]!", \ + "[M] has slashed at [src]!") + log_combat(M, src, "attacked") + if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful + return 1 + apply_damage(damage, BRUTE, affecting, armor_block) - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!") - log_combat(M, src, "attacked") - if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful - return 1 - apply_damage(damage, BRUTE, affecting, armor_block) - - if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead. - var/obj/item/I = get_active_held_item() - if(I && dropItemToGround(I)) - playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) - visible_message("[M] disarmed [src]!", \ - "[M] disarmed [src]!") + if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead. + var/obj/item/I = get_active_held_item() + if(I && dropItemToGround(I)) + playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) + visible_message("[M] disarmed [src]!", \ + "[M] disarmed [src]!") + else + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + if(!lying) //CITADEL EDIT + Knockdown(100, TRUE, FALSE, 30, 25) else - playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) - if(!lying) //CITADEL EDIT - Knockdown(100, TRUE, FALSE, 30, 25) - else - Knockdown(100) - log_combat(M, src, "tackled") - visible_message("[M] has tackled down [src]!", \ - "[M] has tackled down [src]!") - + Knockdown(100) + log_combat(M, src, "tackled") + visible_message("[M] has tackled down [src]!", \ + "[M] has tackled down [src]!") /mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L) - - if(..()) //successful larva bite. - var/damage = rand(1, 3) - if(check_shields(L, damage, "the [L.name]")) - return 0 - if(stat != DEAD) - L.amount_grown = min(L.amount_grown + damage, L.max_grown) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) + . = ..() + if(!.) //unsuccessful larva bite. + return + var/damage = rand(1, 3) + if(stat != DEAD) + L.amount_grown = min(L.amount_grown + damage, L.max_grown) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + var/armor_block = run_armor_check(affecting, "melee") + apply_damage(damage, BRUTE, affecting, armor_block) /mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) . = ..() if(.) var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration)) - return FALSE var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful return TRUE @@ -332,23 +261,22 @@ /mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - var/damage = rand(5, 25) - if(M.is_adult) - damage = rand(10, 35) + . = ..() + if(!.) //unsuccessful slime attack + return + var/damage = rand(5, 25) + if(M.is_adult) + damage = rand(10, 35) - if(check_shields(M, damage, "the [M.name]")) - return 0 + var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) + if(!dam_zone) //Dismemberment successful + return 1 - var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) - if(!dam_zone) //Dismemberment successful - return 1 - - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + var/armor_block = run_armor_check(affecting, "melee") + apply_damage(damage, BRUTE, affecting, armor_block) /mob/living/carbon/human/mech_melee_attack(obj/mecha/M) if(M.occupant.a_intent == INTENT_HARM) @@ -652,7 +580,7 @@ if(mind) if((mind.assigned_role == "Station Engineer") || (mind.assigned_role == "Chief Engineer") ) gain = 100 - if(mind.assigned_role == "Clown") + if(HAS_TRAIT(mind, TRAIT_CLOWN_MENTALITY)) gain = rand(-300, 300) investigate_log("([key_name(src)]) has been consumed by the singularity.", INVESTIGATE_SINGULO) //Oh that's where the clown ended up! gib() diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 8bf6b4ad3a..2e069297b3 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -545,6 +545,20 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) //Underwear, Undershirts & Socks if(!(NO_UNDERWEAR in species_traits)) + + if(H.socks && H.get_num_legs(FALSE) >= 2) + if(H.hidden_socks) + H.socks = "Nude" + else + H.socks = H.saved_socks + var/datum/sprite_accessory/underwear/socks/S = GLOB.socks_list[H.socks] + if(S) + var/digilegs = ((DIGITIGRADE in species_traits) && S.has_digitigrade) ? "_d" : "" + var/mutable_appearance/MA = mutable_appearance(S.icon, "[S.icon_state][digilegs]", -BODY_LAYER) + if(S.has_color) + MA.color = "#[H.socks_color]" + standing += MA + if(H.underwear) if(H.hidden_underwear) H.underwear = "Nude" @@ -552,8 +566,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.underwear = H.saved_underwear var/datum/sprite_accessory/underwear/bottom/B = GLOB.underwear_list[H.underwear] if(B) - var/mutable_appearance/MA = mutable_appearance(B.icon, B.icon_state, -BODY_LAYER) - if(UNDIE_COLORABLE(B)) + var/digilegs = ((DIGITIGRADE in species_traits) && B.has_digitigrade) ? "_d" : "" + var/mutable_appearance/MA = mutable_appearance(B.icon, "[B.icon_state][digilegs]", -BODY_LAYER) + if(B.has_color) MA.color = "#[H.undie_color]" standing += MA @@ -564,28 +579,16 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.undershirt = H.saved_undershirt var/datum/sprite_accessory/underwear/top/T = GLOB.undershirt_list[H.undershirt] if(T) + var/state = "[T.icon_state][((DIGITIGRADE in species_traits) && T.has_digitigrade) ? "_d" : ""]" var/mutable_appearance/MA if(H.dna.species.sexes && H.gender == FEMALE) - MA = wear_female_version(T.icon_state, T.icon, BODY_LAYER) + MA = wear_female_version(state, T.icon, BODY_LAYER) else - MA = mutable_appearance(T.icon, T.icon_state, -BODY_LAYER) - if(UNDIE_COLORABLE(T)) + MA = mutable_appearance(T.icon, state, -BODY_LAYER) + if(T.has_color) MA.color = "#[H.shirt_color]" standing += MA - if(H.socks && H.get_num_legs(FALSE) >= 2) - if(H.hidden_socks) - H.socks = "Nude" - else - H.socks = H.saved_socks - var/datum/sprite_accessory/underwear/socks/S = GLOB.socks_list[H.socks] - if(S) - var/digilegs = (DIGITIGRADE in species_traits) ? "_d" : "" - var/mutable_appearance/MA = mutable_appearance(S.icon, "[S.icon_state][digilegs]", -BODY_LAYER) - if(UNDIE_COLORABLE(S)) - MA.color = "#[H.socks_color]" - standing += MA - if(standing.len) H.overlays_standing[BODY_LAYER] = standing @@ -1589,20 +1592,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) user.adjustStaminaLossBuffered(3) return FALSE else if(aim_for_groin && (target == user || target.lying || same_dir) && (target_on_help || target_restrained || target_aiming_for_groin)) + if(target.client?.prefs.cit_toggles & NO_ASS_SLAP) + to_chat(user,"A force stays your hand, preventing you from slapping \the [target]'s ass!") + return FALSE user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP) user.adjustStaminaLossBuffered(3) - if(HAS_TRAIT(target, TRAIT_ASSBLASTUSA)) - var/hit_zone = (user.held_index_to_dir(user.active_hand_index) == "l" ? "l_":"r_") + "arm" - user.adjustStaminaLoss(20, affected_zone = hit_zone) - user.visible_message(\ - "\The [user] slaps \the [target]'s ass, but their hand bounces off like they hit metal!",\ - "You slap [user == target ? "your" : "\the [target]'s"] ass, but feel an intense amount of pain as you realise their buns are harder than steel!",\ - "You hear a slap." - ) - playsound(target.loc, 'sound/weapons/tap.ogg', 50, 1, -1) - user.emote("scream") - return FALSE - playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) user.visible_message(\ "\The [user] slaps \the [target]'s ass!",\ @@ -1691,11 +1685,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) attacker_style = M.mind.martial_art if(attacker_style?.pacifism_check && HAS_TRAIT(M, TRAIT_PACIFISM)) // most martial arts are quite harmful, alas. attacker_style = null - if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) - log_combat(M, H, "attempted to touch") - H.visible_message("[M] attempted to touch [H]!") - return 0 - SEND_SIGNAL(M, COMSIG_MOB_ATTACK_HAND, M, H, attacker_style) switch(M.a_intent) if("help") help(M, H, attacker_style) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 3247c96632..52ae32b23c 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -51,11 +51,7 @@ . = ..() to_chat(C, "[info_text]") - C.real_name = "[pick(GLOB.nightmare_names)]" - C.name = C.real_name - if(C.mind) - C.mind.name = C.real_name - C.dna.real_name = C.real_name + C.fully_replace_character_name("[pick(GLOB.nightmare_names)]") /datum/species/shadow/nightmare/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) var/turf/T = H.loc @@ -127,8 +123,8 @@ /obj/item/organ/heart/nightmare/Remove(mob/living/carbon/M, special = 0) respawn_progress = 0 if(blade && special != HEART_SPECIAL_SHADOWIFY) - QDEL_NULL(blade) M.visible_message("\The [blade] disintegrates!") + QDEL_NULL(blade) ..() /obj/item/organ/heart/nightmare/Stop() @@ -183,15 +179,21 @@ . = ..() if(!proximity) return - if(isopenturf(AM)) //So you can actually melee with it - return - if(isliving(AM)) + if(isopenturf(AM)) + var/turf/open/T = AM + if(T.light_range && !isspaceturf(T)) //no fairy grass or light tile can escape the fury of the darkness. + to_chat(user, "You scrape away [T] with your [name] and snuff out its lights.") + T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) + else if(isliving(AM)) var/mob/living/L = AM if(iscyborg(AM)) var/mob/living/silicon/robot/borg = AM - if(!borg.lamp_cooldown) + if(borg.lamp_intensity) borg.update_headlamp(TRUE, INFINITY) to_chat(borg, "Your headlamp is fried! You'll need a human to help replace it.") + for(var/obj/item/assembly/flash/cyborg/F in borg.held_items) + if(!F.crit_fail) + F.burn_out() else for(var/obj/item/O in AM) if(O.light_range && O.light_power) diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm index 92ad87f6b5..56ef4fe24a 100644 --- a/code/modules/mob/living/carbon/monkey/combat.dm +++ b/code/modules/mob/living/carbon/monkey/combat.dm @@ -369,6 +369,23 @@ retaliate(L) return ..() +/mob/living/carbon/monkey/attack_alien(mob/living/carbon/alien/humanoid/M) + if(M.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) + retaliate(M) + else if(M.a_intent == INTENT_DISARM && prob(MONKEY_RETALIATE_DISARM_PROB)) + retaliate(M) + return ..() + +/mob/living/carbon/monkey/attack_larva(mob/living/carbon/alien/larva/L) + if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) + retaliate(L) + return ..() + +/mob/living/carbon/monkey/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) + retaliate(user) + return ..() + /mob/living/carbon/monkey/attack_paw(mob/living/L) if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB)) retaliate(L) diff --git a/code/modules/mob/living/carbon/monkey/monkey_defense.dm b/code/modules/mob/living/carbon/monkey/monkey_defense.dm index df90dd56fd..32e3d21ee2 100644 --- a/code/modules/mob/living/carbon/monkey/monkey_defense.dm +++ b/code/modules/mob/living/carbon/monkey/monkey_defense.dm @@ -6,37 +6,55 @@ ..() /mob/living/carbon/monkey/attack_paw(mob/living/M) - if(..()) //successful monkey bite. - var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - if(M.limb_destroyer) - dismembering_strike(M, affecting.body_zone) - if(stat != DEAD) - var/dmg = rand(1, 5) - apply_damage(dmg, BRUTE, affecting) + . = ..() + if(!.) //unsuccessful monkey bite. + return + var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + if(M.limb_destroyer) + dismembering_strike(M, affecting.body_zone) + var/dmg = rand(1, 5) + apply_damage(dmg, BRUTE, affecting) /mob/living/carbon/monkey/attack_larva(mob/living/carbon/alien/larva/L) - if(..()) //successful larva bite. - var/damage = rand(1, 3) - if(stat != DEAD) - L.amount_grown = min(L.amount_grown + damage, L.max_grown) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - apply_damage(damage, BRUTE, affecting) + . = ..() + if(!.) //unsuccessful larva bite + return + var/damage = rand(1, 3) + if(stat != DEAD) + L.amount_grown = min(L.amount_grown + damage, L.max_grown) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + apply_damage(damage, BRUTE, affecting) + +/mob/living/carbon/monkey/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + . = ..(user, TRUE) + if(.) + return + var/hulk_verb = pick("smash","pummel") + playsound(loc, user.dna.species.attack_sound, 25, 1, -1) + var/message = "[user] has [hulk_verb]ed [src]!" + visible_message("[message]", \ + "[message]") + adjustBruteLoss(15) + return TRUE /mob/living/carbon/monkey/attack_hand(mob/living/carbon/human/M) - if(..()) //To allow surgery to return properly. + . = ..() + if(.) //To allow surgery to return properly. return - switch(M.a_intent) - if("help") + if(INTENT_HELP) help_shake_act(M) - if("grab") + if(INTENT_GRAB) grabbedby(M) - if("harm") + if(INTENT_HARM) + if(HAS_TRAIT(M, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt [src]!") + return M.do_attack_animation(src, ATTACK_EFFECT_PUNCH) if (prob(75)) visible_message("[M] has punched [name]!", \ @@ -60,7 +78,7 @@ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) visible_message("[M] has attempted to punch [name]!", \ "[M] has attempted to punch [name]!", null, COMBAT_MESSAGE_RANGE) - if("disarm") + if(INTENT_DISARM) if(!IsUnconscious()) M.do_attack_animation(src, ATTACK_EFFECT_DISARM) if (prob(25)) @@ -74,50 +92,51 @@ visible_message("[M] has disarmed [src]!", "[M] has disarmed [src]!", null, COMBAT_MESSAGE_RANGE) /mob/living/carbon/monkey/attack_alien(mob/living/carbon/alien/humanoid/M) - if(..()) //if harm or disarm intent. - if (M.a_intent == INTENT_HARM) - if ((prob(95) && health > 0)) - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - var/damage = rand(15, 30) - if (damage >= 25) - damage = rand(20, 40) - if(AmountUnconscious() < 300) - Unconscious(rand(200, 300)) - visible_message("[M] has wounded [name]!", \ - "[M] has wounded [name]!", null, COMBAT_MESSAGE_RANGE) - else - visible_message("[M] has slashed [name]!", \ - "[M] has slashed [name]!", null, COMBAT_MESSAGE_RANGE) - - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - log_combat(M, src, "attacked") - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful - return 1 - apply_damage(damage, BRUTE, affecting) - + . = ..() + if(!.) // the attack was blocked or was help/grab intent + return + if (M.a_intent == INTENT_HARM) + if ((prob(95) && health > 0)) + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + var/damage = rand(15, 30) + if (damage >= 25) + damage = rand(20, 40) + if(AmountUnconscious() < 300) + Unconscious(rand(200, 300)) + visible_message("[M] has wounded [name]!", \ + "[M] has wounded [name]!", null, COMBAT_MESSAGE_RANGE) else - playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1) - visible_message("[M] has attempted to lunge at [name]!", \ - "[M] has attempted to lunge at [name]!", null, COMBAT_MESSAGE_RANGE) + visible_message("[M] has slashed [name]!", \ + "[M] has slashed [name]!", null, COMBAT_MESSAGE_RANGE) - if (M.a_intent == INTENT_DISARM) - var/obj/item/I = null - playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) - if(prob(95)) - Knockdown(20) - visible_message("[M] has tackled down [name]!", \ - "[M] has tackled down [name]!", null, COMBAT_MESSAGE_RANGE) + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) + log_combat(M, src, "attacked") + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful + return 1 + apply_damage(damage, BRUTE, affecting) + + else + playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1) + visible_message("[M] has attempted to lunge at [name]!", \ + "[M] has attempted to lunge at [name]!", null, COMBAT_MESSAGE_RANGE) + + else + var/obj/item/I = null + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + if(prob(95)) + Knockdown(20) + visible_message("[M] has tackled down [name]!", \ + "[M] has tackled down [name]!", null, COMBAT_MESSAGE_RANGE) + else + I = get_active_held_item() + if(dropItemToGround(I)) + visible_message("[M] has disarmed [name]!", "[M] has disarmed [name]!", null, COMBAT_MESSAGE_RANGE) else - I = get_active_held_item() - if(dropItemToGround(I)) - visible_message("[M] has disarmed [name]!", "[M] has disarmed [name]!", null, COMBAT_MESSAGE_RANGE) - else - I = null - log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") - updatehealth() - + I = null + log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") + updatehealth() /mob/living/carbon/monkey/attack_animal(mob/living/simple_animal/M) . = ..() @@ -132,17 +151,19 @@ apply_damage(damage, M.melee_damage_type, affecting) /mob/living/carbon/monkey/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - var/damage = rand(5, 35) - if(M.is_adult) - damage = rand(20, 40) - var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) - if(!dam_zone) //Dismemberment successful - return 1 - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) - if(!affecting) - affecting = get_bodypart(BODY_ZONE_CHEST) - apply_damage(damage, BRUTE, affecting) + . = ..() + if(!.) //unsuccessful slime attack + return + var/damage = rand(5, 35) + if(M.is_adult) + damage = rand(20, 40) + var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) + if(!dam_zone) //Dismemberment successful + return 1 + var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone)) + if(!affecting) + affecting = get_bodypart(BODY_ZONE_CHEST) + apply_damage(damage, BRUTE, affecting) /mob/living/carbon/monkey/acid_act(acidpwr, acid_volume, bodyzone_hit) . = 1 diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 8345ef916d..1c6546fd83 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -91,7 +91,9 @@ if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf) && !(signal & COMPONENT_BLOCK_DEATH_BROADCAST)) var/rendered = "[mind.name] has died at [get_area_name(T)]." deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE) - + if (client && client.prefs && client.prefs.auto_ooc) + if (!(client.prefs.chat_toggles & CHAT_OOC)) + client.prefs.chat_toggles ^= CHAT_OOC if (client) client.move_delay = initial(client.move_delay) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 722c984309..3c5c5e3518 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -36,7 +36,50 @@ /mob/living/proc/on_hit(obj/item/projectile/P) return +/mob/living/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) + var/block_chance_modifier = round(damage / -3) + for(var/obj/item/I in held_items) + if(!istype(I, /obj/item/clothing)) + var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) + return TRUE + return FALSE + +/mob/living/proc/check_reflect(def_zone) //Reflection checks for anything in your hands, based on the reflection chance of the object(s) + for(var/obj/item/I in held_items) + if(I.IsReflect(def_zone)) + return TRUE + return FALSE + +/mob/living/proc/reflect_bullet_check(obj/item/projectile/P, def_zone) + if(P.is_reflectable && check_reflect(def_zone)) // Checks if you've passed a reflection% check + visible_message("The [P.name] gets reflected by [src]!", \ + "The [P.name] gets reflected by [src]!") + // Find a turf near or on the original location to bounce to + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + // redirect the projectile + P.original = locate(new_x, new_y, P.z) + P.starting = curloc + P.firer = src + P.yo = new_y - curloc.y + P.xo = new_x - curloc.x + var/new_angle_s = P.Angle + rand(120,240) + while(new_angle_s > 180) // Translate to regular projectile degrees + new_angle_s -= 360 + P.setAngle(new_angle_s) + return TRUE + return FALSE + /mob/living/bullet_act(obj/item/projectile/P, def_zone) + if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself + if(reflect_bullet_check(P, def_zone)) + return -1 // complete projectile permutation + if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) + P.on_hit(src, 100, def_zone) + return 2 var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, armor) @@ -55,9 +98,32 @@ else return 0 +/mob/living/proc/catch_item(obj/item/I, skip_throw_mode_check = FALSE) + return FALSE + +/mob/living/proc/embed_item(obj/item/I) + return + +/mob/living/proc/can_embed(obj/item/I) + return FALSE + /mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE) - if(istype(AM, /obj/item)) - var/obj/item/I = AM + var/obj/item/I + var/throwpower = 30 + if(isitem(AM)) + I = AM + throwpower = I.throwforce + if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK)) + hitpush = FALSE + skipcatch = TRUE + blocked = TRUE + else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) + embed_item(I) + hitpush = FALSE + skipcatch = TRUE //can't catch the now embedded item + if(I) + if(!skipcatch && isturf(I.loc) && catch_item(I)) + return TRUE var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest var/dtype = BRUTE var/volume = I.get_volume_by_throwforce_and_or_w_class() @@ -214,6 +280,24 @@ Move(user.loc) return 1 +/mob/living/attack_hand(mob/user) + ..() //Ignoring parent return value here. + SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user) + if((user != src) && user.a_intent != INTENT_HELP && check_shields(user, 0, user.name, attack_type = UNARMED_ATTACK)) + log_combat(user, src, "attempted to touch") + visible_message("[user] attempted to touch [src]!") + return TRUE + +/mob/living/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "You don't want to hurt [src]!") + return TRUE + var/hulk_verb = pick("smash","pummel") + if(user != src && check_shields(user, 15, "the [hulk_verb]ing")) + return TRUE + ..() + return FALSE /mob/living/attack_slime(mob/living/simple_animal/slime/M) if(!SSticker.HasRoundStarted()) @@ -229,6 +313,12 @@ to_chat(M, "You don't want to hurt anyone!") return FALSE + var/damage = rand(5, 35) + if(M.is_adult) + damage = rand(20, 40) + if(check_shields(M, damage, "the [M.name]")) + return FALSE + if (stat != DEAD) log_combat(M, src, "attacked") M.do_attack_animation(src) @@ -245,7 +335,8 @@ if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - + if(check_shields(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", MELEE_ATTACK, M.armour_penetration)) + return FALSE if(M.attack_sound) playsound(loc, M.attack_sound, 50, 1, 1) M.do_attack_animation(src) @@ -256,10 +347,6 @@ /mob/living/attack_paw(mob/living/carbon/monkey/M) - if(isturf(loc) && istype(loc.loc, /area/start)) - to_chat(M, "No attacking people at spawn, you jackass.") - return FALSE - if (M.a_intent == INTENT_HARM) if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") @@ -268,6 +355,8 @@ if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH)) to_chat(M, "You can't bite with your mouth covered!") return FALSE + if(check_shields(M, 0, "the [M.name]")) + return FALSE M.do_attack_animation(src, ATTACK_EFFECT_BITE) if (prob(75)) log_combat(M, src, "attacked") @@ -282,15 +371,16 @@ /mob/living/attack_larva(mob/living/carbon/alien/larva/L) switch(L.a_intent) - if("help") + if(INTENT_HELP) visible_message("[L.name] rubs its head against [src].") return FALSE else if(HAS_TRAIT(L, TRAIT_PACIFISM)) to_chat(L, "You don't want to hurt anyone!") - return - + return FALSE + if(L != src && check_shields(L, rand(1, 3), "the [L.name]")) + return FALSE L.do_attack_animation(src) if(prob(90)) log_combat(L, src, "attacked") @@ -301,24 +391,29 @@ else visible_message("[L.name] has attempted to bite [src]!", \ "[L.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) - return FALSE /mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) + if((M != src) && M.a_intent != INTENT_HELP && check_shields(M, 0, "the [M.name]")) + visible_message("[M] attempted to touch [src]!") + return FALSE switch(M.a_intent) - if ("help") - visible_message("[M] caresses [src] with its scythe like arm.") + if (INTENT_HELP) + if(!isalien(src)) //I know it's ugly, but the alien vs alien attack_alien behaviour is a bit different. + visible_message("[M] caresses [src] with its scythe like arm.") return FALSE - if ("grab") + if (INTENT_GRAB) grabbedby(M) return FALSE - if("harm") + if(INTENT_HARM) if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - M.do_attack_animation(src) + if(!isalien(src)) + M.do_attack_animation(src) return TRUE - if("disarm") - M.do_attack_animation(src, ATTACK_EFFECT_DISARM) + if(INTENT_DISARM) + if(!isalien(src)) + M.do_attack_animation(src, ATTACK_EFFECT_DISARM) return TRUE /mob/living/ex_act(severity, target, origin) diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index 7c59c2b791..97d26f672a 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -1,15 +1,9 @@ - /mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone) + . = ..() + if(!.) + return FALSE if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. spark_system.start() - return ..() - - -/mob/living/silicon/ai/attack_alien(mob/living/carbon/alien/humanoid/M) - if(!SSticker.HasRoundStarted()) - to_chat(M, "You cannot attack people before the game has started.") - return - ..() /mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user) return //immune to slimes diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm index dfc718f1ab..5ac841326b 100644 --- a/code/modules/mob/living/silicon/pai/pai_defense.dm +++ b/code/modules/mob/living/silicon/pai/pai_defense.dm @@ -26,13 +26,14 @@ fold_in(force = 1) Knockdown(200) +//ATTACK HAND IGNORING PARENT RETURN VALUE /mob/living/silicon/pai/attack_hand(mob/living/carbon/human/user) switch(user.a_intent) - if("help") + if(INTENT_HELP) visible_message("[user] gently pats [src] on the head, eliciting an off-putting buzzing from its holographic field.") - if("disarm") + if(INTENT_DISARM) visible_message("[user] boops [src] on the head!") - if("harm") + if(INTENT_HARM) user.do_attack_animation(src) if (user.name == master) visible_message("Responding to its master's touch, [src] disengages its holochassis emitter, rapidly losing coherence.") @@ -41,14 +42,19 @@ if(user.put_in_hands(card)) user.visible_message("[user] promptly scoops up [user.p_their()] pAI's card.") else + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "You don't want to hurt [src]!") + return visible_message("[user] stomps on [src]!.") take_holo_damage(2) + else + grabbedby(user) -/mob/living/silicon/pai/bullet_act(obj/item/projectile/Proj) - if(Proj.stun) +/mob/living/silicon/pai/bullet_act(obj/item/projectile/P, def_zone) + if(P.stun) fold_in(force = TRUE) - src.visible_message("The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!") - . = ..(Proj) + visible_message("The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!") + . = ..() /mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping to_chat(src, "Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.") diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 0f09b6f62a..7e06c66eff 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -13,7 +13,19 @@ spark_system.start() return ..() +/mob/living/silicon/robot/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + . = ..() + if(.) + spark_system.start() + spawn(0) + step_away(src,user,15) + sleep(3) + step_away(src,user,15) + /mob/living/silicon/robot/attack_alien(mob/living/carbon/alien/humanoid/M) + . = ..() + if(!.) // the attack was blocked or was help/grab intent + return if (M.a_intent == INTENT_DISARM) if(!(lying)) M.do_attack_animation(src, ATTACK_EFFECT_DISARM) @@ -30,24 +42,19 @@ visible_message("[M] has forced back [src]!", \ "[M] has forced back [src]!", null, COMBAT_MESSAGE_RANGE) playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1) - else - ..() - return /mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime shock - flash_act() - var/stunprob = M.powerlevel * 7 + 10 - if(prob(stunprob) && M.powerlevel >= 8) - adjustBruteLoss(M.powerlevel * rand(6,10)) - - var/damage = rand(1, 3) - + . = ..() + if(!.) //unsuccessful slime shock + return + var/stunprob = M.powerlevel * 7 + 10 + var/damage = M.powerlevel * rand(6,10) + if(prob(stunprob) && M.powerlevel >= 8) + flash_act(affect_silicon = TRUE) //my borg eyes! if(M.is_adult) - damage = rand(20, 40) + damage += rand(10, 20) else - damage = rand(5, 35) - damage = round(damage / 2) // borgs receive half damage + damage += rand(2, 17) adjustBruteLoss(damage) updatehealth() @@ -56,23 +63,17 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /mob/living/silicon/robot/attack_hand(mob/living/carbon/human/user) add_fingerprint(user) - if(opened && !wiresexposed && !issilicon(user)) - if(cell) - cell.update_icon() - cell.add_fingerprint(user) - user.put_in_active_hand(cell) - to_chat(user, "You remove \the [cell].") - cell = null - update_icons() - diag_hud_set_borgcell() + if(opened && !wiresexposed && cell && !issilicon(user)) + cell.update_icon() + cell.add_fingerprint(user) + user.put_in_active_hand(cell) + to_chat(user, "You remove \the [cell].") + cell = null + update_icons() + diag_hud_set_borgcell() if(!opened) - if(..()) // hulk attack - spark_system.start() - spawn(0) - step_away(src,user,15) - sleep(3) - step_away(src,user,15) + return ..() /mob/living/silicon/robot/fire_act() if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them @@ -182,9 +183,9 @@ if (stat != DEAD) adjustBruteLoss(30) -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) +/mob/living/silicon/robot/bullet_act(obj/item/projectile/P, def_zone) + ..() updatehealth() - if(prob(75) && Proj.damage > 0) + if(prob(75) && P.damage > 0) spark_system.start() return 2 diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index 073a2eec2b..ca8ad25713 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -6,7 +6,10 @@ return 2 /mob/living/silicon/attack_alien(mob/living/carbon/alien/humanoid/M) - if(..()) //if harm or disarm intent + . = ..() + if(!.) // the attack was blocked or was help/grab intent + return + if(M.a_intent == INTENT_HARM) var/damage = 20 if (prob(90)) log_combat(M, src, "attacked") @@ -49,34 +52,33 @@ /mob/living/silicon/attack_paw(mob/living/user) return attack_hand(user) -/mob/living/silicon/attack_larva(mob/living/carbon/alien/larva/L) - if(L.a_intent == INTENT_HELP) - visible_message("[L.name] rubs its head against [src].") - -/mob/living/silicon/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/mob/living/silicon/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) - ..(user, 1) + . = ..(user, TRUE) + if(.) + return adjustBruteLoss(rand(10, 15)) playsound(loc, "punch", 25, 1, -1) visible_message("[user] has punched [src]!", \ "[user] has punched [src]!") - return 1 - return 0 + return TRUE + return FALSE -//ATTACK HAND IGNORING PARENT RETURN VALUE /mob/living/silicon/attack_hand(mob/living/carbon/human/M) + . = ..() + if(.) //the attack was blocked + return switch(M.a_intent) - if ("help") + if (INTENT_HELP) M.visible_message("[M] pets [src].", \ "You pet [src].") - if("grab") + if(INTENT_GRAB) grabbedby(M) else M.do_attack_animation(src, ATTACK_EFFECT_PUNCH) playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) visible_message("[M] punches [src], but doesn't leave a dent.", \ "[M] punches [src], but doesn't leave a dent.", null, COMBAT_MESSAGE_RANGE) - return 0 /mob/living/silicon/attack_drone(mob/living/simple_animal/drone/M) if(M.a_intent == INTENT_HARM) @@ -108,19 +110,25 @@ M.visible_message("[M] is thrown off of [src]!") flash_act(affect_silicon = 1) -/mob/living/silicon/bullet_act(obj/item/projectile/Proj) - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - adjustBruteLoss(Proj.damage) - if(prob(Proj.damage*1.5)) +/mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone) + if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself + if(reflect_bullet_check(P, def_zone)) + return -1 // complete projectile permutation + if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) + P.on_hit(src, 100, def_zone) + return 2 + if((P.damage_type == BRUTE || P.damage_type == BURN)) + adjustBruteLoss(P.damage) + if(prob(P.damage*1.5)) for(var/mob/living/M in buckled_mobs) M.visible_message("[M] is knocked off of [src]!") unbuckle_mob(M) M.Knockdown(40) - if(Proj.stun || Proj.knockdown) + if(P.stun || P.knockdown) for(var/mob/living/M in buckled_mobs) unbuckle_mob(M) - M.visible_message("[M] is knocked off of [src] by the [Proj]!") - Proj.on_hit(src) + M.visible_message("[M] is knocked off of [src] by the [P]!") + P.on_hit(src) return 2 /mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 793df63c87..0dfa126e79 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -1,20 +1,22 @@ /mob/living/simple_animal/attack_hand(mob/living/carbon/human/M) - ..() + . = ..() + if(.) //the attack was blocked + return switch(M.a_intent) - if("help") + if(INTENT_HELP) if (health > 0) visible_message("[M] [response_help] [src].") playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if("grab") + if(INTENT_GRAB) if(grab_state >= GRAB_AGGRESSIVE && isliving(pulling)) vore_attack(M, pulling) else grabbedby(M) - if("harm", "disarm") + if(INTENT_HARM, INTENT_DISARM) if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt [src]!") return @@ -27,12 +29,11 @@ updatehealth() return TRUE -/mob/living/simple_animal/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) +/mob/living/simple_animal/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "You don't want to hurt [src]!") - return FALSE - ..(user, 1) + . = ..(user, TRUE) + if(.) + return playsound(loc, "punch", 25, 1, -1) visible_message("[user] has punched [src]!", \ "[user] has punched [src]!", null, COMBAT_MESSAGE_RANGE) @@ -40,32 +41,32 @@ return TRUE /mob/living/simple_animal/attack_paw(mob/living/carbon/monkey/M) - if(..()) //successful monkey bite. - if(stat != DEAD) - var/damage = rand(1, 3) - attack_threshold_check(damage) - return 1 + . = ..() + if(.) //successful larva bite + var/damage = rand(1, 3) + attack_threshold_check(damage) + return 1 if (M.a_intent == INTENT_HELP) if (health > 0) visible_message("[M.name] [response_help] [src].") playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - /mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M) - if(..()) //if harm or disarm intent. - if(M.a_intent == INTENT_DISARM) - playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) - visible_message("[M] [response_disarm] [name]!", \ - "[M] [response_disarm] [name]!", null, COMBAT_MESSAGE_RANGE) - log_combat(M, src, "disarmed") - else - var/damage = rand(15, 30) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!", null, COMBAT_MESSAGE_RANGE) - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - attack_threshold_check(damage) - log_combat(M, src, "attacked") - return 1 + . = ..() + if(!.) // the attack was blocked or was help/grab intent + return + if(M.a_intent == INTENT_DISARM) + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + visible_message("[M] [response_disarm] [name]!", \ + "[M] [response_disarm] [name]!", null, COMBAT_MESSAGE_RANGE) + log_combat(M, src, "disarmed") + else + var/damage = rand(15, 30) + visible_message("[M] has slashed at [src]!", \ + "[M] has slashed at [src]!", null, COMBAT_MESSAGE_RANGE) + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + attack_threshold_check(damage) + log_combat(M, src, "attacked") /mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L) . = ..() @@ -82,7 +83,8 @@ return attack_threshold_check(damage, M.melee_damage_type) /mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack + . = ..() + if(.) //successful slime shock var/damage = rand(15, 25) if(M.is_adult) damage = rand(20, 35) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 2de4ca39fd..109c7b4636 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -113,7 +113,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, mode = BOT_HUNT /mob/living/simple_animal/bot/honkbot/attack_hand(mob/living/carbon/human/H) - if(H.a_intent == "harm") + if(H.a_intent == INTENT_HARM) retaliate(H) addtimer(CALLBACK(src, .proc/react_buzz), 5) return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 02e171e4c4..bda309f7c7 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -232,9 +232,9 @@ /mob/living/simple_animal/pet/cat/attack_hand(mob/living/carbon/human/M) . = ..() switch(M.a_intent) - if("help") + if(INTENT_HELP) wuv(1, M) - if("harm") + if(INTENT_HARM) wuv(-1, M) /mob/living/simple_animal/pet/cat/proc/wuv(change, mob/M) @@ -290,7 +290,9 @@ D.decorate_donut() /mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) - ..() + . = ..() + if(.) //the attack was blocked + return if(L.a_intent == INTENT_HARM && L.reagents && !stat) L.reagents.add_reagent("nutriment", 0.4) L.reagents.add_reagent("vitamin", 0.4) diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 0d933d171b..986e5c9b4d 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -643,9 +643,9 @@ /mob/living/simple_animal/pet/dog/attack_hand(mob/living/carbon/human/M) . = ..() switch(M.a_intent) - if("help") + if(INTENT_HELP) wuv(1,M) - if("harm") + if(INTENT_HARM) wuv(-1,M) /mob/living/simple_animal/pet/dog/proc/wuv(change, mob/M) diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm index e40eb585e8..c9207fcf89 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm @@ -31,9 +31,9 @@ //picky up the drone c: /mob/living/simple_animal/drone/attack_hand(mob/user) - ..() - if(user.a_intent == INTENT_HELP) - mob_try_pickup(user) + if(user.a_intent != INTENT_HELP) + return ..() // TODO: convert picking up mobs into an element or component. + mob_try_pickup(user) /mob/living/simple_animal/drone/proc/try_reactivate(mob/living/user) var/mob/dead/observer/G = get_ghost() diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm index 49514d0d12..914d38022f 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/charger.dm @@ -54,10 +54,8 @@ var/blocked = FALSE if(hasmatchingsummoner(A)) //if the summoner matches don't hurt them blocked = TRUE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK)) - blocked = TRUE + if(L.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK)) + blocked = TRUE if(!blocked) L.drop_all_held_items() L.visible_message("[src] slams into [L]!", "[src] slams into you!") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 397d40925b..7cfae9310a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -594,12 +594,15 @@ Difficulty: Normal var/list/hit_things = list() //we hit these already, ignore them var/friendly_fire_check = FALSE var/bursting = FALSE //if we're bursting and need to hit anyone crossing us + var/list/nohurt -/obj/effect/temp_visual/hierophant/blast/Initialize(mapload, new_caster, friendly_fire, list/only_hit_once) +/obj/effect/temp_visual/hierophant/blast/Initialize(mapload, new_caster, friendly_fire, list/only_hit_once, list/donthurt = null) . = ..() if(only_hit_once) hit_things = only_hit_once friendly_fire_check = friendly_fire + if(donthurt) + hit_things += donthurt if(new_caster) hit_things += new_caster if(ismineralturf(loc)) //drill mineral turfs diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 8c2fbef15e..c6386540f7 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -25,7 +25,7 @@ mob_size = MOB_SIZE_LARGE layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway - flags_1 = PREVENT_CONTENTS_EXPLOSION_1 + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1 var/list/crusher_loot var/medal_type var/score_type = BOSS_SCORE diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 04a1b4a468..0a8320788e 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -24,7 +24,8 @@ var/list/attack_action_types = list() var/can_talk = FALSE var/obj/loot_drop = null - + var/owner + //Gives player-controlled variants the ability to swap attacks /mob/living/simple_animal/hostile/asteroid/elite/Initialize(mapload) . = ..() @@ -53,14 +54,14 @@ if(ismineralturf(target)) var/turf/closed/mineral/M = target M.gets_drilled() - + //Elites can't talk (normally)! /mob/living/simple_animal/hostile/asteroid/elite/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(can_talk) . = ..() return TRUE return FALSE - + /*Basic setup for elite attacks, based on Whoneedspace's megafauna attack setup. While using this makes the system rely on OnFire, it still gives options for timers not tied to OnFire, and it makes using attacks consistent accross the board for player-controlled elites.*/ @@ -82,11 +83,11 @@ While using this makes the system rely on OnFire, it still gives options for tim /datum/action/innate/elite_attack/Activate() M.chosen_attack = chosen_attack_num to_chat(M, chosen_message) - + /mob/living/simple_animal/hostile/asteroid/elite/updatehealth() . = ..() update_health_hud() - + /mob/living/simple_animal/hostile/asteroid/elite/update_health_hud() if(hud_used) var/severity = 0 @@ -144,7 +145,7 @@ While using this makes the system rely on OnFire, it still gives options for tim gpstag = "Menacing Signal" desc = "You're not quite sure how a signal can be menacing." invisibility = 100 - + /obj/structure/elite_tumor/attack_hand(mob/user) . = ..() if(ishuman(user)) @@ -179,7 +180,7 @@ While using this makes the system rely on OnFire, it still gives options for tim activity = TUMOR_INACTIVE activator = null - + obj/structure/elite_tumor/proc/spawn_elite(var/mob/dead/observer/elitemind) var/selectedspawn = pick(potentialspawns) mychild = new selectedspawn(loc) @@ -199,18 +200,18 @@ obj/structure/elite_tumor/proc/return_elite() if(boosted) mychild.maxHealth = mychild.maxHealth * 2 mychild.health = mychild.maxHealth - + /obj/structure/elite_tumor/Initialize(mapload) . = ..() internal = new/obj/item/gps/internal/elite(src) START_PROCESSING(SSobj, src) - + /obj/structure/elite_tumor/Destroy() STOP_PROCESSING(SSobj, src) mychild = null activator = null return ..() - + /obj/structure/elite_tumor/process() if(isturf(loc)) for(var/mob/living/simple_animal/hostile/asteroid/elite/elitehere in loc) @@ -218,7 +219,7 @@ obj/structure/elite_tumor/proc/return_elite() mychild.adjustHealth(-mychild.maxHealth*0.05) var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(mychild)) H.color = "#FF0000" - + /obj/structure/elite_tumor/attackby(obj/item/I, mob/user, params) . = ..() if(istype(I, /obj/item/organ/regenerative_core) && activity == TUMOR_INACTIVE && !boosted) @@ -232,7 +233,7 @@ obj/structure/elite_tumor/proc/return_elite() desc = "[desc] This one seems to glow with a strong intensity." qdel(core) return TRUE - + /obj/structure/elite_tumor/proc/arena_checks() if(activity != TUMOR_ACTIVE || QDELETED(src)) return @@ -240,13 +241,13 @@ obj/structure/elite_tumor/proc/return_elite() INVOKE_ASYNC(src, .proc/arena_trap) //Gets another arena trap queued up for when this one runs out. INVOKE_ASYNC(src, .proc/border_check) //Checks to see if our fighters got out of the arena somehow. addtimer(CALLBACK(src, .proc/arena_checks), 50) - + /obj/structure/elite_tumor/proc/fighters_check() if(activator != null && activator.stat == DEAD || activity == TUMOR_ACTIVE && QDELETED(activator)) onEliteWon() if(mychild != null && mychild.stat == DEAD || activity == TUMOR_ACTIVE && QDELETED(mychild)) onEliteLoss() - + /obj/structure/elite_tumor/proc/arena_trap() var/turf/T = get_turf(src) if(loc == null) @@ -257,7 +258,7 @@ obj/structure/elite_tumor/proc/return_elite() newwall = new /obj/effect/temp_visual/elite_tumor_wall(t, src) newwall.activator = src.activator newwall.ourelite = src.mychild - + /obj/structure/elite_tumor/proc/border_check() if(activator != null && get_dist(src, activator) >= 12) activator.forceMove(loc) @@ -267,7 +268,7 @@ obj/structure/elite_tumor/proc/return_elite() mychild.forceMove(loc) visible_message("[mychild] suddenly reappears above [src]!") playsound(loc,'sound/effects/phasein.ogg', 200, 0, 50, TRUE, TRUE) - + obj/structure/elite_tumor/proc/onEliteLoss() playsound(loc,'sound/effects/tendril_destroyed.ogg', 200, 0, 50, TRUE, TRUE) visible_message("[src] begins to convulse violently before beginning to dissipate.") @@ -286,7 +287,7 @@ obj/structure/elite_tumor/proc/onEliteLoss() mychild = null activator = null qdel(src) - + obj/structure/elite_tumor/proc/onEliteWon() activity = TUMOR_PASSIVE activator = null @@ -300,7 +301,7 @@ obj/structure/elite_tumor/proc/onEliteWon() to_chat(mychild, "As the life in the activator's eyes fade, the forcefield around you dies out and you feel your power subside.\nDespite this inferno being your home, you feel as if you aren't welcome here anymore.\nWithout any guidance, your purpose is now for you to decide.") to_chat(mychild, "Your max health has been halved, but can now heal by standing on your tumor. Note, it's your only way to heal.\nBear in mind, if anyone interacts with your tumor, you'll be resummoned here to carry out another fight. In such a case, you will regain your full max health.\nAlso, be weary of your fellow inhabitants, they likely won't be happy to see you!") to_chat(mychild, "Note that you are a lavaland monster, and thus not allied to the station. You should not cooperate or act friendly with any station crew unless under extreme circumstances!") - + /obj/item/tumor_shard name = "tumor shard" desc = "A strange, sharp, crystal shard from an odd tumor on Lavaland. Stabbing the corpse of a lavaland elite with this will revive them, assuming their soul still lingers. Revived lavaland elites only have half their max health, but are completely loyal to their reviver." @@ -313,7 +314,7 @@ obj/structure/elite_tumor/proc/onEliteWon() w_class = WEIGHT_CLASS_SMALL throw_speed = 3 throw_range = 5 - + /obj/item/tumor_shard/afterattack(atom/target, mob/user, proximity_flag) . = ..() if(istype(target, /mob/living/simple_animal/hostile/asteroid/elite) && proximity_flag) @@ -331,10 +332,11 @@ obj/structure/elite_tumor/proc/onEliteWon() E.health = E.maxHealth E.desc = "[E.desc] However, this one appears appears less wild in nature, and calmer around people." E.sentience_type = SENTIENCE_ORGANIC + E.owner = user qdel(src) else to_chat(user, "[src] only works on the corpse of a sentient lavaland elite.") - + /obj/effect/temp_visual/elite_tumor_wall name = "magic wall" icon = 'icons/turf/walls/hierophant_wall_temp.dmi' @@ -347,7 +349,7 @@ obj/structure/elite_tumor/proc/onEliteWon() color = rgb(255,0,0) light_range = MINIMUM_USEFUL_LIGHT_RANGE light_color = LIGHT_COLOR_RED - + /obj/effect/temp_visual/elite_tumor_wall/Initialize(mapload, new_caster) . = ..() queue_smooth_neighbors(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm index 540470d505..e65c4f5b20 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm @@ -44,34 +44,34 @@ /datum/action/innate/elite_attack/magic_box, /datum/action/innate/elite_attack/pandora_teleport, /datum/action/innate/elite_attack/aoe_squares) - + var/sing_shot_length = 8 var/cooldown_time = 20 - + /datum/action/innate/elite_attack/singular_shot name = "Singular Shot" button_icon_state = "singular_shot" chosen_message = "You are now creating a single linear magic square." chosen_attack_num = SINGULAR_SHOT - + /datum/action/innate/elite_attack/magic_box name = "Magic Box" button_icon_state = "magic_box" chosen_message = "You are now attacking with a box of magic squares." chosen_attack_num = MAGIC_BOX - + /datum/action/innate/elite_attack/pandora_teleport name = "Line Teleport" button_icon_state = "pandora_teleport" chosen_message = "You will now teleport to your target." chosen_attack_num = PANDORA_TELEPORT - + /datum/action/innate/elite_attack/aoe_squares name = "AOE Blast" button_icon_state = "aoe_squares" chosen_message = "Your attacks will spawn an AOE blast at your target location." chosen_attack_num = AOE_SQUARES - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/OpenFire() if(client) switch(chosen_attack) @@ -94,7 +94,7 @@ pandora_teleport(target) if(AOE_SQUARES) aoe_squares(target) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/Life() . = ..() if(health >= maxHealth * 0.5) @@ -105,28 +105,28 @@ return else cooldown_time = 10 - -/mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/singular_shot(target) + +/mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/singular_shot(target) ranged_cooldown = world.time + (cooldown_time * 0.5) var/dir_to_target = get_dir(get_turf(src), get_turf(target)) var/turf/T = get_step(get_turf(src), dir_to_target) singular_shot_line(sing_shot_length, dir_to_target, T) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/singular_shot_line(var/procsleft, var/angleused, var/turf/T) if(procsleft <= 0) return - new /obj/effect/temp_visual/hierophant/blast/pandora(T, src) + new /obj/effect/temp_visual/hierophant/blast/pandora(T, src, null, null, list(owner)) T = get_step(T, angleused) procsleft = procsleft - 1 addtimer(CALLBACK(src, .proc/singular_shot_line, procsleft, angleused, T), 2) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/magic_box(target) ranged_cooldown = world.time + cooldown_time var/turf/T = get_turf(target) for(var/t in spiral_range_turfs(3, T)) if(get_dist(t, T) > 1) - new /obj/effect/temp_visual/hierophant/blast/pandora(t, src) - + new /obj/effect/temp_visual/hierophant/blast/pandora(t, src, null, null, list(owner)) + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/pandora_teleport(target) ranged_cooldown = world.time + cooldown_time var/turf/T = get_turf(target) @@ -135,45 +135,45 @@ new /obj/effect/temp_visual/hierophant/telegraph(source, src) playsound(source,'sound/machines/airlockopen.ogg', 200, 1) addtimer(CALLBACK(src, .proc/pandora_teleport_2, T, source), 2) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/pandora_teleport_2(var/turf/T, var/turf/source) new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, src) new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, src) for(var/t in RANGE_TURFS(1, T)) - new /obj/effect/temp_visual/hierophant/blast/pandora(t, src) + new /obj/effect/temp_visual/hierophant/blast/pandora(t, src, null, null, list(owner)) for(var/t in RANGE_TURFS(1, source)) - new /obj/effect/temp_visual/hierophant/blast/pandora(t, src) + new /obj/effect/temp_visual/hierophant/blast/pandora(t, src, null, null, list(owner)) animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out visible_message("[src] fades out!") density = FALSE addtimer(CALLBACK(src, .proc/pandora_teleport_3, T), 2) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/pandora_teleport_3(var/turf/T) forceMove(T) animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN density = TRUE visible_message("[src] fades in!") - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/aoe_squares(target) ranged_cooldown = world.time + cooldown_time var/turf/T = get_turf(target) - new /obj/effect/temp_visual/hierophant/blast/pandora(T, src) + new /obj/effect/temp_visual/hierophant/blast/pandora(T, src, null, null, list(owner)) var/max_size = 2 addtimer(CALLBACK(src, .proc/aoe_squares_2, T, 0, max_size), 2) - + /mob/living/simple_animal/hostile/asteroid/elite/pandora/proc/aoe_squares_2(var/turf/T, var/ring, var/max_size) if(ring > max_size) return for(var/t in spiral_range_turfs(ring, T)) if(get_dist(t, T) == ring) - new /obj/effect/temp_visual/hierophant/blast/pandora(t, src) + new /obj/effect/temp_visual/hierophant/blast/pandora(t, src, null, null, list(owner)) addtimer(CALLBACK(src, .proc/aoe_squares_2, T, (ring + 1), max_size), 2) - + //The specific version of hiero's squares pandora uses /obj/effect/temp_visual/hierophant/blast/pandora damage = 20 monster_damage_boost = FALSE - + //Pandora's loot: Hope /obj/item/clothing/accessory/pandora_hope name = "Hope" @@ -181,7 +181,7 @@ icon = 'icons/obj/lavaland/elite_trophies.dmi' icon_state = "hope" resistance_flags = FIRE_PROOF - + /obj/item/clothing/accessory/pandora_hope/on_uniform_equip(obj/item/clothing/under/U, user) var/mob/living/L = user if(L && L.mind) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm index 05dec578bd..03d2365016 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm @@ -20,7 +20,7 @@ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE mob_size = MOB_SIZE_LARGE var/icon_aggro = null - var/crusher_drop_mod = 5 + var/crusher_drop_mod = 25 /mob/living/simple_animal/hostile/asteroid/Initialize(mapload) . = ..() @@ -58,7 +58,7 @@ /mob/living/simple_animal/hostile/asteroid/death(gibbed) SSblackbox.record_feedback("tally", "mobs_killed_mining", 1, type) var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) - if(C && crusher_loot && prob((C.total_damage/maxHealth) * crusher_drop_mod)) //on average, you'll need to kill 20 creatures before getting the item + if(C && crusher_loot && prob((C.total_damage/maxHealth) * crusher_drop_mod)) //on average, you'll need to kill 4 creatures before getting the item spawn_crusher_loot() ..(gibbed) diff --git a/code/modules/mob/living/simple_animal/hostile/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm index 514edf896b..a5e3cd8120 100644 --- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm +++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm @@ -166,7 +166,9 @@ ..() /mob/living/simple_animal/hostile/mushroom/attack_hand(mob/living/carbon/human/M) - ..() + . = ..() + if(.) // the attack was blocked + return if(M.a_intent == INTENT_HARM) Bruise() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index d1e10ea693..439ea5b2bf 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -253,33 +253,34 @@ return /mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - if(M == src) - return - if(buckled) - Feedstop(silent = TRUE) - visible_message("[M] pulls [src] off!") - return - attacked += 5 - if(nutrition >= 100) //steal some nutrition. negval handled in life() - nutrition -= (50 + (40 * M.is_adult)) - M.add_nutrition(50 + (40 * M.is_adult)) - if(health > 0) - M.adjustBruteLoss(-10 + (-10 * M.is_adult)) - M.updatehealth() + . = ..() + if(!. || M == src) //unsuccessful slime shock + return + if(buckled) + Feedstop(silent = TRUE) + visible_message("[M] pulls [src] off!") + return + attacked += 5 + if(nutrition >= 100) //steal some nutrition. negval handled in life() + nutrition -= (50 + (40 * M.is_adult)) + M.add_nutrition(50 + (40 * M.is_adult)) + if(health > 0) + M.adjustBruteLoss(-10 + (-10 * M.is_adult)) + M.updatehealth() /mob/living/simple_animal/slime/attack_animal(mob/living/simple_animal/M) . = ..() if(.) attacked += 10 - /mob/living/simple_animal/slime/attack_paw(mob/living/carbon/monkey/M) - if(..()) //successful monkey bite. + . = ..() + if(.)//successful monkey bite. attacked += 10 /mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L) - if(..()) //successful larva bite. + . = ..() + if(.) //successful larva bite. attacked += 10 /mob/living/simple_animal/slime/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) @@ -321,9 +322,11 @@ attacked += 10 /mob/living/simple_animal/slime/attack_alien(mob/living/carbon/alien/humanoid/M) - if(..()) //if harm or disarm intent. - attacked += 10 - discipline_slime(M) + . = ..() + if(!.) // the attack was blocked or was help/grab intent + return + attacked += 10 + discipline_slime(M) /mob/living/simple_animal/slime/attackby(obj/item/W, mob/living/user, params) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 1a3f2fca3b..427e30f2e2 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -459,6 +459,11 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA if(!ckey || !new_mob) CRASH("transfer_ckey() called [ckey ? "" : "on a ckey-less mob[new_mob ? "" : " and "]"][new_mob ? "" : "without a valid mob target"]!") SEND_SIGNAL(new_mob, COMSIG_MOB_PRE_PLAYER_CHANGE, new_mob, src) + if (client && client.prefs && client.prefs.auto_ooc) + if (client.prefs.chat_toggles & CHAT_OOC && isliving(new_mob)) + client.prefs.chat_toggles ^= CHAT_OOC + if (!(client.prefs.chat_toggles & CHAT_OOC) && isdead(new_mob)) + client.prefs.chat_toggles ^= CHAT_OOC new_mob.ckey = ckey if(send_signal) SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob, src) diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm index 8c304d8a2b..56c50078c1 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm @@ -26,10 +26,9 @@ Contents: /obj/item/clothing/suit/space/space_ninja/proc/enable_signals() if(!affecting) return - RegisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE), .proc/reduce_stealth) + RegisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE), .proc/reduce_stealth) RegisterSignal(affecting, COMSIG_MOVABLE_BUMP, .proc/bumping_stealth) - /obj/item/clothing/suit/space/space_ninja/proc/reduce_stealth(datum/source) affecting.alpha = min(affecting.alpha + 40, 100) @@ -42,7 +41,7 @@ Contents: return FALSE stealth = !stealth stealth_cooldown = world.time + 5 SECONDS - UnregisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE)) + UnregisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE)) animate(affecting, alpha = 255, time = 3 SECONDS) affecting.visible_message("[affecting.name] appears from thin air!", \ "You are now visible.") diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm index 2ca6ce2611..f3f7e45378 100644 --- a/code/modules/paperwork/paperplane.dm +++ b/code/modules/paperwork/paperplane.dm @@ -102,7 +102,7 @@ /obj/item/paperplane/throw_impact(atom/hit_atom) if(iscarbon(hit_atom)) var/mob/living/carbon/C = hit_atom - if(C.can_catch_item(TRUE)) + if(!C.get_active_held_item() && !C.restrained()) var/datum/action/innate/origami/origami_action = locate() in C.actions if(origami_action?.active) //if they're a master of origami and have the ability turned on, force throwmode on so they'll automatically catch the plane. C.throw_mode_on() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 531c6082b0..19fdfd2b7e 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -37,6 +37,7 @@ var/burst_spread = 0 //Spread induced by the gun itself during burst fire per iteration. Only checked if spread is 0. var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once. var/inaccuracy_modifier = 1 + var/pb_knockback = 0 lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' @@ -125,6 +126,10 @@ if(message) if(pointblank) user.visible_message("[user] fires [src] point blank at [pbtarget]!", null, null, COMBAT_MESSAGE_RANGE) + if(pb_knockback > 0) + var/atom/throw_target = get_edge_target_turf(pbtarget, user.dir) + pbtarget.throw_at(throw_target, pb_knockback, 2) + else user.visible_message("[user] fires [src]!", null, null, COMBAT_MESSAGE_RANGE) diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index bcb212a031..7fb4a8232e 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -267,6 +267,7 @@ fire_delay = 0 pin = /obj/item/firing_pin/implant/pindicate actions_types = list() + pb_knockback = 2 /obj/item/gun/ballistic/automatic/shotgun/bulldog/unrestricted pin = /obj/item/firing_pin diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index d1a5f12888..31a5131804 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -273,6 +273,7 @@ "Maple" = "dshotgun-l", "Rosewood" = "dshotgun-p" ) + pb_knockback = 3 // it's a super shotgun! /obj/item/gun/ballistic/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) ..() @@ -352,7 +353,7 @@ clumsy_check = 0 /obj/item/gun/ballistic/revolver/reverse/can_trigger_gun(mob/living/user) - if((HAS_TRAIT(user, TRAIT_CLUMSY)) || (user.mind && user.mind.assigned_role == "Clown")) + if((HAS_TRAIT(user, TRAIT_CLUMSY)) || (user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY))) return ..() if(process_fire(user, user, FALSE, null, BODY_ZONE_HEAD)) user.visible_message("[user] somehow manages to shoot [user.p_them()]self in the face!", "You somehow shoot yourself in the face! How the hell?!") diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 571525d8f0..deec187f88 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -12,6 +12,8 @@ var/recentpump = 0 // to prevent spammage weapon_weight = WEAPON_MEDIUM + pb_knockback = 2 + /obj/item/gun/ballistic/shotgun/attackby(obj/item/A, mob/user, params) . = ..() if(.) diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index 8b358832b0..8f9bc13583 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -56,6 +56,7 @@ item_flags = NONE casing_ejector = FALSE can_suppress = FALSE + pb_knockback = 0 /obj/item/gun/ballistic/shotgun/toy/process_chamber(empty_chamber = 0) ..() diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index 1e2070de1b..0465a611cd 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -135,7 +135,7 @@ // A gun with ultra-honk pin is useful for clown and useless for everyone else. /obj/item/firing_pin/clown/ultra/pin_auth(mob/living/user) playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) - if(user && (!(HAS_TRAIT(user, TRAIT_CLUMSY)) && !(user.mind && user.mind.assigned_role == "Clown"))) + if(user && (!(HAS_TRAIT(user, TRAIT_CLUMSY)) && !(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)))) return FALSE return TRUE diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 1ac62ba651..513626465a 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -260,7 +260,7 @@ if(!amount) return vol_each = min(reagents.total_volume / amount, 50) - var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return var/obj/item/reagent_containers/pill/P @@ -287,7 +287,7 @@ adjust_item_drop_location(P) reagents.trans_to(P,vol_each) else - var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(drop_location()) @@ -313,7 +313,7 @@ if(!amount) return vol_each = min(reagents.total_volume / amount, 40) - var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return var/obj/item/reagent_containers/pill/P @@ -331,7 +331,7 @@ return if(condi) - var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return var/obj/item/reagent_containers/food/condiment/P = new(drop_location()) @@ -344,7 +344,7 @@ if(text2num(many)) amount_full = round(reagents.total_volume / 30) vol_part = ((reagents.total_volume*1000) % 30000) / 1000 //% operator doesn't support decimals. - var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return @@ -372,7 +372,7 @@ if(text2num(many)) amount_full = round(reagents.total_volume / 60) vol_part = reagents.total_volume % 60 - var/name = stripped_input(usr, "Name:","Name your hypovial!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr, "Name:","Name your hypovial!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return @@ -407,7 +407,7 @@ return vol_each = min(reagents.total_volume / amount, 20) - var/name = stripped_input(usr,"Name:","Name your SmartDart!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN) + var/name = html_decode(stripped_input(usr,"Name:","Name your SmartDart!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)) if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) return diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 5d31bfae9b..e8551074e7 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -247,6 +247,23 @@ . = 1 ..() +/datum/reagent/consumable/coconutmilk + name = "Coconut Milk" + id = "coconutmilk" + description = "A transparent white liquid extracted from coconuts. Rich in taste." + color = "#DFDFDF" // rgb: 223, 223, 223 + taste_description = "sweet milk" + quality = DRINK_GOOD + glass_icon_state = "glass_white" + glass_name = "glass of coconut milk" + glass_desc = "White and nutritious goodness!" + +/datum/reagent/consumable/coconutmilk/on_mob_life(mob/living/carbon/M) + if(M.getBruteLoss() && prob(20)) + M.heal_bodypart_damage(2,0, 0) + . = 1 + ..() + /datum/reagent/consumable/cream name = "Cream" id = "cream" diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 23eb24ed15..64e693a326 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -622,7 +622,7 @@ M.adjustBruteLoss(-1*REM, 0) M.adjustFireLoss(-1*REM, 0) M.adjustOxyLoss(-1*REM, 0) - M.adjustToxLoss(-1*REM, 0) + M.adjustToxLoss(-1*REM, 0, TRUE) //heals TOXINLOVERs ..() /datum/reagent/consumable/honey/reaction_mob(mob/living/M, method=TOUCH, reac_volume) diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 2bd5fb2d9f..94ad5fdb2c 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -26,12 +26,11 @@ Borg Hypospray var/bypass_protection = 0 //If the hypospray can go through armor or thick material var/list/datum/reagents/reagent_list = list() - var/list/reagent_ids = list("dexalin", "kelotane", "bicaridine", "antitoxin", "epinephrine", "spaceacillin", "salglu_solution") + var/list/reagent_ids = list("dexalin", "kelotane", "bicaridine", "antitoxin", "epinephrine", "spaceacillin", "salglu_solution", "insulin") var/accepts_reagent_upgrades = TRUE //If upgrades can increase number of reagents dispensed. var/list/modes = list() //Basically the inverse of reagent_ids. Instead of having numbers as "keys" and strings as values it has strings as keys and numbers as values. //Used as list for input() in shakers. - /obj/item/reagent_containers/borghypo/Initialize() . = ..() @@ -40,12 +39,10 @@ Borg Hypospray START_PROCESSING(SSobj, src) - /obj/item/reagent_containers/borghypo/Destroy() STOP_PROCESSING(SSobj, src) return ..() - /obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg charge_tick++ if(charge_tick >= recharge_time) @@ -162,7 +159,7 @@ Borg Hypospray icon_state = "borghypo_s" charge_cost = 20 recharge_time = 2 - reagent_ids = list("syndicate_nanites", "potass_iodide", "morphine") + reagent_ids = list("syndicate_nanites", "potass_iodide", "morphine", "insulin") bypass_protection = 1 accepts_reagent_upgrades = FALSE @@ -178,7 +175,6 @@ Borg Shaker charge_cost = 20 //Lots of reagents all regenerating at once, so the charge cost is lower. They also regenerate faster. recharge_time = 3 accepts_reagent_upgrades = FALSE - reagent_ids = list("beer", "orangejuice", "grenadine", "limejuice", "tomatojuice", "cola", "tonic", "sodawater", "ice", "cream", "whiskey", "vodka", "rum", "gin", "tequila", "vermouth", "wine", "kahlua", "cognac", "ale", "milk", "coffee", "banana", "lemonjuice") /obj/item/reagent_containers/borghypo/borgshaker/attack(mob/M, mob/user) @@ -234,23 +230,21 @@ Borg Shaker charge_cost = 20 //Lots of reagents all regenerating at once, so the charge cost is lower. They also regenerate faster. recharge_time = 3 accepts_reagent_upgrades = FALSE - reagent_ids = list("fakebeer", "fernet") /obj/item/reagent_containers/borghypo/peace name = "Peace Hypospray" - - reagent_ids = list("dizzysolution","tiresolution","synthpax") + reagent_ids = list("dizzysolution", "tiresolution", "synthpax", "insulin") accepts_reagent_upgrades = FALSE /obj/item/reagent_containers/borghypo/peace/hacked desc = "Everything's peaceful in death!" icon_state = "borghypo_s" - reagent_ids = list("dizzysolution","tiresolution","synthpax","tirizene","sulfonal","sodium_thiopental","cyanide","fentanyl") + reagent_ids = list("dizzysolution", "tiresolution", "synthpax", "tirizene", "sulfonal", "sodium_thiopental", "cyanide", "fentanyl") accepts_reagent_upgrades = FALSE /obj/item/reagent_containers/borghypo/epi - name = "epinephrine injector" + name = "Stabilizer injector" desc = "An advanced chemical synthesizer and injection system, designed to stabilize patients." - reagent_ids = list("epinephrine") + reagent_ids = list("epinephrine", "insulin") accepts_reagent_upgrades = FALSE diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 3ed505c360..85248250a2 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -109,11 +109,13 @@ /obj/item/reagent_containers/glass/beaker name = "beaker" - desc = "A beaker. It can hold up to 50 units. Unable to withstand extreme pHes" + desc = "A beaker. It can hold up to 60 units. Unable to withstand extreme pHes." icon = 'icons/obj/chemical.dmi' + volume = 60 icon_state = "beaker" item_state = "beaker" materials = list(MAT_GLASS=500) + possible_transfer_amounts = list(5,10,15,20,25,30,60) beaker_weakness_bitflag = PH_WEAK /obj/item/reagent_containers/glass/beaker/Initialize() @@ -156,28 +158,28 @@ /obj/item/reagent_containers/glass/beaker/jar name = "honey jar" - desc = "A jar for honey. It can hold up to 50 units of sweet delight. Unable to withstand reagents of an extreme pH." + desc = "A jar for honey. It can hold up to 60 units of sweet delight. Unable to withstand reagents of an extreme pH." icon = 'icons/obj/chemical.dmi' icon_state = "vapour" /obj/item/reagent_containers/glass/beaker/large name = "large beaker" - desc = "A large beaker. Can hold up to 100 units. Unable to withstand reagents of an extreme pH." + desc = "A large beaker. Can hold up to 120 units. Unable to withstand reagents of an extreme pH." icon_state = "beakerlarge" materials = list(MAT_GLASS=2500) - volume = 100 + volume = 120 amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,20,25,30,50,100) + possible_transfer_amounts = list(5,10,15,20,25,30,40,60,120) container_HP = 3 /obj/item/reagent_containers/glass/beaker/plastic name = "x-large beaker" - desc = "An extra-large beaker. Can hold up to 150 units. Is able to resist acid and alkaline solutions, but melts at 444K" + desc = "An extra-large beaker. Can hold up to 180 units. Is able to resist acid and alkaline solutions, but melts at 444 K." icon_state = "beakerwhite" materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000) - volume = 150 + volume = 180 amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,20,25,30,50,100,150) + possible_transfer_amounts = list(5,10,15,20,25,30,40,60,120,180) /obj/item/reagent_containers/glass/beaker/plastic/Initialize() beaker_weakness_bitflag &= ~PH_WEAK @@ -191,14 +193,14 @@ /obj/item/reagent_containers/glass/beaker/meta name = "metamaterial beaker" - desc = "A large beaker. Can hold up to 200 units. Is able to withstand all chemical situations." + desc = "A large beaker. Can hold up to 240 units, and is able to withstand all chemical situations." icon_state = "beakergold" materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000, MAT_GOLD=1000, MAT_TITANIUM=1000) - volume = 200 + volume = 240 amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(5,10,15,20,25,30,50,100,200) + possible_transfer_amounts = list(5,10,15,20,25,30,40,60,120,200,240) -/obj/item/reagent_containers/glass/beaker/meta/Initialize() +/obj/item/reagent_containers/glass/beaker/meta/Initialize() // why the fuck can't you just set the beaker weakness bitflags to nothing? fuck you beaker_weakness_bitflag &= ~PH_WEAK . = ..() @@ -228,7 +230,7 @@ volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300) - container_HP = 4 + container_HP = 5 /obj/item/reagent_containers/glass/beaker/cryoxadone list_reagents = list("cryoxadone" = 30) diff --git a/code/modules/reagents/reagent_containers/hypovial.dm b/code/modules/reagents/reagent_containers/hypovial.dm index ba5ce48a4a..482877afcd 100644 --- a/code/modules/reagents/reagent_containers/hypovial.dm +++ b/code/modules/reagents/reagent_containers/hypovial.dm @@ -1,6 +1,6 @@ //hypovials used with the MkII hypospray. See hypospray.dm. -/obj/item/reagent_containers/glass/bottle/vial +/obj/item/reagent_containers/glass/bottle/vial // these have literally no fucking right to just be better beakers that you can shit out of a chemmaster name = "broken hypovial" desc = "A hypovial compatible with most hyposprays." icon_state = "hypovial" @@ -27,6 +27,8 @@ for(var/R in comes_with) reagents.add_reagent(R,comes_with[R]) update_icon() +// beaker_weakness_bitflag |= PH_WEAK // fuck you if you're using these like beakers +// beaker_weakness_bitflag |= TEMP_WEAK /obj/item/reagent_containers/glass/bottle/vial/on_reagent_change() @@ -60,11 +62,11 @@ /obj/item/reagent_containers/glass/bottle/vial/small name = "hypovial" volume = 60 - possible_transfer_amounts = list(5,10) + possible_transfer_amounts = list(1,2,5,10,20,30) /obj/item/reagent_containers/glass/bottle/vial/small/bluespace volume = 120 - possible_transfer_amounts = list(5,10) + possible_transfer_amounts = list(1,2,5,10,20,30,40) name = "bluespace hypovial" icon_state = "hypovialbs" unique_reskin = null @@ -74,7 +76,7 @@ desc = "A large hypovial, for deluxe hypospray models." icon_state = "hypoviallarge" volume = 120 - possible_transfer_amounts = list(5,10,15,20) + possible_transfer_amounts = list(1,2,5,10,20,30,40,60) unique_reskin = list("large hypovial" = "hypoviallarge", "large red hypovial" = "hypoviallarge-b", "large blue hypovial" = "hypoviallarge-d", @@ -106,7 +108,7 @@ add_overlay(filling) /obj/item/reagent_containers/glass/bottle/vial/large/bluespace - possible_transfer_amounts = list(5,10,15,20) + possible_transfer_amounts = list(1,2,5,10,20,30,40,60) name = "bluespace large hypovial" volume = 240 icon_state = "hypoviallargebs" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index e70362553c..f2ded3a57b 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -152,6 +152,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/telescopiciv + name = "Telescopic IV Drip" + desc = "An IV drip with an advanced infusion pump that can both drain blood into and inject liquids from attached containers. Blood packs are processed at an accelerated rate. This one is telescopic, and can be picked up and put down." + id = "telescopiciv" + build_type = PROTOLATHE + materials = list(MAT_METAL = 5000, MAT_GLASS = 3500, MAT_SILVER = 1000) + build_path = /obj/item/tele_iv + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/holobarrier_med name = "PENLITE holobarrier projector" desc = "PENLITE holobarriers, a device that halts individuals with malicious diseases." diff --git a/code/modules/research/designs/nanite_designs.dm b/code/modules/research/designs/nanite_designs.dm index 177d7073a1..09fe1d9c9b 100644 --- a/code/modules/research/designs/nanite_designs.dm +++ b/code/modules/research/designs/nanite_designs.dm @@ -25,6 +25,20 @@ program_type = /datum/nanite_program/viral category = list("Utility Nanites") +/datum/design/nanites/research + name = "Distributed Computing" + desc = "The nanites aid the research servers by performing a portion of its calculations, increasing research point generation." + id = "research_nanites" + program_type = /datum/nanite_program/research + category = list("Utility Nanites") + +/datum/design/nanites/researchplus + name = "Neural Network" + desc = "The nanites link the host's brains together forming a neural research network, that becomes more efficient with the amount of total hosts. Can be overloaded to increase research output." + id = "researchplus_nanites" + program_type = /datum/nanite_program/researchplus + category = list("Utility Nanites") + /datum/design/nanites/monitoring name = "Monitoring" desc = "The nanites monitor the host's vitals and location, sending them to the suit sensor network." @@ -39,6 +53,13 @@ program_type = /datum/nanite_program/triggered/self_scan category = list("Utility Nanites") +/datum/design/nanites/dermal_button + name = "Dermal Button" + desc = "Displays a button on the host's skin, which can be used to send a signal to the nanites." + id = "dermal_button_nanites" + program_type = /datum/nanite_program/dermal_button + category = list("Utility Nanites") + /datum/design/nanites/stealth name = "Stealth" desc = "The nanites hide their activity and programming from superficial scans." @@ -46,6 +67,15 @@ program_type = /datum/nanite_program/stealth category = list("Utility Nanites") + +/datum/design/nanites/reduced_diagnostics + name = "Reduced Diagnostics" + desc = "Disables some high-cost diagnostics in the nanites, making them unable to communicate their program list to portable scanners. \ + Doing so saves some power, slightly increasing their replication speed." + id = "red_diag_nanites" + program_type = /datum/nanite_program/reduced_diagnostics + category = list("Utility Nanites") + /datum/design/nanites/access name = "Subdermal ID" desc = "The nanites store the host's ID access rights in a subdermal magnetic strip. Updates when triggered, copying the host's current access." diff --git a/code/modules/research/nanites/nanite_programs/utility.dm b/code/modules/research/nanites/nanite_programs/utility.dm index a269d01ac0..3db482d989 100644 --- a/code/modules/research/nanites/nanite_programs/utility.dm +++ b/code/modules/research/nanites/nanite_programs/utility.dm @@ -130,7 +130,7 @@ /datum/nanite_program/stealth name = "Stealth" - desc = "The nanites hide their activity and programming from superficial scans." + desc = "The nanites mask their activity from superficial scans, becoming undetectable by HUDs and non-specialized scanners." rogue_types = list(/datum/nanite_program/toxic) use_rate = 0.2 @@ -142,6 +142,22 @@ . = ..() nanites.stealth = FALSE +/datum/nanite_program/reduced_diagnostics + name = "Reduced Diagnostics" + desc = "Disables some high-cost diagnostics in the nanites, making them unable to communicate their program list to portable scanners. \ + Doing so saves some power, slightly increasing their replication speed." + rogue_types = list(/datum/nanite_program/toxic) + use_rate = -0.1 + +/datum/nanite_program/reduced_diagnostics/enable_passive_effect() + . = ..() + nanites.diagnostics = FALSE + +/datum/nanite_program/reduced_diagnostics/disable_passive_effect() + . = ..() + nanites.diagnostics = TRUE + + /datum/nanite_program/relay name = "Relay" desc = "The nanites receive and relay long-range nanite signals." @@ -271,3 +287,138 @@ if(fault == src) return fault.software_error() + +/datum/nanite_program/dermal_button + name = "Dermal Button" + desc = "Displays a button on the host's skin, which can be used to send a signal to the nanites." + extra_settings = list("Sent Code","Button Name","Icon","Color") + unique = FALSE + var/datum/action/innate/nanite_button/button + var/button_name = "Button" + var/icon = "power" + var/color = "green" + var/sent_code = 0 + +/datum/nanite_program/dermal_button/set_extra_setting(user, setting) + if(setting == "Sent Code") + var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num + if(isnull(new_code)) + return + sent_code = CLAMP(round(new_code, 1), 1, 9999) + if(setting == "Button Name") + var/new_button_name = stripped_input(user, "Choose the name for the button.", "Button Name", button_name, MAX_NAME_LEN) + if(!new_button_name) + return + button_name = new_button_name + if(setting == "Icon") + var/new_icon = input("Select the icon to display on the button:", name) as null|anything in list("one","two","three","four","five","plus","minus","power") + if(!new_icon) + return + icon = new_icon + if(setting == "Color") + var/new_color = input("Select the color of the button's icon:", name) as null|anything in list("green","red","yellow","blue") + if(!new_color) + return + color = new_color + +/datum/nanite_program/dermal_button/get_extra_setting(setting) + if(setting == "Sent Code") + return sent_code + if(setting == "Button Name") + return button_name + if(setting == "Icon") + return capitalize(icon) + if(setting == "Color") + return capitalize(color) + +/datum/nanite_program/dermal_button/copy_extra_settings_to(datum/nanite_program/dermal_button/target) + target.sent_code = sent_code + target.button_name = button_name + target.icon = icon + target.color = color + +/datum/nanite_program/dermal_button/enable_passive_effect() + . = ..() + if(!button) + button = new(src, button_name, icon, color) + button.target = host_mob + button.Grant(host_mob) + +/datum/nanite_program/dermal_button/disable_passive_effect() + . = ..() + if(button) + button.Remove(host_mob) + +/datum/nanite_program/dermal_button/on_mob_remove() + . = ..() + qdel(button) + +/datum/nanite_program/dermal_button/proc/press() + if(activated) + host_mob.visible_message("[host_mob] presses a button on [host_mob.p_their()] forearm.", + "You press the nanite button on your forearm.", null, 2) + SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, sent_code, "a [name] program") + +/datum/action/innate/nanite_button + name = "Button" + icon_icon = 'icons/mob/actions/actions_items.dmi' + check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS + button_icon_state = "power_green" + var/datum/nanite_program/dermal_button/program + +/datum/action/innate/nanite_button/New(datum/nanite_program/dermal_button/_program, _name, _icon, _color) + ..() + program = _program + name = _name + button_icon_state = "[_icon]_[_color]" + +/datum/action/innate/nanite_button/Activate() + program.press() + +/datum/nanite_program/research + name = "Distributed Computing" + desc = "The nanites aid the research servers by performing a portion of its calculations, increasing research point generation." + use_rate = 0.2 + rogue_types = list(/datum/nanite_program/toxic) + +/datum/nanite_program/research/active_effect() + if(!iscarbon(host_mob)) + return + var/points = 1 + if(!host_mob.client) //less brainpower + points *= 0.25 + SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points)) + +/datum/nanite_program/researchplus + name = "Neural Network" + desc = "The nanites link the host's brains together forming a neural research network, that becomes more efficient with the amount of total hosts." + use_rate = 0.3 + rogue_types = list(/datum/nanite_program/brain_decay) + +/datum/nanite_program/researchplus/enable_passive_effect() + . = ..() + if(!iscarbon(host_mob)) + return + if(host_mob.client) + SSnanites.neural_network_count++ + else + SSnanites.neural_network_count += 0.25 + +/datum/nanite_program/researchplus/disable_passive_effect() + . = ..() + if(!iscarbon(host_mob)) + return + if(host_mob.client) + SSnanites.neural_network_count-- + else + SSnanites.neural_network_count -= 0.25 + +/datum/nanite_program/researchplus/active_effect() + if(!iscarbon(host_mob)) + return + var/mob/living/carbon/C = host_mob + var/points = round(SSnanites.neural_network_count / 12, 0.1) + if(!C.client) //less brainpower + points *= 0.25 + SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points)) + diff --git a/code/modules/research/nanites/program_disks.dm b/code/modules/research/nanites/program_disks.dm index 86b7803fc9..f780f40932 100644 --- a/code/modules/research/nanites/program_disks.dm +++ b/code/modules/research/nanites/program_disks.dm @@ -130,4 +130,16 @@ program_type = /datum/nanite_program/pacifying /obj/item/disk/nanite_program/stun - program_type = /datum/nanite_program/triggered/stun \ No newline at end of file + program_type = /datum/nanite_program/triggered/stun + +/obj/item/disk/nanite_program/dermal_button + program_type = /datum/nanite_program/dermal_button + +/obj/item/disk/nanite_program/research + program_type = /datum/nanite_program/research + +/obj/item/disk/nanite_program/researchplus + program_type = /datum/nanite_program/researchplus + +/obj/item/disk/nanite_program/reduced_diagnostics + program_type = /datum/nanite_program/reduced_diagnostics \ No newline at end of file diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index fb0cdd755e..a44495fccb 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -71,7 +71,7 @@ display_name = "Biological Technology" description = "What makes us tick." //the MC, silly! prereq_ids = list("base") - design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen") + design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen", "telescopiciv") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -960,7 +960,7 @@ prereq_ids = list("datatheory","robotics") design_ids = list("nanite_disk","nanite_remote","nanite_scanner",\ "nanite_chamber","public_nanite_chamber","nanite_chamber_control","nanite_programmer","nanite_program_hub","nanite_cloud_control",\ - "relay_nanites", "monitoring_nanites", "access_nanites", "repairing_nanites","sensor_nanite_volume", "repeater_nanites", "relay_repeater_nanites") + "relay_nanites", "monitoring_nanites", "access_nanites", "repairing_nanites","sensor_nanite_volume", "repeater_nanites", "relay_repeater_nanites","red_diag_nanites") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -969,7 +969,7 @@ display_name = "Smart Nanite Programming" description = "Nanite programs that require nanites to perform complex actions, act independently, roam or seek targets." prereq_ids = list("nanite_base","adv_robotics") - design_ids = list("purging_nanites", "metabolic_nanites", "stealth_nanites", "memleak_nanites","sensor_voice_nanites", "voice_nanites") + design_ids = list("purging_nanites", "research_nanites", "metabolic_nanites", "stealth_nanites", "memleak_nanites","sensor_voice_nanites", "voice_nanites") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000) export_price = 4000 @@ -978,7 +978,7 @@ display_name = "Mesh Nanite Programming" description = "Nanite programs that require static structures and membranes." prereq_ids = list("nanite_base","engineering") - design_ids = list("hardening_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites") + design_ids = list("hardening_nanites", "dermal_button_nanites", "refractive_nanites", "cryo_nanites", "conductive_nanites", "shock_nanites", "emp_nanites", "temperature_nanites") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -1015,7 +1015,7 @@ display_name = "Harmonic Nanite Programming" description = "Nanite programs that require seamless integration between nanites and biology." prereq_ids = list("nanite_bio","nanite_smart","nanite_mesh") - design_ids = list("fakedeath_nanites","aggressive_nanites","defib_nanites","regenerative_plus_nanites","brainheal_plus_nanites","purging_plus_nanites","adrenaline_nanites") + design_ids = list("fakedeath_nanites","researchplus_nanites","aggressive_nanites","defib_nanites","regenerative_plus_nanites","brainheal_plus_nanites","purging_plus_nanites","adrenaline_nanites") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000) export_price = 8000 diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index dedff3ae04..161461d099 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -560,7 +560,7 @@ else if((findtext(message, honk_words))) cooldown = COOLDOWN_MEME addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, get_turf(user), 'sound/items/bikehorn.ogg', 300, 1), 25) - if(user.mind && user.mind.assigned_role == "Clown") + if(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)) for(var/mob/living/carbon/C in listeners) C.slip(140 * power_multiplier) cooldown = COOLDOWN_MEME @@ -807,9 +807,8 @@ E.enthrallTally += (power_multiplier*(((length(message))/200) + 1)) //encourage players to say more than one word. else E.enthrallTally += power_multiplier*1.25 //thinking about it, I don't know how this can proc - if(L.canbearoused) - if(L.client?.prefs.lewdchem) - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "[E.enthrallGender] is so nice to listen to."), 5) + if(L.canbearoused && E.lewd) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "[E.enthrallGender] is so nice to listen to."), 5) E.cooldown += 1 //REWARD mixable works @@ -820,7 +819,7 @@ power_multiplier *= distancelist[get_dist(user, V)+1] if(L == user) continue - if (L.client?.prefs.lewdchem) + if (E.lewd) addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "[E.enthrallGender] has praised me!!"), 5) if(HAS_TRAIT(L, TRAIT_NYMPHO)) L.adjustArousalLoss(2*power_multiplier) @@ -832,7 +831,7 @@ addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "I've been praised for doing a good job!"), 5) E.resistanceTally -= power_multiplier E.enthrallTally += power_multiplier - var/descmessage = "[(L.client?.prefs.lewdchem?"I feel so happy! I'm a good pet who [E.enthrallGender] loves!":"I did a good job!")]" + var/descmessage = "[(E.lewd?"I feel so happy! I'm a good pet who [E.enthrallGender] loves!":"I did a good job!")]" SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "enthrallpraise", /datum/mood_event/enthrallpraise, descmessage) E.cooldown += 1 @@ -841,10 +840,10 @@ for(var/V in listeners) var/mob/living/L = V var/datum/status_effect/chem/enthrall/E = L.has_status_effect(/datum/status_effect/chem/enthrall) - var/descmessage = "[(L.client?.prefs.lewdchem?"I've failed [E.enthrallGender]... What a bad, bad pet!":"I did a bad job...")]" + var/descmessage = "[(E.lewd?"I've failed [E.enthrallGender]... What a bad, bad pet!":"I did a bad job...")]" if(L == user) continue - if (L.client?.prefs.lewdchem) + if (E.lewd) if(HAS_TRAIT(L, TRAIT_MASO)) L.adjustArousalLoss(3*power_multiplier) descmessage += "And yet, it feels so good..!" //I don't really understand masco, is this the right sort of thing they like? @@ -871,7 +870,7 @@ var/datum/status_effect/chem/enthrall/E = C.has_status_effect(/datum/status_effect/chem/enthrall) REMOVE_TRAIT(C, TRAIT_MUTE, "enthrall") C.silent = 0 - if(C.client?.prefs.lewdchem) + if(E.lewd) addtimer(CALLBACK(C, /atom/movable/proc/say, "[E.enthrallGender]"), 5) else addtimer(CALLBACK(C, /atom/movable/proc/say, "[E.master]"), 5) @@ -887,7 +886,7 @@ E.phase = 3 E.status = null user.emote("snap") - if(L.client?.prefs.lewdchem) + if(E.lewd) addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "The snapping of your [E.enthrallGender]'s fingers brings you back to your enthralled state, obedient and ready to serve."), 5) else addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "The snapping of [E.master]'s fingers brings you back to being under their influence."), 5) @@ -910,11 +909,11 @@ addtimer(CALLBACK(H, /atom/movable/proc/say, "I feel happy being with you."), 5) continue if(2) - speaktrigger += "[(H.client?.prefs.lewdchem?"I think I'm in love with you... ":"I find you really inspirational, ")]" //' + speaktrigger += "[(E.lewd?"I think I'm in love with you... ":"I find you really inspirational, ")]" //' if(3) - speaktrigger += "[(H.client?.prefs.lewdchem?"I'm devoted to being your pet":"I'm commited to following your cause!")]! " + speaktrigger += "[(E.lewd?"I'm devoted to being your pet":"I'm commited to following your cause!")]! " if(4) - speaktrigger += "[(H.client?.prefs.lewdchem?"You are my whole world and all of my being belongs to you, ":"I cannot think of anything else but aiding your cause, ")] "//Redflags!! + speaktrigger += "[(E.lewd?"You are my whole world and all of my being belongs to you, ":"I cannot think of anything else but aiding your cause, ")] "//Redflags!! //mood var/datum/component/mood/mood = H.GetComponent(/datum/component/mood) @@ -1003,7 +1002,7 @@ speaktrigger += "I feel like I'm on the brink of losing my mind, " //horny - if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && H.client?.prefs.lewdchem) + if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && E.lewd) switch(H.getArousalLoss()) if(40 to 60) speaktrigger += "I'm feeling a little horny, " @@ -1013,10 +1012,10 @@ speaktrigger += "I'm really, really horny, " //collar - if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar) && H.client?.prefs.lewdchem) + if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar) && E.lewd) speaktrigger += "I love the collar you gave me, " //End - if(H.client?.prefs.lewdchem) + if(E.lewd) speaktrigger += "[E.enthrallGender]!" else speaktrigger += "[user.first_name()]!" @@ -1044,7 +1043,7 @@ REMOVE_TRAIT(C, TRAIT_MUTE, "enthrall") C.silent = 0 E.cooldown += 3 - to_chat(user, "You [(C.client?.prefs.lewdchem?"allow [C] to speak again":"encourage [C] to speak again")].") + to_chat(user, "You [(E.lewd?"allow [C] to speak again":"encourage [C] to speak again")].") //Antiresist @@ -1074,7 +1073,7 @@ for(var/mob/living/carbon/C in listeners) var/datum/status_effect/chem/enthrall/E = C.has_status_effect(/datum/status_effect/chem/enthrall) if(E.phase == 4) - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You're unable to forget about [(C.client?.prefs.lewdchem?"the dominating presence of [E.enthrallGender]":"[E.master]")]!"), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You're unable to forget about [(E.lewd?"the dominating presence of [E.enthrallGender]":"[E.master]")]!"), 5) continue addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You wake up, forgetting everything that just happened. You must've dozed off..? How embarassing!"), 5) C.Sleeping(50) @@ -1086,7 +1085,7 @@ if(3) E.phase = 0 E.cooldown = 0 - if(C.client?.prefs.lewdchem) + if(E.lewd) addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You revert to yourself before being enthralled by your [E.enthrallGender], with no memory of what happened."), 5) else addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You revert to who you were before, with no memory of what happened with [E.master]."), 5) @@ -1112,7 +1111,7 @@ var/mob/living/carbon/human/H = V var/datum/status_effect/chem/enthrall/E = H.has_status_effect(/datum/status_effect/chem/enthrall) if(E.phase > 1) - if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && H.client?.prefs.lewdchem) // probably a redundant check but for good measure + if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && E.lewd) // probably a redundant check but for good measure addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "Your [E.enthrallGender] pushes you over the limit, overwhelming your body with pleasure."), 5) H.mob_climax(forced_climax=TRUE) H.SetStun(20) @@ -1168,7 +1167,7 @@ for(var/obj/item/W in items) if(W == H.wear_suit) H.dropItemToGround(W, TRUE) - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "Before you can even think about it, you quickly remove your clothes in response to [(H.client?.prefs.lewdchem?"your [E.enthrallGender]'s command'":"[E.master]'s directive'")]."), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "Before you can even think about it, you quickly remove your clothes in response to [(E.lewd?"your [E.enthrallGender]'s command'":"[E.master]'s directive'")]."), 5) E.cooldown += 10 //WALK @@ -1206,7 +1205,7 @@ if(2 to INFINITY) L.lay_down() E.cooldown += 10 - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "[(L.client?.prefs.lewdchem?"You eagerly lie down!":"You suddenly lie down!")]"), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, L, "[(E.lewd?"You eagerly lie down!":"You suddenly lie down!")]"), 5) to_chat(user, "You encourage [L] to lie down.") //KNOCKDOWN @@ -1237,7 +1236,7 @@ for (var/trigger in E.customTriggers) speaktrigger += "[trigger], " to_chat(user, "[C] whispers, \"[speaktrigger] are my triggers.\"")//So they don't trigger themselves! - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You whisper your triggers to [(C.client?.prefs.lewdchem?"Your [E.enthrallGender]":"[E.master]")]."), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, C, "You whisper your triggers to [(E.lewd?"Your [E.enthrallGender]":"[E.master]")]."), 5) //CUSTOM TRIGGERS @@ -1249,7 +1248,7 @@ if (get_dist(user, H) > 1)//Requires user to be next to their pet. to_chat(user, "You need to be next to your pet to give them a new trigger!") continue - if(!H.client?.prefs.lewdchem) + if(!E.lewd) to_chat(user, "[H] seems incapable of being implanted with triggers.") continue else @@ -1272,7 +1271,7 @@ E.customTriggers[trigger] = trigger2 log_game("FERMICHEM: [H] has been implanted by [user] with [trigger], triggering [trigger2].") E.mental_capacity -= 5 - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(H.client?.prefs.lewdchem?"your [E.enthrallGender]":"[E.master]")] whispers you a new trigger."), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(E.lewd?"your [E.enthrallGender]":"[E.master]")] whispers you a new trigger."), 5) to_chat(user, "You sucessfully set the trigger word [trigger] in [H]") else to_chat(user, "Your pet looks at you confused, it seems they don't understand that effect!") @@ -1290,7 +1289,7 @@ if (get_dist(user, H) > 1)//Requires user to be next to their pet. to_chat(user, "You need to be next to your pet to give them a new echophrase!") continue - if(!H.client?.prefs.lewdchem) + if(!E.lewd) to_chat(user, "[H] seems incapable of being implanted with an echoing phrase.") continue else @@ -1334,7 +1333,7 @@ objective = replacetext(lowertext(objective), "suicide", "self-love") message_admins("[H] has been implanted by [user] with the objective [objective].") log_game("FERMICHEM: [H] has been implanted by [user] with the objective [objective] via MKUltra.") - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(H.client?.prefs.lewdchem?"Your [E.enthrallGender]":"[E.master]")] whispers you a new objective."), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(E.lewd?"Your [E.enthrallGender]":"[E.master]")] whispers you a new objective."), 5) brainwash(H, objective) E.mental_capacity -= 200 to_chat(user, "You sucessfully give an objective to [H]") @@ -1348,7 +1347,7 @@ for(var/V in listeners) var/mob/living/carbon/human/H = V var/datum/status_effect/chem/enthrall/E = H.has_status_effect(/datum/status_effect/chem/enthrall) - if(E.phase >= 3 && H.client?.prefs.lewdchem) + if(E.phase >= 3 && E.lewd) var/instill = stripped_input(user, "Instill an emotion in [H].", MAX_MESSAGE_LEN) to_chat(H, "[instill]") to_chat(user, "You sucessfully instill a feeling in [H]") @@ -1363,7 +1362,7 @@ if(E.phase > 1) if(user.ckey == E.enthrallID && user.real_name == E.master.real_name) E.master = user - addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(H.client?.prefs.lewdchem?"You hear the words of your [E.enthrallGender] again!! They're back!!":"You recognise the voice of [E.master].")]"), 5) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "[(E.lewd?"You hear the words of your [E.enthrallGender] again!! They're back!!":"You recognise the voice of [E.master].")]"), 5) to_chat(user, "[H] looks at you with sparkling eyes, recognising you!") //I dunno how to do state objectives without them revealing they're an antag diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm index a2a9c37cfd..1415604aff 100644 --- a/code/modules/vehicles/cars/clowncar.dm +++ b/code/modules/vehicles/cars/clowncar.dm @@ -21,7 +21,7 @@ /obj/vehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M) if(ishuman(M)) var/mob/living/carbon/human/H = M - if(H.mind && H.mind.assigned_role == "Clown") //Ensures only clowns can drive the car. (Including more at once) + if(H.mind && HAS_TRAIT(H.mind, TRAIT_CLOWN_MENTALITY)) //Ensures only clowns can drive the car. (Including more at once) add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION) return add_control_flags(M, VEHICLE_CONTROL_KIDNAPPED) diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm index 626abd51ef..6144f4c57a 100644 --- a/code/modules/vending/clothesmate.dm +++ b/code/modules/vending/clothesmate.dm @@ -70,14 +70,22 @@ /obj/item/clothing/under/skirt/red = 3, /obj/item/clothing/under/skirt/purple = 3, /obj/item/clothing/under/sundress = 4, + /obj/item/clothing/under/sundresswhite = 4, /obj/item/clothing/under/stripeddress = 3, /obj/item/clothing/under/sailordress = 3, + /obj/item/clothing/under/sweptskirt = 3, + /obj/item/clothing/under/greendress = 3, + /obj/item/clothing/under/pinkdress = 3, /obj/item/clothing/under/redeveninggown = 3, /obj/item/clothing/under/blacktango = 3, + /obj/item/clothing/under/westernbustle = 3, + /obj/item/clothing/under/flamenco = 3, + /obj/item/clothing/under/flowerdress = 3, /obj/item/clothing/under/plaid_skirt = 3, /obj/item/clothing/under/plaid_skirt/blue = 3, /obj/item/clothing/under/plaid_skirt/purple = 3, /obj/item/clothing/under/plaid_skirt/green = 3, + /obj/item/clothing/under/croptop = 3, /obj/item/clothing/glasses/regular = 2, /obj/item/clothing/glasses/regular/jamjar = 2, /obj/item/clothing/head/sombrero = 3, diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm index 5c092c3659..03241c4a86 100644 --- a/code/modules/vending/megaseed.dm +++ b/code/modules/vending/megaseed.dm @@ -14,6 +14,7 @@ /obj/item/seeds/chanter = 3, /obj/item/seeds/chili = 3, /obj/item/seeds/cocoapod = 3, + /obj/item/seeds/coconut = 3, /obj/item/seeds/coffee = 3, /obj/item/seeds/cotton = 3, /obj/item/seeds/corn = 3, diff --git a/html/changelog.html b/html/changelog.html index 8eabba83df..21cade10d2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,328 @@ -->
+

30 December 2019

+

AnturK updated:

+ +

Arturlang updated:

+ +

BlueWildrose updated:

+ +

Commandersand updated:

+ +

DeltaFire15 updated:

+ +

Detective-Google updated:

+ +

Fermis updated:

+ +

Fikou updated:

+ +

Ghommie updated:

+ +

Ghommie (also porting PRs by AnturK and Arkatos) updated:

+ +

Ghommie, Skogol updated:

+ +

GrayRachnid updated:

+ +

Hatterhat updated:

+ +

ItzGabby updated:

+ +

KeRSedChaplain updated:

+ +

Linzolle updated:

+ +

Mickyan, nemvar, RaveRadbury, AnturK, SpaceManiac updated:

+ +

Narcissisko (ported by Hatterhat) updated:

+ +

Nervere and subject217, Militaires, py01, nemvar updated:

+ +

PersianXerxes updated:

+ +

Putnam updated:

+ +

Putnam3145 updated:

+ +

Seris02 updated:

+ +

ShizCalev updated:

+ +

Sishen1542 updated:

+ +

Toriate updated:

+ +

Trilbyspaceclone updated:

+ +

Useroth updated:

+ +

Xantholne updated:

+ +

kappa-sama updated:

+ +

keronshb updated:

+ +

kevinz000 updated:

+ +

lolman360 updated:

+ +

nemvar updated:

+ +

nicbn, Kevinz000, ShizCalev updated:

+ +

r4d6 updated:

+ +

shellspeed1 updated:

+ +

07 December 2019

AffectedArc07 updated: