From d2778bb97f690fb1dccb123abc68b828b6909456 Mon Sep 17 00:00:00 2001 From: "C.L" Date: Sun, 24 Jul 2022 21:06:42 -0400 Subject: [PATCH 01/29] Places advancedtoolchecks and fixes two-handed weapons for simple_mobs Modernizes code to today's standards. Also makes it so that simple_mobs that have hands can use two handed weapons and guns correctly. Bonus of making it so scugs can now mine and put ointment and bruise packs on. --- code/game/objects/items.dm | 4 ++++ code/game/objects/items/devices/scanners/gas.dm | 2 +- code/game/objects/items/devices/scanners/health.dm | 2 +- .../game/objects/items/devices/scanners/mass_spectrometer.dm | 2 +- code/game/objects/items/stacks/medical.dm | 5 ++--- code/game/objects/items/weapons/weaponry.dm | 2 +- code/modules/mining/mine_turfs.dm | 2 +- code/modules/projectiles/gun.dm | 2 ++ 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8b45f1d001f..1dd1e54ddbe 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -159,6 +159,10 @@ var/obj/item/organ/external/hand = H.organs_by_name[check_hand] if(istype(hand) && hand.is_usable()) return TRUE + var/mob/living/simple_mob/S = M + if(istype(S) && S.has_hands) //Are they a mob? And do they have hands? + return TRUE + return FALSE diff --git a/code/game/objects/items/devices/scanners/gas.dm b/code/game/objects/items/devices/scanners/gas.dm index ca35911448b..9fbc319d6db 100644 --- a/code/game/objects/items/devices/scanners/gas.dm +++ b/code/game/objects/items/devices/scanners/gas.dm @@ -23,7 +23,7 @@ /obj/item/device/analyzer/attack_self(mob/user as mob) if (user.stat) return - if (!(ishuman(user) || ticker) && ticker.mode.name != "monkey") + if (!user.IsAdvancedToolUser()) to_chat(usr, "You don't have the dexterity to do this!") return diff --git a/code/game/objects/items/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm index 98aa6ec2db4..02b33bdec56 100644 --- a/code/game/objects/items/devices/scanners/health.dm +++ b/code/game/objects/items/devices/scanners/health.dm @@ -41,7 +41,7 @@ dat += "Body Temperature: ???" user.show_message("[dat]", 1) return - if (!(ishuman(user) || ticker) && ticker.mode.name != "monkey") + if (!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return diff --git a/code/game/objects/items/devices/scanners/mass_spectrometer.dm b/code/game/objects/items/devices/scanners/mass_spectrometer.dm index fee41eb8804..5c044fc535b 100644 --- a/code/game/objects/items/devices/scanners/mass_spectrometer.dm +++ b/code/game/objects/items/devices/scanners/mass_spectrometer.dm @@ -30,7 +30,7 @@ /obj/item/device/mass_spectrometer/attack_self(mob/user as mob) if (user.stat) return - if (!(ishuman(user) || ticker) && ticker.mode.name != "monkey") + if (!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return if(reagents.total_volume) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index f065ec3ea17..9676919792d 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -20,8 +20,7 @@ to_chat(user, "\The [src] cannot be applied to [M]!") return 1 - if ( ! (istype(user, /mob/living/carbon/human) || \ - istype(user, /mob/living/silicon)) ) + if (!M.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return 1 @@ -129,7 +128,7 @@ if(used >= available) to_chat(user, "You run out of [src]!") break - + if (W.current_stage <= W.max_bleeding_stage) user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ "You bandage \a [W.desc] on [M]'s [affecting.name]." ) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 02c25c736d0..26f32002420 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -19,7 +19,7 @@ user.setClickCooldown(user.get_attack_speed(src)) user.do_attack_animation(M) - if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") + if (!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index f0eb2eca185..e36d5ba8c06 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -352,7 +352,7 @@ var/list/mining_overlay_cache = list() //Not even going to touch this pile of spaghetti /turf/simulated/mineral/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (!(istype(usr, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") + if (!user.IsAdvancedToolUser()) to_chat(user, "You don't have the dexterity to do this!") return diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 8d0002f0daf..0252396b2c0 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -570,6 +570,7 @@ if(one_handed_penalty) if(!src.is_held_twohanded(user)) + to_chat(user, "IS HELD TWOHANDED PENALTY.") switch(one_handed_penalty) if(1 to 15) if(prob(50)) //don't need to tell them every single time @@ -581,6 +582,7 @@ if(46 to INFINITY) to_chat(user, "You struggle to keep \the [src] on target with just one hand!") else if(!user.can_wield_item(src)) + to_chat(user, "CAN WIELD ITEM PENALTY.") switch(one_handed_penalty) if(1 to 15) if(prob(50)) //don't need to tell them every single time From 9d2d51c9dccadd591d3230ec43b943da6f5d4328 Mon Sep 17 00:00:00 2001 From: "C.L" Date: Sun, 24 Jul 2022 21:08:26 -0400 Subject: [PATCH 02/29] Removes debug text. --- code/modules/projectiles/gun.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 0252396b2c0..8d0002f0daf 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -570,7 +570,6 @@ if(one_handed_penalty) if(!src.is_held_twohanded(user)) - to_chat(user, "IS HELD TWOHANDED PENALTY.") switch(one_handed_penalty) if(1 to 15) if(prob(50)) //don't need to tell them every single time @@ -582,7 +581,6 @@ if(46 to INFINITY) to_chat(user, "You struggle to keep \the [src] on target with just one hand!") else if(!user.can_wield_item(src)) - to_chat(user, "CAN WIELD ITEM PENALTY.") switch(one_handed_penalty) if(1 to 15) if(prob(50)) //don't need to tell them every single time From a08a3520d293b53c93dc0362099a3e9cb96a8359 Mon Sep 17 00:00:00 2001 From: Heroman Date: Tue, 26 Jul 2022 05:36:50 +1000 Subject: [PATCH 03/29] Fixes practice shells not loading on poi --- code/modules/projectiles/ammunition/ammo_boxes.dm | 2 +- maps/submaps/surface_submaps/plains/Thiefc.dmm | 2 +- maps/submaps/surface_submaps/plains/Thiefc_vr.dmm | 2 +- maps/tether/submaps/tether_centcom.dmm | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/projectiles/ammunition/ammo_boxes.dm b/code/modules/projectiles/ammunition/ammo_boxes.dm index 566b878c44f..533a27bc4c9 100644 --- a/code/modules/projectiles/ammunition/ammo_boxes.dm +++ b/code/modules/projectiles/ammunition/ammo_boxes.dm @@ -10,7 +10,7 @@ # # Use this format, followed by the caliber. For example, a shotgun's caliber # variable is "12g" as a result. Ergo, a shotgun round's path would have "a12g", -# or a magazine with shotgun shells would be "m12g" instead. To avoid confusion +# or a magazine with shotgun shells would be "m12g" instead. To avoid confusion // Avoid confusion my ass, when ammo is called only shells and suddenly switches to being called some obscure-ass military number # for developers and in-game admins spawning these items, stick to this format. # Likewise, when creating new rounds, the caliber variable should match whatever # the name says. diff --git a/maps/submaps/surface_submaps/plains/Thiefc.dmm b/maps/submaps/surface_submaps/plains/Thiefc.dmm index d83aa9631fc..a19b8e603e7 100644 --- a/maps/submaps/surface_submaps/plains/Thiefc.dmm +++ b/maps/submaps/surface_submaps/plains/Thiefc.dmm @@ -13,7 +13,7 @@ "m" = (/obj/structure/table/steel,/obj/item/weapon/paper{desc = "This is some bullshit. First chance we got to knick something that looks half decent turns out to be mostly junk. And now we're getting calls that the other stash we've got has got a spider problem. Fuck this, Between the shitty weather, The pissed of bugs, And now apparently some NT goon running around in the area there's hardly enough reason to stick around. I'm telling Carl I'm out tommorow."; name = "Note"},/turf/simulated/floor/outdoors/dirt{outdoors = 0},/area/submap/Thiefc) "n" = (/obj/vehicle/train/trolley,/turf/simulated/floor/outdoors/dirt{outdoors = 0},/area/submap/Thiefc) "o" = (/obj/structure/closet/crate,/obj/item/weapon/cell/device/weapon,/obj/item/weapon/cell/device/weapon,/turf/simulated/floor/outdoors/dirt{outdoors = 0},/area/submap/Thiefc) -"p" = (/obj/structure/closet/crate,/obj/item/ammo_magazine/ammo_box/b12g,/obj/item/weapon/storage/box/practiceshells,/obj/random/projectile/scrapped_gun,/turf/simulated/floor/outdoors/dirt{outdoors = 0},/area/submap/Thiefc) +"p" = (/obj/structure/closet/crate,/obj/item/ammo_magazine/ammo_box/b12g,/obj/item/ammo_magazine/ammo_box/b12g/practice,/obj/random/projectile/scrapped_gun,/turf/simulated/floor/outdoors/dirt{outdoors = 0},/area/submap/Thiefc) (1,1,1) = {" aaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/maps/submaps/surface_submaps/plains/Thiefc_vr.dmm b/maps/submaps/surface_submaps/plains/Thiefc_vr.dmm index 599d3521551..5528c6029ca 100644 --- a/maps/submaps/surface_submaps/plains/Thiefc_vr.dmm +++ b/maps/submaps/surface_submaps/plains/Thiefc_vr.dmm @@ -119,7 +119,7 @@ /area/submap/Thiefc) "p" = ( /obj/structure/closet/crate, -/obj/item/weapon/storage/box/practiceshells, +/obj/item/ammo_magazine/ammo_box/b12g/practice, /turf/simulated/floor/outdoors/dirt/virgo3b{ outdoors = 0 }, diff --git a/maps/tether/submaps/tether_centcom.dmm b/maps/tether/submaps/tether_centcom.dmm index aec901cfe70..83c57847b06 100644 --- a/maps/tether/submaps/tether_centcom.dmm +++ b/maps/tether/submaps/tether_centcom.dmm @@ -1895,7 +1895,7 @@ /obj/machinery/vending/cigarette{ name = "hacked cigarette machine"; prices = list(); - products = list(/obj/item/weapon/storage/fancy/cigarettes=10,/obj/item/weapon/storage/box/matches=10,/obj/item/weapon/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) + products = list(/obj/item/weapon/storage/fancy/cigarettes = 10, /obj/item/weapon/storage/box/matches = 10, /obj/item/weapon/flame/lighter/zippo = 4, /obj/item/clothing/mask/smokable/cigarette/cigar/havana = 2) }, /turf/simulated/floor/wood, /area/centcom/specops) From e8e05d96ef3fc62887bf6ae0e062f3d6475155b7 Mon Sep 17 00:00:00 2001 From: Eli Atonis Date: Tue, 26 Jul 2022 07:13:07 +1000 Subject: [PATCH 04/29] Ghost spawning as mice from trash piles Added the ability for observers to click on trash piles to trigger the mouse spawn prompt. Doing so spawns them on the pile with the same "crawls out of the trash" message a normal mouse would trigger. --- code/game/objects/structures/trash_pile_vr.dm | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/code/game/objects/structures/trash_pile_vr.dm b/code/game/objects/structures/trash_pile_vr.dm index 037facb0eca..d1e9a0593e2 100644 --- a/code/game/objects/structures/trash_pile_vr.dm +++ b/code/game/objects/structures/trash_pile_vr.dm @@ -84,6 +84,49 @@ else return ..() +/obj/structure/trash_pile/attack_ghost(mob/observer/user as mob) + if(config.disable_player_mice) + to_chat(user, "Spawning as a mouse is currently disabled.") + return + + //VOREStation Add Start + if(jobban_isbanned(user, "GhostRoles")) + to_chat(user, "You cannot become a mouse because you are banned from playing ghost roles.") + return + //VOREStation Add End + + if(!user.MayRespawn(1)) + return + + var/turf/T = get_turf(src) + if(!T || (T.z in using_map.admin_levels)) + to_chat(user, "You may not spawn as a mouse on this Z-level.") + return + + var/timedifference = world.time - user.client.time_died_as_mouse + if(user.client.time_died_as_mouse && timedifference <= mouse_respawn_time * 600) + var/timedifference_text + timedifference_text = time2text(mouse_respawn_time * 600 - timedifference,"mm:ss") + to_chat(user, "You may only spawn again as a mouse more than [mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") + return + + var/response = tgui_alert(user, "Are you -sure- you want to become a mouse?","Are you sure you want to squeek?",list("Squeek!","Nope!")) + if(response != "Squeek!") return //Hit the wrong key...again. + + var/mob/living/simple_mob/animal/passive/mouse/host + host = new /mob/living/simple_mob/animal/passive/mouse(get_turf(src)) + + if(host) + if(config.uneducated_mice) + host.universal_understand = 0 + announce_ghost_joinleave(src, 0, "They are now a mouse.") + host.ckey = user.ckey + to_chat(host, "You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent.") + + var/atom/A = get_holder_at_turf_level(src) + A.visible_message("[host] crawls out of \the [src].") + return + /obj/structure/trash_pile/attack_hand(mob/user) //Human mob if(ishuman(user)) From 8bb4dbb66a9b03ffc97a535287ab9cc6efcc57d7 Mon Sep 17 00:00:00 2001 From: Rakkun Date: Tue, 26 Jul 2022 21:50:50 -0300 Subject: [PATCH 05/29] Ratchet sprite Creates the sprite of a simple ratchet to maybe be coded and used in the future as a reskinned wrench. --- icons/obj/tools_vr.dmi | Bin 15879 -> 16071 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/tools_vr.dmi b/icons/obj/tools_vr.dmi index 05de9a423a4eb858618b5d54eeedb26e9715448c..576d69cfabaa808ad9e66221937de670ca605d16 100644 GIT binary patch literal 16071 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 Mdh)PD-u%`70#+|c8~^|S From 7258e452fbbae31d16fe00669efdd60d1df0e904 Mon Sep 17 00:00:00 2001 From: "DESKTOP-4BUBN9N\\Mike" Date: Wed, 27 Jul 2022 08:05:44 +0100 Subject: [PATCH 06/29] xenochimera unarmed attack and regeneration tweaks --- .../carbon/human/species/species_attack_vr.dm | 135 +++++++------- .../station/station_special_abilities_vr.dm | 168 +++++++++--------- .../species/station/station_special_vr.dm | 5 +- 3 files changed, 164 insertions(+), 144 deletions(-) diff --git a/code/modules/mob/living/carbon/human/species/species_attack_vr.dm b/code/modules/mob/living/carbon/human/species/species_attack_vr.dm index 9e2c720efa3..b1d9ec8425b 100644 --- a/code/modules/mob/living/carbon/human/species/species_attack_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_attack_vr.dm @@ -1,60 +1,75 @@ -/datum/unarmed_attack/bite/sharp/numbing //Is using this against someone you are truly trying to fight a bad idea? Yes. Yes it is. - attack_verb = list("bit") - attack_noun = list("fangs") - attack_sound = 'sound/weapons/bite.ogg' - shredding = 0 - sharp = TRUE - edge = TRUE - -/datum/unarmed_attack/bite/sharp/numbing/show_attack(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - var/obj/item/organ/external/affecting = target.get_organ(zone) - - attack_damage = CLAMP(attack_damage, 1, 5) - if(target == user) - user.visible_message("[user] [pick(attack_verb)] \himself in the [affecting.name]!") - return 0 //No venom for you. - switch(zone) - if(BP_HEAD, O_MOUTH, O_EYES) - // ----- HEAD ----- // - switch(attack_damage) - if(1 to 2) - user.visible_message("[user]'s fangs scrape across [target]'s cheek!") - to_chat(target, "Your face feels tingly!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) //Have to add this here, otherwise the swtich fails. - if(3 to 4) - user.visible_message("[user]'s fangs pierce into [target]'s neck at an odd, awkward angle!") - to_chat(target, "Your neck feels like it's on fire before going numb!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(5) - user.visible_message("[user] sinks \his [pick(attack_noun)] deep into [target]'s neck, causing the vein to bulge outwards at some type of chemical is pumped into it!") - to_chat(target, "Your neck feels like it's going to burst! Moments later, you simply can't feel your neck any longer, the numbness beginning to spread throughout your body!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - else - // ----- BODY ----- // - switch(attack_damage) - if(1 to 2) - user.visible_message("[user]'s fangs scrape across [target]'s [affecting.name]!") - to_chat(target, "Your [affecting.name] feels tingly!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(3 to 4) - user.visible_message("[user]'s fangs pierce [pick("", "", "the side of")] [target]'s [affecting.name]!") - to_chat(target, "Your [affecting.name] feels like it's on fire before going numb!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(5) - user.visible_message("[user]'s fangs sink deep into [target]'s [affecting.name], one of their veins bulging outwards from the sudden fluid pumped into it!") - to_chat(target, "Your [affecting.name] feels like it's going to burst! Moments later, you simply can't feel your [affecting.name] any longer, the numbness slowly spreading throughout your body!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - -/datum/unarmed_attack/claws/shadekin - -/datum/unarmed_attack/claws/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - ..() - if(!(target == user)) - user.shadekin_adjust_energy(attack_damage) - -/datum/unarmed_attack/bite/sharp/shadekin - -/datum/unarmed_attack/bite/sharp/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - ..() - if(!(target == user)) - user.shadekin_adjust_energy(attack_damage) \ No newline at end of file +/datum/unarmed_attack/bite/sharp/numbing //Is using this against someone you are truly trying to fight a bad idea? Yes. Yes it is. + attack_verb = list("bit") + attack_noun = list("fangs") + attack_sound = 'sound/weapons/bite.ogg' + shredding = 0 + sharp = TRUE + edge = TRUE + +/datum/unarmed_attack/bite/sharp/numbing/show_attack(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + var/obj/item/organ/external/affecting = target.get_organ(zone) + + attack_damage = CLAMP(attack_damage, 1, 5) + if(target == user) + user.visible_message("[user] [pick(attack_verb)] \himself in the [affecting.name]!") + return 0 //No venom for you. + switch(zone) + if(BP_HEAD, O_MOUTH, O_EYES) + // ----- HEAD ----- // + switch(attack_damage) + if(1 to 2) + user.visible_message("[user]'s fangs scrape across [target]'s cheek!") + to_chat(target, "Your face feels tingly!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) //Have to add this here, otherwise the swtich fails. + if(3 to 4) + user.visible_message("[user]'s fangs pierce into [target]'s neck at an odd, awkward angle!") + to_chat(target, "Your neck feels like it's on fire before going numb!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(5) + user.visible_message("[user] sinks \his [pick(attack_noun)] deep into [target]'s neck, causing the vein to bulge outwards at some type of chemical is pumped into it!") + to_chat(target, "Your neck feels like it's going to burst! Moments later, you simply can't feel your neck any longer, the numbness beginning to spread throughout your body!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + else + // ----- BODY ----- // + switch(attack_damage) + if(1 to 2) + user.visible_message("[user]'s fangs scrape across [target]'s [affecting.name]!") + to_chat(target, "Your [affecting.name] feels tingly!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(3 to 4) + user.visible_message("[user]'s fangs pierce [pick("", "", "the side of")] [target]'s [affecting.name]!") + to_chat(target, "Your [affecting.name] feels like it's on fire before going numb!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(5) + user.visible_message("[user]'s fangs sink deep into [target]'s [affecting.name], one of their veins bulging outwards from the sudden fluid pumped into it!") + to_chat(target, "Your [affecting.name] feels like it's going to burst! Moments later, you simply can't feel your [affecting.name] any longer, the numbness slowly spreading throughout your body!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + +/datum/unarmed_attack/claws/shadekin + +/datum/unarmed_attack/claws/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + ..() + if(!(target == user)) + user.shadekin_adjust_energy(attack_damage) + +/datum/unarmed_attack/bite/sharp/shadekin + +/datum/unarmed_attack/bite/sharp/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + ..() + if(!(target == user)) + user.shadekin_adjust_energy(attack_damage) + +/datum/unarmed_attack/claws/chimera //special feral attack that gets stronger as they get angrier + +/datum/unarmed_attack/claws/chimera/apply_effects(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/armour,var/attack_damage,var/zone) + ..() + if(user.feral && !(target == user)) + var/bonusdamage = user.feral/5 + target.apply_damage(bonusdamage, damage_type, zone, armour, 0, sharp=src.sharp, edge=src.edge) //they get their armour soak already against the regular attack + if (bonusdamage > 15) + var/selfdamagezone = null + if (user.hand) + selfdamagezone=pick(BP_L_ARM, BP_L_HAND) + else + selfdamagezone=pick(BP_R_ARM, BP_R_HAND) + user.apply_damage(max(0, (bonusdamage-15)/2), BRUTE, selfdamagezone, 0, 0, sharp=FALSE, edge=FALSE) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index 34a70546435..a4333760a8d 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -3,10 +3,18 @@ set category = "Abilities" // Sanity is mostly handled in chimera_regenerate() - - var/confirm = tgui_alert(usr, "Are you sure you want to completely reconstruct your form? This process can take up to twenty minutes, depending on how hungry you are, and you will be unable to move.", "Confirm Regeneration", list("Yes", "No")) - if(confirm == "Yes") - chimera_regenerate() + if(stat == DEAD) + var/confirm = tgui_alert(usr, "Are you sure you want to regenerate your corpse? This process can take up to thirty minutes.", "Confirm Regeneration", list("Yes", "No")) + if(confirm == "Yes") + chimera_regenerate() + else if (quickcheckuninjured()) + var/confirm = tgui_alert(usr, "Are you sure you want to regenerate? As you are uninjured this will only take 30 seconds and match your appearance to your character slot.", "Confirm Regeneration", list("Yes", "No")) + if(confirm == "Yes") + chimera_regenerate() + else + var/confirm = tgui_alert(usr, "Are you sure you want to completely reconstruct your form? This process can take up to fifteen minutes, depending on how hungry you are, and you will be unable to move.", "Confirm Regeneration", list("Yes", "No")) + if(confirm == "Yes") + chimera_regenerate() /mob/living/carbon/human/proc/chimera_regenerate() //If they're already regenerating @@ -21,40 +29,40 @@ to_chat(src, "You can't use that ability again so soon!") return - var/nutrition_used = nutrition * 0.5 - var/time = (240+960/(1 + nutrition_used/75)) + var/time = min(900, (120+780/(1 + nutrition/100))) //capped at 15 mins, roughly 6 minutes at 250 (yellow) nutrition, 4.1 minutes at 500 (grey), cannot be below 2 mins + if (quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness + time = 30 //Clicked regen while dead. if(stat == DEAD) - //Has nutrition and dead, allow regen. - if(hasnutriment()) - to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.") + //reviving from dead takes extra nutriment to be provided from outside OR takes twice as long and consumes extra at the end + if(!hasnutriment()) + time = time*2 - //Scary spawnerization. - revive_ready = REVIVING_NOW - revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently. - throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution) - spawn(time SECONDS) - // Was dead, now not dead. - if(stat != DEAD) - to_chat(src, "Your body has recovered from its ordeal, ready to regenerate itself again.") - revive_ready = REVIVING_READY //reset their cooldown - clear_alert("regen") - throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) + to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.") - // Was dead, still dead. - else - to_chat(src, "Consciousness begins to stir as your new body awakens, ready to hatch.") - verbs |= /mob/living/carbon/human/proc/hatch - revive_ready = REVIVING_DONE - src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) - clear_alert("regen") - throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) + //Scary spawnerization. + revive_ready = REVIVING_NOW + revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently. + throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution) + spawn(time SECONDS) + // check to see if they've been fixed by outside forces in the meantime such as defibbing + if(stat != DEAD) + to_chat(src, "Your body has recovered from its ordeal, ready to regenerate itself again.") + revive_ready = REVIVING_READY //reset their cooldown + clear_alert("regen") + throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) + + // Was dead, still dead. + else + to_chat(src, "Consciousness begins to stir as your new body awakens, ready to hatch.") + verbs |= /mob/living/carbon/human/proc/hatch + revive_ready = REVIVING_DONE + src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) + clear_alert("regen") + throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) - //Dead until nutrition injected. - else - to_chat(src, "Your body is too damaged to regenerate without additional nutrients to feed what few living cells remain.") //Clicked regen while NOT dead else @@ -66,29 +74,17 @@ throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution) spawn(time SECONDS) - //If they're still alive after regenning. - if(stat != DEAD) + //Slightly different flavour messages + if(stat != DEAD || hasnutriment()) to_chat(src, "Consciousness begins to stir as your new body awakens, ready to hatch..") - verbs |= /mob/living/carbon/human/proc/hatch - revive_ready = REVIVING_DONE - src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) - clear_alert("regen") - throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) - - //Was alive, now dead - else if(hasnutriment()) - to_chat(src, "Consciousness begins to stir as your new body awakens, ready to hatch..") - verbs |= /mob/living/carbon/human/proc/hatch - revive_ready = REVIVING_DONE - src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) - clear_alert("regen") - throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) - - //Dead until nutrition injected. else - to_chat(src, "Your body was unable to regenerate, what few living cells remain require additional nutrients to complete the process.") - revive_ready = REVIVING_READY //reset their cooldown - clear_alert("regen") + to_chat(src, "Consciousness begins to stir as your battered body struggles to recover from its ordeal..") + verbs |= /mob/living/carbon/human/proc/hatch + revive_ready = REVIVING_DONE + src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) + clear_alert("regen") + throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) + /mob/living/carbon/human/proc/hasnutriment() if (bloodstr.has_reagent("nutriment", 30) || src.bloodstr.has_reagent("protein", 15)) //protein needs half as much. For reference, a steak contains 9u protein. @@ -97,6 +93,16 @@ return TRUE else return FALSE +/mob/living/carbon/human/proc/quickcheckuninjured() + if (getBruteLoss() || getFireLoss() || getHalLoss() || getToxLoss() || getOxyLoss() || getBrainLoss()) //fails if they have any of the main damage types + return FALSE + for (var/obj/item/organ/O in organs) //check their organs just in case they're being sneaky and somehow have organ damage but no health damage + if (O.damage) + return FALSE + for (var/obj/item/organ/O in internal_organs) //check their organs just in case they're being sneaky and somehow have organ damage but no health damage + if (O.damage) + return FALSE + return TRUE /mob/living/carbon/human/proc/hatch() set name = "Hatch" @@ -112,58 +118,54 @@ //Dead when hatching if(stat == DEAD) - //Check again for nutriment (necessary?) - if(hasnutriment()) - chimera_hatch() - adjustBrainLoss(10) // if they're reviving from dead, they come back with 10 brainloss on top of whatever's unhealed. - visible_message("

The lifeless husk of [src] bursts open, revealing a new, intact copy in the pool of viscera.

") //Bloody hell... - clear_alert("hatch") - return - - //Don't have nutriment to hatch! Or you somehow died in between completing your revive and hitting hatch. - else - to_chat(src, "Your body was unable to regenerate, what few living cells remain require additional nutrients to complete the process.") - verbs -= /mob/living/carbon/human/proc/hatch - revive_ready = REVIVING_READY //reset their cooldown they can try again when they're given a kickstart - clear_alert("hatch") + //Reviving from ded takes extra nutrition - if it isn't provided from outside sources, it comes from you + if(!hasnutriment()) + nutrition=nutrition * 0.75 + chimera_hatch() + adjustBrainLoss(5) // if they're reviving from dead, they come back with 5 brainloss on top of whatever's unhealed. + visible_message("

The former corpse staggers to its feet, all its former wounds having vanished...

") //Bloody hell... + clear_alert("hatch") + return //Alive when hatching else chimera_hatch() - visible_message("

The dormant husk of [src] bursts open, revealing a new, intact copy in the pool of viscera.

") //Bloody hell... + + visible_message("

[src] rises to \his feet.

") //Bloody hell... clear_alert("hatch") /mob/living/carbon/human/proc/chimera_hatch() verbs -= /mob/living/carbon/human/proc/hatch to_chat(src, "Your new body awakens, bursting free from your old skin.") - //Modify and record values (half nutrition and braindamage) - var/old_nutrition = nutrition * 0.5 - var/braindamage = (brainloss * 0.5) //Can only heal half brain damage. - + var/old_nutrition = nutrition + var/braindamage = min(5, max(0, (brainloss-1) * 0.5)) //brainloss is tricky to heal and might take a couple of goes to get rid of completely. + var/uninjured=quickcheckuninjured() //I did have special snowflake code, but this is easier. revive() mutations.Remove(HUSK) - nutrition = old_nutrition setBrainLoss(braindamage) - //Drop everything - for(var/obj/item/W in src) - drop_from_inventory(W) + if(!uninjured) + nutrition = old_nutrition * 0.5 + //Drop everything + for(var/obj/item/W in src) + drop_from_inventory(W) + //Visual effects + var/T = get_turf(src) + var/blood_color = species.blood_color + var/flesh_color = species.flesh_color + new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color) + visible_message("

The lifeless husk of [src] bursts open, revealing a new, intact copy in the pool of viscera.

") //Bloody hell... + playsound(T, 'sound/effects/mob_effects/xenochimera/hatch.ogg', 50) + else //lower cost for doing a quick cosmetic revive + nutrition = old_nutrition * 0.9 //Unfreeze some things does_not_breathe = FALSE update_canmove() weakened = 2 - //Visual effects - var/T = get_turf(src) - var/blood_color = species.blood_color - var/flesh_color = species.flesh_color - new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color) - - playsound(T, 'sound/effects/mob_effects/xenochimera/hatch.ogg', 50) - revive_ready = world.time + 10 MINUTES //set the cooldown CHOMPEdit: Reduced this to 10 minutes, you're playing with fire if you're reviving that often. /mob/living/carbon/human/proc/revivingreset() // keep this as a debug proc or potential future use diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 40827c09166..72e0e144d3d 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -9,7 +9,7 @@ name_plural = "Xenochimeras" icobase = 'icons/mob/human_races/r_xenochimera.dmi' deform = 'icons/mob/human_races/r_def_xenochimera.dmi' - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws/chimera, /datum/unarmed_attack/bite/sharp) darksight = 8 //critters with instincts to hide in the dark need to see in the dark - about as good as tajara. slowdown = -0.2 //scuttly, but not as scuttly as a tajara or a teshari. brute_mod = 0.8 //About as tanky to brute as a Unathi. They'll probably snap and go feral when hurt though. @@ -97,6 +97,9 @@ //Very low pressure damage if(adjusted_pressure2 <= 20) H.take_overall_damage(brute=LOW_PRESSURE_DAMAGE, used_weapon = "Low Pressure") + //they handle areas where they can't breathe better than most, but it still lowers their effective health as well as all the other bad stuff that comes with unbreathable environments + if(H.getOxyLoss() >= 50) + H.does_not_breathe = TRUE //Cold hurts and gives them pain messages, eventually weakening and paralysing, but doesn't damage or trigger feral. //NB: 'body_temperature' used here is the 'setpoint' species var From 83e3e72b3116240aeab5e1a384967da8ee3a67a0 Mon Sep 17 00:00:00 2001 From: Pandora Date: Thu, 28 Jul 2022 05:17:53 +0100 Subject: [PATCH 07/29] Update engineering_vr.dm --- code/game/jobs/job/engineering_vr.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/game/jobs/job/engineering_vr.dm b/code/game/jobs/job/engineering_vr.dm index 86c3f2e6b15..268ef0f32c2 100644 --- a/code/game/jobs/job/engineering_vr.dm +++ b/code/game/jobs/job/engineering_vr.dm @@ -27,13 +27,18 @@ /datum/job/engineer pto_type = PTO_ENGINEERING alt_titles = list("Maintenance Technician" = /datum/alt_title/maint_tech, "Engine Technician" = /datum/alt_title/engine_tech, - "Electrician" = /datum/alt_title/electrician, "Construction Engineer" = /datum/alt_title/construction_engi) + "Electrician" = /datum/alt_title/electrician, "Construction Engineer" = /datum/alt_title/construction_engi, "Engineering Contractor" = /datum/alt_title/engineering_contractor) /datum/alt_title/construction_engi title = "Construction Engineer" title_blurb = "A Construction Engineer fulfills similar duties to other engineers, but usually occupies spare time with construction of extra facilities in dedicated areas or \ as additions to station layout." +/datum/alt_title/engineering_contractor + title = "Engineering Contractor" + title_blurb = "An Engineering Contractor fulfills similar duties to other engineers, but isn't directly employed by NT proper. + + /datum/job/atmos @@ -49,4 +54,4 @@ /datum/alt_title/disposals_tech title = "Disposals Technician" - title_blurb = "A Disposals Technician is an Atmospheric Technician still and can fulfill all the same duties, although specializes more in disposals delivery system's operations and configurations." \ No newline at end of file + title_blurb = "A Disposals Technician is an Atmospheric Technician still and can fulfill all the same duties, although specializes more in disposals delivery system's operations and configurations." From a03cfd1f9e46de1e6dbc3ea87d285c61cbe413fd Mon Sep 17 00:00:00 2001 From: Pandora Date: Thu, 28 Jul 2022 05:22:25 +0100 Subject: [PATCH 08/29] Update medical_vr.dm --- code/game/jobs/job/medical_vr.dm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/game/jobs/job/medical_vr.dm b/code/game/jobs/job/medical_vr.dm index d38a5656300..2aa8e3c9047 100644 --- a/code/game/jobs/job/medical_vr.dm +++ b/code/game/jobs/job/medical_vr.dm @@ -27,7 +27,7 @@ pto_type = PTO_MEDICAL alt_titles = list("Physician" = /datum/alt_title/physician, "Medical Practitioner" = /datum/alt_title/medical_practitioner, "Surgeon" = /datum/alt_title/surgeon, "Emergency Physician" = /datum/alt_title/emergency_physician, "Nurse" = /datum/alt_title/nurse, "Orderly" = /datum/alt_title/orderly, - "Virologist" = /datum/alt_title/virologist) + "Virologist" = /datum/alt_title/virologist, "Medical Contractor = /datum/alt_title/medical_contractor) /datum/alt_title/physician @@ -42,6 +42,10 @@ and dirty labor around the department." title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse +/datum/alt_title/medical_contractor + title = "Medical Contractor" + title_blurb = "A Medical Contractor can be anything from a full-blown doctor to the likes of a nurse or orderly, but isn't directly employed by NT proper. + /datum/job/chemist pto_type = PTO_MEDICAL @@ -80,4 +84,4 @@ title_outfit = /decl/hierarchy/outfit/job/medical/paramedic/emt /datum/alt_title/sar - title = "Search and Rescue" \ No newline at end of file + title = "Search and Rescue" From 229980a863db1b7ed7a1f100b1fadeb4f95fcbd1 Mon Sep 17 00:00:00 2001 From: Pandora Date: Thu, 28 Jul 2022 05:25:53 +0100 Subject: [PATCH 09/29] Update medical_vr.dm --- code/game/jobs/job/medical_vr.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/jobs/job/medical_vr.dm b/code/game/jobs/job/medical_vr.dm index 2aa8e3c9047..3e7795eceda 100644 --- a/code/game/jobs/job/medical_vr.dm +++ b/code/game/jobs/job/medical_vr.dm @@ -27,7 +27,7 @@ pto_type = PTO_MEDICAL alt_titles = list("Physician" = /datum/alt_title/physician, "Medical Practitioner" = /datum/alt_title/medical_practitioner, "Surgeon" = /datum/alt_title/surgeon, "Emergency Physician" = /datum/alt_title/emergency_physician, "Nurse" = /datum/alt_title/nurse, "Orderly" = /datum/alt_title/orderly, - "Virologist" = /datum/alt_title/virologist, "Medical Contractor = /datum/alt_title/medical_contractor) + "Virologist" = /datum/alt_title/virologist, "Medical Contractor" = /datum/alt_title/medical_contractor) /datum/alt_title/physician From b318d0de633ecc528ff9f530297bbb2670b8d9d0 Mon Sep 17 00:00:00 2001 From: Pandora Date: Thu, 28 Jul 2022 05:34:23 +0100 Subject: [PATCH 10/29] Update medical_vr.dm --- code/game/jobs/job/medical_vr.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/jobs/job/medical_vr.dm b/code/game/jobs/job/medical_vr.dm index 3e7795eceda..e26b398a4ee 100644 --- a/code/game/jobs/job/medical_vr.dm +++ b/code/game/jobs/job/medical_vr.dm @@ -44,7 +44,7 @@ /datum/alt_title/medical_contractor title = "Medical Contractor" - title_blurb = "A Medical Contractor can be anything from a full-blown doctor to the likes of a nurse or orderly, but isn't directly employed by NT proper. + title_blurb = "A Medical Contractor can be anything from a full-blown doctor to the likes of a nurse or orderly, but isn't directly employed by NT proper." /datum/job/chemist From 012e774db278729c3ca5f1ff3dee85e6de6c8d7c Mon Sep 17 00:00:00 2001 From: Pandora Date: Thu, 28 Jul 2022 05:34:58 +0100 Subject: [PATCH 11/29] This is why coding when shattered is a bad idea --- code/game/jobs/job/engineering_vr.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/jobs/job/engineering_vr.dm b/code/game/jobs/job/engineering_vr.dm index 268ef0f32c2..f931f0dfaeb 100644 --- a/code/game/jobs/job/engineering_vr.dm +++ b/code/game/jobs/job/engineering_vr.dm @@ -36,7 +36,7 @@ /datum/alt_title/engineering_contractor title = "Engineering Contractor" - title_blurb = "An Engineering Contractor fulfills similar duties to other engineers, but isn't directly employed by NT proper. + title_blurb = "An Engineering Contractor fulfills similar duties to other engineers, but isn't directly employed by NT proper." From 4c1f8de5578f797c978a501eb19c9e5148054900 Mon Sep 17 00:00:00 2001 From: Heroman Date: Thu, 28 Jul 2022 16:38:39 +1000 Subject: [PATCH 12/29] Selective bellymode now selectable and pref-adjustible --- code/__defines/belly_modes_vr.dm | 1 + .../living/simple_mob/subtypes/vore/vore.dm | 1 + code/modules/vore/eating/belly_obj_vr.dm | 5 ++- .../vore/eating/bellymodes_datum_vr.dm | 40 ++++++++++++++----- code/modules/vore/eating/living_vr.dm | 4 ++ code/modules/vore/eating/vore_vr.dm | 6 +++ code/modules/vore/eating/vorepanel_vr.dm | 15 +++++++ code/modules/vore/mouseray.dm | 1 + tgui/packages/tgui/interfaces/VorePanel.js | 10 +++++ tgui/public/tgui.bundle.js | 2 +- 10 files changed, 73 insertions(+), 12 deletions(-) diff --git a/code/__defines/belly_modes_vr.dm b/code/__defines/belly_modes_vr.dm index 5560155de36..c86e66f1c06 100644 --- a/code/__defines/belly_modes_vr.dm +++ b/code/__defines/belly_modes_vr.dm @@ -1,4 +1,5 @@ // Normal digestion modes +#define DM_DEFAULT "Default" // Not a real bellymode, used for handling on 'selective' bellymode prefs. #define DM_HOLD "Hold" #define DM_HOLD_ABSORBED "Hold Absorbed" // Not a real bellymode, used for handling different idle messages for absorbed prey. #define DM_DIGEST "Digest" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm index 035f287d629..48953e15a84 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm @@ -24,6 +24,7 @@ allowmobvore = client.prefs_vr.allowmobvore permit_healbelly = client.prefs_vr.permit_healbelly noisy = client.prefs_vr.noisy + selective_preference = client.prefs_vr.selective_preference drop_vore = client.prefs_vr.drop_vore stumble_vore = client.prefs_vr.stumble_vore diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index 6ffdd0cca5c..6904014e2df 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -50,6 +50,7 @@ var/emote_time = 60 // How long between stomach emotes at prey (in seconds) var/emote_active = TRUE // Are we even giving emotes out at all or not? var/next_emote = 0 // When we're supposed to print our next emote, as a world.time + var/selective_preference = DM_DIGEST // Which type of selective bellymode do we default to? // Generally just used by AI var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location @@ -58,7 +59,7 @@ //I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere. //Actual full digest modes - var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_ABSORB,DM_DRAIN,DM_UNABSORB,DM_HEAL,DM_SHRINK,DM_GROW,DM_SIZE_STEAL,DM_EGG) + var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_ABSORB,DM_DRAIN,DM_SELECT,DM_UNABSORB,DM_HEAL,DM_SHRINK,DM_GROW,DM_SIZE_STEAL,DM_EGG) //Digest mode addon flags var/tmp/static/list/mode_flag_list = list("Numbing" = DM_FLAG_NUMBING, "Stripping" = DM_FLAG_STRIPPING, "Leave Remains" = DM_FLAG_LEAVEREMAINS, "Muffles" = DM_FLAG_THICKBELLY, "Affect Worn Items" = DM_FLAG_AFFECTWORN, "Jams Sensors" = DM_FLAG_JAMSENSORS, "Complete Absorb" = DM_FLAG_FORCEPSAY) //Item related modes @@ -200,6 +201,7 @@ "emote_lists", "emote_time", "emote_active", + "selective_preference", "mode_flags", "item_digest_mode", "contaminates", @@ -1128,6 +1130,7 @@ dupe.egg_type = egg_type dupe.emote_time = emote_time dupe.emote_active = emote_active + dupe.selective_preference = selective_preference dupe.save_digest_mode = save_digest_mode //// Object-holding variables diff --git a/code/modules/vore/eating/bellymodes_datum_vr.dm b/code/modules/vore/eating/bellymodes_datum_vr.dm index ce67d58b1be..6eb115e0442 100644 --- a/code/modules/vore/eating/bellymodes_datum_vr.dm +++ b/code/modules/vore/eating/bellymodes_datum_vr.dm @@ -223,15 +223,35 @@ GLOBAL_LIST_INIT(digest_modes, list()) id = DM_SELECT /datum/digest_mode/selective/process_mob(obj/belly/B, mob/living/L) - var/datum/digest_mode/tempmode = GLOB.digest_modes["Hold"] + var/datum/digest_mode/tempmode = GLOB.digest_modes[DM_HOLD] // Default to Hold in case of big oof fallback //if not absorbed, see if they're food - if(L.digestable) - tempmode = GLOB.digest_modes["Digest"] - //if they're indigestible, then try to absorb them - else if(L.absorbable) - tempmode = GLOB.digest_modes["Absorb"] - //if they're not absorbable, just drain - else - tempmode = GLOB.digest_modes["Drain"] - //finally, process the mob as if they were subject to the chosen mode + switch(L.selective_preference) // First, we respect prey prefs + if(DM_DIGEST) + if(L.digestable) + tempmode = GLOB.digest_modes[DM_DIGEST] // They want to be digested and can be, Digest + else + tempmode = GLOB.digest_modes[DM_DRAIN] // They want to be digested but can't be! Drain. + if(DM_ABSORB) + if(L.absorbable) + tempmode = GLOB.digest_modes[DM_ABSORB] // They want to be absorbed and can be. Absorb. + else + tempmode = GLOB.digest_modes[DM_DRAIN] // They want to be absorbed but can't be! Drain. + if(DM_DRAIN) + tempmode = GLOB.digest_modes[DM_DRAIN] // They want to be drained. Drain. + if(DM_DEFAULT) + switch(B.selective_preference) // They don't actually care? Time for our own preference. + if(DM_DIGEST) + if(L.digestable) + tempmode = GLOB.digest_modes[DM_DIGEST] // We prefer digestion and they're digestible? Digest + else if(L.absorbable) + tempmode = GLOB.digest_modes[DM_ABSORB] // If not digestible, are they absorbable? Then absorb. + else + tempmode = GLOB.digest_modes[DM_DRAIN] // Otherwise drain. + if(DM_ABSORB) + if(L.absorbable) + tempmode = GLOB.digest_modes[DM_ABSORB] // We prefer absorption and they're absorbable? Absorb. + else if(L.digestable) + tempmode = GLOB.digest_modes[DM_DIGEST] // If not absorbable, are they digestible? Then digest. + else + tempmode = GLOB.digest_modes[DM_DRAIN] // Otherwise drain. return tempmode.process_mob(B, L) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 0c1ac09b5e8..e9db52b9af9 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -45,6 +45,7 @@ var/adminbus_eat_minerals = FALSE // This creature subsists on a diet of pure adminium. var/vis_height = 32 // Sprite height used for resize features. var/show_vore_fx = TRUE // Show belly fullscreens + var/selective_preference = DM_DEFAULT // Preference for selective bellymode var/regen_sounds = list( 'sound/effects/mob_effects/xenochimera/regen_1.ogg', 'sound/effects/mob_effects/xenochimera/regen_2.ogg', @@ -263,6 +264,7 @@ P.vore_smell = src.vore_smell P.permit_healbelly = src.permit_healbelly P.noisy = src.noisy + P.selective_preference = src.selective_preference P.show_vore_fx = src.show_vore_fx P.can_be_drop_prey = src.can_be_drop_prey P.can_be_drop_pred = src.can_be_drop_pred @@ -307,6 +309,7 @@ vore_taste = P.vore_taste vore_smell = P.vore_smell permit_healbelly = P.permit_healbelly + selective_preference = P.selective_preference noisy = P.noisy show_vore_fx = P.show_vore_fx can_be_drop_prey = P.can_be_drop_prey @@ -1013,6 +1016,7 @@ dispvoreprefs += "Leaves Remains: [digest_leave_remains ? "Enabled" : "Disabled"]
" dispvoreprefs += "Mob Vore: [allowmobvore ? "Enabled" : "Disabled"]
" dispvoreprefs += "Healbelly permission: [permit_healbelly ? "Allowed" : "Disallowed"]
" + dispvoreprefs += "Selective Mode Pref: [src.selective_preference]
" dispvoreprefs += "Spontaneous vore prey: [can_be_drop_prey ? "Enabled" : "Disabled"]
" dispvoreprefs += "Spontaneous vore pred: [can_be_drop_pred ? "Enabled" : "Disabled"]
" dispvoreprefs += "Drop Vore: [drop_vore ? "Enabled" : "Disabled"]
" diff --git a/code/modules/vore/eating/vore_vr.dm b/code/modules/vore/eating/vore_vr.dm index b4d4ef8c7b8..0d36a0b9671 100644 --- a/code/modules/vore/eating/vore_vr.dm +++ b/code/modules/vore/eating/vore_vr.dm @@ -70,6 +70,8 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE var/vore_taste = "nothing in particular" var/vore_smell = "nothing in particular" + var/selective_preference = DM_DEFAULT + var/nutrition_message_visible = TRUE var/list/nutrition_messages = list( @@ -168,6 +170,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE vore_smell = json_from_file["vore_smell"] permit_healbelly = json_from_file["permit_healbelly"] noisy = json_from_file["noisy"] + selective_preference = json_from_file["selective_preference"] show_vore_fx = json_from_file["show_vore_fx"] can_be_drop_prey = json_from_file["can_be_drop_prey"] can_be_drop_pred = json_from_file["can_be_drop_pred"] @@ -201,6 +204,8 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE allowmobvore = TRUE if(isnull(permit_healbelly)) permit_healbelly = TRUE + if(isnull(selective_preference)) + selective_preference = DM_DEFAULT if (isnull(noisy)) noisy = FALSE if(isnull(show_vore_fx)) @@ -274,6 +279,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE "vore_smell" = vore_smell, "permit_healbelly" = permit_healbelly, "noisy" = noisy, + "selective_preference" = selective_preference, "show_vore_fx" = show_vore_fx, "can_be_drop_prey" = can_be_drop_prey, "can_be_drop_pred" = can_be_drop_pred, diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index 904933c0715..f39bb66e131 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -181,6 +181,7 @@ "shrink_grow_size" = selected.shrink_grow_size, "emote_time" = selected.emote_time, "emote_active" = selected.emote_active, + "selective_preference" = selected.selective_preference, "nutrition_ex" = host.nutrition_message_visible, "weight_ex" = host.weight_message_visible, "belly_fullscreen" = selected.belly_fullscreen, @@ -487,6 +488,14 @@ host.stumble_vore = !host.stumble_vore unsaved_changes = TRUE return TRUE + if("switch_selective_mode_pref") + host.selective_preference = tgui_input_list(usr, "What would you prefer happen to you with selective bellymode?","Selective Bellymode", list(DM_DEFAULT, DM_DIGEST, DM_ABSORB, DM_DRAIN)) + if(!(host.selective_preference)) + host.selective_preference = DM_DEFAULT + if(host.client.prefs_vr) + host.client.prefs_vr.selective_preference = host.selective_preference + unsaved_changes = TRUE + return TRUE if("toggle_nutrition_ex") host.nutrition_message_visible = !host.nutrition_message_visible unsaved_changes = TRUE @@ -1079,6 +1088,12 @@ if("b_emoteactive") host.vore_selected.emote_active = !host.vore_selected.emote_active . = TRUE + if("b_selective_mode_pref_toggle") + if(host.vore_selected.selective_preference == DM_DIGEST) + host.vore_selected.selective_preference = DM_ABSORB + else + host.vore_selected.selective_preference = DM_DIGEST + . = TRUE if("b_emotetime") var/new_time = tgui_input_number(user, "Choose the period it takes for idle belly emotes to be shown to prey. Measured in seconds, Minimum 1 minute, Maximum 10 minutes.", "Set Belly Emote Delay.", host.vore_selected.digest_brute, 600, 60) if(new_time == null) diff --git a/code/modules/vore/mouseray.dm b/code/modules/vore/mouseray.dm index dc9e503cd1b..ca646e37ba5 100644 --- a/code/modules/vore/mouseray.dm +++ b/code/modules/vore/mouseray.dm @@ -230,6 +230,7 @@ new_mob.allowmobvore = allowmobvore new_mob.permit_healbelly = permit_healbelly new_mob.noisy = noisy + new_mob.selective_preference = selective_preference new_mob.drop_vore = drop_vore new_mob.stumble_vore = stumble_vore new_mob.slip_vore = slip_vore diff --git a/tgui/packages/tgui/interfaces/VorePanel.js b/tgui/packages/tgui/interfaces/VorePanel.js index ed1dbc8d3e7..0235d80d80a 100644 --- a/tgui/packages/tgui/interfaces/VorePanel.js +++ b/tgui/packages/tgui/interfaces/VorePanel.js @@ -499,6 +499,7 @@ const VoreSelectedBellyOptions = (props, context) => { contaminate_flavor, contaminate_color, egg_type, + selective_preference, save_digest_mode, } = belly; @@ -610,6 +611,12 @@ const VoreSelectedBellyOptions = (props, context) => { content={capitalize(egg_type)} /> + +