diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index a5f2c5d0f6..cf98755a36 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -53,6 +53,7 @@ #define GELADIKINESIS /datum/mutation/human/geladikinesis #define CRYOKINESIS /datum/mutation/human/cryokinesis #define SPIDER_WEB /datum/mutation/human/webbing +#define CLUWNEMUT /datum/mutation/human/cluwne #define UI_CHANGED "ui changed" #define UE_CHANGED "ue changed" diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index 0d97634a66..bd6ae98594 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -56,8 +56,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define UNUSED_RESERVATION_TURF_1 (1<<1) /// If a turf can be made dirty at roundstart. This is also used in areas. #define CAN_BE_DIRTY_1 (1<<2) -/// If blood cultists can draw runes or build structures on this turf -#define CULT_PERMITTED_1 (1<<3) /// Blocks lava rivers being generated on the turf #define NO_LAVA_GEN_1 (1<<6) /// Blocks ruins spawning on the turf @@ -92,6 +90,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define ABDUCTOR_PROOF (1<<11) /// If an area should be hidden from power consoles, power/atmosphere alerts, etc. #define NO_ALERTS (1<<12) +/// If blood cultists can draw runes or build structures on this AREA. +#define CULT_PERMITTED (1<<13) /* These defines are used specifically with the atom/pass_flags bitmask diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 831f5a5c85..d121458def 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -82,6 +82,7 @@ #define ADMIN_PUNISHMENT_PICKLE "Pickle-ify" #define ADMIN_PUNISHMENT_FRY "Fry" #define ADMIN_PUNISHMENT_PERFORATE ":B:erforate" +#define ADMIN_PUNISHMENT_CLUWNE "Cluwne" #define ADMIN_PUNISHMENT_GOODBYE "Their final message" //sandstorm #define AHELP_ACTIVE 1 diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 58947b00d7..0e3aa1a6ff 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -160,7 +160,7 @@ #define ATMOS_TANK_PLASMA "plasma=70000;TEMP=293.15" #define ATMOS_TANK_O2 "o2=100000;TEMP=293.15" #define ATMOS_TANK_N2 "n2=100000;TEMP=293.15" -#define ATMOS_TANK_AIRMIX "o2=2644;n2=10580;TEMP=293.15" +#define ATMOS_TANK_AIRMIX "o2=2811;n2=10583;TEMP=293.15" //LAVALAND #define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index d219736abe..a6d411a8dd 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -158,7 +158,7 @@ /// how long they're staggered for #define SHOVE_STAGGER_DURATION 35 /// how long they're off balance for -#define SHOVE_OFFBALANCE_DURATION 30 +#define SHOVE_OFFBALANCE_DURATION 3 SECONDS //Embedded objects diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index 683681a9a6..d3caedac22 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -23,6 +23,14 @@ #define WINDOW_IN_FRAME 1 #define WINDOW_SCREWED_TO_FRAME 2 +//reinforced plasma window construction states +#define PRWINDOW_FRAME_BOLTED 3 +#define PRWINDOW_BARS_CUT 4 +#define PRWINDOW_POPPED 5 +#define PRWINDOW_BOLTS_OUT 6 +#define PRWINDOW_BOLTS_HEATED 7 +#define PRWINDOW_SECURE 8 + //airlock assembly construction states #define AIRLOCK_ASSEMBLY_NEEDS_WIRES 0 #define AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS 1 diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 9c6f16e724..781fa38b94 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -15,7 +15,7 @@ GLOBAL_LIST_INIT(bitfields, list( "ABDUCTOR_PROOF" = ABDUCTOR_PROOF, "BLOBS_ALLOWED" = BLOBS_ALLOWED, "BLOCK_SUICIDE" = BLOCK_SUICIDE, - // "CULT_PERMITTED" = CULT_PERMITTED, + "CULT_PERMITTED" = CULT_PERMITTED, "FLORA_ALLOWED" = FLORA_ALLOWED, "HIDDEN_AREA" = HIDDEN_AREA, "MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED, diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 63f4aff408..1e0b9e272f 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -192,13 +192,10 @@ scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE) enter_subsystem() - //dogborg check sandstorm thingy thing. var/mob/living/silicon/robot/R = target - if(iscyborg(R)) //without this it would check for module on every mob speaking, and you would NOT remove this unless you love runtimes - if(R.module.dogborg == TRUE || R.dogborg == TRUE) //BOYS, WHAT THE HELL, GREAT INCONSISTENCY! + if(iscyborg(R)) + if(R.module.dogborg == TRUE || R.dogborg == TRUE) //I hate whoever that thought that putting two types of dogborg that don't even sync up properly was good message.pixel_x = 16 - else - message.pixel_x = 0 /** * Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion diff --git a/code/datums/mutations/cluwne.dm b/code/datums/mutations/cluwne.dm new file mode 100644 index 0000000000..59b7752372 --- /dev/null +++ b/code/datums/mutations/cluwne.dm @@ -0,0 +1,57 @@ +/datum/mutation/human/cluwne + + name = "Cluwne" + quality = NEGATIVE + locked = TRUE + text_gain_indication = "You feel like your brain is tearing itself apart." + +/datum/mutation/human/cluwne/on_acquiring(mob/living/carbon/human/owner) + if(..()) + return + owner.dna.add_mutation(CLOWNMUT) + owner.dna.add_mutation(EPILEPSY) + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199) + + var/mob/living/carbon/human/H = owner + + if(!istype(H.wear_mask, /obj/item/clothing/mask/gas/cluwne)) + if(!H.dropItemToGround(H.wear_mask)) + qdel(H.wear_mask) + H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/cluwne(H), SLOT_WEAR_MASK) + if(!istype(H.w_uniform, /obj/item/clothing/under/cluwne)) + if(!H.dropItemToGround(H.w_uniform)) + qdel(H.w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/under/cluwne(H), SLOT_W_UNIFORM) + if(!istype(H.shoes, /obj/item/clothing/shoes/clown_shoes/cluwne)) + if(!H.dropItemToGround(H.shoes)) + qdel(H.shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/clown_shoes/cluwne(H), SLOT_SHOES) + + owner.equip_to_slot_or_del(new /obj/item/clothing/gloves/color/white(owner), SLOT_GLOVES) // this is purely for cosmetic purposes incase they aren't wearing anything in that slot + owner.equip_to_slot_or_del(new /obj/item/storage/backpack/clown(owner), SLOT_BACK) // ditto + +/datum/mutation/human/cluwne/on_life(mob/living/carbon/human/owner) + if((prob(15) && owner.IsUnconscious())) + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199) // there I changed it to setBrainLoss + switch(rand(1, 6)) + if(1) + owner.say("HONK") + if(2 to 5) + owner.emote("scream") + if(6) + owner.Stun(1) + owner.Knockdown(20) + owner.Jitter(500) + +/datum/mutation/human/cluwne/on_losing(mob/living/carbon/human/owner) + owner.adjust_fire_stacks(1) + owner.IgniteMob() + owner.dna.add_mutation(CLUWNEMUT) + +/mob/living/carbon/human/proc/cluwneify() + dna.add_mutation(CLUWNEMUT) + emote("scream") + regenerate_icons() + visible_message("[src]'s body glows green, the glow dissipating only to leave behind a cluwne formerly known as [src]!", \ + "Your brain feels like it's being torn apart, there is only the honkmother now.") + flash_act() diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm index a27f4b291e..57f4fdb8de 100644 --- a/code/game/area/Space_Station_13_areas.dm +++ b/code/game/area/Space_Station_13_areas.dm @@ -209,7 +209,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "Generic Maintenance" // ambience_index = AMBIENCE_MAINT ambientsounds = MAINTENANCE - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED // airlock_wires = /datum/wires/airlock/maint sound_environment = SOUND_AREA_TUNNEL_ENCLOSED @@ -294,7 +294,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/maintenance/department/science/xenobiology name = "Xenobiology Maintenance" icon_state = "xenomaint" - area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA | XENOBIOLOGY_COMPATIBLE + area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA | XENOBIOLOGY_COMPATIBLE | CULT_PERMITTED //Maintenance - Generic @@ -598,11 +598,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/commons name = "Crew Quarters" sound_environment = SOUND_AREA_STANDARD_STATION + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED /area/commons/dorms name = "Dormitories" icon_state = "dorms" - area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA /area/commons/dorms/barracks name = "Sleep Barracks" @@ -899,7 +899,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station // mood_bonus = 5 // mood_message = "I love being in the library!\n" // mood_trait = TRAIT_INTROVERT - flags_1 = CULT_PERMITTED_1 + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED sound_environment = SOUND_AREA_LARGE_SOFTFLOOR /area/service/library/lounge @@ -926,7 +926,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/service/library/abandoned name = "Abandoned Library" icon_state = "abandoned_library" - flags_1 = CULT_PERMITTED_1 nightshift_public_area = NIGHTSHIFT_AREA_NONE /area/service/chapel @@ -973,8 +972,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/service/janitor name = "Custodial Closet" icon_state = "janitor" - flags_1 = CULT_PERMITTED_1 sound_environment = SOUND_AREA_SMALL_ENCLOSED + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED /area/service/janitor/aux name = "Auxiliary Custodial Closet" @@ -1027,7 +1026,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/engineering/atmos name = "Atmospherics" icon_state = "atmos" - flags_1 = CULT_PERMITTED_1 /area/engineering/atmos/upper name = "Upper Atmospherics" @@ -1035,7 +1033,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/engineering/atmospherics_engine name = "Atmospherics Engine" icon_state = "atmos_engine" - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED /area/engineering/lobby name = "Engineering Lobby" @@ -1044,7 +1042,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/engineering/supermatter name = "Supermatter Engine" icon_state = "engine_sm" - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED sound_environment = SOUND_AREA_SMALL_ENCLOSED /area/engineering/break_room @@ -1084,7 +1082,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/engineering/storage/tcomms name = "Telecomms Storage" icon_state = "tcom" - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED clockwork_warp_allowed = FALSE //Engineering - Construction @@ -1314,7 +1312,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/medical/virology name = "Virology" icon_state = "virology" - flags_1 = CULT_PERMITTED_1 /area/medical/morgue name = "Morgue" @@ -1608,7 +1605,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/science/test_area name = "Toxins Test Area" icon_state = "tox_test" - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED /area/science/mixing name = "Toxins Mixing Lab" @@ -1617,7 +1614,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/science/mixing/chamber name = "Toxins Mixing Chamber" icon_state = "tox_mix_chamber" - area_flags = BLOBS_ALLOWED | UNIQUE_AREA + area_flags = BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED /area/science/misc_lab name = "Testing Lab" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index bc7a802df5..2a21d72119 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -13,7 +13,7 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT invisibility = INVISIBILITY_LIGHTING - var/area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA + var/area_flags = VALID_TERRITORY | BLOBS_ALLOWED | UNIQUE_AREA | CULT_PERMITTED var/fire = null ///Whether there is an atmos alarm in this area diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm index 09adaf72f9..0befe0bd86 100644 --- a/code/game/area/areas/centcom.dm +++ b/code/game/area/areas/centcom.dm @@ -11,7 +11,7 @@ dynamic_lighting = DYNAMIC_LIGHTING_FORCED requires_power = FALSE has_gravity = STANDARD_GRAVITY - area_flags = VALID_TERRITORY | UNIQUE_AREA | NOTELEPORT + area_flags = UNIQUE_AREA | NOTELEPORT flags_1 = NONE /area/centcom/control diff --git a/code/game/area/areas/holodeck.dm b/code/game/area/areas/holodeck.dm index c639080775..3bc73a945f 100644 --- a/code/game/area/areas/holodeck.dm +++ b/code/game/area/areas/holodeck.dm @@ -3,7 +3,6 @@ icon_state = "Holodeck" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED flags_1 = NONE - area_flags = VALID_TERRITORY | UNIQUE_AREA sound_environment = SOUND_ENVIRONMENT_PADDED_CELL var/obj/machinery/computer/holodeck/linked @@ -87,9 +86,6 @@ /area/holodeck/rec_center/lounge name = "Holodeck - Lounge" -/area/holodeck/rec_center/medical - name = "Holodeck - Emergency Medical" - /area/holodeck/rec_center/pet_lounge name = "Holodeck - Pet Park" @@ -128,6 +124,10 @@ name = "Holodeck - Holdout Bunker" restricted = 1 +/area/holodeck/rec_center/medical + name = "Holodeck - Emergency Medical" + restricted = 1 + /area/holodeck/rec_center/anthophila name = "Holodeck - Anthophila" restricted = 1 diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm index 78bf7a1311..c8ee2973c4 100644 --- a/code/game/area/areas/shuttles.dm +++ b/code/game/area/areas/shuttles.dm @@ -157,10 +157,10 @@ /area/shuttle/escape name = "Emergency Shuttle" - area_flags = BLOBS_ALLOWED + area_flags = BLOBS_ALLOWED | CULT_PERMITTED // area_limited_icon_smoothing = /area/shuttle/escape canSmoothWithAreas = /area/shuttle/escape - flags_1 = CAN_BE_DIRTY_1 // | CULT_PERMITTED_1 + flags_1 = CAN_BE_DIRTY_1 /area/shuttle/escape/backup name = "Backup Emergency Shuttle" diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 9953b593d9..64b950a417 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -18,6 +18,8 @@ GLOBAL_LIST_INIT(meteorsB, list(/obj/effect/meteor/meaty=5, /obj/effect/meteor/m GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event +GLOBAL_LIST_INIT(meteorsCat, list(/obj/effect/meteor/cat)) //for cat meteors + /////////////////////////////// //Meteor spawning global procs @@ -345,6 +347,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event ..() if(prob(20)) explosion(src.loc,2,4,6,8) + +/obj/effect/meteor/cat + name = "space cat" + icon_state = "catmeteor" + desc = "Meow." + hits = 1 + heavy = FALSE + meteorsound = 'sound/effects/meow1.ogg' + meteordrop = list(/mob/living/simple_animal/pet/cat/space) + threat = 0 + dropamt = 1 ////////////////////////// //Spookoween meteors diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index bb8cad4fbc..9b02396763 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -341,6 +341,9 @@ return TRUE . = ..() +/obj/structure/foamedmetal/resin/BlockSuperconductivity() + return TRUE + #undef ALUMINUM_FOAM #undef IRON_FOAM #undef RESIN_FOAM diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 069e72b318..d7eeb2789b 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -378,6 +378,12 @@ icon_state = "shieldsparkles" duration = 6 +/obj/effect/temp_visual/impact_effect/judgement_cut + name = "judgement cut impact" + icon = 'icons/effects/effects.dmi' + icon_state = "impact_judgement" + duration = 6 + /obj/effect/temp_visual/heart name = "heart" icon = 'icons/mob/animal.dmi' @@ -535,3 +541,7 @@ /obj/effect/temp_visual/slime_puddle/reverse icon_state = "from_puddle" duration = 7 + +/obj/effect/temp_visual/gib_animation + icon = 'icons/mob/mob.dmi' + duration = 15 diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm index d958a9190d..36529e4dd5 100644 --- a/code/game/objects/items/devices/dogborg_sleeper.dm +++ b/code/game/objects/items/devices/dogborg_sleeper.dm @@ -90,15 +90,15 @@ if(patient) to_chat(user, "Your [src.name] is already occupied.") return - + if (!CHECK_BITFIELD(target.vore_flags,DEVOURABLE)) to_chat(user, "The target registers an error code. Unable to insert into [src.name].") return - + var/voracious = TRUE if(!target.client || !(target.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER)) voracious = FALSE - + user.visible_message("[hound.name] is carefully inserting [target.name] into their [src.name].", "You start placing [target] into your [src.name]...") if(do_after (user, 100, target = target) && !target.buckled && !target.anchored && !patient) if(!in_range(src, target)) //Proximity is probably old news by now, do a new check. @@ -132,16 +132,16 @@ if(!hound) go_out(user) return - + if(user.a_intent == INTENT_HELP) user.visible_message("[user] gently rubs the flexible confines.", \ "You gently rub [hound.name]'s flexible confines.") return - + var/voracious = TRUE if(!user.client || !(user.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER)) voracious = FALSE - + if(prob(escape_chance) && !escape_pending) user.visible_message("You see [voracious ? "[user] struggling against the expanded material of [hound]'s gut!" : "and hear [user] pounding against something inside of [hound]'s [src.name]!"]", \ "[voracious ? "You start struggling inside of [src.name]'s tight, flexible confines," : "You start pounding against the metallic walls of [src.name],"] managing to trigger a hidden emergency release... (this will take about [DisplayTimeText(breakout_time)].)", \ @@ -319,7 +319,7 @@ hound.sleeper_nv = TRUE else hound.sleeper_nv = FALSE - + //Update icon and return new patient hound.update_icons() return @@ -406,7 +406,7 @@ escape_pending = FALSE to_chat(hound, "Your [src.name] clicks as its self-cleaning cycle ends. NOTE: Foreign objects are still detected. Resume self-cleaning?") playsound(loc, 'sound/machines/click.ogg', 50, 1) - + if(!contents || length(contents) == 0) //Belly is entirely empty to_chat(hound, "Your [src.name] chimes as it completes its self-cleaning cycle.") @@ -452,7 +452,7 @@ hound.cell.use(inject_cost) //-750 charge per injection //var/units = round(patient.reagents.get_reagent_amount(chem)) to_chat(hound, "Injecting [inject_amount] unit\s of [chem] into occupant.") //If they were immersed, the reagents wouldn't leave with them. - + /obj/item/dogborg/sleeper/proc/chem_allowed(chem) if(!patient || !patient.reagents) return @@ -488,25 +488,25 @@ if(patient) to_chat(user, "Your [src.name] is already occupied.") return - + if(!CHECK_BITFIELD(target.vore_flags,DEVOURABLE)) to_chat(user, "The target registers an error code. Unable to insert into [src.name].") return - + var/voracious = TRUE if(!target.client || !(target.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER)) voracious = FALSE - + user.visible_message("[hound.name] is ingesting [target] into their [src.name].", "You start ingesting [target] into your [src.name]...") if(do_after(user, 30, target = target) && !patient && !target.buckled) - + //if(patient) //to_chat(user,"Your [src.name] is already occupied.") //return //if(target.buckled) //to_chat(user,"[target] is buckled and can not be put into your [src.name].") //return - + target.forceMove(src) target.reset_perspective(src) update_gut(hound) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 2c3964e8f0..87b92e40aa 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -383,8 +383,8 @@ GENETICS SCANNER else if (S.flying_species != initial(S.flying_species)) mutant = TRUE - msg += "\tReported Species: [H.dna.custom_species ? H.dna.custom_species : S.name]\n" - msg += "\tBase Species: [S.name]\n" + msg += "\tReported Species: [H.spec_trait_examine_font()][H.dna.custom_species ? H.dna.custom_species : S.name]\n" + msg += "\tBase Species: [H.spec_trait_examine_font()][S.name]\n" if(mutant) msg += "\tSubject has mutations present.\n" msg += "\tBody temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)\n" diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 69d4ff573e..57fe8dd0c2 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -596,7 +596,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \ return var/turf/T = get_turf(user) //we may have moved. adjust as needed... var/area/A = get_area(user) - if((!is_station_level(T.z) && !is_mining_level(T.z)) || (A && !(A.flags_1 & CULT_PERMITTED_1))) + if((!is_station_level(T.z) && !is_mining_level(T.z)) || !(A?.area_flags & CULT_PERMITTED)) to_chat(user, "The veil is not weak enough here.") return FALSE return ..() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index e40e158895..6fbe1bc357 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) var/ini_dir = null var/state = WINDOW_OUT_OF_FRAME var/reinf = FALSE + var/extra_reinforced = FALSE var/heat_resistance = 800 var/decon_speed = 30 var/wtype = "glass" @@ -65,6 +66,20 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) . += "The window is out of the frame, but could be pried in. It is screwed to the floor." else if(!anchored) . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." + switch(state) + if(PRWINDOW_SECURE) + if(extra_reinforced) + . += "It's been screwed in with one way screws, you'd need to heat their solder cover to have any chance of backing them out." + else + . += "It's been screwed in with solid screws, you'd need to screw them out to unsecure the window." + if(PRWINDOW_BOLTS_HEATED) + . += "The solder cover melts away, and you'll likely be able to unscrew them now." + if(PRWINDOW_BOLTS_OUT) + . += "The screws have been removed, revealing a small gap you could fit a prying tool in." + if(PRWINDOW_POPPED) + . += "The main plate of the window has popped out of the frame, exposing some bars that look like they can be cut." + if(PRWINDOW_BARS_CUT) + . += "The main pane can be easily moved out of the way to reveal some bolts holding the frame in." else if(anchored) . += "The window is screwed to the floor." @@ -75,8 +90,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) . = ..() if(direct) setDir(direct) - if(reinf && anchored) + + if(extra_reinforced && anchored) + state = PRWINDOW_SECURE + + else if(reinf && anchored) state = WINDOW_SCREWED_TO_FRAME + if(mapload && electrochromatic_id && electrochromatic_id[1] == "!") electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id) @@ -230,29 +250,26 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) make_electrochromatic(K.id) qdel(K) - if(!(flags_1&NODECONSTRUCT_1)) + if(!(flags_1 & NODECONSTRUCT_1) && !(state >= PRWINDOW_FRAME_BOLTED)) if(I.tool_behaviour == TOOL_SCREWDRIVER) I.play_tool_sound(src, 75) - if(reinf) - if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME) - to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...") - if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME && anchored) + to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...") + if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + if(extra_reinforced && state == WINDOW_IN_FRAME) + state = PRWINDOW_SECURE + else state = (state == WINDOW_IN_FRAME ? WINDOW_SCREWED_TO_FRAME : WINDOW_IN_FRAME) - to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.") - else if(state == WINDOW_OUT_OF_FRAME) - to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...") - if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) - setAnchored(!anchored) - to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") - else //if we're not reinforced, we don't need to check or update state - to_chat(user, "You begin to [anchored ? "unscrew the window from":"screw the window to"] the floor...") - if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_anchored, anchored))) + to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.") + else if(state == WINDOW_OUT_OF_FRAME) + to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...") + if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) setAnchored(!anchored) - to_chat(user, "You [anchored ? "fasten the window to":"unfasten the window from"] the floor.") + to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") return - else if(I.tool_behaviour == TOOL_CROWBAR && reinf && (state == WINDOW_OUT_OF_FRAME || state == WINDOW_IN_FRAME)) + else if(I.tool_behaviour == TOOL_CROWBAR && reinf && (state == WINDOW_OUT_OF_FRAME || state == WINDOW_IN_FRAME) && anchored) to_chat(user, "You begin to lever the window [state == WINDOW_OUT_OF_FRAME ? "into":"out of"] the frame...") I.play_tool_sound(src, 75) if(I.use_tool(src, user, decon_speed, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) @@ -270,8 +287,67 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) to_chat(user, "You successfully disassemble [src].") qdel(src) return + if(!reinf || !anchored) + return ..() + switch(state) + if(PRWINDOW_SECURE) + if(extra_reinforced) + if(I.tool_behaviour == TOOL_WELDER && user.a_intent == INTENT_HARM) + user.visible_message("[user] holds \the [I] to the security screws on \the [src]...", + "You begin heating the security screws on \the [src]...") + if(I.use_tool(src, user, 180, volume = 100)) + to_chat(user, "The security bolts are glowing white hot and look ready to be removed.") + state = PRWINDOW_BOLTS_HEATED + addtimer(CALLBACK(src, .proc/cool_bolts), 300) + return + else + if(I.tool_behaviour == TOOL_SCREWDRIVER) + user.visible_message("[user] digs into the screws and starts removing them...", + "You dig into the screws hard and they start turning...") + if(I.use_tool(src, user, 80, volume = 50)) + state = PRWINDOW_BOLTS_OUT + to_chat(user, "The screws come out, and a gap forms around the edge of the pane.") + return + if(PRWINDOW_BOLTS_HEATED) + if(I.tool_behaviour == TOOL_SCREWDRIVER) + user.visible_message("[user] digs into the security screws and starts removing them...", + "You dig into the screws hard and they start turning...") + if(I.use_tool(src, user, 80, volume = 50)) + state = PRWINDOW_BOLTS_OUT + to_chat(user, "The screws come out, and a gap forms around the edge of the pane.") + return + if(PRWINDOW_BOLTS_OUT) + if(I.tool_behaviour == TOOL_CROWBAR) + user.visible_message("[user] wedges \the [I] into the gap in the frame and starts prying...", + "You wedge \the [I] into the gap in the frame and start prying...") + if(I.use_tool(src, user, 50, volume = 50)) + state = PRWINDOW_POPPED + to_chat(user, "The panel pops out of the frame, exposing some thin metal bars that looks like they can be cut.") + return + if(PRWINDOW_POPPED) + if(I.tool_behaviour == TOOL_WIRECUTTER) + user.visible_message("[user] starts cutting the exposed bars on \the [src]...", + "You start cutting the exposed bars on \the [src]") + if(I.use_tool(src, user, 30, volume = 50)) + state = PRWINDOW_BARS_CUT + to_chat(user, "The panels falls out of the way exposing the frame bolts.") + return + if(PRWINDOW_BARS_CUT) + if(I.tool_behaviour == TOOL_WRENCH) + user.visible_message("[user] starts unfastening \the [src] from the frame...", + "You start unfastening the bolts from the frame...") + if(I.use_tool(src, user, 50, volume = 50)) + to_chat(user, "You unscrew the bolts from the frame and the window pops loose.") + state = WINDOW_OUT_OF_FRAME + setAnchored(FALSE) + return return ..() +/obj/structure/window/proc/cool_bolts() + if(state == PRWINDOW_BOLTS_HEATED) + state = PRWINDOW_SECURE + visible_message("The bolts on \the [src] look like they've cooled off...") + /obj/structure/window/setAnchored(anchorvalue) ..() air_update_turf(TRUE) @@ -577,6 +653,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) desc = "A window made out of a plasma-silicate alloy and a rod matrix. It looks hopelessly tough to break and is most likely nigh fireproof." icon_state = "plasmarwindow" reinf = TRUE + extra_reinforced = TRUE heat_resistance = 50000 armor = list("melee" = 85, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) max_integrity = 500 @@ -642,6 +719,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) icon = 'icons/obj/smooth_structures/rplasma_window.dmi' icon_state = "rplasmawindow" dir = FULLTILE_WINDOW_DIR + state = PRWINDOW_SECURE max_integrity = 1000 fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 @@ -726,6 +804,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) fulltile = TRUE flags_1 = PREVENT_CLICK_UNDER_1 reinf = TRUE + extra_reinforced = TRUE heat_resistance = 1600 armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) smooth = SMOOTH_TRUE @@ -758,6 +837,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) wave_explosion_block = EXPLOSION_BLOCK_BOROSILICATE_WINDOW wave_explosion_multiply = EXPLOSION_DAMPEN_BOROSILICATE_WINDOW decon_speed = 40 + extra_reinforced = TRUE glass_type = /obj/item/stack/tile/brass glass_amount = 1 reinf = FALSE diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7d7ddd15c6..9433832e2e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -116,6 +116,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/show_tip, /client/proc/smite, /client/proc/admin_away, + /client/proc/spawn_floor_cluwne, /client/proc/cmd_admin_toggle_fov, //CIT CHANGE - FOV /client/proc/roll_dices //CIT CHANGE - Adds dice verb )) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 9a3eeedc1f..d982cbb5ea 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1346,10 +1346,11 @@ Traitors and the like can also be revived with the previous role mostly intact. ADMIN_PUNISHMENT_SHOES, ADMIN_PUNISHMENT_PICKLE, ADMIN_PUNISHMENT_FRY, - ADMIN_PUNISHMENT_CRACK, - ADMIN_PUNISHMENT_BLEED, - ADMIN_PUNISHMENT_SCARIFY, - ADMIN_PUNISHMENT_GOODBYE) + ADMIN_PUNISHMENT_CRACK, + ADMIN_PUNISHMENT_BLEED, + ADMIN_PUNISHMENT_SCARIFY, + ADMIN_PUNISHMENT_CLUWNE, + ADMIN_PUNISHMENT_GOODBYE) var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list @@ -1515,6 +1516,11 @@ Traitors and the like can also be revived with the previous role mostly intact. to_chat(usr,"[C] does not have knottable shoes!") return sick_kicks.adjust_laces(SHOES_KNOTTED) + if(ADMIN_PUNISHMENT_CLUWNE) + if(!iscarbon(target)) + to_chat(usr,"This must be used on a carbon mob.") + return + target.cluwneify() if(ADMIN_PUNISHMENT_GOODBYE) //sandstorm punish :) it starts here var/mob/living/C = target if(C.stat == DEAD) @@ -1712,3 +1718,23 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!source) return REMOVE_TRAIT(D,chosen_trait,source) + +/client/proc/spawn_floor_cluwne() + set category = "Admin.Fun" + set name = "Unleash Floor Cluwne" + set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!" + var/target + + if(!check_rights(R_FUN)) + return + + var/turf/T = get_turf(usr) + target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in GLOB.player_list + if(target && ishuman(target)) + var/mob/living/carbon/human/H = target + var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) + FC.Acquire_Victim(H) + else + new /mob/living/simple_animal/hostile/floor_cluwne(T) + log_admin("[key_name(usr)] spawned floor cluwne.") + message_admins("[key_name(usr)] spawned floor cluwne.") diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm index 6b6dd64f72..41c82bb45e 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm @@ -25,7 +25,7 @@ /datum/clockwork_scripture/create_object/stargazer/check_special_requirements() var/area/A = get_area(invoker) var/turf/T = get_turf(invoker) - if(!is_station_level(invoker.z) || isspaceturf(T) || !(A.area_flags & VALID_TERRITORY)) + if(!is_station_level(invoker.z) || isspaceturf(T) || !(A?.area_flags & CULT_PERMITTED)) to_chat(invoker, "Stargazers can't be built off-station.") return return ..() diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm index 6802576bd6..caa45d55d2 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm @@ -34,7 +34,7 @@ return FALSE var/area/A = get_area(invoker) var/turf/T = get_turf(invoker) - if(!is_station_level(T.z) || isspaceturf(T) || !(A.area_flags & VALID_TERRITORY) || isshuttleturf(T)) + if(!is_station_level(T.z) || isspaceturf(T) || !(A?.area_flags & CULT_PERMITTED) || isshuttleturf(T)) to_chat(invoker, "You must be on the station to activate the Ark!") return FALSE if(GLOB.clockwork_gateway_activated) diff --git a/code/modules/antagonists/clockcult/clock_structures/stargazer.dm b/code/modules/antagonists/clockcult/clock_structures/stargazer.dm index 88a29f14f1..dea8f428ba 100644 --- a/code/modules/antagonists/clockcult/clock_structures/stargazer.dm +++ b/code/modules/antagonists/clockcult/clock_structures/stargazer.dm @@ -52,7 +52,7 @@ break if(has_starlight && anchored) var/area/A = get_area(src) - if(A.outdoors || A.map_name == "Space" || !(A.area_flags & VALID_TERRITORY)) + if(A.outdoors || A.map_name == "Space" || !(A?.area_flags & CULT_PERMITTED)) has_starlight = FALSE if(old_status != has_starlight) if(has_starlight) diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 30ccb731c3..0cc0ed133e 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -419,7 +419,7 @@ var/sanity = 0 while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100) var/area/summon = pick(GLOB.sortedAreas - summon_spots) - if(summon && is_station_level(summon.z) && !(summon.area_flags & VALID_TERRITORY)) + if(summon && is_station_level(summon.z) && (summon.area_flags & VALID_TERRITORY)) summon_spots += summon sanity++ update_explanation_text() diff --git a/code/modules/antagonists/cult/ritual.dm b/code/modules/antagonists/cult/ritual.dm index 1aef9654e3..98889169c6 100644 --- a/code/modules/antagonists/cult/ritual.dm +++ b/code/modules/antagonists/cult/ritual.dm @@ -148,7 +148,7 @@ This file contains the cult dagger and rune list code to_chat(user, "There is already a rune here.") return FALSE var/area/A = get_area(T) - if((!is_station_level(T.z) && !is_mining_level(T.z)) || !(A?.area_flags & VALID_TERRITORY)) + if((!is_station_level(T.z) && !is_mining_level(T.z)) || !(A?.area_flags & CULT_PERMITTED)) to_chat(user, "The veil is not weak enough here.") return FALSE return TRUE diff --git a/code/modules/antagonists/eldritch_cult/eldritch_book.dm b/code/modules/antagonists/eldritch_cult/eldritch_book.dm index 265f4db095..808932443f 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_book.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_book.dm @@ -89,6 +89,7 @@ var/list/lore = list() data["charges"] = charge + data["total_sacs"] = cultie.total_sacrifices for(var/X in to_know) lore = list() @@ -96,7 +97,11 @@ lore["type"] = EK.type lore["name"] = EK.name lore["cost"] = EK.cost - lore["disabled"] = EK.cost <= charge ? FALSE : TRUE + lore["sacs"] = EK.sacs_needed + if(EK.cost <= charge && cultie.total_sacrifices >= EK.sacs_needed) + lore["disabled"] = FALSE + else + lore["disabled"] = TRUE lore["path"] = EK.route lore["state"] = "Research" lore["flavour"] = EK.gain_text @@ -108,6 +113,7 @@ var/datum/eldritch_knowledge/EK = known[X] lore["name"] = EK.name lore["cost"] = EK.cost + lore["sacs"] = EK.sacs_needed lore["disabled"] = TRUE lore["path"] = EK.route lore["state"] = "Researched" diff --git a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm index a390c04a1c..3d6059dfe3 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm @@ -165,6 +165,25 @@ new /obj/effect/reality_smash(chosen_location) ReworkNetwork() +/** +*CIT CHANGE +* +*Creates a singular reality smash +*Credit to slimelust +*/ + +/datum/reality_smash_tracker/proc/RandomSpawnSmash(var/deferred = FALSE) + var/turf/chosen_location = get_safe_random_station_turf() + //we also dont want them close to each other, at least 1 tile of separation + var/obj/effect/reality_smash/what_if_i_have_one = locate() in range(1, chosen_location) + var/obj/effect/broken_illusion/what_if_i_had_one_but_got_used = locate() in range(1, chosen_location) + var/tries = 10 + while((what_if_i_have_one || what_if_i_had_one_but_got_used) && tries-- > 0) + chosen_location = get_safe_random_station_turf() + new /obj/effect/reality_smash(chosen_location) + if(!deferred) + ReworkNetwork() + /** * Adds a mind to the list of people that can see the reality smashes * @@ -200,15 +219,23 @@ /obj/effect/broken_illusion/Initialize() . = ..() addtimer(CALLBACK(src,.proc/show_presence),15 SECONDS) + addtimer(CALLBACK(src,.proc/remove_presence),195 SECONDS) var/image/I = image('icons/effects/eldritch.dmi',src,null,OBJ_LAYER) I.override = TRUE add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "pierced_reality", I) +/obj/effect/broken_illusion/Destroy() + GLOB.reality_smash_track.RandomSpawnSmash() + return ..() + ///Makes this obj appear out of nothing /obj/effect/broken_illusion/proc/show_presence() animate(src,alpha = 255,time = 15 SECONDS) +/obj/effect/broken_illusion/proc/remove_presence() + qdel(src) + /obj/effect/broken_illusion/attack_hand(mob/living/user, list/modifiers) if(!ishuman(user)) return ..() diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm index 86ef2afacb..87a4ddf7eb 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm @@ -44,46 +44,6 @@ if(target.stat == DEAD) to_chat(user,"[target.real_name] is dead. Bring them onto a transmutation rune!") -/datum/action/innate/heretic_shatter - name = "Shattering Offer" - desc = "After a brief delay, you will be granted salvation from a dire situation at the cost of your blade. (Teleports you to a random safe turf on your current z level after a windup, but destroys your blade.)" - background_icon_state = "bg_ecult" - button_icon_state = "shatter" - icon_icon = 'icons/mob/actions/actions_ecult.dmi' - check_flags = MOBILITY_HOLD|MOBILITY_MOVE|MOBILITY_USE - var/mob/living/carbon/human/holder - var/obj/item/melee/sickly_blade/sword - -/datum/action/innate/heretic_shatter/Grant(mob/user, obj/object) - sword = object - holder = user - //i know what im doing - return ..() - -/datum/action/innate/heretic_shatter/IsAvailable() - if(IS_HERETIC(holder) || IS_HERETIC_MONSTER(holder)) - return ..() - else - return FALSE - -/datum/action/innate/heretic_shatter/Activate() - if(do_after(holder,10, target = holder)) - if(!sword || QDELETED(sword)) - return - if(!IsAvailable()) //Never trust the user. - return - var/swordz = (get_turf(sword))?.z //SHOULD usually have a turf but if it doesn't better be prepared. - if(!swordz) - to_chat(holder, "[sword] flickers but remains in place, as do you...") - return - var/turf/safe_turf = find_safe_turf(zlevels = swordz, extended_safety_checks = TRUE) - if(!safe_turf) - to_chat(holder, "[sword] flickers but remains in place, as do you...") - return - do_teleport(holder,safe_turf,forceMove = TRUE,channel=TELEPORT_CHANNEL_MAGIC) - to_chat(holder,"You feel a gust of energy flow through your body... the Rusted Hills heard your call...") - qdel(sword) - /obj/item/melee/sickly_blade name = "sickly blade" desc = "A sickly green crescent blade, decorated with an ornamental eye. You feel like you're being watched..." @@ -101,11 +61,6 @@ throwforce = 10 hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "rended") - var/datum/action/innate/heretic_shatter/linked_action - -/obj/item/melee/sickly_blade/Initialize() - . = ..() - linked_action = new(src) /obj/item/melee/sickly_blade/attack(mob/living/target, mob/living/user) if(!(IS_HERETIC(user) || IS_HERETIC_MONSTER(user))) @@ -120,13 +75,17 @@ return return ..() -/obj/item/melee/sickly_blade/pickup(mob/user) - . = ..() - linked_action.Grant(user, src) - -/obj/item/melee/sickly_blade/dropped(mob/user, silent) - . = ..() - linked_action.Remove(user, src) +/obj/item/melee/sickly_blade/attack_self(mob/user) + var/turf/safe_turf = find_safe_turf(zlevels = z, extended_safety_checks = TRUE) + if(IS_HERETIC(user) || IS_HERETIC_MONSTER(user)) + if(do_teleport(user, safe_turf, forceMove = TRUE, channel = TELEPORT_CHANNEL_MAGIC)) + to_chat(user,"As you shatter [src], you feel a gust of energy flow through your body. The Rusted Hills heard your call...") + else + to_chat(user,"You shatter [src], but your plea goes unanswered.") + else + to_chat(user,"You shatter [src].") + playsound(src, "shatter", 70, TRUE) //copied from the code for smashing a glass sheet onto the ground to turn it into a shard + qdel(src) /obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() @@ -141,6 +100,11 @@ else eldritch_knowledge_datum.on_ranged_attack_eldritch_blade(target,user,click_parameters) +/obj/item/melee/sickly_blade/examine(mob/user) + . = ..() + if(IS_HERETIC(user) || IS_HERETIC_MONSTER(user)) + . += "A heretic (or a servant of one) can shatter this blade to teleport to a random, mostly safe location by activating it in-hand." + /obj/item/melee/sickly_blade/rust name = "rusted blade" desc = "This crescent blade is decrepit, wasting to rust. Yet still it bites, ripping flesh and bone with jagged, rotten teeth." @@ -243,6 +207,7 @@ // slightly worse than normal cult robes armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) pocket_storage_component_path = /datum/component/storage/concrete/pockets/void_cloak + mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON /obj/item/clothing/suit/hooded/cultrobes/void/ToggleHood() if(!iscarbon(loc)) diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm index d2132be4bc..33a17c3278 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm @@ -15,6 +15,8 @@ var/gain_text = "" ///Cost of knowledge in souls var/cost = 0 + ///Required sacrifices to unlock + var/sacs_needed = 0 ///Next knowledge in the research tree var/list/next_knowledge = list() ///What knowledge is incompatible with this. This will simply make it impossible to research knowledges that are in banned_knowledge once this gets researched. @@ -230,7 +232,6 @@ atoms -= H H.gib() - /////////////// ///Base lore/// /////////////// diff --git a/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm index 0256382723..e453f5456d 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm @@ -109,10 +109,15 @@ gain_text = "When the Glory of the Lantern scorches and sears their skin, nothing will protect them from the ashes." desc = "Fire off five streams of fire from your hand, each setting ablaze targets hit and scorching them upon contact." cost = 2 + sacs_needed = 3 spell_to_add = /obj/effect/proc_holder/spell/pointed/nightwatchers_rite next_knowledge = list(/datum/eldritch_knowledge/final/ash_final) route = PATH_ASH +/datum/eldritch_knowledge/spell/nightwatchers_rite/on_gain(mob/user) + . = ..() + priority_announce("Large heat signatures discovered! A swelling fiery horror is coming..", sound = 'sound/misc/notice1.ogg') + /datum/eldritch_knowledge/ash_blade_upgrade name = "Fiery Blade" gain_text = "Blade in hand, he swung and swung as the ash fell from the skies. His city, his people... all burnt to cinders, and yet life still remained in his charred body." @@ -181,6 +186,7 @@ desc = "Bring 3 corpses onto a transmutation rune, you will become immune to fire, the vacuum of space, cold and other enviromental hazards and become overall sturdier to all other damages. You will gain a spell that passively creates ring of fire around you as well ,as you will gain a powerful ability that lets you create a wave of flames all around you." required_atoms = list(/mob/living/carbon/human) cost = 5 + sacs_needed = 8 route = PATH_ASH var/list/trait_list = list(TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm index 9e71f34735..023be4b3e7 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm @@ -170,7 +170,7 @@ gain_text = "The Uncanny Man, who walks alone in the valley between the worlds... I was able to summon his aid." desc = "You can now summon a Raw Prophet by transmutating a pair of eyes, a left arm and a pool of blood. Raw prophets have increased seeing range, as well as X-Ray vision, but they are very fragile." cost = 1 - required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm,/obj/effect/decal/cleanable/blood) + required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/effect/decal/cleanable/blood) mob_to_summon = /mob/living/simple_animal/hostile/eldritch/raw_prophet next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/rune_carver,/datum/eldritch_knowledge/curse/paralysis) route = PATH_FLESH @@ -217,6 +217,7 @@ desc = "Bring 3 bodies onto a transmutation rune to shed your human form and ascend to untold power." required_atoms = list(/mob/living/carbon/human) cost = 5 + sacs_needed = 8 route = PATH_FLESH /datum/eldritch_knowledge/final/flesh_final/on_finished_recipe(mob/living/user, list/atoms, loc) @@ -257,6 +258,11 @@ gain_text = "The ignorant mind that inhabits their feeble bodies will crumble when they acknowledge - willingly or not, the truth." desc = "By forcing the knowledge of the Mansus upon my foes, I can show them things that would drive any normal man insane." cost = 2 + sacs_needed = 3 spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mad_touch next_knowledge = list(/datum/eldritch_knowledge/final/flesh_final) route = PATH_FLESH + +/datum/eldritch_knowledge/spell/touch_of_madness/on_gain(mob/user) + . = ..() + priority_announce("The stench of rotting flesh fills the air... An approaching abomination has been detected!", sound = 'sound/misc/notice1.ogg') diff --git a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm index aa927ffcfc..509c35261e 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm @@ -159,15 +159,21 @@ desc = "Applying your knowledge of rust to the human body, a knowledge that could decay your foes from the inside out, resulting in organ failure, vomiting, or eventual death through the peeling of rotting flesh." gain_text = "Rust, decay, it's all the same. All that remains is application." cost = 2 + sacs_needed = 3 spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay next_knowledge = list(/datum/eldritch_knowledge/final/rust_final) route = PATH_RUST +/datum/eldritch_knowledge/spell/grasp_of_decay/on_gain(mob/user) + . = ..() + priority_announce("A foul wind is blowing... The floor creaks with rust as something sinister approaches!", sound = 'sound/misc/notice1.ogg') + /datum/eldritch_knowledge/final/rust_final name = "Rustbringer's Oath" desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall." gain_text = "Champion of rust. Corruptor of steel. Fear the dark for the Rustbringer has come! Rusted Hills, CALL MY NAME!" cost = 5 + sacs_needed = 8 required_atoms = list(/mob/living/carbon/human) route = PATH_RUST diff --git a/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm index 6046bfbdf5..97b5af0f4d 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm @@ -160,15 +160,21 @@ gain_text = "This world will be my stage, and nothing will be out of my reach." desc = "Gain the ability to mark a 7x7 area as your domain after a short delay. Creatures in your domain are slowed and branded with a void mark, allowing you to quickly teleport to them and slash them, further inhibiting their ability to move." cost = 2 + sacs_needed = 3 spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/domain_expansion next_knowledge = list(/datum/eldritch_knowledge/final/void_final) route = PATH_VOID +/datum/eldritch_knowledge/spell/domain_expansion/on_gain(mob/user) + . = ..() + priority_announce("Echos of the lost in space are heard... An ominous presence is being detected! ", sound = 'sound/misc/notice1.ogg') + /datum/eldritch_knowledge/final/void_final name = "Waltz at the End of Time" desc = "Bring 3 corpses onto the transmutation rune. After you finish the ritual you will automatically silence people around you and will summon a snow storm around you." gain_text = "The world falls into darkness. I stand in an empty plane, small flakes of ice fall from the sky. The Aristocrat stands before me, he motions to me. We will play a waltz to the whispers of dying reality, as the world is destroyed before our eyes." cost = 5 + sacs_needed = 8 required_atoms = list(/mob/living/carbon/human) route = PATH_VOID ///soundloop for the void theme diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 31989fe241..537c714966 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -492,7 +492,7 @@ var/targetAmount = 100 /datum/objective/revenant/New() - targetAmount = rand(350,600) + targetAmount = rand(150,300) explanation_text = "Absorb [targetAmount] points of essence from humans." ..() diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 8303de60cc..c328fa232d 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -145,7 +145,7 @@ if("VICTIM") var/mob/living/carbon/human/T = target var/datum/antagonist/cult/C = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE) - if(C?.cult_team.is_sacrifice_target(T.mind)) + if(C && C.cult_team.is_sacrifice_target(T.mind)) if(iscultist(user)) to_chat(user, "\"This soul is mine. SACRIFICE THEM!\"") else diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index 9b2def48d7..21de660edb 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -252,6 +252,10 @@ name = "Barnyard Curse" spell_type = /obj/effect/proc_holder/spell/pointed/barnyardcurse +/datum/spellbook_entry/cluwne + name = "Cluwne Curse" + spell_type = /obj/effect/proc_holder/spell/targeted/cluwnecurse + /datum/spellbook_entry/charge name = "Charge" spell_type = /obj/effect/proc_holder/spell/targeted/charge diff --git a/code/modules/clothing/masks/cluwne.dm b/code/modules/clothing/masks/cluwne.dm new file mode 100644 index 0000000000..a1177e2b47 --- /dev/null +++ b/code/modules/clothing/masks/cluwne.dm @@ -0,0 +1,106 @@ +/obj/item/clothing/mask/gas/cluwne + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." + flags_cover = MASKCOVERSEYES + icon_state = "cluwne" + item_state = "cluwne" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + clothing_flags = ALLOWINTERNALS + item_flags = ABSTRACT | DROPDEL + flags_inv = HIDEEARS|HIDEEYES + var/voicechange = TRUE + var/last_sound = 0 + var/delay = 15 + +/obj/item/clothing/mask/gas/cluwne/Initialize() + .=..() + ADD_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT) + ADD_TRAIT(src, CURSED_ITEM_TRAIT, CLOTHING_TRAIT) + +/obj/item/clothing/mask/gas/cluwne/proc/play_laugh1() + if(world.time - delay > last_sound) + playsound (src, 'sound/voice/cluwnelaugh1.ogg', 30, 1) + last_sound = world.time + +/obj/item/clothing/mask/gas/cluwne/proc/play_laugh2() + if(world.time - delay > last_sound) + playsound (src, 'sound/voice/cluwnelaugh2.ogg', 30, 1) + last_sound = world.time + +/obj/item/clothing/mask/gas/cluwne/proc/play_laugh3() + if(world.time - delay > last_sound) + playsound (src, 'sound/voice/cluwnelaugh3.ogg', 30, 1) + last_sound = world.time + +/obj/item/clothing/mask/gas/cluwne/equipped(mob/user, slot) //when you put it on + var/mob/living/carbon/C = user + if((C.wear_mask == src) && (voicechange)) + play_laugh1() + return ..() + +/obj/item/clothing/mask/gas/cluwne/handle_speech(datum/source, list/speech_args) + if(voicechange) + if(prob(5)) //the brain isn't fully gone yet... + speech_args[SPEECH_MESSAGE] = pick("HELP ME!!","PLEASE KILL ME!!","I WANT TO DIE!!", "END MY SUFFERING", "I CANT TAKE THIS ANYMORE!!" ,"SOMEBODY STOP ME!!") + play_laugh2() + if(prob(3)) + speech_args[SPEECH_MESSAGE] = pick("HOOOOINKKKKKKK!!", "HOINK HOINK HOINK HOINK!!","HOINK HOINK!!","HOOOOOOIIINKKKK!!") //but most of the time they cant speak, + play_laugh3() + else + speech_args[SPEECH_MESSAGE] = pick("HEEEENKKKKKK!!", "HONK HONK HONK HONK!!","HONK HONK!!","HOOOOOONKKKK!!") //More sounds, + play_laugh1() + return SPEECH_MESSAGE + +/obj/item/clothing/mask/gas/cluwne/equipped(mob/user, slot) + . = ..() + if(!ishuman(user)) + return + if(slot == SLOT_WEAR_MASK) + var/mob/living/carbon/human/H = user + H.dna.add_mutation(CLUWNEMUT) + return + +/obj/item/clothing/mask/gas/cluwne/happy_cluwne + name = "Happy Cluwne Mask" + desc = "The mask of a poor cluwne that has been scrubbed of its curse by the Nanotrasen supernatural machinations division. Guaranteed to be %99 curse free and %99.9 not haunted. " + item_flags = ABSTRACT + var/can_cluwne = FALSE + var/is_cursed = FALSE //i don't care that this is *slightly* memory wasteful, it's just one more byte and it's not like some madman is going to spawn thousands of these + var/is_very_cursed = FALSE + +/obj/item/clothing/mask/gas/cluwne/happy_cluwne/Initialize() + .=..() + if(prob(1)) //this function pre-determines the logic of the cluwne mask. applying and reapplying the mask does not alter or change anything + is_cursed = TRUE + is_very_cursed = FALSE + else if(prob(0.1)) + is_cursed = FALSE + is_very_cursed = TRUE + +/obj/item/clothing/mask/gas/cluwne/happy_cluwne/attack_self(mob/user) + voicechange = !voicechange + to_chat(user, "You turn the voice box [voicechange ? "on" : "off"]!") + if(voicechange) + play_laugh1() + +/obj/item/clothing/mask/gas/cluwne/happy_cluwne/equipped(mob/user, slot) + . = ..() + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(slot == SLOT_WEAR_MASK) + if(is_cursed && can_cluwne) //logic predetermined + log_admin("[key_name(H)] was made into a cluwne by [src]") + message_admins("[key_name(H)] got cluwned by [src]") + to_chat(H, "The masks straps suddenly tighten to your face and your thoughts are erased by a horrible green light!") + H.dropItemToGround(src) + H.cluwneify() + qdel(src) + else if(is_very_cursed && can_cluwne) + var/turf/T = get_turf(src) + var/mob/living/simple_animal/hostile/floor_cluwne/S = new(T) + S.Acquire_Victim(user) + log_admin("[key_name(user)] summoned a floor cluwne using the [src]") + message_admins("[key_name(user)] summoned a floor cluwne using the [src]") + to_chat(H, "The mask suddenly slips off your face and... slides under the floor?") + to_chat(H, "...dneirf uoy ot gnoleb ton seod tahT") diff --git a/code/modules/clothing/shoes/cluwne.dm b/code/modules/clothing/shoes/cluwne.dm new file mode 100644 index 0000000000..fbe1ba624b --- /dev/null +++ b/code/modules/clothing/shoes/cluwne.dm @@ -0,0 +1,29 @@ +/obj/item/clothing/shoes/clown_shoes/cluwne + desc = "The prankster's standard-issue clowning shoes. Damn, they're huge!" + name = "clown shoes" + icon_state = "cluwne" + item_state = "cluwne" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + item_flags = DROPDEL + var/footstep = 1 + +/obj/item/clothing/shoes/clown_shoes/cluwne/Initialize() + .=..() + ADD_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT) + ADD_TRAIT(src, CURSED_ITEM_TRAIT, CLOTHING_TRAIT) + +/obj/item/clothing/shoes/clown_shoes/cluwne/step_action() + if(footstep > 1) + playsound(src, "clownstep", 50, 1) + footstep = 0 + else + footstep++ + +/obj/item/clothing/shoes/clown_shoes/cluwne/equipped(mob/user, slot) + . = ..() + if(!ishuman(user)) + return + if(slot == SLOT_SHOES) + var/mob/living/carbon/human/H = user + H.dna.add_mutation(CLUWNEMUT) + return diff --git a/code/modules/clothing/under/cluwne.dm b/code/modules/clothing/under/cluwne.dm new file mode 100644 index 0000000000..5a908c5663 --- /dev/null +++ b/code/modules/clothing/under/cluwne.dm @@ -0,0 +1,22 @@ +/obj/item/clothing/under/cluwne + name = "clown suit" + desc = "'HONK!'" + icon_state = "greenclown" + item_state = "greenclown" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + item_flags = DROPDEL + can_adjust = 0 + mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON + +/obj/item/clothing/under/cluwne/Initialize() + .=..() + ADD_TRAIT(src, TRAIT_NODROP, CLOTHING_TRAIT) + ADD_TRAIT(src, CURSED_ITEM_TRAIT, CLOTHING_TRAIT) + +/obj/item/clothing/under/cluwne/equipped(mob/living/carbon/user, slot) + if(!ishuman(user)) + return + if(slot == SLOT_W_UNIFORM) + var/mob/living/carbon/human/H = user + H.dna.add_mutation(CLUWNEMUT) + return ..() diff --git a/code/modules/clothing/under/trek.dm b/code/modules/clothing/under/trek.dm index 8b6b8055bd..89e42d2566 100644 --- a/code/modules/clothing/under/trek.dm +++ b/code/modules/clothing/under/trek.dm @@ -102,9 +102,14 @@ desc = "An uniform worn by command officers since 2420s." icon_state = "orv_com" -/obj/item/clothing/under/trek/engsec/orv +/obj/item/clothing/under/trek/eng/orv desc = "An uniform worn by operations officers since 2420s." - icon_state = "orv_ops" + icon_state = "orv_eng" + +/obj/item/clothing/under/trek/sec/orv + desc = "An uniform worn by security officers since 2420s." + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10) + icon_state = "orv_sec" /obj/item/clothing/under/trek/medsci/orv desc = "An uniform worn by medsci officers since 2420s." @@ -114,13 +119,20 @@ //honestly no idea why i added specified comm. uniforms but w/e /obj/item/clothing/under/trek/command/orv/captain name = "captain uniform" - desc = "An uniform worn by captains since 2550s." + desc = "An uniform worn by captains and commanders since 2550s." icon_state = "orv_com_capt" -/obj/item/clothing/under/trek/command/orv/engsec - name = "operations command uniform" - desc = "An uniform worn by operations command officers since 2550s." - icon_state = "orv_com_ops" +/obj/item/clothing/under/trek/command/orv/eng + name = "engineering command uniform" + desc = "An uniform worn by Chief Engineers since 2550s." + icon_state = "orv_com_eng" + +/obj/item/clothing/under/trek/command/orv/sec + name = "security command uniform" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 10) + desc = "An uniform worn by Heads of Security since 2550s." + icon_state = "orv_com_sec" + /obj/item/clothing/under/trek/command/orv/medsci name = "medsci command uniform" @@ -131,13 +143,11 @@ name = "adjutant uniform" desc = "An uniform worn by adjutants (assistants) since 2550s." icon_state = "orv_ass" - item_state = "gy_suit" /obj/item/clothing/under/trek/orv/service name = "service uniform" desc = "An uniform worn by service officers since 2550s." icon_state = "orv_srv" - item_state = "g_suit" //The Motion Picture /obj/item/clothing/under/trek/fedutil diff --git a/code/modules/events/cat_surgeon.dm b/code/modules/events/cat_surgeon.dm new file mode 100644 index 0000000000..d968e5e1f8 --- /dev/null +++ b/code/modules/events/cat_surgeon.dm @@ -0,0 +1,22 @@ +/datum/round_event_control/cat_surgeon + name = "Cat Surgeon" + typepath = /datum/round_event/cat_surgeon + max_occurrences = 1 + weight = 10 + + +/datum/round_event/cat_surgeon/start() + var/list/spawn_locs = list() + for(var/X in GLOB.xeno_spawn) + spawn_locs += X + + if(!spawn_locs.len) + message_admins("No valid spawn locations found, aborting...") + return MAP_ERROR + + var/turf/T = get_turf(pick(spawn_locs)) + var/mob/living/simple_animal/hostile/cat_butcherer/S = new(T) + playsound(S, 'sound/misc/catscream.ogg', 50, 1, -1) + message_admins("A cat surgeon has been spawned at [COORD(T)][ADMIN_JMP(T)]") + log_game("A cat surgeon has been spawned at [COORD(T)]") + return SUCCESSFUL_SPAWN diff --git a/code/modules/events/cat_wave.dm b/code/modules/events/cat_wave.dm new file mode 100644 index 0000000000..155da8ca08 --- /dev/null +++ b/code/modules/events/cat_wave.dm @@ -0,0 +1,11 @@ +/datum/round_event_control/meteor_wave/cat + name = "Meteor Wave: CATastrophic" + typepath = /datum/round_event/meteor_wave/cat + weight = 10 + max_occurrences = 1 + +/datum/round_event/meteor_wave/cat + wave_name = "cat" + +/datum/round_event/meteor_wave/cat/announce(fake) + priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert", "meteors") diff --git a/code/modules/events/floorcluwne.dm b/code/modules/events/floorcluwne.dm new file mode 100644 index 0000000000..3df636809a --- /dev/null +++ b/code/modules/events/floorcluwne.dm @@ -0,0 +1,23 @@ +/datum/round_event_control/floor_cluwne + name = "Floor Cluwne" + typepath = /datum/round_event/floor_cluwne + max_occurrences = 1 + min_players = 20 + weight = 10 + + +/datum/round_event/floor_cluwne/start() + var/list/spawn_locs = list() + for(var/X in GLOB.xeno_spawn) + spawn_locs += X + + if(!spawn_locs.len) + message_admins("No valid spawn locations found, aborting...") + return MAP_ERROR + + var/turf/T = get_turf(pick(spawn_locs)) + var/mob/living/simple_animal/hostile/floor_cluwne/S = new(T) + playsound(S, 'sound/misc/bikehorn_creepy.ogg', 50, 1, -1) + message_admins("A floor cluwne has been spawned at [COORD(T)][ADMIN_JMP(T)]") + log_game("A floor cluwne has been spawned at [COORD(T)]") + return SUCCESSFUL_SPAWN diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm index e69af1df13..9aa9cac258 100644 --- a/code/modules/events/meteor_wave.dm +++ b/code/modules/events/meteor_wave.dm @@ -56,6 +56,8 @@ wave_type = GLOB.meteorsC if("halloween") wave_type = GLOB.meteorsSPOOKY + if("cat") + wave_type = GLOB.meteorsCat else WARNING("Wave name of [wave_name] not recognised.") kill() diff --git a/code/modules/food_and_drinks/food.dmi b/code/modules/food_and_drinks/food.dmi new file mode 100644 index 0000000000..3f38a8068e Binary files /dev/null and b/code/modules/food_and_drinks/food.dmi differ diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index 152740f932..402192e034 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -391,6 +391,15 @@ bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1) tastes = list("meat" = 3, "smokey sauce" = 1) foodtype = MEAT + +/obj/item/reagent_containers/food/snacks/goliathcalamari + name = "goliath calamari" + desc = "ACKCHYUALLY, this isn't calamari, guys!" + icon_state = "goliathcalamari" + bonus_reagents = list(/datum/reagent/consumable/nutriment/vitamin = 3, /datum/reagent/consumable/capsaicin = 1) + list_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/nutriment/vitamin = 5, /datum/reagent/consumable/capsaicin = 2) + tastes = list("rock" = 1, "meat" = 1) + foodtype = MEAT ////////////// Cubes ////////// diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm index 2409032760..769bf1dc19 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm @@ -213,3 +213,12 @@ ) result = /obj/item/reagent_containers/food/snacks/salad/gumbo subcategory = CAT_MEAT + +/datum/crafting_recipe/food/goliathcalamari + name = "Goliath Calamari" + reqs = list( + /obj/item/reagent_containers/food/snacks/meat/slab/goliath = 2, + /datum/reagent/consumable/cooking_oil = 10 + ) + result = /obj/item/reagent_containers/food/snacks/goliathcalamari + diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index 26494dcb34..b799c5e592 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -83,7 +83,8 @@ for (var/_A in mind.antag_datums) var/datum/antagonist/A = _A - if (A.show_to_ghosts) + var/mob/dead/observer/O = user + if (A?.show_to_ghosts || !O?.can_reenter_corpse) was_antagonist = TRUE serialized["antag"] = A.name antagonists += list(serialized) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index cc13b65161..b109a1736e 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -30,7 +30,7 @@ if(skipface || get_visible_name() == "Unknown") . += "You can't make out what species they are." else - . += "[t_He] [t_is] a [dna.custom_species ? dna.custom_species : dna.species.name]!" + . += "[t_He] [t_is] a [spec_trait_examine_font()][dna.custom_species ? dna.custom_species : dna.species.name]!" //Underwear var/shirt_hidden = undershirt_hidden() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 89609c8790..0f7ff1dd2e 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1137,6 +1137,30 @@ . = ..() set_species(race) +/** + * # `spec_trait_examine_font()` + * + * This gets a humanoid's special examine font, which is used to color their species name during examine / health analyzing. + * The first of these that applies is returned. + * Returns: + * * Metallic font if robotic + * * Cyan if a toxinlover + * * Purple if plasmaperson + * * Rock / Brownish if a golem + * * Green if none of the others apply (aka, generic organic) +*/ +/mob/living/carbon/human/proc/spec_trait_examine_font() + if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM)) + return "" + if(HAS_TRAIT(src, TRAIT_TOXINLOVER)) + return "" + if(isplasmaman(src)) + return "" + + /mob/living/carbon/human/get_tooltip_data() var/t_He = p_they(TRUE) var/t_is = p_are() @@ -1145,7 +1169,7 @@ if(skipface || get_visible_name() == "Unknown") . += "You can't make out what species they are." else - . += "[t_He] [t_is] a [dna.custom_species ? dna.custom_species : dna.species.name]" + . += "[t_He] [t_is] a [spec_trait_examine_font()][dna.custom_species ? dna.custom_species : dna.species.name]" SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, usr, .) /mob/living/carbon/human/species/abductor diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index c91ad274fb..f98c791016 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -2125,6 +2125,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/turf/target_shove_turf = get_step(target.loc, shove_dir) var/mob/living/carbon/human/target_collateral_human var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied + var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND) //Thank you based whoneedsspace target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents @@ -2139,7 +2140,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/append_message = "" if(shove_blocked && !target.buckled) var/directional_blocked = !target.Adjacent(target_shove_turf) - var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND) if((directional_blocked || !(target_collateral_human || target_shove_turf.shove_act(target, user))) && !targetatrest) target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_SOLID) target.visible_message("[user.name] shoves [target.name], knocking them down!", @@ -2168,11 +2168,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) append_message += ", loosening their grip on [target_held_item]" else append_message += ", but couldn't loose their grip on [target_held_item]" - else if(target_held_item) - if(target.dropItemToGround(target_held_item)) - target.visible_message("[target.name] drops \the [target_held_item]!!", - "You drop \the [target_held_item]!!", null, COMBAT_MESSAGE_RANGE) - append_message += ", causing them to drop [target_held_item]" + else if(target.has_status_effect(STATUS_EFFECT_OFF_BALANCE)) + if(target_held_item) + if(shove_blocked) + if (target.buckled) + return + else if(target.dropItemToGround(target_held_item)) + target.visible_message("[target.name] drops \the [target_held_item]!!", + "You drop \the [target_held_item]!!", null, COMBAT_MESSAGE_RANGE) + append_message += ", causing them to drop [target_held_item]" target.ShoveOffBalance(SHOVE_OFFBALANCE_DURATION) log_combat(user, target, "shoved", append_message) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 71f0eff8e4..94fa5eff01 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -249,7 +249,7 @@ R.update_module_innate() RM.rebuild_modules() INVOKE_ASYNC(RM, .proc/do_transform_animation) - if(RM.dogborg) + if(RM.dogborg || R.dogborg) RM.dogborg_equip() R.typing_indicator_state = /obj/effect/overlay/typing_indicator/machine/dogborg else diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index fbe2e372e2..e9be6cc8a8 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -47,6 +47,8 @@ var/hack_software = FALSE //Will be able to use hacking actions var/interaction_range = 7 //wireless control range + typing_indicator_state = /obj/effect/overlay/typing_indicator/machine + /mob/living/silicon/Initialize() . = ..() GLOB.silicon_mobs += src diff --git a/code/modules/mob/living/simple_animal/hostile/cat_butcher.dm b/code/modules/mob/living/simple_animal/hostile/cat_butcher.dm index d85f75cbbd..6950c6409f 100644 --- a/code/modules/mob/living/simple_animal/hostile/cat_butcher.dm +++ b/code/modules/mob/living/simple_animal/hostile/cat_butcher.dm @@ -6,37 +6,108 @@ icon_living = "cat_butcher" icon_dead = "syndicate_dead" icon_gib = "syndicate_gib" + projectiletype = /obj/item/projectile/bullet/dart/catranq + projectilesound = 'sound/items/syringeproj.ogg' + ranged = 1 + ranged_message = "fires the syringe gun at" + ranged_cooldown_time = 30 speak_chance = 0 turns_per_move = 5 + response_help_continuous = "pokes" + response_harm_continuous = "hits" speed = 0 stat_attack = UNCONSCIOUS robust_searching = 1 maxHealth = 100 health = 100 - harm_intent_damage = 5 melee_damage_lower = 15 melee_damage_upper = 15 attack_verb_continuous = "slashes at" - attack_verb_simple = "slash at" attack_sound = 'sound/weapons/circsawhit.ogg' a_intent = INTENT_HARM - mob_biotypes = MOB_ORGANIC|MOB_HUMANOID - loot = list(/obj/effect/mob_spawn/human/corpse/cat_butcher, /obj/item/circular_saw) + mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID) + loot = list(/obj/effect/mob_spawn/human/corpse/cat_butcher, /obj/item/circular_saw, /obj/item/gun/syringe) atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 15 faction = list("hostile") check_friendly_fire = 1 status_flags = CANPUSH - del_on_death = 1 + del_on_death = TRUE + var/impatience = 0 + rapid_melee = 2 //this lets him attack nearly as fast as a normal player, instead of half as fast as one. Without this, due to mood differences, a greytider in melee can actually facetank and beat him to death with only fists + dodging = TRUE + +/mob/living/simple_animal/hostile/cat_butcherer/Life() + . = ..() + if(prob(10) && health <= maxHealth && !target) //heal himself when not in combat + var/healamount = min(maxHealth-health, 20) + visible_message("[src] medicates themself.", "You medicate yourself.") + health += healamount + +/mob/living/simple_animal/hostile/cat_butcherer/CanAttack(atom/the_target) + if(iscarbon(target)) + var/mob/living/carbon/human/C = target + if(C.getorgan(/obj/item/organ/ears/cat) && C.getorgan(/obj/item/organ/tail/cat) && C.has_trauma_type(/datum/brain_trauma/severe/pacifism))//he wont attack his creations + if(C.stat && !istype(C.dna.species, /datum/species/ipc))//unless they need healing + return ..() + else + return FALSE + return ..() /mob/living/simple_animal/hostile/cat_butcherer/AttackingTarget() - . = ..() - if(. && prob(35) && iscarbon(target)) + if(iscarbon(target)) var/mob/living/carbon/human/L = target - var/obj/item/organ/tail/cat/tail = L.getorgan(/obj/item/organ/tail/cat) - if(!QDELETED(tail)) - visible_message("[src] severs [L]'s tail in one swift swipe!", "You sever [L]'s tail in one swift swipe.") - tail.Remove() - var/obj/item/organ/tail/cat/dropped_tail = new(target.drop_location()) - dropped_tail.color = L.hair_color - return 1 + if(!L.getorgan(/obj/item/organ/ears/cat) && L.stat) //target doesnt have cat ears + if(L.getorgan(/obj/item/organ/ears)) //slice off the old ears + var/obj/item/organ/ears/ears = L.getorgan(/obj/item/organ/ears) + visible_message("[src] slices off [L]'s ears!", "You slice [L]'s ears off.") + ears.Remove(L) + ears.forceMove(get_turf(L)) + else //implant new ears + visible_message("[src] attaches a pair of cat ears to [L]!", "You attach a pair of cat ears to [L].") + var/obj/item/organ/ears/cat/newears = new + newears.Insert(L, drop_if_replaced = FALSE) + return + else if(!L.getorgan(/obj/item/organ/tail/cat) && L.stat) + if(L.getorgan(/obj/item/organ/tail)) //cut off the tail if they have one already + var/obj/item/organ/tail/tail = L.getorgan(/obj/item/organ/tail) + visible_message("[src] severs [L]'s tail in one swift swipe!", "You sever [L]'s tail in one swift swipe.") + tail.Remove(L) + tail.forceMove(get_turf(L)) + else //put a cat tail on + visible_message("[src] attaches a cat tail to [L]!", "You attach a tail to [L].") + var/obj/item/organ/tail/cat/newtail = new + newtail.Insert(L, drop_if_replaced = FALSE) + return + else if(!L.has_trauma_type(/datum/brain_trauma/severe/pacifism) && L.getorgan(/obj/item/organ/ears/cat) && L.getorgan(/obj/item/organ/tail/cat)) //still does damage. This also lacks a Stat check- felinids beware. + visible_message("[src] drills a hole in [L]'s skull!", "You pacify [L]. Another successful creation.") + if(L.stat == CONSCIOUS) + L.emote("scream") + L.gain_trauma(/datum/brain_trauma/severe/pacifism, TRAUMA_RESILIENCE_BASIC) + say("Such a GOOD KITTY!!") + if(L.mind && maxHealth <= 300) //if he robusts a tider, he becomes stronger + maxHealth += 20 + adjustHealth(-(maxHealth)) //he heals whenever he finishes + else if(L.stat) //quickly heal them up and move on to our next target! + visible_message("[src] injects [L] with an unknown medicine!", "You inject [L] with medicine.") + L.SetSleeping(0, FALSE) + L.SetUnconscious(0, FALSE) + L.adjustOxyLoss(-50)// do CPR first + if(L.blood_volume <= 500) //bandage them up and give em some blood if they're bleeding + L.blood_volume += 30 + L.bleedsuppress = 0 + if(L.getBruteLoss() >= 50)// first, did we beat them into crit? if so, heal that + var/healing = min(L.getBruteLoss(), 120) + L.adjustBruteLoss(-healing) + L.bleedsuppress = 0 //bandage their ass + return + else if(L.getFireLoss() >= 50) // are they still down from other damage? fix it, but not as fast as the burns + var/healing = min(L.getFireLoss(), 50) + L.adjustFireLoss(-healing) + impatience += 50 + if(prob(impatience)) + FindTarget()//so we don't focus on some unconscious dude when we could get our eyes on the prize + impatience = 0 + say("Bah!!") + return + return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm b/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm new file mode 100644 index 0000000000..b6d5db7d6d --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/floor_cluwne.dm @@ -0,0 +1,457 @@ +GLOBAL_VAR_INIT(floor_cluwnes, 0) + +#define STAGE_HAUNT 1 +#define STAGE_SPOOK 2 +#define STAGE_TORMENT 3 +#define STAGE_ATTACK 4 +#define MANIFEST_DELAY 9 + +/mob/living/simple_animal/hostile/floor_cluwne + name = "???" + desc = "...." + icon = 'icons/obj/clothing/masks.dmi' + icon_state = "cluwne" + icon_living = "cluwne" + icon_gib = "clown_gib" + maxHealth = 250 + health = 250 + speed = -1 + attack_verb_continuous = "attacks" + attack_verb_simple = "attack" + attack_sound = 'sound/items/bikehorn.ogg' + del_on_death = TRUE + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB | LETPASSTHROW | PASSGLASS | PASSBLOB//it's practically a ghost when unmanifested (under the floor) + loot = list(/obj/item/clothing/mask/gas/cluwne) + wander = FALSE + minimum_distance = 2 + move_to_delay = 1 + movement_type = FLYING + environment_smash = FALSE + lose_patience_timeout = FALSE + pixel_y = 8 + pressure_resistance = 200 + minbodytemp = 0 + maxbodytemp = 1500 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + var/mob/living/carbon/human/current_victim + var/manifested = FALSE + var/switch_stage = 60 + var/stage = STAGE_HAUNT + var/interest = 0 + var/target_area + var/invalid_area_typecache = list(/area/space, /area/lavaland, /area/centcom, /area/reebe, /area/shuttle/syndicate) + var/eating = FALSE + var/dontkill = FALSE //for if we just wanna curse a fucker + var/obj/effect/dummy/floorcluwne_orbit/poi + var/obj/effect/temp_visual/fcluwne_manifest/cluwnehole + move_resist = INFINITY + hud_type = /datum/hud/ghost + hud_possible = list(ANTAG_HUD) + + +/mob/living/simple_animal/hostile/floor_cluwne/Initialize() + . = ..() + access_card = new /obj/item/card/id(src) + var/datum/job/captain/C = new /datum/job/captain + access_card.access = C.get_access() + invalid_area_typecache = typecacheof(invalid_area_typecache) + Manifest() + if(!current_victim) + Acquire_Victim() + poi = new(src) + +/mob/living/simple_animal/hostile/floor_cluwne/med_hud_set_health() + return //we use a different hud + +/mob/living/simple_animal/hostile/floor_cluwne/med_hud_set_status() + return //we use a different hud + +/mob/living/simple_animal/hostile/floor_cluwne/Destroy() + QDEL_NULL(poi) + return ..() + + +/mob/living/simple_animal/hostile/floor_cluwne/attack_hand(mob/living/carbon/human/M) + ..() + playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) + + +/mob/living/simple_animal/hostile/floor_cluwne/CanPass(atom/A, turf/target) + return TRUE + + +/mob/living/simple_animal/hostile/floor_cluwne/Life() + do_jitter_animation(1000) + pixel_y = 8 + + var/area/A = get_area(src.loc) + if(is_type_in_typecache(A, invalid_area_typecache) || !is_station_level(z)) + var/area = pick(GLOB.teleportlocs) + var/area/tp = GLOB.teleportlocs[area] + forceMove(pick(get_area_turfs(tp.type))) + + if(!current_victim) + Acquire_Victim() + + if(stage && !manifested) + INVOKE_ASYNC(src, .proc/On_Stage) + + if(stage == STAGE_ATTACK) + playsound(src, 'sound/misc/cluwne_breathing.ogg', 75, 1) + + if(eating) + return + + var/turf/T = get_turf(current_victim) + A = get_area(T) + if(prob(5))//checks roughly every 20 ticks + if(current_victim.stat == DEAD || current_victim.dna.check_mutation(CLUWNEMUT) || is_type_in_typecache(A, invalid_area_typecache) || !is_station_level(current_victim.z)) + if(!Found_You()) + Acquire_Victim() + + if(get_dist(src, current_victim) > 9 && !manifested && !is_type_in_typecache(A, invalid_area_typecache))//if cluwne gets stuck he just teleports + do_teleport(src, T) + + interest++ + if(interest >= switch_stage * 4 && !dontkill) + stage = STAGE_ATTACK + + else if(interest >= switch_stage * 2) + stage = STAGE_TORMENT + + else if(interest >= switch_stage) + stage = STAGE_SPOOK + + else if(interest < switch_stage) + stage = STAGE_HAUNT + + ..() + +/mob/living/simple_animal/hostile/floor_cluwne/Goto(target, delay, minimum_distance) + var/area/A = get_area(current_victim.loc) + if(!manifested && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(current_victim.z)) + walk_to(src, target, minimum_distance, delay) + else + walk_to(src,0) + + +/mob/living/simple_animal/hostile/floor_cluwne/FindTarget() + return current_victim + + +/mob/living/simple_animal/hostile/floor_cluwne/CanAttack(atom/the_target)//you will not escape + return TRUE + + +/mob/living/simple_animal/hostile/floor_cluwne/AttackingTarget() + return + + +/mob/living/simple_animal/hostile/floor_cluwne/LoseTarget() + return + + +/mob/living/simple_animal/hostile/floor_cluwne/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)//prevents runtimes with machine fuckery + return FALSE + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Found_You() + for(var/obj/structure/closet/hiding_spot in orange(7,src)) + if(current_victim.loc == hiding_spot) + hiding_spot.bust_open() + current_victim.Paralyze(40) + to_chat(current_victim, "...edih t'nac uoY") + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Acquire_Victim(specific) + for(var/I in GLOB.player_list)//better than a potential recursive loop + var/mob/living/carbon/human/H = pick(GLOB.player_list)//so the check is fair + var/area/A + + if(specific) + H = specific + A = get_area(H.loc) + if(H.stat != DEAD && H.has_dna() && !H.dna.check_mutation(CLUWNEMUT) && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(H.z)) + return target = current_victim + + A = get_area(H.loc) + if(H && ishuman(H) && H.stat != DEAD && H != current_victim && H.has_dna() && !H.dna.check_mutation(CLUWNEMUT) && !is_type_in_typecache(A, invalid_area_typecache) && is_station_level(H.z)) + current_victim = H + interest = 0 + stage = STAGE_HAUNT + return target = current_victim + + message_admins("Floor Cluwne was deleted due to a lack of valid targets, if this was a manually targeted instance please re-evaluate your choice.") + qdel(src) + + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Manifest()//handles disappearing and appearance anim + if(manifested) + mobility_flags &= ~MOBILITY_MOVE + update_mobility() + cluwnehole = new(src.loc) + addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Appear), MANIFEST_DELAY) + else + layer = GAME_PLANE + invisibility = INVISIBILITY_OBSERVER + density = FALSE + mobility_flags |= MOBILITY_MOVE + update_mobility() + if(cluwnehole) + qdel(cluwnehole) + + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Appear()//handled in a seperate proc so floor cluwne doesn't appear before the animation finishes + layer = LYING_MOB_LAYER + invisibility = FALSE + density = TRUE + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Reset_View(screens, colour, mob/living/carbon/human/H) + if(screens) + for(var/whole_screen in screens) + animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING) + if(colour && H) + H.client.color = colour + + +/mob/living/simple_animal/hostile/floor_cluwne/proc/On_Stage() + var/mob/living/carbon/human/H = current_victim + switch(stage) + + if(STAGE_HAUNT) + + if(prob(5)) + H.blur_eyes(1) + + if(prob(5)) + H.playsound_local(src,'sound/voice/cluwnelaugh2_reversed.ogg', 1) + + if(prob(5)) + H.playsound_local(src,'sound/misc/bikehorn_creepy.ogg', 5) + + if(prob(3)) + var/obj/item/I = locate() in orange(H, 8) + if(I && !I.anchored) + I.throw_at(H, 4, 3) + to_chat(H, "What threw that?") + + if(STAGE_SPOOK) + + if(prob(4)) + var/turf/T = get_turf(H) + T.handle_slip(H, 20) + to_chat(H, "The floor shifts underneath you!") + + if(prob(5)) + H.playsound_local(src,'sound/voice/cluwnelaugh2.ogg', 2) + + if(prob(5)) + H.playsound_local(src,'sound/voice/cluwnelaugh2_reversed.ogg', 2) + + if(prob(5)) + H.playsound_local(src,'sound/misc/bikehorn_creepy.ogg', 10) + to_chat(H, "knoh") + + if(prob(5)) + var/obj/item/I = locate() in orange(H, 8) + if(I && !I.anchored) + I.throw_at(H, 4, 3) + to_chat(H, "What threw that?") + + if(prob(2)) + to_chat(H, "yalp ot tnaw I") + Appear() + manifested = FALSE + addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 1) + + if(STAGE_TORMENT) + + if(prob(5)) + var/turf/T = get_turf(H) + T.handle_slip(H, 20) + to_chat(H, "The floor shifts underneath you!") + + if(prob(3)) + playsound(src,pick('sound/spookoween/scary_horn.ogg', 'sound/spookoween/scary_horn2.ogg', 'sound/spookoween/scary_horn3.ogg'), 30, 1) + + if(prob(3)) + playsound(src,'sound/voice/cluwnelaugh1.ogg', 30, 1) + + if(prob(3)) + playsound(src,'sound/voice/cluwnelaugh2_reversed.ogg', 30, 1) + + if(prob(5)) + playsound(src,'sound/misc/bikehorn_creepy.ogg', 30, 1) + + if(prob(4)) + for(var/obj/item/I in orange(8, H)) + if(!I.anchored) + I.throw_at(H, 4, 3) + to_chat(H, "What the hell?!") + + if(prob(2)) + to_chat(H, "Something feels very wrong...") + H.playsound_local(src,'sound/hallucinations/behind_you1.ogg', 25) + H.flash_act() + + if(prob(2)) + to_chat(H, "!?REHTOMKNOH eht esiarp uoy oD") + to_chat(H, "Something grabs your foot!") + H.playsound_local(src,'sound/hallucinations/i_see_you1.ogg', 25) + H.Stun(20) + + if(prob(3)) + to_chat(H, "KNOH ?od nottub siht seod tahW") + for(var/turf/open/O in RANGE_TURFS(6, src)) + O.MakeSlippery(TURF_WET_WATER, 10) + playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1) + + if(prob(1)) + to_chat(H, "WHAT THE FUCK IS THAT?!") + to_chat(H, ".KNOH !nuf hcum os si uoy htiw gniyalP .KNOH KNOH KNOH") + H.playsound_local(src,'sound/hallucinations/im_here1.ogg', 25) + H.reagents.add_reagent(/datum/reagent/toxin/mindbreaker, 3) + H.reagents.add_reagent(/datum/reagent/consumable/laughter, 5) + H.reagents.add_reagent(/datum/reagent/mercury, 3) + Appear() + manifested = FALSE + addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Manifest), 2) + for(var/obj/machinery/light/L in range(8, H)) + L.flicker() + + + if(STAGE_ATTACK) + if(dontkill) + stage = STAGE_TORMENT + return + if(!eating) + Found_You() + for(var/I in getline(src,H)) + var/turf/T = I + if(T.density) + forceMove(H.loc) + for(var/obj/structure/O in T) + if(O.density || istype(O, /obj/machinery/door/airlock)) + forceMove(H.loc) + to_chat(H, "You feel the floor closing in on your feet!") + H.Paralyze(300) + INVOKE_ASYNC(H, /mob.proc/emote, "scream") + H.adjustBruteLoss(10) + manifested = TRUE + Manifest() + if(!eating) + addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Grab, H), 50, TIMER_OVERRIDE|TIMER_UNIQUE) + for(var/turf/open/O in RANGE_TURFS(6, src)) + O.MakeSlippery(TURF_WET_LUBE, 20) + playsound(src, 'sound/effects/meteorimpact.ogg', 30, 1) + eating = TRUE + + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Grab(mob/living/carbon/human/H) + if (H != current_victim) + message_admins("Cluwne tried to grab someone who's not the target. Returning to life stage.") + return + to_chat(H, "You feel a cold, gloved hand clamp down on your ankle!") + for(var/I in 1 to get_dist(src, H)) + if(do_after(src, 5, target = H)) + step_towards(H, src) + playsound(H, pick('sound/effects/bodyscrape-01.ogg', 'sound/effects/bodyscrape-02.ogg'), 20, 1, -4) + if(prob(40)) + H.emote("scream") + else if(prob(25)) + H.say(pick("HELP ME!!","IT'S GOT ME!!","DON'T LET IT TAKE ME!!",";SOMETHING'S KILLING ME!!","HOLY FUCK!!")) + playsound(src, pick('sound/voice/cluwnelaugh1.ogg', 'sound/voice/cluwnelaugh2.ogg', 'sound/voice/cluwnelaugh3.ogg'), 50, 1) + + if(get_dist(src,H) <= 1) + visible_message("[src] begins dragging [H] under the floor!") + if(do_after(src, 50, target = H) && eating) + H.become_blind() + H.layer = GAME_PLANE + H.invisibility = INVISIBILITY_OBSERVER + H.density = FALSE + H.anchored = TRUE + addtimer(CALLBACK(src, /mob/living/simple_animal/hostile/floor_cluwne/.proc/Kill, H), 100, TIMER_OVERRIDE|TIMER_UNIQUE) + visible_message("[src] pulls [H] under!") + to_chat(H, "[src] drags you underneath the floor!") + else + eating = FALSE + else + eating = FALSE + manifested = FALSE + Manifest() + + +/mob/living/simple_animal/hostile/floor_cluwne/proc/Kill(mob/living/carbon/human/H) + if (H != current_victim) + message_admins("Cluwne tried to kill someone who's not the target. Returning to life stage.") + H.invisibility = initial(H.invisibility) + return + if(!istype(H) || !H.client) + H.invisibility = initial(H.invisibility) + Acquire_Victim() + return + playsound(H, 'sound/effects/cluwne_feast.ogg', 100, 0, -4) + var/old_color = H.client.color + var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0) + var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0) + H.client.color = pure_red + animate(H.client,color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT) + for(var/turf/T in orange(H, 4)) + H.add_splatter_floor(T) + if(do_after(src, 50, target = H)) + H.unequip_everything()//more runtime prevention + if(prob(75)) + H.gib(FALSE) + else + H.cluwneify() + H.adjustBruteLoss(30) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 100, 100) + H.cure_blind() + H.layer = initial(H.layer) + H.invisibility = initial(H.invisibility) + H.density = initial(H.density) + H.anchored = initial(H.anchored) + H.blur_eyes(10) + animate(H.client,color = old_color, time = 20) + + eating = FALSE + switch_stage = switch_stage * 0.75 //he gets faster after each feast + for(var/mob/M in GLOB.player_list) + M.playsound_local(get_turf(M), 'sound/misc/honk_echo_distant.ogg', 50, 1, pressure_affected = FALSE) + + interest = 0 + stage = STAGE_HAUNT + Acquire_Victim() + +//manifestation animation +/obj/effect/temp_visual/fcluwne_manifest + icon = 'icons/turf/floors.dmi' + icon_state = "fcluwne_open" + layer = TURF_LAYER + duration = 600 + randomdir = FALSE + +/obj/effect/temp_visual/fcluwne_manifest/Initialize() + . = ..() + playsound(src, 'sound/misc/floor_cluwne_emerge.ogg', 100, 1) + flick("fcluwne_manifest",src) + +/obj/effect/dummy/floorcluwne_orbit + name = "floor cluwne" + desc = "If you have this, tell a coder or admin!" + +/obj/effect/dummy/floorcluwne_orbit/Initialize() + . = ..() + GLOB.floor_cluwnes++ + name += " ([GLOB.floor_cluwnes])" + GLOB.poi_list += src + +/obj/effect/dummy/floorcluwne_orbit/Destroy() + . = ..() + GLOB.poi_list -= src + +#undef STAGE_HAUNT +#undef STAGE_SPOOK +#undef STAGE_TORMENT +#undef STAGE_ATTACK +#undef MANIFEST_DELAY diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index 3513a916f9..600222df68 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -6,6 +6,7 @@ var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while var/SStun = 0 // stun variable + typing_indicator_state = /obj/effect/overlay/typing_indicator/slime /mob/living/simple_animal/slime/BiologicalLife(seconds, times_fired) if(!(. = ..())) diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 159d370b92..570f01f5d9 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -55,3 +55,12 @@ GLOBAL_LIST_EMPTY(typing_indicator_overlays) icon_state = "normal_typing" appearance_flags = RESET_COLOR | TILE_BOUND | PIXEL_SCALE layer = ABOVE_FLY_LAYER + +/obj/effect/overlay/typing_indicator/machine + icon_state = "machine_typing" + +/obj/effect/overlay/typing_indicator/machine/dogborg + icon = 'icons/mob/talk_64x64.dmi' + +/obj/effect/overlay/typing_indicator/slime + icon_state = "slime_typing" diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm index 8380bf82e9..821f6d002f 100644 --- a/code/modules/projectiles/ammunition/special/magic.dm +++ b/code/modules/projectiles/ammunition/special/magic.dm @@ -35,6 +35,9 @@ /obj/item/ammo_casing/magic/spellblade projectile_type = /obj/item/projectile/magic/spellblade +/obj/item/ammo_casing/magic/judgement_cut + projectile_type = /obj/item/projectile/judgement_cut + /obj/item/ammo_casing/magic/arcane_barrage projectile_type = /obj/item/projectile/magic/arcane_barrage diff --git a/code/modules/projectiles/guns/magic/motivation.dm b/code/modules/projectiles/guns/magic/motivation.dm new file mode 100644 index 0000000000..0eaeea4418 --- /dev/null +++ b/code/modules/projectiles/guns/magic/motivation.dm @@ -0,0 +1,91 @@ +/obj/item/gun/magic/staff/motivation + name = "Motivation" + desc = "Rumored to have the ability to open up a portal the depths of Lavaland." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "motivation" + item_state = "motivation" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + fire_sound = 'sound/weapons/judgementhit.ogg' + ammo_type = /obj/item/ammo_casing/magic/judgement_cut + force = 20 //so it's worth that 20 tc + armour_penetration = 50 + w_class = WEIGHT_CLASS_NORMAL + slot_flags = ITEM_SLOT_BELT + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + sharpness = SHARP_EDGED + max_integrity = 200 + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + max_charges = 3 + recharge_rate = 5 + var/datum/action/judgement_cut/judgementcut = new/datum/action/judgement_cut() + block_parry_data = /datum/block_parry_data/motivation + +//to get this to toggle correctly +/obj/item/gun/magic/staff/motivation/Initialize() + . = ..() + judgementcut = new(src) + +//lets the user know that their judgment cuts are recharging +/obj/item/gun/magic/staff/motivation/shoot_with_empty_chamber(mob/living/user as mob|obj) + to_chat(user, "Judgment Cut is recharging.") + +//action button to toggle judgement cuts on/off +/datum/action/judgement_cut + name = "Judgement Cut - Allows Motivation to slash at a longer distance." + icon_icon = 'icons/obj/projectiles.dmi' + button_icon_state = "judgement_fire" + var/judgement_toggled = TRUE + +//lets the user know that you toggled them on/off +/datum/action/judgement_cut/Trigger() + judgement_toggled = !judgement_toggled + to_chat(owner, "You [judgement_toggled ? "enable" : "disable"] Judgement Cuts with Motivation.") + +//Prevents "firing" the judgement cuts if toggled off and lets the user know +/obj/item/gun/magic/staff/motivation/can_trigger_gun(mob/living/user) + . = ..() + if(!judgementcut.judgement_toggled) + to_chat(user, " Judgment Cut is disabled.") + return FALSE + +//adds/removes judgement cut and judgement cut end upon pickup/drop +/obj/item/gun/magic/staff/motivation/pickup(mob/living/user) + . = ..() + judgementcut.Grant(user, src) + user.update_icons() + playsound(src, 'sound/items/unsheath.ogg', 25, 1) + +/obj/item/gun/magic/staff/motivation/dropped(mob/user) + . = ..() + judgementcut.Remove(user) + user.update_icons() + +//A parry tight enough to stagger, but not to counter attack +/datum/block_parry_data/motivation + parry_time_windup = 0.5 + parry_time_active = 5 + parry_time_spindown = 0 + parry_attack_types = ALL + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 2 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + parry_time_perfect = 0 + parry_time_perfect_leeway = 3 + parry_time_perfect_leeway_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 1 + ) + parry_imperfect_falloff_percent_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 50 // useless after 3rd decisecond + ) + parry_imperfect_falloff_percent = 30 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 1 + parry_efficiency_perfect = 100 + parry_data = list( + PARRY_STAGGER_ATTACKER = 10 + ) + parry_failed_stagger_duration = 2 SECONDS + parry_failed_clickcd_duration = CLICK_CD_RANGE + parry_cooldown = 0 diff --git a/code/modules/projectiles/projectile/bullets/dart_syringe.dm b/code/modules/projectiles/projectile/bullets/dart_syringe.dm index 25809cc7ca..bc2a7c40fb 100644 --- a/code/modules/projectiles/projectile/bullets/dart_syringe.dm +++ b/code/modules/projectiles/projectile/bullets/dart_syringe.dm @@ -38,6 +38,10 @@ reagents.add_reagent(/datum/reagent/foaming_agent, 5) reagents.add_reagent(/datum/reagent/toxin/acid, 5) +/obj/item/projectile/bullet/dart/catranq/Initialize() + . = ..() + reagents.add_reagent(/datum/reagent/fermi/furranium, 5) // Turns out I don't even need to give this guy actual tranquilizer chems. + /obj/item/projectile/bullet/dart/syringe name = "syringe" icon_state = "syringeproj" diff --git a/code/modules/projectiles/projectile/special/judgement_cut.dm b/code/modules/projectiles/projectile/special/judgement_cut.dm new file mode 100644 index 0000000000..275bd54a80 --- /dev/null +++ b/code/modules/projectiles/projectile/special/judgement_cut.dm @@ -0,0 +1,15 @@ +/obj/item/projectile/judgement_cut + name = "judgement cut" + icon_state = "judgement_fire" + hitsound = 'sound/weapons/judgementfire.ogg' + damage = 10 + damage_type = BRUTE + range = 30 + is_reflectable = FALSE + sharpness = SHARP_EDGED + impact_effect_type = /obj/effect/temp_visual/impact_effect/judgement_cut + +/obj/item/projectile/judgement_cut/on_hit(atom/target, blocked = FALSE) + . = ..() + if(ishuman(target)) + new /obj/effect/temp_visual/impact_effect/judgement_cut(src) diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 6eb0d5825c..1e9e1aa679 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -741,6 +741,13 @@ required_reagents = list(/datum/reagent/liquid_dark_matter = 5, /datum/reagent/medicine/synaptizine = 10, /datum/reagent/medicine/oculine = 10, /datum/reagent/mutationtoxin = 1) required_temp = 600 +/datum/chemical_reaction/slimejelly + name = "slimejelly" + results = list(/datum/reagent/toxin/slimejelly = 5) + required_reagents = list(/datum/reagent/oil = 3, /datum/reagent/radium = 2, /datum/reagent/consumable/tinlux = 1) + required_container = /obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom + mix_message = "The mushroom's insides bubble and pop and it becomes very limp." + /datum/chemical_reaction/slime_extractification required_reagents = list(/datum/reagent/toxin/slimejelly = 30, /datum/reagent/consumable/frostoil = 5, /datum/reagent/toxin/plasma = 5) mix_message = "The mixture condenses into a ball." diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 74ac0fc166..6044e8131d 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -27,8 +27,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( /obj/machinery/syndicatebomb, /obj/item/hilbertshotel, /obj/machinery/launchpad, - /obj/machinery/disposal, - /obj/structure/disposalpipe, /obj/item/hilbertshotel, /obj/machinery/camera, /obj/item/gps, diff --git a/code/modules/spells/spell_types/cluwnecurse.dm b/code/modules/spells/spell_types/cluwnecurse.dm new file mode 100644 index 0000000000..2da87b9e50 --- /dev/null +++ b/code/modules/spells/spell_types/cluwnecurse.dm @@ -0,0 +1,36 @@ +/obj/effect/proc_holder/spell/targeted/cluwnecurse + name = "Curse of the Cluwne" + desc = "This spell dooms the fate of any unlucky soul to the live of a pitiful cluwne, a terrible creature that is hunted for fun." + school = "transmutation" + charge_type = "recharge" + charge_max = 600 + charge_counter = 0 + clothes_req = SPELL_WIZARD_GARB + stat_allowed = 0 + invocation = "CLU WO'NIS CA'TE'BEST'IS MAXIMUS!" + invocation_type = "shout" + range = 3 + cooldown_min = 75 + selection_type = "range" + var/list/compatible_mobs = list(/mob/living/carbon/human) + action_icon = 'icons/mob/actions/actions_spells.dmi' + action_icon_state = "cluwne" + +/obj/effect/proc_holder/spell/targeted/cluwnecurse/cast(list/targets, mob/user = usr) + if(!targets.len) + to_chat(user, "No target found in range.") + return + var/mob/living/carbon/target = targets[1] + if(!(target.type in compatible_mobs)) + to_chat(user, "You are unable to curse [target]!") + return + if(!(target in oview(range))) + to_chat(user, "They are too far away!") + return + var/mob/living/carbon/human/H = target + H.cluwneify() + +/datum/action/spell_action/New(Target) + ..() + var/obj/effect/proc_holder/spell/S = Target + icon_icon = S.action_icon diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 168a12a2ef..d595c494b5 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -214,6 +214,7 @@ switch(animal_origin) if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take some additional burn //nothing can burn with so much snowflake code around burn *= 1.2 + stamina = 0 /* // START WOUND HANDLING diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm index de15b16b68..064d1bb429 100644 --- a/code/modules/uplink/uplink_items/uplink_clothing.dm +++ b/code/modules/uplink/uplink_items/uplink_clothing.dm @@ -103,7 +103,7 @@ name = "Mechanical Eyepatch" desc = "An eyepatch that connects itself to your eye socket, enhancing your shooting to an impossible degree, allowing your bullets to ricochet far more often than usual." item = /obj/item/clothing/glasses/eyepatch/syndicate - cost = 8 + cost = 4 /datum/uplink_item/device_tools/ablative_armwraps name = "Ablative Armwraps" diff --git a/code/modules/uplink/uplink_items/uplink_dangerous.dm b/code/modules/uplink/uplink_items/uplink_dangerous.dm index 948d17d168..f13d11736f 100644 --- a/code/modules/uplink/uplink_items/uplink_dangerous.dm +++ b/code/modules/uplink/uplink_items/uplink_dangerous.dm @@ -189,7 +189,7 @@ desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an \ organic host as a home base and source of fuel. Holoparasites come in various types and share damage with their host." item = /obj/item/storage/box/syndie_kit/guardian - cost = 15 + cost = 12 limited_stock = 1 // you can only have one holopara apparently? refundable = TRUE cant_discount = TRUE @@ -204,7 +204,7 @@ desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an \ organic host as a home base and source of fuel. Holoparasites come in various types and share damage with their host." item = /obj/item/storage/box/syndie_kit/nukieguardian - cost = 15 + cost = 8 refundable = TRUE surplus = 50 refund_path = /obj/item/guardiancreator/tech/choose/nukie @@ -286,3 +286,13 @@ item = /obj/item/gun/ballistic/automatic/toy/pistol/riot cost = 3 surplus = 10 + +/datum/uplink_item/dangerous/motivation + name = "Motivation" + desc = "An ancient blade said to have ties with Lavaland's most inner demons. \ + Allows you to cut from a far distance!" + item = /obj/item/gun/magic/staff/motivation + cost = 20 + player_minimum = 20 + exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/traitor/internal_affairs) + cant_discount = TRUE diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm index 4545fab2a7..08a7986fa3 100644 --- a/code/modules/vehicles/cars/car.dm +++ b/code/modules/vehicles/cars/car.dm @@ -76,11 +76,15 @@ return FALSE if(occupant_amount() >= max_occupants) return FALSE - if(do_mob(forcer, get_enter_delay(M), target = src)) + var/atom/old_loc = loc + if(do_mob(forcer, M, get_enter_delay(M), extra_checks=CALLBACK(src, /obj/vehicle/sealed/car/proc/is_car_stationary, old_loc))) mob_forced_enter(M, silent) return TRUE return FALSE +/obj/vehicle/sealed/car/proc/is_car_stationary(atom/old_loc) + return (old_loc == loc) + /obj/vehicle/sealed/car/proc/mob_forced_enter(mob/M, silent = FALSE) if(!silent) M.visible_message("[M] is forced into \the [src]!") diff --git a/html/changelog.html b/html/changelog.html index 9755afad55..468865d453 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,70 @@ -->
+

