From bbca8405efd8355ac805be59311e8bf2b9e163f4 Mon Sep 17 00:00:00 2001 From: Tigercat2000 Date: Tue, 16 May 2017 21:30:38 -0700 Subject: [PATCH] -tg- Move Refactor This commit ports /tg/'s move refactor. The throwing system has been replaced entirely, removing the necessity of throw_at_fast and resolving multiple outstanding issues, such as crossbows being unusable. Spacedrifting has also been upgraded to function with the new throwing system. It is now it's own process. Tickcomp has been killed, and the config values have been adjusted to more or less match live Paradise. All mobs now share a common Bump() proc. There are only four mobtypes which do not, including humans and simple animals. With the exception of mob types that do not ever want to Bump() or be Bumped(), they should call the parent proc. Human movement slowdown has been moderately tweaked in how it stacks effects; It shouldn't be significantly different from a player perspective. Mobs will now spread fire if they bump into another mob. I don't want to set the world on fiiiire, I just want start a flame in your heart~ For player facing changes: Input delay has been reduced by roughly ~50ms for any direction keys, by advantage of a previously unknown flag on byond verbs which allow them to operate independently from the tick rate of the server. You may need to clear your interface.dmf file if you have a custom skin for this change to function. --- code/__DEFINES/callbacks.dm | 2 + code/__DEFINES/combat.dm | 3 + code/__DEFINES/flags.dm | 11 +- code/__DEFINES/misc.dm | 5 +- code/__DEFINES/mob.dm | 2 +- code/_onclick/adjacent.dm | 20 +- code/controllers/Processes/spacedrift.dm | 53 ++++ code/controllers/Processes/throwing.dm | 138 +++++++++ code/controllers/configuration.dm | 4 - code/datums/callback.dm | 98 +++++++ code/game/atoms.dm | 12 +- code/game/atoms_movable.dm | 262 ++++++++---------- .../machinery/computer/HolodeckControl.dm | 2 +- code/game/machinery/doors/windowdoor.dm | 10 +- code/game/machinery/shieldgen.dm | 54 ++-- code/game/mecha/mecha.dm | 8 +- code/game/objects/effects/aliens.dm | 4 +- .../game/objects/effects/spawners/lootdrop.dm | 2 +- code/game/objects/items.dm | 55 ++-- code/game/objects/items/policetape.dm | 2 +- code/game/objects/items/weapons/RCL.dm | 2 +- code/game/objects/items/weapons/dice.dm | 6 +- code/game/objects/objs.dm | 5 +- .../structures/crates_lockers/closets.dm | 4 +- code/game/objects/structures/grille.dm | 4 +- code/game/objects/structures/morgue.dm | 4 +- code/game/objects/structures/tables_racks.dm | 11 +- code/game/objects/structures/window.dm | 34 +-- code/game/objects/weapons.dm | 5 - code/modules/admin/verbs/ticklag.dm | 3 - code/modules/arcade/mob_hunt/mob_avatar.dm | 6 +- code/modules/clothing/spacesuits/rig/rig.dm | 5 - code/modules/fish/fishtank.dm | 9 +- code/modules/flufftext/Hallucination.dm | 47 +--- .../mob/living/carbon/alien/alien_defenses.dm | 4 +- .../carbon/alien/humanoid/caste/hunter.dm | 54 +--- .../living/carbon/alien/humanoid/humanoid.dm | 26 -- .../mob/living/carbon/alien/larva/larva.dm | 32 --- code/modules/mob/living/carbon/carbon.dm | 23 ++ .../mob/living/carbon/carbon_defenses.dm | 21 +- code/modules/mob/living/carbon/human/human.dm | 87 ------ .../mob/living/carbon/human/human_defense.dm | 92 ++---- .../mob/living/carbon/human/human_movement.dm | 92 +++--- .../carbon/human/interactive/interactive.dm | 2 +- code/modules/mob/living/carbon/slime/slime.dm | 71 ++--- code/modules/mob/living/living.dm | 233 +++++++++++----- code/modules/mob/living/living_defense.dm | 97 ++++--- code/modules/mob/living/silicon/pai/pai.dm | 4 +- .../mob/living/silicon/robot/drone/drone.dm | 19 +- .../modules/mob/living/silicon/robot/robot.dm | 32 --- code/modules/mob/living/silicon/silicon.dm | 25 -- .../mob/living/simple_animal/bot/secbot.dm | 4 +- .../living/simple_animal/friendly/corgi.dm | 35 --- .../hostile/megafauna/bubblegum.dm | 4 +- .../simple_animal/hostile/megafauna/dragon.dm | 2 +- .../simple_animal/hostile/spaceworms.dm | 1 - .../hostile/terror_spiders/reproduction.dm | 11 +- .../hostile/terror_spiders/terror_spiders.dm | 19 +- .../mob/living/simple_animal/simple_animal.dm | 14 - code/modules/mob/mob_movement.dm | 257 +++++++++-------- code/modules/mob/pulling.dm | 6 +- code/modules/pda/PDA.dm | 5 - code/modules/pda/mob_hunt_game_app.dm | 6 - code/modules/projectiles/guns/throw.dm | 6 +- config/example/config.txt | 3 - config/example/game_options.txt | 14 +- interface/skin.dmf | 120 ++++---- paradise.dme | 4 + sound/weapons/throwtap.ogg | Bin 0 -> 10411 bytes 69 files changed, 1126 insertions(+), 1191 deletions(-) create mode 100644 code/__DEFINES/callbacks.dm create mode 100644 code/controllers/Processes/spacedrift.dm create mode 100644 code/controllers/Processes/throwing.dm create mode 100644 code/datums/callback.dm create mode 100644 sound/weapons/throwtap.ogg diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm new file mode 100644 index 00000000000..c0d8ac15ace --- /dev/null +++ b/code/__DEFINES/callbacks.dm @@ -0,0 +1,2 @@ +#define CALLBACK new /datum/callback +#define INVOKE_ASYNC ImmediateInvokeAsync \ No newline at end of file diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 0bd2b82b42a..499be494056 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -70,3 +70,6 @@ #define WEAPON_LIGHT 0 #define WEAPON_MEDIUM 1 #define WEAPON_HEAVY 2 + +// Embedded objects +#define EMBED_THROWSPEED_THRESHOLD 15 \ No newline at end of file diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 1cba02075a7..05133d226f1 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -77,11 +77,12 @@ #define SAFE 16 //flags for pass_flags -#define PASSTABLE 1 -#define PASSGLASS 2 -#define PASSGRILLE 4 -#define PASSBLOB 8 -#define PASSMOB 16 +#define PASSTABLE 1 +#define PASSGLASS 2 +#define PASSGRILLE 4 +#define PASSBLOB 8 +#define PASSMOB 16 +#define LETPASSTHROW 32 //turf-only flags #define NOJAUNT 1 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index b3263a93425..99079fd81bb 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -329,4 +329,7 @@ #define TENDRIL_CLEAR_SCORE "Tendrils Killed" // The number of station goals generated each round. -#define STATION_GOAL_BUDGET 1 \ No newline at end of file +#define STATION_GOAL_BUDGET 1 + +#define FIRST_DIAG_STEP 1 +#define SECOND_DIAG_STEP 2 \ No newline at end of file diff --git a/code/__DEFINES/mob.dm b/code/__DEFINES/mob.dm index 97cea621dae..c2295b9c5dc 100644 --- a/code/__DEFINES/mob.dm +++ b/code/__DEFINES/mob.dm @@ -47,7 +47,7 @@ #define HUMAN_STRIP_DELAY 40 //takes 40ds = 4s to strip someone. #define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien. -#define SHOES_SLOWDOWN -1.0 // How much shoes slow you down by default. Negative values speed you up +#define SHOES_SLOWDOWN 0 // How much shoes slow you down by default. Negative values speed you up //Mob attribute defaults. diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 5a84463c8f1..038174388ff 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -23,7 +23,7 @@ * If you are in the same turf, always true * If you are vertically/horizontally adjacent, ensure there are no border objects * If you are diagonally adjacent, ensure you can pass through at least one of the mutually adjacent square. - * Passing through in this case ignores anything with the throwpass flag, such as tables, racks, and morgue trays. + * Passing through in this case ignores anything with the LETPASSTHROW flag, such as tables, racks, and morgue trays. */ /turf/Adjacent(var/atom/neighbor, var/atom/target = null) var/turf/T0 = get_turf(neighbor) @@ -82,26 +82,18 @@ /* This checks if you there is uninterrupted airspace between that turf and this one. - This is defined as any dense ON_BORDER object, or any dense object without throwpass. + This is defined as any dense ON_BORDER object, or any dense object without LETPASSTHROW. The border_only flag allows you to not objects (for source and destination squares) */ /turf/proc/ClickCross(var/target_dir, var/border_only, var/target_atom = null) for(var/obj/O in src) - if( !O.density || O == target_atom || O.throwpass) continue // throwpass is used for anything you can click through + if( !O.density || O == target_atom || (O.pass_flags & LETPASSTHROW)) + continue // LETPASSTHROW is used for anything you can click through - if( O.flags&ON_BORDER) // windows have throwpass but are on border, check them first + if( O.flags&ON_BORDER) // windows are on border, check them first if( O.dir & target_dir || O.dir&(O.dir-1) ) // full tile windows are just diagonals mechanically return 0 else if( !border_only ) // dense, not on border, cannot pass over return 0 - return 1 -/* - Aside: throwpass does not do what I thought it did originally, and is only used for checking whether or not - a thrown object should stop after already successfully entering a square. Currently the throw code involved - only seems to affect hitting mobs, because the checks performed against objects are already performed when - entering or leaving the square. Since throwpass isn't used on mobs, but only on objects, it is effectively - useless. Throwpass may later need to be removed and replaced with a passcheck (bitfield on movable atom passflags). - - Since I don't want to complicate the click code rework by messing with unrelated systems it won't be changed here. -*/ \ No newline at end of file + return 1 \ No newline at end of file diff --git a/code/controllers/Processes/spacedrift.dm b/code/controllers/Processes/spacedrift.dm new file mode 100644 index 00000000000..d799aeb99d9 --- /dev/null +++ b/code/controllers/Processes/spacedrift.dm @@ -0,0 +1,53 @@ +var/global/datum/controller/process/spacedrift/drift_master + +/datum/controller/process/spacedrift + var/list/processing_list = list() + +/datum/controller/process/spacedrift/setup() + name = "spacedrift" + schedule_interval = 5 + start_delay = 20 + log_startup_progress("Spacedrift starting up.") + +/datum/controller/process/spacedrift/statProcess() + ..() + stat(null, "P:[processing_list.len]") + +/datum/controller/process/spacedrift/doWork() + var/list/currentrun = processing_list.Copy() + + while(currentrun.len) + var/atom/movable/AM = currentrun[currentrun.len] + currentrun.len-- + if(!AM) + processing_list -= AM + SCHECK + continue + + if(AM.inertia_next_move > world.time) + SCHECK + continue + + if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0)) + AM.inertia_dir = 0 + + if(!AM.inertia_dir) + AM.inertia_last_loc = null + processing_list -= AM + SCHECK + continue + + var/old_dir = AM.dir + var/old_loc = AM.loc + AM.inertia_moving = TRUE + step(AM, AM.inertia_dir) + AM.inertia_moving = FALSE + AM.inertia_next_move = world.time + AM.inertia_move_delay + if(AM.loc == old_loc) + AM.inertia_dir = 0 + + AM.setDir(old_dir) + AM.inertia_last_loc = AM.loc + SCHECK + +DECLARE_GLOBAL_CONTROLLER(spacedrift, drift_master) \ No newline at end of file diff --git a/code/controllers/Processes/throwing.dm b/code/controllers/Processes/throwing.dm new file mode 100644 index 00000000000..a11c1252d35 --- /dev/null +++ b/code/controllers/Processes/throwing.dm @@ -0,0 +1,138 @@ +#define MAX_THROWING_DIST 512 // 2 z-levels on default width +#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run. + +var/global/datum/controller/process/throwing/throw_master + +/datum/controller/process/throwing + var/list/processing_list + +/datum/controller/process/throwing/setup() + name = "throwing" + schedule_interval = 1 + start_delay = 20 + log_startup_progress("Throw ticker starting up.") + +/datum/controller/process/throwing/statProcess() + ..() + stat(null, "P:[processing_list.len]") + +/datum/controller/process/throwing/started() + ..() + if(!processing_list) + processing_list = list() + +/datum/controller/process/throwing/doWork() + for(last_object in processing_list) + var/atom/movable/AM = last_object + if(istype(AM) && isnull(AM.gcDestroyed)) + var/datum/thrownthing/TT = processing_list[AM] + if(istype(TT) && isnull(TT.gcDestroyed)) + TT.tick() + SCHECK + else + catchBadType(TT) + processing_list -= AM + AM.throwing = null + else + catchBadType(AM) + processing_list -= AM + SCHECK + +DECLARE_GLOBAL_CONTROLLER(throwing, throw_master) + +/datum/thrownthing + var/atom/movable/thrownthing + var/atom/target + var/turf/target_turf + var/init_dir + var/maxrange + var/speed + var/mob/thrower + var/diagonals_first + var/dist_travelled = 0 + var/start_time + var/dist_x + var/dist_y + var/dx + var/dy + var/pure_diagonal + var/diagonal_error + var/datum/callback/callback + +/datum/thrownthing/proc/tick() + var/atom/movable/AM = thrownthing + if(!isturf(AM.loc) || !AM.throwing) + finalize() + return + + if(dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept + finalize() + return + + var/atom/step + + // calculate how many tiles to move, making up for any missed ticks. + if((dist_travelled >= maxrange || AM.loc == target_turf) && has_gravity(AM, AM.loc)) + finalize() + return + + if(dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction + step = get_step(AM, get_dir(AM, target_turf)) + else + step = get_step(AM, init_dir) + + if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first + if(diagonal_error >= 0 && max(dist_x, dist_y) - dist_travelled != 1) // we do a step forward unless we're right before the target + step = get_step(AM, dx) + diagonal_error += (diagonal_error < 0) ? dist_x / 2 : -dist_y + + if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge + finalize() + return + + AM.Move(step, get_dir(AM, step)) + + if(!AM.throwing) // we hit something during our move + finalize(hit = TRUE) + return + + dist_travelled++ + + if(dist_travelled > MAX_THROWING_DIST) + finalize() + return + +/datum/thrownthing/proc/finalize(hit = FALSE) + set waitfor = 0 + throw_master.processing_list -= thrownthing + // done throwning, either because it hit something or it finished moving + thrownthing.throwing = null + if(!hit) + for(var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on. + var/atom/A = thing + if(A == target) + hit = 1 + thrownthing.throw_impact(A, src) + break + if(!hit) + thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground. + thrownthing.newtonian_move(init_dir) + else + thrownthing.newtonian_move(init_dir) + if(callback) + callback.Invoke() + +/datum/thrownthing/proc/hit_atom(atom/A) + thrownthing.throw_impact(A, src) + thrownthing.newtonian_move(init_dir) + finalize(TRUE) + +/datum/thrownthing/proc/hitcheck() + for(var/thing in get_turf(thrownthing)) + var/atom/movable/AM = thing + if(AM == thrownthing) + continue + if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER)) + thrownthing.throwing = null + thrownthing.throw_impact(AM, src) + return TRUE \ No newline at end of file diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 6cccb48e492..2ab267ce5a8 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -39,7 +39,6 @@ var/allow_Metadata = 0 // Metadata is supported. var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. var/Ticklag = 0.5 - var/Tickcomp = 0 var/socket_talk = 0 // use socket_talk to communicate with other processes var/list/resource_urls = null var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. @@ -445,9 +444,6 @@ if("socket_talk") socket_talk = text2num(value) - if("tickcomp") - Tickcomp = 1 - if("allow_antag_hud") config.antag_hud_allowed = 1 diff --git a/code/datums/callback.dm b/code/datums/callback.dm new file mode 100644 index 00000000000..02909c1a196 --- /dev/null +++ b/code/datums/callback.dm @@ -0,0 +1,98 @@ +/* + USAGE: + + var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn) + var/timerid = addtimer(C, time, timertype) + OR + var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype) + + Note: proc strings can only be given for datum proc calls, global procs must be proc paths + Also proc strings are strongly advised against because they don't compile error if the proc stops existing + See the note on proc typepath shortcuts + + INVOKING THE CALLBACK: + var/result = C.Invoke(args, to, add) //additional args are added after the ones given when the callback was created + OR + var/result = C.InvokeAsync(args, to, add) //Sleeps will not block, returns . on the first sleep (then continues on in the "background" after the sleep/block ends), otherwise operates normally. + OR + INVOKE_ASYNC() to immediately create and call InvokeAsync + + PROC TYPEPATH SHORTCUTS (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...) + + global proc while in another global proc: + .procname + Example: + CALLBACK(GLOBAL_PROC, .some_proc_here) + + proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents: + .procname + Example: + CALLBACK(src, .some_proc_here) + + + when the above doesn't apply: + .proc/procname + Example: + CALLBACK(src, .proc/some_proc_here) + + proc defined on a parent of a some type: + /some/type/.proc/some_proc_here + + + + Other wise you will have to do the full typepath of the proc (/type/of/thing/proc/procname) + +*/ + +/datum/callback + var/datum/object = GLOBAL_PROC + var/delegate + var/list/arguments + +/datum/callback/New(thingtocall, proctocall, ...) + if(thingtocall) + object = thingtocall + delegate = proctocall + if(length(args) > 2) + arguments = args.Copy(3) + +/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...) + set waitfor = FALSE + + if(!thingtocall) + return + + var/list/calling_arguments = length(args) > 2 ? args.Copy(3) : null + + if(thingtocall == GLOBAL_PROC) + call(proctocall)(arglist(calling_arguments)) + else + call(thingtocall, proctocall)(arglist(calling_arguments)) + +/datum/callback/proc/Invoke(...) + if(!object) + return + var/list/calling_arguments = arguments + if(length(args)) + if(length(arguments)) + calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean + else + calling_arguments = args + if(object == GLOBAL_PROC) + return call(delegate)(arglist(calling_arguments)) + return call(object, delegate)(arglist(calling_arguments)) + +//copy and pasted because fuck proc overhead +/datum/callback/proc/InvokeAsync(...) + set waitfor = FALSE + if(!object) + return + var/list/calling_arguments = arguments + if(length(args)) + if(length(arguments)) + calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean + else + calling_arguments = args + if(object == GLOBAL_PROC) + return call(delegate)(arglist(calling_arguments)) + return call(object, delegate)(arglist(calling_arguments)) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index b5d25c7f5a1..5d1dd5c74fb 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -9,7 +9,6 @@ var/blood_color var/last_bumped = 0 var/pass_flags = 0 - var/throwpass = 0 var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. var/simulated = 1 //filter for actions - used by lighting overlays var/atom_say_verb = "says" @@ -236,10 +235,13 @@ /atom/proc/emag_act() return -/atom/proc/hitby(atom/movable/AM as mob|obj) - if(density) - AM.throwing = 0 - return +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(src, "hitby_react", 2, TRUE, AM) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) /atom/proc/add_hiddenprint(mob/living/M as mob) if(isnull(M)) return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 7dd8d0c5be8..906706de62e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -6,15 +6,21 @@ // var/elevation = 2 - not used anywhere var/move_speed = 10 var/l_move_time = 1 - var/throwing = 0 - var/thrower - var/turf/throw_source = null - var/throw_speed = 2 + var/datum/thrownthing/throwing = null + var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported var/throw_range = 7 - var/no_spin_thrown = 0 //set this to 1 if you don't want an item that you throw to spin, no matter what. -Fox + var/no_spin = 0 + var/no_spin_thrown = 0 var/moved_recently = 0 var/mob/pulledby = null + var/inertia_dir = 0 + var/atom/inertia_last_loc + var/inertia_moving = 0 + var/inertia_next_move = 0 + var/inertia_move_delay = 5 + + var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move var/area/areaMaster @@ -71,61 +77,74 @@ if(!(direct & (direct - 1))) //Cardinal move . = ..() else //Diagonal move, split it into cardinal moves + moving_diagonally = FIRST_DIAG_STEP if(direct & 1) if(direct & 4) if(step(src, NORTH)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) else if(step(src, EAST)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) else if(direct & 8) if(step(src, NORTH)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) else if(step(src, WEST)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) else if(direct & 2) if(direct & 4) if(step(src, SOUTH)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) else if(step(src, EAST)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) else if(direct & 8) if(step(src, SOUTH)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) else if(step(src, WEST)) + moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) - + moving_diagonally = 0 + return if(!loc || (loc == oldloc && oldloc != newloc)) last_move = 0 return + if(.) + Moved(oldloc, direct) + last_move = direct src.move_speed = world.time - src.l_move_time src.l_move_time = world.time - spawn(5) // Causes space drifting. /tg/station has no concept of speed, we just use 5 - if(loc && direct && last_move == direct) - if(loc == newloc) //Remove this check and people can accelerate. Not opening that can of worms just yet. - newtonian_move(last_move) - if(. && buckled_mob && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob . = 0 +// Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/OldLoc, Dir) + if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(Dir) + return 1 -// Previously known as Crossed() +// Previously known as HasEntered() // This is automatically called when something enters your square /atom/movable/Crossed(atom/movable/AM) return -/atom/movable/Bump(var/atom/A as mob|obj|turf|area, sendBump) - if(src.throwing) - src.throw_impact(A) - - if(A && sendBump) - A.last_bumped = world.time +/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). + if(A && yes) + if(throwing) + throwing.hit_atom(A) + . = 1 + if(!A || qdeleted(A)) + return A.Bumped(src) - else - ..() /atom/movable/proc/forceMove(atom/destination) var/turf/old_loc = loc @@ -166,29 +185,6 @@ update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) - if(istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - M.hitby(src,speed) - - else if(isobj(hit_atom)) - var/obj/O = hit_atom - if(!O.anchored) - step(O, src.dir) - O.hitby(src,speed) - - else if(isturf(hit_atom)) - src.throwing = 0 - var/turf/T = hit_atom - if(T.density) - spawn(2) - step(src, turn(src.dir, 180)) - if(istype(src,/mob/living)) - var/mob/living/M = src - M.turf_collision(T, speed) - - //Called whenever an object moves and by mobs when they attempt to move themselves through space //And when an object or action applies a force on src, see newtonian_move() below //Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting @@ -201,13 +197,15 @@ if(pulledby && !pulledby.pulling) return 1 + if(throwing) + return 1 + if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier return 1 return 0 /atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity - if(!loc || Process_Spacemove(0)) inertia_dir = 0 return 0 @@ -216,119 +214,93 @@ if(!direction) return 1 - var/old_dir = dir - . = step(src, direction) - dir = old_dir + inertia_last_loc = loc + drift_master.processing_list[src] = src + return 1 -//decided whether a movable atom being thrown can pass through the turf it is in. -/atom/movable/proc/hit_check(var/speed) - if(src.throwing) - for(var/atom/A in get_turf(src)) - if(A == src) continue - if(istype(A,/mob/living)) - if(A:lying) continue - src.throw_impact(A,speed) - if(isobj(A)) - if(A.density && !A.throwpass) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement - src.throw_impact(A,speed) -/atom/movable/proc/throw_at_fast(atom/target, range, speed, thrower, no_spin) +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) set waitfor = 0 - throw_at(target, range, speed, thrower, no_spin) + return hit_atom.hitby(src) -/atom/movable/proc/throw_at(atom/target, range, speed, thrower, no_spin) - if(!target || !src || (flags & NODROP)) +/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked) + if(!anchored && hitpush) + step(src, AM.dir) + ..() + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, var/datum/callback/callback) + if(!target || (flags & NODROP) || speed <= 0) return 0 - //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target - src.throwing = 1 - src.thrower = thrower - src.throw_source = get_turf(src) //store the origin turf - if(target.allow_spin) // turns out 1000+ spinning objects being thrown at the singularity creates lag - Iamgoofball - if(!no_spin_thrown && !no_spin) - SpinAnimation(5, 1) + if(pulledby) + pulledby.stop_pulling() + + // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? + if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) + var/user_momentum = thrower.movement_delay() + if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead + user_momentum = world.tick_lag + + user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses + + if(get_dir(thrower, target) & last_move) + user_momentum = user_momentum // basically a noop, but needed + else if(get_dir(target, thrower) & last_move) + user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly + else + user_momentum = 0 + + if(user_momentum) + // first lets add that momentum to range + range *= (user_momentum / speed) + 1 + //then lets add it to speed + speed += user_momentum + if(speed <= 0) + return //no throw speed, the user was moving too fast. + + var/datum/thrownthing/TT = new() + TT.thrownthing = src + TT.target = target + TT.target_turf = get_turf(target) + TT.init_dir = get_dir(src, target) + TT.maxrange = range + TT.speed = speed + TT.thrower = thrower + TT.diagonals_first = diagonals_first + TT.callback = callback + var/dist_x = abs(target.x - src.x) var/dist_y = abs(target.y - src.y) + var/dx = (target.x > src.x) ? EAST : WEST + var/dy = (target.y > src.y) ? NORTH : SOUTH - var/dx - if(target.x > src.x) - dx = EAST - else - dx = WEST + if(dist_x == dist_y) + TT.pure_diagonal = 1 - var/dy - if(target.y > src.y) - dy = NORTH - else - dy = SOUTH - var/dist_travelled = 0 - var/dist_since_sleep = 0 - var/area/a = get_area(src.loc) - if(dist_x > dist_y) - var/error = dist_x/2 - dist_y - while(src && target &&((((src.x < target.x && dx == EAST) || (src.x > target.x && dx == WEST)) && dist_travelled < range) || (a && a.has_gravity == 0) || istype(src.loc, /turf/space)) && src.throwing && istype(src.loc, /turf)) - // only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up - if(error < 0) - var/atom/step = get_step(src, dy) - if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge - break - src.Move(step, get_dir(loc, step)) - hit_check(speed) - error += dist_x - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - else - var/atom/step = get_step(src, dx) - if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge - break - src.Move(step) - hit_check(speed) - error -= dist_y - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - a = get_area(src.loc) - else - var/error = dist_y/2 - dist_x - while(src && target &&((((src.y < target.y && dy == NORTH) || (src.y > target.y && dy == SOUTH)) && dist_travelled < range) || (a && a.has_gravity == 0) || istype(src.loc, /turf/space)) && src.throwing && istype(src.loc, /turf)) - // only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up - if(error < 0) - var/atom/step = get_step(src, dx) - if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge - break - src.Move(step) - hit_check(speed) - error += dist_y - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - else - var/atom/step = get_step(src, dy) - if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge - break - src.Move(step) - hit_check(speed) - error -= dist_x - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) + else if(dist_x <= dist_y) + var/olddist_x = dist_x + var/olddx = dx + dist_x = dist_y + dist_y = olddist_x + dx = dy + dy = olddx + TT.dist_x = dist_x + TT.dist_y = dist_y + TT.dx = dx + TT.dy = dy + TT.diagonal_error = dist_x / 2 - dist_y + TT.start_time = world.time - a = get_area(src.loc) + if(pulledby) + pulledby.stop_pulling() - //done throwing, either because it hit something or it finished moving - if(isobj(src)) src.throw_impact(get_turf(src),speed) - src.throwing = 0 - src.thrower = null - src.throw_source = null + throwing = TT + if(spin && !no_spin && !no_spin_thrown) + SpinAnimation(5, 1) + + throw_master.processing_list[src] = TT + TT.tick() //Overlays diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index 0aaba4caf13..886dac328de 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -498,7 +498,7 @@ icon_state = "hoop" anchored = 1 density = 1 - throwpass = 1 + pass_flags = LETPASSTHROW /obj/structure/holohoop/attackby(obj/item/weapon/W as obj, mob/user as mob, params) if(istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 2cd5f0a7a34..baaf936e189 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -167,18 +167,16 @@ ..() //When an object is thrown at the window -/obj/machinery/door/window/hitby(AM as mob|obj) - +/obj/machinery/door/window/hitby(atom/movable/AM) ..() var/tforce = 0 if(ismob(AM)) tforce = 40 - else - tforce = AM:throwforce + else if(isobj(AM)) + var/obj/O = AM + tforce = O.throwforce playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) take_damage(tforce) - //..() //Does this really need to be here twice? The parent proc doesn't even do anything yet. - Nodrak - return /obj/machinery/door/window/mech_melee_attack(obj/mecha/M) if(M.damtype == "brute") diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index ff319d5037d..071ae52c942 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -94,51 +94,49 @@ /obj/machinery/shield/hitby(AM as mob|obj) - - //Super realistic, resource-intensive, real-time damage calculations. + ..() var/tforce = 0 if(ismob(AM)) tforce = 40 - else - tforce = AM:throwforce + else if(isobj(AM)) + var/obj/O = AM + tforce = O.throwforce - src.health -= tforce + health -= tforce //This seemed to be the best sound for hitting a force field. - playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1) + playsound(loc, 'sound/effects/EMPulse.ogg', 100, 1) //Handle the destruction of the shield - if(src.health <= 0) + if(health <= 0) visible_message("The [src] dissipates") qdel(src) return //The shield becomes dense to absorb the blow.. purely asthetic. opacity = 1 - spawn(20) if(src) opacity = 0 - - ..() - return - + spawn(20) + if(src) + opacity = 0 /obj/machinery/shieldgen - name = "Emergency shield projector" - desc = "Used to seal minor hull breaches." - icon = 'icons/obj/objects.dmi' - icon_state = "shieldoff" - density = 1 - opacity = 0 - anchored = 0 - pressure_resistance = 2*ONE_ATMOSPHERE - req_access = list(access_engine) - var/const/max_health = 100 - var/health = max_health - var/active = 0 - var/malfunction = 0 //Malfunction causes parts of the shield to slowly dissapate - var/list/deployed_shields = list() - var/is_open = 0 //Whether or not the wires are exposed - var/locked = 0 + name = "Emergency shield projector" + desc = "Used to seal minor hull breaches." + icon = 'icons/obj/objects.dmi' + icon_state = "shieldoff" + density = 1 + opacity = 0 + anchored = 0 + pressure_resistance = 2*ONE_ATMOSPHERE + req_access = list(access_engine) + var/const/max_health = 100 + var/health = max_health + var/active = 0 + var/malfunction = 0 //Malfunction causes parts of the shield to slowly dissapate + var/list/deployed_shields = list() + var/is_open = 0 //Whether or not the wires are exposed + var/locked = 0 /obj/machinery/shieldgen/Destroy() QDEL_LIST(deployed_shields) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index fdf1b341488..2fa9b3af00f 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -368,9 +368,12 @@ breakthrough = 1 else - throwing = 0 //so mechas don't get stuck when landing after being sent by a Mass Driver + if(throwing) + throwing.finalize(FALSE) crashing = null + ..() + if(breakthrough) if(crashing) spawn(1) @@ -524,7 +527,7 @@ user.create_attack_log("attacked [name]") return -/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper +/obj/mecha/hitby(atom/movable/A) //wrapper ..() log_message("Hit by [A].",1) @@ -537,6 +540,7 @@ dam_coeff = B.damage_coeff counter_tracking = 1 break + if(istype(A, /obj/item/mecha_parts/mecha_tracking)) if(!counter_tracking) A.forceMove(src) diff --git a/code/game/objects/effects/aliens.dm b/code/game/objects/effects/aliens.dm index 122f1741a3c..ebeda7a77c0 100644 --- a/code/game/objects/effects/aliens.dm +++ b/code/game/objects/effects/aliens.dm @@ -104,9 +104,9 @@ /obj/structure/alien/resin/hitby(atom/movable/AM) ..() var/tforce = 0 - if(!isobj(AM)) + if(ismob(AM)) tforce = 10 - else + else if(isobj(AM)) var/obj/O = AM tforce = O.throwforce playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 5972d75f3dd..7045a06a875 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -67,7 +67,7 @@ /obj/item/clothing/gloves/color/black = 20, /obj/item/clothing/head/hardhat = 10, /obj/item/clothing/head/hardhat/red = 10, - /obj/item/clothing/head/that{throwforce = 1; throwing = 1} = 10, + /obj/item/clothing/head/that = 10, /obj/item/clothing/head/ushanka = 10, /obj/item/clothing/head/welding = 10, /obj/item/clothing/mask/gas = 10, diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index f67178e1e67..f56ada78145 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -61,6 +61,8 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d var/block_chance = 0 var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom + var/mob/thrownby = null + var/toolspeed = 1 // If this item is a tool, the speed multiplier /* Species-specific sprites, concept stolen from Paradise//vg/. @@ -217,7 +219,8 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d var/obj/item/weapon/storage/S = src.loc S.remove_from_storage(src) - src.throwing = 0 + if(throwing) + throwing.finalize(FALSE) if(loc == user) if(!user.unEquip(src)) return 0 @@ -231,36 +234,6 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d user.put_in_active_hand(src) return 1 - -/obj/item/attack_alien(mob/user as mob) - - if(isalien(user)) // -- TLE - var/mob/living/carbon/alien/A = user - - if(!A.has_fine_manipulation) - if(src in A.contents) // To stop Aliens having items stuck in their pockets - A.unEquip(src) - to_chat(user, "Your claws aren't capable of such fine manipulation.") - return - - if(istype(src.loc, /obj/item/weapon/storage)) - for(var/mob/M in range(1, src.loc)) - if(M.s_active == src.loc) - if(M.client) - M.client.screen -= src - src.throwing = 0 - if(src.loc == user) - if(!user.unEquip(src)) - return - else - if(istype(src.loc, /mob/living)) - return - src.pickup(user) - - user.put_in_active_hand(src) - return - - /obj/item/attack_alien(mob/user as mob) var/mob/living/carbon/alien/A = user @@ -323,9 +296,6 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d /obj/item/proc/talk_into(mob/M, var/text, var/channel=null) return -/obj/item/proc/moved(mob/user, old_loc) - return - /obj/item/proc/dropped(mob/user) for(var/X in actions) var/datum/action/A = X @@ -546,6 +516,23 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d throw_at(S,14,3) else ..() +/obj/item/throw_impact(atom/A) + if(A && !qdeleted(A)) + var/itempush = 1 + if(w_class < 4) + itempush = 0 // too light to push anything + return A.hitby(src, 0, itempush) + +/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) + thrownby = thrower + callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own + . = ..(target, range, speed, thrower, spin, diagonals_first, callback) + +/obj/item/proc/after_throw(datum/callback/callback) + if(callback) //call the original callback + . = callback.Invoke() + throw_speed = initial(throw_speed) //explosions change this. + /obj/item/proc/pwr_drain() return 0 // Process Kill diff --git a/code/game/objects/items/policetape.dm b/code/game/objects/items/policetape.dm index f171e984f36..26b0e8772af 100644 --- a/code/game/objects/items/policetape.dm +++ b/code/game/objects/items/policetape.dm @@ -151,7 +151,7 @@ var/list/tape_roll_applications = list() if(!density) return 1 if(height==0) return 1 - if((mover.pass_flags & PASSTABLE || istype(mover, /obj/effect/meteor) || mover.throwing == 1) ) + if((mover.pass_flags & PASSTABLE || istype(mover, /obj/effect/meteor) || mover.throwing)) return 1 else if(ismob(mover) && allowed(mover)) return 1 diff --git a/code/game/objects/items/weapons/RCL.dm b/code/game/objects/items/weapons/RCL.dm index 8e0357ecd13..0655c0b4b8b 100644 --- a/code/game/objects/items/weapons/RCL.dm +++ b/code/game/objects/items/weapons/RCL.dm @@ -115,7 +115,7 @@ last = C break -/obj/item/weapon/twohanded/rcl/moved(mob/user, turf/old_loc, direct) +/obj/item/weapon/twohanded/rcl/on_mob_move(direct, mob/user) if(active) trigger(user) diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm index 5714f5e4310..80274a4d7ca 100644 --- a/code/game/objects/items/weapons/dice.dm +++ b/code/game/objects/items/weapons/dice.dm @@ -111,10 +111,10 @@ /obj/item/weapon/dice/attack_self(mob/user as mob) diceroll(user) -/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/user as mob) +/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) if(!..()) return - diceroll(user) + diceroll(thrower) /obj/item/weapon/dice/proc/diceroll(mob/user) result = rand(1, sides) @@ -133,7 +133,7 @@ user.visible_message("[user] has thrown [src]. It lands on [result]. [comment]", \ "You throw [src]. It lands on [result]. [comment]", \ "You hear [src] rolling, it sounds like a [fake_result].") - else if(src.throwing == 0) //Dice was thrown and is coming to rest + else if(!throwing) //Dice was thrown and is coming to rest visible_message("[src] rolls to a stop, landing on [result]. [comment]") /obj/item/weapon/dice/d20/e20/diceroll(mob/user as mob, thrown) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 24fdc2e43ce..844c1828200 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -316,4 +316,7 @@ a { for(var/obj/item/Item in contents) //Empty out the contents Item.forceMove(new_loc) if(burn) - Item.fire_act() //Set them on fire, too \ No newline at end of file + Item.fire_act() //Set them on fire, too + +/obj/proc/on_mob_move(dir, mob/user) + return \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 7180e59b800..6db9a26fd8d 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -56,7 +56,7 @@ if(throwing) // you keep some momentum when getting out of a thrown closet step(AM, dir) if(throwing) - throwing = 0 + throwing.finalize(FALSE) /obj/structure/closet/proc/open() if(opened) @@ -327,7 +327,7 @@ /obj/structure/closet/attack_hand(mob/user) add_fingerprint(user) toggle(user) - + /obj/structure/closet/attack_ghost(mob/user) if(user.can_advanced_admin_interact()) toggle(user) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index df1309b3d03..c1b4984c158 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -297,8 +297,8 @@ tforce = 5 else if(isobj(AM)) if(prob(50)) - var/obj/item/I = AM - tforce = max(0, I.throwforce * 0.5) + var/obj/O = AM + tforce = max(0, O.throwforce * 0.5) else if(anchored && !broken) var/turf/T = get_turf(src) var/obj/structure/cable/C = T.get_cable_node() diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index e5c0a7f9179..596673ba0c7 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -190,7 +190,7 @@ layer = 2.0 var/obj/structure/morgue/connected = null anchored = 1.0 - throwpass = 1 + pass_flags = LETPASSTHROW /obj/structure/m_tray/attack_hand(mob/user as mob) @@ -432,7 +432,7 @@ layer = 2.0 var/obj/structure/crematorium/connected = null anchored = 1.0 - throwpass = 1 + pass_flags = LETPASSTHROW /obj/structure/c_tray/attack_hand(mob/user as mob) if(connected) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 76dc6a507a3..55181579699 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -18,7 +18,7 @@ density = 1 anchored = 1.0 layer = 2.8 - throwpass = 1 //You can throw objects over this, despite it's density.") + pass_flags = LETPASSTHROW climbable = 1 var/parts = /obj/item/weapon/table_parts @@ -127,7 +127,8 @@ return /obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) return 1 + if(height == 0) + return 1 if(istype(mover,/obj/item/projectile)) return (check_cover(mover,target)) if(ismob(mover)) @@ -136,6 +137,8 @@ return 1 if(istype(mover) && mover.checkpass(PASSTABLE)) return 1 + if(mover.throwing) + return 1 if(locate(/obj/structure/table) in get_turf(mover)) return 1 if(flipped) @@ -526,7 +529,7 @@ icon_state = "rack" density = 1 anchored = 1.0 - throwpass = 1 //You can throw objects over this, despite it's density. + pass_flags = LETPASSTHROW var/parts = /obj/item/weapon/rack_parts var/health = 5 @@ -563,6 +566,8 @@ return 1 if(istype(mover) && mover.checkpass(PASSTABLE)) return 1 + if(mover.throwing) + return 1 else return 0 diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index c74e2b47861..9da30e5fe82 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -108,22 +108,24 @@ var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8f return 1 -/obj/structure/window/hitby(atom/movable/AM) - if(!CanPass(AM, get_step(src, AM.dir))) //So thrown objects that cross a tile with non-full windows will no longer hit the window even if it isn't visually obstructing the path. - ..() - var/tforce = 0 - if(isobj(AM)) - var/obj/item/I = AM - tforce = I.throwforce - if(reinf) tforce *= 0.25 - playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) - health = max(0, health - tforce) - if(health <= 7 && !reinf) - anchored = 0 - update_nearby_icons() - step(src, get_dir(AM, src)) - if(health <= 0) - destroy() +/obj/structure/window/hitby(atom/movable/AM) + ..() + var/tforce = 0 + if(ismob(AM)) + tforce = 10 + else if(isobj(AM)) + var/obj/O = AM + tforce = O.throwforce + if(reinf) + tforce *= 0.25 + playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1) + health = max(0, health - tforce) + if(health <= 7 && !reinf) + anchored = 0 + update_nearby_icons() + step(src, get_dir(AM, src)) + if(health <= 0) + destroy() /obj/structure/window/attack_hand(mob/user as mob) diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm index b62efa1c049..f35fa65b3c7 100644 --- a/code/game/objects/weapons.dm +++ b/code/game/objects/weapons.dm @@ -2,8 +2,3 @@ name = "weapon" icon = 'icons/obj/weapons.dmi' hitsound = "swing_hit" - -/obj/item/weapon/Bump(mob/M as mob) - spawn(0) - ..() - return \ No newline at end of file diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm index f49984b8fb3..316adf935b0 100644 --- a/code/modules/admin/verbs/ticklag.dm +++ b/code/modules/admin/verbs/ticklag.dm @@ -15,9 +15,6 @@ world.tick_lag = newtick feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - switch(alert("Enable Tick Compensation?","Tick Comp is currently: [config.Tickcomp]","Yes","No")) - if("Yes") config.Tickcomp = 1 - else config.Tickcomp = 0 else to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 8717d5eb2a5..4603622da2a 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -42,9 +42,9 @@ attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob return 1 -/obj/effect/nanomob/hitby(obj/item/O) - if(istype(O, /obj/item/device/pda)) - var/obj/item/device/pda/P = O +/obj/effect/nanomob/hitby(atom/movable/AM) + if(istype(AM, /obj/item/device/pda)) + var/obj/item/device/pda/P = AM attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob return 1 diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 33bb0bcd269..24f5a340760 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -985,11 +985,6 @@ // AIs are a bit slower than regular and ignore move intent. wearer_move_delay = world.time + ai_controlled_move_delay - var/tickcomp = 0 - if(config.Tickcomp) - tickcomp = ((1/(world.tick_lag))*1.3) - 1.3 - wearer_move_delay += tickcomp - if(wearer.buckled) //if we're buckled to something, tell it we moved. return wearer.buckled.relaymove(wearer, direction) diff --git a/code/modules/fish/fishtank.dm b/code/modules/fish/fishtank.dm index bbd7615b6ce..34d393fad0c 100644 --- a/code/modules/fish/fishtank.dm +++ b/code/modules/fish/fishtank.dm @@ -11,7 +11,7 @@ icon_state = "tank1" density = 0 anchored = 0 - throwpass = 0 + pass_flags = 0 var/tank_type = "" // Type of aquarium, used for icon updating var/water_capacity = 0 // Number of units the tank holds (varies with tank type) @@ -38,8 +38,7 @@ icon_state = "bowl1" density = 0 // Small enough to not block stuff anchored = 0 // Small enough to move even when filled - throwpass = 1 // Just like at the county fair, you can't seem to throw the ball in to win the goldfish - pass_flags = PASSTABLE // Small enough to pull onto a table + pass_flags = PASSTABLE | LETPASSTHROW // Just like at the county fair, you can't seem to throw the ball in to win the goldfish, and it's small enough to pull onto a table tank_type = "bowl" water_capacity = 50 // Not very big, therefore it can't hold much @@ -57,7 +56,7 @@ icon_state = "tank1" density = 1 anchored = 1 - throwpass = 1 // You can throw objects over this, despite it's density, because it's short enough. + pass_flags = LETPASSTHROW tank_type = "tank" water_capacity = 200 // Decent sized, holds almost 2 full buckets @@ -75,7 +74,7 @@ icon_state = "wall1" density = 1 anchored = 1 - throwpass = 0 // This thing is the size of a wall, you can't throw past it. + pass_flags = 0 // This thing is the size of a wall, you can't throw past it. tank_type = "wall" water_capacity = 500 // This thing fills an entire tile, it holds a lot. diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index f9c97c87a0b..64bb91a9db4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -180,49 +180,6 @@ Gunshots/explosions/opening doors/less rare audio (done) ..() name = "alien hunter ([rand(1, 1000)])" -/obj/effect/hallucination/simple/xeno/throw_at(atom/target, range, speed) // TODO : Make diagonal trhow into proc/property - if(!target || !src || (flags & NODROP)) - return 0 - - throwing = 1 - - var/dist_x = abs(target.x - x) - var/dist_y = abs(target.y - y) - var/dist_travelled = 0 - var/dist_since_sleep = 0 - - var/tdist_x = dist_x; - var/tdist_y = dist_y; - - if(dist_x <= dist_y) - tdist_x = dist_y; - tdist_y = dist_x; - - var/error = tdist_x/2 - tdist_y - while(target && (((((dist_x > dist_y) && ((x < target.x) || (x > target.x))) || ((dist_x <= dist_y) && ((y < target.y) || (y > target.y))) || (x > target.x)) && dist_travelled < range) || !has_gravity(src))) - if(!throwing) - break - if(!istype(loc, /turf)) - break - - var/atom/step = get_step(src, get_dir(src, target)) - if(!step) - break - Move(step, get_dir(src, step)) - hit_check() - error += (error < 0) ? tdist_x : -tdist_y; - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - - - throwing = 0 - throw_impact(get_turf(src)) - - return 1 - /obj/effect/hallucination/simple/xeno/throw_impact(A) update_icon("alienh_pounce") if(A == target) @@ -247,12 +204,12 @@ Gunshots/explosions/opening doors/less rare audio (done) if(!xeno) return xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) - xeno.throw_at(target,7,1) + xeno.throw_at(target,7,1, spin = 0, diagonals_first = 1) sleep(10) if(!xeno) return xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32) - xeno.throw_at(pump,7,1) + xeno.throw_at(pump,7,1, spin = 0, diagonals_first = 1) sleep(10) if(!xeno) return diff --git a/code/modules/mob/living/carbon/alien/alien_defenses.dm b/code/modules/mob/living/carbon/alien/alien_defenses.dm index c1ce34bf8e7..65e32e2fae9 100644 --- a/code/modules/mob/living/carbon/alien/alien_defenses.dm +++ b/code/modules/mob/living/carbon/alien/alien_defenses.dm @@ -1,5 +1,5 @@ -/mob/living/carbon/alien/hitby(atom/movable/AM) - ..(AM, 1) +/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush) + ..(AM, hitpush = 0) /*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other. As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble. diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index bc1e1e4f56d..cdd1b55bcf9 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -79,17 +79,18 @@ else //Maybe uses plasma in the future, although that wouldn't make any sense... leaping = 1 update_icons() - throw_at(A,MAX_ALIEN_LEAP_DIST,1) - leaping = 0 - update_icons() + throw_at(A, MAX_ALIEN_LEAP_DIST, 1, spin = 0, diagonals_first = 1, callback = CALLBACK(src, .leap_end)) + +/mob/living/carbon/alien/humanoid/hunter/proc/leap_end() + leaping = 0 + update_icons() /mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A) - if(!leaping) return ..() if(A) - if(istype(A, /mob/living)) + if(isliving(A)) var/mob/living/L = A var/blocked = 0 if(ishuman(A)) @@ -126,46 +127,3 @@ if(leaping) return ..() - - -//Modified throw_at() that will use diagonal dirs where appropriate -//instead of locking it to cardinal dirs -/mob/living/carbon/alien/humanoid/throw_at(atom/target, range, speed) - if(!target || !src) return 0 - - src.throwing = 1 - - var/dist_x = abs(target.x - src.x) - var/dist_y = abs(target.y - src.y) - var/dist_travelled = 0 - var/dist_since_sleep = 0 - - var/tdist_x = dist_x; - var/tdist_y = dist_y; - - if(dist_x <= dist_y) - tdist_x = dist_y; - tdist_y = dist_x; - - var/error = tdist_x/2 - tdist_y - while(target && (((((dist_x > dist_y) && ((src.x < target.x) || (src.x > target.x))) || ((dist_x <= dist_y) && ((src.y < target.y) || (src.y > target.y))) || (src.x > target.x)) && dist_travelled < range) || !has_gravity(src))) - - if(!src.throwing) break - if(!istype(src.loc, /turf)) break - - var/atom/step = get_step(src, get_dir(src,target)) - if(!step) - break - src.Move(step, get_dir(src, step)) - hit_check() - error += (error < 0) ? tdist_x : -tdist_y; - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - - - src.throwing = 0 - - return 1 diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index b99cde337d6..735b4ccb777 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -24,32 +24,6 @@ add_language("Hivemind") ..() -//This is fine, works the same as a human -/mob/living/carbon/alien/humanoid/Bump(atom/movable/AM as mob|obj, yes) - spawn( 0 ) - if((!( yes ) || now_pushing)) - return - now_pushing = 0 - ..() - if(!istype(AM, /atom/movable)) - return - - if(ismob(AM)) - var/mob/tmob = AM - tmob.LAssailant = src - - if(!now_pushing) - now_pushing = 1 - if(!AM.anchored) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window/full)) - for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = null - return - return /mob/living/carbon/alien/humanoid/movement_delay() var/tally = 0 diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index a78ab9c4c77..df00e862b38 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -26,38 +26,6 @@ ..() -//This is fine, works the same as a human -/mob/living/carbon/alien/larva/Bump(atom/movable/AM as mob|obj, yes) - - spawn( 0 ) - if((!( yes ) || now_pushing)) - return - now_pushing = 1 - if(ismob(AM)) - var/mob/tmob = AM - if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations)) - if(prob(70)) - to_chat(src, "You fail to push [tmob]'s fat ass out of the way.") - now_pushing = 0 - return - if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 - return - tmob.LAssailant = src - - now_pushing = 0 - ..() - if(!( istype(AM, /atom/movable) )) - return - if(!( now_pushing )) - now_pushing = 1 - if(!( AM.anchored )) - var/t = get_dir(src, AM) - step(AM, t) - now_pushing = null - return - return - //This needs to be fixed /mob/living/carbon/alien/larva/Stat() ..() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 5911c04f563..141d760287a 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -492,6 +492,29 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, //Throwing stuff +/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum) + . = ..() + var/hurt = TRUE + /*if(istype(throwingdatum, /datum/thrownthing)) + var/datum/thrownthing/D = throwingdatum + if(isrobot(D.thrower)) + var/mob/living/silicon/robot/R = D.thrower + if(!R.emagged) + hurt = FALSE*/ + if(hit_atom.density && isturf(hit_atom)) + if(hurt) + Weaken(1) + take_organ_damage(10) + if(iscarbon(hit_atom) && hit_atom != src) + var/mob/living/carbon/victim = hit_atom + if(hurt) + victim.take_organ_damage(10) + take_organ_damage(10) + victim.Weaken(1) + Weaken(1) + visible_message("[src] crashes into [victim], knocking them both over!", "You violently crash into [victim]!") + playsound(src, 'sound/weapons/punch1.ogg', 50, 1) + /mob/living/carbon/proc/toggle_throw_mode() if(in_throw_mode) throw_mode_off() diff --git a/code/modules/mob/living/carbon/carbon_defenses.dm b/code/modules/mob/living/carbon/carbon_defenses.dm index b2dcf20f1b2..1c911a3f661 100644 --- a/code/modules/mob/living/carbon/carbon_defenses.dm +++ b/code/modules/mob/living/carbon/carbon_defenses.dm @@ -1,13 +1,14 @@ -/mob/living/carbon/hitby(atom/movable/AM) - if(in_throw_mode && !get_active_hand()) //empty active hand and we're in throw mode - if(canmove && !restrained()) - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(isturf(I.loc)) - put_in_active_hand(I) - visible_message("[src] catches [I]!") - throw_mode_off() - return +/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0) + if(!skipcatch) + if(in_throw_mode && !get_active_hand()) //empty active hand and we're in throw mode + if(canmove && !restrained()) + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(isturf(I.loc)) + put_in_active_hand(I) + visible_message("[src] catches [I]!") + throw_mode_off() + return 1 ..() /mob/living/carbon/water_act(volume, temperature, source) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1c012655cdc..a82a6223b36 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -147,93 +147,6 @@ /mob/living/carbon/human/stok/New(var/new_loc) ..(new_loc, "Stok") -/mob/living/carbon/human/Bump(atom/movable/AM, yes) - if(!(yes) || now_pushing || buckled) - return 0 - now_pushing = 1 - if(ismob(AM)) - var/mob/tmob = AM - - //BubbleWrap - Should stop you pushing a restrained person out of the way - //i still don't get it, is this supposed to be 'bubblewrapping' or was it made by a guy named 'BubbleWrap' - if(ishuman(tmob)) - for(var/mob/M in range(tmob, 1)) - - if(tmob.pinned.len || (M.pulling == tmob && (tmob.restrained() && !M.restrained()) && M.stat == CONSCIOUS)) - if(!(world.time % 5)) //tmob is pinned to wall, or is restrained and pulled by a concious unrestrained human - to_chat(src, "[tmob] is restrained, you cannot push past.") - now_pushing = 0 - return 0 - - //I have to fucking document this somewhere- the above if(tmob.pinned.len || etc) check above had - //locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) at the end of it - //FIRST OF ALL, THAT IS NOT HOW YOU FUCKING USE LOCATE() - //SECOND OF ALL, OH GOD, WHY WOULD YOU EVER WANT GRABBED MOBS TO BE UNABLE TO BE PUSHED PAST GOD - - if(tmob.pulling == M && (M.restrained() && !tmob.restrained()) && tmob.stat == CONSCIOUS) - if(!(world.time % 5)) - to_chat(src, "[tmob] is restraining [M], you cannot push past.") - now_pushing = 0 - return 0 - - //Leaping mobs just land on the tile, no pushing, no anything. - if(status_flags & LEAPING) - loc = tmob.loc - status_flags &= ~LEAPING - now_pushing = 0 - return - - //BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller - //it might be 'bubblewrapping' given that this rhymes with 'hugboxing' - if((tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || restrained())) - if((canmove && tmob.canmove) && (!tmob.buckled && !tmob.buckled_mob)) - var/turf/oldloc = loc - loc = tmob.loc - tmob.loc = oldloc - now_pushing = 0 - for(var/mob/living/carbon/slime/slime in view(1,tmob)) - if(slime.Victim == tmob) - slime.UpdateFeed() - return - - if(ishuman(tmob) && (FAT in tmob.mutations)) - if(prob(40) && !(FAT in src.mutations)) - to_chat(src, "You fail to push [tmob]'s fat ass out of the way.") - now_pushing = 0 - return - - - //anti-riot equipment is also anti-push - if(tmob.r_hand && (prob(tmob.r_hand.block_chance * 2)) && !istype(tmob.r_hand, /obj/item/clothing)) - now_pushing = 0 - return - if(tmob.l_hand && (prob(tmob.l_hand.block_chance * 2)) && !istype(tmob.l_hand, /obj/item/clothing)) - now_pushing = 0 - return - - if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 - return - - tmob.LAssailant = src - - now_pushing = 0 - spawn(0) - ..() - if(!istype(AM, /atom/movable)) - return - if(!now_pushing) - now_pushing = 1 - - if(!AM.anchored) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window/full)) - for(var/obj/structure/window/win in get_step(AM, t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = 0 - /mob/living/carbon/human/Stat() ..() statpanel("Status") diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 3f25873e5f2..585ac587ffc 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -313,85 +313,37 @@ emp_act return 1*/ //this proc handles being hit by a thrown atom -/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = 5) +/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) + var/obj/item/I + var/throwpower = 30 if(istype(AM, /obj/item)) - var/obj/item/I = AM - - if(in_throw_mode && !get_active_hand() && speed <= 5) //empty active hand and we're in throw mode - if(canmove && !restrained()) - if(isturf(I.loc)) - put_in_active_hand(I) - visible_message("[src] catches [I]!") - throw_mode_off() - return - - var/zone = ran_zone("chest", 65) - var/dtype = BRUTE - if(istype(I, /obj/item/weapon)) - var/obj/item/weapon/W = I - dtype = W.damtype - var/throw_damage = I.throwforce*(speed/5) - - I.throwing = 0 //it hit, so stop moving - - if((I.thrower != src) && check_shields(throw_damage, "\the [I.name]", I, THROWN_PROJECTILE_ATTACK)) - return - - var/obj/item/organ/external/affecting = get_organ(zone) - if(!affecting) - var/missverb = (I.gender == PLURAL) ? "whizz" : "whizzes" - visible_message("\The [I] [missverb] past [src]'s missing [parse_zone(zone)]!", - "\The [I] [missverb] past your missing [parse_zone(zone)]!") - return - var/hit_area = affecting.name - - src.visible_message("[src] has been hit in the [hit_area] by [I].") - var/armor = run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].", I.armour_penetration) //I guess "melee" is the best fit here - - apply_damage(throw_damage, dtype, zone, armor, is_sharp(I), has_edge(I), I) - - if(ismob(I.thrower)) - var/mob/M = I.thrower - if(M) - add_logs(M, src, "hit", I, " (thrown)", print_attack_log = I.throwforce) - - //thrown weapon embedded object code. - if(dtype == BRUTE && istype(I)) - if(!I.is_robot_module()) + I = AM + throwpower = I.throwforce + if(I.thrownby == src) //No throwing stuff at yourself to trigger reactions + return ..() + if(check_shields(throwpower, "\the [AM.name]", AM, THROWN_PROJECTILE_ATTACK)) + hitpush = 0 + skipcatch = 1 + blocked = 1 + /*else if(I) + if(I.throw_speed >= EMBED_THROWSPEED_THRESHOLD) + if(!I.is_robot_module()) + var/armor = run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].", I.armour_penetration) //I guess "melee" is the best fit here var/sharp = is_sharp(I) - var/damage = throw_damage + var/damage = throwpower * (I.throw_speed / 5) if(armor) - damage /= armor+1 + damage /= armor + 1 //blunt objects should really not be embedding in things unless a huge amount of force is involved - var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3) - var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class + var/embed_chance = sharp? damage / I.w_class : damage/(I.w_class * 3) + var/embed_threshold = sharp? 5 * I.w_class : 15 * I.w_class //Sharp objects will always embed if they do enough damage. //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold - if(((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))) && (I.no_embed == 0)) - affecting.embed(I) - - // Begin BS12 momentum-transfer code. - if(I.throw_source && speed >= 15) - var/obj/item/weapon/W = I - var/momentum = speed/2 - var/dir = get_dir(I.throw_source, src) - - visible_message("[src] staggers under the impact!","You stagger under the impact!") - src.throw_at(get_edge_target_turf(src,dir),1,momentum) - - if(!W || !src) return - - if(W.loc == src && W.sharp) //Projectile is embedded and suitable for pinning. - var/turf/T = near_wall(dir,2) - - if(T) - src.loc = T - visible_message("[src] is pinned to the wall by [I]!","You are pinned to the wall by [I]!") - src.anchored = 1 - src.pinned += I + if(((sharp && prob(damage / (10 * I.w_class) * 100)) || (damage > embed_threshold && prob(embed_chance))) && (I.no_embed == 0)) + affecting.embed(I)*/ + return ..() /mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index cf66c1d2c15..c65e5f70f99 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,67 +1,75 @@ /mob/living/carbon/human/movement_delay() - var/tally = 0 + . = 0 + . += ..() - if(species.slowdown) - tally = species.slowdown + var/gravity = 1 if(!has_gravity(src)) - return -1 // It's hard to be slowed down in space by... anything + gravity = 0 - if(flying) return -1 + if(flying) + gravity = 0 if(embedded_flag) handle_embedded_objects() //Moving with objects stuck in you can cause bad times. if(slowed) - tally += 10 + . += 10 - var/health_deficiency = (maxHealth - health + staminaloss) - if(reagents) - for(var/datum/reagent/R in reagents.reagent_list) - if(R.shock_reduction) - health_deficiency -= R.shock_reduction - if(health_deficiency >= 40) - tally += (health_deficiency / 25) + if(gravity) + if(species.slowdown) + . = species.slowdown - var/hungry = (500 - nutrition)/5 // So overeat would be 100 and default level would be 80 - if(hungry >= 70) - tally += hungry/50 + var/health_deficiency = (maxHealth - health + staminaloss) + if(reagents) + for(var/datum/reagent/R in reagents.reagent_list) + if(R.shock_reduction) + health_deficiency -= R.shock_reduction + if(health_deficiency >= 40) + . += (health_deficiency / 25) - if(wear_suit) - tally += wear_suit.slowdown + var/hungry = (500 - nutrition)/5 // So overeat would be 100 and default level would be 80 + if(hungry >= 70) + . += hungry/50 - if(!buckled) - if(shoes) - tally += shoes.slowdown + if(wear_suit) + . += wear_suit.slowdown - if(shock_stage >= 10) tally += 3 + if(!buckled) + if(shoes) + . += shoes.slowdown - if(back) - tally += back.slowdown + if(shock_stage >= 10) + . += 3 - if(l_hand && (l_hand.flags & HANDSLOW)) - tally += l_hand.slowdown - if(r_hand && (r_hand.flags & HANDSLOW)) - tally += r_hand.slowdown + if(back) + . += back.slowdown - if(FAT in src.mutations) - tally += 1.5 - if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) - tally += (BODYTEMP_COLD_DAMAGE_LIMIT - bodytemperature) / COLD_SLOWDOWN_FACTOR + if(l_hand && (l_hand.flags & HANDSLOW)) + . += l_hand.slowdown + if(r_hand && (r_hand.flags & HANDSLOW)) + . += r_hand.slowdown - tally += 2*stance_damage //damaged/missing feet or legs is slow + if(FAT in src.mutations) + . += 1.5 - if(RUN in mutations) - tally = -1 - if(status_flags & IGNORESLOWDOWN) // make sure this is always at the end so we don't have ignore slowdown getting ignored itself - tally = -1 + if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) + . += (BODYTEMP_COLD_DAMAGE_LIMIT - bodytemperature) / COLD_SLOWDOWN_FACTOR - if(status_flags & GOTTAGOFAST) - tally -= 1 - if(status_flags & GOTTAGOREALLYFAST) - tally -= 2 + . += 2 * stance_damage //damaged/missing feet or legs is slow - return (tally + config.human_delay) + if(RUN in mutations) + . = -1 + + if(status_flags & IGNORESLOWDOWN) // make sure this is always at the end so we don't have ignore slowdown getting ignored itself + . = -1 + + if(status_flags & GOTTAGOFAST) + . -= 1 + if(status_flags & GOTTAGOREALLYFAST) + . -= 2 + + . += config.human_delay /mob/living/carbon/human/Process_Spacemove(movement_dir = 0) diff --git a/code/modules/mob/living/carbon/human/interactive/interactive.dm b/code/modules/mob/living/carbon/human/interactive/interactive.dm index a208752af24..9f27956b764 100644 --- a/code/modules/mob/living/carbon/human/interactive/interactive.dm +++ b/code/modules/mob/living/carbon/human/interactive/interactive.dm @@ -449,7 +449,7 @@ ..() retalTarget(user) -/mob/living/carbon/human/interactive/hitby(atom/movable/AM) +/mob/living/carbon/human/interactive/hitby(atom/movable/AM, skipcatch, hitpush, blocked) ..() var/mob/living/carbon/C = locate(/mob/living/carbon) in view(SNPC_MIN_RANGE_FIND, src) if(C) diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm index 003cd4a079e..779b5807236 100644 --- a/code/modules/mob/living/carbon/slime/slime.dm +++ b/code/modules/mob/living/carbon/slime/slime.dm @@ -103,59 +103,24 @@ return tally + config.slime_delay -/mob/living/carbon/slime/Bump(atom/movable/AM as mob|obj, yes) - if((!(yes) || now_pushing)) - return - now_pushing = 1 - - if(isobj(AM)) - if(!client && powerlevel > 0) - var/probab = 10 - switch(powerlevel) - if(1 to 2) probab = 20 - if(3 to 4) probab = 30 - if(5 to 6) probab = 40 - if(7 to 8) probab = 60 - if(9) probab = 70 - if(10) probab = 95 - if(prob(probab)) - if(istype(AM, /obj/structure/window) || istype(AM, /obj/structure/grille)) - if(nutrition <= get_hunger_nutrition() && !Atkcool) - if(is_adult || prob(5)) - AM.attack_slime(src) - spawn() - Atkcool = 1 - sleep(45) - Atkcool = 0 - - if(ismob(AM)) - var/mob/tmob = AM - - if(is_adult) - if(istype(tmob, /mob/living/carbon/human)) - if(prob(90)) - now_pushing = 0 - return - else - if(istype(tmob, /mob/living/carbon/human)) - now_pushing = 0 - return - - now_pushing = 0 - ..() - if(!istype(AM, /atom/movable)) - return - if(!( now_pushing )) - now_pushing = 1 - if(!( AM.anchored )) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window)) - if(AM:ini_dir == NORTHWEST || AM:ini_dir == NORTHEAST || AM:ini_dir == SOUTHWEST || AM:ini_dir == SOUTHEAST) - for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = null +/mob/living/carbon/slime/ObjBump(obj/O) + if(!client && powerlevel > 0) + var/chance = 10 + switch(powerlevel) + if(1 to 2) chance = 20 + if(3 to 4) chance = 30 + if(5 to 6) chance = 40 + if(7 to 8) chance = 60 + if(9) chance = 70 + if(10) chance = 95 + if(prob(chance)) + if(istype(O, /obj/structure/window) || istype(O, /obj/structure/grille)) + if(nutrition <= get_hunger_nutrition() && !Atkcool) + if(is_adult || prob(5)) + O.attack_slime(src) + Atkcool = 1 + spawn(45) + Atkcool = 0 /mob/living/carbon/slime/Process_Spacemove(var/movement_dir = 0) return 2 diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 525559e8e6c..b8a5284072d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -14,6 +14,121 @@ /mob/living/proc/OpenCraftingMenu() return +//Generic Bump(). Override MobBump() and ObjBump() instead of this. +/mob/living/Bump(atom/A, yes) + if(..()) //we are thrown onto something + return + if(buckled || !yes || now_pushing) + return + if(ismob(A)) + if(MobBump(A)) + return + if(isobj(A)) + if(ObjBump(A)) + return + if(istype(A, /atom/movable)) + if(PushAM(A)) + return + +//Called when we bump into a mob +/mob/living/proc/MobBump(mob/M) + //Even if we don't push/swap places, we "touched" them, so spread fire + spreadFire(M) + + if(now_pushing) + return 1 + + //Should stop you pushing a restrained person out of the way + if(isliving(M)) + var/mob/living/L = M + if(L.pulledby && L.pulledby != src && L.restrained()) + if(!(world.time % 5)) + to_chat(src, "[L] is restrained, you cannot push past.") + return 1 + + if(L.pulling) + if(ismob(L.pulling)) + var/mob/P = L.pulling + if(P.restrained()) + if(!(world.time % 5)) + to_chat(src, "[L] is restrained, you cannot push past.") + return 1 + + if(moving_diagonally) //no mob swap during diagonal moves. + return 1 + + if(!M.buckled && !M.has_buckled_mobs()) + var/mob_swap + //the puller can always swap with it's victim if on grab intent + if(M.pulledby == src && a_intent == I_GRAB) + mob_swap = 1 + //restrained people act if they were on 'help' intent to prevent a person being pulled from being seperated from their puller + else if((M.restrained() || M.a_intent == I_HELP) && (restrained() || a_intent == I_HELP)) + mob_swap = 1 + if(mob_swap) + //switch our position with M + if(loc && !loc.Adjacent(M.loc)) + return 1 + now_pushing = 1 + var/oldloc = loc + var/oldMloc = M.loc + + var/M_passmob = (M.pass_flags & PASSMOB) // we give PASSMOB to both mobs to avoid bumping other mobs during swap. + var/src_passmob = (pass_flags & PASSMOB) + M.pass_flags |= PASSMOB + pass_flags |= PASSMOB + + M.Move(oldloc) + Move(oldMloc) + + if(!src_passmob) + pass_flags &= ~PASSMOB + if(!M_passmob) + M.pass_flags &= ~PASSMOB + + now_pushing = 0 + return 1 + + // okay, so we didn't switch. but should we push? + // not if he's not CANPUSH of course + if(!(M.status_flags & CANPUSH)) + return 1 + //anti-riot equipment is also anti-push + if(M.r_hand && (prob(M.r_hand.block_chance * 2)) && !istype(M.r_hand, /obj/item/clothing)) + return 1 + if(M.l_hand && (prob(M.l_hand.block_chance * 2)) && !istype(M.l_hand, /obj/item/clothing)) + return 1 + +//Called when we bump into an obj +/mob/living/proc/ObjBump(obj/O) + return + +//Called when we want to push an atom/movable +/mob/living/proc/PushAM(atom/movable/AM) + if(now_pushing) + return 1 + if(moving_diagonally) // no pushing during diagonal moves + return 1 + if(!client && (mob_size < MOB_SIZE_SMALL)) + return + if(!AM.anchored) + now_pushing = 1 + var/t = get_dir(src, AM) + if(istype(AM, /obj/structure/window/full)) + for(var/obj/structure/window/win in get_step(AM, t)) + now_pushing = 0 + return + if(pulling == AM) + stop_pulling() + var/current_dir + if(isliving(AM)) + current_dir = AM.dir + step(AM, t) + if(current_dir) + AM.setDir(current_dir) + now_pushing = 0 + + /mob/living/Stat() . = ..() if(. && get_rig_stats) @@ -430,83 +545,47 @@ else return 0 + var/atom/movable/pullee = pulling + if(pullee && get_dist(src, pullee) > 1) + stop_pulling() + if(pullee && !isturf(pullee.loc) && pullee.loc != loc) + log_game("DEBUG: [src]'s pull on [pullee] was broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") + stop_pulling() if(restrained()) stop_pulling() - - var/t7 = 1 - if(restrained()) - for(var/mob/living/M in range(src, 1)) - if((M.pulling == src && M.stat == 0 && !( M.restrained() ))) - t7 = null - if(t7 && pulling && (get_dist(src, pulling) <= 1 || pulling.loc == loc)) - var/turf/T = loc - . = ..() - - if(pulling && pulling.loc) - if(!( isturf(pulling.loc) )) - stop_pulling() - return - else - if(Debug) - diary <<"pulling disappeared? at [__LINE__] in mob.dm - pulling = [pulling]" - diary <<"REPORT THIS" - - ///// - if(pulling && pulling.anchored) - stop_pulling() - return - - if(!restrained()) - var/diag = get_dir(src, pulling) - if((diag - 1) & diag) - else - diag = null - if((get_dist(src, pulling) > 1 || diag)) - if(isliving(pulling)) - var/mob/living/M = pulling - var/ok = 1 - if(locate(/obj/item/weapon/grab, M.grabbed_by)) - if(prob(75)) - var/obj/item/weapon/grab/G = pick(M.grabbed_by) - if(istype(G, /obj/item/weapon/grab)) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] has been pulled from []'s grip by []", G.affecting, G.assailant, src), 1) - //G = null - qdel(G) - else - ok = 0 - if(locate(/obj/item/weapon/grab, M.grabbed_by.len)) - ok = 0 - if(ok) - var/atom/movable/t = M.pulling - M.stop_pulling() - - if(M.lying && (prob(M.getBruteLoss() / 6))) - var/turf/location = M.loc - if(istype(location, /turf/simulated)) - location.add_blood(M) - pulling.Move(T, get_dir(pulling, T)) - if(M) - M.start_pulling(t) - else - if(pulling) - pulling.Move(T, get_dir(pulling, T)) - else - stop_pulling() - . = ..() - - if(s_active && !( s_active in contents ) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much. - s_active.close(src) - - if(.) // did we actually move? + var/turf/T = loc + . = ..() + if(.) handle_footstep(loc) step_count++ + if(pulling && pulling == pullee) // we were pulling a thing and didn't lose it during our move. + if(pulling.anchored) + stop_pulling() + return + + var/pull_dir = get_dir(src, pulling) + if(get_dist(src, pulling) > 1 || ((pull_dir - 1) & pull_dir)) // puller and pullee more than one tile away or in diagonal position + //if(isliving(pulling)) + //var/mob/living/M = pulling + //if(M.lying && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth))) + //M.makeTrail(T) + pulling.Move(T, get_dir(pulling, T)) // the pullee tries to reach our previous position + if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up + stop_pulling() + + if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //seperated from our puller and not in the middle of a diagonal move + pulledby.stop_pulling() + + if(s_active && !(s_active in contents) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much. + s_active.close(src) + if(update_slimes) for(var/mob/living/carbon/slime/M in view(1,src)) M.UpdateFeed(src) + /mob/living/proc/handle_footstep(turf/T) if(istype(T)) return 1 @@ -838,13 +917,25 @@ visible_message("[user] butchers [src].") gib() -/mob/living/movement_delay() - var/tally = 0 +/mob/living/movement_delay(ignorewalk = 0) + . = ..() + if(isturf(loc)) + var/turf/T = loc + . += T.slowdown if(slowed) - tally += 10 + . += 10 + if(ignorewalk) + . += config.run_speed + else + switch(m_intent) + if("run") + if(drowsyness > 0) + . += 6 + . += config.run_speed + if("walk") + . += config.walk_speed - return tally /mob/living/proc/can_use_guns(var/obj/item/weapon/gun/G) if(G.trigger_guard != TRIGGER_GUARD_ALLOW_ALL && !IsAdvancedToolUser() && !issmall(src)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index bdca022648e..3aec754eabb 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -52,7 +52,7 @@ if(P.dismemberment) check_projectile_dismemberment(P, def_zone) return P.on_hit(src, armor, def_zone) - + /mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) return 0 @@ -65,60 +65,47 @@ O.emp_act(severity) ..() +/obj/item/proc/get_volume_by_throwforce_and_or_w_class() + if(throwforce && w_class) + return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 + else if(w_class) + return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 + else + return 0 + //this proc handles being hit by a thrown atom -/mob/living/hitby(atom/movable/AM as mob|obj,var/speed = 5)//Standardization and logging -Sieve +/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0)//Standardization and logging -Sieve if(istype(AM, /obj/item)) var/obj/item/I = AM var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest - var/dtype = BRUTE + var/dtype = BRUTE + var/volume = I.get_volume_by_throwforce_and_or_w_class() if(istype(I, /obj/item/weapon)) var/obj/item/weapon/W = I dtype = W.damtype - if(W.hitsound && W.throwforce > 0) - playsound(loc, W.hitsound, 30, 1, -1) + if(W.throwforce > 0) //If the weapon's throwforce is greater than zero... + if(W.hitsound) //...and hitsound is defined... + playsound(loc, W.hitsound, volume, 1, -1) //...play the weapon's hitsound. + else //Otherwise, if hitsound isn't defined... + playsound(loc, 'sound/weapons/genhit1.ogg', volume, 1, -1) //...play genhit1.ogg. - //run to-hit check here + else if(I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero... + playsound(loc, 'sound/weapons/genhit1.ogg', volume, 1, -1)//...play genhit1.ogg + if(!I.throwforce)// Otherwise, if the item's throwforce is 0... + playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg. + if(!blocked) + visible_message("[src] has been hit by [I].", + "[src] has been hit by [I].") + var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) + apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), has_edge(I), I) + if(I.thrownby) + add_logs(I.thrownby, src, "hit", I) + else + return 1 + else + playsound(loc, 'sound/weapons/genhit1.ogg', 50, 1, -1) //...play genhit1.ogg.) + ..() - var/throw_damage = I.throwforce*(speed/5) - - src.visible_message("[src] has been hit by [I].") - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) - - apply_damage(throw_damage, dtype, zone, armor, is_sharp(I), has_edge(I), I) - - I.throwing = 0 //it hit, so stop moving - - if(ismob(I.thrower)) - var/mob/M = I.thrower - if(M) - create_attack_log("Has been hit with a [I], thrown by [key_name(M)]") - M.create_attack_log("Hit [key_name(src)] with a thrown [I]") - if(!istype(src,/mob/living/simple_animal/mouse)) - msg_admin_attack("[key_name_admin(src)] was hit by a [I], thrown by [key_name_admin(M)]") - - // Begin BS12 momentum-transfer code. - if(I.throw_source && speed >= 15) - var/obj/item/weapon/W = I - var/momentum = speed/2 - var/dir = get_dir(I.throw_source, src) - - visible_message("[src] staggers under the impact!","You stagger under the impact!") - src.throw_at(get_edge_target_turf(src,dir),1,momentum) - - if(!W || !src) return - - if(W.sharp) //Projectile is suitable for pinning. - //Handles embedding for non-humans and simple_animals. - I.loc = src - embedded += I - - var/turf/T = near_wall(dir,2) - - if(T) - src.loc = T - visible_message("[src] is pinned to the wall by [I]!","You are pinned to the wall by [I]!") - src.anchored = 1 - src.pinned += I /mob/living/mech_melee_attack(obj/mecha/M) if(M.occupant.a_intent == I_HARM) @@ -201,6 +188,24 @@ adjust_fire_stacks(3) IgniteMob() +//Share fire evenly between the two mobs +//Called in MobBump() and Crossed() +/mob/living/proc/spreadFire(mob/living/L) + if(!istype(L)) + return + var/L_old_on_fire = L.on_fire + + if(on_fire) //Only spread fire stacks if we're on fire + fire_stacks /= 2 + L.fire_stacks += fire_stacks + if(L.IgniteMob()) + log_game("[key_name(src)] bumped into [key_name(L)] and set them on fire") + + if(L_old_on_fire) //Only ignite us and gain their stacks if they were onfire before we bumped them + L.fire_stacks /= 2 + fire_stacks += L.fire_stacks + IgniteMob() + //Mobs on Fire end /mob/living/water_act(volume, temperature) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 5218d399007..202984fd14e 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -539,10 +539,10 @@ card.forceMove(card.loc) icon_state = "[chassis]" -/mob/living/silicon/pai/Bump(atom/movable/AM as mob|obj, yes) +/mob/living/silicon/pai/Bump() return -/mob/living/silicon/pai/Bumped(AM as mob|obj) +/mob/living/silicon/pai/Bumped() return /mob/living/silicon/pai/start_pulling(var/atom/movable/AM) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 4c52f2f415d..bbda7a05318 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -310,18 +310,15 @@ */ -/mob/living/silicon/robot/drone/Bump(atom/movable/AM as mob|obj, yes) - if(!yes || ( \ - !istype(AM,/obj/machinery/door) && \ - !istype(AM,/obj/machinery/recharge_station) && \ - !istype(AM,/obj/machinery/disposal/deliveryChute) && \ - !istype(AM,/obj/machinery/teleport/hub) && \ - !istype(AM,/obj/effect/portal) - )) return - ..() - return +/mob/living/silicon/robot/drone/Bump(atom/movable/AM, yes) + if(istype(AM, /obj/machinery/door) \ + || istype(AM, /obj/machinery/recharge_station) \ + || istype(AM, /obj/machinery/disposal/deliveryChute) \ + || istype(AM, /obj/machinery/teleport/hub) \ + || istype(AM, /obj/effect/portal)) + return ..() -/mob/living/silicon/robot/drone/Bumped(AM as mob|obj) +/mob/living/silicon/robot/drone/Bumped(atom/movable/AM) return /mob/living/silicon/robot/drone/start_pulling(var/atom/movable/AM) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 8ab82e7d9ca..9d93d0baa77 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -552,38 +552,6 @@ var/list/robot_verbs_default = list( return 2 -/mob/living/silicon/robot/Bump(atom/movable/AM as mob|obj, yes) - spawn( 0 ) - if((!( yes ) || now_pushing)) - return - now_pushing = 1 - if(ismob(AM)) - var/mob/tmob = AM - if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations)) - if(prob(20)) - to_chat(usr, "You fail to push [tmob]'s fat ass out of the way.") - now_pushing = 0 - return - if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 - return - now_pushing = 0 - ..() - if(!istype(AM, /atom/movable)) - return - if(!now_pushing) - now_pushing = 1 - if(!AM.anchored) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window/full)) - for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = null - return - return - /mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob, params) if(istype(W, /obj/item/weapon/restraints/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do return diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index d7435fdff5a..16b81582faa 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -195,31 +195,6 @@ src << browse(dat, "window=airoster") onclose(src, "airoster") -/mob/living/silicon/Bump(atom/movable/AM as mob|obj, yes) //Allows the AI to bump into mobs if it's itself pushed - if((!( yes ) || now_pushing)) - return - now_pushing = 1 - if(ismob(AM)) - var/mob/tmob = AM - if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 - return - now_pushing = 0 - ..() - if(!istype(AM, /atom/movable)) - return - if(!now_pushing) - now_pushing = 1 - if(!AM.anchored) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window)) - if(AM:ini_dir == NORTHWEST || AM:ini_dir == NORTHEAST || AM:ini_dir == SOUTHWEST || AM:ini_dir == SOUTHEAST) - for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = null - /mob/living/silicon/assess_threat() //Secbots won't hunt silicon units return -10 diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index de09cc11084..a77429eb631 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -218,8 +218,8 @@ Auto Patrol: []"}, /mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) if(istype(AM, /obj/item)) var/obj/item/I = AM - if(I.throwforce < src.health && I.thrower && (istype(I.thrower, /mob/living/carbon/human))) - var/mob/living/carbon/human/H = I.thrower + if(I.throwforce < src.health && I.thrownby && ishuman(I.thrownby)) + var/mob/living/carbon/human/H = I.thrownby retaliate(H) ..() diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index 86beb8f6bda..97702a28027 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -470,41 +470,6 @@ name = "Corgi meat" desc = "Tastes like... well you know..." -/mob/living/simple_animal/pet/corgi/Ian/Bump(atom/movable/AM as mob|obj, yes) - - spawn( 0 ) - if((!( yes ) || now_pushing)) - return - now_pushing = 1 - if(ismob(AM)) - var/mob/tmob = AM - if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations)) - if(prob(70)) - to_chat(src, "You fail to push [tmob]'s fat ass out of the way.") - now_pushing = 0 - return - if(!(tmob.status_flags & CANPUSH)) - now_pushing = 0 - return - - tmob.LAssailant = src - now_pushing = 0 - ..() - if(!( istype(AM, /atom/movable) )) - return - if(!( now_pushing )) - now_pushing = 1 - if(!( AM.anchored )) - var/t = get_dir(src, AM) - if(istype(AM, /obj/structure/window/full)) - for(var/obj/structure/window/win in get_step(AM,t)) - now_pushing = 0 - return - step(AM, t) - now_pushing = null - return - return - /mob/living/simple_animal/pet/corgi/regenerate_icons() overlays.Cut() if(inventory_head) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 85a351606f6..d6d1d92618d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -103,7 +103,7 @@ Difficulty: Hard triple_charge() else spawn(0) - warp_charge() + warp_charge() /mob/living/simple_animal/hostile/megafauna/bubblegum/New() ..() @@ -186,7 +186,7 @@ Difficulty: Hard shake_camera(L, 4, 3) shake_camera(src, 2, 3) var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src))) - L.throw_at_fast(throwtarget, 3) + L.throw_at(throwtarget, 3) charging = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm index 5afa7613c78..38f8c121d5d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm @@ -238,7 +238,7 @@ Difficulty: Medium if(L.loc == loc) throw_dir = pick(alldirs) var/throwtarget = get_edge_target_turf(src, throw_dir) - L.throw_at_fast(throwtarget, 3) + L.throw_at(throwtarget, 3) visible_message("[L] is thrown clear of [src]!") for(var/mob/M in range(7, src)) diff --git a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm index 64469bb87ef..b1393e84ef9 100644 --- a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm +++ b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm @@ -123,7 +123,6 @@ //Attempt to eat things we bump into, Mobs, Walls, Clowns /mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/obstacle) - attemptToEat(obstacle) //Attempt to eat things, only the head can eat diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index d1ffa073582..d9c20c09172 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -25,13 +25,12 @@ ts_spiderling_list -= src return ..() -/obj/structure/spider/spiderling/terror_spiderling/Bump(atom/A) - if(istype(A, /obj/structure/table)) - forceMove(A.loc) - else if(istype(A, /obj/machinery/recharge_station)) +/obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O) + if(istype(O, /obj/structure/table)) + forceMove(O.loc) + else if(istype(O, /obj/machinery/recharge_station)) qdel(src) - else - ..() + . = ..() /obj/structure/spider/spiderling/terror_spiderling/process() if(travelling_in_vent) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index caff5e2017f..0f85d78d01f 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -329,16 +329,17 @@ var/global/list/ts_spiderling_list = list() /mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_special_action() return -/mob/living/simple_animal/hostile/poison/terror_spider/Bump(atom/A) - if(istype(A, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/L = A +/mob/living/simple_animal/hostile/poison/terror_spider/ObjBump(obj/O) + if(istype(O, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/L = O if(L.density) - try_open_airlock(L) - if(istype(A, /obj/machinery/door/firedoor)) - var/obj/machinery/door/firedoor/F = A + return try_open_airlock(L) + if(istype(O, /obj/machinery/door/firedoor)) + var/obj/machinery/door/firedoor/F = O if(F.density && !F.welded) F.open() - ..() + return 1 + . = ..() /mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext) for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) @@ -362,6 +363,7 @@ var/global/list/ts_spiderling_list = list() to_chat(src, "The door is bolted shut.") else if(D.allowed(src)) D.open(1) + return 1 else if(D.arePowerSystemsOn() && (spider_opens_doors != 2)) to_chat(src, "The door's motors resist your efforts to force it.") else if(!spider_opens_doors) @@ -369,4 +371,5 @@ var/global/list/ts_spiderling_list = list() else visible_message("[src] pries open the door!") playsound(src.loc, "sparks", 100, 1) - D.open(1) \ No newline at end of file + D.open(1) + return 1 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 7c951cebbc9..bd6ed65a979 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -250,20 +250,6 @@ else if(bodytemperature > maxbodytemp) adjustBruteLoss(heat_damage_per_tick) -/mob/living/simple_animal/Bumped(AM as mob|obj) - if(!AM) return - - if(resting || buckled) - return - - if(isturf(src.loc)) - if((status_flags & CANPUSH) && ismob(AM)) - var/newamloc = src.loc - src.loc = AM:loc - AM:loc = newamloc - else - ..() - /mob/living/simple_animal/gib() if(icon_gib) flick(icon_gib, src) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index cef870811db..03ba9618bd4 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -15,21 +15,27 @@ 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/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/South() - ..() - - -/client/West() - ..() - - -/client/East() - ..() +/client/verb/moveleft() + set name = ".moveleft" + set instant = 1 + Move(get_step(mob, WEST), WEST) /client/Northeast() @@ -105,12 +111,11 @@ /client/verb/toggle_throw_mode() set hidden = 1 - if(!istype(mob, /mob/living/carbon)) - return - if(!mob.stat && isturf(mob.loc) && !mob.restrained()) - mob:toggle_throw_mode() + if(iscarbon(mob)) + var/mob/living/carbon/C = mob + C.toggle_throw_mode() else - return + to_chat(usr, "This mob type cannot throw items.") /client/verb/drop_item() @@ -134,16 +139,16 @@ /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.dir = direct + step(mob.control_object, direct) + if(!mob.control_object) + return + mob.control_object.setDir(direct) else - mob.control_object.forceMove(get_step(mob.control_object,direct)) + mob.control_object.forceMove(get_step(mob.control_object, direct)) return /client/Move(n, direct) - if(viewingCanvas) view = world.view //Reset the view winset(src, "mapwindow.map", "icon-size=[src.reset_stretch]") @@ -152,19 +157,28 @@ if(mob.hud_used) mob.hud_used.show_hud(HUD_STYLE_STANDARD) - if(mob.control_object) Move_object(direct) + if(world.time < move_delay) + return - if(world.time < move_delay) return + move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick + if(!mob || !mob.loc) + return 0 - if(!isliving(mob)) return mob.Move(n,direct) + if(mob.notransform) + return 0 //This is sota the goto stop mobs from moving var - if(moving) return 0 + if(mob.control_object) + return Move_object(direct) - if(!mob) return + if(!isliving(mob)) + return mob.Move(n, direct) - if(mob.stat==DEAD) return + if(mob.stat == DEAD) + mob.ghostize() + return 0 - if(mob.notransform) return//This is sota the goto stop mobs from moving var + if(moving) + return 0 if(isliving(mob)) var/mob/living/L = mob @@ -172,126 +186,105 @@ Process_Incorpmove(direct) return - if(Process_Grab()) return - - if(mob.buckled) //if we're buckled to something, tell it we moved. - return mob.buckled.relaymove(mob, direct) - - if(mob.remote_control) //we're controlling something, our movement is relayed to it + if(mob.remote_control) //we're controlling something, our movement is relayed to it return mob.remote_control.relaymove(mob, direct) if(isAI(mob)) if(istype(mob.loc, /obj/item/device/aicard)) var/obj/O = mob.loc - return O.relaymove(mob, direct) //aicards have special relaymove stuff - return AIMove(n,direct,mob) + return O.relaymove(mob, direct) // aicards have special relaymove stuff + return AIMove(n, direct, mob) + + + if(Process_Grab()) + return + + if(mob.buckled) //if we're buckled to something, tell it we moved. + return mob.buckled.relaymove(mob, direct) if(!mob.canmove) return - //if(istype(mob.loc, /turf/space) || (mob.flags & NOGRAV)) - // if(!mob.Process_Spacemove(0)) return 0 - if(!mob.lastarea) mob.lastarea = get_area(mob.loc) - if(isobj(mob.loc) || ismob(mob.loc))//Inside an object, tell it we moved + 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(istype(mob.get_active_hand(), /obj/item)) - var/obj/item/I = mob.get_active_hand() - I.moved(mob, n, direct) - + if(!mob.Process_Spacemove(direct)) - return 0 + return 0 - if(isturf(mob.loc)) - - if(mob.restrained())//Why being pulled while cuffed prevents you from moving - for(var/mob/M in range(mob, 1)) - if(M.pulling == mob) - if(!M.restrained() && M.stat == 0 && M.canmove && mob.Adjacent(M)) - to_chat(src, "You're restrained! You can't move!") - return 0 - else - M.stop_pulling() - - if(mob.pinned.len) - to_chat(src, "You're pinned to a wall by [mob.pinned[1]]!") - return 0 - - var/turf/T = mob.loc - move_delay = world.time//set move delay - move_delay += T.slowdown - mob.last_movement = world.time - switch(mob.m_intent) - if("run") - if(mob.drowsyness > 0) - move_delay += 6 - move_delay += 1+config.run_speed - if("walk") - move_delay += 1+config.walk_speed - move_delay += mob.movement_delay() - - if(config.Tickcomp) - move_delay -= 1.3 - var/tickcomp = ((1/(world.tick_lag))*1.3) - move_delay = move_delay + tickcomp - - //We are now going to move - moving = 1 - //Something with pulling things - if(locate(/obj/item/weapon/grab, mob)) - move_delay = max(move_delay, world.time + 7) - var/list/L = mob.ret_grab() - if(istype(L, /list)) - if(L.len == 2) - L -= mob - var/mob/M = L[1] - if(M) - if((get_dist(mob, M) <= 1 || M.loc == mob.loc)) - . = ..() - if(isturf(M.loc)) - var/diag = get_dir(mob, M) - if((diag - 1) & diag) - else - diag = null - if((get_dist(mob, M) > 1 || diag)) - step(M, get_dir(M.loc, T)) + if(mob.restrained()) // Why being pulled while cuffed prevents you from moving + for(var/mob/M in orange(1, mob)) + if(M.pulling == mob) + if(!M.incapacitated() && mob.Adjacent(M)) + to_chat(src, "You're restrained! You can't move!") + move_delay = world.time + 10 + return 0 else - for(var/mob/M in L) - M.other_mobs = 1 - if(mob != M) - M.animate_movement = 3 - for(var/mob/M in L) - spawn( 0 ) - step(M, direct) - return - spawn( 1 ) - M.other_mobs = null - M.animate_movement = 2 - return + M.stop_pulling() - else if(mob.confused) - step(mob, pick(cardinal)) - else - . = ..() - for(var/obj/item/weapon/grab/G in mob) - if(G.state == GRAB_NECK) - mob.setDir(reverse_dir[direct]) - G.adjust_position() - for(var/obj/item/weapon/grab/G in mob.grabbed_by) - G.adjust_position() + //We are now going to move + moving = 1 + move_delay = mob.movement_delay() + world.time + mob.last_movement = world.time + + if(locate(/obj/item/weapon/grab, mob)) + move_delay = max(move_delay, world.time + 7) + var/list/L = mob.ret_grab() + if(istype(L, /list)) + if(L.len == 2) + L -= mob + var/mob/M = L[1] + if(M) + if((get_dist(mob, M) <= 1 || M.loc == mob.loc)) + var/turf/prev_loc = mob.loc + . = ..() + if(M && isturf(M.loc)) // Mob may get deleted during parent call + var/diag = get_dir(mob, M) + if((diag - 1) & diag) + else + diag = null + if((get_dist(mob, M) > 1 || diag)) + step(M, get_dir(M.loc, prev_loc)) + else + for(var/mob/M in L) + M.other_mobs = 1 + if(mob != M) + M.animate_movement = 3 + for(var/mob/M in L) + spawn(0) + step(M, direct) + return + spawn(1) + M.other_mobs = null + M.animate_movement = 2 + return + + else if(mob.confused) + step(mob, pick(cardinal)) + else + . = ..() + + for(var/obj/item/weapon/grab/G in mob) + if(G.state == GRAB_NECK) + mob.setDir(reverse_dir[direct]) + G.adjust_position() + for(var/obj/item/weapon/grab/G in mob.grabbed_by) + G.adjust_position() + + moving = 0 + if(mob && .) + if(mob.throwing) + mob.throwing.finalize(FALSE) + + for(var/obj/O in mob) + O.on_mob_move(direct, mob) - moving = 0 - if(mob && .) - mob.throwing = 0 - return . - return ///Process_Grab() @@ -408,13 +401,13 @@ 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." + var/opposite_dir = turn(movement_dir, 180) + if(backup.newtonian_move(opposite_dir)) //You're pushing off something movable, so it moves + to_chat(src, "You push off of [backup] to propel yourself.") return 1 return 0 /mob/get_spacemove_backup() - var/atom/movable/dense_object_backup for(var/A in orange(1, get_turf(src))) if(isarea(A)) continue @@ -434,9 +427,7 @@ return AM if(pulling == AM) continue - dense_object_backup = AM - break - . = dense_object_backup + . = AM /mob/proc/mob_has_gravity(turf/T) diff --git a/code/modules/mob/pulling.dm b/code/modules/mob/pulling.dm index 6a6a66675b0..5a532ee1a42 100644 --- a/code/modules/mob/pulling.dm +++ b/code/modules/mob/pulling.dm @@ -15,7 +15,11 @@ return stop_pulling() - src.pulling = AM + if(AM.pulledby) + visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.") + AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. + + pulling = AM AM.pulledby = src if(pullin) pullin.update_icon(src) diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index 3ff11689078..d5208a01b8f 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -502,8 +502,3 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/process() if(current_app) current_app.program_process() - -/obj/item/device/pda/hit_check(speed) - if(current_app) - current_app.program_hit_check() - ..() \ No newline at end of file diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index 9b8e3225976..bec10f782ab 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -74,12 +74,6 @@ return scan_nearby() -/datum/data/pda/app/mob_hunter_game/program_hit_check() - if(!pda) - return - for(var/obj/effect/nanomob/hit_mob in get_turf(pda)) - hit_mob.hitby(pda) - /datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = 0) if(!captured) return 0 diff --git a/code/modules/projectiles/guns/throw.dm b/code/modules/projectiles/guns/throw.dm index f19069bc32f..0ea753f4cfa 100644 --- a/code/modules/projectiles/guns/throw.dm +++ b/code/modules/projectiles/guns/throw.dm @@ -16,10 +16,10 @@ return /obj/item/weapon/gun/throw/proc/get_throwrange() - return projectile_speed + return projectile_range /obj/item/weapon/gun/throw/proc/get_throwspeed() - return projectile_range + return projectile_speed /obj/item/weapon/gun/throw/proc/modify_projectile(obj/item/I, on_chamber = 0) return @@ -80,7 +80,7 @@ to_launch = null modify_projectile(I) playsound(user, fire_sound, 50, 1) - I.throw_at(target, get_throwrange(), get_throwspeed(), user, 1) + I.throw_at(target, get_throwrange(), get_throwspeed(), user, FALSE) message_admins("[key_name_admin(user)] fired \a [I] from a [src].") log_game("[key_name_admin(user)] used \a [src].") process_chamber() diff --git a/config/example/config.txt b/config/example/config.txt index 60702180691..5617b5ad039 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -206,9 +206,6 @@ ALLOW_HOLIDAYS ##Defines the ticklag for the world. 0.9 is the normal one, 0.5 is smoother. TICKLAG 0.5 -## Defines if Tick Compensation is used. It results in a minor slowdown of movement of all mobs, but attempts to result in a level movement speed across all ticks. Recommended if tickrate is lowered. -TICKCOMP 1 - ## Whether the server will talk to other processes through socket_talk SOCKET_TALK 0 diff --git a/config/example/game_options.txt b/config/example/game_options.txt index 5d82bfb425d..2934ce86726 100644 --- a/config/example/game_options.txt +++ b/config/example/game_options.txt @@ -38,17 +38,17 @@ REVIVAL_BRAIN_LIFE -1 ## We suggest editing these variabled in-game to find a good speed for your server. To do this you must be a high level admin. Open the 'debug' tab ingame. Select "Debug Controller" and then, in the popup, select "Configuration". These variables should have the same name. ## These values get directly added to values and totals in-game. To speed things up make the number negative, to slow things down, make the number positive. -## These modify the run/walk speed of all mobs before the mob-specific modifiers are applied. +## These modify the run/walk speed of all mobs before the mob-specific modifiers are applied. RUN_SPEED 1 WALK_SPEED 4 ## The variables below affect the movement of specific mob types. -HUMAN_DELAY 0 -ROBOT_DELAY 0 -MONKEY_DELAY 0 -ALIEN_DELAY 0 -METROID_DELAY 0 -ANIMAL_DELAY 0 +HUMAN_DELAY 1.5 +ROBOT_DELAY 1.5 +MONKEY_DELAY 1.5 +ALIEN_DELAY 1.5 +METROID_DELAY 1.5 +ANIMAL_DELAY 1.5 ## Comment for "normal" explosions, which ignore obstacles ## Uncomment for explosions that react to doors and walls diff --git a/interface/skin.dmf b/interface/skin.dmf index 6a6d616832f..c64da39f6e6 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -29,7 +29,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "AZRFF-CTRL+NORTH" name = "CTRL+NORTH" @@ -37,7 +37,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "AZRFF-CTRL+EAST" name = "CTRL+EAST" @@ -45,7 +45,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "AZRFF-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -53,7 +53,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "AZRFF-INSERT" name = "INSERT" @@ -85,7 +85,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "AZRFF-CTRL+B" name = "CTRL+B" @@ -105,7 +105,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-CTRL+Q+REP" name = "CTRL+Q+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "AZRFF-CTRL+R" name = "CTRL+R" @@ -113,7 +113,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "AZRFF-CTRL+W" name = "CTRL+W" @@ -129,7 +129,7 @@ macro "AZERTYoff" is-disabled = false elem "AZRFF-CTRL+Z+REP" name = "CTRL+Z+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "AZRFF-F1" name = "F1" @@ -215,7 +215,7 @@ macro "AZERTYon" is-disabled = false elem "AZRON-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "AZRON-CTRL+NORTH" name = "CTRL+NORTH" @@ -223,7 +223,7 @@ macro "AZERTYon" is-disabled = false elem "AZRON-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "AZRON-CTRL+EAST" name = "CTRL+EAST" @@ -231,7 +231,7 @@ macro "AZERTYon" is-disabled = false elem "AZRON-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "AZRON-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -239,7 +239,7 @@ macro "AZERTYon" is-disabled = false elem "AZRON-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "AZRON-INSERT" name = "INSERT" @@ -291,11 +291,11 @@ macro "AZERTYon" is-disabled = false elem "AZRON-D+REP" name = "D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "AZRON-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "AZRON-CTRL+B" name = "CTRL+B" @@ -335,11 +335,11 @@ macro "AZERTYon" is-disabled = false elem "AZRON-Q+REP" name = "Q+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "AZRON-CTRL+Q+REP" name = "CTRL+Q+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "AZRON-R" name = "R" @@ -351,11 +351,11 @@ macro "AZERTYon" is-disabled = false elem "s_key" name = "S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "AZRON-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "AZRON-T" name = "T" @@ -395,11 +395,11 @@ macro "AZERTYon" is-disabled = false elem "w_key" name = "Z+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "AZRON-CTRL+Z+REP" name = "CTRL+Z+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "AZRON-F1" name = "F1" @@ -481,7 +481,7 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "BRGHK-CTRL+NORTH" name = "CTRL+NORTH" @@ -489,7 +489,7 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "BRGHK-CTRL+EAST" name = "CTRL+EAST" @@ -497,7 +497,7 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "BRGHK-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -505,7 +505,7 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "BRGHK-INSERT" name = "INSERT" @@ -549,11 +549,11 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-A+REP" name = "A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "BRGHK-CTRL+A+REP" name = "CTRL+A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "BRGHK-B" name = "B" @@ -581,11 +581,11 @@ macro "borghotkeymode" is-disabled = false elem "BRGHK-D+REP" name = "D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "BRGHK-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "BRGHK-F" name = "F" @@ -613,19 +613,19 @@ macro "borghotkeymode" is-disabled = false elem "s_key" name = "S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "BRGHK-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "w_key" name = "W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "BRGHK-CTRL+W+REP" name = "CTRL+W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "BRGHK-X" name = "X" @@ -735,7 +735,7 @@ macro "macro" is-disabled = false elem "MACRO-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "MACRO-CTRL+NORTH" name = "CTRL+NORTH" @@ -743,7 +743,7 @@ macro "macro" is-disabled = false elem "MACRO-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "MACRO-CTRL+EAST" name = "CTRL+EAST" @@ -751,7 +751,7 @@ macro "macro" is-disabled = false elem "MACRO-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "MACRO-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -759,7 +759,7 @@ macro "macro" is-disabled = false elem "MACRO-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "MACRO-INSERT" name = "INSERT" @@ -787,11 +787,11 @@ macro "macro" is-disabled = false elem "MACRO-CTRL+A+REP" name = "CTRL+A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "MACRO-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "MACRO-CTRL+B" name = "CTRL+B" @@ -819,11 +819,11 @@ macro "macro" is-disabled = false elem "MACRO-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "MACRO-CTRL+W+REP" name = "CTRL+W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "MACRO-CTRL+X" name = "CTRL+X" @@ -921,7 +921,7 @@ macro "hotkeymode" is-disabled = false elem "HKMODE-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "HKMODE-CTRL+NORTH" name = "CTRL+NORTH" @@ -929,7 +929,7 @@ macro "hotkeymode" is-disabled = false elem "HKMODE-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "HKMODE-CTRL+EAST" name = "CTRL+EAST" @@ -937,7 +937,7 @@ macro "hotkeymode" is-disabled = false elem "HKMODE-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "HKMODE-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -945,7 +945,7 @@ macro "hotkeymode" is-disabled = false elem "HKMODE-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "HKMODE-INSERT" name = "INSERT" @@ -1001,19 +1001,19 @@ macro "hotkeymode" is-disabled = false elem "HKMODE-A+REP" name = "A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "HKMODE-CTRL+A+REP" name = "CTRL+A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "HKMODE-D+REP" name = "D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "HKMODE-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "HKMODE-B" name = "B" @@ -1073,11 +1073,11 @@ macro "hotkeymode" is-disabled = false elem "s_key" name = "S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "HKMODE-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "HKMODE-T" name = "T" @@ -1085,11 +1085,11 @@ macro "hotkeymode" is-disabled = false elem "w_key" name = "W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "HKMODE-CTRL+W+REP" name = "CTRL+W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "HKMODE-X" name = "X" @@ -1195,7 +1195,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-WEST+REP" name = "WEST+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "BRGMACRO-CTRL+NORTH" name = "CTRL+NORTH" @@ -1203,7 +1203,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-NORTH+REP" name = "NORTH+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "BRGMACRO-CTRL+EAST" name = "CTRL+EAST" @@ -1211,7 +1211,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-EAST+REP" name = "EAST+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "BRGMACRO-CTRL+SOUTH" name = "CTRL+SOUTH" @@ -1219,7 +1219,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-SOUTH+REP" name = "SOUTH+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "BRGMACRO-INSERT" name = "INSERT" @@ -1247,7 +1247,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-CTRL+A+REP" name = "CTRL+A+REP" - command = ".west" + command = ".moveleft" is-disabled = false elem "BRGMACRO-CTRL+B" name = "CTRL+B" @@ -1259,7 +1259,7 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-CTRL+D+REP" name = "CTRL+D+REP" - command = ".east" + command = ".moveright" is-disabled = false elem "BRGMACRO-CTRL+F" name = "CTRL+F" @@ -1275,11 +1275,11 @@ macro "borgmacro" is-disabled = false elem "BRGMACRO-CTRL+S+REP" name = "CTRL+S+REP" - command = ".south" + command = ".movedown" is-disabled = false elem "BRGMACRO-CTRL+W+REP" name = "CTRL+W+REP" - command = ".north" + command = ".moveup" is-disabled = false elem "BRGMACRO-CTRL+X" name = "CTRL+X" diff --git a/paradise.dme b/paradise.dme index 0230f89436b..ef365903003 100644 --- a/paradise.dme +++ b/paradise.dme @@ -19,6 +19,7 @@ #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\atmospherics.dm" #include "code\__DEFINES\bots.dm" +#include "code\__DEFINES\callbacks.dm" #include "code\__DEFINES\clothing.dm" #include "code\__DEFINES\combat.dm" #include "code\__DEFINES\construction.dm" @@ -188,7 +189,9 @@ #include "code\controllers\Processes\obj.dm" #include "code\controllers\Processes\pipenet.dm" #include "code\controllers\Processes\shuttles.dm" +#include "code\controllers\Processes\spacedrift.dm" #include "code\controllers\Processes\sun.dm" +#include "code\controllers\Processes\throwing.dm" #include "code\controllers\Processes\ticker.dm" #include "code\controllers\Processes\timer.dm" #include "code\controllers\Processes\weather.dm" @@ -199,6 +202,7 @@ #include "code\datums\ai_laws.dm" #include "code\datums\beam.dm" #include "code\datums\browser.dm" +#include "code\datums\callback.dm" #include "code\datums\cargoprofile.dm" #include "code\datums\computerfiles.dm" #include "code\datums\datacore.dm" diff --git a/sound/weapons/throwtap.ogg b/sound/weapons/throwtap.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7806a78981892ec6026a850bebbc51378ae12196 GIT binary patch literal 10411 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 a#67mbgUf1K{j%X_cZ0v(VSQ~3#Qkro7YuIz literal 0 HcmV?d00001