diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index d77e62978c4..fd919cc648a 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -25,6 +25,10 @@ #define INCORPOREAL_SHADE 4 // Shady #define INCORPOREAL_MECH 5 // stripped down bstech +#define MOB_GRAB_NONE 0 +#define MOB_GRAB_NORMAL 1 +#define MOB_GRAB_FIREMAN 2 + // Grab levels. #define GRAB_PASSIVE 1 #define GRAB_AGGRESSIVE 2 diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index a739205e43e..d9e00701600 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -53,16 +53,25 @@ if(does_teleport) teleport(user) -/obj/effect/portal/proc/teleport(atom/movable/M) +/obj/effect/portal/proc/teleport(atom/movable/movable) if(!does_teleport) // just to be safe return - if(istype(M, /obj/effect)) //sparks don't teleport + if(istype(movable, /obj/effect)) //sparks don't teleport return if(!target) qdel(src) return var/has_teleported = FALSE - if(istype(M, /atom/movable)) + var/list/things_to_teleport = list(movable) + if(ismob(movable)) + var/mob/M = movable + if(M.pulling) + things_to_teleport += M.pulling + if(ishuman(movable)) + var/mob/living/carbon/human/H = movable + for(var/obj/item/grab/G in list(H.l_hand, H.r_hand)) + things_to_teleport += G.affecting + for(var/atom/movable/M in things_to_teleport) if(has_failed) //oh dear a problem, put em in deep space icon_state = "portal1" // only tell people the portal failed after a teleport has been done desc = "A bluespace tear in space, reaching directly to another point within this region. Definitely unstable." @@ -72,7 +81,7 @@ if(do_teleport(M, target, precision)) has_teleported = TRUE if(!has_teleported) - visible_message(SPAN_WARNING("\The [src] oscillates violently as \the [M] comes into contact with it, and collapses! Seems like the rift was unstable...")) + visible_message(SPAN_WARNING("\The [src] oscillates violently as \the [movable] comes into contact with it, and collapses! Seems like the rift was unstable...")) qdel(src) /obj/effect/portal/Destroy() diff --git a/code/modules/mob/living/carbon/alien/alien_attacks.dm b/code/modules/mob/living/carbon/alien/alien_attacks.dm index afd2ec36093..26cf988661a 100644 --- a/code/modules/mob/living/carbon/alien/alien_attacks.dm +++ b/code/modules/mob/living/carbon/alien/alien_attacks.dm @@ -15,7 +15,7 @@ if (I_GRAB) if (M == src) return - var/obj/item/grab/G = new /obj/item/grab( M, src ) + var/obj/item/grab/G = new /obj/item/grab(M, M, src) M.put_in_active_hand(G) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 330d569e22d..961461b515b 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -167,7 +167,7 @@ /mob/living/carbon/swap_hand() var/obj/item/item_in_hand = src.get_active_hand() if(item_in_hand) //this segment checks if the item in your hand is twohanded. - if(istype(item_in_hand,/obj/item/material/twohanded) || istype(item_in_hand,/obj/item/gun) || istype(item_in_hand,/obj/item/pickaxe)) + if(istype(item_in_hand,/obj/item/material/twohanded) || istype(item_in_hand,/obj/item/gun) || istype(item_in_hand,/obj/item/pickaxe) || istype(item_in_hand, /obj/item/grab)) if(item_in_hand:wielded == 1) to_chat(usr, SPAN_WARNING("Your other hand is too busy holding the [item_in_hand.name]")) return diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 372f300708d..d8d0097e440 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -134,7 +134,7 @@ if(w_uniform) w_uniform.add_fingerprint(M) - var/obj/item/grab/G = new /obj/item/grab(M, src) + var/obj/item/grab/G = new /obj/item/grab(M, M, src) if(buckled_to) to_chat(M, "You cannot grab [src], [src.get_pronoun("he")] [get_pronoun("is")] buckled in!") if(!G) //the grab will delete itself in New if affecting is anchored diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index b82713acccf..a90545ae5cd 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -558,7 +558,7 @@ emp_act if(src.w_uniform) src.w_uniform.add_fingerprint(src) - var/obj/item/grab/G = new /obj/item/grab(user, src) + var/obj/item/grab/G = new /obj/item/grab(user, user, src) if(buckled_to) to_chat(user, "You cannot grab [src], [get_pronoun("he")] [get_pronoun("is")] buckled in!") if(!G) //the grab will delete itself in New if affecting is anchored diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm index 0a68226782b..8bc6ab6b45f 100644 --- a/code/modules/mob/living/carbon/slime/slime.dm +++ b/code/modules/mob/living/carbon/slime/slime.dm @@ -338,7 +338,7 @@ if(I_GRAB) if(M == src || anchored) return - var/obj/item/grab/G = new /obj/item/grab(M, src) + var/obj/item/grab/G = new /obj/item/grab(M, M, src) M.put_in_active_hand(G) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index dd8c4173166..4401daa8f85 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -481,7 +481,7 @@ if (!attempt_grab(M)) return - var/obj/item/grab/G = new /obj/item/grab(M, src) + var/obj/item/grab/G = new /obj/item/grab(M, M, src) M.put_in_active_hand(G) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index a80159f0dc4..657a14b4ad7 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -936,11 +936,12 @@ else if(buckled_to) anchored = 1 canmove = 0 - if(buckled_to.buckle_lying != -1) - lying = buckled_to.buckle_lying - if(buckled_to.buckle_movable) - anchored = 0 - canmove = 1 + if(isobj(buckled_to)) + if(buckled_to.buckle_lying != -1) + lying = buckled_to.buckle_lying + if(buckled_to.buckle_movable) + anchored = 0 + canmove = 1 else if(captured) anchored = 1 canmove = 0 @@ -957,6 +958,10 @@ density = initial(density) for(var/obj/item/grab/G in grabbed_by) + if(G.wielded) + canmove = FALSE + lying = TRUE + break if(G.state >= GRAB_AGGRESSIVE) canmove = 0 break diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 866bc2af492..59dac40bcd4 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -42,6 +42,9 @@ var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position var/has_choked = FALSE //Used as a counter for choking people. + var/obj/item/grab/linked_grab + var/wielded = FALSE + layer = SCREEN_LAYER abstract = 1 item_state = "nothing" @@ -51,15 +54,13 @@ pickup_sound = null equip_sound = null -/obj/item/grab/New(mob/user, mob/victim) - ..() - loc = user +/obj/item/grab/Initialize(mapload, mob/user, mob/victim) + . = ..() assailant = user affecting = victim if(affecting.anchored || !assailant.Adjacent(victim)) - qdel(src) - return + return INITIALIZE_HINT_QDEL affecting.grabbed_by += src @@ -81,7 +82,7 @@ //Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. /obj/item/grab/proc/throw_held() if(affecting) - if(affecting.buckled_to) + if(affecting.buckled_to && affecting.buckled_to != assailant) // can't throw in fireman carries atm, but future proofing return null if(state >= GRAB_AGGRESSIVE) animate(affecting, pixel_x = affecting.get_standard_pixel_x(), pixel_y = affecting.get_standard_pixel_y(), 4, 1) @@ -203,13 +204,20 @@ /obj/item/grab/proc/adjust_position() if(!affecting) return - if(affecting.buckled_to) + if(affecting.buckled_to && affecting.buckled_to != assailant) animate(affecting, pixel_x = affecting.get_standard_pixel_x(), pixel_y = affecting.get_standard_pixel_y(), 4, 1, LINEAR_EASING) return - if(affecting.lying || force_down) + if(affecting.lying || force_down || wielded) affecting.update_canmove() - animate(affecting, pixel_x = affecting.get_standard_pixel_x(), pixel_y = affecting.get_standard_pixel_y(), 5, 1, LINEAR_EASING) - affecting.set_dir(SOUTH) + var/shift_amount = wielded ? 6 : 0 + animate(affecting, pixel_x = affecting.get_standard_pixel_x(), pixel_y = affecting.get_standard_pixel_y() + shift_amount, 5, 1, LINEAR_EASING) + var/set_dir = wielded ? assailant.dir : SOUTH + affecting.set_dir(set_dir) + if(wielded) + if(assailant.dir != NORTH) + affecting.layer = assailant.layer - 0.1 + else + affecting.layer = assailant.layer + 0.1 return var/shift = 0 var/adir = get_dir(assailant, affecting) @@ -251,6 +259,8 @@ return if(state == GRAB_UPGRADING) return + if(wielded) + return if(!assailant.canClick()) return if(!assailant.canmove || assailant.lying) @@ -400,8 +410,18 @@ var/destroying = 0 /obj/item/grab/Destroy() + if(!QDELETED(linked_grab)) + qdel(linked_grab) + + if(wielded) + if(affecting.buckled_to == assailant) + affecting.buckled_to = null + affecting.update_canmove() + affecting.anchored = FALSE + moved_event.unregister(assailant, src, /obj/item/grab/proc/move_affecting) + animate(affecting, pixel_x = affecting.get_standard_pixel_x(), pixel_y = affecting.get_standard_pixel_y(), 4, 1, LINEAR_EASING) - affecting.layer = 4 + affecting.layer = initial(affecting.layer) if(affecting) ADD_FALLING_ATOM(affecting) // Makes the grabbee check if they can fall. affecting.grabbed_by -= src @@ -414,3 +434,68 @@ hud = null destroying = 1 // stops us calling qdel(src) on dropped() return ..() + +/obj/item/grab/MouseDrop(mob/living/carbon/human/H) + if(wielded || affecting.buckled_to || !istype(H) || assailant != H || H.get_active_hand() != src) + return + if(!ishuman(affecting)) + to_chat(H, SPAN_WARNING("You can only fireman carry humanoids!")) + return + var/mob/living/carbon/human/affected_human = affecting + if(affected_human.species.mob_size > 25) + to_chat(H, SPAN_WARNING("\The [affected_human] is way too big to fireman carry!")) + return + if(state < GRAB_AGGRESSIVE) + to_chat(H, SPAN_WARNING("You need an aggressive grab before you can fireman carry someone!")) + return + if(H.get_inactive_hand()) + to_chat(H, SPAN_WARNING("Your other hand must be empty to fireman carry someone!")) + return + + H.visible_message("[H] starts lifting \the [affecting] onto their shoulders...", SPAN_NOTICE("You start lifting \the [affecting] onto your shoulders...")) + + if(!do_after(H, 3 SECONDS, TRUE)) + return + + if(affecting.buckled_to) + return + + if(H.get_inactive_hand()) + to_chat(H, SPAN_WARNING("Your other hand must be empty to fireman carry someone!")) + return + + var/obj/item/grab/offhand/OH = new /obj/item/grab/offhand(H, H, affecting, src) + H.put_in_hands(OH) + + H.visible_message("[H] lifts \the [affecting] onto their shoulders!", SPAN_NOTICE("You lift \the [affecting] onto your shoulders!")) + + affecting.buckled_to = assailant + affecting.forceMove(H.loc) + adjust_position() + moved_event.register(assailant, src, /obj/item/grab/proc/move_affecting) + +/obj/item/grab/proc/move_affecting() + if(affecting && assailant.Adjacent(affecting)) // Only move if it's near us. + affecting.forceMove(assailant.loc) + +/obj/item/grab/offhand + icon_state = "!reinforce" + +/obj/item/grab/offhand/Initialize(mapload, mob/user, mob/victim, var/obj/item/grab/linked) + . = ..() + linked_grab = linked + linked.linked_grab = src + linked_grab.wielded = TRUE + + linked_grab.state = GRAB_AGGRESSIVE + + icon_state = "!reinforce" + hud.icon_state = "!reinforce" + linked_grab.icon_state = "!reinforce" + linked_grab.hud.icon_state = "!reinforce" + +/obj/item/grab/offhand/process() + return + +/obj/item/grab/offhand/adjust_position() + return \ No newline at end of file diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 75e5de49ffa..3c5363d43ae 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1230,4 +1230,19 @@ proc/is_blind(A) /mob/proc/can_hear_radio(var/list/speaker_coverage = list()) var/turf/ear = get_turf(src) if(ear && speaker_coverage[ear]) - return TRUE \ No newline at end of file + return TRUE + +/mob/proc/has_grab() + . = MOB_GRAB_NONE + if(istype(l_hand, /obj/item/grab)) + var/obj/item/grab/l_grab = l_hand + if(l_grab.wielded) + . = max(MOB_GRAB_FIREMAN, .) + else + . = max(MOB_GRAB_NORMAL, .) + if(istype(r_hand, /obj/item/grab)) + var/obj/item/grab/r_grab = r_hand + if(r_grab.wielded) + . = max(MOB_GRAB_FIREMAN, .) + else + . = max(MOB_GRAB_NORMAL, .) \ No newline at end of file diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index fa9966809f8..6ce8cb54123 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -334,7 +334,10 @@ //We are now going to move moving = 1 //Something with pulling things - if (mob_is_human && (istype(mob:l_hand, /obj/item/grab) || istype(mob:r_hand, /obj/item/grab))) + var/grab_level = mob.has_grab() + if(grab_level == MOB_GRAB_FIREMAN) + move_delay++ + if (mob_is_human && grab_level == MOB_GRAB_NORMAL) move_delay = max(move_delay, world.time + 7) var/list/L = mob.ret_grab() if(istype(L, /list)) diff --git a/html/changelogs/geeves-fireman_carrying.yml b/html/changelogs/geeves-fireman_carrying.yml new file mode 100644 index 00000000000..06fc229bede --- /dev/null +++ b/html/changelogs/geeves-fireman_carrying.yml @@ -0,0 +1,7 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "It is now possible to fireman carry someone, provided you have an aggressive grab or higher on them. Simply drag the grab in your active hand onto your sprite and wait for a bit. To release, drop the grab." + - rscadd: "Pulling or grabbing someone and dragging them into a portal will now teleport it/them with you." \ No newline at end of file