diff --git a/code/datums/riding.dm b/code/datums/riding.dm index 27911b88dfb..20bbc1552ad 100644 --- a/code/datums/riding.dm +++ b/code/datums/riding.dm @@ -15,6 +15,17 @@ else ridden.layer = OBJ_LAYER +/datum/riding/proc/on_vehicle_move() + for(var/mob/living/M in ridden.buckled_mobs) + ride_check(M) + handle_vehicle_offsets() + handle_vehicle_layer() + +/datum/riding/proc/ride_check(mob/living/M) + return TRUE + +/datum/riding/proc/force_dismount(mob/living/M) + ridden.unbuckle_mob(M) //Override this to set your vehicle's various pixel offsets //if they differ between directions, otherwise use the @@ -36,7 +47,6 @@ return TRUE return FALSE - //BUCKLE HOOKS /datum/riding/proc/restore_position(mob/living/buckled_mob) if(istype(buckled_mob)) @@ -45,11 +55,6 @@ if(buckled_mob.client) buckled_mob.client.change_view(world.view) - - - - - //MOVEMENT /datum/riding/proc/handle_ride(mob/user, direction) if(user.incapacitated()) @@ -298,12 +303,59 @@ else user << "You'll need something to guide the [ridden.name]." -//CYBORGS. NO, THEY ARE NOT ANIMALS. +///////Humans. Yes, I said humans. No, this won't end well...////////// +/datum/riding/human + keytype = null + +/datum/riding/human/ride_check(mob/living/M) + var/mob/living/carbon/human/H = ridden //IF this runtimes I'm blaming the admins. + if(M.incapacitated(FALSE, TRUE) || H.incapacitated(FALSE, TRUE)) + M.visible_message("[M] falls off of [ridden]!") + ridden.unbuckle_mob(M) + return FALSE + if(M.restrained(TRUE)) + M.visible_message("[M] can't hang onto [ridden] with their hands cuffed!") //Honestly this should put the ridden mob in a chokehold. + ridden.unbuckle_mob(M) + return FALSE + if(H.pulling == M) + H.stop_pulling() + +/datum/riding/human/handle_vehicle_offsets() + for(var/mob/living/M in ridden.buckled_mobs) + M.setDir(ridden.dir) + switch(ridden.dir) + if(NORTH) + M.pixel_x = 0 + M.pixel_y = 6 + if(SOUTH) + M.pixel_x = 0 + M.pixel_y = 6 + if(EAST) + M.pixel_x = -6 + M.pixel_y = 4 + if(WEST) + M.pixel_x = 6 + M.pixel_y = 4 + +/datum/riding/human/handle_vehicle_layer() + if(ridden.buckled_mobs && ridden.buckled_mobs.len) + if(ridden.dir == SOUTH) + ridden.layer = ABOVE_MOB_LAYER + else + ridden.layer = OBJ_LAYER + else + ridden.layer = MOB_LAYER + +/datum/riding/human/force_dismount(mob/living/user) + ridden.unbuckle_mob(user) + user.Weaken(3) + user.Stun(3) + user.visible_message("[ridden] pushes [user] off of them!") + /datum/riding/cyborg keytype = null - vehicle_move_delay = 1 -/datum/riding/cyborg/proc/ride_check(mob/user) +/datum/riding/cyborg/ride_check(mob/user) if(user.incapacitated()) var/kick = TRUE if(istype(ridden, /mob/living/silicon/robot)) @@ -322,11 +374,12 @@ return /datum/riding/cyborg/handle_vehicle_layer() - if(ridden.dir == SOUTH) - ridden.layer = ABOVE_MOB_LAYER + if(ridden.buckled_mobs && ridden.buckled_mobs.len) + if(ridden.dir == SOUTH) + ridden.layer = ABOVE_MOB_LAYER + else + ridden.layer = OBJ_LAYER else - ridden.layer = OBJ_LAYER - if(!ridden.buckled_mobs) ridden.layer = MOB_LAYER /datum/riding/cyborg/handle_vehicle_offsets() @@ -353,19 +406,65 @@ M.pixel_x = 6 M.pixel_y = 3 -/datum/riding/cyborg/proc/on_vehicle_move() - for(var/mob/living/M in ridden.buckled_mobs) - ride_check(M) - handle_vehicle_offsets() - handle_vehicle_layer() +/datum/riding/cyborg/force_dismount(mob/living/M) + ridden.unbuckle_mob(M) + var/turf/target = get_edge_target_turf(ridden, ridden.dir) + var/turf/targetm = get_step(get_turf(ridden), ridden.dir) + M.Move(targetm) + M.visible_message("[M] is thrown clear of [ridden]!") + M.throw_at(target, 14, 5, ridden) + M.Weaken(3) -/datum/riding/cyborg/proc/force_dismount() - for(var/mob/living/M in ridden.buckled_mobs) - ridden.unbuckle_mob(M) - var/turf/target = get_edge_target_turf(ridden, ridden.dir) - var/turf/targetm = get_step(get_turf(ridden), ridden.dir) - M.Move(targetm) - M.visible_message("[M] is thrown clear of [ridden] by rapid spinning!") - M.throw_at(target, 14, 5, ridden) - M.Weaken(3) +/datum/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1) + var/amount_equipped = 0 + for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--) + var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user) + inhand.rider = user + inhand.ridden = ridden + if(user.put_in_hands(inhand, TRUE)) + amount_equipped++ + else + break + if(amount_equipped >= amount_required) + return TRUE + else + unequip_buckle_inhands(user) + return FALSE +/datum/riding/proc/unequip_buckle_inhands(mob/living/carbon/user) + for(var/obj/item/riding_offhand/O in user.contents) + if(O.ridden != ridden) + CRASH("RIDING OFFHAND ON WRONG MOB") + continue + if(O.selfdeleting) + continue + else + qdel(O) + return TRUE + +/obj/item/riding_offhand + name = "offhand" + icon = 'icons/obj/weapons.dmi' + icon_state = "offhand" + w_class = WEIGHT_CLASS_HUGE + flags = ABSTRACT | DROPDEL | NOBLUDGEON + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/mob/living/carbon/rider + var/mob/living/ridden + var/selfdeleting = FALSE + +/obj/item/riding_offhand/dropped() + selfdeleting = TRUE + . = ..() + +/obj/item/riding_offhand/equipped() + if(loc != rider) + selfdeleting = TRUE + qdel(src) + . = ..() + +/obj/item/riding_offhand/Destroy() + if(selfdeleting) + if(rider in ridden.buckled_mobs) + ridden.unbuckle_mob(rider) + . = ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 0ff51c45cf7..b0eac013cd8 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -895,3 +895,39 @@ .["Make alien"] = "?_src_=vars;makealien=\ref[src]" .["Make slime"] = "?_src_=vars;makeslime=\ref[src]" .["Toggle Purrbation"] = "?_src_=vars;purrbation=\ref[src]" + +/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) + if((target != pulling) || (grab_state < GRAB_AGGRESSIVE) || (user != target)) //Get consent first :^) + . = ..() + return + buckle_mob(target, FALSE, TRUE, TRUE) + . = ..() + +/mob/living/carbon/human/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE, yes = FALSE) + if(!yes) + return + if(!is_type_in_typecache(M, can_ride_typecache)) + M.visible_message("[M] really can't seem to mount [src]...") + return + if(!riding_datum) + riding_datum = new /datum/riding/human + riding_datum.ridden = src + if(buckled_mobs && ((M in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs))) + return + if(buckled) //NO INFINITE STACKING!! + return + if(M.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) + M.visible_message("[M] can't hang onto [src]!") + return + if(iscarbon(M) && (!riding_datum.equip_buckle_inhands(M, 2))) //MAKE SURE THIS IS LAST!! + M.visible_message("[M] can't climb onto [src] because [M.p_their()] hands are full!") + return + . = ..(M, force, check_loc) + stop_pulling() + +/mob/living/carbon/human/unbuckle_mob(mob/living/M) + if(iscarbon(M)) + if(riding_datum) + riding_datum.unequip_buckle_inhands(M) + riding_datum.restore_position(M) + . = ..(M) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 2cc4c859920..16c2ae46bc4 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -201,6 +201,9 @@ /mob/living/carbon/human/attack_hand(mob/living/carbon/human/M) if(..()) //to allow surgery to return properly. return + if(M.a_intent == INTENT_DISARM) + if(M.buckled_mobs && (src in M.buckled_mobs) && M.riding_datum) + M.riding_datum.force_dismount(src) dna.species.spec_attack_hand(M, src) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 6bd8d9cef98..d712ce9b6a9 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -50,3 +50,6 @@ var/global/default_martial_art = new/datum/martial_art var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects var/datum/personal_crafting/handcrafting + can_buckle = TRUE + buckle_lying = FALSE + can_ride_typecache = list(/mob/living/carbon/human) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index ed5828dfa4b..7abcefe143e 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -55,7 +55,8 @@ //End bloody footprints S.step_action() - + if(buckled_mobs && buckled_mobs.len && riding_datum) + riding_datum.on_vehicle_move() /mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee. if(..()) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index ddcd73cbb88..2b0e14350c8 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -462,6 +462,10 @@ user.spin(20, 1) if(istype(user, /mob/living/silicon/robot)) var/mob/living/silicon/robot/R = user - if(R.riding_datum) - R.riding_datum.force_dismount() + if(R.buckled_mobs) + for(var/mob/M in R.buckled_mobs) + if(R.riding_datum) + R.riding_datum.force_dismount(M) + else + R.unbuckle_all_mobs() ..() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 224222169df..fb06982843b 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -91,7 +91,7 @@ can_buckle = TRUE buckle_lying = FALSE - var/datum/riding/cyborg/riding_datum = null + can_ride_typecache = list(/mob/living/carbon/human) /mob/living/silicon/robot/New(loc) spark_system = new /datum/effect_system/spark_spread() @@ -1006,6 +1006,9 @@ buckle_mob(M) /mob/living/silicon/robot/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) + if(!is_type_in_typecache(M, can_ride_typecache)) + M.visible_message("[M] really can't seem to mount the [src]...") + return if(!riding_datum) riding_datum = new /datum/riding/cyborg riding_datum.ridden = src @@ -1020,69 +1023,18 @@ return if(M.restrained()) return - if(iscyborg(M)) - M.visible_message("[M] really can't seem to mount the [src]...") - return - if(isbot(M)) - M.visible_message("No. Just... no.") - return if(module) if(!module.allow_riding) M.visible_message("Unfortunately, [M] just can't seem to hold onto [src]!") return - if(iscarbon(M)) - if(!equip_buckle_inhands(M)) //MAKE SURE THIS IS LAST! - M.visible_message("[M] can't climb onto [src] because his hands are full!") - return + if(iscarbon(M) && (!riding_datum.equip_buckle_inhands(M, 1))) + M.visible_message("[M] can't climb onto [src] because his hands are full!") + return . = ..(M, force, check_loc) - riding_datum.handle_vehicle_offsets() /mob/living/silicon/robot/unbuckle_mob(mob/user) if(iscarbon(user)) - unequip_buckle_inhands(user) + if(riding_datum) + riding_datum.unequip_buckle_inhands(user) + riding_datum.restore_position(user) . = ..(user) - riding_datum.restore_position(user) - -/mob/living/silicon/robot/proc/unequip_buckle_inhands(mob/living/carbon/user) - for(var/obj/item/cyborgride_offhand/O in user.contents) - if(O.ridden != src) - CRASH("RIDING OFFHAND ON WRONG MOB") - continue - if(O.selfdeleting) - continue - else - qdel(O) - return TRUE - -/mob/living/silicon/robot/proc/equip_buckle_inhands(mob/living/carbon/user) - var/obj/item/cyborgride_offhand/inhand = new /obj/item/cyborgride_offhand(user) - inhand.rider = user - inhand.ridden = src - return user.put_in_hands(inhand, TRUE) - -/obj/item/cyborgride_offhand - name = "offhand" - icon = 'icons/obj/weapons.dmi' - icon_state = "offhand" - w_class = WEIGHT_CLASS_HUGE - flags = ABSTRACT | DROPDEL | NOBLUDGEON - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/mob/living/carbon/rider - var/mob/living/silicon/robot/ridden - var/selfdeleting = FALSE - -/obj/item/cyborgride_offhand/dropped() - selfdeleting = TRUE - . = ..() - -/obj/item/cyborgride_offhand/equipped() - if(loc != rider) - selfdeleting = TRUE - qdel(src) - . = ..() - -/obj/item/cyborgride_offhand/Destroy() - if(selfdeleting) - if(rider in ridden.buckled_mobs) - ridden.unbuckle_mob(rider) - . = ..() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index b2772c7312f..e9a26713e2f 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -84,7 +84,6 @@ //domestication var/tame = 0 - var/datum/riding/riding_datum = null /mob/living/simple_animal/New() ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c75ca28534b..1384ebe5d7d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -28,6 +28,7 @@ var/next_mob_id = 0 else living_mob_list += src prepare_huds() + can_ride_typecache = typecacheof(can_ride_typecache) ..() /atom/proc/prepare_huds() @@ -944,4 +945,8 @@ var/next_mob_id = 0 switch(var_name) if ("attack_log") return debug_variable(var_name, attack_log, 0, src, FALSE) - . = ..() \ No newline at end of file + . = ..() + +/mob/post_buckle_mob(mob/living/M) + riding_datum.handle_vehicle_offsets() + riding_datum.handle_vehicle_layer() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index e6ce7a37443..db64420414e 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -145,4 +145,6 @@ var/list/observers = null //The list of people observing this mob. - var/list/progressbars = null //for stacking do_after bars \ No newline at end of file + var/list/progressbars = null //for stacking do_after bars + var/datum/riding/riding_datum + var/list/can_ride_typecache = list() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 3e6cb63df32..0b8c488ccd8 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -1,435 +1,435 @@ -/mob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover, /obj/item/projectile) || mover.throwing) - return (!density || lying) - if(mover.checkpass(PASSMOB)) - return 1 - if(buckled == mover) - return 1 - if(ismob(mover)) - if (mover in buckled_mobs) - return 1 - return (!mover.density || !density || lying) - - -//The byond version of these verbs wait for the next tick before acting. -// instant verbs however can run mid tick or even during the time between ticks. -/client/verb/moveup() - set name = ".moveup" - set instant = 1 - Move(get_step(mob, NORTH), NORTH) - -/client/verb/movedown() - set name = ".movedown" - set instant = 1 - Move(get_step(mob, SOUTH), SOUTH) - -/client/verb/moveright() - set name = ".moveright" - set instant = 1 - Move(get_step(mob, EAST), EAST) - -/client/verb/moveleft() - set name = ".moveleft" - set instant = 1 - Move(get_step(mob, WEST), WEST) - -/client/Northeast() - swap_hand() - return - - -/client/Southeast() - attack_self() - return - - -/client/Southwest() - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.toggle_throw_mode() - else - usr << "This mob type cannot throw items." - return - - -/client/Northwest() - if(!usr.get_active_held_item()) - usr << "You have nothing to drop in your hand!" - return - usr.drop_item() - -//This gets called when you press the delete button. -/client/verb/delete_key_pressed() - set hidden = 1 - - if(!usr.pulling) - usr << "You are not pulling anything." - return - usr.stop_pulling() - -/client/verb/swap_hand() - set category = "IC" - set name = "Swap hands" - - if(mob) - mob.swap_hand() - -/client/verb/attack_self() - set hidden = 1 - if(mob) - mob.mode() - return - - -/client/verb/drop_item() - set hidden = 1 - if(!iscyborg(mob)) - mob.drop_item_v() - return - - -/client/Center() - if(isobj(mob.loc)) - var/obj/O = mob.loc - if(mob.canmove) - return O.relaymove(mob, 0) - return - - -/client/proc/Move_object(direct) - if(mob && mob.control_object) - if(mob.control_object.density) - step(mob.control_object,direct) - if(!mob.control_object) - return - mob.control_object.setDir(direct) - else - mob.control_object.loc = get_step(mob.control_object,direct) - return - - -/client/Move(n, direct) - if(world.time < move_delay) - return 0 - move_delay = world.time+world.tick_lag //this is here because Move() can now be called mutiple times per tick - if(!mob || !mob.loc) - return 0 - if(mob.notransform) - return 0 //This is sota the goto stop mobs from moving var - if(mob.control_object) - return Move_object(direct) - if(!isliving(mob)) - return mob.Move(n,direct) - if(mob.stat == DEAD) - mob.ghostize() - return 0 - if(moving) - return 0 - if(mob.force_moving) - return 0 - if(isliving(mob)) - var/mob/living/L = mob - if(L.incorporeal_move) //Move though walls - Process_Incorpmove(direct) - return 0 - - if(mob.remote_control) //we're controlling something, our movement is relayed to it - return mob.remote_control.relaymove(mob, direct) - - if(isAI(mob)) - return AIMove(n,direct,mob) - - if(Process_Grab()) //are we restrained by someone's grip? - return - - if(mob.buckled) //if we're buckled to something, tell it we moved. - return mob.buckled.relaymove(mob, direct) - - if(!mob.canmove) - return 0 - - if(isobj(mob.loc) || ismob(mob.loc)) //Inside an object, tell it we moved - var/atom/O = mob.loc - return O.relaymove(mob, direct) - - if(!mob.Process_Spacemove(direct)) - return 0 - - //We are now going to move - moving = 1 - move_delay = mob.movement_delay() + world.time - - if(mob.confused) - if(mob.confused > 40) - step(mob, pick(cardinal)) - else if(prob(mob.confused * 1.5)) - step(mob, angle2dir(dir2angle(direct) + pick(90, -90))) - else if(prob(mob.confused * 3)) - step(mob, angle2dir(dir2angle(direct) + pick(45, -45))) - else - step(mob, direct) - else - . = ..() - - moving = 0 - if(mob && .) - if(mob.throwing) - mob.throwing.finalize(FALSE) - - for(var/obj/O in mob) - O.on_mob_move(direct, src) - - return . - - -///Process_Grab() -///Called by client/Move() -///Checks to see if you are being grabbed and if so attemps to break it -/client/proc/Process_Grab() - if(mob.pulledby) - if(mob.incapacitated(ignore_restraints = 1)) - move_delay = world.time + 10 - return 1 - else if(mob.restrained(ignore_grab = 1)) - move_delay = world.time + 10 - src << "You're restrained! You can't move!" - return 1 - else - return mob.resist_grab(1) - - -///Process_Incorpmove -///Called by client/Move() -///Allows mobs to run though walls -/client/proc/Process_Incorpmove(direct) - var/turf/mobloc = get_turf(mob) - if(!isliving(mob)) - return - var/mob/living/L = mob - switch(L.incorporeal_move) - if(1) - L.loc = get_step(L, direct) - L.setDir(direct) - if(2) - if(prob(50)) - var/locx - var/locy - switch(direct) - if(NORTH) - locx = mobloc.x - locy = (mobloc.y+2) - if(locy>world.maxy) - return - if(SOUTH) - locx = mobloc.x - locy = (mobloc.y-2) - if(locy<1) - return - if(EAST) - locy = mobloc.y - locx = (mobloc.x+2) - if(locx>world.maxx) - return - if(WEST) - locy = mobloc.y - locx = (mobloc.x-2) - if(locx<1) - return - else - return - L.loc = locate(locx,locy,mobloc.z) - var/limit = 2//For only two trailing shadows. - for(var/turf/T in getline(mobloc, L.loc)) - new /obj/effect/overlay/temp/dir_setting/ninja/shadow(T, L.dir) - limit-- - if(limit<=0) - break - else - new /obj/effect/overlay/temp/dir_setting/ninja/shadow(mobloc, L.dir) - L.loc = get_step(L, direct) - L.setDir(direct) - if(3) //Incorporeal move, but blocked by holy-watered tiles and salt piles. - var/turf/open/floor/stepTurf = get_step(L, direct) - for(var/obj/effect/decal/cleanable/salt/S in stepTurf) - L << "[S] bars your passage!" - if(isrevenant(L)) - var/mob/living/simple_animal/revenant/R = L - R.reveal(20) - R.stun(20) - return - if(stepTurf.flags & NOJAUNT) - L << "Holy energies block your path." - else - L.loc = get_step(L, direct) - L.setDir(direct) - return 1 - - -///Process_Spacemove -///Called by /client/Move() -///For moving in space -///Return 1 for movement 0 for none -/mob/Process_Spacemove(movement_dir = 0) - if(..()) - return 1 - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - if(backup.newtonian_move(turn(movement_dir, 180))) //You're pushing off something movable, so it moves - src << "You push off of [backup] to propel yourself." - return 1 - return 0 - -/mob/get_spacemove_backup() - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) - continue - else if(isturf(A)) - var/turf/turf = A - if(isspaceturf(turf)) - continue - if(!turf.density && !mob_negates_gravity()) - continue - return A - else - var/atom/movable/AM = A - if(AM == buckled) //Kind of unnecessary but let's just be sure - continue - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - if(pulling == AM) - continue - . = AM - -/mob/proc/mob_has_gravity() - return has_gravity() - -/mob/proc/mob_negates_gravity() - return 0 - -//moves the mob/object we're pulling -/mob/proc/Move_Pulled(atom/A) - if(!pulling) - return - if(pulling.anchored || !pulling.Adjacent(src)) - stop_pulling() - return - if(isliving(pulling)) - var/mob/living/L = pulling - if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it - stop_pulling() - return - if(A == loc && pulling.density) - return - if(!Process_Spacemove(get_dir(pulling.loc, A))) - return - step(pulling, get_dir(pulling.loc, A)) - - -/mob/proc/slip(s_amount, w_amount, obj/O, lube) - return - -/mob/proc/update_gravity() - return - -//bodypart selection - Cyberboss -//8 toggles through head - eyes - mouth -//4: r-arm 5: chest 6: l-arm -//1: r-leg 2: groin 3: l-leg - -/client/proc/check_has_body_select() - return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) - -/client/verb/body_toggle_head() - set name = "body-toggle-head" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/next_in_line - switch(mob.zone_selected) - if("head") - next_in_line = "eyes" - if("eyes") - next_in_line = "mouth" - else - next_in_line = "head" - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/body_r_arm() - set name = "body-r-arm" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("r_arm", mob) - -/client/verb/body_chest() - set name = "body-chest" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("chest", mob) - -/client/verb/body_l_arm() - set name = "body-l-arm" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("l_arm", mob) - -/client/verb/body_r_leg() - set name = "body-r-leg" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("r_leg", mob) - -/client/verb/body_groin() - set name = "body-groin" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("groin", mob) - -/client/verb/body_l_leg() - set name = "body-l-leg" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone("l_leg", mob) - -/client/verb/toggle_walk_run() - set name = "toggle-walk-run" - set hidden = TRUE - set instant = TRUE - if(mob) - mob.toggle_move_intent() - -/mob/proc/toggle_move_intent() - if(hud_used && hud_used.static_inventory) - for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) - selector.toggle(src) +/mob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover, /obj/item/projectile) || mover.throwing) + return (!density || lying) + if(mover.checkpass(PASSMOB)) + return 1 + if(buckled == mover) + return 1 + if(ismob(mover)) + if (mover in buckled_mobs) + return 1 + return (!mover.density || !density || lying) + + +//The byond version of these verbs wait for the next tick before acting. +// instant verbs however can run mid tick or even during the time between ticks. +/client/verb/moveup() + set name = ".moveup" + set instant = 1 + Move(get_step(mob, NORTH), NORTH) + +/client/verb/movedown() + set name = ".movedown" + set instant = 1 + Move(get_step(mob, SOUTH), SOUTH) + +/client/verb/moveright() + set name = ".moveright" + set instant = 1 + Move(get_step(mob, EAST), EAST) + +/client/verb/moveleft() + set name = ".moveleft" + set instant = 1 + Move(get_step(mob, WEST), WEST) + +/client/Northeast() + swap_hand() + return + + +/client/Southeast() + attack_self() + return + + +/client/Southwest() + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.toggle_throw_mode() + else + usr << "This mob type cannot throw items." + return + + +/client/Northwest() + if(!usr.get_active_held_item()) + usr << "You have nothing to drop in your hand!" + return + usr.drop_item() + +//This gets called when you press the delete button. +/client/verb/delete_key_pressed() + set hidden = 1 + + if(!usr.pulling) + usr << "You are not pulling anything." + return + usr.stop_pulling() + +/client/verb/swap_hand() + set category = "IC" + set name = "Swap hands" + + if(mob) + mob.swap_hand() + +/client/verb/attack_self() + set hidden = 1 + if(mob) + mob.mode() + return + + +/client/verb/drop_item() + set hidden = 1 + if(!iscyborg(mob)) + mob.drop_item_v() + return + + +/client/Center() + if(isobj(mob.loc)) + var/obj/O = mob.loc + if(mob.canmove) + return O.relaymove(mob, 0) + return + + +/client/proc/Move_object(direct) + if(mob && mob.control_object) + if(mob.control_object.density) + step(mob.control_object,direct) + if(!mob.control_object) + return + mob.control_object.setDir(direct) + else + mob.control_object.loc = get_step(mob.control_object,direct) + return + + +/client/Move(n, direct) + if(world.time < move_delay) + return 0 + move_delay = world.time+world.tick_lag //this is here because Move() can now be called mutiple times per tick + if(!mob || !mob.loc) + return 0 + if(mob.notransform) + return 0 //This is sota the goto stop mobs from moving var + if(mob.control_object) + return Move_object(direct) + if(!isliving(mob)) + return mob.Move(n,direct) + if(mob.stat == DEAD) + mob.ghostize() + return 0 + if(moving) + return 0 + if(mob.force_moving) + return 0 + if(isliving(mob)) + var/mob/living/L = mob + if(L.incorporeal_move) //Move though walls + Process_Incorpmove(direct) + return 0 + + if(mob.remote_control) //we're controlling something, our movement is relayed to it + return mob.remote_control.relaymove(mob, direct) + + if(isAI(mob)) + return AIMove(n,direct,mob) + + if(Process_Grab()) //are we restrained by someone's grip? + return + + if(mob.buckled) //if we're buckled to something, tell it we moved. + return mob.buckled.relaymove(mob, direct) + + if(!mob.canmove) + return 0 + + if(isobj(mob.loc) || ismob(mob.loc)) //Inside an object, tell it we moved + var/atom/O = mob.loc + return O.relaymove(mob, direct) + + if(!mob.Process_Spacemove(direct)) + return 0 + + //We are now going to move + moving = 1 + move_delay = mob.movement_delay() + world.time + + if(mob.confused) + if(mob.confused > 40) + step(mob, pick(cardinal)) + else if(prob(mob.confused * 1.5)) + step(mob, angle2dir(dir2angle(direct) + pick(90, -90))) + else if(prob(mob.confused * 3)) + step(mob, angle2dir(dir2angle(direct) + pick(45, -45))) + else + step(mob, direct) + else + . = ..() + + moving = 0 + if(mob && .) + if(mob.throwing) + mob.throwing.finalize(FALSE) + + for(var/obj/O in mob) + O.on_mob_move(direct, src) + + return . + + +///Process_Grab() +///Called by client/Move() +///Checks to see if you are being grabbed and if so attemps to break it +/client/proc/Process_Grab() + if(mob.pulledby) + if(mob.incapacitated(ignore_restraints = 1)) + move_delay = world.time + 10 + return 1 + else if(mob.restrained(ignore_grab = 1)) + move_delay = world.time + 10 + src << "You're restrained! You can't move!" + return 1 + else + return mob.resist_grab(1) + + +///Process_Incorpmove +///Called by client/Move() +///Allows mobs to run though walls +/client/proc/Process_Incorpmove(direct) + var/turf/mobloc = get_turf(mob) + if(!isliving(mob)) + return + var/mob/living/L = mob + switch(L.incorporeal_move) + if(1) + L.loc = get_step(L, direct) + L.setDir(direct) + if(2) + if(prob(50)) + var/locx + var/locy + switch(direct) + if(NORTH) + locx = mobloc.x + locy = (mobloc.y+2) + if(locy>world.maxy) + return + if(SOUTH) + locx = mobloc.x + locy = (mobloc.y-2) + if(locy<1) + return + if(EAST) + locy = mobloc.y + locx = (mobloc.x+2) + if(locx>world.maxx) + return + if(WEST) + locy = mobloc.y + locx = (mobloc.x-2) + if(locx<1) + return + else + return + L.loc = locate(locx,locy,mobloc.z) + var/limit = 2//For only two trailing shadows. + for(var/turf/T in getline(mobloc, L.loc)) + new /obj/effect/overlay/temp/dir_setting/ninja/shadow(T, L.dir) + limit-- + if(limit<=0) + break + else + new /obj/effect/overlay/temp/dir_setting/ninja/shadow(mobloc, L.dir) + L.loc = get_step(L, direct) + L.setDir(direct) + if(3) //Incorporeal move, but blocked by holy-watered tiles and salt piles. + var/turf/open/floor/stepTurf = get_step(L, direct) + for(var/obj/effect/decal/cleanable/salt/S in stepTurf) + L << "[S] bars your passage!" + if(isrevenant(L)) + var/mob/living/simple_animal/revenant/R = L + R.reveal(20) + R.stun(20) + return + if(stepTurf.flags & NOJAUNT) + L << "Holy energies block your path." + else + L.loc = get_step(L, direct) + L.setDir(direct) + return 1 + + +///Process_Spacemove +///Called by /client/Move() +///For moving in space +///Return 1 for movement 0 for none +/mob/Process_Spacemove(movement_dir = 0) + if(..()) + return 1 + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + if(backup.newtonian_move(turn(movement_dir, 180))) //You're pushing off something movable, so it moves + src << "You push off of [backup] to propel yourself." + return 1 + return 0 + +/mob/get_spacemove_backup() + for(var/A in orange(1, get_turf(src))) + if(isarea(A)) + continue + else if(isturf(A)) + var/turf/turf = A + if(isspaceturf(turf)) + continue + if(!turf.density && !mob_negates_gravity()) + continue + return A + else + var/atom/movable/AM = A + if(AM == buckled) //Kind of unnecessary but let's just be sure + continue + if(!AM.CanPass(src) || AM.density) + if(AM.anchored) + return AM + if(pulling == AM) + continue + . = AM + +/mob/proc/mob_has_gravity() + return has_gravity() + +/mob/proc/mob_negates_gravity() + return 0 + +//moves the mob/object we're pulling +/mob/proc/Move_Pulled(atom/A) + if(!pulling) + return + if(pulling.anchored || !pulling.Adjacent(src)) + stop_pulling() + return + if(isliving(pulling)) + var/mob/living/L = pulling + if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it + stop_pulling() + return + if(A == loc && pulling.density) + return + if(!Process_Spacemove(get_dir(pulling.loc, A))) + return + step(pulling, get_dir(pulling.loc, A)) + + +/mob/proc/slip(s_amount, w_amount, obj/O, lube) + return + +/mob/proc/update_gravity() + return + +//bodypart selection - Cyberboss +//8 toggles through head - eyes - mouth +//4: r-arm 5: chest 6: l-arm +//1: r-leg 2: groin 3: l-leg + +/client/proc/check_has_body_select() + return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) + +/client/verb/body_toggle_head() + set name = "body-toggle-head" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + switch(mob.zone_selected) + if("head") + next_in_line = "eyes" + if("eyes") + next_in_line = "mouth" + else + next_in_line = "head" + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_r_arm() + set name = "body-r-arm" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("r_arm", mob) + +/client/verb/body_chest() + set name = "body-chest" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("chest", mob) + +/client/verb/body_l_arm() + set name = "body-l-arm" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("l_arm", mob) + +/client/verb/body_r_leg() + set name = "body-r-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("r_leg", mob) + +/client/verb/body_groin() + set name = "body-groin" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("groin", mob) + +/client/verb/body_l_leg() + set name = "body-l-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone("l_leg", mob) + +/client/verb/toggle_walk_run() + set name = "toggle-walk-run" + set hidden = TRUE + set instant = TRUE + if(mob) + mob.toggle_move_intent() + +/mob/proc/toggle_move_intent() + if(hud_used && hud_used.static_inventory) + for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) + selector.toggle(src)