diff --git a/code/__DEFINES/do_afters.dm b/code/__DEFINES/do_afters.dm
index 456cf940400..5b35e69d0fc 100644
--- a/code/__DEFINES/do_afters.dm
+++ b/code/__DEFINES/do_afters.dm
@@ -2,3 +2,4 @@
#define DOAFTER_SOURCE_MECHADRILL "doafter_mechadrill"
#define DOAFTER_SOURCE_SURVIVALPEN "doafter_survivalpen"
#define DOAFTER_SOURCE_GETTING_UP "doafter_gettingup"
+#define DOAFTER_SOURCE_CLIMBING_LADDER "doafter_climbingladder"
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index ae0f7a89b00..2882139d7a4 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -1,13 +1,13 @@
-/// Checks for RIGHT_CLICK in modifiers and runs attack_hand_secondary if so. Returns TRUE if normal chain blocked
+/// Checks for RIGHT_CLICK in modifiers and runs resolve_right_click_attack if so. Returns TRUE if normal chain blocked.
/mob/living/proc/right_click_attack_chain(atom/target, list/modifiers)
if (!LAZYACCESS(modifiers, RIGHT_CLICK))
return
- var/secondary_result = target.attack_hand_secondary(src, modifiers)
+ var/secondary_result = resolve_right_click_attack(target, modifiers)
if (secondary_result == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || secondary_result == SECONDARY_ATTACK_CONTINUE_CHAIN)
return TRUE
else if (secondary_result != SECONDARY_ATTACK_CALL_NORMAL)
- CRASH("attack_hand_secondary did not return a SECONDARY_ATTACK_* define.")
+ CRASH("resolve_right_click_attack (probably attack_hand_secondary) did not return a SECONDARY_ATTACK_* define.")
/*
Humans:
@@ -37,7 +37,8 @@
if(!right_click_attack_chain(A, modifiers) && !dna?.species?.spec_unarmedattack(src, A, modifiers)) //Because species like monkeys dont use attack hand
A.attack_hand(src, modifiers)
-
+/mob/living/carbon/human/resolve_right_click_attack(atom/target, list/modifiers)
+ return target.attack_hand_secondary(src, modifiers)
/// Return TRUE to cancel other attack hand effects that respect it. Modifiers is the assoc list for click info such as if it was a right click.
/atom/proc/attack_hand(mob/user, list/modifiers)
@@ -124,16 +125,43 @@
/mob/living/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+ return FALSE
+ if(!right_click_attack_chain(attack_target, modifiers))
+ resolve_unarmed_attack(attack_target, modifiers)
+ return TRUE
+
+/**
+ * Called when the unarmed attack hasn't been stopped by the LIVING_UNARMED_ATTACK_BLOCKED macro or the right_click_attack_chain proc.
+ * This will call an attack proc that can vary from mob type to mob type on the target.
+ */
+/mob/living/proc/resolve_unarmed_attack(atom/attack_target, list/modifiers)
attack_target.attack_animal(src, modifiers)
+/**
+ * Called when an unarmed attack performed with right click hasn't been stopped by the LIVING_UNARMED_ATTACK_BLOCKED macro.
+ * This will call a secondary attack proc that can vary from mob type to mob type on the target.
+ * Sometimes, a target is interacted differently when right_clicked, in that case the secondary attack proc should return
+ * a SECONDARY_ATTACK_* value that's not SECONDARY_ATTACK_CALL_NORMAL.
+ * Otherwise, it should just return SECONDARY_ATTACK_CALL_NORMAL. Failure to do so will result in an exception (runtime error).
+ */
+/mob/living/proc/resolve_right_click_attack(atom/target, list/modifiers)
+ return target.attack_animal_secondary(src, modifiers)
+
/atom/proc/attack_animal(mob/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_ANIMAL, user)
+/**
+ * Called when a simple animal or basic mob right clicks an atom.
+ * Returns a SECONDARY_ATTACK_* value.
+ */
+/atom/proc/attack_animal_secondary(mob/user, list/modifiers)
+ return SECONDARY_ATTACK_CALL_NORMAL
+
+///Apparently this is only used by AI datums for basic mobs. A player controlling a basic mob will call attack_animal() when clicking another atom.
/atom/proc/attack_basic_mob(mob/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_BASIC_MOB, user)
-///Attacked by monkey
+///Attacked by monkey. It doesn't need its own *_secondary proc as it just uses attack_hand_secondary instead.
/atom/proc/attack_paw(mob/user, list/modifiers)
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_PAW, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
return TRUE
@@ -144,54 +172,85 @@
Aliens
Defaults to same as monkey in most places
*/
-/mob/living/carbon/alien/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+/mob/living/carbon/alien/resolve_unarmed_attack(atom/attack_target, list/modifiers)
attack_target.attack_alien(src, modifiers)
+/mob/living/carbon/alien/resolve_right_click_attack(atom/target, list/modifiers)
+ return target.attack_alien_secondary(src, modifiers)
+
/atom/proc/attack_alien(mob/living/carbon/alien/user, list/modifiers)
attack_paw(user, modifiers)
- return
+/**
+ * Called when an alien right clicks an atom.
+ * Returns a SECONDARY_ATTACK_* value.
+ */
+/atom/proc/attack_alien_secondary(mob/living/carbon/alien/user, list/modifiers)
+ return SECONDARY_ATTACK_CALL_NORMAL
// Babby aliens
-/mob/living/carbon/alien/larva/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
- attack_target.attack_larva(src)
+/mob/living/carbon/alien/larva/resolve_unarmed_attack(atom/attack_target, list/modifiers)
+ attack_target.attack_larva(src, modifiers)
-/atom/proc/attack_larva(mob/user)
+/mob/living/carbon/alien/larva/resolve_right_click_attack(atom/target, list/modifiers)
+ return target.attack_larva_secondary(src, modifiers)
+
+/atom/proc/attack_larva(mob/user, list/modifiers)
return
+/**
+ * Called when an alien larva right clicks an atom.
+ * Returns a SECONDARY_ATTACK_* value.
+ */
+/atom/proc/attack_larva_secondary(mob/user, list/modifiers)
+ return SECONDARY_ATTACK_CALL_NORMAL
+
/*
Slimes
Nothing happening here
*/
-/mob/living/simple_animal/slime/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+/mob/living/simple_animal/slime/resolve_unarmed_attack(atom/attack_target, proximity_flag, list/modifiers)
if(isturf(attack_target))
return ..()
- attack_target.attack_slime(src)
+ attack_target.attack_slime(src, modifiers)
-/atom/proc/attack_slime(mob/user)
+/mob/living/simple_animal/slime/resolve_right_click_attack(atom/target, list/modifiers)
+ if(isturf(target))
+ return ..()
+ return target.attack_slime_secondary(src, modifiers)
+
+/atom/proc/attack_slime(mob/user, list/modifiers)
return
+/**
+ * Called when a slime mob right clicks an atom (that is not a turf).
+ * Returns a SECONDARY_ATTACK_* value.
+ */
+/atom/proc/attack_slime_secondary(mob/user, list/modifiers)
+ return SECONDARY_ATTACK_CALL_NORMAL
/*
Drones
*/
-/mob/living/simple_animal/drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+
+/mob/living/simple_animal/drone/resolve_unarmed_attack(atom/attack_target, proximity_flag, list/modifiers)
attack_target.attack_drone(src, modifiers)
-/// Defaults to attack_hand or attack_hand_secondary. Override it when you don't want drones to do same stuff as humans.
-/atom/proc/attack_drone(mob/living/simple_animal/drone/user, list/modifiers)
- if(!user.right_click_attack_chain(src, modifiers))
- attack_hand(user, modifiers)
+/mob/living/simple_animal/drone/resolve_right_click_attack(atom/target, list/modifiers)
+ return target.attack_drone_secondary(src, modifiers)
+/// Defaults to attack_hand. Override it when you don't want drones to do same stuff as humans.
+/atom/proc/attack_drone(mob/living/simple_animal/drone/user, list/modifiers)
+ attack_hand(user, modifiers)
+
+/**
+ * Called when a maintenance drone right clicks an atom.
+ * Defaults to attack_hand_secondary.
+ * When overriding it, remember that it ought to return a SECONDARY_ATTACK_* value.
+ */
+/atom/proc/attack_drone_secondary(mob/living/simple_animal/drone/user, list/modifiers)
+ return attack_hand_secondary(user, modifiers)
/*
Brain
@@ -213,26 +272,28 @@
Simple animals
*/
-/mob/living/simple_animal/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+/mob/living/simple_animal/resolve_unarmed_attack(atom/attack_target, list/modifiers)
if(dextrous && (isitem(attack_target) || !combat_mode))
attack_target.attack_hand(src, modifiers)
update_inv_hands()
else
return ..()
+/mob/living/simple_animal/resolve_right_click_attack(atom/target, list/modifiers)
+ if(dextrous && (isitem(target) || !combat_mode))
+ . = target.attack_hand_secondary(src, modifiers)
+ update_inv_hands()
+ else
+ return ..()
/*
Hostile animals
*/
-/mob/living/simple_animal/hostile/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
- if(LIVING_UNARMED_ATTACK_BLOCKED(attack_target))
- return
+/mob/living/simple_animal/hostile/resolve_unarmed_attack(atom/attack_target, list/modifiers)
GiveTarget(attack_target)
if(dextrous && (isitem(attack_target) || !combat_mode))
- ..()
+ return ..()
else
AttackingTarget(attack_target)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 343cabad61c..0d73b7b8613 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -136,7 +136,7 @@
/obj/effect/dummy/chameleon/attack_animal(mob/user, list/modifiers)
master.disrupt()
-/obj/effect/dummy/chameleon/attack_slime()
+/obj/effect/dummy/chameleon/attack_slime(mob/user, list/modifiers)
master.disrupt()
/obj/effect/dummy/chameleon/attack_alien(mob/user, list/modifiers)
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 9282f261896..5daee514f69 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -93,7 +93,7 @@
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
-/obj/attack_slime(mob/living/simple_animal/slime/user)
+/obj/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
if(!user.is_adult)
return
attack_generic(user, rand(10, 15), BRUTE, MELEE, 1)
diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm
index c737d7f4143..b24df6bfea2 100644
--- a/code/game/objects/structures/ladders.dm
+++ b/code/game/objects/structures/ladders.dm
@@ -9,8 +9,8 @@
var/obj/structure/ladder/down //the ladder below this one
var/obj/structure/ladder/up //the ladder above this one
var/crafted = FALSE
- /// Optional travel time for ladder in deciseconds
- var/travel_time = 0
+ /// travel time for ladder in deciseconds
+ var/travel_time = 1 SECONDS
/obj/structure/ladder/Initialize(mapload, obj/structure/ladder/up, obj/structure/ladder/down)
..()
@@ -23,8 +23,22 @@
src.down = down
down.up = src
down.update_appearance()
+
+ register_context()
+
return INITIALIZE_HINT_LATELOAD
+/obj/structure/ladder/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(up)
+ context[SCREENTIP_CONTEXT_LMB] = "Climb up"
+ if(down)
+ context[SCREENTIP_CONTEXT_RMB] = "Climb down"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/structure/ladder/examine(mob/user)
+ . = ..()
+ . += span_info("Left-click it to start moving up; Right-click to start moving down.")
+
/obj/structure/ladder/Destroy(force)
GLOB.ladders -= src
disconnect()
@@ -70,50 +84,89 @@
visible_message(span_danger("[src] is torn to pieces by the gravitational pull!"))
qdel(src)
-/obj/structure/ladder/proc/travel(going_up, mob/user, is_ghost, obj/structure/ladder/ladder)
+/obj/structure/ladder/proc/use(mob/user, going_up = TRUE)
+ if(!in_range(src, user) || DOING_INTERACTION(user, DOAFTER_SOURCE_CLIMBING_LADDER))
+ return
+
+ if(!up && !down)
+ balloon_alert(user, "doesn't lead anywhere!")
+ return
+ if(going_up ? !up : !down)
+ balloon_alert(user, "can't go any further [going_up ? "up" : "down"]")
+ return
+ if(travel_time)
+ INVOKE_ASYNC(src, .proc/start_travelling, user, going_up)
+ else
+ travel(user, going_up)
+ add_fingerprint(user)
+
+/obj/structure/ladder/proc/start_travelling(mob/user, going_up)
+ show_initial_fluff_message(user, going_up)
+ if(do_after(user, travel_time, target = src, interaction_key = DOAFTER_SOURCE_CLIMBING_LADDER))
+ travel(user, going_up)
+
+/// The message shown when the player starts climbing the ladder
+/obj/structure/ladder/proc/show_initial_fluff_message(mob/user, going_up)
+ var/up_down = going_up ? "up" : "down"
+ user.balloon_alert_to_viewers("climbing [up_down]...")
+
+/obj/structure/ladder/proc/travel(mob/user, going_up = TRUE, is_ghost = FALSE)
+ var/obj/structure/ladder/ladder = going_up ? up : down
+ if(!ladder)
+ balloon_alert(user, "there's nothing that way!")
+ return
var/response = SEND_SIGNAL(user, COMSIG_LADDER_TRAVEL, src, ladder, going_up)
if(response & LADDER_TRAVEL_BLOCK)
return
- if(!is_ghost)
- ladder.add_fingerprint(user)
- if(!do_after(user, travel_time, target = src))
- return
- show_fluff_message(going_up, user)
-
var/turf/target = get_turf(ladder)
user.zMove(target = target, z_move_flags = ZMOVE_CHECK_PULLEDBY|ZMOVE_ALLOW_BUCKLED|ZMOVE_INCLUDE_PULLED)
- ladder.use(user) //reopening ladder radial menu ahead
-/obj/structure/ladder/proc/use(mob/user, is_ghost=FALSE)
- if (!is_ghost && !in_range(src, user))
- return
+ if(!is_ghost)
+ show_final_fluff_message(user, ladder, going_up)
+ // to avoid having players hunt for the pixels of a ladder that goes through several stories and is
+ // partially covered by the sprites of their mobs, a radial menu will be displayed over them.
+ // this way players can keep climbing up or down with ease until they reach an end.
+ if(ladder.up && ladder.down)
+ ladder.show_options(user, is_ghost)
+
+/// The messages shown after the player has finished climbing. Players can see this happen from either src or the destination so we've 2 POVs here
+/obj/structure/ladder/proc/show_final_fluff_message(mob/user, obj/structure/ladder/destination, going_up)
+ var/up_down = going_up ? "up" : "down"
+
+ //POV of players around the source
+ visible_message(span_notice("[user] climbs [up_down] [src]."))
+ //POV of players around the destination
+ user.balloon_alert_to_viewers("climbed [up_down]")
+
+/// Shows a radial menu that players can use to climb up and down a stair.
+/obj/structure/ladder/proc/show_options(mob/user, is_ghost = FALSE)
var/list/tool_list = list()
- if (up)
- tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH)
- if (down)
- tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH)
- if (!length(tool_list))
- to_chat(user, span_warning("[src] doesn't seem to lead anywhere!"))
- return
+ tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH)
+ tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH)
- var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, is_ghost), require_near = !is_ghost, tooltips = TRUE)
- if (!is_ghost && !in_range(src, user))
- return // nice try
+ var/datum/callback/check_menu
+ if(!is_ghost)
+ check_menu = CALLBACK(src, .proc/check_menu, user)
+ var/result = show_radial_menu(user, src, tool_list, custom_check = check_menu, require_near = !is_ghost, tooltips = TRUE)
+
+ var/going_up
switch(result)
if("Up")
- travel(TRUE, user, is_ghost, up)
+ going_up = TRUE
if("Down")
- travel(FALSE, user, is_ghost, down)
+ going_up = FALSE
if("Cancel")
return
- if(!is_ghost)
- add_fingerprint(user)
+ if(is_ghost || !travel_time)
+ travel(user, going_up, is_ghost)
+ else
+ INVOKE_ASYNC(src, .proc/start_travelling, user, going_up)
/obj/structure/ladder/proc/check_menu(mob/user, is_ghost)
- if(user.incapacitated() || (!user.Adjacent(src) && !is_ghost))
+ if(user.incapacitated() || (!user.Adjacent(src)))
return FALSE
return TRUE
@@ -123,39 +176,101 @@
return
use(user)
+/obj/structure/ladder/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+//Not be called when right clicking as a monkey. attack_hand_secondary() handles that.
/obj/structure/ladder/attack_paw(mob/user, list/modifiers)
- return use(user)
+ use(user)
+ return TRUE
/obj/structure/ladder/attack_alien(mob/user, list/modifiers)
- return use(user)
+ use(user)
+ return TRUE
-/obj/structure/ladder/attack_larva(mob/user)
- return use(user)
+/obj/structure/ladder/attack_alien_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/structure/ladder/attack_animal(mob/user)
- return use(user)
+/obj/structure/ladder/attack_larva(mob/user, list/modifiers)
+ use(user)
+ return TRUE
-/obj/structure/ladder/attack_slime(mob/user)
- return use(user)
+/obj/structure/ladder/attack_larva_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/structure/ladder/attackby(obj/item/W, mob/user, params)
- return use(user)
+/obj/structure/ladder/attack_animal(mob/user, list/modifiers)
+ use(user)
+ return TRUE
-/obj/structure/ladder/attack_robot(mob/living/silicon/robot/R)
- if(R.Adjacent(src))
- return use(R)
+/obj/structure/ladder/attack_animal_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/structure/ladder/attack_slime(mob/user, list/modifiers)
+ use(user)
+ return TRUE
+
+/obj/structure/ladder/attack_slime_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/structure/ladder/attackby(obj/item/item, mob/user, params)
+ use(user)
+ return TRUE
+
+/obj/structure/ladder/attackby_secondary(obj/item/item, mob/user, params)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/structure/ladder/attack_robot(mob/living/silicon/robot/user)
+ if(user.Adjacent(src))
+ use(user)
+ return TRUE
+
+/obj/structure/ladder/attack_robot_secondary(mob/living/silicon/robot/user)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || !user.Adjacent(src))
+ return SECONDARY_ATTACK_CONTINUE_CHAIN
+ use(user, going_up = FALSE)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/structure/ladder/attack_ghost(mob/dead/observer/user)
- use(user, TRUE)
+ ghost_use(user)
return ..()
-/obj/structure/ladder/proc/show_fluff_message(going_up, mob/user)
- if(going_up)
- user.visible_message(span_notice("[user] climbs up [src]."), span_notice("You climb up [src]."))
- else
- user.visible_message(span_notice("[user] climbs down [src]."), span_notice("You climb down [src]."))
-
+///Ghosts use the byond default popup menu function on right click, so this is going to work a little differently for them.
+/obj/structure/ladder/proc/ghost_use(mob/user)
+ if (!up && !down)
+ balloon_alert(user, "doesn't lead anywhere!")
+ return
+ if(!up) //only goes down
+ travel(user, going_up = FALSE, is_ghost = TRUE)
+ else if(!down) //only goes up
+ travel(user, going_up = TRUE, is_ghost = TRUE)
+ else //goes both ways
+ show_options(user, is_ghost = TRUE)
// Indestructible away mission ladders which link based on a mapped ID and height value rather than X/Y/Z.
/obj/structure/ladder/unbreakable
diff --git a/code/modules/awaymissions/mission_code/Cabin.dm b/code/modules/awaymissions/mission_code/Cabin.dm
index 560942117a6..de3c1010f7e 100644
--- a/code/modules/awaymissions/mission_code/Cabin.dm
+++ b/code/modules/awaymissions/mission_code/Cabin.dm
@@ -114,11 +114,22 @@
. = ..()
AddElement(/datum/element/update_icon_blocker)
-/obj/structure/ladder/unbreakable/rune/show_fluff_message(up,mob/user)
- user.visible_message(span_notice("[user] activates \the [src]."), span_notice("You activate \the [src]."))
+/obj/structure/ladder/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(up)
+ context[SCREENTIP_CONTEXT_LMB] = "Warp up"
+ if(down)
+ context[SCREENTIP_CONTEXT_RMB] = "Warp down"
+ return CONTEXTUAL_SCREENTIP_SET
-/obj/structure/ladder/unbreakable/rune/use(mob/user, is_ghost=FALSE)
- if(is_ghost || !IS_WIZARD(user))
+/obj/structure/ladder/unbreakable/rune/show_initial_fluff_message(mob/user, going_up)
+ user.balloon_alert_to_viewers("activating...")
+
+/obj/structure/ladder/unbreakable/rune/show_final_fluff_message(mob/user, going_up)
+ visible_message(span_notice("[user] activates [src] and teleports away."))
+ user.balloon_alert_to_viewers("warped in")
+
+/obj/structure/ladder/unbreakable/rune/use(mob/user, going_up = TRUE)
+ if(!IS_WIZARD(user))
..()
/*Cabin's forest. Removed in the new cabin map since it was buggy and I prefer manual placement.*/
diff --git a/code/modules/awaymissions/signpost.dm b/code/modules/awaymissions/signpost.dm
index ea0d22ac39f..64aaca7aa99 100644
--- a/code/modules/awaymissions/signpost.dm
+++ b/code/modules/awaymissions/signpost.dm
@@ -39,14 +39,14 @@
/obj/structure/signpost/attack_hulk(mob/user)
return
-/obj/structure/signpost/attack_larva(mob/user)
+/obj/structure/signpost/attack_larva(mob/user, list/modifiers)
return interact(user)
/obj/structure/signpost/attack_robot(mob/user)
if (Adjacent(user))
return interact(user)
-/obj/structure/signpost/attack_slime(mob/user)
+/obj/structure/signpost/attack_slime(mob/user, list/modifiers)
return interact(user)
/obj/structure/signpost/attack_animal(mob/user, list/modifiers)
diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm
index 83c85808256..fa29e662bc3 100644
--- a/code/modules/awaymissions/super_secret_room.dm
+++ b/code/modules/awaymissions/super_secret_room.dm
@@ -99,13 +99,13 @@
/obj/structure/speaking_tile/attack_hulk(mob/user)
return
-/obj/structure/speaking_tile/attack_larva(mob/user)
+/obj/structure/speaking_tile/attack_larva(mob/user, list/modifiers)
return interact(user)
/obj/structure/speaking_tile/attack_ai(mob/user)
return interact(user)
-/obj/structure/speaking_tile/attack_slime(mob/user)
+/obj/structure/speaking_tile/attack_slime(mob/user, list/modifiers)
return interact(user)
/obj/structure/speaking_tile/attack_animal(mob/user, list/modifiers)
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
index c9d36928a65..383d78d76d5 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
@@ -307,10 +307,10 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
/turf/closed/indestructible/hoteldoor/attack_hulk(mob/living/carbon/human/user)
promptExit(user)
-/turf/closed/indestructible/hoteldoor/attack_larva(mob/user)
+/turf/closed/indestructible/hoteldoor/attack_larva(mob/user, list/modifiers)
promptExit(user)
-/turf/closed/indestructible/hoteldoor/attack_slime(mob/user)
+/turf/closed/indestructible/hoteldoor/attack_slime(mob/user, list/modifiers)
promptExit(user)
/turf/closed/indestructible/hoteldoor/attack_robot(mob/user)
diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm
index 06993b1d1ea..16bf15d46fd 100644
--- a/code/modules/mob/living/basic/basic_defense.dm
+++ b/code/modules/mob/living/basic/basic_defense.dm
@@ -87,7 +87,7 @@
log_combat(user, src, "attacked")
return 1
-/mob/living/basic/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/basic/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
. = ..()
if(. && stat != DEAD) //successful larva bite
var/damage = rand(5, 10)
@@ -107,7 +107,7 @@
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
return attack_threshold_check(damage, user.melee_damage_type)
-/mob/living/basic/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/basic/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(15, 25)
if(M.is_adult)
@@ -119,6 +119,11 @@
return
return ..()
+/mob/living/basic/attack_drone_secondary(mob/living/simple_animal/drone/M)
+ if(M.combat_mode)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
/mob/living/basic/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = MELEE, actuallydamage = TRUE)
var/temp_damage = damage
if(!damage_coeff[damagetype])
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 2b2ede04d64..fe613aaaa63 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -43,7 +43,7 @@ In all, this is a lot like the monkey code. /N
-/mob/living/carbon/alien/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/carbon/alien/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
return attack_alien(L)
@@ -91,7 +91,7 @@ In all, this is a lot like the monkey code. /N
if(STAMINA)
adjustStaminaLoss(damage)
-/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(5, 35)
if(M.is_adult)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index f1270a9f3e9..48b527a4b8c 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -144,6 +144,9 @@
/mob/living/carbon/attack_drone(mob/living/simple_animal/drone/user)
return //so we don't call the carbon's attack_hand().
+/mob/living/carbon/attack_drone_secondary(mob/living/simple_animal/drone/user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/carbon/attack_hand(mob/living/carbon/human/user, list/modifiers)
@@ -199,7 +202,7 @@
return TRUE
-/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
if(M.powerlevel > 0)
var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 3005e0dc511..3cecdb6350c 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -321,7 +321,7 @@
-/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
. = ..()
if(!.)
return //successful larva bite.
@@ -375,7 +375,7 @@
apply_damage(damage, user.melee_damage_type, affecting, armor, wound_bonus = user.wound_bonus, bare_wound_bonus = user.bare_wound_bonus, sharpness = user.sharpness, attack_direction = attack_direction)
-/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
. = ..()
if(!.) // slime attack failed
return
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index e080475e1af..6d99a50b09a 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -208,7 +208,7 @@
return TRUE
-/mob/living/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(!SSticker.HasRoundStarted())
to_chat(M, "You cannot attack people before the game has started.")
return
@@ -315,7 +315,7 @@
return FALSE
-/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
if(L.combat_mode)
if(HAS_TRAIT(L, TRAIT_PACIFISM))
to_chat(L, span_warning("You don't want to hurt anyone!"))
diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm
index 17e894ccc9f..5124f5c221e 100644
--- a/code/modules/mob/living/silicon/ai/ai_defense.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defense.dm
@@ -17,7 +17,7 @@
return
..()
-/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user)
+/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
return //immune to slimes
/mob/living/silicon/ai/blob_act(obj/structure/blob/B)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 721ab6422f7..f24136d4757 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -204,7 +204,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
..()
return
-/mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime shock
flash_act()
var/stunprob = M.powerlevel * 7 + 10
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 7370930b811..587d3410cb3 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -45,7 +45,7 @@
/mob/living/silicon/attack_paw(mob/living/user, list/modifiers)
return attack_hand(user, modifiers)
-/mob/living/silicon/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/silicon/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
if(!L.combat_mode)
visible_message(span_notice("[L.name] rubs its head against [src]."))
@@ -85,6 +85,11 @@
return
return ..()
+/mob/living/silicon/attack_drone_secondary(mob/living/simple_animal/drone/M)
+ if(M.combat_mode)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
/mob/living/silicon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
if(buckled_mobs)
for(var/mob/living/M in buckled_mobs)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 3610bc8633a..7301344cc71 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -84,7 +84,7 @@
log_combat(user, src, "attacked")
return 1
-/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
. = ..()
if(. && stat != DEAD) //successful larva bite
var/damage = rand(5, 10)
@@ -104,7 +104,7 @@
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
return attack_threshold_check(damage, user.melee_damage_type)
-/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(15, 25)
if(M.is_adult)
@@ -116,6 +116,11 @@
return
return ..()
+/mob/living/simple_animal/attack_drone_secondary(mob/living/simple_animal/drone/M)
+ if(M.combat_mode)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ return ..()
+
/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = MELEE, actuallydamage = TRUE)
var/temp_damage = damage
if(!damage_coeff[damagetype])
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
index 305c221f319..effd7faa3b0 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
@@ -28,6 +28,9 @@
else
to_chat(drone, span_warning("You need to remain still to cannibalize [src]!"))
+/mob/living/simple_animal/drone/attack_drone_secondary(mob/living/simple_animal/drone/drone)
+ return SECONDARY_ATTACK_CALL_NORMAL
+
//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/simple_animal/drone/attack_hand(mob/user, list/modifiers)
if(ishuman(user))
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 6e4c0cf8539..754e5e73d15 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -277,7 +277,7 @@
/mob/living/simple_animal/slime/attack_ui(slot, params)
return
-/mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/M)
+/mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
if(M == src)
return
@@ -304,7 +304,7 @@
if(..()) //successful monkey bite.
attacked += 10
-/mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L)
+/mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
if(..()) //successful larva bite.
attacked += 10