mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-14 17:45:02 +01:00
Converts tippable behavior from cows and medibots to a component (tip over anything with adminbus) (#59705)
This PR converts cow-tipping and medibot-tipping into a component, /datum/component/tippable. Cows and medibots now use the tippable component to control their tipping behaviors. This PR also goes through a few atom_attack_hand signals and makes them all send the same arguments.
This commit is contained in:
@@ -1223,7 +1223,7 @@
|
||||
#define COMPONENT_SKIP_ATTACK (1<<1)
|
||||
///from base of atom/attack_ghost(): (mob/dead/observer/ghost)
|
||||
#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost"
|
||||
///from base of atom/attack_hand(): (mob/user)
|
||||
///from base of atom/attack_hand(): (mob/user, list/modifiers)
|
||||
#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand"
|
||||
///from base of atom/attack_paw(): (mob/user)
|
||||
#define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#define BOT_WAIT_FOR_NAV 16 // waiting for nav computation
|
||||
#define BOT_NO_ROUTE 17 // no destination beacon found (or no route)
|
||||
#define BOT_SHOWERSTANCE 18 // cleaning unhygienic humans
|
||||
#define BOT_TIPPED 19 // someone tipped a medibot over ;_;
|
||||
#define BOT_TIPPED 19 // someone tipped a bot over ;_;
|
||||
|
||||
//Bot types
|
||||
#define SEC_BOT (1<<0) // Secutritrons (Beepsky) and ED-209s
|
||||
|
||||
@@ -603,8 +603,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define SOULSTONE_TRAIT "soulstone"
|
||||
/// Trait applied to slimes by low temperature
|
||||
#define SLIME_COLD "slime-cold"
|
||||
/// Trait applied to bots by being tipped over
|
||||
#define BOT_TIPPED_OVER "bot-tipped-over"
|
||||
/// Trait applied to mobs by being tipped over
|
||||
#define TIPPED_OVER "tipped-over"
|
||||
/// Trait applied to PAIs by being folded
|
||||
#define PAI_FOLDED "pai-folded"
|
||||
/// Trait applied to brain mobs when they lack external aid for locomotion, such as being inside a mech.
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Tippable component. For making mobs able to be tipped, like cows and medibots.
|
||||
*/
|
||||
/datum/component/tippable
|
||||
/// Time it takes to tip the mob. Can be 0, for instant tipping.
|
||||
var/tip_time = 3 SECONDS
|
||||
/// Time it takes to untip the mob. Can also be 0, for instant untip.
|
||||
var/untip_time = 1 SECONDS
|
||||
/// Time it takes for the mob to right itself. Can be 0 for instant self-righting, or null, to never self-right.
|
||||
var/self_right_time = 60 SECONDS
|
||||
/// Whether the mob is currently tipped.
|
||||
var/is_tipped = FALSE
|
||||
/// Callback to additional behavior before being tipped (on try_tip). Return anything from this callback to cancel the tip.
|
||||
var/datum/callback/pre_tipped_callback
|
||||
/// Callback to additional behavior after successfully tipping the mob.
|
||||
var/datum/callback/post_tipped_callback
|
||||
/// Callback to additional behavior after being untipped.
|
||||
var/datum/callback/post_untipped_callback
|
||||
|
||||
/datum/component/tippable/Initialize(
|
||||
tip_time = 3 SECONDS,
|
||||
untip_time = 1 SECONDS,
|
||||
self_right_time = 60 SECONDS,
|
||||
datum/callback/pre_tipped_callback,
|
||||
datum/callback/post_tipped_callback,
|
||||
datum/callback/post_untipped_callback)
|
||||
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.tip_time = tip_time
|
||||
src.untip_time = untip_time
|
||||
src.self_right_time = self_right_time
|
||||
src.pre_tipped_callback = pre_tipped_callback
|
||||
src.post_tipped_callback = post_tipped_callback
|
||||
src.post_untipped_callback = post_untipped_callback
|
||||
|
||||
/datum/component/tippable/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/interact_with_tippable)
|
||||
|
||||
/datum/component/tippable/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_ATOM_ATTACK_HAND)
|
||||
|
||||
/datum/component/tippable/Destroy()
|
||||
if(pre_tipped_callback)
|
||||
QDEL_NULL(pre_tipped_callback)
|
||||
if(post_tipped_callback)
|
||||
QDEL_NULL(post_tipped_callback)
|
||||
if(post_untipped_callback)
|
||||
QDEL_NULL(post_untipped_callback)
|
||||
return ..()
|
||||
|
||||
/*
|
||||
* Attempt to interact with [source], either tipping it or helping it up.
|
||||
*
|
||||
* source - the mob being tipped over
|
||||
* user - the mob interacting with source
|
||||
* modifiers - list of on click modifiers (we only tip mobs over using right click!)
|
||||
*/
|
||||
/datum/component/tippable/proc/interact_with_tippable(mob/living/source, mob/user, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/mob/living/living_user = user
|
||||
if(DOING_INTERACTION_WITH_TARGET(user, source))
|
||||
return
|
||||
if(istype(living_user) && !living_user.combat_mode)
|
||||
return
|
||||
|
||||
if(is_tipped)
|
||||
INVOKE_ASYNC(src, .proc/try_untip, source, user)
|
||||
else if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
||||
INVOKE_ASYNC(src, .proc/try_tip, source, user)
|
||||
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/*
|
||||
* Try to tip over [tipped_mob].
|
||||
* If the mob is dead, or optional callback returns a value, or our do-after fails, we don't tip the mob.
|
||||
* Otherwise, upon completing of the do_after, tip over the mob.
|
||||
*
|
||||
* tipped_mob - the mob being tipped over
|
||||
* tipper - the mob tipping the tipped_mob
|
||||
*/
|
||||
/datum/component/tippable/proc/try_tip(mob/living/tipped_mob, mob/tipper)
|
||||
if(tipped_mob.stat != CONSCIOUS)
|
||||
return
|
||||
|
||||
if(pre_tipped_callback?.Invoke(tipper))
|
||||
return
|
||||
|
||||
if(tip_time > 0)
|
||||
to_chat(tipper, span_warning("You begin tipping over [tipped_mob]..."))
|
||||
tipped_mob.visible_message(
|
||||
span_warning("[tipper] begins tipping over [tipped_mob]."),
|
||||
span_userdanger("[tipper] begins tipping you over!"),
|
||||
ignored_mobs = tipper
|
||||
)
|
||||
|
||||
if(!do_after(tipper, tip_time, target = tipped_mob))
|
||||
to_chat(tipper, span_danger("You fail to tip over [tipped_mob]."))
|
||||
return
|
||||
do_tip(tipped_mob, tipper)
|
||||
|
||||
/*
|
||||
* Actually tip over the mob, setting it to tipped.
|
||||
* Also invoking any callbacks we have, with the tipper as the argument,
|
||||
* and set a timer to right our self-right our tipped mob if we can.
|
||||
*
|
||||
* tipped_mob - the mob who was tipped
|
||||
* tipper - the mob who tipped the tipped_mob
|
||||
*/
|
||||
/datum/component/tippable/proc/do_tip(mob/living/tipped_mob, mob/tipper)
|
||||
if(QDELETED(tipped_mob))
|
||||
CRASH("Tippable component: do_tip() called with QDELETED tipped_mob!")
|
||||
|
||||
to_chat(tipper, span_warning("You tip over [tipped_mob]."))
|
||||
tipped_mob.visible_message(
|
||||
span_warning("[tipper] tips over [tipped_mob]."),
|
||||
span_userdanger("You are tipped over by [tipper]!"),
|
||||
ignored_mobs = tipper
|
||||
)
|
||||
|
||||
set_tipped_status(tipped_mob, TRUE)
|
||||
post_tipped_callback?.Invoke(tipper)
|
||||
if(isnull(self_right_time))
|
||||
return
|
||||
else if(self_right_time <= 0)
|
||||
right_self(tipped_mob)
|
||||
else
|
||||
addtimer(CALLBACK(src, .proc/right_self, tipped_mob), self_right_time)
|
||||
|
||||
/*
|
||||
* Try to untip a mob that has been tipped.
|
||||
* After a do-after is completed, we untip the mob.
|
||||
*
|
||||
* tipped_mob - the mob who is tipped
|
||||
* untipper - the mob who is untipping the tipped_mob
|
||||
*/
|
||||
/datum/component/tippable/proc/try_untip(mob/living/tipped_mob, mob/untipper)
|
||||
if(untip_time > 0)
|
||||
to_chat(untipper, span_notice("You begin righting [tipped_mob]..."))
|
||||
tipped_mob.visible_message(
|
||||
span_notice("[untipper] begins righting [tipped_mob]."),
|
||||
span_notice("[untipper] begins righting you."),
|
||||
ignored_mobs = untipper
|
||||
)
|
||||
|
||||
if(!do_after(untipper, untip_time, target = tipped_mob))
|
||||
to_chat(untipper, span_warning("You fail to right [tipped_mob]."))
|
||||
return
|
||||
|
||||
do_untip(tipped_mob, untipper)
|
||||
|
||||
/*
|
||||
* Actually untip over the mob, setting it to untipped.
|
||||
* Also invoke any untip callbacks we have, with the untipper as the argument.
|
||||
*
|
||||
* tipped_mob - the mob who was tipped
|
||||
* tipper - the mob who tipped the tipped_mob
|
||||
*/
|
||||
/datum/component/tippable/proc/do_untip(mob/living/tipped_mob, mob/untipper)
|
||||
if(QDELETED(tipped_mob))
|
||||
return
|
||||
|
||||
to_chat(untipper, span_notice("You right [tipped_mob]."))
|
||||
tipped_mob.visible_message(
|
||||
span_notice("[untipper] rights [tipped_mob]."),
|
||||
span_notice("You are righted by [untipper]!"),
|
||||
ignored_mobs = untipper
|
||||
)
|
||||
|
||||
set_tipped_status(tipped_mob, FALSE)
|
||||
post_untipped_callback?.Invoke(untipper)
|
||||
|
||||
/*
|
||||
* Proc called after a timer to have a tipped mob un-tip itself after a certain length of time.
|
||||
* Sets our mob to untipped and invokes the untipped callback without any arguments if we have one.
|
||||
*
|
||||
* tipped_mob - the mob who was tipped, and is freeing itself
|
||||
*/
|
||||
/datum/component/tippable/proc/right_self(mob/living/tipped_mob)
|
||||
if(!is_tipped || QDELETED(tipped_mob))
|
||||
return
|
||||
|
||||
set_tipped_status(tipped_mob, FALSE)
|
||||
post_untipped_callback?.Invoke()
|
||||
|
||||
tipped_mob.visible_message(
|
||||
span_notice("[tipped_mob] rights itself."),
|
||||
span_notice("You right yourself.")
|
||||
)
|
||||
|
||||
/*
|
||||
* Toggles our tipped status between tipped or untipped (TRUE or FALSE)
|
||||
* also handles rotating our mob and adding immobilization traits
|
||||
*
|
||||
* tipped_mob - the mob we're setting to tipped or untipped
|
||||
* new_status - the tipped status we're setting the mob to - TRUE for tipped, FALSE for untipped
|
||||
*/
|
||||
/datum/component/tippable/proc/set_tipped_status(mob/living/tipped_mob, new_status = FALSE)
|
||||
is_tipped = new_status
|
||||
if(is_tipped)
|
||||
tipped_mob.transform = turn(tipped_mob.transform, 180)
|
||||
ADD_TRAIT(tipped_mob, TRAIT_IMMOBILIZED, TIPPED_OVER)
|
||||
else
|
||||
tipped_mob.transform = turn(tipped_mob.transform, -180)
|
||||
REMOVE_TRAIT(tipped_mob, TRAIT_IMMOBILIZED, TIPPED_OVER)
|
||||
@@ -146,7 +146,7 @@
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/mob/living/carbon/attack_hand(mob/living/carbon/human/user, list/modifiers)
|
||||
|
||||
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
||||
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
||||
. = TRUE
|
||||
for(var/thing in diseases)
|
||||
var/datum/disease/D = thing
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/mob/living/silicon/attack_hand(mob/living/carbon/human/user, list/modifiers)
|
||||
. = FALSE
|
||||
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
||||
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
|
||||
. = TRUE
|
||||
if(has_buckled_mobs() && !user.combat_mode)
|
||||
user_unbuckle_mob(buckled_mobs[1], user)
|
||||
|
||||
@@ -33,43 +33,43 @@
|
||||
window_name = "Automatic Medical Unit v1.1"
|
||||
data_hud_type = DATA_HUD_MEDICAL_ADVANCED
|
||||
path_image_color = "#DDDDFF"
|
||||
/// drop determining variable
|
||||
/// drop determining variable
|
||||
var/healthanalyzer = /obj/item/healthanalyzer
|
||||
/// drop determining variable
|
||||
/// drop determining variable
|
||||
var/firstaid = /obj/item/storage/firstaid
|
||||
///based off medkit_X skins in aibots.dmi for your selection; X goes here IE medskin_tox means skin var should be "tox"
|
||||
///based off medkit_X skins in aibots.dmi for your selection; X goes here IE medskin_tox means skin var should be "tox"
|
||||
var/skin
|
||||
var/mob/living/carbon/patient
|
||||
var/mob/living/carbon/oldpatient
|
||||
var/oldloc
|
||||
var/last_found = 0
|
||||
/// Don't spam the "HEY I'M COMING" messages
|
||||
/// Don't spam the "HEY I'M COMING" messages
|
||||
var/last_newpatient_speak = 0
|
||||
/// How much healing do we do at a time?
|
||||
/// How much healing do we do at a time?
|
||||
var/heal_amount = 2.5
|
||||
/// Start healing when they have this much damage in a category
|
||||
/// Start healing when they have this much damage in a category
|
||||
var/heal_threshold = 10
|
||||
/// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types
|
||||
/// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types
|
||||
var/damagetype_healer = BRUTE
|
||||
/// If active, the bot will transmit a critical patient alert to MedHUD users.
|
||||
/// If active, the bot will transmit a critical patient alert to MedHUD users.
|
||||
var/declare_crit = TRUE
|
||||
/// Prevents spam of critical patient alerts.
|
||||
/// Prevents spam of critical patient alerts.
|
||||
var/declare_cooldown = FALSE
|
||||
/// If enabled, the Medibot will not move automatically.
|
||||
/// If enabled, the Medibot will not move automatically.
|
||||
var/stationary_mode = FALSE
|
||||
|
||||
/// silences the medbot if TRUE
|
||||
/// silences the medbot if TRUE
|
||||
var/shut_up = FALSE
|
||||
/// techweb linked to the medbot
|
||||
/// techweb linked to the medbot
|
||||
var/datum/techweb/linked_techweb
|
||||
///Is the medbot currently tending wounds
|
||||
///Is the medbot currently tending wounds
|
||||
var/tending = FALSE
|
||||
///How panicked we are about being tipped over (why would you do this?)
|
||||
///How panicked we are about being tipped over (why would you do this?)
|
||||
var/tipped_status = MEDBOT_PANIC_NONE
|
||||
///The name we got when we were tipped
|
||||
///The name we got when we were tipped
|
||||
var/tipper_name
|
||||
///The last time we were tipped/righted and said a voice line, to avoid spam
|
||||
var/last_tipping_action_voice = 0
|
||||
///Cooldown to track last time we were tipped/righted and said a voice line, to avoid spam
|
||||
COOLDOWN_DECLARE(last_tipping_action_voice)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/mysterious
|
||||
name = "\improper Mysterious Medibot"
|
||||
@@ -119,6 +119,13 @@
|
||||
if(damagetype_healer == "all")
|
||||
return
|
||||
|
||||
AddComponent(/datum/component/tippable, \
|
||||
tip_time = 3 SECONDS, \
|
||||
untip_time = 3 SECONDS, \
|
||||
self_right_time = 3.5 MINUTES, \
|
||||
pre_tipped_callback = CALLBACK(src, .proc/pre_tip_over), \
|
||||
post_tipped_callback = CALLBACK(src, .proc/after_tip_over), \
|
||||
post_untipped_callback = CALLBACK(src, .proc/after_righted))
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/bot_reset()
|
||||
..()
|
||||
@@ -244,37 +251,60 @@
|
||||
else
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/tip_over(mob/user)
|
||||
ADD_TRAIT(src, TRAIT_IMMOBILIZED, BOT_TIPPED_OVER)
|
||||
playsound(src, 'sound/machines/warning-buzzer.ogg', 50)
|
||||
user.visible_message(span_danger("[user] tips over [src]!"), span_danger("You tip [src] over!"))
|
||||
/*
|
||||
* Proc used in a callback for before this medibot is tipped by the tippable component.
|
||||
*
|
||||
* user - the mob who is tipping us over
|
||||
*/
|
||||
/mob/living/simple_animal/bot/medbot/proc/pre_tip_over(mob/user)
|
||||
if(!COOLDOWN_FINISHED(src, last_tipping_action_voice))
|
||||
return
|
||||
|
||||
COOLDOWN_START(src, last_tipping_action_voice, 15 SECONDS) // message for tipping happens when we start interacting, message for righting comes after finishing
|
||||
var/static/list/messagevoice = list(
|
||||
"Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg',
|
||||
"Please don't..." = 'sound/voice/medbot/please_dont.ogg',
|
||||
"I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg',
|
||||
"Nooo..." = 'sound/voice/medbot/nooo.ogg',
|
||||
"Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg',
|
||||
)
|
||||
var/message = pick(messagevoice)
|
||||
speak(message)
|
||||
playsound(src, messagevoice[message], 70, FALSE)
|
||||
|
||||
/*
|
||||
* Proc used in a callback for after this medibot is tipped by the tippable component.
|
||||
*
|
||||
* user - the mob who tipped us over
|
||||
*/
|
||||
/mob/living/simple_animal/bot/medbot/proc/after_tip_over(mob/user)
|
||||
mode = BOT_TIPPED
|
||||
var/matrix/mat = transform
|
||||
transform = mat.Turn(180)
|
||||
tipper_name = user.name
|
||||
playsound(src, 'sound/machines/warning-buzzer.ogg', 50)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/set_right(mob/user)
|
||||
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, BOT_TIPPED_OVER)
|
||||
/*
|
||||
* Proc used in a callback for after this medibot is righted, either by themselves or by a mob, by the tippable component.
|
||||
*
|
||||
* user - the mob who righted us. Can be null.
|
||||
*/
|
||||
/mob/living/simple_animal/bot/medbot/proc/after_righted(mob/user)
|
||||
var/list/messagevoice
|
||||
|
||||
if(user)
|
||||
user.visible_message(span_notice("[user] sets [src] right-side up!"), span_green("You set [src] right-side up!"))
|
||||
if(user.name == tipper_name)
|
||||
messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg')
|
||||
else
|
||||
messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg')
|
||||
else
|
||||
visible_message(span_notice("[src] manages to wriggle enough to right itself."))
|
||||
messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg')
|
||||
tipper_name = null
|
||||
if(world.time > last_tipping_action_voice + 15 SECONDS)
|
||||
last_tipping_action_voice = world.time
|
||||
|
||||
if(COOLDOWN_FINISHED(src, last_tipping_action_voice))
|
||||
COOLDOWN_START(src, last_tipping_action_voice, 15 SECONDS)
|
||||
var/message = pick(messagevoice)
|
||||
speak(message)
|
||||
playsound(src, messagevoice[message], 70)
|
||||
tipped_status = MEDBOT_PANIC_NONE
|
||||
mode = BOT_IDLE
|
||||
transform = matrix()
|
||||
|
||||
/// if someone tipped us over, check whether we should ask for help or just right ourselves eventually
|
||||
/mob/living/simple_animal/bot/medbot/proc/handle_panic()
|
||||
@@ -294,7 +324,6 @@
|
||||
messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg')
|
||||
if(MEDBOT_PANIC_END)
|
||||
speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel)
|
||||
set_right() // strong independent medbot
|
||||
|
||||
if(prob(tipped_status))
|
||||
do_jitter_animation(tipped_status * 0.1)
|
||||
@@ -452,31 +481,6 @@
|
||||
if(damagetype_healer == "all" && treat_me_for.len)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/attack_hand(mob/living/carbon/human/user, list/modifiers)
|
||||
if(DOING_INTERACTION_WITH_TARGET(user, src))
|
||||
to_chat(user, span_warning("You're already interacting with [src]."))
|
||||
return
|
||||
|
||||
if(LAZYACCESS(modifiers, RIGHT_CLICK) && mode != BOT_TIPPED)
|
||||
user.visible_message(span_danger("[user] begins tipping over [src]."), span_warning("You begin tipping over [src]..."))
|
||||
|
||||
if(world.time > last_tipping_action_voice + 15 SECONDS)
|
||||
last_tipping_action_voice = world.time // message for tipping happens when we start interacting, message for righting comes after finishing
|
||||
var/list/messagevoice = list("Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg',"Please don't..." = 'sound/voice/medbot/please_dont.ogg',"I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg', "Nooo..." = 'sound/voice/medbot/nooo.ogg', "Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg')
|
||||
var/message = pick(messagevoice)
|
||||
speak(message)
|
||||
playsound(src, messagevoice[message], 70, FALSE)
|
||||
|
||||
if(do_after(user, 3 SECONDS, target=src))
|
||||
tip_over(user)
|
||||
|
||||
else if(!user.combat_mode && mode == BOT_TIPPED)
|
||||
user.visible_message(span_notice("[user] begins righting [src]."), span_notice("You begin righting [src]..."))
|
||||
if(do_after(user, 3 SECONDS, target=src))
|
||||
set_right(user)
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A, proximity_flag, list/modifiers)
|
||||
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
|
||||
return
|
||||
|
||||
@@ -133,6 +133,11 @@
|
||||
|
||||
/mob/living/simple_animal/cow/Initialize()
|
||||
AddComponent(/datum/component/udder)
|
||||
AddComponent(/datum/component/tippable, \
|
||||
tip_time = 0.5 SECONDS, \
|
||||
untip_time = 0.5 SECONDS, \
|
||||
self_right_time = rand(25 SECONDS, 50 SECONDS), \
|
||||
post_tipped_callback = CALLBACK(src, .proc/after_cow_tipped))
|
||||
AddElement(/datum/element/pet_bonus, "moos happily!")
|
||||
add_cell_sample()
|
||||
make_tameable()
|
||||
@@ -150,34 +155,43 @@
|
||||
buckle_lying = 0
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/cow)
|
||||
|
||||
/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/user, list/modifiers)
|
||||
if(!stat && LAZYACCESS(modifiers, RIGHT_CLICK) && icon_state != icon_dead)
|
||||
user.visible_message(span_warning("[user] tips over [src]."),
|
||||
span_notice("You tip over [src]."))
|
||||
to_chat(src, span_userdanger("You are tipped over by [user]!"))
|
||||
Paralyze(60, ignore_canstun = TRUE)
|
||||
icon_state = icon_dead
|
||||
addtimer(CALLBACK(src, .proc/cow_tipped, user), rand(20,50))
|
||||
/*
|
||||
* Proc called via callback after the cow is tipped by the tippable component.
|
||||
* Begins a timer for us pleading for help.
|
||||
*
|
||||
* tipper - the mob who tipped us
|
||||
*/
|
||||
/mob/living/simple_animal/cow/proc/after_cow_tipped(mob/living/carbon/tipper)
|
||||
addtimer(CALLBACK(src, .proc/look_for_help, tipper), rand(10 SECONDS, 20 SECONDS))
|
||||
|
||||
else
|
||||
..()
|
||||
/*
|
||||
* Find a mob in a short radius around us (prioritizing the person who originally tipped us)
|
||||
* and either look at them for help, or give up. No actual mechanical difference between the two.
|
||||
*
|
||||
* tipper - the mob who originally tipped us
|
||||
*/
|
||||
/mob/living/simple_animal/cow/proc/look_for_help(mob/living/carbon/tipper)
|
||||
// visible part of the visible message
|
||||
var/seen_message = ""
|
||||
// self part of the visible message
|
||||
var/self_message = ""
|
||||
// the mob we're looking to for aid
|
||||
var/mob/living/carbon/savior
|
||||
// look for someone in a radius around us for help. If our original tipper is in range, prioritize them
|
||||
for(var/mob/living/carbon/potential_aid in oview(3, get_turf(src)))
|
||||
if(potential_aid == tipper)
|
||||
savior = tipper
|
||||
break
|
||||
savior = potential_aid
|
||||
|
||||
/mob/living/simple_animal/cow/proc/cow_tipped(mob/living/carbon/M)
|
||||
if(QDELETED(M) || stat)
|
||||
return
|
||||
icon_state = icon_living
|
||||
var/external
|
||||
var/internal
|
||||
if(prob(75))
|
||||
var/text = pick("imploringly.", "pleadingly.",
|
||||
"with a resigned expression.")
|
||||
external = "[src] looks at [M] [text]"
|
||||
internal = "You look at [M] [text]"
|
||||
if(prob(75) && savior)
|
||||
var/text = pick("imploringly", "pleadingly", "with a resigned expression")
|
||||
seen_message = "[src] looks at [savior] [text]."
|
||||
self_message = "You look at [savior] [text]."
|
||||
else
|
||||
external = "[src] seems resigned to its fate."
|
||||
internal = "You resign yourself to your fate."
|
||||
visible_message(span_notice("[external]"),
|
||||
span_revennotice("[internal]"))
|
||||
seen_message = "[src] seems resigned to its fate."
|
||||
self_message = "You resign yourself to your fate."
|
||||
visible_message(span_notice("[seen_message]"), span_notice("[self_message]"))
|
||||
|
||||
///Wisdom cow, gives XP to a random skill and speaks wisdoms
|
||||
/mob/living/simple_animal/cow/wisdom
|
||||
|
||||
@@ -578,6 +578,7 @@
|
||||
#include "code\datums\components\technoshy.dm"
|
||||
#include "code\datums\components\tether.dm"
|
||||
#include "code\datums\components\thermite.dm"
|
||||
#include "code\datums\components\tippable.dm"
|
||||
#include "code\datums\components\trapdoor.dm"
|
||||
#include "code\datums\components\twohanded.dm"
|
||||
#include "code\datums\components\udder.dm"
|
||||
|
||||
Reference in New Issue
Block a user