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