10 June 2021

+

Arturlang updated:

+
    +
  • Holoparasites for traitors now cost 12 crystals, for operatives 8, the ricochet eyepath traitor item now 4.
  • +
+

DrPainis updated:

+
    +
  • goliath calamari
  • +
  • cat meteors
  • +
+

Linzolle updated:

+
    +
  • cults can build in maintenance (and other small areas) again.
  • +
  • centcom can no longer be selected as the target area for narsie to be summoned??????
  • +
+

MrJWhit updated:

+
    +
  • Moves medical holodeck to the restricted category
  • +
+

SandPoot updated:

+
    +
  • Uses some of the existing images for the typing indicators.
  • +
  • Fixes soulstone shard not working for non-cultists.
  • +
+

WanderingFox95 updated:

+
    +
  • A random event for the cat surgeon to invade the station. Listen for scary noises!
  • +
  • Screaming Cat SFX, you know, for the mood.
  • +
+

bunny232 updated:

+
    +
  • Atmos resin now properly prevents all atmos from moving
  • +
  • Air tanks now properly have a 21/79 o2/n2 mix
  • +
  • Hydroponics can now make 5u of slimejelly by injecting 3 oil, 2 radium and 1 tinea luxor into a glowshroom
  • +
+

keronshb updated:

+
    +
  • Made it so Off Balance only disarms if they're shoved into a wall or person.
  • +
  • Reduced Off Balance time to 2 seconds.
  • +
  • Pierced Realities despawn after 3 minutes and 15 seconds, new unresearched realities spawn in after that time elsewhere to help other heretics get back into the game.
  • +
  • A required sacrifice amount for heretic's second to last and last powers are added to discourage only rushing for holes.
  • +
  • An announcement automatically plays to everyone that there's a heretic gunning for ascension upon learning the 2nd to last power
  • +
  • Blade Shatters are now used in hand other than with a HUD icon tweak: Adjusted some TGUI menus for the book to reflect how many sacrifices a heretic has and how many are required for certain powers
  • +
  • Fixes sprite issue for Void Cloak for people who have digigrade legs.
  • +
  • Fixes the Raw Prophet recipe to match the description
  • +
  • Lets the Cargo Shuttle use Disposal Pipes again
  • +
  • Adds motivation and adds it to the uplink
  • +
  • Adds Judgement Cut projectiles
  • +
  • Adds Judgement Cut hit effects and firing effects.
  • +
  • added sounds for the firing and hit sounds of Judgement Cuts, created by @dzahlus
  • +
  • Adds Floor Cluwnes and event for midround
  • +
  • Adds Cluwne mutation
  • +
  • Adds Cluwne spell
  • +
  • Adds Cluwnes
  • +
  • Adds Cluwne Mask + shoes
  • +
  • Adds Floor Cluwne spawn button
  • +
  • Adds Cluwne smite button
  • +
+

zeroisthebiggay updated:

+
    +
  • Fixed an exploit allowing you to grab people from anywhere with a clowncar.
  • +
  • revenant essence objective reduced
  • +
+

06 June 2021

bunny232 updated: