diff --git a/ModularTegustation/Teguicons/224x128.dmi b/ModularTegustation/Teguicons/224x128.dmi
index 819aa03181de..c5f0545ffa0b 100644
Binary files a/ModularTegustation/Teguicons/224x128.dmi and b/ModularTegustation/Teguicons/224x128.dmi differ
diff --git a/ModularTegustation/Teguicons/64x64.dmi b/ModularTegustation/Teguicons/64x64.dmi
index 194d51aba53f..25b26371e688 100644
Binary files a/ModularTegustation/Teguicons/64x64.dmi and b/ModularTegustation/Teguicons/64x64.dmi differ
diff --git a/ModularTegustation/Teguicons/tegu_effects10x10.dmi b/ModularTegustation/Teguicons/tegu_effects10x10.dmi
index 2192969662a6..c5892eb167bc 100644
Binary files a/ModularTegustation/Teguicons/tegu_effects10x10.dmi and b/ModularTegustation/Teguicons/tegu_effects10x10.dmi differ
diff --git a/ModularTegustation/Teguicons/tegumobs.dmi b/ModularTegustation/Teguicons/tegumobs.dmi
index 45e634f47bbb..60b3c6707137 100644
Binary files a/ModularTegustation/Teguicons/tegumobs.dmi and b/ModularTegustation/Teguicons/tegumobs.dmi differ
diff --git a/ModularTegustation/_adventure_console/adventure_layout.dm b/ModularTegustation/_adventure_console/adventure_layout.dm
index 709d0eccf1f4..9f2b1daa620b 100644
--- a/ModularTegustation/_adventure_console/adventure_layout.dm
+++ b/ModularTegustation/_adventure_console/adventure_layout.dm
@@ -2,6 +2,7 @@
* TEXT BASED ADVENTURES
* Adventures that are mostly predefined paths.
* This was difficult to finalize since i havent made a text based adventure before.
+ * Special defines such as button macros are in the code/_DEFINES/~lobotomy_defines.dm
*/
/datum/adventure_layout
@@ -160,7 +161,7 @@
/datum/adventure_layout/proc/TravelUI(obj/machinery/call_machine)
switch(travel_mode)
- if(ADVENTURE_MODE_BATTLE to ADVENTURE_MODE_EVENT_BATTLE)
+ if(ADVENTURE_MODE_BATTLE, ADVENTURE_MODE_EVENT_BATTLE)
if(!enemy_desc)
GenerateEnemy()
. += BattleModeDisplay(call_machine)
@@ -259,7 +260,7 @@
/*I put || in here and the code got upset so i have to do TO instead
Eugh maybe we will fix it later on if adventure mode event battle stops being 3. -IP*/
- if(ADVENTURE_MODE_BATTLE to ADVENTURE_MODE_EVENT_BATTLE)
+ if(ADVENTURE_MODE_BATTLE, ADVENTURE_MODE_EVENT_BATTLE)
BattleModeReact(num)
//Reactions for adventure based on mode.
@@ -291,6 +292,9 @@
temp_text += "
YOU RUN AWAY FROM YOUR OPPONENT
5 DAMAGE HEALED
EVENT PROGRESS -5
"
AdjustHP(5)
AdjustProgress(-5)
+ paths_to_tread.Cut()
+ enemy_desc = null
+ travel_mode = ADVENTURE_MODE_TRAVEL
else
DoBattle(0)
temp_text += "
YOU FAIL TO ESCAPE THE ENEMY
"
diff --git a/ModularTegustation/_adventure_console/console.dm b/ModularTegustation/_adventure_console/console.dm
index dadc301ffe01..8532836caab1 100644
--- a/ModularTegustation/_adventure_console/console.dm
+++ b/ModularTegustation/_adventure_console/console.dm
@@ -9,12 +9,13 @@
density = TRUE
//Keepin it simple and soft coded.
var/datum/adventure_layout/adventure_data
+ //Saved profiles that are password locked
+ var/list/profile_list = list()
//Console with debug text adventure program for testing.
/obj/machinery/text_adventure_console/debug/Initialize()
. = ..()
- if(!adventure_data)
- adventure_data = new(TRUE)
+ NewProfile(TRUE)
//Stolen from nanite_program_hub.dm
/obj/machinery/text_adventure_console/update_overlays()
@@ -28,9 +29,12 @@
if(isliving(user))
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
var/dat
- if(!adventure_data)
- adventure_data = new
- dat += adventure_data.Adventure(src, user)
+ if(adventure_data)
+ dat += adventure_data.Adventure(src, user)
+ dat += "
LOG OUT
\
+ CREATE PASSWORD"
+ else
+ dat += ProfileMenu()
var/datum/browser/popup = new(user, "Adventure", "AdventureTest", 500, 600)
popup.set_content(dat)
popup.open()
@@ -47,6 +51,39 @@
usr.set_machine(src)
add_fingerprint(usr)
+ //Profile Selection
+ if(href_list["profile"])
+ var/set_profile = profile_list[text2num(href_list["profile"])]
+ if(!set_profile)
+ updateUsrDialog()
+ return TRUE
+ if(profile_list[set_profile] != null)
+ var/input_password = input(usr, "PLEASE TYPE IN PASSWORD", "PASSWORD INPUT") as null|num
+ if(input_password != profile_list[set_profile])
+ to_chat(usr, span_notice("INCORRECT PASSWORD"))
+ return TRUE
+ adventure_data = set_profile
+ updateUsrDialog()
+ return TRUE
+
+ if(href_list["log_out_profile"])
+ adventure_data = null
+ updateUsrDialog()
+ return TRUE
+
+ if(href_list["new_profile"])
+ NewProfile()
+ updateUsrDialog()
+ return TRUE
+
+ if(href_list["new_profile_password"])
+ var/new_password = input(usr, "PLEASE TYPE IN 3 DIGIT PASSWORD", "PASSWORD INPUT") as null|num
+ if(new_password)
+ profile_list[adventure_data] = clamp(new_password,100,999)
+ to_chat(usr, span_notice("PASSWORD SUCCESSFULLY CHANGED TO [profile_list[adventure_data]]"))
+ updateUsrDialog()
+ return TRUE
+
//Setting display menu for the text adventure.
if(href_list["set_display"])
var/set_display = text2num(href_list["set_display"])
@@ -110,3 +147,26 @@
playsound(get_turf(src), 'sound/machines/pda_button2.ogg', 50, TRUE)
updateUsrDialog()
return TRUE
+
+/obj/machinery/text_adventure_console/proc/ProfileMenu(href, href_list)
+ . = "\
+ -------------------
\
+ |PROFILE SELECTION|
\
+ -------------------
"
+ var/profile_num = 1
+ for(var/i in profile_list)
+ if(i)
+ . += "|PROFILE [profile_num]
"
+ profile_num++
+
+ . += "|NEW PROFILE
\
+ -----------------"
+
+/obj/machinery/text_adventure_console/proc/NewProfile(debug_profile = FALSE)
+ if(profile_list.len > 4)
+ to_chat(usr, span_notice("MAXIMUM PROFILES REACHED"))
+ return
+ var/new_profile = new /datum/adventure_layout(debug_profile)
+ profile_list += new_profile
+ profile_list[new_profile] = null
+ adventure_data = new_profile
diff --git a/ModularTegustation/lc13_machinery.dm b/ModularTegustation/lc13_machinery.dm
index b7c3f672f1fc..6e688eac8f1e 100644
--- a/ModularTegustation/lc13_machinery.dm
+++ b/ModularTegustation/lc13_machinery.dm
@@ -129,3 +129,180 @@
if(C.linked_console)
LAZYADD(abnormalities, "[C.AbnormalityInfo()]: [C.relative_location]")
sortList(abnormalities)
+
+ /*---------------\
+ |Torso Fabricator|
+ \---------------*/
+#define ANIMATE_FABRICATOR_ACTIVE flick("fab_robot_a", src)
+/*
+* When someone who has the time to convert tegu cloners
+* into ours you can remove this code. -IP
+*/
+/obj/machinery/body_fabricator
+ name = "torso fabricator"
+ desc = "A fabricator for constructing humanoid bodies for the bodiless. Place a brain inside and activate! -NO REFUNDS-."
+ icon = 'icons/mob/hivebot.dmi'
+ icon_state = "fab_robot"
+ density = TRUE
+ layer = BELOW_OBJ_LAYER
+ use_power = NO_POWER_USE
+ var/active = FALSE
+ var/stored_money = 0
+ var/prosthetic_cost = 300
+ var/organic_cost = 1200
+ var/obj/item/organ/brain/slotted_brain
+
+/obj/machinery/body_fabricator/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/holochip))
+ var/obj/item/holochip/H = I
+ var/ahn_amount = H.get_item_credit_value()
+ H.spend(ahn_amount)
+ AdjustMoney(ahn_amount)
+ return
+
+ if(!slotted_brain)
+ if(istype(I, /obj/item/bodypart/head))
+ var/obj/item/bodypart/head/heed = I
+ if(heed.brain)
+ SlottedHead(heed)
+ return
+ if(istype(I, /obj/item/organ/brain))
+ var/obj/item/organ/brain/B = I
+ SlottedBrain(B)
+ return
+ ..()
+
+/obj/machinery/body_fabricator/ui_interact(mob/user)
+ . = ..()
+ if(isliving(user))
+ playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ var/dat
+ dat += "FABRICATION_FUNDS: [stored_money]
----------------------
"
+ if(slotted_brain)
+ if(slotted_brain)
+ dat += "BRAIN DETECTED|
--
"
+ dat += " PRINT PROSTHETIC TORSO: [prosthetic_cost] AHN:
"
+ dat += " Areas of the body have been replaced with scrap prosthetics. Clients have claimed to suffer a small attribute decrease.
"
+ dat += " PRINT ORGANIC TORSO: [organic_cost] AHN
"
+ dat += " Through undisclosed means we will print you a new torso with no attribute decay.
"
+ else
+ dat += "NO BRAIN DETECTED|
--
"
+ var/datum/browser/popup = new(user, "body_fab", "body fabricator", 500, 550)
+ popup.set_content(dat)
+ popup.open()
+ return
+
+/obj/machinery/body_fabricator/Topic(href, href_list)
+ . = ..()
+ if(.)
+ return .
+ if(ishuman(usr))
+ usr.set_machine(src)
+ add_fingerprint(usr)
+ if(href_list["PRINT_PROSTHETIC"])
+ if(stored_money < prosthetic_cost)
+ return
+ ConstructTorso(2)
+ AdjustMoney(-prosthetic_cost)
+ updateUsrDialog()
+ return TRUE
+ if(href_list["PRINT_ORGANIC"])
+ if(stored_money < organic_cost)
+ return
+ ConstructTorso(1)
+ AdjustMoney(-organic_cost)
+ updateUsrDialog()
+ return TRUE
+
+/obj/machinery/body_fabricator/proc/AdjustMoney(amount)
+ stored_money += amount
+
+/*
+* In Library of Ruina there is a fixer that has their body
+* damaged by clowns so their coworkers behead them and take
+* them to get a new body cloned for them. That is the
+* inspiration for the torso fabricator.
+*/
+/obj/machinery/body_fabricator/proc/SlottedBrain(obj/item/organ/brain/B)
+ if(slotted_brain)
+ return FALSE
+ slotted_brain = B
+ B.forceMove(src)
+ return TRUE
+
+/obj/machinery/body_fabricator/proc/SlottedHead(obj/item/bodypart/head/H)
+ if(slotted_brain)
+ return FALSE
+ if(!H.brain)
+ return FALSE
+ slotted_brain = H.brain
+ H.drop_organs()
+ slotted_brain.forceMove(src)
+ qdel(H)
+ return TRUE
+
+/*
+* Okay so when your gibbed your head contains your brainmob
+* but when your brain is cut out of the head the brain now
+* contains the brainmob. The brainmob is the one who has
+* the previous owners dna stored in it.
+*/
+/obj/machinery/body_fabricator/proc/ConstructTorso(biotype = 1)
+ playsound(get_turf(src), 'sound/machines/click.ogg', 10, TRUE)
+ var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
+ //YOU DIDNT PAY FOR LIMBS
+ RemoveAllLimbs(H)
+
+ //DNA TRANSFER GO!!!
+ if(slotted_brain)
+ var/mob/living/brain/B = locate(/mob/living/brain) in slotted_brain
+ var/datum/dna/gibbed_dna = B.stored_dna
+ if(gibbed_dna)
+ H.real_name = gibbed_dna.real_name
+ gibbed_dna.transfer_identity(H)
+
+ //BRAIN INSERTION
+ if(slotted_brain)
+ slotted_brain.Insert(H)
+
+ //REVIVE
+ H.revive(full_heal = FALSE, admin_revive = FALSE)
+ H.emote("gasp")
+ H.Jitter(100)
+
+ //YOU DIDNT PAY FOR PREMIUM SO WE ARE MAKING YOUR BODY WORSE
+ if(biotype == 2)
+ RoboticizeBody(H)
+ H.adjust_all_attribute_levels(-5)
+ H.updateappearance()
+ DumpBody(H)
+
+/obj/machinery/body_fabricator/proc/RoboticizeBody(mob/living/carbon/human/H)
+ var/obj/item/bodypart/head/robot/robohead = new /obj/item/bodypart/head/robot(src)
+ var/old_head = H.get_bodypart(BODY_ZONE_HEAD)
+ robohead.replace_limb(H)
+ qdel(old_head)
+
+ var/obj/item/bodypart/chest/robot/robobody = new /obj/item/bodypart/chest/robot(src)
+ var/refuse = H.get_bodypart(BODY_ZONE_CHEST)
+ robobody.replace_limb(H)
+ qdel(refuse)
+
+/obj/machinery/body_fabricator/proc/RemoveAllLimbs(mob/living/carbon/human/H)
+ var/static/list/zones = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
+ for(var/zone in zones)
+ var/obj/item/bodypart/BP = H.get_bodypart(zone)
+ if(BP)
+ BP.drop_limb()
+ qdel(BP)
+
+/obj/machinery/body_fabricator/proc/DumpBody(mob/living/carbon/human/dude)
+ slotted_brain = null
+ ANIMATE_FABRICATOR_ACTIVE
+ playsound(get_turf(src), 'sound/effects/cashregister.ogg', 35, 3, 3)
+ sleep(32)
+ playsound(get_turf(src), 'sound/effects/bin_close.ogg', 35, 3, 3)
+ playsound(get_turf(src), 'sound/misc/splort.ogg', 35, 3, 3)
+ dude.forceMove(get_turf(src))
+
+#undef ANIMATE_FABRICATOR_ACTIVE
diff --git a/ModularTegustation/tegu_items/lc13boss_summon.dm b/ModularTegustation/tegu_items/lc13boss_summon.dm
new file mode 100644
index 000000000000..a409f55e5d80
--- /dev/null
+++ b/ModularTegustation/tegu_items/lc13boss_summon.dm
@@ -0,0 +1,193 @@
+
+#define NORMAL_MODE 0
+#define ACTIVE_MODE 1
+#define REWARD_MODE 2
+
+//LC13 Boss Books
+/obj/item/lor_boss_book
+ name = "book of gibberish"
+ desc = "A collection of pages bound by some sort of leather or metal. This object has a mystical aura to it."
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "lor"
+ //If the book is currently releasing a monster or if the next interaction will give a reward.
+ var/book_mode = NORMAL_MODE
+ //If we spawn at a far away place.
+ var/distant_spawn = TRUE
+ //Glowing animation
+ var/glow_animation = icon('icons/obj/bureaucracy.dmi', "lor_glow_overlay")
+ //Reward item when the abnormality is killed
+ var/obj/reward_item = /obj/item/rawpe
+ //The monster we spawn
+ var/mob/living/simple_animal/monster
+ //The monster we currently have spawned
+ var/mob/living/simple_animal/escapee
+ //Abnormalities we can initialize containing
+ var/list/abnos = list(
+ /mob/living/simple_animal/hostile/abnormality/fragment,
+ /mob/living/simple_animal/hostile/abnormality/my_sweet_home,
+ /mob/living/simple_animal/hostile/abnormality/so_that_no_cry,
+ /mob/living/simple_animal/hostile/abnormality/pinocchio,
+ /mob/living/simple_animal/hostile/abnormality/steam,
+ /mob/living/simple_animal/hostile/abnormality/jangsan,
+ /mob/living/simple_animal/hostile/abnormality/helper,
+ /mob/living/simple_animal/hostile/abnormality/golden_apple,
+ /mob/living/simple_animal/hostile/abnormality/funeral,
+ /mob/living/simple_animal/hostile/abnormality/blue_shepherd,
+ )
+
+/obj/item/lor_boss_book/Initialize()
+ . = ..()
+ FillBook()
+
+/obj/item/lor_boss_book/attack_hand(mob/user)
+ . = ..()
+ if(book_mode == REWARD_MODE)
+ WinReward(user)
+
+/obj/item/lor_boss_book/attack_self(mob/user)
+ if(book_mode == REWARD_MODE)
+ WinReward(user)
+ return
+ if(book_mode == ACTIVE_MODE || !monster || escapee)
+ to_chat(user, span_notice("The pages of the book are empty."))
+ return
+ if(!iscarbon(user))
+ to_chat(user, span_notice("The book refuses to open."))
+ return
+ visible_message(span_warning("[src] opens up and begins to turn its pages rapidly!"))
+ book_mode = ACTIVE_MODE
+ ReleasePrisioner(user)
+
+/obj/item/lor_boss_book/Destroy()
+ UnregisterSignal(escapee, COMSIG_LIVING_DEATH)
+ return ..()
+
+/obj/item/lor_boss_book/proc/WinReward(mob/living/carbon/user)
+ visible_message(span_nicegreen("The book dissolves leaving behind a reward."))
+ var/obj/item/I = new reward_item(get_turf(src))
+ I.name = "[initial(monster.name)] [I.name]"
+ qdel(src)
+
+/obj/item/lor_boss_book/proc/FillBook()
+ if(!monster && abnos.len)
+ monster = pick(abnos)
+ name = "sealed [initial(monster.name)]"
+
+//Buildup to releasing the sealed abnormality.
+/obj/item/lor_boss_book/proc/ReleasePrisioner(mob/living/librarian)
+ if(QDELETED(src))
+ return
+ src.forceMove(get_turf(src))
+ icon_state = "lor_open"
+ add_overlay(glow_animation)
+ //In total it should take 5 minutes or five page turns to release the abnormality. This is to prevent people from opening a book in a crowd.
+ for(var/i = 1 to 5)
+ sleep(1 SECONDS)
+ if(QDELETED(src))
+ return
+ if(PreventSummonCondition())
+ icon_state = "lor"
+ book_mode = NORMAL_MODE
+ cut_overlay(glow_animation)
+ return
+ playsound(get_turf(src), 'sound/effects/pageturn1.ogg', 45, 3, 3)
+ flick("lor_flip", src)
+
+ escapee = BuildPrisioner(ChooseSummonLocation(), monster)
+ cut_overlay(glow_animation)
+ playsound(get_turf(src), 'sound/effects/whirthunk.ogg', 50, 3, 3)
+ new /obj/effect/temp_visual/turn_book(get_turf(src))
+
+//Conditions to prevent summmoning.
+/obj/item/lor_boss_book/proc/PreventSummonCondition()
+ if(iscarbon(loc))
+ //If the book isnt on the floor then it wont open.
+ var/mob/living/carbon/C = loc
+ to_chat(C, span_notice("The book is held closed in your inventory."))
+ return TRUE
+ if(!isturf(loc))
+ return TRUE
+
+/* Chooses where to summon based on if there is
+ distortion landmarks. If no distortion landmarks
+ just put it where the book is.
+ Yes i know there is also the visible message included
+ in this proc im just too tired to seperate that into
+ its own thing and it only changes based on the same
+ criteria.*/
+/obj/item/lor_boss_book/proc/ChooseSummonLocation()
+ if(distant_spawn && LAZYLEN(SScityevents.distortion))
+ //What are the visual or signature differences between distortions and abnormalities?
+ minor_announce("DANGER: Distortion located in the backstreets. Hana has issued a suppression order.", "Local Activity Alert:", TRUE)
+ visible_message(span_warning("Light pours out of [src] before darting into the backstreets!"))
+ return get_turf(pick(SScityevents.distortion))
+ visible_message(span_warning("Something crawls out of [src]!"))
+ return get_turf(src)
+
+
+//Builds the prisioner.
+/obj/item/lor_boss_book/proc/BuildPrisioner(turf/spawn_turf, mob/living/L)
+ anchored = TRUE
+ name = "weird book"
+ desc = "An open book that appears to be stuck in place."
+ var/mob/living/mon = new L(spawn_turf)
+ if(isabnormalitymob(mon))
+ var/mob/living/simple_animal/hostile/abnormality/A = mon
+ if(!A)
+ return
+ A.BreachEffect(null, BREACH_PINK)
+ RegisterSignal(mon, COMSIG_LIVING_DEATH, .proc/Reward)
+ return mon
+
+//Deletes released abno and changes to reward mode.
+/obj/item/lor_boss_book/proc/Reward()
+ SIGNAL_HANDLER
+
+ //Abnormalities auto clean up themselves.
+ if(escapee && !isabnormalitymob(escapee))
+ qdel(escapee)
+ book_mode = REWARD_MODE
+ color = COLOR_VIVID_YELLOW
+
+
+/*
+* Book that contains Waw Level Abnormalities
+*/
+/obj/item/lor_boss_book/waw
+ abnos = list(
+ /mob/living/simple_animal/hostile/abnormality/big_bird,
+ /mob/living/simple_animal/hostile/abnormality/nosferatu,
+ /mob/living/simple_animal/hostile/abnormality/sphinx,
+ /mob/living/simple_animal/hostile/abnormality/warden,
+ )
+/*
+* Book that contains Aleph Level Abnormalities
+* Now is the time of monsters.
+*/
+/obj/item/lor_boss_book/aleph
+ abnos = list(
+ /mob/living/simple_animal/hostile/abnormality/mountain,
+ /mob/living/simple_animal/hostile/abnormality/nothing_there,
+ /mob/living/simple_animal/hostile/abnormality/melting_love,
+ /mob/living/simple_animal/hostile/abnormality/censored,
+ /mob/living/simple_animal/hostile/abnormality/distortedform,
+ )
+
+//Special Varient for people who summon abnormalities.
+/obj/item/lor_boss_book/librarian
+ distant_spawn = FALSE
+
+/obj/item/lor_boss_book/librarian/Initialize()
+ . = ..()
+ name += " librarian copy"
+
+/obj/item/lor_boss_book/librarian/PreventSummonCondition()
+ return
+
+/obj/item/lor_boss_book/librarian/ReleasePrisioner(mob/living/librarian)
+ . = ..()
+ escapee.faction = librarian.faction.Copy()
+
+#undef NORMAL_MODE
+#undef ACTIVE_MODE
+#undef REWARD_MODE
diff --git a/ModularTegustation/tegu_items/rcorp/!abno_overwrites.dm b/ModularTegustation/tegu_items/rcorp/!abno_overwrites.dm
new file mode 100644
index 000000000000..8ccfc50d2fb9
--- /dev/null
+++ b/ModularTegustation/tegu_items/rcorp/!abno_overwrites.dm
@@ -0,0 +1,51 @@
+//This file is for overwrites for initializes for the RCA gamemode.
+
+//Scarecrow heals less but can infinitely suck bodies.
+//For MOST of RCA, Scarecrow could heal infinitely. This allowed him to function.
+//With that removed he no longer fulfills his role of a tank.
+/mob/living/simple_animal/hostile/abnormality/scarecrow/Initialize()
+ ..()
+ if(IsCombatMap())
+ braineating = FALSE
+ healthmodifier = 0.02
+
+//Jangsen is slow, and blocks bullets fully now to let them function as a tank
+/mob/living/simple_animal/hostile/abnormality/jangsan/Initialize()
+ ..()
+ if(IsCombatMap())
+ bullet_threshold = 150
+
+
+//R-Corp cannot eat 180 white damage
+/mob/living/simple_animal/hostile/abnormality/alriune/Initialize()
+ . = ..()
+ if(IsCombatMap())
+ pulse_damage = 70
+
+//Helper can't be stunned for a million fuckin years
+/mob/living/simple_animal/hostile/abnormality/helper/Initialize()
+ . = ..()
+ if(IsCombatMap())
+ stuntime = 2 SECONDS
+
+//Frag needs a little damage buff
+/mob/living/simple_animal/hostile/abnormality/fragment/Initialize()
+ ..()
+ if(IsCombatMap())
+ melee_damage_lower = 22
+ melee_damage_upper = 25
+ song_damage = 8
+
+//Clown could be a bit faster, and a bit more damage
+/mob/living/simple_animal/hostile/abnormality/clown/Initialize()
+ if(IsCombatMap())
+ move_to_delay = 2.3
+ melee_damage_lower = 20
+ melee_damage_upper = 20
+ ..()
+
+
+/mob/living/simple_animal/hostile/abnormality/voiddream/Transform()
+ if(IsCombatMap())
+ return
+ ..()
diff --git a/ModularTegustation/tegu_items/rcorp/landmarks.dm b/ModularTegustation/tegu_items/rcorp/landmarks.dm
index 12bf03141b64..e449dec8ebcc 100644
--- a/ModularTegustation/tegu_items/rcorp/landmarks.dm
+++ b/ModularTegustation/tegu_items/rcorp/landmarks.dm
@@ -1,38 +1,37 @@
GLOBAL_LIST_INIT(easycombat, list(
- /mob/living/simple_animal/hostile/abnormality/ppodae,
/mob/living/simple_animal/hostile/abnormality/blue_shepherd,
- /mob/living/simple_animal/hostile/abnormality/dimensional_refraction,
/mob/living/simple_animal/hostile/abnormality/helper,
- /mob/living/simple_animal/hostile/abnormality/apex_predator,
- /mob/living/simple_animal/hostile/abnormality/cleaner,
/mob/living/simple_animal/hostile/abnormality/smile,
/mob/living/simple_animal/hostile/abnormality/pinocchio,
+ /mob/living/simple_animal/hostile/abnormality/fragment,
))
-GLOBAL_LIST_INIT(easysupport, list(/mob/living/simple_animal/hostile/abnormality/fragment,
+GLOBAL_LIST_INIT(easysupport, list(
/mob/living/simple_animal/hostile/abnormality/funeral,
- /mob/living/simple_animal/hostile/abnormality/voiddream,
/mob/living/simple_animal/hostile/abnormality/pisc_mermaid,
- /mob/living/simple_animal/hostile/abnormality/rudolta,
/mob/living/simple_animal/hostile/abnormality/redblooded,
/mob/living/simple_animal/hostile/abnormality/wayward,
+ /mob/living/simple_animal/hostile/abnormality/ppodae,
+ /mob/living/simple_animal/hostile/abnormality/apex_predator,
+ /mob/living/simple_animal/hostile/abnormality/cleaner,
))
-GLOBAL_LIST_INIT(easytank, list(/mob/living/simple_animal/hostile/abnormality/jangsan,
+GLOBAL_LIST_INIT(easytank, list(
+ /mob/living/simple_animal/hostile/abnormality/jangsan,
/mob/living/simple_animal/hostile/abnormality/scarecrow,
- /mob/living/simple_animal/hostile/abnormality/black_swan,
/mob/living/simple_animal/hostile/abnormality/kqe,
+ /mob/living/simple_animal/hostile/abnormality/warden,
+ /mob/living/simple_animal/hostile/abnormality/golden_apple,
))
GLOBAL_LIST_INIT(hardcombat, list(
/mob/living/simple_animal/hostile/abnormality/clouded_monk,
/mob/living/simple_animal/hostile/abnormality/clown,
/mob/living/simple_animal/hostile/abnormality/nosferatu,
- /mob/living/simple_animal/hostile/abnormality/big_bird,
/mob/living/simple_animal/hostile/abnormality/big_wolf,
- /mob/living/simple_animal/hostile/abnormality/warden,
- /mob/living/simple_animal/hostile/abnormality/fire_bird,
/mob/living/simple_animal/hostile/abnormality/luna,
+ /mob/living/simple_animal/hostile/abnormality/dimensional_refraction,
+ /mob/living/simple_animal/hostile/abnormality/black_swan,
))
GLOBAL_LIST_INIT(hardsupport, list(/mob/living/simple_animal/hostile/abnormality/sphinx,
@@ -43,13 +42,15 @@ GLOBAL_LIST_INIT(hardsupport, list(/mob/living/simple_animal/hostile/abnormality
/mob/living/simple_animal/hostile/abnormality/yin,
/mob/living/simple_animal/hostile/abnormality/pygmalion,
/mob/living/simple_animal/hostile/abnormality/alriune,
+ /mob/living/simple_animal/hostile/abnormality/rudolta,
+ /mob/living/simple_animal/hostile/abnormality/big_bird,
+ /mob/living/simple_animal/hostile/abnormality/fire_bird,
))
GLOBAL_LIST_INIT(hardtank, list(/mob/living/simple_animal/hostile/abnormality/melting_love,
/mob/living/simple_animal/hostile/abnormality/nothing_there,
/mob/living/simple_animal/hostile/abnormality/censored,
/mob/living/simple_animal/hostile/abnormality/titania,
- /mob/living/simple_animal/hostile/abnormality/golden_apple,
/mob/living/simple_animal/hostile/abnormality/greed_king,
/mob/living/simple_animal/hostile/abnormality/eris,
))
diff --git a/ModularTegustation/tegu_items/rcorp/objective.dm b/ModularTegustation/tegu_items/rcorp/objective.dm
index 881ab47de616..10635ce0e7cb 100644
--- a/ModularTegustation/tegu_items/rcorp/objective.dm
+++ b/ModularTegustation/tegu_items/rcorp/objective.dm
@@ -191,6 +191,16 @@ GLOBAL_VAR_INIT(rcorp_wincondition, 0) //what state the game is in.
icon_state = "hivebot_fab_on"
density = 1
anchored = 1
+ resistance_flags = INDESTRUCTIBLE
+
+/obj/structure/rcorpcomms/Initialize()
+ ..()
+ addtimer(CALLBACK(src, PROC_REF(vulnerable)), 15 MINUTES)
+
+/obj/structure/rcorpcomms/proc/vulnerable()
+ minor_announce("Warning: The communications shields are now disabled. Communications are now vulnerable" , "R-Corporation Command Update")
+ icon_state = "hivebot_fab"
+ resistance_flags &= ~INDESTRUCTIBLE
/obj/structure/rcorpcomms/deconstruct(disassembled = TRUE)
for(var/mob/M in GLOB.player_list)
diff --git a/ModularTegustation/tegu_mobs/lc13_humanoids.dm b/ModularTegustation/tegu_mobs/lc13_humanoids.dm
index 3d1479b12678..65298c3aecaf 100644
--- a/ModularTegustation/tegu_mobs/lc13_humanoids.dm
+++ b/ModularTegustation/tegu_mobs/lc13_humanoids.dm
@@ -217,3 +217,478 @@ Skittish, they prefer to move in groups and will run away if the enemies are in
return
else
. = ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer
+ name = "fixer"
+ desc = "One of the many inhabitants of the backstreets, extremely weak and skittish."
+ icon_state = "flame_fixer"
+ icon_living = "flame_fixer"
+ icon_dead = "flame_fixer"
+ move_resist = MOVE_FORCE_STRONG
+ maxHealth = 1500
+ health = 1500
+ move_to_delay = 4
+ melee_damage_lower = 11
+ melee_damage_upper = 16
+ rapid_melee = 2
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_verb_continuous = "slices"
+ attack_verb_simple = "slice"
+ del_on_death = TRUE
+ var/can_act = TRUE
+
+
+/mob/living/simple_animal/hostile/humanoid/fixer/Move()
+ if(!can_act)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/AttackingTarget(atom/attacked_target)
+ if(!can_act)
+ return FALSE
+ . = ..()
+
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal
+ name = "Metal Fixer"
+ desc = "A dude covered in a full white cloak and always wear a white mask. He seems to be wearing a tactical vest."
+ icon_state = "metal_fixer"
+ icon_living = "metal_fixer"
+ icon_dead = "metal_fixer"
+ var/icon_attacking = "metal_fixer_weapon"
+ maxHealth = 1500
+ health = 1500
+ damage_coeff = list(BRUTE = 1, RED_DAMAGE = 0.7, WHITE_DAMAGE = 1.3, BLACK_DAMAGE = 0.4, PALE_DAMAGE = 1.5)
+ move_to_delay = 5
+ melee_damage_lower = 10
+ melee_damage_upper = 14
+ melee_damage_type = BLACK_DAMAGE
+ rapid_melee = 2
+ attack_sound = 'sound/weapons/fixer/generic/blade3.ogg'
+ attack_verb_continuous = "slices"
+ attack_verb_simple = "slice"
+ del_on_death = TRUE
+ ranged = TRUE
+ var/statue_type = /mob/living/simple_animal/hostile/metal_fixer_statue
+ var/shots_cooldown = 50
+ var/max_statues = 12
+ var/health_lost_per_statue = 100
+ var/list/statues = list()
+ var/current_healthloss = 0
+ var/aoe_cooldown = 150
+ var/last_aoe_time = 0
+ var/aoe_damage = 50
+ var/stun_duration = 50
+ var/spike_line_cooldown = 150
+ var/last_spike_line_time = 0
+ var/creation_line_cooldown = 100
+ var/last_creation_line_time = 0
+ var/statue_cooldown = 25
+ var/last_statue_cooldown_time = 0
+ var/self_damage_statue = 100
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/Aggro()
+ icon_state = icon_attacking
+ . = ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/LoseTarget()
+ icon_state = icon_living
+ . = ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/OpenFire()
+ ranged_cooldown = world.time + shots_cooldown
+ if (world.time > last_spike_line_time + spike_line_cooldown)
+ last_spike_line_time = world.time
+ say("Experience is what brought me here.")
+
+ playsound(src, 'sound/weapons/fixer/hana_pierce.ogg', 200, TRUE, 2) // pick sound
+ for(var/d in GLOB.cardinals)
+ var/turf/E = get_step(src, d)
+ shoot_projectile(E)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/AttackingTarget(atom/attacked_target)
+ if(!can_act)
+ return FALSE
+
+ if (ranged_cooldown <= world.time)
+ OpenFire()
+
+ // do AOE
+ if (world.time > last_aoe_time + aoe_cooldown)
+ last_aoe_time = world.time
+ can_act = FALSE
+ say("This is the culmination of my work.")
+ SLEEP_CHECK_DEATH(20)
+ var/hit_statue = FALSE
+ for(var/turf/T in view(2, src))
+ playsound(src, 'sound/weapons/fixer/generic/finisher2.ogg', 200, TRUE, 2)
+ new /obj/effect/temp_visual/slice(T)
+ for(var/mob/living/L in T)
+ if (istype(L, /mob/living/simple_animal/hostile/metal_fixer_statue))
+ var/mob/living/simple_animal/hostile/metal_fixer_statue/S = L
+ qdel(S)
+ hit_statue = TRUE
+ HurtInTurf(T, list(), aoe_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE)
+
+ if (hit_statue)
+ say("...")
+ adjustHealth(self_damage_statue)
+ var/mutable_appearance/colored_overlay = mutable_appearance(icon, "small_stagger", layer + 0.1)
+ add_overlay(colored_overlay)
+ SLEEP_CHECK_DEATH(stun_duration)
+ cut_overlays()
+ can_act = TRUE
+ else
+ . = ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
+ //say("Damage taken. Current health: [health]")
+ var/old_health = health
+ . = ..()
+ var/health_loss = old_health - health
+ current_healthloss += health_loss
+ if (current_healthloss > health_lost_per_statue)
+ current_healthloss -= health_lost_per_statue
+ spawn_statue()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/proc/spawn_statue()
+ if (statues.len < max_statues && world.time > last_statue_cooldown_time + statue_cooldown)
+ last_statue_cooldown_time = world.time
+ var/list/available_turfs = list()
+ for(var/turf/T in view(4, loc))
+ if(isfloorturf(T) && !T.density && !locate(/mob/living) in T)
+ available_turfs += T
+ visible_message("[src] start spawning a statue! Turfs: [available_turfs.len]")
+ if (world.time > last_creation_line_time + creation_line_cooldown)
+ last_creation_line_time = world.time
+ say("The days of the past.")
+
+ if(available_turfs.len)
+ var/turf/statue_turf = pick(available_turfs)
+ var/mob/living/simple_animal/hostile/metal_fixer_statue/S = new statue_type(statue_turf)
+ statues += S
+ S.metal = src
+ S.icon_state = "memory_statute_grow" // Set the initial icon state to the rising animation
+ flick("memory_statute_grow", S) // Play the rising animation
+ spawn(10) // Wait for the animation to finish
+ S.icon_state = initial(S.icon_state) // Set the icon state back to the default statue icon
+ visible_message("[src] spawns a statue. ")
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/proc/shoot_projectile(turf/marker, set_angle)
+ if(!isnum(set_angle) && (!marker || marker == loc))
+ return
+ var/turf/startloc = get_turf(src)
+ var/obj/projectile/P = new /obj/projectile/metal_fixer(startloc)
+ P.preparePixelProjectile(marker, startloc)
+ P.firer = src
+ if(target)
+ P.original = target
+ P.fire(set_angle)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/metal/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
+ if (istype(P, /obj/projectile/metal_fixer))
+ adjustHealth(-P.damage)
+ playsound(src, 'sound/abnormalities/voiddream/skill.ogg', 200, TRUE, 2)
+ visible_message("[P] contacts with [src] and heals them!")
+ DamageEffect(P.damage, P.damage_type)
+ else
+ . = ..()
+
+/obj/projectile/metal_fixer
+ name ="metal bolt"
+ icon_state= "chronobolt"
+ damage = 25
+ speed = 1
+ damage_type = BLACK_DAMAGE
+ projectile_piercing = PASSMOB
+ ricochets_max = 3
+ ricochet_chance = 100
+ ricochet_decay_chance = 1
+ ricochet_decay_damage = 1
+ ricochet_incidence_leeway = 0
+
+/obj/projectile/metal_fixer/check_ricochet_flag(atom/A)
+ if(istype(A, /turf/closed))
+ return TRUE
+ return FALSE
+
+/obj/projectile/metal_fixer/on_hit(atom/target, blocked = FALSE)
+ if(firer==target)
+ //var/mob/living/simple_animal/hostile/humanoid/fixer/metal/M = target
+ qdel(src)
+ return BULLET_ACT_BLOCK
+ . = ..()
+
+
+/mob/living/simple_animal/hostile/metal_fixer_statue
+ name = "Memory Statue"
+ desc = "A statue created by the Metal Fixer."
+ icon = 'ModularTegustation/Teguicons/tegumobs.dmi'
+ icon_state = "memory_statute"
+ damage_coeff = list(BRUTE = 1, RED_DAMAGE = 0.5, WHITE_DAMAGE = 0, BLACK_DAMAGE = 2, PALE_DAMAGE = 2)
+ health = 100
+ maxHealth = 100
+ speed = 0
+ move_resist = INFINITY
+ mob_size = MOB_SIZE_HUGE
+ var/mob/living/simple_animal/hostile/humanoid/fixer/metal/metal
+ var/heal_cooldown = 50
+ var/heal_timer
+ var/heal_per_tick = 25
+ var/self_destruct_timer
+
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
+ if (istype(P, /obj/projectile/metal_fixer))
+ DamageEffect(P.damage, P.damage_type)
+ else
+ . = ..()
+
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/Initialize()
+ . = ..()
+ heal_timer = addtimer(CALLBACK(src, .proc/heal_metal_fixer), heal_cooldown, TIMER_STOPPABLE)
+ self_destruct_timer = addtimer(CALLBACK(src, .proc/self_destruct), 0.5 MINUTES, TIMER_STOPPABLE)
+ AIStatus = AI_OFF
+ stop_automated_movement = TRUE
+ anchored = TRUE
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/Destroy()
+ deltimer(heal_timer)
+ deltimer(self_destruct_timer)
+ return ..()
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/proc/self_destruct()
+ visible_message("The statue crumbles and self-destructs!")
+ qdel(src)
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
+ . = ..()
+ if(health <= 0)
+ visible_message("The statue crumbles into pieces!")
+ qdel(src)
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/proc/heal_metal_fixer()
+ if(metal)
+ metal.adjustHealth(-heal_per_tick)
+ visible_message("The statue heals the Metal Fixer!")
+ playsound(src, 'sound/abnormalities/rosesign/rose_summon.ogg', 200, TRUE, 2)
+ icon_state = "memory_statute_heal" // Set the initial icon state to the rising animation
+ flick("memory_statute_heal", src) // Play the rising animation
+ spawn(10) // Wait for the animation to finish
+ icon_state = initial(icon_state) // Set the icon state back to the default statue icon
+ heal_timer = addtimer(CALLBACK(src, .proc/heal_metal_fixer), heal_cooldown, TIMER_STOPPABLE)
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/AttackingTarget()
+ return FALSE
+
+/mob/living/simple_animal/hostile/metal_fixer_statue/CanAttack(atom/the_target)
+ return FALSE
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame
+ name = "Flame Fixer"
+ desc = "A lanky young man with fair skin, dark eyes, and an often overoptimistic expression. A heavy spear decorated with vibrant patterns on the head."
+ icon_state = "flame_fixer"
+ icon_living = "flame_fixer"
+ icon_dead = "flame_fixer"
+ maxHealth = 1500
+ health = 1500
+ damage_coeff = list(BRUTE = 1, RED_DAMAGE = 0.4, WHITE_DAMAGE = 0.7, BLACK_DAMAGE = 1.3, PALE_DAMAGE = 1.5)
+ move_to_delay = 4
+ melee_damage_lower = 20
+ melee_damage_upper = 24
+ melee_damage_type = RED_DAMAGE
+ rapid_melee = 0.5
+ attack_sound = 'sound/weapons/fixer/generic/spear3.ogg'
+ attack_verb_continuous = "pierces"
+ attack_verb_simple = "pierce"
+ del_on_death = TRUE
+ ranged = TRUE
+ ranged_cooldown_time = 75
+ melee_reach = 2
+ var/burn_stacks = 2
+ projectiletype = /obj/projectile/flame_fixer
+ var/damage_reflection = FALSE
+ var/dash_cooldown = 150
+ var/last_dash = 0
+ var/dash_damage = 50
+ var/last_counter = 0
+ var/counter_cooldown = 30
+ var/last_voice_line = 0
+ var/voice_line_cooldown = 250
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/proc/TripleDash()
+ // if dash is off cooldown stun until the end of dashes and say quote
+ // wait 2 sec for the first dash
+ // after 2 sec dash towards the target dealing red dmg and applying burn
+ // repeat 3 times with 1 sec delay between each
+ // unstun
+ if (world.time > last_dash + dash_cooldown)
+ last_dash = world.time
+ can_act = FALSE
+ say("Dissatisfaction")
+ icon_state = "flame_fixer_dashing"
+ SLEEP_CHECK_DEATH(20)
+ Dash(target)
+ Dash(target)
+ Dash(target)
+ icon_state = initial(icon_state)
+ last_dash = world.time
+ can_act = TRUE
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/proc/Dash(dash_target)
+ if (!dash_target)
+ return
+ var/turf/target_turf = get_turf(dash_target)
+ var/list/hit_mob = list()
+ //do_shaky_animation(2)
+ if(do_after(src, 0.5 SECONDS, target = src))
+ var/turf/wallcheck = get_turf(src)
+ var/enemy_direction = get_dir(src, target_turf)
+ for(var/i=0 to 7)
+ if(get_turf(src) != wallcheck || stat == DEAD )
+ break
+ wallcheck = get_step(src, enemy_direction)
+ if(!ClearSky(wallcheck))
+ break
+ //without this the attack happens instantly
+ sleep(0.5)
+ forceMove(wallcheck)
+ playsound(wallcheck, 'sound/weapons/ego/burn_sword.ogg', 20, 0, 4)
+ for(var/turf/T in orange(get_turf(src), 1))
+ if(isclosedturf(T))
+ continue
+ new /obj/effect/temp_visual/mech_fire(T)
+ for(var/mob/living/L in T)
+ if(!faction_check_mob(L, FALSE) || locate(L) in hit_mob)
+ L.apply_damage(dash_damage, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ LAZYADD(hit_mob, L)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/proc/ClearSky(turf/T)
+ if(!T || isclosedturf(T) || T == loc)
+ return FALSE
+ if(locate(/obj/structure/window) in T.contents)
+ return FALSE
+ if(locate(/obj/structure/table) in T.contents)
+ return FALSE
+ if(locate(/obj/structure/railing) in T.contents)
+ return FALSE
+ for(var/obj/machinery/door/D in T.contents)
+ if(D.density)
+ return FALSE
+ return TRUE
+
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/OpenFire(atom/A)
+ if (!can_act)
+ return
+ TripleDash()
+ . = ..()
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/Shoot(atom/targeted_atom)
+ var/obj/projectile/flame_fixer/P = ..()
+ P.set_homing_target(target)
+ if (world.time > last_voice_line + voice_line_cooldown)
+ say("Helios fire!")
+ last_voice_line = world.time
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/AttackingTarget(atom/attacked_target)
+ // check cooldown and start countering
+ // stop melee start stun for 4 sec
+ // animate windup for 1 sec
+ // change icon_state to counter
+ // if they hit after wind up during counter deal RED damage and stamina damage
+ // counter has random cooldown 15-40 sec
+ if (!can_act)
+ return
+ TripleDash()
+
+ if (world.time > last_counter + counter_cooldown)
+ last_counter = world.time
+ can_act = FALSE
+ icon_state = "flame_fixer_counter_start"
+ say("Debilitation")
+ SLEEP_CHECK_DEATH(10)
+ damage_reflection = TRUE
+ icon_state = "flame_fixer_counter"
+ SLEEP_CHECK_DEATH(40)
+ damage_reflection = FALSE
+ can_act = TRUE
+ icon_state = initial(icon_state)
+ last_counter = world.time
+ counter_cooldown = rand(100, 250)
+ return
+
+ . = ..()
+ if (istype(target, /mob/living))
+ var/mob/living/L = target
+ L.apply_lc_burn(burn_stacks)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/bullet_act(obj/projectile/Proj, def_zone, piercing_hit = FALSE)
+ ..()
+ if(damage_reflection && Proj.firer)
+ if(get_dist(Proj.firer, src) < 8)
+ ReflectDamage(Proj.firer, Proj.damage_type, Proj.damage)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/attackby(obj/item/I, mob/living/user, params)
+ ..()
+ if(!damage_reflection)
+ return
+ ReflectDamage(user, I.damtype, I.force)
+
+/mob/living/simple_animal/hostile/humanoid/fixer/flame/proc/ReflectDamage(mob/living/attacker, attack_type = RED_DAMAGE, damage)
+ if(damage < 1)
+ return
+ if(!damage_reflection)
+ return
+ var/turf/jump_turf = get_step(attacker, pick(GLOB.alldirs))
+ if(jump_turf.is_blocked_turf(exclude_mobs = TRUE))
+ jump_turf = get_turf(attacker)
+ forceMove(jump_turf)
+ playsound(src, 'sound/weapons/ego/burn_guard.ogg', min(15 + damage, 100), TRUE, 4)
+ attacker.visible_message(span_danger("[src] hits [attacker] with a counterattack!"), span_userdanger("[src] counters your attack!"))
+ do_attack_animation(attacker)
+ attacker.apply_damage(damage * 2, attack_type, null, attacker.getarmor(null, attack_type))
+ attacker.apply_damage(damage, STAMINA, null, null)
+
+
+
+/obj/projectile/flame_fixer
+ name ="flame bolt"
+ icon_state= "helios_fire"
+ damage = 15
+ speed = 8
+ damage_type = RED_DAMAGE
+ //projectile_piercing = PASSMOB
+ ricochets_max = 20
+ ricochet_chance = 100
+ ricochet_decay_chance = 1
+ ricochet_decay_damage = 1
+ ricochet_incidence_leeway = 0
+ homing = TRUE
+ homing_turn_speed = 10 //Angle per tick.
+ var/stun_duration = 75
+ var/burn_stacks = 20
+
+
+/obj/projectile/flame_fixer/check_ricochet_flag(atom/A)
+ if(istype(A, /turf/closed))
+ return TRUE
+ return FALSE
+
+/obj/projectile/flame_fixer/on_hit(atom/target, blocked = FALSE)
+ if (istype(target, /mob/living))
+ var/mob/living/L = target
+ L.apply_lc_burn(burn_stacks)
+ if(firer==target)
+ var/mob/living/simple_animal/hostile/humanoid/fixer/flame/F = target
+ qdel(src)
+ F.can_act = FALSE
+ F.say("Derealization...")
+ var/mutable_appearance/colored_overlay = mutable_appearance(F.icon, "small_stagger", F.layer + 0.1)
+ F.add_overlay(colored_overlay)
+ sleep(stun_duration)
+ F.cut_overlays()
+ F.can_act = TRUE
+ return BULLET_ACT_BLOCK
+ . = ..()
diff --git a/_maps/RandomRooms/rcorp/facility/city.dmm b/_maps/RandomRooms/rcorp/facility/city.dmm
index 8a0a9d235705..0de0fd1a80ee 100644
--- a/_maps/RandomRooms/rcorp/facility/city.dmm
+++ b/_maps/RandomRooms/rcorp/facility/city.dmm
@@ -639,6 +639,12 @@
/obj/structure/chair/sofa/right,
/turf/open/floor/wood,
/area/city/outskirts)
+"ik" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"ip" = (
/obj/machinery/door/airlock/medical{
hackProof = 1;
@@ -1243,6 +1249,12 @@
},
/turf/open/floor/carpet/black,
/area/city/outskirts)
+"pS" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"pV" = (
/obj/structure/chair/wood{
dir = 8
@@ -4186,32 +4198,32 @@ WS
WS
WS
WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-Vg
-Au
-Au
-Au
-Au
-Au
-Au
-Au
-Vg
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+pS
+pS
+pS
+pS
+pS
+ik
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
WS
WS
WS
@@ -4249,9 +4261,10 @@ WS
Fl
Fl
Fl
-AQ
-AQ
-AQ
+pS
+pS
+pS
+ik
Fl
Fl
Fl
@@ -4261,9 +4274,8 @@ Fl
Fl
Fl
Fl
-HD
-HD
-HD
+pS
+ik
Fl
Fl
Fl
@@ -4357,6 +4369,7 @@ WS
WS
WS
WS
+WS
Vg
Vg
Au
@@ -4404,7 +4417,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -4521,6 +4533,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -4567,7 +4580,6 @@ WS
WS
WS
WS
-WS
Vg
BJ
BJ
@@ -4684,6 +4696,7 @@ WS
WS
WS
WS
+WS
Vg
Vg
Au
@@ -4717,7 +4730,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -4848,6 +4860,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -4873,7 +4886,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -5011,6 +5023,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5036,7 +5049,6 @@ WS
WS
WS
WS
-WS
Vg
Ld
Au
@@ -5174,6 +5186,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5191,7 +5204,6 @@ Vg
Vg
Vg
Vg
-Vg
WS
WS
WS
@@ -5337,6 +5349,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5348,7 +5361,6 @@ Au
Au
Au
Au
-Au
sF
Au
Au
@@ -5500,7 +5512,7 @@ WS
WS
WS
WS
-Vg
+WS
Vg
Vg
Vg
diff --git a/_maps/RandomRooms/rcorp/facility/city2.dmm b/_maps/RandomRooms/rcorp/facility/city2.dmm
index 451fd9dad35b..3e176f94f5f8 100644
--- a/_maps/RandomRooms/rcorp/facility/city2.dmm
+++ b/_maps/RandomRooms/rcorp/facility/city2.dmm
@@ -1962,6 +1962,12 @@
/obj/structure/chair/sofa/left,
/turf/open/floor/wood,
/area/city/outskirts)
+"xD" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"xH" = (
/obj/structure/chair/wood,
/turf/open/floor/carpet/black,
@@ -3650,6 +3656,12 @@
/obj/structure/rack,
/turf/open/floor/wood,
/area/city/outskirts)
+"Te" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"Tp" = (
/obj/structure/mineral_door/wood,
/turf/open/floor/carpet,
@@ -4166,32 +4178,32 @@ WS
WS
WS
WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-Vg
-Au
-Au
-Au
-Au
-Au
-Au
-Au
-Vg
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+xD
+xD
+xD
+xD
+xD
+Te
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
WS
WS
WS
@@ -4229,9 +4241,10 @@ WS
Fl
Fl
Fl
-AQ
-AQ
-AQ
+xD
+xD
+xD
+Te
Fl
Fl
Fl
@@ -4241,9 +4254,8 @@ Fl
Fl
Fl
Fl
-HD
-HD
-HD
+xD
+Te
Fl
Fl
Fl
@@ -4337,6 +4349,7 @@ WS
WS
WS
WS
+WS
Vg
Vg
Au
@@ -4384,7 +4397,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -4501,6 +4513,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -4547,7 +4560,6 @@ WS
WS
WS
WS
-WS
Vg
BJ
BJ
@@ -4664,6 +4676,7 @@ WS
WS
WS
WS
+WS
Vg
Vg
Au
@@ -4697,7 +4710,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -4828,6 +4840,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -4853,7 +4866,6 @@ WS
WS
WS
WS
-WS
Vg
Vg
Vg
@@ -4991,6 +5003,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5016,7 +5029,6 @@ WS
WS
WS
WS
-WS
Vg
Ld
Au
@@ -5154,6 +5166,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5171,7 +5184,6 @@ Vg
Vg
Vg
Vg
-Vg
WS
WS
WS
@@ -5317,6 +5329,7 @@ WS
WS
WS
WS
+WS
Vg
Au
Au
@@ -5328,7 +5341,6 @@ Au
Au
Au
Au
-Au
sF
Au
Au
@@ -5480,7 +5492,7 @@ WS
WS
WS
WS
-Vg
+WS
Vg
Vg
Vg
diff --git a/_maps/RandomRooms/rcorp/facility/maze.dmm b/_maps/RandomRooms/rcorp/facility/maze.dmm
index d58cb390b564..d27f84603c4d 100644
--- a/_maps/RandomRooms/rcorp/facility/maze.dmm
+++ b/_maps/RandomRooms/rcorp/facility/maze.dmm
@@ -202,6 +202,12 @@
"Fl" = (
/turf/closed/indestructible/syndicate,
/area/city/outskirts)
+"FE" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"Gh" = (
/obj/effect/turf_decal/siding/wideplating/dark{
dir = 4
@@ -280,6 +286,12 @@
},
/turf/open/floor/plasteel/dark,
/area/city/outskirts)
+"Oo" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"Op" = (
/obj/structure/riser/dark,
/turf/open/floor/plasteel/dark,
@@ -399,29 +411,17 @@ Fl
Fl
Fl
Fl
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-oS
-Fl
-Fl
-Fl
Fl
Fl
Fl
Fl
Fl
+FE
+FE
+FE
+FE
+FE
+Oo
Fl
Fl
Fl
@@ -453,13 +453,25 @@ WS
WS
WS
WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
+WS
Fl
Fl
Fl
-MD
-gS
-gS
-aN
+FE
+FE
+FE
+Oo
Fl
Fl
Fl
@@ -468,9 +480,9 @@ Fl
Fl
Fl
Fl
-oS
-oS
-oS
+Fl
+FE
+Oo
Fl
Fl
Fl
diff --git a/_maps/RandomRooms/rcorp/facility/maze2.dmm b/_maps/RandomRooms/rcorp/facility/maze2.dmm
index 24f397df1f18..f9c7338b95d3 100644
--- a/_maps/RandomRooms/rcorp/facility/maze2.dmm
+++ b/_maps/RandomRooms/rcorp/facility/maze2.dmm
@@ -73,6 +73,12 @@
/obj/effect/landmark/objectivespawn,
/turf/open/floor/stone,
/area/city/outskirts)
+"l" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"m" = (
/obj/effect/turf_decal/siding/wideplating/dark/corner{
dir = 8
@@ -319,6 +325,12 @@
slowdown = 0
},
/area/city/outskirts)
+"X" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"Y" = (
/obj/effect/turf_decal/siding/wideplating/dark{
dir = 4
@@ -366,29 +378,17 @@ Z
Z
Z
Z
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-U
-Z
-Z
-Z
Z
Z
Z
Z
Z
+l
+l
+l
+l
+l
+X
Z
Z
Z
@@ -420,13 +420,25 @@ a
a
a
a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
Z
Z
Z
-G
-F
-F
-A
+l
+l
+l
+X
Z
Z
Z
@@ -435,9 +447,9 @@ Z
Z
Z
Z
-U
-U
-U
+Z
+l
+X
Z
Z
Z
diff --git a/_maps/RandomRooms/rcorp/facility/offices.dmm b/_maps/RandomRooms/rcorp/facility/offices.dmm
index 94bab7283a9a..8e6baa0c5f64 100644
--- a/_maps/RandomRooms/rcorp/facility/offices.dmm
+++ b/_maps/RandomRooms/rcorp/facility/offices.dmm
@@ -8,13 +8,6 @@
},
/turf/open/floor/plasteel,
/area/city/outskirts)
-"aN" = (
-/obj/machinery/door/poddoor/shutters/indestructible{
- id = "inside"
- },
-/obj/machinery/door/airlock/public/glass,
-/turf/open/floor/plating/asteroid,
-/area/city/outskirts)
"bn" = (
/obj/item/toy/plush/bongbong,
/turf/open/floor/plasteel/dark,
@@ -71,6 +64,12 @@
/obj/effect/landmark/abnospawn/hardtank,
/turf/open/floor/facility/dark,
/area/city/outskirts)
+"iB" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"iH" = (
/obj/effect/turf_decal/siding/purple{
dir = 4
@@ -164,6 +163,12 @@
},
/turf/open/floor/plasteel,
/area/city/outskirts)
+"oX" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"pc" = (
/obj/structure/chair,
/turf/open/floor/facility,
@@ -489,21 +494,30 @@ WS
WS
WS
WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
Fl
-aN
-aN
-aN
-aN
-aN
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+oX
+oX
+oX
+oX
+oX
+iB
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
+Fl
Fl
Fl
WS
@@ -540,23 +554,13 @@ WS
WS
WS
WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-WS
-Fl
Fl
Fl
-HD
-HD
-HD
-Fl
Fl
+oX
+oX
+oX
+iB
Fl
Fl
Fl
@@ -564,10 +568,11 @@ Fl
Fl
Fl
Fl
-Ub
-HD
-HD
Fl
+oX
+oX
+oX
+iB
Fl
Fl
Fl
diff --git a/_maps/RandomRooms/rcorp/facility/raidboss.dmm b/_maps/RandomRooms/rcorp/facility/raidboss.dmm
index e8931e0622a2..d8ee26feb1ed 100644
--- a/_maps/RandomRooms/rcorp/facility/raidboss.dmm
+++ b/_maps/RandomRooms/rcorp/facility/raidboss.dmm
@@ -17,6 +17,12 @@
"oS" = (
/turf/open/floor/plasteel/dark,
/area/city/outskirts)
+"qF" = (
+/obj/machinery/door/poddoor/shutters/indestructible{
+ id = "inside"
+ },
+/turf/open/floor/plating/asteroid,
+/area/city/outskirts)
"rh" = (
/obj/structure/barricade/wooden,
/turf/open/floor/facility,
@@ -39,6 +45,12 @@
/obj/effect/gibspawner/human,
/turf/open/floor/facility,
/area/city/outskirts)
+"VG" = (
+/obj/machinery/button/door/indestructible{
+ id = "inside"
+ },
+/turf/closed/indestructible/syndicate,
+/area/city/outskirts)
"WS" = (
/turf/closed/indestructible/rock,
/area/city/outskirts)
@@ -137,10 +149,10 @@ WS
Fl
Fl
Fl
-HD
-HD
-HD
-Fl
+qF
+qF
+qF
+VG
Fl
Fl
Fl
@@ -149,10 +161,10 @@ Fl
Fl
Fl
Fl
-HD
-HD
-HD
-Fl
+qF
+qF
+qF
+VG
Fl
Fl
Fl
diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm
index fcf34e91c9b8..1ee54078d529 100644
--- a/code/__DEFINES/ai.dm
+++ b/code/__DEFINES/ai.dm
@@ -41,10 +41,11 @@
#define BB_INSANE_BEST_FORCE_FOUND "BB_insane_bestforcefound"
#define BB_INSANE_ENEMIES "BB_insane_enemies"
#define BB_INSANE_BLACKLISTITEMS "BB_insane_blacklistitems"
+#define BB_INSANE_TEMPORARY_BLACKLISTITEMS "BB_insane_temporary_blacklistitems"
#define BB_INSANE_PICKUPTARGET "BB_insane_pickuptarget"
#define BB_INSANE_CURRENT_ATTACK_TARGET "BB_insane_current_attack_target"
-
+#define INSANE_MINIMUM_WEAPON_FORCE 10
///Haunted item controller defines
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 18975f17130e..e4e62d048d4c 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -326,6 +326,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Used for limbs.
#define TRAIT_DISABLED_BY_WOUND "disabled-by-wound"
+/// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT().
+#define TRAIT_CLIMBABLE "trait_climbable"
+
///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
diff --git a/code/__DEFINES/~lobotomy_defines/adventure.dm b/code/__DEFINES/~lobotomy_defines/adventure.dm
index 479636486c81..f1dc251baeb6 100644
--- a/code/__DEFINES/~lobotomy_defines/adventure.dm
+++ b/code/__DEFINES/~lobotomy_defines/adventure.dm
@@ -1,13 +1,13 @@
// defines used in ModularTegustation/_adventure_console
-#define DEBUG_TEXT_DISPLAY (1<<0)
-#define NORMAL_TEXT_DISPLAY (1<<1)
-#define ADVENTURE_TEXT_DISPLAY (1<<2)
+#define DEBUG_TEXT_DISPLAY 1
+#define NORMAL_TEXT_DISPLAY 2
+#define ADVENTURE_TEXT_DISPLAY 3
//Modes for what is displayed on the adventure panel.
-#define ADVENTURE_MODE_TRAVEL (1<<0)
-#define ADVENTURE_MODE_BATTLE (1<<1)
-#define ADVENTURE_MODE_EVENT_BATTLE (1<<2)
+#define ADVENTURE_MODE_TRAVEL 1
+#define ADVENTURE_MODE_BATTLE 2
+#define ADVENTURE_MODE_EVENT_BATTLE 3
//Stats defined. I honestly didnt want to make it have limbus company sins but here we are.
#define WRATH_STAT 1
diff --git a/code/__DEFINES/~lobotomy_defines/weapon.dm b/code/__DEFINES/~lobotomy_defines/weapon.dm
new file mode 100644
index 000000000000..815536782752
--- /dev/null
+++ b/code/__DEFINES/~lobotomy_defines/weapon.dm
@@ -0,0 +1,4 @@
+// Knockback defines
+#define KNOCKBACK_LIGHT (1<<0)
+#define KNOCKBACK_MEDIUM (1<<1)
+#define KNOCKBACK_HEAVY (1<<2)
diff --git a/code/__HELPERS/AStar.dm b/code/__HELPERS/AStar.dm
index e2c95ba071a0..e75ea888fbc2 100644
--- a/code/__HELPERS/AStar.dm
+++ b/code/__HELPERS/AStar.dm
@@ -199,6 +199,9 @@ Actual Adjacent procs :
for(var/obj/structure/window/W in src)
if(!W.CanAStarPass(ID, adir))
return TRUE
+ for(var/obj/structure/railing/R in src)
+ if(!R.CanAStarPass(ID, adir, caller))
+ return TRUE
for(var/obj/machinery/door/window/W in src)
if(!W.CanAStarPass(ID, adir))
return TRUE
diff --git a/code/datums/abnormality/_ego_datum/he.dm b/code/datums/abnormality/_ego_datum/he.dm
index ea8aa8216b6c..cda2edd87223 100644
--- a/code/datums/abnormality/_ego_datum/he.dm
+++ b/code/datums/abnormality/_ego_datum/he.dm
@@ -402,13 +402,22 @@
item_path = /obj/item/ego_weapon/faelantern
cost = 35
+//Memories of a Better Time - Morii
+/datum/ego_datum/armor/morii
+ item_path = /obj/item/clothing/suit/armor/ego_gear/he/morii
+ cost = 35
+
+/datum/ego_datum/weapon/morii
+ item_path = /obj/item/ego_weapon/morii
+ cost = 35
+
//Will You Play - Voodoo doll
/datum/ego_datum/armor/voodoo
item_path = /obj/item/clothing/suit/armor/ego_gear/he/voodoo
cost = 35
/datum/ego_datum/weapon/voodoo
- item_path = /obj/item/ego_weapon/voodoo
+ item_path = /obj/item/ego_weapon/mini/voodoo
cost = 35
//Eris - Coiling
diff --git a/code/datums/abnormality/_ego_datum/waw.dm b/code/datums/abnormality/_ego_datum/waw.dm
index 3e07dcf1bb69..8dc5baf5fc2c 100644
--- a/code/datums/abnormality/_ego_datum/waw.dm
+++ b/code/datums/abnormality/_ego_datum/waw.dm
@@ -192,6 +192,12 @@
item_path = /obj/item/clothing/suit/armor/ego_gear/waw/exuviae
cost = 50
+/datum/ego_datum/exuviae
+ name = "Naked Nest Cure"
+ item_category = "Extract"
+ item_path = /obj/item/serpentspoison
+ cost = 20
+
// Ebony Queen's Apple - Ebony Stem
/datum/ego_datum/armor/ebony_stem
item_path = /obj/item/clothing/suit/armor/ego_gear/waw/ebony_stem
diff --git a/code/datums/ai/sanity/_sanityloss_controller.dm b/code/datums/ai/sanity/_sanityloss_controller.dm
index b9fa70e6c245..cace0a107e0a 100644
--- a/code/datums/ai/sanity/_sanityloss_controller.dm
+++ b/code/datums/ai/sanity/_sanityloss_controller.dm
@@ -2,6 +2,7 @@
continue_processing_when_client = TRUE
blackboard = list(BB_INSANE_BEST_FORCE_FOUND = 10,\
BB_INSANE_BLACKLISTITEMS = list(),\
+ BB_INSANE_TEMPORARY_BLACKLISTITEMS = list(),\
BB_INSANE_PICKUPTARGET = null,\
BB_INSANE_CURRENT_ATTACK_TARGET = null)
max_target_distance = 20
@@ -14,6 +15,8 @@
var/list/current_path = list()
var/next_smash = 0
+ var/timerid = null
+
/datum/ai_controller/insane/TryPossessPawn(atom/new_pawn)
if(!ishuman(new_pawn))
return AI_CONTROLLER_INCOMPATIBLE
@@ -99,7 +102,6 @@
lines_type = /datum/ai_behavior/say_line/insanity_lines
resist_chance = 80 // Anger powered break out attempts
var/list/currently_scared = list()
- var/timerid = null
var/interest = 3
var/asshole = FALSE
@@ -109,6 +111,9 @@
total_locations |= SScityevents.spawners
return
total_locations |= GLOB.xeno_spawn // Avoids Department centers, unlike Wander Panic
+ var/mob/living/L = pawn
+ L.a_intent = INTENT_HARM
+ L.active_hand_index = 1
/datum/ai_controller/insane/murder/PerformMovement(delta_time)
if(!isnull(timerid))
@@ -117,33 +122,51 @@
/datum/ai_controller/insane/murder/MoveTo(delta_time)
var/mob/living/living_pawn = pawn
- if(!able_to_run() || !current_movement_target || QDELETED(current_movement_target) || current_movement_target.z != living_pawn.z || get_dist(living_pawn, current_movement_target) > max_target_distance)
+ if(IS_DEAD_OR_INCAP(living_pawn))
+ deltimer(timerid)
timerid = null
- return FALSE
- var/mob/living/selected_enemy = blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
+ return
+ var/atom/selected_enemy = blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
+ if(!current_movement_target || QDELETED(current_movement_target) || current_movement_target.z != living_pawn.z || get_dist(living_pawn, current_movement_target) > max_target_distance)
+ if(selected_enemy && selected_enemy.z == living_pawn.z && get_dist(living_pawn, selected_enemy) <= max_target_distance)
+ current_movement_target = selected_enemy
+ else
+ deltimer(timerid)
+ timerid = null
+ return FALSE
var/move_mod = living_pawn.cached_multiplicative_slowdown
- var/obj/item/gun/ego_gun/banger = locate() in living_pawn.held_items
- if(banger)
+ var/isGunHealingTargetSanity = FALSE
+ var/obj/item/gun/ego_gun/banger
+ if(living_pawn.held_items && living_pawn.held_items.len == 2 && istype(living_pawn.held_items[1], /obj/item/gun/ego_gun))
+ banger = living_pawn.held_items[1]
+ var/obj/item/ammo_casing/casing = initial(banger.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) == WHITE_DAMAGE && ishuman(selected_enemy))
+ var/mob/living/carbon/human/H = selected_enemy
+ if(H.sanity_lost)
+ isGunHealingTargetSanity = TRUE
move_mod *= 1.4
else
move_mod *= 1.2
-
- timerid = addtimer(CALLBACK(src, PROC_REF(MoveTo), delta_time), move_mod) // SLIGHTLY slower than what they should be *BUT* takes corners better.
+ timerid = addtimer(CALLBACK(src, PROC_REF(MoveTo), delta_time), delta_time * move_mod, TIMER_STOPPABLE) // SLIGHTLY slower than what they should be *BUT* takes corners better.
var/turf/our_turf = get_turf(living_pawn)
var/turf/target_turf
- var/current_dist = get_dist(living_pawn, selected_enemy)
- if((current_dist < 2) && banger && asshole)
+ var/current_dist = get_dist(living_pawn, current_movement_target)
+ if((current_dist < 2) && banger && !isGunHealingTargetSanity)
target_turf = get_step_away(living_pawn, current_movement_target)
- else if (current_dist == 2)
- return
- else
- target_turf = get_step_towards(living_pawn, current_movement_target)
- if(!is_type_in_typecache(target_turf, GLOB.dangerous_turfs))
+ else if ((!banger || isGunHealingTargetSanity) && current_dist >= 2 || current_dist >= 5)
+ var/list/path = get_path_to(living_pawn, current_movement_target, TYPE_PROC_REF(/turf, Distance_cardinal), 0, 30, 1, TYPE_PROC_REF(/turf, reachableTurftestWithMobs))
+ if(path.len > 1)
+ target_turf = path[2]
+ else
+ target_turf = get_step_towards(living_pawn, current_movement_target)
+ if(target_turf)
living_pawn.Move(target_turf, get_dir(our_turf, target_turf))
- if(!(selected_enemy in viewers(7, living_pawn))) // If you can't see the target enough
+
+ if(!(current_movement_target in oview(7, living_pawn))) // If you can't see the target enough
interest--
if(interest <= 0) // Give up
interest = 3
@@ -155,48 +178,135 @@
if(get_dist(living_pawn, current_movement_target) > max_target_distance)
CancelActions()
pathing_attempts = 0
- if(our_turf == get_turf(living_pawn) && !isliving(current_movement_target))
+ return
+ if(our_turf == get_turf(living_pawn) && !isliving(current_movement_target) && !ismecha(current_movement_target))
if(++pathing_attempts >= MAX_PATHING_ATTEMPTS)
CancelActions()
pathing_attempts = 0
-
+ return
return TRUE
+/turf/proc/reachableTurftestWithMobs(caller, turf/T, ID, simulated_only)
+ if(T && !T.density && !(simulated_only && SSpathfinder.space_type_cache[T.type]) && !LinkBlockedWithAccess(T,caller, ID))
+ if(is_type_in_typecache(T, GLOB.dangerous_turfs) && !(locate(/obj/structure/lattice) in T))
+ return FALSE
+ for(var/mob/living/L in T)
+ if(!L.CanPass(caller, T))
+ return FALSE
+ return TRUE
+
/datum/ai_controller/insane/murder/SelectBehaviors(delta_time)
- ..()
var/mob/living/living_pawn = pawn
- var/mob/living/selected_enemy = blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
+ if(IS_DEAD_OR_INCAP(living_pawn))
+ return
+
+ ..()
+ var/atom/selected_enemy = blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
+
+ var/has_weapon = FALSE
+ var/has_non_white_weapon = FALSE
+ for(var/obj/item/I in living_pawn.held_items)
+ if(istype(I, /obj/item/offhand))
+ continue
+ if(GetEffectiveItemForce(I) <= INSANE_MINIMUM_WEAPON_FORCE)
+ living_pawn.dropItemToGround(I, force = TRUE)
+ continue
+ has_weapon = TRUE
+ if(istype(I, /obj/item/gun/ego_gun))
+ var/obj/item/gun/ego_gun/G = I
+ var/obj/item/ammo_casing/casing = initial(G.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) != WHITE_DAMAGE)
+ has_non_white_weapon = TRUE
+ else if(I.damtype != WHITE_DAMAGE)
+ has_non_white_weapon = TRUE
+
+ //if no item try to find something before fighting if already have item then it will do until the fight ends
+ var/mob/living/carbon/human/human_target = selected_enemy
+ var/list/weapon_list
+ if(istype(human_target) && human_target.sanity_lost && !has_non_white_weapon)
+ weapon_list = TryFindWeapon(FALSE)
+ else if(!has_weapon)
+ weapon_list = TryFindWeapon()
+ if(weapon_list && weapon_list.len > 0)
+ var/obj/item/weapon = weapon_list[1]
+ blackboard[BB_INSANE_PICKUPTARGET] = weapon
+ if(isturf(weapon.loc))
+ current_movement_target = weapon
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/ground)
+ else
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/inventory)
+ return
+
+ if(!selected_enemy)
+ if(!FindEnemies())
+ return
+ selected_enemy = blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
// Ah ha! We'll fight our current enemy.
- if(selected_enemy && istype(selected_enemy))
- if(selected_enemy.status_flags & GODMODE)
+ if(selected_enemy && isliving(selected_enemy))
+ var/mob/living/living_enemy = selected_enemy
+ if(living_enemy.status_flags & GODMODE)
blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
return
- if(!(selected_enemy in livinginrange(10, living_pawn)))
+ if(!(living_enemy in livinginrange(10, living_pawn)))
blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
return
- if(selected_enemy.stat != DEAD)
- current_movement_target = selected_enemy
+ if(living_enemy.stat != DEAD)
+ current_movement_target = living_enemy
if(DT_PROB(50, delta_time))
current_behaviors += GET_AI_BEHAVIOR(lines_type)
current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insanity_attack_mob)
return
blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
return
+ else if(selected_enemy && ismecha(selected_enemy))
+ var/obj/vehicle/sealed/mecha/mecha_enemy = selected_enemy
+ if(get_dist(living_pawn, mecha_enemy) > 10 || living_pawn.z != mecha_enemy.z)
+ blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
+ return
+ if(!mecha_enemy.occupants || mecha_enemy.occupants.len < 1)
+ blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
+ return
+ current_movement_target = mecha_enemy
+ blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = mecha_enemy.occupants[1]
+ if(DT_PROB(50, delta_time))
+ current_behaviors += GET_AI_BEHAVIOR(lines_type)
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insanity_attack_mob)
+ return
- // No current enemy? We'll arm ourselves!
- if(TryFindWeapon()) // Find a weapon before a new enemy.
+ weapon_list = TryFindWeapon()
+ if(weapon_list && weapon_list.len > 0)
+ var/obj/item/weapon = weapon_list[1]
+ blackboard[BB_INSANE_PICKUPTARGET] = weapon
+ if(isturf(weapon.loc))
+ current_movement_target = weapon
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/ground)
+ else
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/inventory)
return
- // Armed enough..? Well we'll find a new person to fight!
if(FindEnemies())
return
-
/datum/ai_controller/insane/murder/PerformIdleBehavior(delta_time)
- // No current enemy? We'll arm ourselves!
- if(TryFindWeapon()) // Find a weapon before a new enemy.
+ var/mob/living/living_pawn = pawn
+ if(IS_DEAD_OR_INCAP(living_pawn))
return
+ // No current enemy? We'll arm ourselves!
+ var/list/weapon_list = TryFindWeapon()
+ if(weapon_list)
+ for(var/obj/item/weapon in weapon_list)
+ var/list/path = get_path_to(living_pawn, weapon, TYPE_PROC_REF(/turf, Distance_cardinal), 0, 30, 1, TYPE_PROC_REF(/turf, reachableTurftestWithMobs))
+ if(path.len == 0 && weapon.loc != living_pawn.loc && weapon.loc != living_pawn)
+ continue
+ blackboard[BB_INSANE_PICKUPTARGET] = weapon
+ if(isturf(weapon.loc))
+ current_movement_target = weapon
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/ground)
+ else
+ current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/inventory)
+ return
// Armed enough..? Well we'll find a new person to fight!
if(FindEnemies())
return
@@ -228,67 +338,140 @@
return SHOULD_RESIST(living_pawn)
return FALSE
-/datum/ai_controller/insane/murder/proc/TryFindWeapon()
+/proc/ComparatorItemForceGreater(obj/item/left, obj/item/right)
+ return GetEffectiveItemForce(left) > GetEffectiveItemForce(right)
+
+/datum/ai_controller/insane/murder/proc/TryFindWeapon(is_white_allowed = TRUE)
var/mob/living/living_pawn = pawn
+ var/list/weapons = list()
- if(!locate(/obj/item) in living_pawn.held_items)
- blackboard[BB_INSANE_BEST_FORCE_FOUND] = 10
+ blackboard[BB_INSANE_BEST_FORCE_FOUND] = INSANE_MINIMUM_WEAPON_FORCE
- var/obj/item/W
- for(var/obj/item/i in living_pawn.get_equipped_items())
- if(!istype(i))
+ var/list/item_blacklist = blackboard[BB_INSANE_BLACKLISTITEMS]
+ for(var/obj/item/I in living_pawn.held_items)
+ if(blackboard[BB_INSANE_BLACKLISTITEMS][I])
continue
- if(blackboard[BB_INSANE_BLACKLISTITEMS][i])
+ if(blackboard[BB_INSANE_TEMPORARY_BLACKLISTITEMS][I])
continue
- if(!IsBetterWeapon(living_pawn, i, blackboard[BB_INSANE_BEST_FORCE_FOUND]))
+ if(istype(I, /obj/item/offhand))
continue
- blackboard[BB_INSANE_PICKUPTARGET] = i
- current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/inventory)
- return TRUE
+ var/item_force = GetEffectiveItemForce(I)
+ if(item_force <= INSANE_MINIMUM_WEAPON_FORCE)
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ var/obj/item/ego_weapon/EW = I
+ var/obj/item/gun/ego_gun/EG = I
+ if(istype(EW) && !EW.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ if(istype(EG))
+ if(!EG.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ if(!is_white_allowed)
+ var/obj/item/ammo_casing/casing = initial(EG.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) == WHITE_DAMAGE)
+ continue
+ if(!is_white_allowed && I.damtype == WHITE_DAMAGE)
+ continue
+ if(item_force > blackboard[BB_INSANE_BEST_FORCE_FOUND])
+ blackboard[BB_INSANE_BEST_FORCE_FOUND] = item_force
- for(var/obj/item/i in view(7, living_pawn))
- if(!istype(i))
+ var/list/items_to_check = living_pawn.get_equipped_items(include_pockets = TRUE)
+ for(var/obj/item/storage/B in items_to_check)
+ items_to_check += B.contents
+ for(var/obj/item/i in items_to_check)
+ if(blackboard[BB_INSANE_BLACKLISTITEMS][i])
+ continue
+ if(blackboard[BB_INSANE_TEMPORARY_BLACKLISTITEMS][i])
+ continue
+ var/obj/item/ego_weapon/EW = i
+ var/obj/item/gun/ego_gun/EG = i
+ if(istype(EW) && !EW.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(i, force = TRUE)
+ item_blacklist[i] = TRUE
+ continue
+ if(istype(EG))
+ if(!EG.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(i, force = TRUE)
+ item_blacklist[i] = TRUE
+ continue
+ if(!is_white_allowed)
+ var/obj/item/ammo_casing/casing = initial(EG.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) == WHITE_DAMAGE)
+ continue
+ if(!is_white_allowed && i.damtype == WHITE_DAMAGE)
continue
+ if(!IsBetterWeapon(living_pawn, i, blackboard[BB_INSANE_BEST_FORCE_FOUND]))
+ continue
+ sorted_insert(weapons, i, GLOBAL_PROC_REF(ComparatorItemForceGreater))
+ for(var/obj/item/i in oview(7, living_pawn))
if(blackboard[BB_INSANE_BLACKLISTITEMS][i])
continue
+ if(blackboard[BB_INSANE_TEMPORARY_BLACKLISTITEMS][i])
+ continue
+ var/obj/item/ego_weapon/EW = i
+ var/obj/item/gun/ego_gun/EG = i
+ if(istype(EW) && !EW.CanUseEgo(living_pawn))
+ item_blacklist[i] = TRUE
+ continue
+ if(istype(EG))
+ if(!EG.CanUseEgo(living_pawn))
+ item_blacklist[i] = TRUE
+ continue
+ if(!is_white_allowed)
+ var/obj/item/ammo_casing/casing = initial(EG.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) == WHITE_DAMAGE)
+ continue
+ if(!is_white_allowed && i.damtype == WHITE_DAMAGE)
+ continue
if(!IsBetterWeapon(living_pawn, i, blackboard[BB_INSANE_BEST_FORCE_FOUND]))
continue
- W = i
- break
+ sorted_insert(weapons, i, GLOBAL_PROC_REF(ComparatorItemForceGreater))
+ if(weapons.len)
+ return weapons
+ return null
- if(W)
- blackboard[BB_INSANE_PICKUPTARGET] = W
- current_movement_target = W
- current_behaviors += GET_AI_BEHAVIOR(/datum/ai_behavior/insane_equip/ground)
- return TRUE
- return FALSE
-
-/proc/IsBetterWeapon(mob/living/L, obj/item/I, current_highest_force)
- var/weapon_power = I.force
- weapon_power *= 1 + (get_attribute_level(L, JUSTICE_ATTRIBUTE)/100)
+/proc/GetEffectiveItemForce(obj/item/I, considerRangedAttack = TRUE, justice = 0)
+ var/power = I.force * (1 + justice / 100)
if(istype(I, /obj/item/ego_weapon))
- var/obj/item/ego_weapon/ego_i = I
- weapon_power /= ego_i.attack_speed ? ego_i.attack_speed : 1
- else if(istype(I, /obj/item/gun/ego_gun))
+ var/obj/item/ego_weapon/EW = I
+ power *= EW.force_multiplier
+ power /= EW.attack_speed ? CLICK_CD_MELEE * EW.attack_speed / 10 : CLICK_CD_MELEE / 10 //damage per second
+ else if(considerRangedAttack && istype(I, /obj/item/gun/ego_gun))
var/obj/item/gun/ego_gun/gun_i = I
var/obj/item/ammo_casing/casing = initial(gun_i.ammo_type)
var/obj/projectile/boolet = initial(casing.projectile_type)
- weapon_power = initial(boolet.damage) * gun_i.burst_size * initial(casing.pellets)
+ var/gun_power = initial(boolet.damage) * gun_i.burst_size * initial(casing.pellets)
if(gun_i.autofire)
- weapon_power *= gun_i.autofire
+ gun_power /= gun_i.autofire / 10
else
- weapon_power /= (gun_i.fire_delay ? gun_i.fire_delay : 10)/10
- return weapon_power > current_highest_force
+ gun_power /= gun_i.fire_delay > CLICK_CD_RANGE ? gun_i.fire_delay / 10 : CLICK_CD_RANGE / 10
+ if(gun_power > power)
+ power = gun_power
+ else
+ power /= CLICK_CD_MELEE / 10
+ return power
+
+/proc/IsBetterWeapon(mob/living/carbon/human/H, obj/item/I, current_highest_force, considerRangedAttack = TRUE, applyJustice = FALSE)
+ var/justice = applyJustice ? get_modified_attribute_level(H, JUSTICE_ATTRIBUTE) : 0
+ return GetEffectiveItemForce(I, considerRangedAttack, justice) > current_highest_force
/datum/ai_controller/insane/murder/proc/FindEnemies()
. = FALSE
var/mob/living/living_pawn = pawn
- var/list/potential_enemies = viewers(9, living_pawn)
+ var/list/potential_enemies = livinginview(9, living_pawn)
if(!LAZYLEN(potential_enemies)) // We aint see shit!
return
- var/attempt_count = 0
+ var/list/weighted_list = list()
for(var/mob/living/L in potential_enemies) // Oh the CHOICES!
if(L == living_pawn)
continue
@@ -296,10 +479,28 @@
continue
if(L.stat == DEAD)
continue
- attempt_count++
- if(DT_PROB(33, attempt_count) || potential_enemies.len == 1) // Target spotted (bold)
- blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = L
- return TRUE
+ if(living_pawn.see_invisible < L.invisibility)
+ continue
+ if(!isturf(L.loc) && !ismecha(L.loc))
+ continue
+ weighted_list += L
+ for(var/i in weighted_list)
+ if(istype(i, /mob/living/simple_animal/hostile))
+ weighted_list[i] = 3
+ else if(ishuman(i))
+ var/mob/living/carbon/human/H = i
+ if(H.sanity_lost)
+ weighted_list[i] = 2
+ else if(ismecha(H.loc))
+ weighted_list[i] = 3
+ else
+ weighted_list[i] = 5
+ else
+ weighted_list[i] = 1
+ if(weighted_list.len > 0)
+ blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = pickweight(weighted_list)
+ return TRUE
+ return FALSE
// We stop trying to pick up a weapon if we're suddenly attacked
/datum/ai_controller/insane/murder/retaliate(mob/living/L)
@@ -327,10 +528,16 @@
retaliate(L)
return
+//Does not work /mob/living/bullet_act does not call parent and does not send any signals
/datum/ai_controller/insane/murder/on_bullet_act(datum/source, obj/projectile/Proj)
..()
if(isliving(Proj.firer))
retaliate(Proj.firer)
+ return
+ if(ismecha(Proj.firer))
+ var/obj/vehicle/sealed/mecha/M = Proj.firer
+ if(M.occupants && M.occupants.len > 0)
+ retaliate(M.occupants[1])
return
/datum/ai_controller/insane/murder/on_hitby(datum/source, atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
@@ -346,8 +553,15 @@
/datum/ai_controller/insane/murder/on_Crossed(datum/source, atom/movable/AM)
..()
var/mob/living/living_pawn = pawn
- if(!IS_DEAD_OR_INCAP(living_pawn) && ismob(AM))
- retaliate(AM)
+ if(IS_DEAD_OR_INCAP(living_pawn) || living_pawn.see_invisible < AM.invisibility)
+ return
+ var/mob/living/living_thing = AM
+ if(istype(living_thing) && !(living_thing.status_flags & GODMODE) && living_thing.stat != DEAD)
+ retaliate(living_thing)
+ return
+ var/obj/vehicle/sealed/mecha/mech = AM
+ if(istype(mech) && mech.occupants && mech.occupants.len > 0)
+ retaliate(mech.occupants[1])
return
return
diff --git a/code/datums/ai/sanity/sanityloss_behaviors.dm b/code/datums/ai/sanity/sanityloss_behaviors.dm
index 14e3f08d9dad..f0c8322831f7 100644
--- a/code/datums/ai/sanity/sanityloss_behaviors.dm
+++ b/code/datums/ai/sanity/sanityloss_behaviors.dm
@@ -30,44 +30,127 @@
/datum/ai_behavior/insanity_attack_mob/perform(delta_time, datum/ai_controller/insane/murder/controller)
. = ..()
-
- var/mob/living/target = controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
var/mob/living/living_pawn = controller.pawn
- if(!target || target?.stat == DEAD || target?.status_flags & GODMODE)
- finish_action(controller, TRUE) //Target == owned
+ var/atom/target = controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
+ if(IS_DEAD_OR_INCAP(living_pawn) || !target || living_pawn.see_invisible < target.invisibility)
+ finish_action(controller, TRUE)
+ return
+ if(isliving(target))
+ var/mob/living/living_target = target
+ if(living_target.stat == DEAD || (living_target.status_flags & GODMODE))
+ finish_action(controller, TRUE)
+ return
+ else if(ismecha(target))
+ var/obj/vehicle/sealed/mecha/mech_target = target
+ if(!mech_target.occupants || mech_target.occupants.len < 1)
+ finish_action(controller, TRUE)
+ return
+ else
+ finish_action(controller, TRUE)
+ return
+ var/mob/living/carbon/C = living_pawn
+ if(istype(C) && C.handcuffed)
+ C.resist_restraints()
+ controller.current_movement_target = null
+ return
+ if(living_pawn.pulledby)
+ if(living_pawn.pulledby != target)
+ controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = living_pawn.pulledby
+ target = living_pawn.pulledby
+ controller.current_movement_target = living_pawn.pulledby
+ living_pawn.resist_grab()
+ if(living_pawn.buckled)
+ living_pawn.resist_buckle()
+ if(living_pawn.buckled)
+ attack(controller, living_pawn.buckled, delta_time)
+ return
+ if(living_pawn.loc && !isturf(living_pawn.loc))
+ living_pawn.loc.container_resist_act(living_pawn)
+ if(!isturf(living_pawn.loc))
+ attack(controller, living_pawn.loc, delta_time)
+ return
+ var/list/item_blacklist = controller.blackboard[BB_INSANE_BLACKLISTITEMS]
+ var/has_weapon = FALSE
+ var/has_non_white_weapon = FALSE
+ for(var/obj/item/I in living_pawn.held_items)
+ if(istype(I, /obj/item/offhand))
+ continue
+ if(GetEffectiveItemForce(I) <= INSANE_MINIMUM_WEAPON_FORCE)
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ var/obj/item/ego_weapon/EW = I
+ var/obj/item/gun/ego_gun/EG = I
+ if(istype(EW) && !EW.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ if(istype(EG))
+ if(!EG.CanUseEgo(living_pawn))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ item_blacklist[I] = TRUE
+ continue
+ has_weapon = TRUE
+ var/obj/item/ammo_casing/casing = initial(EG.ammo_type)
+ var/obj/projectile/boolet = initial(casing.projectile_type)
+ if(initial(boolet.damage_type) != WHITE_DAMAGE)
+ has_non_white_weapon = TRUE
+ continue
+ has_weapon = TRUE
+ if(I.damtype != WHITE_DAMAGE)
+ has_non_white_weapon = TRUE
+
+ var/found_new_weapon = FALSE
+ var/mob/living/carbon/human/human_target = target
+ var/need_non_white_weapon = FALSE
+ if(istype(human_target) && human_target.sanity_lost && !has_non_white_weapon)
+ need_non_white_weapon = TRUE
+ if(!has_weapon || need_non_white_weapon)
+ var/list/temp_blacklist = controller.blackboard[BB_INSANE_TEMPORARY_BLACKLISTITEMS]
+ temp_blacklist.Cut()
+ var/list/weapon_list = controller.TryFindWeapon(!need_non_white_weapon)
+ if(weapon_list)
+ for(var/obj/item/weapon in weapon_list)
+ var/list/path = get_path_to(living_pawn, weapon, TYPE_PROC_REF(/turf, Distance_cardinal), 0, 30, 1, TYPE_PROC_REF(/turf, reachableTurftestWithMobs))
+ if(path.len == 0 && weapon.loc != living_pawn.loc && weapon.loc != living_pawn)
+ temp_blacklist[weapon] = TRUE
+ continue
+ found_new_weapon = TRUE
+ if(found_new_weapon)
+ finish_action(controller, FALSE)
+ return
- if(isturf(target.loc) && !IS_DEAD_OR_INCAP(living_pawn))
+ var/atom/thing_to_target
+ if(isturf(target.loc))
+ thing_to_target = target
+ else if(isobj(target.loc))
+ thing_to_target = target.loc
+ if(thing_to_target)
+ controller.current_movement_target = thing_to_target
if(!living_pawn.Adjacent(target))
var/obj/item/gun/ego_gun/banger = locate() in living_pawn.held_items
if(banger)
- ranged_attack(controller, target, delta_time)
+ ranged_attack(controller, thing_to_target, delta_time)
+ else
+ DestroyPathToTarget(controller, thing_to_target, delta_time)
return
- // check if target has a weapon
- var/obj/item/W
- for(var/obj/item/I in target.held_items)
- if(!(I.item_flags & ABSTRACT) && I.force > 5)
- W = I
- break
-
- // if the target has a weapon, chance to disarm them
- if(W && DT_PROB(20, delta_time))
- living_pawn.a_intent = INTENT_DISARM
- else
- living_pawn.a_intent = INTENT_HARM
- attack(controller, target, delta_time)
-
+ attack(controller, thing_to_target, delta_time)
+ else
+ finish_action(controller, TRUE)
+ return
/datum/ai_behavior/insanity_attack_mob/finish_action(datum/ai_controller/controller, succeeded)
. = ..()
var/mob/living/living_pawn = controller.pawn
walk(living_pawn, 0)
- controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
+ if(succeeded)
+ controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET] = null
/// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little
-/datum/ai_behavior/insanity_attack_mob/proc/attack(datum/ai_controller/insane/murder/controller, mob/living/target, delta_time)
+/datum/ai_behavior/insanity_attack_mob/proc/attack(datum/ai_controller/insane/murder/controller, atom/target, delta_time)
var/mob/living/living_pawn = controller.pawn
if(!living_pawn)
return
@@ -79,39 +162,53 @@
return
var/obj/item/weapon = null
- var/highest_force = 5
+ var/highest_force = INSANE_MINIMUM_WEAPON_FORCE
for(var/obj/item/I in living_pawn.held_items)
- if(istype(I, /obj/item/ego_weapon))
- var/obj/item/ego_weapon/EW = I
- if(!EW.CanUseEgo(living_pawn)) // I CAN'T USE THIS TO KILL!
- living_pawn.dropItemToGround(EW, force = TRUE) // YEET
- var/list/item_blacklist = controller.blackboard[BB_INSANE_BLACKLISTITEMS]
- item_blacklist[EW] = TRUE
- continue
if(I.damtype == WHITE_DAMAGE && ishuman(target))
var/mob/living/carbon/human/H = target
if(H.sanity_lost) // So we don't restore sanity of insane
continue
- if(I.force > highest_force)
+ var/weapon_power = GetEffectiveItemForce(I, FALSE)
+ if(weapon_power > highest_force)
weapon = I
- highest_force = I.force
+ highest_force = weapon_power
living_pawn.face_atom(target)
// attack with weapon if we have one
if(weapon)
- if(istype(weapon, /obj/item/ego_weapon))
- var/obj/item/ego_weapon/EGO = weapon
- living_pawn.changeNext_move(CLICK_CD_MELEE * EGO.attack_speed)
- else
- living_pawn.changeNext_move(CLICK_CD_MELEE)
+ if(living_pawn.held_items.len == 2 && living_pawn.held_items[1] != weapon)
+ living_pawn.held_items[2] = living_pawn.held_items[1]
+ living_pawn.held_items[1] = weapon
weapon.melee_attack_chain(living_pawn, target)
- else
+ if(istype(weapon, /obj/item/ego_weapon))
+ var/obj/item/ego_weapon/EW = weapon
+ var/cooldown = EW.attack_speed ? CLICK_CD_MELEE * EW.attack_speed : CLICK_CD_MELEE
+ var/hit_count = max(floor(10 * delta_time / cooldown), 1)
+ if(hit_count >= 2)
+ for(var/i in 2 to hit_count)
+ addtimer(CALLBACK(src, PROC_REF(DelayedMeleeAttack), living_pawn, weapon, target), cooldown * (i - 1))
+ else if(isliving(target))
+ var/mob/living/L = target
+ // check if target has a weapon
+ var/obj/item/W
+ for(var/obj/item/I in L.held_items)
+ if(!(I.item_flags & ABSTRACT) && GetEffectiveItemForce(I) > INSANE_MINIMUM_WEAPON_FORCE)
+ W = I
+ break
+ // if the target has a weapon, chance to disarm them
+ if(W && DT_PROB(25, delta_time))
+ living_pawn.a_intent = INTENT_DISARM
living_pawn.UnarmedAttack(target)
living_pawn.changeNext_move(CLICK_CD_MELEE)
+ living_pawn.a_intent = INTENT_HARM
+
+/datum/ai_behavior/insanity_attack_mob/proc/DelayedMeleeAttack(mob/living/user, obj/item/weapon, atom/target)
+ if(weapon && !IS_DEAD_OR_INCAP(user) && user.Adjacent(target) && (weapon in user.held_items))
+ weapon.melee_attack_chain(user, target)
/// attack using this GUN we found.
-/datum/ai_behavior/insanity_attack_mob/proc/ranged_attack(datum/ai_controller/insane/murder/controller, mob/living/target, delta_time)
+/datum/ai_behavior/insanity_attack_mob/proc/ranged_attack(datum/ai_controller/insane/murder/controller, atom/target, delta_time)
var/mob/living/living_pawn = controller.pawn
if(!living_pawn)
return
@@ -119,36 +216,88 @@
if(living_pawn.next_move > world.time)
return
+ if(living_pawn.held_items[1] && living_pawn.held_items[2])
+ for(var/obj/item/gun/ego_gun/G in living_pawn.held_items)
+ if(G.weapon_weight == WEAPON_HEAVY)
+ var/obj/item/I = living_pawn.held_items[1]
+ if(GetEffectiveItemForce(living_pawn.held_items[1]) > GetEffectiveItemForce(living_pawn.held_items[2]))
+ I = living_pawn.held_items[2]
+ if(!I.equip_to_best_slot(living_pawn, FALSE))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ break
+
var/obj/item/gun/ego_gun/banger = null
- var/highest_force = 5
for(var/obj/item/gun/ego_gun/G in living_pawn.held_items)
- var/full_hands = (G.weapon_weight == WEAPON_HEAVY) && living_pawn.held_items[1] && living_pawn.held_items[2]
- if(!G.CanUseEgo(living_pawn) || full_hands || !G.can_shoot()) // I CAN'T USE THIS TO KILL!
- living_pawn.dropItemToGround(G, force = TRUE) // YEET
- var/list/item_blacklist = controller.blackboard[BB_INSANE_BLACKLISTITEMS]
- item_blacklist[G] = TRUE
- continue
var/obj/item/ammo_casing/casing = initial(G.ammo_type)
var/obj/projectile/boolet = initial(casing.projectile_type)
if(initial(boolet.damage_type) == WHITE_DAMAGE && ishuman(target))
var/mob/living/carbon/human/H = target
if(H.sanity_lost) // So we don't restore sanity of insane
continue
- if(IsBetterWeapon(living_pawn, G, highest_force))
- banger = G
- highest_force = initial(boolet.damage) * G.burst_size * initial(G.ammo_type.pellets)
- if(G.autofire)
- highest_force *= G.autofire
- else
- highest_force /= (G.fire_delay ? G.fire_delay : 10)/10
+ if(living_pawn.held_items.len == 2 && living_pawn.held_items[1] != G)
+ living_pawn.held_items[2] = living_pawn.held_items[1]
+ living_pawn.held_items[1] = G
+ banger = G
+ break
if(!banger)
return
living_pawn.face_atom(target)
- living_pawn.changeNext_move(banger.fire_delay ? banger.fire_delay : 2)
+ var/delay
+ if(banger.autofire)
+ delay = banger.autofire
+ else
+ delay = banger.fire_delay > CLICK_CD_RANGE ? banger.fire_delay : CLICK_CD_RANGE
+ living_pawn.changeNext_move(CLICK_CD_RANGE)
+ var/shots = max(floor(10 * delta_time / delay), 1)
+ delay = 10 * delta_time / shots
banger.afterattack(target, living_pawn, FALSE)
+ for(var/i in 2 to shots)
+ addtimer(CALLBACK(src, PROC_REF(DelayedGunAttack), living_pawn, banger, target, living_pawn.next_move), delay * (i - 1))
+
+/datum/ai_behavior/insanity_attack_mob/proc/DelayedGunAttack(mob/living/user, obj/item/gun/weapon, atom/target, next_move)
+ if(weapon && !IS_DEAD_OR_INCAP(user) && (weapon in user.held_items))
+ weapon.spread += 20
+ weapon.afterattack(target, user, FALSE)
+ weapon.spread -= 20
+ user.next_move = next_move
+
+/datum/ai_behavior/insanity_attack_mob/proc/DestroyPathToTarget(datum/ai_controller/insane/murder/controller, atom/target, delta_time)
+ var/dir_to_target = get_dir(controller.pawn, target)
+ var/dir_list = list()
+ if(ISDIAGONALDIR(dir_to_target))
+ for(var/direction in GLOB.cardinals)
+ if(direction & dir_to_target)
+ dir_list += direction
+ else
+ dir_list += dir_to_target
+ var/turf/pawn_turf = get_turf(controller.pawn)
+ for(var/obj/structure/window/W in pawn_turf)
+ if(!W.CanAStarPass(null, dir_to_target))
+ attack(controller, W, delta_time)
+ return
+ for(var/obj/structure/railing/R in pawn_turf)
+ if(!R.CanAStarPass(null, dir_to_target, controller.pawn))
+ attack(controller, R, delta_time)
+ return
+ for(var/direction in dir_list)
+ var/turf/T = get_step(controller.pawn, direction)
+ if(QDELETED(T))
+ return
+ for(var/obj/O in T.contents)
+ if(!O.Adjacent(controller.pawn))
+ continue
+ if(ismecha(O) || ismachinery(O) || isstructure(O))
+ if(O.resistance_flags & INDESTRUCTIBLE)
+ continue
+ if(!O.density)
+ continue
+ if(O.IsObscured())
+ continue
+ attack(controller, O, delta_time)
+ return
/datum/ai_behavior/insane_equip
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
@@ -168,7 +317,6 @@
var/mob/living/living_pawn = controller.pawn
var/obj/item/target = controller.blackboard[BB_INSANE_PICKUPTARGET]
- var/best_force = controller.blackboard[BB_INSANE_BEST_FORCE_FOUND]
if(!isturf(living_pawn.loc))
finish_action(controller, TRUE)
@@ -182,43 +330,30 @@
finish_action(controller, FALSE)
return
- if(istype(target, /obj/item/ego_weapon)) // Oh, it's EGO!
- var/obj/item/ego_weapon/EW = target
- if(!EW.CanUseEgo(living_pawn)) // Can't use it? Stop trying to.
- finish_action(controller, FALSE)
- return
-
// If we can't move towards the item
- if(!get_path_to(living_pawn, get_turf(target), TYPE_PROC_REF(/turf, Distance_cardinal), 0, 10))
- finish_action(controller, FALSE)
+ if(!get_path_to(living_pawn, get_turf(target), TYPE_PROC_REF(/turf, Distance_cardinal), 0, 30, 1, TYPE_PROC_REF(/turf, reachableTurftestWithMobs)) && living_pawn.loc != target.loc && target.loc != living_pawn && !(target.loc in living_pawn.contents))
+ finish_action(controller, TRUE)
return
- // Strong weapon
-
- if(IsBetterWeapon(living_pawn, target, best_force))
+ if(isturf(target.loc) || (target in living_pawn.contents) || (target.loc in living_pawn.contents))
var/obj/item/left_item = living_pawn.get_item_for_held_index(LEFT_HANDS)
var/obj/item/right_item = living_pawn.get_item_for_held_index(RIGHT_HANDS)
- if((left_item != null) && (right_item != null))
- if(left_item.force < right_item.force) // Drop the old one, man...
- living_pawn.dropItemToGround(left_item, force = TRUE)
- else
- living_pawn.dropItemToGround(right_item, force = TRUE)
+ if(target.datum_components && (locate(/datum/component/two_handed) in target.datum_components))
+ for(var/obj/item/I in living_pawn.held_items)
+ if(!I.equip_to_best_slot(living_pawn, FALSE))
+ living_pawn.dropItemToGround(I, force = TRUE)
+ else if((left_item != null) && (right_item != null))
+ var/obj/item/I = right_item
+ if(GetEffectiveItemForce(left_item) < GetEffectiveItemForce(right_item)) // Drop the old one, man...
+ I = left_item
+ if(!I.equip_to_best_slot(living_pawn, FALSE))
+ living_pawn.dropItemToGround(I, force = TRUE)
+
living_pawn.put_in_hands(target)
- var/weapon_power = target.force
- if(istype(target, /obj/item/gun/ego_gun))
- var/obj/item/gun/ego_gun/gun_target = target
- var/obj/item/ammo_casing/casing = initial(gun_target.ammo_type)
- var/obj/projectile/boolet = initial(casing.projectile_type)
- weapon_power = initial(boolet.damage) * gun_target.burst_size * initial(casing.pellets)
- if(gun_target.autofire)
- weapon_power *= gun_target.autofire
- else
- weapon_power /= gun_target.fire_delay/10
- controller.blackboard[BB_INSANE_BEST_FORCE_FOUND] = weapon_power
+ controller.blackboard[BB_INSANE_BEST_FORCE_FOUND] = GetEffectiveItemForce(target)
finish_action(controller, TRUE)
return
-
- finish_action(controller, FALSE)
+ finish_action(controller, TRUE)
/datum/ai_behavior/insane_equip/inventory/perform(delta_time, datum/ai_controller/controller)
. = ..()
@@ -259,27 +394,44 @@
return
var/turf/target = controller.blackboard[BB_INSANE_CURRENT_ATTACK_TARGET]
- if(!LAZYLEN(controller.current_path) && !living_pawn.Adjacent(target))
+ if(living_pawn.Adjacent(target))
+ controller.pathing_attempts = 0
+ controller.current_path.Cut()
+ finish_action(controller, FALSE)
+ return
+ if(!LAZYLEN(controller.current_path))
controller.current_path = get_path_to(living_pawn, target, TYPE_PROC_REF(/turf, Distance_cardinal), 0, 120)
if(!LAZYLEN(controller.current_path)) // Returned FALSE or null.
finish_action(controller, FALSE)
return
controller.current_path.Remove(controller.current_path[1])
MoveInPath(controller)
+ return
+ if(!controller.timerid)
+ MoveInPath(controller)
+ return
/datum/ai_behavior/insanity_wander/proc/MoveInPath(datum/ai_controller/insane/controller)
+ controller.timerid = null
var/mob/living/living_pawn = controller.pawn
- if(!living_pawn)
+ if(!living_pawn || IS_DEAD_OR_INCAP(living_pawn))
controller.pathing_attempts = 0
controller.current_path = list() // Reset the path and stop
finish_action(controller, TRUE)
- return
+ return FALSE
if(!PreMoveCheck(controller, living_pawn))
- return
+ if(!QDELETED(controller))
+ controller.pathing_attempts = 0
+ controller.current_path.Cut()
+ finish_action(controller, TRUE)
+ if(istype(controller, /datum/ai_controller/insane/murder))
+ var/datum/ai_controller/insane/murder/M = controller
+ M.FindEnemies()
+ return FALSE
// Movement
- if(LAZYLEN(controller.current_path) && !IS_DEAD_OR_INCAP(living_pawn))
+ if(LAZYLEN(controller.current_path))
var/target_turf = controller.current_path[1]
- if(target_turf && get_dist(living_pawn, target_turf) < 3)
+ if(target_turf && get_dist(living_pawn, target_turf) < 2)
if(!step_towards(living_pawn, target_turf)) //If it fails to move
controller.pathing_attempts++
if(controller.pathing_attempts >= MAX_PATHING_ATTEMPTS)
@@ -294,7 +446,7 @@
else
controller.pathing_attempts++
var/move_delay = max(0.8, 0.2 + living_pawn.cached_multiplicative_slowdown - (get_modified_attribute_level(living_pawn, JUSTICE_ATTRIBUTE) * movement_mod))
- addtimer(CALLBACK(src, PROC_REF(MoveInPath), controller), move_delay)
+ controller.timerid = addtimer(CALLBACK(src, PROC_REF(MoveInPath), controller), move_delay)
return TRUE
controller.pathing_attempts = 0
controller.current_path = list() // Reset the path and stop
@@ -338,17 +490,17 @@
// Same as the above insanity, but they look for a target between moves.
/datum/ai_behavior/insanity_wander/murder_wander/PreMoveCheck(datum/ai_controller/insane/murder/controller, mob/living/living_pawn)
- for(var/mob/living/L in viewers(9, living_pawn))
+ for(var/mob/living/L in livinginview(9, living_pawn))
if(L == living_pawn)
continue
if(L.status_flags & GODMODE)
continue
if(L.stat == DEAD)
continue
- controller.pathing_attempts = 0
- controller.current_path = list() // Reset the path and stop
- finish_action(controller, TRUE)
- controller.FindEnemies()
+ if(!isturf(L.loc) && !ismecha(L.loc))
+ continue
+ if(living_pawn.see_invisible < L.invisibility)
+ continue
return FALSE
return ..()
diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm
index 2106fce29482..cff5359758c9 100644
--- a/code/datums/elements/climbable.dm
+++ b/code/datums/elements/climbable.dm
@@ -22,9 +22,11 @@
RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(target, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive))
RegisterSignal(target, COMSIG_ATOM_BUMPED, PROC_REF(try_speedrun))
+ ADD_TRAIT(target, TRAIT_CLIMBABLE, src)
/datum/element/climbable/Detach(datum/target, force)
UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_EXAMINE, COMSIG_MOUSEDROPPED_ONTO, COMSIG_ATOM_BUMPED))
+ REMOVE_TRAIT(target, TRAIT_CLIMBABLE, src)
return ..()
/datum/element/climbable/proc/on_examine(atom/source, mob/user, list/examine_texts)
diff --git a/code/game/machinery/computer/abnormality_portraits.dm b/code/game/machinery/computer/abnormality_portraits.dm
index e6010ec7f87d..f78fe5db9ddf 100644
--- a/code/game/machinery/computer/abnormality_portraits.dm
+++ b/code/game/machinery/computer/abnormality_portraits.dm
@@ -6,11 +6,13 @@
/proc/get_portrait_path()
var/list/paths = list(
+ 'icons/UI_Icons/abnormality_portraits/apex.png',
'icons/UI_Icons/abnormality_portraits/alriune.png',
'icons/UI_Icons/abnormality_portraits/army_in_black.png',
'icons/UI_Icons/abnormality_portraits/bald.png',
'icons/UI_Icons/abnormality_portraits/beanstalk.png',
'icons/UI_Icons/abnormality_portraits/beauty_beast.png',
+ 'icons/UI_Icons/abnormality_portraits/better_memories.png',
'icons/UI_Icons/abnormality_portraits/bottle.png',
'icons/UI_Icons/abnormality_portraits/big_bird.png',
'icons/UI_Icons/abnormality_portraits/big_wolf.png',
@@ -25,6 +27,7 @@
'icons/UI_Icons/abnormality_portraits/cinderella.png',
'icons/UI_Icons/abnormality_portraits/clouded_monk.png',
'icons/UI_Icons/abnormality_portraits/clown_smiling.png',
+ 'icons/UI_Icons/abnormality_portraits/contract.png',
'icons/UI_Icons/abnormality_portraits/crumbling_armor.png',
'icons/UI_Icons/abnormality_portraits/cube.png',
'icons/UI_Icons/abnormality_portraits/der_freischutz.png',
@@ -34,8 +37,10 @@
'icons/UI_Icons/abnormality_portraits/dingle_dangle.png',
'icons/UI_Icons/abnormality_portraits/dreaming_current.png',
'icons/UI_Icons/abnormality_portraits/drowned_sisters.png',
+ 'icons/UI_Icons/abnormality_portraits/eris.png',
'icons/UI_Icons/abnormality_portraits/express_train.png',
'icons/UI_Icons/abnormality_portraits/fairy_festival.png',
+ 'icons/UI_Icons/abnormality_portraits/falada.png',
'icons/UI_Icons/abnormality_portraits/fallen_amurdad.png',
'icons/UI_Icons/abnormality_portraits/fan.png',
'icons/UI_Icons/abnormality_portraits/fairies.png',
@@ -56,8 +61,10 @@
'icons/UI_Icons/abnormality_portraits/hookah.png',
'icons/UI_Icons/abnormality_portraits/jangsan.png',
'icons/UI_Icons/abnormality_portraits/judgement_bird.png',
+ 'icons/UI_Icons/abnormality_portraits/khz.png',
'icons/UI_Icons/abnormality_portraits/lady_facing_the_wall.png',
'icons/UI_Icons/abnormality_portraits/laetitia.png',
+ 'icons/UI_Icons/abnormality_portraits/last_shot.png',
'icons/UI_Icons/abnormality_portraits/little_prince.png',
'icons/UI_Icons/abnormality_portraits/little_red.png',
'icons/UI_Icons/abnormality_portraits/luna.png',
@@ -89,6 +96,8 @@
'icons/UI_Icons/abnormality_portraits/puss_in_boots.png',
'icons/UI_Icons/abnormality_portraits/pygmalion.png',
'icons/UI_Icons/abnormality_portraits/queen_bee.png',
+ 'icons/UI_Icons/abnormality_portraits/red_blooded_american.png',
+ 'icons/UI_Icons/abnormality_portraits/red_queen.png',
'icons/UI_Icons/abnormality_portraits/red_shoes.png',
'icons/UI_Icons/abnormality_portraits/red_buddy.png',
'icons/UI_Icons/abnormality_portraits/road_home.png',
@@ -99,10 +108,12 @@
'icons/UI_Icons/abnormality_portraits/scorched_girl.png',
'icons/UI_Icons/abnormality_portraits/screenwriter.png',
'icons/UI_Icons/abnormality_portraits/shadow.png',
+ 'icons/UI_Icons/abnormality_portraits/shrimp_executive.png',
'icons/UI_Icons/abnormality_portraits/shy_look.png',
'icons/UI_Icons/abnormality_portraits/silence.png',
'icons/UI_Icons/abnormality_portraits/silent_orchestra.png',
'icons/UI_Icons/abnormality_portraits/silent_girl.png',
+ 'icons/UI_Icons/abnormality_portraits/simple_smile.png',
'icons/UI_Icons/abnormality_portraits/siren.png',
'icons/UI_Icons/abnormality_portraits/singing_machine.png',
'icons/UI_Icons/abnormality_portraits/spring.png',
@@ -111,6 +122,7 @@
'icons/UI_Icons/abnormality_portraits/snow_queen.png',
'icons/UI_Icons/abnormality_portraits/snow_whites_apple.png',
'icons/UI_Icons/abnormality_portraits/someonesportrait.png',
+ 'icons/UI_Icons/abnormality_portraits/space.png',
'icons/UI_Icons/abnormality_portraits/spider_bud.png',
'icons/UI_Icons/abnormality_portraits/summer.png',
'icons/UI_Icons/abnormality_portraits/summer_deity.png',
@@ -121,9 +133,11 @@
'icons/UI_Icons/abnormality_portraits/void_dream.png',
'icons/UI_Icons/abnormality_portraits/we_can_change_anything.png',
'icons/UI_Icons/abnormality_portraits/warden.png',
+ 'icons/UI_Icons/abnormality_portraits/watchman.png',
'icons/UI_Icons/abnormality_portraits/wellcheers.png',
'icons/UI_Icons/abnormality_portraits/white_night.png',
'icons/UI_Icons/abnormality_portraits/white_lake.png',
+ 'icons/UI_Icons/abnormality_portraits/will_you_play.png',
'icons/UI_Icons/abnormality_portraits/winter.png',
'icons/UI_Icons/abnormality_portraits/winter_deity.png',
'icons/UI_Icons/abnormality_portraits/wrath_servant.png',
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 575ab1f4da8e..29b633f79ae8 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -702,6 +702,11 @@
icon_state = "slice"
duration = 4
+/obj/effect/temp_visual/mech_fire
+ name = "mech_fire"
+ icon_state = "mech_fire"
+ duration = 4
+
/obj/effect/temp_visual/dir_setting/slash
name = "slash"
icon_state = "slash"
@@ -784,20 +789,6 @@
icon_state = "apocalypse_enchant_effect"
layer = ABOVE_ALL_MOB_LAYER
-/obj/effect/temp_visual/ambermidnight_hole
- name = "hole"
- icon = 'ModularTegustation/Teguicons/224x128.dmi'
- icon_state = "ambermidnight_hole"
- duration = 10 SECONDS
- pixel_x = -96
- base_pixel_x = -96
- pixel_y = -16
- base_pixel_y = -16
-
-/obj/effect/temp_visual/ambermidnight_hole/Initialize()
- . = ..()
- animate(src, alpha = 0, time = duration)
-
/obj/effect/temp_visual/cross
name = "holy cross"
icon = 'icons/effects/32x64.dmi'
diff --git a/code/game/objects/items/ego_weapons/_ego_weapon.dm b/code/game/objects/items/ego_weapons/_ego_weapon.dm
index ee65200ba474..b47090c192a8 100644
--- a/code/game/objects/items/ego_weapons/_ego_weapon.dm
+++ b/code/game/objects/items/ego_weapons/_ego_weapon.dm
@@ -16,13 +16,44 @@
/// Is CleanUp proc running?
var/cleaning = FALSE
+ /// How much knockback does this weapon deal, if at all?
+ var/knockback = FALSE
+
/obj/item/ego_weapon/attack(mob/living/target, mob/living/user)
if(!CanUseEgo(user))
return FALSE
. = ..()
if(attack_speed)
user.changeNext_move(CLICK_CD_MELEE * attack_speed)
- return TRUE // If we want to do "if(!.)" checks, this has to exist.
+
+ if(target.anchored || !knockback) // lets not throw machines around
+ return TRUE
+
+ var/atom/throw_target = get_edge_target_turf(target, user.dir)
+ switch(knockback)
+ if(KNOCKBACK_LIGHT)
+ var/whack_speed = (prob(60) ? 1 : 4)
+ target.throw_at(throw_target, rand(1, 2), whack_speed, user)
+
+ if(KNOCKBACK_MEDIUM)
+ var/whack_speed = (prob(60) ? 3 : 6)
+ target.throw_at(throw_target, rand(2, 3), whack_speed, user)
+
+ if(KNOCKBACK_HEAVY) // neck status: snapped
+ target.throw_at(throw_target, 7, 7, user)
+
+ else // should only be used by admins messing around in-game, please consider using above variables as a coder
+ target.throw_at(throw_target, (knockback * 0.5) , knockback, user)
+
+ return TRUE
+
+/obj/item/ego_weapon/attack_obj(obj/target, mob/living/user)
+ if(!CanUseEgo(user))
+ return FALSE
+ . = ..()
+ if(attack_speed)
+ user.changeNext_move(CLICK_CD_MELEE * attack_speed)
+ return TRUE
/obj/item/ego_weapon/examine(mob/user)
. = ..()
@@ -41,23 +72,44 @@
if(throwforce>force)
. += span_notice("This weapon deals [throwforce] [damtype] damage when thrown.")
- if(!attack_speed)
+ switch(attack_speed)
+ if(-INFINITY to 0.39)
+ . += span_notice("This weapon has a very fast attack speed.")
+
+ if(0.4 to 0.69) // nice
+ . += span_notice("This weapon has a fast attack speed.")
+
+ if(0.7 to 0.99)
+ . += span_notice("This weapon attacks slightly faster than normal.")
+
+ if(1) // why
+ attack_speed = FALSE
+ CRASH("[src] has a unique attack speed variable that does nothing, please inform coders to delete the variable")
+
+ if(1.01 to 1.49)
+ . += span_notice("This weapon attacks slightly slower than normal.")
+
+ if(1.5 to 1.99)
+ . += span_notice("This weapon has a slow attack speed.")
+
+ if(2 to INFINITY)
+ . += span_notice("This weapon attacks extremely slow.")
+
+ if(!knockback)
return
- //Can't switch for less than for some reason
- if(attack_speed<0.4)
- . += span_notice("This weapon has a very fast attack speed.")
- else if(attack_speed<0.7)
- . += span_notice("This weapon has a fast attack speed.")
- else if(attack_speed<1)
- . += span_notice("This weapon attacks slightly faster than normal.")
- else if(attack_speed<1.5)
- . += span_notice("This weapon attacks slightly slower than normal.")
- else if(attack_speed<2)
- . += span_notice("This weapon has a slow attack speed.")
- else if(attack_speed>=2)
- . += span_notice("This weapon attacks extremely slow.")
+ switch(knockback)
+ if(KNOCKBACK_LIGHT)
+ . += span_notice("This weapon has slight enemy knockback.")
+
+ if(KNOCKBACK_MEDIUM)
+ . += span_notice("This weapon has decent enemy knockback.")
+
+ if(KNOCKBACK_HEAVY)
+ . += span_notice("This weapon has neck-snapping enemy knockback.")
+ else
+ . += span_notice("This weapon has [knockback >= 10 ? "neck-snapping": ""] enemy knockback.")
/obj/item/ego_weapon/Topic(href, href_list)
. = ..()
diff --git a/code/game/objects/items/ego_weapons/aleph.dm b/code/game/objects/items/ego_weapons/aleph.dm
index c7bcacf6f640..da92c87347a9 100644
--- a/code/game/objects/items/ego_weapons/aleph.dm
+++ b/code/game/objects/items/ego_weapons/aleph.dm
@@ -252,7 +252,7 @@
if(do_after(user, 5, target))
target.visible_message(span_danger("[user] rears up and slams into [target]!"), \
- span_userdanger("[user] punches you with everything you got!!"), COMBAT_MESSAGE_RANGE, user)
+ span_userdanger("[user] punches you with everything you got!!"), vision_distance = COMBAT_MESSAGE_RANGE, ignored_mobs = user)
to_chat(user, span_danger("You throw your entire body into this punch!"))
goldrush_damage = force
//I gotta regrab justice here
@@ -677,11 +677,13 @@
current_season = SSlobotomy_events.current_season
icon_state = current_season
if(current_season == "summer")
+ knockback = KNOCKBACK_LIGHT
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
else
+ knockback = FALSE
lefthand_file = 'icons/mob/inhands/weapons/ego_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/ego_righthand.dmi'
inhand_x_dimension = 32
@@ -701,16 +703,6 @@
damtype = season_list[current_season][8]
desc = season_list[current_season][10]
-/obj/item/ego_weapon/seasons/attack(mob/living/target, mob/living/user) //other forms could probably use something. Probably.
- . = ..()
- if(!.)
- return FALSE
- if(current_season == "summer")
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
-
/obj/item/ego_weapon/seasons/get_clamped_volume()
return 40
@@ -722,6 +714,7 @@
force = 35 //Twilight but lower in terms of damage
attack_speed = 1.8
damtype = RED_DAMAGE
+ knockback = KNOCKBACK_MEDIUM
attack_verb_continuous = list("pulverizes", "bashes", "slams", "blockades")
attack_verb_simple = list("pulverize", "bash", "slam", "blockade")
hitsound = 'sound/abnormalities/distortedform/slam.ogg'
@@ -739,22 +732,17 @@
attacking = TRUE //ALWAYS blocking ranged attacks
-
/obj/item/ego_weapon/shield/distortion/EgoAttackInfo(mob/user)
return span_notice("It deals [force * 4] red, white, black and pale damage combined.")
/obj/item/ego_weapon/shield/distortion/attack(mob/living/target, mob/living/user)
. = ..()
if(!.)
- return FALSE
+ return
for(var/damage_type in list(WHITE_DAMAGE, BLACK_DAMAGE, PALE_DAMAGE))
damtype = damage_type
target.attacked_by(src, user)
damtype = initial(damtype)
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 3 : 6)
- target.throw_at(throw_target, rand(2, 3), whack_speed, user)
/obj/item/ego_weapon/shield/distortion/CanUseEgo(mob/living/user)
. = ..()
@@ -824,7 +812,7 @@
return
if(ability_cooldown > world.time)
to_chat(user, span_warning("You have used this ability too recently!"))
- return FALSE
+ return
playsound(src, 'sound/effects/ordeals/white/white_reflect.ogg', 50, TRUE)
to_chat(user, "You cultivate seeds of desires.")
ability_cooldown = world.time + ability_cooldown_time
@@ -868,7 +856,7 @@
return
if(ability_cooldown > world.time)
to_chat(user, span_warning("You have used this ability too recently!"))
- return FALSE
+ return
if(do_after(user, 20, src))
playsound(src, 'sound/weapons/ego/spicebush_special.ogg', 50, FALSE)
to_chat(user, "You plant some flower buds.")
@@ -945,11 +933,11 @@
/obj/item/ego_weapon/willing
name = "the flesh is willing"
desc = "And really nothing will stop it."
- special = "This weapon has knockback."
icon_state = "willing"
force = 105 //Still lower DPS
attack_speed = 1.4
damtype = RED_DAMAGE
+ knockback = KNOCKBACK_LIGHT
attack_verb_continuous = list("bashes", "clubs")
attack_verb_simple = list("bashes", "clubs")
hitsound = 'sound/weapons/fixer/generic/club1.ogg'
@@ -960,16 +948,6 @@
JUSTICE_ATTRIBUTE = 80
)
-
-/obj/item/ego_weapon/willing/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!.)
- return FALSE
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
-
/obj/item/ego_weapon/shield/combust
name = "Combusting Courage"
desc = "A searing blade, setting the world ablaze to eradicate evil. \
@@ -1177,25 +1155,27 @@
current_holder = null
/obj/item/ego_weapon/mockery/attack(mob/living/target, mob/living/carbon/human/user)
+ if(form == "bat")
+ knockback = KNOCKBACK_LIGHT
+ else
+ knockback = FALSE
+
. = ..()
if(!.)
- return FALSE
- switch(form)
- if("bat")
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
- if("hammer")
- for(var/mob/living/L in view(2, target))
- var/aoe = force
- var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
- var/justicemod = 1 + userjust/100
- aoe*=justicemod
- if(user.faction_check_mob(L))
- continue
- L.apply_damage(aoe, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
- new /obj/effect/temp_visual/small_smoke/halfsecond(get_turf(L))
+ return
+
+ if(form != "hammer")
+ return
+
+ for(var/mob/living/L in view(2, target))
+ var/aoe = force
+ var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
+ var/justicemod = 1 + userjust/100
+ aoe*=justicemod
+ if(user.faction_check_mob(L))
+ continue
+ L.apply_damage(aoe, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
+ new /obj/effect/temp_visual/small_smoke/halfsecond(get_turf(L))
/obj/item/ego_weapon/mockery/get_clamped_volume()
return 40
@@ -1241,7 +1221,6 @@
/obj/item/ego_weapon/ultimate_christmas
name = "ultimate christmas"
desc = "The Santa's bag is very heavy, capable of carrying a gift for everyone in the world. This one is no exception."
- special = "This weapon has absurd knockback."
icon_state = "ultimate_christmas"
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
@@ -1250,6 +1229,7 @@
force = 160
attack_speed = 1.6
damtype = RED_DAMAGE
+ knockback = KNOCKBACK_HEAVY
attack_verb_continuous = list("bashes", "clubs")
attack_verb_simple = list("bashes", "clubs")
hitsound = 'sound/abnormalities/rudolta_buff/onrush1.ogg'
@@ -1260,14 +1240,6 @@
JUSTICE_ATTRIBUTE = 80
)
-/obj/item/ego_weapon/ultimate_christmas/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!.)
- return FALSE
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- target.throw_at(throw_target, 7, 7, user)
-
/obj/item/ego_weapon/oberon
name = "oberon"
desc = "Then yes, I am the Oberon you seek."
@@ -1340,6 +1312,7 @@
if(world.time > combo_time)
build_up = 0.8
combo_time = world.time + combo_wait
+ knockback = FALSE
switch(form)
if("scythe")
if(target.health <= (target.maxHealth * 0.5))
@@ -1348,16 +1321,23 @@
force = 150
else
force = 100
+
+ if("bat")
+ knockback = KNOCKBACK_MEDIUM
+
. = ..()
- var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
- var/justicemod = 1 + userjust/100
if(!.)
- return FALSE
+ return
+
+ var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
+ var/justicemod = 1 + userjust / 100
+
switch(form)
if("sword")
var/red = force
red*=justicemod
target.apply_damage(red * force_multiplier, RED_DAMAGE, null, target.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+
if("whip")
var/multihit = force
multihit*= justicemod
@@ -1368,11 +1348,7 @@
target.apply_damage(multihit * force_multiplier, damtype, null, target.run_armor_check(null, damtype), spread_damage = TRUE)
user.do_attack_animation(target)
playsound(loc, hitsound, get_clamped_volume(), TRUE, extrarange = stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
- if("bat")
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 2 : 5)
- target.throw_at(throw_target, rand(2, 4), whack_speed, user)
+
if("hammer")
for(var/mob/living/L in view(2, target))
var/aoe = force
diff --git a/code/game/objects/items/ego_weapons/he.dm b/code/game/objects/items/ego_weapons/he.dm
index 599ee8fb2191..4321385f3889 100644
--- a/code/game/objects/items/ego_weapons/he.dm
+++ b/code/game/objects/items/ego_weapons/he.dm
@@ -222,27 +222,17 @@
/obj/item/ego_weapon/christmas
name = "christmas"
desc = "With my infinite hatred, I give you this gift."
- special = "This weapon has knockback."
icon_state = "christmas"
force = 54 //Still lower DPS
attack_speed = 2
damtype = WHITE_DAMAGE
+ knockback = KNOCKBACK_LIGHT
attack_verb_continuous = list("bashes", "clubs")
attack_verb_simple = list("bashes", "clubs")
hitsound = 'sound/weapons/fixer/generic/club1.ogg'
attribute_requirements = list(
FORTITUDE_ATTRIBUTE = 40
)
-
-/obj/item/ego_weapon/christmas/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!.)
- return FALSE
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
-
/obj/item/ego_weapon/logging
name = "logging"
desc = "A versatile equipment made to cut down trees and people alike."
@@ -1688,12 +1678,13 @@
FORTITUDE_ATTRIBUTE = 40
)
-/obj/item/ego_weapon/voodoo
+/obj/item/ego_weapon/mini/voodoo
name = "voodoo"
desc = "What seems to be a giant half of a scissors pair."
icon_state = "voodoo"
- special = "This weapon deals both red and white damage."
+ special = "This weapon can be paired with itself to unlock the ability to parry."
force = 20
+ attack_speed = 0.7
damtype = WHITE_DAMAGE
attack_verb_continuous = list("stabs", "slashes", "attacks")
attack_verb_simple = list("stab", "slash", "attack")
@@ -1702,9 +1693,22 @@
FORTITUDE_ATTRIBUTE = 40
)
-/obj/item/ego_weapon/voodoo/attack(mob/living/target, mob/living/user)
+/obj/item/ego_weapon/mini/voodoo/attack(mob/living/target, mob/living/user)
+ if(!CanUseEgo(user))
+ return
+ var/combo = FALSE
+ var/mob/living/carbon/human/myman = user
+ var/obj/item/ego_weapon/mini/voodoo/Y = myman.get_inactive_held_item()
+ if(istype(Y)) //dual wielding? if so...
+ combo = TRUE //hits twice, you're spending more PE then you would getting a WAW anyways
..()
- target.apply_damage(force, RED_DAMAGE, null, target.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ if(combo)
+ sleep(attack_speed/2 SECONDS)
+ if(target in view(reach,user))
+ target.attacked_by(src, user)
+ target.send_item_attack_message(src, user,target)
+ user.do_attack_animation(target)
+ playsound(loc, hitsound, get_clamped_volume(), TRUE, extrarange = stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
/obj/item/ego_weapon/nixie
name = "nixie divergence"
@@ -1792,52 +1796,70 @@
..()
force = initial(force)
+/*
+* Look i cant think of anything for this fucking camera
+* to do and its most likely going to do something completely
+* unreleated to the abnormality so whatever. -IP
+*/
+/obj/item/ego_weapon/morii
+ name = "morii"
+ desc = "This camera captures those dying moments one last time."
+ icon_state = "morii"
+ force = 50
+ attack_speed = 2.5
+ damtype = WHITE_DAMAGE
+ attack_verb_continuous = list("bonks", "bashes")
+ attack_verb_simple = list("bonk", "bash")
+ attribute_requirements = list(
+ JUSTICE_ATTRIBUTE = 40
+ )
+
/obj/item/ego_weapon/uturn
name = "u-turn"
desc = "It's a large scythe, that probably hurts a lot."
+ special = "Knocks certain enemies towards you in an area. \
+ This weapon does half damage when attacking 3 or tiles more away."
icon_state = "uturn"
- force = 30
+ force = 40
+ reach = 4
+ attack_speed = 1.3
damtype = RED_DAMAGE
- attack_verb_continuous = list("whips", "lashes", "tears")
- attack_verb_simple = list("whip", "lash", "tear")
- hitsound = 'sound/weapons/whip.ogg'
+ attack_verb_continuous = list("slashes", "slices", "rips", "cuts")
+ attack_verb_simple = list("slash", "slice", "rip", "cut")
+ hitsound = 'sound/weapons/ego/da_capo2.ogg'
attribute_requirements = list(
FORTITUDE_ATTRIBUTE = 40
)
- var/can_spin = TRUE
- var/spinning = FALSE
/obj/item/ego_weapon/uturn/attack(mob/living/target, mob/living/user)
- if(spinning)
- return FALSE
- ..()
- can_spin = FALSE
- addtimer(CALLBACK(src, PROC_REF(spin_reset)), 12)
-
-/obj/item/ego_weapon/uturn/proc/spin_reset()
- can_spin = TRUE
-
-/obj/item/ego_weapon/uturn/attack_self(mob/user)
if(!CanUseEgo(user))
return
- if(!can_spin)
- to_chat(user,span_warning("You attacked too recently."))
- return
- if(do_after(user, 12, src))
- can_spin = TRUE
- addtimer(CALLBACK(src, PROC_REF(spin_reset)), 12)
- playsound(src, 'sound/weapons/ego/harvest.ogg', 75, FALSE, 4)
- for(var/turf/T in orange(1, user))
- new /obj/effect/temp_visual/smash_effect(T)
-
- for(var/mob/living/L in range(1, user))
- var/aoe = 30
- var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
- var/justicemod = 1 + userjust/100
- aoe*=justicemod
- if(L == user || ishuman(L))
- continue
- L.apply_damage(aoe, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ if(get_dist(target, user) > 2)//Spear range for full damage.
+ force = 20
+ . = ..()
+ if(force != initial(force))
+ force = initial(force)
+ var/list/been_hit = list(target)
+ var/turf/end_turf = get_ranged_target_turf_direct(user, target, 4, 0)
+ for(var/turf/T in getline(user, end_turf))
+ if(user in T)
+ continue
+ for(var/turf/T2 in view(T,1))
+ new /obj/effect/temp_visual/smash_effect(T2)
+ for(var/mob/living/L in T2)
+ var/aoe = 10
+ var/userjust = (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE))
+ var/justicemod = 1 + userjust/100
+ aoe*=justicemod
+ aoe*=force_multiplier
+ if(L == user || ishuman(L))
+ continue
+ been_hit = user.HurtInTurf(T2, been_hit, aoe, RED_DAMAGE, hurt_mechs = TRUE, hurt_structure = TRUE)
+ var/atom/throw_target = get_edge_target_turf(L, get_dir(L, get_step_towards(L, get_turf(user))))
+ if(!L.anchored)
+ L.throw_at(throw_target, 1, get_dist(user, L) - 1, user)
+ to_chat(user, MESSAGE_TYPE_WARNING, "You reel in [L]!")
+ to_chat(L, MESSAGE_TYPE_WARNING, "[user] reels you in!")
/obj/item/ego_weapon/giant_tree_branch
name = "giant tree branch"
diff --git a/code/game/objects/items/ego_weapons/special.dm b/code/game/objects/items/ego_weapons/special.dm
index 67167d3f575d..cbd92bb8de5a 100644
--- a/code/game/objects/items/ego_weapons/special.dm
+++ b/code/game/objects/items/ego_weapons/special.dm
@@ -103,14 +103,12 @@
return FALSE
/obj/item/ego_weapon/lance/famiglia/attack(mob/living/target, mob/living/user)
- if(!CanUseEgo(user))
- return
- . = ..()
if(raised)
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
+ knockback = KNOCKBACK_LIGHT
+ else
+ knockback = FALSE
+
+ return ..()
/obj/item/ego_weapon/lance/famiglia/LowerLance(mob/user)
hitsound = 'sound/weapons/ego/spear1.ogg'
diff --git a/code/game/objects/items/ego_weapons/teth.dm b/code/game/objects/items/ego_weapons/teth.dm
index 5292fed1720a..d457cf828da6 100644
--- a/code/game/objects/items/ego_weapons/teth.dm
+++ b/code/game/objects/items/ego_weapons/teth.dm
@@ -58,24 +58,15 @@
/obj/item/ego_weapon/eyes
name = "red eyes"
desc = "It is likely able to hear, touch, smell, as well as see. And most importantly, taste."
- special = "Knocks certain enemies backwards."
icon_state = "eyes"
- force = 35 //Still less DPS, replaces baseball bat
+ force = 35 //Still less DPS, replaces baseball bat
attack_speed = 1.6
damtype = RED_DAMAGE
+ knockback = KNOCKBACK_LIGHT
attack_verb_continuous = list("beats", "smacks")
attack_verb_simple = list("beat", "smack")
hitsound = 'sound/weapons/fixer/generic/gen1.ogg'
-/obj/item/ego_weapon/eyes/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!.)
- return FALSE
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
-
/obj/item/ego_weapon/mini/wrist
name = "wrist cutter"
desc = "The flesh cleanly cut by a sharp tool creates a grotesque pattern with the bloodstains on the suit."
@@ -531,24 +522,15 @@
/obj/item/ego_weapon/sanitizer
name = "sanitizer"
desc = "It's very shocking."
- special = "Knocks certain enemies backwards."
icon_state = "sanitizer"
- force = 35 //Still less DPS, replaces baseball bat
+ force = 35 //Still less DPS, replaces baseball bat?
attack_speed = 1.6
damtype = BLACK_DAMAGE
+ knockback = KNOCKBACK_LIGHT
attack_verb_continuous = list("beats", "smacks")
attack_verb_simple = list("beat", "smack")
hitsound = 'sound/weapons/fixer/generic/gen1.ogg'
-/obj/item/ego_weapon/sanitizer/attack(mob/living/target, mob/living/user)
- . = ..()
- if(!.)
- return FALSE
- var/atom/throw_target = get_edge_target_turf(target, user.dir)
- if(!target.anchored)
- var/whack_speed = (prob(60) ? 1 : 4)
- target.throw_at(throw_target, rand(1, 2), whack_speed, user)
-
/obj/item/ego_weapon/lance/curfew
name = "curfew"
desc = "The thing itself had never forgotten its glory days."
diff --git a/code/game/objects/items/grenades/smokebomb.dm b/code/game/objects/items/grenades/smokebomb.dm
index e83c66bb8667..f1b2ada6cfc8 100644
--- a/code/game/objects/items/grenades/smokebomb.dm
+++ b/code/game/objects/items/grenades/smokebomb.dm
@@ -11,12 +11,12 @@
inhand_icon_state = "smoke"
slot_flags = ITEM_SLOT_BELT
///It's extremely important to keep this list up to date. It helps to generate the insightful description of the smokebomb
- var/static/list/bruh_moment = list("Dank", "Hip", "Lit", "Based", "Robust", "Bruh", "Nyagger")
+ var/static/list/bruh_moment = list("Dank", "Hip", "Lit", "Based", "Robust", "Bruh", "Hoagie Down")
///Here we generate the extremely insightful description.
/obj/item/grenade/smokebomb/Initialize()
. = ..()
- desc = "The word '[pick(bruh_moment)]' is scribbled on it in crayon."
+ desc = "Scribbled on it in crayon is the phrase: '[pick(bruh_moment)]'."
///Here we generate some smoke and also damage blobs??? for some reason. Honestly not sure why we do that.
/obj/item/grenade/smokebomb/detonate(mob/living/lanced_by)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index b24a8c6af8f8..674deed3e6f1 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -67,6 +67,15 @@
return . || mover.throwing || mover.movement_type & checking
return TRUE
+/obj/structure/railing/CanAStarPass(ID, to_dir, caller)
+ if(!density)
+ return TRUE
+ if(dir & to_dir)
+ var/checking = FLYING | FLOATING
+ var/atom/movable/mover = caller
+ return istype(mover) && (mover.throwing || mover.movement_type & checking)
+ return TRUE
+
/obj/structure/railing/corner/CanPass()
..()
return TRUE
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index b8571da4d1e9..3e73f87faae2 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -198,6 +198,20 @@ GLOBAL_LIST_EMPTY(station_turfs)
return TRUE
return FALSE
+/**
+ * Checks whether the specified turf is blocked by something dense inside it, but ignores anything with the climbable trait
+ *
+ * Works similar to is_blocked_turf(), but ignores climbables and has less options. Primarily added for jaunting checks
+ */
+/turf/proc/is_blocked_turf_ignore_climbable()
+ if(density)
+ return TRUE
+
+ for(var/atom/movable/atom_content as anything in contents)
+ if(atom_content.density && !(atom_content.flags_1 & ON_BORDER_1) && !HAS_TRAIT(atom_content, TRAIT_CLIMBABLE))
+ return TRUE
+ return FALSE
+
//zPassIn doesn't necessarily pass an atom!
//direction is direction of travel of air
/turf/proc/zPassIn(atom/movable/A, direction, turf/source)
diff --git a/code/modules/clothing/suits/ego_gear/he.dm b/code/modules/clothing/suits/ego_gear/he.dm
index 3f00877df75c..a9fc4d005276 100644
--- a/code/modules/clothing/suits/ego_gear/he.dm
+++ b/code/modules/clothing/suits/ego_gear/he.dm
@@ -419,6 +419,15 @@ Any attempt to code risk class armor will result in a 10 day Github ban.*/
PRUDENCE_ATTRIBUTE = 40
)
+/obj/item/clothing/suit/armor/ego_gear/he/morii
+ name = "morii"
+ desc = "Those who wear this cloth are trapped wishing to return to a time that is lost to them."
+ icon_state = "morii"
+ armor = list(RED_DAMAGE = 0, WHITE_DAMAGE = 10, BLACK_DAMAGE = 30, PALE_DAMAGE = 30) // 70
+ attribute_requirements = list(
+ JUSTICE_ATTRIBUTE = 40
+ )
+
/obj/item/clothing/suit/armor/ego_gear/he/voodoo
name = "voodoo doll"
desc = "You look like a little doll."
diff --git a/code/modules/mob/living/carbon/human/ego_gifts.dm b/code/modules/mob/living/carbon/human/ego_gifts.dm
index 487d88e636fb..b9ca5bfabea0 100644
--- a/code/modules/mob/living/carbon/human/ego_gifts.dm
+++ b/code/modules/mob/living/carbon/human/ego_gifts.dm
@@ -619,6 +619,13 @@
insight_mod = 3
slot = EYE
+/datum/ego_gifts/morii
+ name = "Morii"
+ icon_state = "morii"
+ temperance_bonus = 2
+ repression_mod = 6
+ slot = EYE
+
/datum/ego_gifts/harmony
name = "Harmony"
icon_state = "harmony"
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index d22d43fbe94d..409ea2078e83 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1393,7 +1393,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return FALSE
user.do_attack_animation(target, atk_effect)
- var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
+ var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh) * (1 + (get_modified_attribute_level(user, JUSTICE_ATTRIBUTE) + get_attribute_level(user, FORTITUDE_ATTRIBUTE)) / 100)
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
@@ -1402,7 +1402,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(atk_effect == ATTACK_EFFECT_KICK || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER)) //kicks never miss (provided your species deals more than 0 damage)
miss_chance = 0
else
- miss_chance = min((user.dna.species.punchdamagehigh/user.dna.species.punchdamagelow) + user.getStaminaLoss() + (user.getBruteLoss()*0.5), 100) //old base chance for a miss + various damage. capped at 100 to prevent weirdness in prob()
+ miss_chance = min((user.dna.species.punchdamagehigh/user.dna.species.punchdamagelow) + user.getStaminaLoss() + (user.getBruteLoss() * 50 / user.maxHealth), 100) //old base chance for a miss + various damage. capped at 100 to prevent weirdness in prob()
if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
playsound(target.loc, user.dna.species.miss_sound, 25, TRUE, -1)
@@ -1412,7 +1412,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
log_combat(user, target, "attempted to punch")
return FALSE
- var/armor_block = target.run_armor_check(affecting, MELEE)
+ var/armor_block = target.run_armor_check(affecting, RED_DAMAGE)
playsound(target.loc, user.dna.species.attack_sound, 25, TRUE, -1)
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/censored.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/censored.dm
index e7e7e53bc650..43d885a588bb 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/censored.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/censored.dm
@@ -173,10 +173,7 @@
Beam(T, "censored", time = 10)
playsound(src, 'sound/weapons/ego/censored3.ogg', 75, FALSE, 5)
for(var/turf/TT in turf_list)
- for(var/mob/living/L in TT)
- if(faction_check_mob(L))
- continue
- L.apply_damage(ability_damage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
+ for(var/mob/living/L in HurtInTurf(TT, list(), ability_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, hurt_structure = TRUE))
new /obj/effect/temp_visual/dir_setting/bloodsplatter(get_turf(L), pick(GLOB.alldirs))
can_act = TRUE
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/last_shot.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/last_shot.dm
index cc0b96e7dd35..9507c518acea 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/last_shot.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/last_shot.dm
@@ -5,6 +5,7 @@ GLOBAL_LIST_EMPTY(meat_list)
desc = "A large ball of flesh, pulsating slowly."
icon = 'ModularTegustation/Teguicons/48x48.dmi'
icon_state = "last_shot"
+ portrait = "last_shot"
pixel_x = -8
base_pixel_x = -8
maxHealth = 3100
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/melting_love.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/melting_love.dm
index 27bb25984bb6..2254daf3402e 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/melting_love.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/melting_love.dm
@@ -15,7 +15,7 @@
threat_level = ALEPH_LEVEL
health = 4000
maxHealth = 4000
- obj_damage = 600
+ obj_damage = 60
damage_coeff = list(RED_DAMAGE = -1, WHITE_DAMAGE = 1, BLACK_DAMAGE = 1.5, PALE_DAMAGE = 0.8)
melee_damage_type = BLACK_DAMAGE
melee_damage_lower = 55
@@ -97,17 +97,15 @@
return .
// AOE attack
- if(isliving(target))
+ if(isliving(target) || ismecha(target))
new /obj/effect/gibspawner/generic/silent/melty_slime(get_turf(target))
for(var/turf/open/T in view(1, target))
var/obj/effect/temp_visual/small_smoke/halfsecond/S = new(T)
S.color = "#FF0081"
- for(var/mob/living/L in view(1, target))
- if(faction_check_mob(L))
- continue
- L.apply_damage(radius_damage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
- L.apply_status_effect(STATUS_EFFECT_SLIMED)
-
+ var/list/got_hit = list()
+ got_hit = HurtInTurf(T, got_hit, radius_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE)
+ for(var/mob/living/L in got_hit)
+ L.apply_status_effect(STATUS_EFFECT_SLIMED)
return ..()
/mob/living/simple_animal/hostile/abnormality/melting_love/Move()
@@ -257,7 +255,7 @@
/* Stats */
health = 400
maxHealth = 400
- obj_damage = 200
+ obj_damage = 60
damage_coeff = list(RED_DAMAGE = -1, WHITE_DAMAGE = 1, BLACK_DAMAGE = 2, PALE_DAMAGE = 1)
melee_damage_type = BLACK_DAMAGE
melee_damage_lower = 20
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/mountain.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/mountain.dm
index 3e2a73d2f5c1..ffdd0b050f80 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/mountain.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/mountain.dm
@@ -117,7 +117,7 @@
if(prob(35) && OpenFire())
return
. = ..()
- if(.)
+ if(. && isliving(target))
var/mob/living/L = target
if(isliving(target) && (L.health < 0 || L.stat == DEAD))
finishing = TRUE
@@ -268,7 +268,7 @@
playsound(get_turf(src), 'sound/abnormalities/mountain/scream.ogg', 75, 1, 5)
var/list/been_hit = list()
for(var/turf/T in view(7, src))
- HurtInTurf(T, been_hit, scream_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, TRUE)
+ HurtInTurf(T, been_hit, scream_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, hurt_hidden = TRUE)
/mob/living/simple_animal/hostile/abnormality/mountain/proc/Slam(range)
if(slam_cooldown > world.time)
@@ -279,7 +279,7 @@
var/list/been_hit = list()
for(var/turf/open/T in view(2, src))
new /obj/effect/temp_visual/small_smoke/halfsecond(T)
- HurtInTurf(T, been_hit, slam_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE)
+ HurtInTurf(T, been_hit, slam_damage, BLACK_DAMAGE, null, TRUE, FALSE, TRUE, hurt_hidden = FALSE, hurt_structure = TRUE, break_not_destroy = TRUE)
/mob/living/simple_animal/hostile/abnormality/mountain/proc/Spit(atom/target)
if(spit_cooldown > world.time)
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/nothing_there.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/nothing_there.dm
index 45516d4c0a2b..dd6808f81fd4 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/nothing_there.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/nothing_there.dm
@@ -330,7 +330,7 @@
if(TF.density)
continue
new /obj/effect/temp_visual/smash_effect(TF)
- been_hit = HurtInTurf(TF, been_hit, hello_damage, RED_DAMAGE, null, TRUE, FALSE, TRUE, TRUE)
+ been_hit = HurtInTurf(TF, been_hit, hello_damage, RED_DAMAGE, null, TRUE, FALSE, TRUE, hurt_structure = TRUE)
for(var/mob/living/L in been_hit)
if(L.health < 0)
L.gib()
@@ -349,7 +349,7 @@
SLEEP_CHECK_DEATH(8)
for(var/turf/T in view(2, src))
new /obj/effect/temp_visual/nt_goodbye(T)
- for(var/mob/living/L in HurtInTurf(T, list(), goodbye_damage, RED_DAMAGE, null, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE))
+ for(var/mob/living/L in HurtInTurf(T, list(), goodbye_damage, RED_DAMAGE, null, TRUE, FALSE, TRUE, hurt_hidden = TRUE, hurt_structure = TRUE))
if(L.health < 0)
L.gib()
playsound(get_turf(src), 'sound/abnormalities/nothingthere/goodbye_attack.ogg', 75, 0, 7)
diff --git a/code/modules/mob/living/simple_animal/abnormality/aleph/space_lady.dm b/code/modules/mob/living/simple_animal/abnormality/aleph/space_lady.dm
index 9b0142d789f2..b42a15dcde10 100644
--- a/code/modules/mob/living/simple_animal/abnormality/aleph/space_lady.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/aleph/space_lady.dm
@@ -4,6 +4,7 @@
icon = 'ModularTegustation/Teguicons/32x48.dmi'
icon_state = "space"
icon_living = "space"
+ portrait = "space"
del_on_death = TRUE
maxHealth = 3200
health = 3200
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/KHz.dm b/code/modules/mob/living/simple_animal/abnormality/he/KHz.dm
index 7dd3b18c60b5..e91b60fab2af 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/KHz.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/KHz.dm
@@ -4,6 +4,7 @@
desc = "A ham radio resting on a table."
icon = 'ModularTegustation/Teguicons/32x32.dmi'
icon_state = "radio"
+ portrait = "khz"
maxHealth = 400
health = 400
threat_level = HE_LEVEL
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/KQE.dm b/code/modules/mob/living/simple_animal/abnormality/he/KQE.dm
index 89045f5306b1..ad4d8534c669 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/KQE.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/KQE.dm
@@ -94,7 +94,7 @@
if(!LAZYLEN(GLOB.department_centers))
heart = TRUE
else
- damage_coeff = list(RED_DAMAGE = 0.3, WHITE_DAMAGE = 0.2, BLACK_DAMAGE = 0.2, PALE_DAMAGE = 0.2)//In regular gamemodes you are now esentially forced to suppress the heart
+ ChangeResistances(list(RED_DAMAGE = 0.3, WHITE_DAMAGE = 0.2, BLACK_DAMAGE = 0.2, PALE_DAMAGE = 0.2)) //In regular gamemodes you are now esentially forced to suppress the heart
var/X = pick(GLOB.department_centers)
var/mob/living/simple_animal/hostile/kqe_heart/H = new(get_turf(X))
heart = H
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/better_memories.dm b/code/modules/mob/living/simple_animal/abnormality/he/better_memories.dm
new file mode 100644
index 000000000000..fb6faa25dd81
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/abnormality/he/better_memories.dm
@@ -0,0 +1,549 @@
+#define MEMORY_DEBUFF /datum/status_effect/display/better_memories_curse
+#define CAMERAFLASH_RANGE 7
+
+/mob/living/simple_animal/hostile/abnormality/better_memories
+ name = "Memories from a Better Time"
+ desc = "A gate of pipes or wires that hold a old TV ontop of it. \
+ Piled on the floor around the gate is birthday cards, old envelopes, \
+ and worn photographs. The inside of the gate is a dark void with a \
+ distant pink light."
+ icon = 'ModularTegustation/Teguicons/64x64.dmi'
+ icon_state = "better_memories"
+ portrait = "better_memories"
+ pixel_x = -16
+ base_pixel_x = -16
+ threat_level = HE_LEVEL
+ melee_damage_type = RED_DAMAGE
+ retreat_distance = 3
+ minimum_distance = 3
+ ranged = TRUE
+ start_qliphoth = 2
+ work_chances = list(
+ ABNORMALITY_WORK_INSTINCT = 20,
+ ABNORMALITY_WORK_INSIGHT = list(20, 30, 30, 30, 30),
+ ABNORMALITY_WORK_ATTACHMENT = list(20, 20, 40, 50, 50),
+ ABNORMALITY_WORK_REPRESSION = list(60, 65, 40, 20, 20)
+ )
+ work_damage_amount = 10
+ work_damage_type = WHITE_DAMAGE
+
+ ego_list = list(
+ /datum/ego_datum/armor/morii,
+ /datum/ego_datum/weapon/morii
+ )
+ gift_type = /datum/ego_gifts/morii
+ //Abnormality Jam Submission
+ abnormality_origin = ABNORMALITY_ORIGIN_ORIGINAL
+ var/minions = 0
+
+/mob/living/simple_animal/hostile/abnormality/better_memories/Login()
+ . = ..()
+ if(!. || !client)
+ return FALSE
+ to_chat(src, "Memories of a Better Time produces a minion when it reaches ZeroQliphoth. \
+ Your mind will be automatically sent to this minion on breach. \
+ The minion is a fast moving entity with a weak red damage stab. Its real power comes \
+ from its ability to apply prudence debuffs to humanoids using its flash ability. \
+ While using your ability your immobilized for 1.5 seconds. It is suggested to use a \
+ hit and run playstyle.")
+
+// Those with low temperance will find a memory in the pile.
+/mob/living/simple_animal/hostile/abnormality/better_memories/PostWorkEffect(mob/living/carbon/human/user, work_type, pe, work_time)
+ if(get_attribute_level(user, TEMPERANCE_ATTRIBUTE) <= 60)
+ datum_reference.qliphoth_change(-1)
+ user.apply_status_effect(MEMORY_DEBUFF)
+ return
+
+// Better memories can have 3 seperate minions who will terroize the facility. Code modified from luna.dm
+/mob/living/simple_animal/hostile/abnormality/better_memories/ZeroQliphoth(mob/living/carbon/human/user)
+ if(minions >= 3)
+ return FALSE
+ var/mob/living/breaching_minion
+ //Normal breach
+ if(!IsCombatMap())
+ var/turf/W = pick(GLOB.department_centers)
+ breaching_minion = SpawnMinion(get_turf(W))
+
+ //--Side Gamemodes stuff--
+ else
+ breaching_minion = SpawnMinion(get_turf(src))
+ QDEL_IN(src, 1 SECONDS) //Destroys the core, as it is unecessary in Rcorp.
+
+ if(client)
+ mind.transfer_to(breaching_minion) //For playable abnormalities, directly lets the playing currently controlling core get control of the spawned mob
+ return
+
+/mob/living/simple_animal/hostile/abnormality/better_memories/proc/SpawnMinion(turf/spawn_turf)
+ var/mob/living/simple_animal/hostile/better_memories_minion/spawningmonster = new(spawn_turf)
+ RegisterSignal(spawningmonster, COMSIG_PARENT_QDELETING, PROC_REF(MinionSlain))
+ minions++
+ return spawningmonster
+
+/mob/living/simple_animal/hostile/abnormality/better_memories/proc/MinionSlain()
+ SIGNAL_HANDLER
+ minions--
+
+
+//Minion Spawn
+/mob/living/simple_animal/hostile/better_memories_minion
+ name = "Memories from a Better Time"
+ desc = "A human with a old styled camera for a head and 8 slender spider legs."
+ icon = 'ModularTegustation/Teguicons/64x64.dmi'
+ icon_state = "better_memories_a"
+ base_pixel_x = -16
+ pixel_x = -16
+ health = 1000
+ maxHealth = 1000
+ melee_damage_type = RED_DAMAGE
+ damage_coeff = list(RED_DAMAGE = 1.5, WHITE_DAMAGE = 1.5, BLACK_DAMAGE = 0.5, PALE_DAMAGE = 0.5)
+ melee_damage_lower = 4
+ melee_damage_upper = 8
+ rapid_melee = 2
+ move_to_delay = 2
+ robust_searching = TRUE
+ ranged = TRUE
+ ranged_cooldown_time = 4 SECONDS
+ stat_attack = HARD_CRIT
+ del_on_death = TRUE
+ attack_verb_continuous = "jabs"
+ attack_verb_simple = "jab"
+ can_patrol = TRUE
+ patrol_cooldown_time = 10 SECONDS
+ var/can_act = TRUE
+ //For when the creature is fleeing
+ var/fleeing_now = FALSE
+ //Variables used to keep track of who each memory is hunting
+ var/current_target
+ var/list/static/hunt_targets = list()
+
+/mob/living/simple_animal/hostile/abnormality/better_memories/Login()
+ . = ..()
+ if(!. || !client)
+ return FALSE
+ to_chat(src, "You are a minion of Memories from a Better Time. \
+ What you actually are is up for debate, but you do know that \
+ you function best as a hit and run fighter using your camera \
+ flash attack to reduce the work success rate, temperance, and \
+ prudence of any agents caught in its sight. Your natural AI \
+ would seek out those who were working on abnormalities and \
+ snap a picture of them.")
+
+/mob/living/simple_animal/hostile/better_memories_minion/Move()
+ if(!can_act)
+ return FALSE
+ return ..()
+
+//The creature can walk over entities that are human sized or smaller.
+/mob/living/simple_animal/hostile/better_memories_minion/CanPassThrough(atom/blocker, turf/target, blocker_opinion)
+ if(isliving(blocker))
+ var/mob/living/M = blocker
+ if(M.mob_size <= MOB_SIZE_HUMAN || (patrol_path.len && M.type == type))
+ return TRUE
+ return ..()
+
+//Directional Cone Flash Attack that applies a work success and stat debuff.
+/mob/living/simple_animal/hostile/better_memories_minion/OpenFire()
+ if(!can_act)
+ return FALSE
+ if(ranged_cooldown > world.time)
+ return FALSE
+ //Measure once.
+ var/targ_dist = get_dist(src, target)
+ if(targ_dist >= (CAMERAFLASH_RANGE - 1))
+ return FALSE
+ if(!AngleCamera(target))
+ if(!client)
+ retreat_distance = null
+ minimum_distance = 1
+ return FALSE
+ can_act = FALSE
+ CameraFlash(src)
+ ranged_cooldown = world.time + ranged_cooldown_time
+ can_act = TRUE
+ if(!client)
+ FleeNow(FindWorking())
+
+/*
+* If patrolling only target people who are working because your
+* most likely hunting a working employee. If the target has the
+* debuff ignore them unless they have done more than 50 damage
+* to you.
+*/
+/mob/living/simple_animal/hostile/better_memories_minion/CanAttack(atom/the_target)
+ . = ..()
+ if(!ishuman(the_target))
+ return
+ var/mob/living/carbon/human/H = the_target
+ if(patrol_path.len)
+ if(!H.is_working)
+ return FALSE
+ if(target_memory[the_target] <= 100)
+ return FALSE
+ if(H.has_status_effect(MEMORY_DEBUFF))
+ //You have inflicted 100 damage to us. Get jabbed.
+ if(target_memory[the_target] <= 100)
+ return FALSE
+
+/mob/living/simple_animal/hostile/better_memories_minion/AttackingTarget()
+ if(!can_act)
+ return FALSE
+ if(!client)
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ /* Dont jab those standing
+ still for their picture.
+ Death is not our goal */
+ if(H.is_working)
+ OpenFire()
+ return
+ if(OpenFire())
+ return
+ return ..()
+
+//Experiment with construct.dm code where the artificers have a melee range condition.
+/mob/living/simple_animal/hostile/better_memories_minion/MoveToTarget(list/possible_targets)
+ ..()
+ //If not human then attack with jabs
+ if(!ishuman(target))
+ retreat_distance = null
+ minimum_distance = 1
+ return
+ else
+ var/mob/living/carbon/human/H = target
+ //If your working and cannot move or are adjacent to me or my health is below 30%. Im going to jab you.
+ if((!H.is_working && ((get_dist(get_turf(src), get_turf(target)) <= 1)) || health < maxHealth * 0.3))
+ retreat_distance = null
+ minimum_distance = 1
+ return
+ retreat_distance = 4
+ minimum_distance = 4
+
+/mob/living/simple_animal/hostile/better_memories_minion/patrol_select()
+ //Due to some weird thing the values in hunt_target become null so this empty's the list out before it gets too long.
+ if(hunt_targets.len > 5)
+ hunt_targets.Cut()
+ var/turf/target_turf
+ if(ranged_cooldown <= world.time)
+ target_turf = FindWorking()
+ current_target = target_turf
+ hunt_targets += target_turf
+
+ if(istype(target_turf))
+ patrol_path = get_path_to(src, target_turf, /turf/proc/Distance_cardinal, 0, 200)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/better_memories_minion/patrol_reset()
+ . = ..()
+ FindTarget()
+ if(current_target)
+ hunt_targets -= current_target
+ current_target = null
+
+/*
+* This is embarrassing code i had 2 choices and it was to add
+* a overridable proc in line 169 of hostile.dm to override the
+* goto that i had put there or create this monster of a code.
+* -IP
+*/
+/mob/living/simple_animal/hostile/better_memories_minion/bullet_act(obj/projectile/P)
+ if(fleeing_now == TRUE)
+ if(prob(50))
+ visible_message(span_userdanger("[src] dodges the [P]!"))
+ return BULLET_ACT_FORCE_PIERCE
+ //Stop ignoring damage
+ StopFleeing()
+ if(patrol_path.len && stat == CONSCIOUS && AIStatus != AI_OFF && !client)
+ var/secondarmor = run_armor_check(null, P.damage_type, "","",P.armour_penetration)
+ var/second_on_hit_state = P.on_hit(src, secondarmor)
+ if(!P.nodamage && second_on_hit_state != BULLET_ACT_BLOCK)
+ apply_damage(P.damage, P.damage_type, null, secondarmor)
+ apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, secondarmor, P.stamina, P.jitter, P.paralyze, P.immobilize)
+ if(P.firer)
+ RegisterAggroValue(P.firer, P.damage, P.damage_type)
+ //If the projectile had no firer then just list it as nobuddy
+ if(!P.firer)
+ if(target_memory["nobuddy"] > 100)
+ patrol_reset()
+ //If our damage value for that person exceeds this number then we consider targeting them.
+ if(target_memory[P.firer] > 100)
+ FindTarget(list(P.firer), 1)
+ return second_on_hit_state
+ return ..()
+
+/mob/living/simple_animal/hostile/better_memories_minion/attacked_by(obj/item/I, mob/living/L)
+ //Stolen MOSB code.
+ if(!client && LAZYLEN(patrol_path) && CanAttack(L))
+ L.attack_animal(src)
+ if(fleeing_now == TRUE && prob(50))
+ StopFleeing()
+ return ..()
+
+/mob/living/simple_animal/hostile/better_memories_minion/CanStartPatrol()
+ if(!fleeing_now)
+ return !(status_flags & GODMODE)
+ return ..()
+
+//Prevents accumulation of hate when actively fleeing.
+/mob/living/simple_animal/hostile/better_memories_minion/RegisterAggroValue(atom/remembered_target, value, damage_type)
+ if(fleeing_now || !can_act)
+ return
+ ..()
+
+/*
+* Modified patrol code since due to it not returning a location i cant
+* use it for flee now() and i dont know if i have the authority to
+* change any of that code since it goes outside the purpose of this.
+* -IP
+*/
+
+/mob/living/simple_animal/hostile/better_memories_minion/proc/FleeDest()
+ //Mobs should stay unpatroled on maps where they're intended to be possessed.
+ if(SSmaptype.maptype in SSmaptype.autopossess)
+ return
+ //Due to some weird thing the values in hunt_target become null so this empty's the list out before it gets too long.
+ if(hunt_targets.len > 5)
+ hunt_targets.Cut()
+
+ var/turf/target_turf
+ if(ranged_cooldown <= world.time)
+ target_turf = FindWorking()
+ current_target = target_turf
+ hunt_targets += target_turf
+
+ if(istype(target_turf))
+ return target_turf
+
+ //Return Department Center instead.
+ if(!LAZYLEN(GLOB.department_centers))
+ return
+ var/list/potential_centers = list()
+ for(var/pos_targ in GLOB.department_centers)
+ var/possible_center_distance = get_dist(src, pos_targ)
+ if(possible_center_distance > 4 && possible_center_distance < 46)
+ potential_centers += pos_targ
+ if(LAZYLEN(potential_centers))
+ return get_turf(pick(potential_centers))
+
+/*
+* Modified big_wolf flee code. This creature focuses only on
+* escaping, ignoring all hostiles and attacks for 1.5 seconds.
+*/
+/mob/living/simple_animal/hostile/better_memories_minion/proc/FleeNow(turf/target_dest)
+ if(health < maxHealth * 0.2)
+ return
+ toggle_ai(AI_OFF)
+ walk_to(src, 0)
+ TemporarySpeedChange(-1, 1.5 SECONDS)
+ fleeing_now = TRUE
+ target_memory.Cut()
+ target = null
+ //Eh whatever make them not instantly patrol again upon reaching their destination.
+ patrol_cooldown = world.time + patrol_cooldown_time
+ if(patrol_to(FleeDest()))
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_animal/hostile/better_memories_minion, StopFleeing)), 1.5 SECONDS)
+ return
+ StopFleeing()
+
+/mob/living/simple_animal/hostile/better_memories_minion/proc/StopFleeing()
+ fleeing_now = FALSE
+ toggle_ai(AI_ON)
+
+//This proc is for preventing the camera from firing at a target that is in its blind spot.
+/mob/living/simple_animal/hostile/better_memories_minion/proc/AngleCamera(atom/cam_focus)
+ switch(Get_Angle(src, cam_focus))
+ //North 0 angle
+ if(340 to 360)
+ return TRUE
+ if(0 to 20)
+ return TRUE
+ //South 180 angle
+ if(160 to 200)
+ return TRUE
+ //East 90 angle
+ if(70 to 110)
+ return TRUE
+ //West 270 angle
+ if(250 to 290)
+ return TRUE
+
+ /*Attack code stolen from cone_spells.dm
+ This proc creates a list of turfs that are hit by the cone */
+/mob/living/simple_animal/hostile/better_memories_minion/proc/CameraFlash(mob/living/user)
+ var/blind_direction
+ if(client)
+ blind_direction = user.dir
+ else
+ blind_direction = get_cardinal_dir(get_turf(src), get_turf(target))
+ var/list/cone_turfs = ConeHelper(get_turf(user), blind_direction)
+ for(var/list/turf_list in cone_turfs)
+ DoConeEffects(turf_list, user, TRUE)
+
+ playsound(loc, 'sound/weapons/armbomb.ogg', 75, TRUE, -3)
+ if(do_after(user, 1.5 SECONDS, target = user))
+ for(var/list/turf_list in cone_turfs)
+ DoConeEffects(turf_list, user, FALSE)
+ playsound(loc, 'sound/effects/pop_expl.ogg', 75, TRUE, -3)
+
+/mob/living/simple_animal/hostile/better_memories_minion/proc/ConeHelper(turf/starter_turf, dir_to_use, cone_levels = CAMERAFLASH_RANGE)
+ var/list/turfs_to_return = list()
+ var/turf/turf_to_use = starter_turf
+ var/turf/left_turf
+ var/turf/right_turf
+ var/right_dir
+ var/left_dir
+ switch(dir_to_use)
+ if(NORTH)
+ left_dir = WEST
+ right_dir = EAST
+ if(SOUTH)
+ left_dir = EAST
+ right_dir = WEST
+ if(EAST)
+ left_dir = NORTH
+ right_dir = SOUTH
+ if(WEST)
+ left_dir = SOUTH
+ right_dir = NORTH
+
+ for(var/i in 1 to cone_levels)
+ if(i == 1)
+ continue
+ var/list/level_turfs = list()
+ turf_to_use = get_step(turf_to_use, dir_to_use)
+ level_turfs += turf_to_use
+ if(i != 1)
+ left_turf = get_step(turf_to_use, left_dir)
+ level_turfs += left_turf
+ right_turf = get_step(turf_to_use, right_dir)
+ level_turfs += right_turf
+ for(var/left_i in 1 to i -CalculateConeShape(i))
+ if(left_turf.density)
+ break
+ left_turf = get_step(left_turf, left_dir)
+ level_turfs += left_turf
+ for(var/right_i in 1 to i -CalculateConeShape(i))
+ if(right_turf.density)
+ break
+ right_turf = get_step(right_turf, right_dir)
+ level_turfs += right_turf
+ turfs_to_return += list(level_turfs)
+ if(i == cone_levels)
+ continue
+ if(turf_to_use.density)
+ break
+ return turfs_to_return
+
+ ///This proc does obj, mob and turf cone effects on all targets in a list
+/mob/living/simple_animal/hostile/better_memories_minion/proc/DoConeEffects(list/target_turf_list, mob/user, telegraph)
+ for(var/target_turf in target_turf_list)
+ //if turf is no longer there
+ if(!target_turf)
+ continue
+ if(telegraph)
+ DoConeTurfEffects(target_turf, 1)
+ if(!telegraph)
+ DoConeTurfEffects(target_turf, 2)
+ if(isopenturf(target_turf))
+ var/turf/open/open_turf = target_turf
+ for(var/mob/living/movable_content in open_turf)
+ if(isliving(movable_content))
+ DoConeMobEffect(movable_content)
+
+ ///This proc deterimines how the spell will affect turfs.
+/mob/living/simple_animal/hostile/better_memories_minion/proc/DoConeTurfEffects(turf/target_turf, type)
+ if(type == 1)
+ new /obj/effect/temp_visual/sparkles/purple(target_turf)
+ if(type == 2)
+ new /obj/effect/temp_visual/dir_setting/ninja/phase(target_turf)
+
+ ///This proc deterimines how the spell will affect mobs.
+/mob/living/simple_animal/hostile/better_memories_minion/proc/DoConeMobEffect(mob/living/target_mob)
+ if(ishuman(target_mob))
+ target_mob.apply_status_effect(MEMORY_DEBUFF)
+
+ ///This proc adjusts the cones width depending on the level.
+/mob/living/simple_animal/hostile/better_memories_minion/proc/CalculateConeShape(current_level)
+ var/end_taper_start = round(CAMERAFLASH_RANGE * 0.8)
+ if(current_level > end_taper_start)
+ //someone more talented and probably come up with a better formula.
+ return (current_level % end_taper_start) * 2
+ else
+ return 2
+
+//Returns the location of a employee who is currently working.
+/mob/living/simple_animal/hostile/better_memories_minion/proc/FindWorking()
+ var/list/low_priority_turfs = list()
+ var/list/medium_priority_turfs = list()
+ for(var/mob/living/carbon/human/H in GLOB.human_list)
+ var/turf/temp_turf_memory
+ if(H.z != z)
+ continue
+ if(get_dist(src, H) < 8)
+ continue
+ if(H.stat == DEAD)
+ continue
+ if(H.has_status_effect(MEMORY_DEBUFF))
+ continue
+ if(H.is_working)
+ if(get_dist(src, H) > 24) // Way too far
+ temp_turf_memory = locate(H.x, H.y - 1, z)
+ //Dont go to a turf someone is already going to
+ if(temp_turf_memory in hunt_targets)
+ continue
+ low_priority_turfs += temp_turf_memory
+ continue
+ temp_turf_memory = locate(H.x, H.y - 1, z)
+ //Dont go to a turf someone is already going to
+ if(temp_turf_memory in hunt_targets)
+ continue
+ medium_priority_turfs += temp_turf_memory
+
+ if(LAZYLEN(medium_priority_turfs))
+ . = get_closest_atom(/turf/open, medium_priority_turfs, src)
+ else if(LAZYLEN(low_priority_turfs))
+ . = get_closest_atom(/turf/open, low_priority_turfs, src)
+
+//Debuff Status Effect
+/datum/status_effect/display/better_memories_curse
+ id = "better_memories_curse"
+ status_type = STATUS_EFFECT_UNIQUE
+ duration = 15 SECONDS
+ tick_interval = 50
+ alert_type = null
+ display_name = "down_arrow"
+
+/datum/status_effect/display/better_memories_curse/on_apply()
+ . = ..()
+ var/mob/living/carbon/human/L = owner
+ L.adjust_attribute_buff(PRUDENCE_ATTRIBUTE, -30)
+ L.adjust_attribute_buff(TEMPERANCE_ATTRIBUTE, -30)
+ L.physiology.work_success_mod -= 0.25
+ to_chat(owner, span_warning("Your distracted by memories of your past."))
+
+/datum/status_effect/display/better_memories_curse/tick()
+ . = ..()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/L = owner
+ if(L.sanity_lost || L.stat == DEAD)
+ qdel(src)
+ L.apply_damage(10, WHITE_DAMAGE, null, L.run_armor_check(null, WHITE_DAMAGE), spread_damage = FALSE)
+ //Unsure if these statements explain what is happening to your character but its enough. -IP
+ to_chat(owner, pick(
+ span_warning("You have trouble recalling your life before this job."),
+ span_warning("You forget your happiest moments."),
+ span_warning("You wonder why your face is wet with tears."),
+ span_warning("You try your best to hold onto the memory of your loved ones."),
+ span_warning("Your forced to reminiscence on a happier time, then its gone."),
+ ))
+
+/datum/status_effect/display/better_memories_curse/on_remove()
+ var/mob/living/carbon/human/L = owner
+ L.adjust_attribute_buff(PRUDENCE_ATTRIBUTE, 30)
+ L.adjust_attribute_buff(TEMPERANCE_ATTRIBUTE, 30)
+ L.physiology.work_success_mod += 0.25
+ return ..()
+
+#undef MEMORY_DEBUFF
+#undef CAMERAFLASH_RANGE
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/eris.dm b/code/modules/mob/living/simple_animal/abnormality/he/eris.dm
index 186d925c336c..03b59a3c1849 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/eris.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/eris.dm
@@ -4,6 +4,7 @@
icon = 'ModularTegustation/Teguicons/48x48.dmi'
icon_state = "eris"
icon_living = "eris"
+ portrait = "eris"
maxHealth = 1100
health = 1100
ranged = TRUE
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/funeral.dm b/code/modules/mob/living/simple_animal/abnormality/he/funeral.dm
index 8ad109dd42f3..73c59951dc33 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/funeral.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/funeral.dm
@@ -222,7 +222,7 @@
/mob/living/simple_animal/hostile/abnormality/funeral/proc/SwarmTurfLinger(turf/T)
for(var/i = 1 to 40) //40 times
- for(var/mob/living/carbon/human/H in HurtInTurf(T, list(), swarm_damage, WHITE_DAMAGE, check_faction = TRUE))
+ for(var/mob/living/carbon/human/H in HurtInTurf(T, list(), swarm_damage, WHITE_DAMAGE, check_faction = TRUE, hurt_mechs = TRUE))
if(H.stat == DEAD)
continue
if(H.sanity_lost)
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/headless_ichthys.dm b/code/modules/mob/living/simple_animal/abnormality/he/headless_ichthys.dm
index fa8cac1b6ade..ee839fa2812e 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/headless_ichthys.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/headless_ichthys.dm
@@ -77,7 +77,7 @@
// Attacks
/mob/living/simple_animal/hostile/abnormality/headless_ichthys/proc/IchthysJump(mob/living/target)
- if(!istype(target) || !can_act)
+ if(!isliving(target) && !ismecha(target) || !can_act)
return
var/dist = get_dist(target, src)
if(dist > 1 && jump_cooldown < world.time)
@@ -106,6 +106,8 @@
L.apply_damage(jump_damage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
if(L.health < 0)
L.gib()
+ for(var/obj/vehicle/sealed/mecha/V in T)
+ V.take_damage(jump_damage, BLACK_DAMAGE)
SLEEP_CHECK_DEATH(0.5 SECONDS)
can_act = TRUE
@@ -145,7 +147,8 @@
for(var/turf/TF in hit_line)
if(TF.density)
break
- for(var/mob/living/L in range(1, TF))
+ var/list/turfs_to_check = range(1, TF)
+ for(var/mob/living/L in turfs_to_check)
if(L.status_flags & GODMODE)
continue
if(L == src) //stop hitting yourself
@@ -161,6 +164,11 @@
already_hit += L
var/truedamage = ishuman(L) ? beam_damage : beam_damage/2 //half damage dealt to nonhumans
L.apply_damage(truedamage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE))
+ for(var/obj/vehicle/sealed/mecha/V in turfs_to_check)
+ if(V in already_hit)
+ continue
+ V.take_damage(beam_damage, BLACK_DAMAGE, attack_dir = get_dir(V, src))
+ already_hit += V
SLEEP_CHECK_DEATH(1.71)
QDEL_NULL(current_beam)
SLEEP_CHECK_DEATH(4 SECONDS) //Rest after laser beam
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/helper.dm b/code/modules/mob/living/simple_animal/abnormality/he/helper.dm
index 2984f0d93b7d..04a31312aebf 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/helper.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/helper.dm
@@ -49,6 +49,7 @@
var/dash_cooldown = 0
var/dash_cooldown_time = 8 SECONDS
var/list/been_hit = list() // Don't get hit twice.
+ var/stuntime = 5 SECONDS
//PLAYABLES ATTACKS
attack_action_types = list(/datum/action/innate/abnormality_attack/toggle/helper_dash_toggle)
@@ -144,7 +145,7 @@
INVOKE_ASYNC(D, TYPE_PROC_REF(/obj/machinery/door, open), 2)
if(stop_charge)
playsound(src, 'sound/abnormalities/helper/disable.ogg', 75, 1)
- SLEEP_CHECK_DEATH(5 SECONDS)
+ SLEEP_CHECK_DEATH(stuntime)
charging = FALSE
return
forceMove(T)
@@ -153,7 +154,8 @@
para = FALSE
SpinAnimation(3, 1, para)
playsound(src, "sound/abnormalities/helper/move0[pick(1,2,3)].ogg", rand(50, 70), 1)
- for(var/mob/living/L in range(1, T))
+ var/list/hit_turfs = range(1, T)
+ for(var/mob/living/L in hit_turfs)
if(!faction_check_mob(L))
if(L in been_hit)
continue
@@ -162,19 +164,22 @@
playsound(L, attack_sound, 75, 1)
var/turf/LT = get_turf(L)
new /obj/effect/temp_visual/kinetic_blast(LT)
- if(ishuman(L))
- var/mob/living/carbon/human/H = L
- // Ugly code
- var/affecting = get_bodypart(ran_zone(pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)))
- var/armor = H.run_armor_check(affecting, melee_damage_type, armour_penetration = src.armour_penetration)
- H.apply_damage(60, src.melee_damage_type, affecting, armor, wound_bonus = src.wound_bonus, bare_wound_bonus = src.bare_wound_bonus, sharpness = src.sharpness)
- else
- L.adjustRedLoss(120)
+ var/damage = 60
+ if(!ishuman(L))
+ damage = 120
+ L.apply_damage(damage, melee_damage_type, null, L.run_armor_check(null, melee_damage_type), spread_damage = TRUE)
if(L.stat >= HARD_CRIT)
L.gib()
continue
- if(!(L in been_hit))
- been_hit += L
+ been_hit += L
+ for(var/obj/vehicle/sealed/mecha/V in hit_turfs)
+ if(V in been_hit)
+ continue
+ visible_message(span_boldwarning("[src] runs through [V]!"))
+ to_chat(V.occupants, span_userdanger("[src] pierces your mech with their spinning blades!"))
+ playsound(V, attack_sound, 75, 1)
+ V.take_damage(60, melee_damage_type, attack_dir = get_dir(V, src))
+ been_hit += V
addtimer(CALLBACK(src, PROC_REF(do_dash), move_dir, (times_ran + 1)), 1)
/* Work effects */
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/jangsan.dm b/code/modules/mob/living/simple_animal/abnormality/he/jangsan.dm
index 7d35c46e3fd8..40bfe79ff8c6 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/jangsan.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/jangsan.dm
@@ -1,3 +1,5 @@
+#define JANGSAN_FEAR_COOLDOWN (8 SECONDS)
+
//Code by Coxswain, EGO sprites by Sky_ and abnormality sprites by Mel
/mob/living/simple_animal/hostile/abnormality/jangsan
name = "Jangsan Tiger"
@@ -41,6 +43,7 @@
)
gift_type = /datum/ego_gifts/maneater
abnormality_origin = ABNORMALITY_ORIGIN_ARTBOOK
+ var/bullet_threshold = 40
//breach related
var/teleport_cooldown
@@ -74,6 +77,29 @@
", let's work together!",
)
+//PLAYABLES ATTACKS
+ attack_action_types = list(/datum/action/cooldown/jangsan_fear)
+
+/datum/action/cooldown/jangsan_fear
+ name = "Fear"
+ icon_icon = 'icons/mob/actions/actions_abnormality.dmi'
+ button_icon_state = "jangsan"
+ check_flags = AB_CHECK_CONSCIOUS
+ transparent_when_unavailable = TRUE
+ cooldown_time = JANGSAN_FEAR_COOLDOWN
+
+/datum/action/cooldown/jangsan_fear/Trigger()
+ if(!..())
+ return FALSE
+ if(!istype(owner, /mob/living/simple_animal/hostile/abnormality/jangsan))
+ return FALSE
+ var/mob/living/simple_animal/hostile/abnormality/jangsan/jangsan = owner
+ if(jangsan.IsContained()) // No more using cooldowns while contained
+ return FALSE
+ StartCooldown()
+ jangsan.TryFearStun()
+ return TRUE
+
//Init
/mob/living/simple_animal/hostile/abnormality/jangsan/Initialize()
. = ..()
@@ -243,6 +269,20 @@
to_chat(target, span_warning("Is that what it really looks like? It's over... I can’t even move my legs..."))
return
+/mob/living/simple_animal/hostile/abnormality/jangsan/proc/TryFearStun()
+ playsound(get_turf(src), 'sound/abnormalities/scaredycat/catgrunt.ogg', 50, 1, 2)
+ for(var/mob/living/carbon/human/H in view(3, src))
+ StatCheck(H)
+ if(faction_check_mob(H, FALSE))
+ continue
+ if(H.stat == DEAD)
+ continue
+ if(weak_counter >= 4)
+ icon_state = "jangsan_bite"
+ FearStun(H)
+ chase_cooldown = world.time + chase_cooldown_time
+ break
+
//targetting
/mob/living/simple_animal/hostile/abnormality/jangsan/PickTarget(list/Targets) //Stolen from MOSB
var/list/highest_priority = list()
@@ -279,7 +319,7 @@
return ..()
/mob/living/simple_animal/hostile/abnormality/jangsan/bullet_act(obj/projectile/P)
- if(P.damage <= 40)
+ if(P.damage <= bullet_threshold)
visible_message(span_userdanger("[P] is caught in [src]'s thick fur!"))
P.Destroy()
return
@@ -301,3 +341,5 @@
. = ..()
animate(src, pixel_x = 0, pixel_z = 16, time = 3 SECONDS)
QDEL_IN(src, 30 SECONDS)
+
+#undef JANGSAN_FEAR_COOLDOWN
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/pinocchio.dm b/code/modules/mob/living/simple_animal/abnormality/he/pinocchio.dm
index 6c803ae30c76..3ba126434c61 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/pinocchio.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/pinocchio.dm
@@ -189,18 +189,21 @@
/obj/item/ego_weapon/marionette/abnormality/dropped(mob/user)
. = ..()
- delete_timer = addtimer(CALLBACK(src, PROC_REF(TryDelete), user), 3 SECONDS, TIMER_STOPPABLE)
+ if(!QDELING(src))
+ delete_timer = addtimer(CALLBACK(src, PROC_REF(TryDelete), user), 3 SECONDS, TIMER_STOPPABLE)
/obj/item/ego_weapon/marionette/abnormality/proc/TryDelete(mob/user)
if(!delete_timer)
return
deltimer(delete_timer)
+ delete_timer = null
qdel(src)
/obj/item/ego_weapon/marionette/abnormality/equipped(mob/living/carbon/human/user, slot)
. = ..()
if(delete_timer)
deltimer(delete_timer)
+ delete_timer = null
if(user.dna.species.id != "puppet")
to_chat(user, span_warning("The [src] collapses into splinters in your hands!"))
qdel(src)
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/red_queen.dm b/code/modules/mob/living/simple_animal/abnormality/he/red_queen.dm
index c4191f718337..bb0a270d13cc 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/red_queen.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/red_queen.dm
@@ -3,6 +3,7 @@
desc = "A noble red abnormality sitting in her chair."
icon = 'ModularTegustation/Teguicons/48x64.dmi'
icon_state = "redqueen"
+ portrait = "red_queen"
pixel_x = -8
base_pixel_x = -8
maxHealth = 650
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/scarecrow.dm b/code/modules/mob/living/simple_animal/abnormality/he/scarecrow.dm
index bcd7a6a610bf..351347ffa737 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/scarecrow.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/scarecrow.dm
@@ -50,6 +50,8 @@
/// Can't move/attack when it's TRUE
var/finishing = FALSE
+ var/braineating = TRUE
+ var/healthmodifier = 0.05 // Can restore 30% of HP
/mob/living/simple_animal/hostile/abnormality/scarecrow/CanAttack(atom/the_target)
if(finishing)
@@ -85,15 +87,16 @@
playsound(get_turf(src), 'sound/abnormalities/scarecrow/drink.ogg', 50, 1)
if(H.health < -120) //prevents infinite healing, corpse is too mangled
break
- adjustBruteLoss(-(maxHealth*0.05)) // Can restore 30% of HP
+ adjustBruteLoss(-(maxHealth*healthmodifier))
H.adjustBruteLoss(20)
SLEEP_CHECK_DEATH(4)
if(!targets_from.Adjacent(H) || QDELETED(H))
finishing = FALSE
return
- for(var/obj/item/organ/O in H.getorganszone(BODY_ZONE_HEAD, TRUE))
- O.Remove(H)
- QDEL_NULL(O)
+ if(braineating)
+ for(var/obj/item/organ/O in H.getorganszone(BODY_ZONE_HEAD, TRUE))
+ O.Remove(H)
+ QDEL_NULL(O)
finishing = FALSE
/mob/living/simple_animal/hostile/abnormality/scarecrow/FailureEffect(mob/living/carbon/human/user, work_type, pe)
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/watchman.dm b/code/modules/mob/living/simple_animal/abnormality/he/watchman.dm
index 4694da59a18f..870b25427504 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/watchman.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/watchman.dm
@@ -4,6 +4,7 @@
icon = 'ModularTegustation/Teguicons/32x64.dmi'
icon_state = "watchman"
icon_living = "watchman"
+ portrait = "watchman"
del_on_death = TRUE
maxHealth = 1200
health = 1200
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/wayward_passenger.dm b/code/modules/mob/living/simple_animal/abnormality/he/wayward_passenger.dm
index 7e3e3beec708..c1ee3e866bba 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/wayward_passenger.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/wayward_passenger.dm
@@ -226,14 +226,13 @@
stop_charge = TRUE
for(var/obj/structure/window/W in T.contents)
stop_charge = TRUE
- for(var/obj/machinery/door/poddoor/P in T.contents)
- stop_charge = TRUE
- continue
+ break
for(var/obj/machinery/door/D in T.contents)
- if(istype(D, /obj/machinery/door/poddoor))
- continue
+ if(!D.CanAStarPass(null))
+ stop_charge = TRUE
+ break
if(D.density)
- D.open(2)
+ INVOKE_ASYNC(D, TYPE_PROC_REF(/obj/machinery/door, open), 2)
if(stop_charge)
playsound(src, 'sound/abnormalities/thunderbird/tbird_bolt.ogg', 75, 1)
charging = FALSE
@@ -241,11 +240,10 @@
return
forceMove(T)
playsound(src,"sound/abnormalities/thunderbird/tbird_peck.ogg", rand(50, 70), 1)
- for(var/turf/TF in range(1, T))//Smash AOE visual
+ var/list/turfs_to_hit = range(1, T)
+ for(var/turf/TF in turfs_to_hit)//Smash AOE visual
new /obj/effect/temp_visual/smash_effect(TF)
- for(var/mob/living/L in range(1, T))//damage applied to targets in range
- if(L.z != z)
- continue
+ for(var/mob/living/L in turfs_to_hit)//damage applied to targets in range
if(!faction_check_mob(L))
if(L in been_hit)
continue
@@ -253,9 +251,17 @@
playsound(L, attack_sound, 75, 1)
var/turf/LT = get_turf(L)
new /obj/effect/temp_visual/kinetic_blast(LT)
- L.apply_damage(60,RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
- if(!(L in been_hit))
- been_hit += L
+ L.apply_damage(60, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ been_hit += L
+ for(var/obj/vehicle/sealed/mecha/V in turfs_to_hit)
+ if(V in been_hit)
+ continue
+ V.visible_message(span_boldwarning("[src] slices through [V]!"))
+ to_chat(V.occupants, span_userdanger("[src] rushes past you, searing your mech with its blades!"))
+ playsound(V, attack_sound, 75, 1)
+ new /obj/effect/temp_visual/kinetic_blast(get_turf(V))
+ V.take_damage(60, RED_DAMAGE, attack_dir = get_dir(V, src))
+ been_hit += V
addtimer(CALLBACK(src, PROC_REF(Do_Dash), move_dir, (times_ran + 1)), 1)
/obj/effect/portal/abno_warp
diff --git a/code/modules/mob/living/simple_animal/abnormality/he/will_you_play.dm b/code/modules/mob/living/simple_animal/abnormality/he/will_you_play.dm
index 9704f0120ab9..540f5781b8e2 100644
--- a/code/modules/mob/living/simple_animal/abnormality/he/will_you_play.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/he/will_you_play.dm
@@ -4,6 +4,7 @@
desc = "A small girl holding a teddy bear."
icon = 'ModularTegustation/Teguicons/32x32.dmi'
icon_state = "willyouplay"
+ portrait = "will_you_play"
maxHealth = 600
health = 600
threat_level = HE_LEVEL
diff --git a/code/modules/mob/living/simple_animal/abnormality/teth/falada.dm b/code/modules/mob/living/simple_animal/abnormality/teth/falada.dm
index 8c65347f34eb..c54fbdb45f85 100644
--- a/code/modules/mob/living/simple_animal/abnormality/teth/falada.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/teth/falada.dm
@@ -8,6 +8,7 @@
icon = 'ModularTegustation/Teguicons/tegumobs.dmi'
icon_state = "falada"
icon_living = "falada"
+ portrait = "falada"
faction = list("neutral")
speak_emote = list("neighs")
threat_level = TETH_LEVEL
diff --git a/code/modules/mob/living/simple_animal/abnormality/teth/redblooded.dm b/code/modules/mob/living/simple_animal/abnormality/teth/redblooded.dm
index e8f265c336d8..d0134357848f 100644
--- a/code/modules/mob/living/simple_animal/abnormality/teth/redblooded.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/teth/redblooded.dm
@@ -4,6 +4,7 @@
icon = 'ModularTegustation/Teguicons/32x48.dmi'
icon_state = "american_idle"
icon_living = "american_idle"
+ portrait = "red_blooded_american"
var/icon_furious = "american_idle_injured"
del_on_death = TRUE
maxHealth = 825
diff --git a/code/modules/mob/living/simple_animal/abnormality/teth/simple_smile.dm b/code/modules/mob/living/simple_animal/abnormality/teth/simple_smile.dm
index 8c741b4ee915..111114038f35 100644
--- a/code/modules/mob/living/simple_animal/abnormality/teth/simple_smile.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/teth/simple_smile.dm
@@ -5,6 +5,7 @@
icon = 'ModularTegustation/Teguicons/32x32.dmi'
icon_state = "smile"
icon_living = "smile"
+ portrait = "simple_smile"
del_on_death = TRUE
maxHealth = 400 //He's a little shit.
health = 400
diff --git a/code/modules/mob/living/simple_animal/abnormality/teth/void_dream.dm b/code/modules/mob/living/simple_animal/abnormality/teth/void_dream.dm
index 3cfe83b190fd..fa167b91647f 100644
--- a/code/modules/mob/living/simple_animal/abnormality/teth/void_dream.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/teth/void_dream.dm
@@ -73,6 +73,8 @@
return
if(punched)
return
+ if(IsCombatMap())
+ return
icon = 'ModularTegustation/Teguicons/32x64.dmi'
punched = TRUE
SpeedChange(-2)
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/alriune.dm b/code/modules/mob/living/simple_animal/abnormality/waw/alriune.dm
index 29ac484012e5..672884cc9790 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/alriune.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/alriune.dm
@@ -144,7 +144,5 @@
petals_next = world.time + petals_next_time + 30
TeleportAway()
icon_state = "alriune_active"
- if(IsCombatMap())
- pulse_damage = 70 //R-Corp cannot eat 180 white damage
return
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/apex_predator.dm b/code/modules/mob/living/simple_animal/abnormality/waw/apex_predator.dm
index 0439f3ad2816..f2f2c068e581 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/apex_predator.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/apex_predator.dm
@@ -4,6 +4,7 @@
icon = 'ModularTegustation/Teguicons/64x64.dmi'
icon_state = "apex"
icon_living = "apex"
+ portrait = "apex"
pixel_x = -16
base_pixel_x = -16
@@ -59,6 +60,8 @@
/mob/living/simple_animal/hostile/abnormality/apex_predator/Move()
+ if(notransform)
+ return ..()
if(busy)
return FALSE
..()
@@ -95,7 +98,6 @@
/mob/living/simple_animal/hostile/abnormality/apex_predator/AttackingTarget()
if(!can_act)
return
-
if(!revealed)
//Will want this to be crazy
say("Behind you.")
@@ -103,18 +105,31 @@
SLEEP_CHECK_DEATH(7)
Decloak()
SLEEP_CHECK_DEATH(3)
-
- var/mob/living/V = target
//Backstab
- if(target in range(1,src))
- visible_message(span_danger("\The [src] rips out [target]'s guts!"))
- new /obj/effect/gibspawner/generic(get_turf(V))
- V.apply_damage(backstab_damage, RED_DAMAGE, null, V.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ if(target in range(1, src))
+ if(isliving(target))
+ var/mob/living/V = target
+ visible_message(span_danger("The [src] rips out [target]'s guts!"))
+ new /obj/effect/gibspawner/generic(get_turf(V))
+ V.apply_damage(backstab_damage, RED_DAMAGE, null, V.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
+ //Backstab succeeds from any one of 3 tiles behind a mecha, backstab from directly behind gets boosted by mecha directional armor weakness
+ else if(ismecha(target))
+ var/relative_angle = abs(dir2angle(target.dir) - dir2angle(get_dir(target, src)))
+ relative_angle = relative_angle > 180 ? 360 - relative_angle : relative_angle
+ if(relative_angle >= 135)
+ visible_message(span_danger("The [src] shreds [target]'s armor!"))
+ var/obj/vehicle/sealed/mecha/M = target
+ M.take_damage(backstab_damage, RED_DAMAGE, attack_dir = get_dir(M, src))
+ new /obj/effect/temp_visual/kinetic_blast(get_turf(M))
+ else
+ visible_message(span_danger("The [src]'s attack misses [target]'s weakspots!"))
+ ..()
+ else
+ ..()
SLEEP_CHECK_DEATH(20)
Cloak()
//Remove target
FindTarget()
-
else
if(!jumping)
Jump()
@@ -146,7 +161,7 @@
return
//For readability
- if((jump_cooldown < world.time) && !(status_flags & GODMODE))
+ if(!jumping && (jump_cooldown < world.time) && !(status_flags & GODMODE))
Jump()
/mob/living/simple_animal/hostile/abnormality/apex_predator/proc/Jump()
@@ -157,24 +172,21 @@
/mob/living/simple_animal/hostile/abnormality/apex_predator/proc/Leap()
density = FALSE
- var/target_turf = get_turf(target)
+ var/turf/target_turf = get_turf(target)
playsound(src, 'sound/weapons/fwoosh.ogg', 300, FALSE, 9)
- throw_at(target_turf, 7, 1, src, FALSE)
+ notransform = TRUE
+ throw_at(target_turf, 7, 1, src, FALSE, callback = CALLBACK(src, PROC_REF(Slam)))
icon_state = "apex_leap"
addtimer(CALLBACK(src, PROC_REF(Slam)), 10)
/mob/living/simple_animal/hostile/abnormality/apex_predator/proc/Slam()
+ notransform = FALSE
icon_state = "apex_crouch"
playsound(src, 'sound/effects/meteorimpact.ogg', 300, FALSE, 9)
for(var/turf/T in range(1, src))
+ HurtInTurf(T, list(), jump_damage, RED_DAMAGE, null, TRUE, FALSE, TRUE)
new /obj/effect/temp_visual/kinetic_blast(T)
- for(var/mob/living/L in range(1, src))
- if(faction_check_mob(L, FALSE))
- continue
- if(L.stat == DEAD)
- continue
- L.apply_damage(jump_damage, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
addtimer(CALLBACK(src, PROC_REF(Reset)), 12)
/mob/living/simple_animal/hostile/abnormality/apex_predator/proc/Reset()
@@ -183,4 +195,3 @@
jumping = FALSE
icon_state = "apex"
jump_cooldown = world.time + jump_cooldown_time
-
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/big_wolf.dm b/code/modules/mob/living/simple_animal/abnormality/waw/big_wolf.dm
index 6760f35cdd2e..f85532289983 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/big_wolf.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/big_wolf.dm
@@ -309,7 +309,7 @@
if(do_after(src, 1 SECONDS, target = src))
var/turf/wallcheck = get_turf(src)
var/enemy_direction = get_dir(src, target_turf)
- for(var/i=0 to 7)
+ for(var/i = 0 to 7)
if(get_turf(src) != wallcheck || stat == DEAD || IsContained())
break
wallcheck = get_step(src, enemy_direction)
@@ -323,10 +323,7 @@
if(isclosedturf(T))
continue
new /obj/effect/temp_visual/slice(T)
- for(var/mob/living/L in T)
- if(!faction_check_mob(L, FALSE) || locate(L) in hit_mob)
- L.apply_damage(50, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE), spread_damage = TRUE)
- LAZYADD(hit_mob, L)
+ hit_mob = HurtInTurf(T, hit_mob, 50, RED_DAMAGE, null, TRUE, FALSE, TRUE, hurt_structure = TRUE)
can_act = TRUE
//Used in Steel noons for if they are allowed to fly through something.
@@ -357,7 +354,8 @@
can_act = FALSE
if(do_after(src, 2 SECONDS, target = src))
new /obj/effect/temp_visual/fragment_song(get_turf(src))
- for(var/mob/living/L in orange(20, src))
+ var/list/turfs_to_check = orange(20, src)
+ for(var/mob/living/L in turfs_to_check)
if(isabnormalitymob(L) && our_health < 50)
var/mob/living/simple_animal/hostile/abnormality/ABNO = L
if(ABNO.IsContained())
@@ -369,6 +367,8 @@
if(L.stat == DEAD)
continue
L.apply_damage(50, WHITE_DAMAGE, null, L.run_armor_check(null, WHITE_DAMAGE), spread_damage = TRUE)
+ for(var/obj/vehicle/V in turfs_to_check)
+ V.take_damage(50, WHITE_DAMAGE)
playsound(get_turf(src), 'sound/abnormalities/big_wolf/Wolf_Howl.ogg', 30, 0, 4)
cut_overlay(visual_overlay)
can_act = TRUE
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/black_swan.dm b/code/modules/mob/living/simple_animal/abnormality/waw/black_swan.dm
index 11fd5334ed5f..b528084db3c4 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/black_swan.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/black_swan.dm
@@ -237,7 +237,10 @@
can_act = FALSE
if(do_after(src, 2 SECONDS, target = src))
new /obj/effect/temp_visual/fragment_song(get_turf(src))
- for(var/mob/living/L in orange(9, src))
+ var/list/turfs_to_check = orange(9, src)
+ for(var/obj/vehicle/sealed/mecha/V in turfs_to_check)
+ V.take_damage(70, WHITE_DAMAGE)
+ for(var/mob/living/L in turfs_to_check)
if(isabnormalitymob(L))
var/mob/living/simple_animal/hostile/abnormality/maybe_brothers = L
if(maybe_brothers.IsContained())
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/contract.dm b/code/modules/mob/living/simple_animal/abnormality/waw/contract.dm
index 15d7ea508b1b..6aeac269bf39 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/contract.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/contract.dm
@@ -6,6 +6,7 @@
desc = "A man with a flaming head sitting behind a desk."
icon = 'ModularTegustation/Teguicons/64x48.dmi'
icon_state = "firstfold"
+ portrait = "contract"
threat_level = WAW_LEVEL
work_chances = list(
ABNORMALITY_WORK_INSTINCT = list(0, 0, 30, 40, 50),
@@ -68,7 +69,7 @@
. = FALSE
if(!(user in total_havers))
return
-
+
switch(work_type)
if(ABNORMALITY_WORK_INSTINCT)
if(user in fort_havers)
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/judgement_bird.dm b/code/modules/mob/living/simple_animal/abnormality/waw/judgement_bird.dm
index 7961caf15cb3..08572681433d 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/judgement_bird.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/judgement_bird.dm
@@ -89,8 +89,6 @@
SLEEP_CHECK_DEATH(2 SECONDS)
playsound(get_turf(src), 'sound/abnormalities/judgementbird/ability.ogg', 75, 0, 7)
for(var/mob/living/L in livinginrange(judgement_range, src))
- if(L.z != z)
- continue
if(faction_check_mob(L, FALSE))
continue
if(L.stat == DEAD)
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/naked_nest.dm b/code/modules/mob/living/simple_animal/abnormality/waw/naked_nest.dm
index 1516ebbe3208..0d1a328bcc24 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/naked_nest.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/naked_nest.dm
@@ -28,6 +28,7 @@
ego_list = list(
/datum/ego_datum/weapon/exuviae,
/datum/ego_datum/armor/exuviae,
+ /datum/ego_datum/exuviae,
)
gift_type = /datum/ego_gifts/exuviae
gift_message = "You manage to shave off a patch of scales."
@@ -45,12 +46,6 @@
var/serpentsnested = 4
var/origin_cooldown = 0
-/mob/living/simple_animal/hostile/abnormality/naked_nest/SuccessEffect(mob/living/carbon/human/user, work_type, pe)
- . = ..()
- to_chat(user, span_notice("The serpents seem to avoid areas of their nest covered in this solution."))
- new /obj/item/serpentspoison(get_turf(user))
- return
-
/mob/living/simple_animal/hostile/abnormality/naked_nest/NeutralEffect(mob/living/carbon/human/user, work_type, pe)
. = ..()
if(prob(30 + PERCENT((user.maxHealth - user.health)/ user.maxHealth)) && !user.NAKED_NESTED)
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/shrimp.dm b/code/modules/mob/living/simple_animal/abnormality/waw/shrimp.dm
index f410d4bc0eda..f5471c95ab53 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/shrimp.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/shrimp.dm
@@ -5,6 +5,7 @@
icon = 'ModularTegustation/Teguicons/32x32.dmi'
icon_state = "executive"
icon_living = "executive"
+ portrait = "shrimp_executive"
faction = list("neutral")
speak_emote = list("burbles")
threat_level = WAW_LEVEL
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/thunder_bird.dm b/code/modules/mob/living/simple_animal/abnormality/waw/thunder_bird.dm
index d4e20cae98ac..c33c27d14496 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/thunder_bird.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/thunder_bird.dm
@@ -188,11 +188,10 @@
return
forceMove(T)
playsound(src,"sound/abnormalities/thunderbird/tbird_peck.ogg", rand(50, 70), 1)
- for(var/turf/TF in range(1, T))//Smash AOE visual
+ var/list/turfs_to_hit = range(1, T)
+ for(var/turf/TF in turfs_to_hit)//Smash AOE visual
new /obj/effect/temp_visual/smash_effect(TF)
- for(var/mob/living/L in range(1, T))//damage applied to targets in range
- if(L.z != z)
- continue
+ for(var/mob/living/L in turfs_to_hit)//damage applied to targets in range
if(!faction_check_mob(L))
if(L in been_hit)
continue
@@ -201,12 +200,20 @@
playsound(L, attack_sound, 75, 1)
var/turf/LT = get_turf(L)
new /obj/effect/temp_visual/kinetic_blast(LT)
- L.apply_damage(100,BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
+ L.apply_damage(100, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.electrocute_act(1, src, flags = SHOCK_NOSTUN)
if(!(L in been_hit))
been_hit += L
+ for(var/obj/vehicle/sealed/mecha/V in turfs_to_hit)
+ if(V in been_hit)
+ continue
+ visible_message(span_boldwarning("[src] runs through [V]!"))
+ to_chat(V.occupants, span_userdanger("[src] rushes past you, arcing electricity throughout the way!"))
+ playsound(V, attack_sound, 75, 1)
+ V.take_damage(100, BLACK_DAMAGE, attack_dir = get_dir(V, src))
+ been_hit += V
addtimer(CALLBACK(src, PROC_REF(do_dash), move_dir, (times_ran + 1)), 1)
/*---Qliphoth Counter---*/
@@ -254,13 +261,11 @@
/mob/living/simple_animal/hostile/abnormality/thunder_bird/proc/fireshell()
fire_cooldown = world.time + fire_cooldown_time
for(var/mob/living/carbon/human/L in livinginrange(fireball_range, src))
- if(L.z != z)
- continue
if(faction_check_mob(L, FALSE))
continue
if (targetAmount <= 2)
++targetAmount
- var/obj/effect/thunderbolt/E = new(get_turf(L))//do this for the # of targets + 1
+ var/obj/effect/thunderbolt/E = new(get_turf(L.loc))//do this for the # of targets + 1
E.master = src
targetAmount = 0
@@ -306,11 +311,14 @@
//Smaller Scorched Girl bomb
/obj/effect/thunderbolt/proc/explode()
playsound(get_turf(src), 'sound/abnormalities/thunderbird/tbird_bolt.ogg', 50, 0, 8)
- for(var/mob/living/carbon/human/H in view(1, src))
- H.apply_damage(boom_damage*1, BLACK_DAMAGE, null, H.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
+ var/list/turfs_to_check = view(1, src)
+ for(var/mob/living/carbon/human/H in turfs_to_check)
+ H.apply_damage(boom_damage, BLACK_DAMAGE, null, H.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
H.electrocute_act(1, src, flags = SHOCK_NOSTUN)
if(H.health < 0)
Convert(H)
+ for(var/obj/vehicle/V in turfs_to_check)
+ V.take_damage(boom_damage, BLACK_DAMAGE)
new /obj/effect/temp_visual/tbirdlightning(get_turf(src))
var/datum/effect_system/smoke_spread/S = new
S.set_up(0, get_turf(src)) //Smoke shouldn't really obstruct your vision
@@ -335,7 +343,7 @@
/*Zombie Stats */
health = 250//subject to change; they all die when thunderbird is suppressed
maxHealth = 250
- obj_damage = 300
+ obj_damage = 60
damage_coeff = list(RED_DAMAGE = 1, WHITE_DAMAGE = 1.5, BLACK_DAMAGE = 0.5, PALE_DAMAGE = 0.5)
melee_damage_type = BLACK_DAMAGE
melee_damage_lower = 20
diff --git a/code/modules/mob/living/simple_animal/abnormality/waw/yin.dm b/code/modules/mob/living/simple_animal/abnormality/waw/yin.dm
index 18fa5b570cd3..0aa2c45d5c59 100644
--- a/code/modules/mob/living/simple_animal/abnormality/waw/yin.dm
+++ b/code/modules/mob/living/simple_animal/abnormality/waw/yin.dm
@@ -110,8 +110,8 @@
/mob/living/simple_animal/hostile/abnormality/yin/Moved(atom/OldLoc, Dir, override = TRUE)
if(!COOLDOWN_FINISHED(src, pulse) || SSlobotomy_events.yin_downed)
return ..()
- var/found = FALSE
- for(var/mob/living/L in view(2, src))
+ var/turfs_to_check = view(2, src)
+ for(var/mob/living/L in turfs_to_check)
if(L == src)
continue
if(L.status_flags & GODMODE)
@@ -120,11 +120,15 @@
continue
if(L.stat >= DEAD)
continue
- found = TRUE
- break
- if(found)
COOLDOWN_START(src, pulse, pulse_cooldown)
INVOKE_ASYNC(src, PROC_REF(Pulse))
+ return ..()
+ for(var/obj/vehicle/sealed/mecha/M in turfs_to_check)
+ if(!M.occupants || length(M.occupants) == 0)
+ continue
+ COOLDOWN_START(src, pulse, pulse_cooldown)
+ INVOKE_ASYNC(src, PROC_REF(Pulse))
+ return ..()
return ..()
/mob/living/simple_animal/hostile/abnormality/yin/death(gibbed)
@@ -198,10 +202,9 @@
. = BULLET_ACT_HIT
if(!P.firer)
return .
- var/mob/living/shooter = P.firer
- if(!istype(shooter))
+ if(!isliving(P.firer) && !ismecha(P.firer))
return .
- FireLaser(shooter)
+ FireLaser(P.firer)
return .
/mob/living/simple_animal/hostile/abnormality/yin/AttackingTarget(atom/attacked_target)
@@ -214,13 +217,7 @@
var/list/to_hit = range(i, src) - hit_turfs
hit_turfs |= to_hit
for(var/turf/open/OT in to_hit)
- for(var/mob/living/L in OT)
- if(faction_check(L.faction, faction_override))
- continue
- if(L in hit)
- continue
- L.apply_damage(pulse_damage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE))
- hit += L
+ hit = HurtInTurf(OT, hit, pulse_damage, BLACK_DAMAGE, null, TRUE, faction_override, TRUE)
new /obj/effect/temp_visual/small_smoke/yin_smoke/short(OT)
sleep(3)
return
@@ -380,11 +377,11 @@
/obj/effect/temp_visual/revenant/cracks/yin/Destroy()
for(var/turf/T in range(1, src))
- if(T.z != z)
- continue
for(var/mob/living/L in T)
if(faction_check(L.faction, src.faction))
continue
L.apply_damage(damage, BLACK_DAMAGE, null, L.run_armor_check(null, BLACK_DAMAGE), spread_damage = TRUE)
+ for(var/obj/vehicle/sealed/mecha/V in T)
+ V.take_damage(damage, BLACK_DAMAGE)
new /obj/effect/temp_visual/small_smoke/yin_smoke/long(T)
return ..()
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index ba5783c8a859..1da39788db90 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -129,10 +129,16 @@
var/temp_damage = damage
if(islist(damage_coeff))
- temp_damage *= damage_coeff[damagetype]
+ ChangeResistances(damage_coeff)
stack_trace("[src] has a damage_coeff list and was hurt!")
- else
- temp_damage *= damage_coeff.getCoeff(damagetype)
+ else if(!istype(damage_coeff))
+ stack_trace("[src] has an invalid damage coeff entirely!? Resetting to default.")
+ if(!istype(unmodified_damage_coeff_datum))
+ stack_trace("[src] has an invalid unmodified damage coeff!? Resetting to 1s")
+ unmodified_damage_coeff_datum = makeDamCoeff()
+ damage_coeff = unmodified_damage_coeff_datum
+ UpdateResistances()
+ temp_damage *= damage_coeff.getCoeff(damagetype)
if(temp_damage >= 0 && temp_damage <= force_threshold)
visible_message(span_warning("[src] looks unharmed!"))
diff --git a/code/modules/mob/living/simple_animal/hostile/ordeal/amber.dm b/code/modules/mob/living/simple_animal/hostile/ordeal/amber.dm
index 2b5dd83c1975..b9c1f0b54b6a 100644
--- a/code/modules/mob/living/simple_animal/hostile/ordeal/amber.dm
+++ b/code/modules/mob/living/simple_animal/hostile/ordeal/amber.dm
@@ -17,19 +17,67 @@
turns_per_move = 2
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/effects/ordeals/amber/dawn_attack.ogg'
+ attack_sound = 'sound/effects/ordeals/amber/dawn_dead.ogg'
damage_coeff = list(RED_DAMAGE = 2, WHITE_DAMAGE = 1, BLACK_DAMAGE = 1, PALE_DAMAGE = 2)
blood_volume = BLOOD_VOLUME_NORMAL
butcher_results = list(/obj/item/food/meat/slab/worm = 1)
guaranteed_butcher_results = list(/obj/item/food/meat/slab/worm = 1)
silk_results = list(/obj/item/stack/sheet/silk/amber_simple = 1)
+ /// This cooldown responds for both the burrowing and spawning in the dawns
+ var/burrow_cooldown
+ var/burrow_cooldown_time = 1 MINUTES
+
+ /// If TRUE - cannot move nor attack
+ var/burrowing = FALSE
+
+ var/can_burrow_solo = TRUE // False for amber dawns spawned by dusks that are still alive
+
/mob/living/simple_animal/hostile/ordeal/amber_bug/Initialize()
. = ..()
base_pixel_x = rand(-6,6)
pixel_x = base_pixel_x
base_pixel_y = rand(-6,6)
pixel_y = base_pixel_y
+ if(LAZYLEN(butcher_results)) //// It burrows in on spawn, spawned ones shouldn't
+ addtimer(CALLBACK(src, PROC_REF(BurrowOut), get_turf(src)))
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/death(gibbed)
+ alpha = 255
+ ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/Life()
+ . = ..()
+ if(!.) // Dead
+ return FALSE
+ if(can_burrow_solo && !burrowing && world.time > burrow_cooldown)
+ BurrowIn()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/CanAttack(atom/the_target)
+ if(burrowing)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/Move()
+ if(burrowing)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/Goto(target, delay, minimum_distance)
+ if(burrowing)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/DestroySurroundings()
+ if(burrowing)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/GiveTarget(new_target)
+ . = ..()
+ if(. && target) //reset burrow cooldown whenever in combat
+ burrow_cooldown = world.time + burrow_cooldown_time
/mob/living/simple_animal/hostile/ordeal/amber_bug/AttackingTarget()
. = ..()
@@ -53,11 +101,47 @@
animate(src, pixel_y = base_pixel_y, time = 2)
return TRUE
+/mob/living/simple_animal/hostile/ordeal/amber_bug/proc/BurrowIn(turf/T)
+ if(!T)
+ T = pick(GLOB.xeno_spawn)
+ burrowing = TRUE
+ visible_message(span_danger("[src] burrows into the ground!"))
+ playsound(get_turf(src), 'sound/effects/ordeals/amber/dawn_dig_in.ogg', 25, 1)
+ animate(src, alpha = 0, time = 5)
+ SLEEP_CHECK_DEATH(5)
+ BurrowOut(T)
+
+/mob/living/simple_animal/hostile/ordeal/amber_bug/proc/BurrowOut(turf/T)
+ burrowing = TRUE
+ alpha = 0
+ var/list/valid_turfs = list(T)
+ for(var/turf/PT in RANGE_TURFS(2, T))
+ if(!PT.is_blocked_turf_ignore_climbable())
+ valid_turfs |= PT
+ var/turf/target_turf = pick(valid_turfs)
+ forceMove(target_turf)
+ new /obj/effect/temp_visual/small_smoke/halfsecond(target_turf)
+ animate(src, alpha = 255, time = 5)
+ playsound(get_turf(src), 'sound/effects/ordeals/amber/dawn_dig_out.ogg', 25, 1)
+ visible_message(span_bolddanger("[src] burrows out from the ground!"))
+ SLEEP_CHECK_DEATH(5)
+ var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(target_turf, src)
+ animate(D, alpha = 0, transform = matrix()*1.5, time = 5)
+ for(var/mob/living/L in target_turf)
+ if(!faction_check_mob(L))
+ L.apply_damage(5, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE))
+ burrow_cooldown = world.time + burrow_cooldown_time
+ burrowing = FALSE
+
//Amber dawn spawned from dusk
/mob/living/simple_animal/hostile/ordeal/amber_bug/spawned
butcher_results = list()
guaranteed_butcher_results = list()
+/mob/living/simple_animal/hostile/ordeal/amber_bug/spawned/Initialize()
+ . = ..()
+ burrow_cooldown = world.time + burrow_cooldown_time
+
/mob/living/simple_animal/hostile/ordeal/amber_bug/spawned/death(gibbed)
density = FALSE
animate(src, alpha = 0, time = 5 SECONDS)
@@ -67,7 +151,7 @@
// Amber dusk
/mob/living/simple_animal/hostile/ordeal/amber_dusk
name = "food chain"
- desc = "A big worm-like creature with giant teeth at the front."
+ desc = "A big worm-like creature with jagged teeth at its front."
icon = 'ModularTegustation/Teguicons/64x48.dmi'
icon_state = "amber_dusk"
icon_living = "amber_dusk"
@@ -91,15 +175,12 @@
damage_coeff = list(RED_DAMAGE = 1.2, WHITE_DAMAGE = 0.8, BLACK_DAMAGE = 0.5, PALE_DAMAGE = 2)
blood_volume = BLOOD_VOLUME_NORMAL
- alpha = 0 // It burrows in on spawn
- density = FALSE
-
/// This cooldown responds for both the burrowing and spawning in the dawns
var/burrow_cooldown
- var/burrow_cooldown_time = 18 SECONDS
+ var/burrow_cooldown_time = 20 SECONDS
/// If TRUE - cannot move nor attack
- var/burrowing = TRUE
+ var/burrowing = FALSE
/// List of currently spawned dawns, so we don't create too many
var/list/spawned_mobs = list()
@@ -115,10 +196,21 @@
return FALSE
return ..()
+/mob/living/simple_animal/hostile/ordeal/amber_dusk/Goto(target, delay, minimum_distance)
+ if(burrowing)
+ return FALSE
+ return ..()
+
+/mob/living/simple_animal/hostile/ordeal/amber_dusk/DestroySurroundings()
+ if(burrowing)
+ return FALSE
+ return ..()
+
/mob/living/simple_animal/hostile/ordeal/amber_dusk/Initialize()
. = ..()
- addtimer(CALLBACK(src, PROC_REF(BurrowOut), get_turf(src)))
soundloop = new(list(src), TRUE)
+ if(LAZYLEN(butcher_results))
+ addtimer(CALLBACK(src, PROC_REF(BurrowOut), get_turf(src)))
/mob/living/simple_animal/hostile/ordeal/amber_dusk/Destroy()
QDEL_NULL(soundloop)
@@ -128,8 +220,7 @@
. = ..()
if(!.) // Dead
return FALSE
- if(world.time > burrow_cooldown)
- burrow_cooldown = world.time + burrow_cooldown_time
+ if(!burrowing && world.time > burrow_cooldown)
AttemptBirth()
BurrowIn()
@@ -137,6 +228,9 @@
if(LAZYLEN(butcher_results))
alpha = 255
soundloop.stop()
+ listclearnulls(spawned_mobs)
+ for(var/mob/living/simple_animal/hostile/ordeal/amber_bug/AB in spawned_mobs)
+ AB.can_burrow_solo = TRUE
..()
/mob/living/simple_animal/hostile/ordeal/amber_dusk/proc/AttemptBirth()
@@ -144,53 +238,71 @@
for(var/mob/living/L in spawned_mobs)
if(L.stat == DEAD)
spawned_mobs -= L
- var/max_spawn = clamp(GLOB.clients.len * 5, 5, 25)
+ var/max_spawn = clamp(GLOB.clients.len * 2, 4, 8)
if(length(spawned_mobs) >= max_spawn)
- return
- visible_message(span_danger("Five smaller bugs appear out of [src]!"))
- for(var/i = 1 to 5)
+ return FALSE
+ burrowing = TRUE
+ playsound(get_turf(src), 'sound/effects/ordeals/amber/dusk_create.ogg', 50, FALSE)
+ SLEEP_CHECK_DEATH(5)
+ visible_message(span_danger("Four smaller bugs emerge from [src]!"))
+ for(var/i = 1 to 4)
var/turf/T = get_step(get_turf(src), pick(0, NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
if(T.density) // Retry
i -= 1
continue
var/mob/living/simple_animal/hostile/ordeal/amber_bug/spawned/nb = new(T)
+ nb.can_burrow_solo = FALSE
spawned_mobs += nb
if(ordeal_reference)
nb.ordeal_reference = ordeal_reference
ordeal_reference.ordeal_mobs += nb
+ SLEEP_CHECK_DEATH(5)
+ burrowing = FALSE
+ return TRUE
/mob/living/simple_animal/hostile/ordeal/amber_dusk/proc/BurrowIn()
burrowing = TRUE
- density = FALSE
+ var/turf/T = pick(GLOB.xeno_spawn)
+ if(!T)
+ T = get_turf(src)
visible_message(span_danger("[src] burrows into the ground!"))
playsound(get_turf(src), 'sound/effects/ordeals/amber/dusk_dig_in.ogg', 50, 1)
- animate(src, alpha = 0, time = 5)
- for(var/turf/open/OT in range(1, src))
- new /obj/effect/temp_visual/small_smoke/halfsecond(OT)
+ animate(src, alpha = 0, time = 10)
SLEEP_CHECK_DEATH(5)
- var/turf/T = pick(GLOB.xeno_spawn)
+ for(var/mob/living/simple_animal/hostile/ordeal/amber_bug/AB in spawned_mobs)
+ addtimer(CALLBACK(AB, PROC_REF(BurrowIn), T))
+ SLEEP_CHECK_DEATH(5)
+ density = FALSE
+ forceMove(T)
BurrowOut(T)
/mob/living/simple_animal/hostile/ordeal/amber_dusk/proc/BurrowOut(turf/T)
- forceMove(T)
+ burrowing = TRUE
+ alpha = 0
+ density = FALSE
for(var/turf/open/OT in range(1, T))
new /obj/effect/temp_visual/small_smoke/halfsecond(OT)
animate(src, alpha = 255, time = 5)
+ playsound(get_turf(src), 'sound/effects/ordeals/amber/dusk_dig_out.ogg', 50, 1)
+ visible_message(span_bolddanger("[src] burrows out from the ground!"))
SLEEP_CHECK_DEATH(5)
density = TRUE
- visible_message(span_danger("[src] burrows out from the ground!"))
- playsound(get_turf(src), 'sound/effects/ordeals/amber/dusk_dig_out.ogg', 50, 1)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(T, src)
animate(D, alpha = 0, transform = matrix()*1.5, time = 5)
for(var/mob/living/L in view(1, src))
if(!faction_check_mob(L))
L.apply_damage(75, RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE))
+ burrow_cooldown = world.time + burrow_cooldown_time
burrowing = FALSE
/mob/living/simple_animal/hostile/ordeal/amber_dusk/spawned
butcher_results = list()
guaranteed_butcher_results = list()
+/mob/living/simple_animal/hostile/ordeal/amber_dusk/spawned/Initialize()
+ . = ..()
+ burrow_cooldown = world.time + burrow_cooldown_time
+
/mob/living/simple_animal/hostile/ordeal/amber_dusk/spawned/death(gibbed)
animate(src, alpha = 0, time = 5 SECONDS)
QDEL_IN(src, 5 SECONDS)
@@ -256,6 +368,7 @@
if(!.) // Dead
return FALSE
if(!burrowing && world.time > burrow_cooldown)
+ AttemptBirth()
BurrowIn()
return
@@ -267,27 +380,25 @@
var/max_spawn = clamp(GLOB.clients.len * 0.6, 2, 8)
if(length(spawned_mobs) >= max_spawn)
return FALSE
+ burrowing = TRUE
playsound(get_turf(src), 'sound/effects/ordeals/amber/midnight_create.ogg', 50, FALSE)
- visible_message(span_danger("Two large bugs appear out of [src]!"))
+ SLEEP_CHECK_DEATH(2 SECONDS)
+ visible_message(span_danger("Two large bugs emerge from [src]!"))
for(var/i = 1 to 2)
- var/turf/T = get_step(get_turf(src), pick(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
+ var/turf/T = get_step(get_turf(src), pick(GLOB.alldirs))
var/mob/living/simple_animal/hostile/ordeal/amber_dusk/spawned/nb = new(T)
spawned_mobs += nb
if(ordeal_reference)
nb.ordeal_reference = ordeal_reference
ordeal_reference.ordeal_mobs += nb
+ SLEEP_CHECK_DEATH(2 SECONDS)
+ burrowing = FALSE
return TRUE
/mob/living/simple_animal/hostile/ordeal/amber_midnight/proc/BurrowIn()
- if(AttemptBirth())
- SLEEP_CHECK_DEATH(2 SECONDS)
burrowing = TRUE
- density = FALSE
visible_message(span_danger("[src] burrows into the ground!"))
playsound(src, 'sound/effects/ordeals/amber/midnight_in.ogg', 50, FALSE, 7)
- animate(src, alpha = 0, time = 7)
- for(var/turf/open/OT in range(2, src))
- new /obj/effect/temp_visual/small_smoke/halfsecond(OT)
icon_state = "ambermidnight_leave"
new /obj/effect/temp_visual/ambermidnight_hole(get_turf(src))
var/list/centers = GLOB.department_centers.Copy()
@@ -295,41 +406,48 @@
for(var/turf/T in centers)
if(locate(type) in T) // Found another amber midnight there
centers -= T
- SLEEP_CHECK_DEATH(7)
+ SLEEP_CHECK_DEATH(9)
+ animate(src, alpha = 0, time = 1)
+ density = FALSE
for(var/turf/T in centers) // We do it twice in case something changed in that time
if(locate(type) in T)
centers -= T
- if(!LAZYLEN(centers))
- return
- var/turf/T = pick(centers)
+ var/turf/T = get_turf(src)
+ if(LAZYLEN(centers))
+ T = pick(centers)
+ SLEEP_CHECK_DEATH(1)
forceMove(T)
BurrowOut()
/mob/living/simple_animal/hostile/ordeal/amber_midnight/proc/BurrowOut()
+ burrowing = TRUE
alpha = 0
density = FALSE
playsound(get_turf(src), 'sound/effects/ordeals/amber/midnight_out_far.ogg', 50, TRUE, 4)
- for(var/i = 1 to 2)
- for(var/turf/open/T in view(3, get_turf(src)))
- new /obj/effect/temp_visual/small_smoke/halfsecond(T)
- SLEEP_CHECK_DEATH(2)
- animate(src, pixel_z = 0, alpha = 255, time = 6)
- icon_state = "ambermidnight_bite"
- SLEEP_CHECK_DEATH(4)
playsound(get_turf(src), 'sound/effects/ordeals/amber/midnight_out.ogg', 75, FALSE, 7)
+ new /obj/effect/temp_visual/ambersmoke(get_turf(src))
+ animate(src, pixel_z = 0, alpha = 255, time = 1)
+ icon_state = "ambermidnight_bite"
+ visible_message(span_danger("[src] burrows out from the ground!"))
+ SLEEP_CHECK_DEATH(8)
playsound(get_turf(src), 'sound/effects/ordeals/amber/midnight_out_far.ogg', 25, FALSE, 24, 2, falloff_distance = 9)
SLEEP_CHECK_DEATH(2)
density = TRUE
- visible_message(span_danger("[src] burrows out from the ground!"))
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(get_turf(src), src)
animate(D, alpha = 0, transform = matrix()*1.25, time = 3)
SLEEP_CHECK_DEATH(2)
+ var/alternate = TRUE // we dont need 50 smoke effects
for(var/turf/open/T in view(7, src))
- new /obj/effect/temp_visual/small_smoke/halfsecond(T)
+ if(alternate)
+ var/obj/effect/temp_visual/small_smoke/halfsecond/SS = new(T)
+ SS.color = LIGHT_COLOR_ORANGE
+ alternate = FALSE
+ else
+ alternate = TRUE
for(var/mob/living/L in view(7, src))
if(faction_check_mob(L))
continue
- var/distance_decrease = get_dist(src, L) * 75
+ var/distance_decrease = get_dist(src, L) * 85
L.apply_damage((1000 - distance_decrease), RED_DAMAGE, null, L.run_armor_check(null, RED_DAMAGE))
if(L.health < 0)
L.gib()
@@ -337,3 +455,27 @@
burrow_cooldown = world.time + burrow_cooldown_time
burrowing = FALSE
icon_state = icon_living
+
+/obj/effect/temp_visual/ambersmoke
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "smoke"
+ duration = 1.5 SECONDS
+ color = COLOR_ORANGE
+ pixel_x = -32
+ base_pixel_x = -32
+ pixel_y = -32
+ base_pixel_y = -32
+
+/obj/effect/temp_visual/ambermidnight_hole
+ name = "hole"
+ icon = 'ModularTegustation/Teguicons/224x128.dmi'
+ icon_state = "ambermidnight_hole"
+ duration = 10 SECONDS
+ pixel_x = -96
+ base_pixel_x = -96
+ pixel_y = -16
+ base_pixel_y = -16
+
+/obj/effect/temp_visual/ambermidnight_hole/Initialize()
+ . = ..()
+ animate(src, alpha = 0, time = duration)
diff --git a/code/modules/ordeals/_ordeal.dm b/code/modules/ordeals/_ordeal.dm
index 8d69b9eceff2..5a7f4d4995de 100644
--- a/code/modules/ordeals/_ordeal.dm
+++ b/code/modules/ordeals/_ordeal.dm
@@ -15,6 +15,8 @@
var/announce_sound = null
/// Mobs spawned by event. On their death - event ends
var/list/ordeal_mobs = list()
+ /// End announcment_text. When event ends
+ var/end_announce_text = "The ordeal has ended."
/// Sound to play on event end, if any
var/end_sound = null
/// Reward in percents to PE upon winning ordeal. From 0 to 1
@@ -50,14 +52,17 @@
// Ends the event
/datum/ordeal/proc/End()
var/total_reward = max(SSlobotomy_corp.box_goal, 3000) * reward_percent
- priority_announce("The ordeal has ended. Facility has been rewarded with [reward_percent*100]% PE.", name, sound=null)
+ priority_announce("The Ordeal has ended. Facility has been rewarded with [reward_percent*100]% PE.", name, sound=null)
SSlobotomy_corp.AdjustAvailableBoxes(total_reward)
SSlobotomy_corp.current_ordeals -= src
SSlobotomy_corp.AddLobPoints(level * 0.5, "Ordeal Reward")
if(end_sound)
for(var/mob/player in GLOB.player_list)
if(player.client)
- player.playsound_local(get_turf(player), end_sound, 35, 0)
+ var/client/watcher = player.client
+ ShowOrdealBlurb(watcher, 25, 40, color, ending = TRUE)
+ if(announce_sound)
+ player.playsound_local(get_turf(player), end_sound, 35, 0)
for(var/mob/living/carbon/human/H in GLOB.player_list)
if(H.stat == DEAD)
continue
@@ -96,7 +101,7 @@
return "Unknown"
//Global special blurb
-/datum/ordeal/proc/ShowOrdealBlurb(client/C, duration, fade_time = 5, text_color = color, text_align = "center", screen_location = "Center-6,Center+3")
+/datum/ordeal/proc/ShowOrdealBlurb(client/C, duration, fade_time = 5, text_color = color, text_align = "center", screen_location = "Center-6,Center+3", ending = FALSE)
if(!C)
return
var/style1 = "font-family: 'Baskerville'; text-align: [text_align]; color: [text_color]; font-size:12pt;"
@@ -114,8 +119,9 @@
C.screen += T
C.screen += BG
animate(T, alpha = 255, time = 10)
- T.maptext = "[name]
[flavor_name]
[announce_text]"
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fade_blurb), C, T, fade_time), duration)
+ var/display_text = ending ? end_announce_text : announce_text
+ T.maptext = "[name]
[flavor_name]
[display_text]"
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fade_blurb), C, T, fade_time), duration) //fade_blurb qdels the object
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fade_blurb), C, BG, fade_time), duration)
//Black background for blurb
diff --git a/code/modules/ordeals/amber_ordeals.dm b/code/modules/ordeals/amber_ordeals.dm
index 305fde55bf47..c2c820e9ed72 100644
--- a/code/modules/ordeals/amber_ordeals.dm
+++ b/code/modules/ordeals/amber_ordeals.dm
@@ -3,6 +3,7 @@
name = "The Dawn of Amber"
flavor_name = "The Perfect Meal"
announce_text = "A perfect meal, an excellent substitute."
+ end_announce_text = "We ate incessantly to live. The inevitable diminution, the waste..."
level = 1
reward_percent = 0.1
announce_sound = 'sound/effects/ordeals/amber_start.ogg'
@@ -12,11 +13,15 @@
spawn_amount = 3
spawn_type = /mob/living/simple_animal/hostile/ordeal/amber_bug
+/datum/ordeal/simplespawn/amber_dawn/DeploymentZone(turf/T, no_center = FALSE)
+ return T //deployment zone unnecessary since amber dawns burrow out of a 5x5 zone
+
// Dusk
/datum/ordeal/simplespawn/amber_dusk
name = "The Dusk of Amber"
flavor_name = "Food Chain"
announce_text = "To accustom oneself to the taste was an inevitable process."
+ end_announce_text = "We could live. We could continue eating."
level = 3
reward_percent = 0.2
announce_sound = 'sound/effects/ordeals/amber_start.ogg'
@@ -33,6 +38,7 @@
name = "The Midnight of Amber"
flavor_name = "Eternal Meal"
announce_text = "They fought amongst themselves to eat the others."
+ end_announce_text = "And the stronger side survived. That, simply, is the story."
level = 4
reward_percent = 0.25
announce_sound = 'sound/effects/ordeals/amber_start.ogg'
diff --git a/code/modules/ordeals/crimson_ordeals.dm b/code/modules/ordeals/crimson_ordeals.dm
index fa23739923ac..b91ed5c983ec 100644
--- a/code/modules/ordeals/crimson_ordeals.dm
+++ b/code/modules/ordeals/crimson_ordeals.dm
@@ -4,6 +4,7 @@
flavor_name = "Cheers for the Beginning"
announce_text = "Let us light a flame yet more radiant in our lives; for life is a candlelight, \
destined to snuff out one day."
+ end_announce_text = "To live is to yearn and fight for our desires."
level = 1
reward_percent = 0.1
announce_sound = 'sound/effects/ordeals/crimson_start.ogg'
@@ -27,6 +28,7 @@
name = "The Noon of Crimson"
flavor_name = "The Harmony of Skin"
announce_text = "We marched from time to time, and we would share our pleasure."
+ end_announce_text = "The collision of one life with another, skin harmonizing, painting a yet more beautiful appearance."
announce_sound = 'sound/effects/ordeals/crimson_start.ogg'
end_sound = 'sound/effects/ordeals/crimson_end.ogg'
level = 2
@@ -42,6 +44,7 @@
name = "The Dusk of Crimson"
flavor_name = "The Struggle at the Climax"
announce_text = "Throwing away our old bodies, we all become one, infinitely continuing the red march."
+ end_announce_text = "One day we will know, and tomorrow we will march hand in hand."
announce_sound = 'sound/effects/ordeals/crimson_start.ogg'
end_sound = 'sound/effects/ordeals/crimson_end.ogg'
level = 3
diff --git a/code/modules/ordeals/green_ordeals.dm b/code/modules/ordeals/green_ordeals.dm
index e02a4a0ffc67..e1d99d76b029 100644
--- a/code/modules/ordeals/green_ordeals.dm
+++ b/code/modules/ordeals/green_ordeals.dm
@@ -5,6 +5,7 @@
flavor_name = "Doubt"
announce_text = "One day, a question crossed through my mind. Where do we come from? \
We were given life and left in this world against our own volition."
+ end_announce_text = "To live was a process full of pain."
announce_sound = 'sound/effects/ordeals/green_start.ogg'
end_sound = 'sound/effects/ordeals/green_end.ogg'
spawn_places = 6
@@ -20,6 +21,7 @@
name = "The Noon of Green"
flavor_name = "Process of Understanding"
announce_text = "In the end, they were bound to life. We existed only to express despair and ire."
+ end_announce_text = "We will understand life and the soul with our own hands."
announce_sound = 'sound/effects/ordeals/green_start.ogg'
end_sound = 'sound/effects/ordeals/green_end.ogg'
level = 2
@@ -36,6 +38,7 @@
name = "The Dusk of Green"
flavor_name = "Where We Must Reach"
announce_text = "We constructed a looming tower to return whence we came."
+ end_announce_text = "There wasn't an answer. We didn't find a single thing we wanted. We only witnessed the death of life itself."
level = 3
reward_percent = 0.2
announce_sound = 'sound/effects/ordeals/green_start.ogg'
@@ -52,6 +55,7 @@
name = "The Midnight of Green"
flavor_name = "Last Helix"
announce_text = "The tower is touched by the sky, and it will leave nothing on the earth."
+ end_announce_text = "Who pays for the suffering and neglect of the lives given to us?"
level = 4
reward_percent = 0.25
announce_sound = 'sound/effects/ordeals/green_start.ogg'
diff --git a/code/modules/ordeals/indigo_ordeals.dm b/code/modules/ordeals/indigo_ordeals.dm
index 5aae38c5d5f8..7c1f7fe3575a 100644
--- a/code/modules/ordeals/indigo_ordeals.dm
+++ b/code/modules/ordeals/indigo_ordeals.dm
@@ -19,6 +19,7 @@
name = "The Noon of Indigo"
flavor_name = "The Sweepers"
announce_text = "When night falls in the Backstreets, they will come."
+ end_announce_text = "When the sun rises anew, not a scrap will remain."
announce_sound = 'sound/effects/ordeals/indigo_start.ogg'
end_sound = 'sound/effects/ordeals/indigo_end.ogg'
level = 2
@@ -34,7 +35,8 @@
/datum/ordeal/specificcommanders/indigo_dusk
name = "The Dusk of Indigo"
flavor_name = "Night in the Backstreets"
- announce_text = "They will not melt. They do not appear to be people."
+ announce_text = "We still have some more fuel. The power of family is not a bad thing."
+ end_announce_text = "Dear neighbors, we could not finish the sweeping."
announce_sound = 'sound/effects/ordeals/indigo_start.ogg'
end_sound = 'sound/effects/ordeals/indigo_end.ogg'
level = 3
diff --git a/code/modules/ordeals/violet_ordeals.dm b/code/modules/ordeals/violet_ordeals.dm
index 550f9c69fd51..a096c57aff87 100644
--- a/code/modules/ordeals/violet_ordeals.dm
+++ b/code/modules/ordeals/violet_ordeals.dm
@@ -4,6 +4,7 @@
name = "The Dawn of Violet"
flavor_name = "Fruit of Understanding"
announce_text = "To gain an understanding of what is incomprehensible, they dream, staring."
+ end_announce_text = "They complied with nothing in their bid to understand. They simply did so."
announce_sound = 'sound/effects/ordeals/violet_start.ogg'
end_sound = 'sound/effects/ordeals/violet_end.ogg'
spawn_places = 4
@@ -20,6 +21,7 @@
name = "The Noon of Violet"
flavor_name = "Grant Us Love"
announce_text = "We could only hear the weakest and faintest of their acts. We sought for love and compassion from them."
+ end_announce_text = "We cannot understand them, nor will they understand us."
announce_sound = 'sound/effects/ordeals/violet_start.ogg'
end_sound = 'sound/effects/ordeals/violet_end.ogg'
level = 2
@@ -43,6 +45,7 @@
name = "The Midnight of Violet"
flavor_name = "The God Delusion"
announce_text = "We incessantly tried to accept it. We wanted to understand them in our heads by any means, regardless of the consequences."
+ end_announce_text = "For the sake of not crumbling in on oneself. The idea that they may impossibly exist, or that they are unreachable and forever enigmatic no matter the path. Unacceptable…"
announce_sound = 'sound/effects/ordeals/violet_start.ogg'
end_sound = 'sound/effects/ordeals/violet_end.ogg'
level = 4
diff --git a/code/modules/ordeals/white_ordeals.dm b/code/modules/ordeals/white_ordeals.dm
index 159c2b681f0d..be8bb4227cbb 100644
--- a/code/modules/ordeals/white_ordeals.dm
+++ b/code/modules/ordeals/white_ordeals.dm
@@ -35,6 +35,7 @@
flavor_name = "A Request"
announce_text = "From meaningless errands, to exploration, to contract killing; they will do whatever you wish, \
so long as you pay them sufficiently."
+ end_announce_text = "They work in the Offices, Syndicates, and the Wings. Their tasks vary from banal things to something truly sublime."
can_run = TRUE
level = 6
reward_percent = 0.1
@@ -54,6 +55,7 @@
flavor_name = "Armaments"
announce_text = "They search constantly, be it for the Backers of the Wings, the Inventions of the Backstreets, \
the Reliques of the Outskirts, the Artefacts of the Ruins..."
+ end_announce_text = "As they have always done, they will overcome all that impedes them, weapons in hand."
can_run = TRUE
level = 7
reward_percent = 0.15
@@ -64,6 +66,8 @@
flavor_name = "The Fixers"
announce_text = "The colossal tower of light was titled The Library. It is only natural for the Fixers \
to be drawn to such a mystic place of life and death."
+ end_announce_text = "Bookhunters… One day they will rummage through The Library reigned over by the Pale Librarian. \
+ They are what shall become of the Fixers."
can_run = TRUE
level = 8
reward_percent = 0.2
@@ -81,6 +85,8 @@
flavor_name = "The Claw"
announce_text = "To know and manipulate all the secrets of the world; that is the \
privilege of the Head, the Eye, and the Claws. It is their honor and absolute power."
+ end_announce_text = "No one dares to stand against them. As long as they exist, \
+ the tale of the Nest will never reach its close."
level = 9
delay = 1
random_delay = FALSE
diff --git a/code/modules/paperwork/records/info/he.dm b/code/modules/paperwork/records/info/he.dm
index 0994e12c9496..76185f67a5ec 100644
--- a/code/modules/paperwork/records/info/he.dm
+++ b/code/modules/paperwork/records/info/he.dm
@@ -478,6 +478,20 @@
"When the work result was neutral, the Qliphoth counter lowered.",
"When the work result was bad, the Qliphoth counter lowered by 2.")
+//Memories of a Better Time
+/obj/item/paper/fluff/info/he/better_memories
+ abno_type = /mob/living/simple_animal/hostile/abnormality/better_memories
+ abno_code = "T-05-195"
+ abno_info = list(
+ "Employees with a Temperance level of less than 3 found a sentimental item in the pile around T-05- before suffering \
+ from the same effect that is applied by T-05-195-1.",
+ "When T-05-195 breached it released T-05-195-1 into the facility.",
+ "T-05-195-1 avoided combat but constantly tried to take a photo of employees using its head",
+ "When a employee had their photo taken they suffered a 15 second reduction to their temperance, prudance, and work success rate due to mental anguish.",
+ "When fleeing from combat T-05-195-1 sought out employees who were in the middle of work.",
+ "Clerk Gamma9 reports that they cant recall what memory T-05-195-1 effect made them remember.",
+ "T-05-195-1 is to be considered a pest and suppressed whenever possible to prevent interference.")
+
//Steam Transport Machine
/obj/item/paper/fluff/info/he/steam
abno_type = /mob/living/simple_animal/hostile/abnormality/steam
diff --git a/code/modules/projectiles/guns/ego_gun.dm b/code/modules/projectiles/guns/ego_gun.dm
index 626bf65665e7..5c6f6ccd6b72 100644
--- a/code/modules/projectiles/guns/ego_gun.dm
+++ b/code/modules/projectiles/guns/ego_gun.dm
@@ -12,7 +12,8 @@
var/obj/item/ammo_casing/ammo_type
var/list/attribute_requirements = list()
var/special
- var/autofire //In Rounds per second
+ ///In deciseconds per round
+ var/autofire
/obj/item/gun/ego_gun/Initialize()
. = ..()
@@ -42,7 +43,7 @@
else
//Give it to 'em in true rounds per minute, accurate to the 5s
- var/rpm = (1/autofire*10)*60
+ var/rpm = 600 / autofire
rpm = round(rpm,5)
. += "This weapon is automatic."
. += "This weapon fires at [rpm] rounds per minute."
diff --git a/icons/UI_Icons/abnormality_portraits/apex.png b/icons/UI_Icons/abnormality_portraits/apex.png
new file mode 100644
index 000000000000..ac53518c3de6
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/apex.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/better_memories.png b/icons/UI_Icons/abnormality_portraits/better_memories.png
new file mode 100644
index 000000000000..c8494d4cacee
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/better_memories.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/contract.png b/icons/UI_Icons/abnormality_portraits/contract.png
new file mode 100644
index 000000000000..75e40a7a0a15
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/contract.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/eris.png b/icons/UI_Icons/abnormality_portraits/eris.png
new file mode 100644
index 000000000000..9a1675e75dda
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/eris.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/falada.png b/icons/UI_Icons/abnormality_portraits/falada.png
new file mode 100644
index 000000000000..11a21fac82fd
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/falada.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/khz.png b/icons/UI_Icons/abnormality_portraits/khz.png
new file mode 100644
index 000000000000..a73873d78231
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/khz.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/last_shot.png b/icons/UI_Icons/abnormality_portraits/last_shot.png
new file mode 100644
index 000000000000..eb5bc997e624
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/last_shot.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/red_blooded_american.png b/icons/UI_Icons/abnormality_portraits/red_blooded_american.png
new file mode 100644
index 000000000000..83e5aaac20ec
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/red_blooded_american.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/red_queen.png b/icons/UI_Icons/abnormality_portraits/red_queen.png
new file mode 100644
index 000000000000..7487a5fb759c
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/red_queen.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/shrimp_executive.png b/icons/UI_Icons/abnormality_portraits/shrimp_executive.png
new file mode 100644
index 000000000000..98a98fbe9399
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/shrimp_executive.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/simple_smile.png b/icons/UI_Icons/abnormality_portraits/simple_smile.png
new file mode 100644
index 000000000000..12c74d209e9f
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/simple_smile.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/space.png b/icons/UI_Icons/abnormality_portraits/space.png
new file mode 100644
index 000000000000..dda06302bdda
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/space.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/watchman.png b/icons/UI_Icons/abnormality_portraits/watchman.png
new file mode 100644
index 000000000000..0e35471f583c
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/watchman.png differ
diff --git a/icons/UI_Icons/abnormality_portraits/will_you_play.png b/icons/UI_Icons/abnormality_portraits/will_you_play.png
new file mode 100644
index 000000000000..198a4272e305
Binary files /dev/null and b/icons/UI_Icons/abnormality_portraits/will_you_play.png differ
diff --git a/icons/mob/actions/actions_abnormality.dmi b/icons/mob/actions/actions_abnormality.dmi
index 11e01722e0fd..093ae47302d6 100644
Binary files a/icons/mob/actions/actions_abnormality.dmi and b/icons/mob/actions/actions_abnormality.dmi differ
diff --git a/icons/mob/clothing/ego_gear/abnormality/he.dmi b/icons/mob/clothing/ego_gear/abnormality/he.dmi
index 9f778ef68f5c..7b277ea3d9ef 100644
Binary files a/icons/mob/clothing/ego_gear/abnormality/he.dmi and b/icons/mob/clothing/ego_gear/abnormality/he.dmi differ
diff --git a/icons/mob/clothing/ego_gear/ego_gifts.dmi b/icons/mob/clothing/ego_gear/ego_gifts.dmi
index 92c6a9b27d50..7a953c15654b 100644
Binary files a/icons/mob/clothing/ego_gear/ego_gifts.dmi and b/icons/mob/clothing/ego_gear/ego_gifts.dmi differ
diff --git a/icons/mob/hivebot.dmi b/icons/mob/hivebot.dmi
index a23bff51243c..86a2b1c51bf9 100644
Binary files a/icons/mob/hivebot.dmi and b/icons/mob/hivebot.dmi differ
diff --git a/icons/mob/inhands/weapons/ego_lefthand.dmi b/icons/mob/inhands/weapons/ego_lefthand.dmi
index d3badf887a45..cfcc6e243b9b 100644
Binary files a/icons/mob/inhands/weapons/ego_lefthand.dmi and b/icons/mob/inhands/weapons/ego_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/ego_righthand.dmi b/icons/mob/inhands/weapons/ego_righthand.dmi
index f01d7d04f83f..86f6914414f8 100644
Binary files a/icons/mob/inhands/weapons/ego_righthand.dmi and b/icons/mob/inhands/weapons/ego_righthand.dmi differ
diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi
index bfa75d1f2a90..908a1d7d3295 100644
Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ
diff --git a/icons/obj/clothing/ego_gear/abnormality/he.dmi b/icons/obj/clothing/ego_gear/abnormality/he.dmi
index 780827618eec..b9402166d25b 100644
Binary files a/icons/obj/clothing/ego_gear/abnormality/he.dmi and b/icons/obj/clothing/ego_gear/abnormality/he.dmi differ
diff --git a/icons/obj/ego_weapons.dmi b/icons/obj/ego_weapons.dmi
index 5b6c81dea56e..216a7eb5c9a6 100644
Binary files a/icons/obj/ego_weapons.dmi and b/icons/obj/ego_weapons.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index 9cd1d05a3f6f..de424aa9998c 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/lobotomy-corp13.dme b/lobotomy-corp13.dme
index 84452bd2e758..c16c97ce2c38 100644
--- a/lobotomy-corp13.dme
+++ b/lobotomy-corp13.dme
@@ -153,6 +153,7 @@
#include "code\__DEFINES\dcs\signals_fish.dm"
#include "code\__DEFINES\research\anomalies.dm"
#include "code\__DEFINES\~lobotomy_defines\adventure.dm"
+#include "code\__DEFINES\~lobotomy_defines\weapon.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_logging.dm"
#include "code\__HELPERS\_string_lists.dm"
@@ -2774,6 +2775,7 @@
#include "code\modules\mob\living\simple_animal\abnormality\aleph\staining_rose.dm"
#include "code\modules\mob\living\simple_animal\abnormality\aleph\titania.dm"
#include "code\modules\mob\living\simple_animal\abnormality\aleph\white_night.dm"
+#include "code\modules\mob\living\simple_animal\abnormality\he\better_memories.dm"
#include "code\modules\mob\living\simple_animal\abnormality\he\blue_shepherd.dm"
#include "code\modules\mob\living\simple_animal\abnormality\he\der_freischutz.dm"
#include "code\modules\mob\living\simple_animal\abnormality\he\doomsday_calendar.dm"
@@ -3970,6 +3972,7 @@
#include "ModularTegustation\tegu_drinks\twistedtea.dm"
#include "ModularTegustation\tegu_items\debug_items.dm"
#include "ModularTegustation\tegu_items\injectors.dm"
+#include "ModularTegustation\tegu_items\lc13boss_summon.dm"
#include "ModularTegustation\tegu_items\lc13unique_items.dm"
#include "ModularTegustation\tegu_items\associations\association_beacon.dm"
#include "ModularTegustation\tegu_items\associations\cityspawners.dm"
@@ -3997,6 +4000,7 @@
#include "ModularTegustation\tegu_items\gadgets\manager_bullets.dm"
#include "ModularTegustation\tegu_items\gadgets\powered.dm"
#include "ModularTegustation\tegu_items\gadgets\unpowered.dm"
+#include "ModularTegustation\tegu_items\rcorp\!abno_overwrites.dm"
#include "ModularTegustation\tegu_items\rcorp\basecode.dm"
#include "ModularTegustation\tegu_items\rcorp\gadgets.dm"
#include "ModularTegustation\tegu_items\rcorp\itemspawner.dm"
diff --git a/sound/effects/ordeals/amber/dawn_attack.ogg b/sound/effects/ordeals/amber/dawn_attack.ogg
new file mode 100644
index 000000000000..93364d088292
Binary files /dev/null and b/sound/effects/ordeals/amber/dawn_attack.ogg differ
diff --git a/sound/effects/ordeals/amber/dawn_dead.ogg b/sound/effects/ordeals/amber/dawn_dead.ogg
new file mode 100644
index 000000000000..ab448708e8d6
Binary files /dev/null and b/sound/effects/ordeals/amber/dawn_dead.ogg differ
diff --git a/sound/effects/ordeals/amber/dawn_dig_in.ogg b/sound/effects/ordeals/amber/dawn_dig_in.ogg
new file mode 100644
index 000000000000..d085e784b64a
Binary files /dev/null and b/sound/effects/ordeals/amber/dawn_dig_in.ogg differ
diff --git a/sound/effects/ordeals/amber/dawn_dig_out.ogg b/sound/effects/ordeals/amber/dawn_dig_out.ogg
new file mode 100644
index 000000000000..7ed400bdc2cd
Binary files /dev/null and b/sound/effects/ordeals/amber/dawn_dig_out.ogg differ
diff --git a/sound/effects/ordeals/amber/dusk_create.ogg b/sound/effects/ordeals/amber/dusk_create.ogg
new file mode 100644
index 000000000000..6b879aeb38d9
Binary files /dev/null and b/sound/effects/ordeals/amber/dusk_create.ogg differ