diff --git a/_maps/map_files/generic/CentComm.dmm b/_maps/map_files/generic/CentComm.dmm index f75b4f36459..dc3d3e31e5b 100644 --- a/_maps/map_files/generic/CentComm.dmm +++ b/_maps/map_files/generic/CentComm.dmm @@ -1765,7 +1765,7 @@ /turf/simulated/wall/indestructible/fakeglass, /area/centcom/specops) "aQh" = ( -/obj/structure/chair/wheelchair/bike{ +/obj/vehicle/motorcycle{ dir = 8 }, /obj/structure/sign/poster/contraband/random{ @@ -6811,7 +6811,7 @@ }, /area/centcom/zone3) "dxs" = ( -/obj/structure/chair/wheelchair/bike{ +/obj/vehicle/motorcycle{ dir = 1 }, /turf/simulated/floor/plasteel{ @@ -44128,7 +44128,7 @@ /area/centcom/supply) "vwk" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/chair/wheelchair/bike{ +/obj/vehicle/motorcycle{ dir = 1 }, /turf/simulated/floor/plasteel{ @@ -44637,7 +44637,7 @@ /turf/simulated/floor/plasteel, /area/shuttle/escape) "vLc" = ( -/obj/structure/chair/wheelchair/bike{ +/obj/vehicle/motorcycle{ dir = 8 }, /obj/effect/decal/cleanable/dirt, @@ -46609,7 +46609,7 @@ /turf/simulated/floor/mech_bay_recharge_floor, /area/centcom/specops) "wGg" = ( -/obj/structure/chair/wheelchair/bike{ +/obj/vehicle/motorcycle{ dir = 8 }, /turf/simulated/floor/wood{ diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 2bc7d6e1a18..b642392b7ea 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -93,3 +93,12 @@ }\ /datum/controller/subsystem/timer/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/timer/##X + +#define MOVEMENT_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/movement/##X);\ +/datum/controller/subsystem/movement/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/movement/##X/fire() {..() /*just so it shows up on the profiler*/} \ +/datum/controller/subsystem/movement/##X + diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index ebdc4666244..67c94710d71 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -70,14 +70,18 @@ #define TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(end_cooldown), cd_source, cd_index), cd_time)) -#define TIMER_COOLDOWN_CHECK(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index) +/// Checks if a timer based cooldown is NOT finished. +#define TIMER_COOLDOWN_RUNNING(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index) + +/// Checks if a timer based cooldown is finished. +#define TIMER_COOLDOWN_FINISHED(cd_source, cd_index) (!TIMER_COOLDOWN_RUNNING(cd_source, cd_index)) #define TIMER_COOLDOWN_END(cd_source, cd_index) LAZYREMOVE(cd_source.cooldowns, cd_index) /* * Stoppable timer cooldowns. * Use indexes the same as the regular tiemr cooldowns. - * They make use of the TIMER_COOLDOWN_CHECK() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one. + * They make use of the TIMER_COOLDOWN_RUNNING() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one. * A bit more expensive than the regular timers, but can be reset before they end and the time left can be checked. */ @@ -85,7 +89,7 @@ #define S_TIMER_COOLDOWN_RESET(cd_source, cd_index) reset_cooldown(cd_source, cd_index) -#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_CHECK(cd_source, cd_index))) +#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_RUNNING(cd_source, cd_index))) /* @@ -100,8 +104,10 @@ #define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + (cd_time)) //Returns true if the cooldown has run its course, false otherwise -#define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index < world.time) +#define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index <= world.time) #define COOLDOWN_RESET(cd_source, cd_index) cd_source.cd_index = 0 +#define COOLDOWN_STARTED(cd_source, cd_index) (cd_source.cd_index != 0) + #define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time)) diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 106788f6eb7..78948f5bef3 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -61,6 +61,17 @@ #define COMSIG_ATOM_ATTACK "atom_attack" ///called when the atom sucessfully has it's density var changed, from base atom/set_density(): (value) #define COMSIG_ATOM_SET_DENSITY "atom_set_density" +///from base of atom/experience_pressure_difference(): (pressure_difference, direction, pressure_resistance_prob_delta) +#define COMSIG_ATOM_PRE_PRESSURE_PUSH "atom_pre_pressure_push" + ///prevents pressure movement + #define COMSIG_ATOM_BLOCKS_PRESSURE (1<<0) +///signal sent out by an atom when it checks if it can be pulled, for additional checks +#define COMSIG_ATOM_CAN_BE_PULLED "movable_can_be_pulled" + #define COMSIG_ATOM_CANT_PULL (1 << 0) +///signal sent out by an atom when it is no longer being pulled by something else : (atom/puller) +#define COMSIG_ATOM_NO_LONGER_PULLED "movable_no_longer_pulled" +///signal sent out by an atom when it is no longer pulling something : (atom/pulling) +#define COMSIG_ATOM_NO_LONGER_PULLING "movable_no_longer_pulling" ///from base of atom/attackby(): (/obj/item, /mob/living, params) #define COMSIG_PARENT_ATTACKBY "atom_attackby" @@ -220,10 +231,7 @@ #define ZIMPACT_NO_MESSAGE (1<<1) /// Do not do the spin animation when landing #define ZIMPACT_NO_SPIN (1<<2) -///from base of atom/movable/experience_pressure_difference(): (pressure_difference, direction, pressure_resistance_prob_delta) -#define COMSIG_ATOM_PRE_PRESSURE_PUSH "atom_pre_pressure_push" - ///prevents pressure movement - #define COMSIG_ATOM_BLOCKS_PRESSURE (1<<0) + ///////////////// ///from base of area/Entered(): (/area) @@ -327,6 +335,22 @@ #define COMSIG_MOVABLE_DISPOSING "movable_disposing" ///called when the movable is removed from a disposal holder object: /obj/structure/disposalpipe/proc/expel(): (obj/structure/disposalholder/H, turf/T, direction) #define COMSIG_MOVABLE_EXIT_DISPOSALS "movable_exit_disposals" +///From base of /datum/move_loop/process() after attempting to move a movable: (datum/move_loop/loop, old_dir) +#define COMSIG_MOVABLE_MOVED_FROM_LOOP "movable_moved_from_loop" +///called when the movable's glide size is updated: (new_glide_size) +#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" +/// from base of atom/movable/Process_Spacemove(): (movement_dir, continuous_move) +#define COMSIG_MOVABLE_SPACEMOVE "spacemove" + #define COMSIG_MOVABLE_STOP_SPACEMOVE (1<<0) +///from base of atom/movable/newtonian_move(): (inertia_direction, start_delay) +#define COMSIG_MOVABLE_NEWTONIAN_MOVE "movable_newtonian_move" + #define COMPONENT_MOVABLE_NEWTONIAN_BLOCK (1<<0) +///from datum/component/drift/apply_initial_visuals(): () +#define COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT "movable_drift_visual_attempt" + #define DRIFT_VISUAL_FAILED (1<<0) +///from datum/component/drift/allow_final_movement(): () +#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input" + #define DRIFT_ALLOW_INPUT (1<<0) // /datum/mind signals @@ -546,6 +570,8 @@ #define MOVE_ARG_NEW_LOC 1 /// The arugment of move_args which dictates our movement direction #define MOVE_ARG_DIRECTION 2 +/// From base of /client/Move(): (direction, old_dir) +#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved" /// From base of /client/Move(): (list/move_args) #define COMSIG_MOB_CLIENT_PRE_LIVING_MOVE "mob_client_pre_living_move" @@ -1047,6 +1073,21 @@ /// Sent from /proc/do_after once a do_after action completes, whether via the bar filling or via interruption. #define COMSIG_DO_AFTER_ENDED "mob_do_after_ended" + // HUD: /// Sent from /datum/hud/proc/eye_z_changed() : (old_offset, new_offset) #define COMSIG_HUD_OFFSET_CHANGED "hud_offset_changed" + + +///from [/datum/move_loop/start_loop] (): +#define COMSIG_MOVELOOP_START "moveloop_start" +///from [/datum/move_loop/stop_loop] (): +#define COMSIG_MOVELOOP_STOP "moveloop_stop" +///from [/datum/move_loop/process] (): +#define COMSIG_MOVELOOP_PREPROCESS_CHECK "moveloop_preprocess_check" + #define MOVELOOP_SKIP_STEP (1<<0) +///from [/datum/move_loop/process] (succeeded, visual_delay): +#define COMSIG_MOVELOOP_POSTPROCESS "moveloop_postprocess" +//from [/datum/move_loop/has_target/jps/recalculate_path] (): +#define COMSIG_MOVELOOP_JPS_REPATH "moveloop_jps_repath" + diff --git a/code/__DEFINES/dcs/signals_object.dm b/code/__DEFINES/dcs/signals_object.dm new file mode 100644 index 00000000000..2f403818a11 --- /dev/null +++ b/code/__DEFINES/dcs/signals_object.dm @@ -0,0 +1,8 @@ +// Jetpack things + +//called in /obj/item/tank/jetpack/proc/turn_on() : () +#define COMSIG_JETPACK_ACTIVATED "jetpack_activated" + #define JETPACK_ACTIVATION_FAILED (1<<0) +//called in /obj/item/tank/jetpack/proc/turn_off() : () +#define COMSIG_JETPACK_DEACTIVATED "jetpack_deactivated" + diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index e273f72e9f8..f1322a4b7d3 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -2,7 +2,6 @@ #define PI 3.1415 #define INFINITY 1e31 //closer than enough -#define SQRT_2 1.41421356237 #define SHORT_REAL_LIMIT 16777216 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 4cb8bdc406a..f2ee29d5629 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -393,3 +393,4 @@ // Return values for [/mob/living/proc/handle_ventcrawl()] #define VENTCRAWL_IN_SUCCESS 1 #define VENTCRAWL_OUT_SUCCESS 2 + diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index 651a7be5df3..4b5dbabb7ba 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -1,3 +1,74 @@ +/// The minimum for glide_size to be clamped to. +#define MIN_GLIDE_SIZE 1 +/// The maximum for glide_size to be clamped to. +/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case. +#define MAX_GLIDE_SIZE 32 + +/// Compensating for time dilation +GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) + +///Broken down, here's what this does: +/// divides the world icon_size (32) by delay divided by ticklag to get the number of pixels something should be moving each tick. +/// The division result is given a min value of 1 to prevent obscenely slow glide sizes from being set +/// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave. +/// The whole result is then clamped to within the range above. +/// Not very readable but it works +#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE)) + +///Similar to DELAY_TO_GLIDE_SIZE, except without the clamping, and it supports piping in an unrelated scalar +#define MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, movement_disparity) (world.icon_size / ((delay) / world.tick_lag) * movement_disparity * GLOB.glide_size_multiplier) + +/// Highest value of var/move_to_delay to allow gliding, looks silly otherwise. +/// This number is arbitary and a subject to change. +#define MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE 16 + +/// Above this multiplicative slowdown simplemobs will stop gliding, cause it looks pretty bad otherwise.. +#define END_GLIDE_SPEED 10 + +//Movement loop priority. Only one loop can run at a time, this dictates that +// Higher numbers beat lower numbers +///Standard, go lower then this if you want to override, higher otherwise +#define MOVEMENT_DEFAULT_PRIORITY 10 +///Very few things should override this +#define MOVEMENT_SPACE_PRIORITY 100 +///Higher then the heavens +#define MOVEMENT_ABOVE_SPACE_PRIORITY (MOVEMENT_SPACE_PRIORITY + 1) + +//Movement loop flags +///Should the loop act immediately following its addition? +#define MOVEMENT_LOOP_START_FAST (1<<0) +///Do we not use the priority system? +#define MOVEMENT_LOOP_IGNORE_PRIORITY (1<<1) +///Should we override the loop's glide? +#define MOVEMENT_LOOP_IGNORE_GLIDE (1<<2) +///Should we not update our movables dir on move? +#define MOVEMENT_LOOP_NO_DIR_UPDATE (1<<3) +///Is the loop moving the movable outside its control, like it's an external force? e.g. footsteps won't play if enabled. +#define MOVEMENT_LOOP_OUTSIDE_CONTROL (1<<4) + +// Movement loop status flags +/// Has the loop been paused, soon to be resumed? +#define MOVELOOP_STATUS_PAUSED (1<<0) +/// Is the loop running? (Is true even when paused) +#define MOVELOOP_STATUS_RUNNING (1<<1) +/// Is the loop queued in a subsystem? +#define MOVELOOP_STATUS_QUEUED (1<<2) + +/** + * Returns a bitfield containing flags both present in `flags` arg and the `processing_move_loop_flags` move_packet variable. + * Has no use outside of procs called within the movement proc chain. + */ +#define CHECK_MOVE_LOOP_FLAGS(movable, flags) (movable.move_packet ? (movable.move_packet.processing_move_loop_flags & (flags)) : NONE) + +//Index defines for movement bucket data packets +#define MOVEMENT_BUCKET_TIME 1 +#define MOVEMENT_BUCKET_LIST 2 + +///Return values for moveloop Move() +#define MOVELOOP_FAILURE 0 +#define MOVELOOP_SUCCESS 1 +#define MOVELOOP_NOT_READY 2 + /** * currently_z_moving defines. Higher numbers mean higher priority. diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 26eaaece874..22fecbac261 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -76,6 +76,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Give us unsafe_unwrenching protection #define TRAIT_GUSTPROTECTION "gustprotection" +/// Unlinks gliding from movement speed, meaning that there will be a delay between movements rather than a single move movement between tiles +#define TRAIT_NO_GLIDE "no_glide" + /// Apply this to make a mob not dense, and remove it when you want it to no longer make them undense, other sorces of undesity will still apply. Always define a unique source when adding a new instance of this! #define TRAIT_UNDENSE "undense" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 52521378e8a..578ae4d3383 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -5,38 +5,6 @@ * Sorting */ -/* - * Misc - */ - - // binary search sorted insert -// IN: Object to be inserted -// LIST: List to insert object into -// TYPECONT: The typepath of the contents of the list -// COMPARE: The variable on the objects to compare -#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ - var/__BIN_CTTL = length(LIST);\ - if(!__BIN_CTTL) {\ - LIST += IN;\ - } else {\ - var/__BIN_LEFT = 1;\ - var/__BIN_RIGHT = __BIN_CTTL;\ - var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - var/##TYPECONT/__BIN_ITEM;\ - while(__BIN_LEFT < __BIN_RIGHT) {\ - __BIN_ITEM = LIST[__BIN_MID];\ - if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ - __BIN_LEFT = __BIN_MID + 1;\ - } else {\ - __BIN_RIGHT = __BIN_MID;\ - };\ - __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - };\ - __BIN_ITEM = LIST[__BIN_MID];\ - __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ - LIST.Insert(__BIN_MID, IN);\ - } - /// Passed into BINARY_INSERT to compare keys #define COMPARE_KEY __BIN_LIST[__BIN_MID] /// Passed into BINARY_INSERT to compare values @@ -51,7 +19,7 @@ * COMPARISON: The variable on the objects to compare * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE. */ -#define BINARY_INSERT_TG(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ +#define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ do {\ var/list/__BIN_LIST = LIST;\ var/__BIN_CTTL = length(__BIN_LIST);\ @@ -78,6 +46,45 @@ } while(FALSE) +#define SORT_FIRST_INDEX(list) (list[1]) +#define SORT_COMPARE_DIRECTLY(thing) (thing) +#define SORT_VAR_NO_TYPE(varname) var/varname +/**** + * Even more custom binary search sorted insert, using defines instead of vars + * INPUT: Item to be inserted + * LIST: List to insert INPUT into + * TYPECONT: A define setting the var to the typepath of the contents of the list + * COMPARE: The item to compare against, usualy the same as INPUT + * COMPARISON: A define that takes an item to compare as input, and returns their comparable value + * COMPTYPE: How should the list be compared? Either COMPARE_KEY or COMPARE_VALUE. + */ +#define BINARY_INSERT_DEFINE(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ + do {\ + var/list/__BIN_LIST = LIST;\ + var/__BIN_CTTL = length(__BIN_LIST);\ + if(!__BIN_CTTL) {\ + __BIN_LIST += INPUT;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + ##TYPECONT(__BIN_ITEM);\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = COMPTYPE;\ + if(##COMPARISON(__BIN_ITEM) <= ##COMPARISON(COMPARE)) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = COMPTYPE;\ + __BIN_MID = ##COMPARISON(__BIN_ITEM) > ##COMPARISON(COMPARE) ? __BIN_MID : __BIN_MID + 1;\ + __BIN_LIST.Insert(__BIN_MID, INPUT);\ + };\ + } while(FALSE) + + //Returns a list in plain english as a string /proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) var/total = input.len diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index a86e0c8f9dc..de5c9db690a 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -320,7 +320,7 @@ var/atom/target_loc = target?.loc var/drifting = FALSE - if(!user.Process_Spacemove(NONE) && user.inertia_dir) + if(SSmove_manager.processing_on(user, SSspacedrift)) drifting = TRUE var/holding = user.get_active_hand() @@ -350,7 +350,7 @@ . = FALSE break - if(drifting && (!(timed_action_flags & IGNORE_SPACE_DRIFT) || !user.inertia_dir)) + if(drifting && (!(timed_action_flags & IGNORE_SPACE_DRIFT) || !SSmove_manager.processing_on(user, SSspacedrift))) drifting = FALSE user_loc = user.loc diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index be16118ee11..1d21ab82880 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -2145,3 +2145,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) return ITEM_SLOT_LEGCUFFED_STRING if(ITEM_SLOT_ACCESSORY) return ITEM_SLOT_ACCESSORY_STRING + + +/proc/return_typenames(type) + return splittext("[type]", "/") diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 67b7015c9e6..4182c187706 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -47,6 +47,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_LASEREYES" = TRAIT_LASEREYES, "TRAIT_MUTE" = TRAIT_MUTE, "TRAIT_NEGATES_GRAVITY" = TRAIT_NEGATES_GRAVITY, + "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE, "TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL, "TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE, "TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE, diff --git a/code/controllers/subsystem/movement/move_handler.dm b/code/controllers/subsystem/movement/move_handler.dm new file mode 100644 index 00000000000..13c1225c47b --- /dev/null +++ b/code/controllers/subsystem/movement/move_handler.dm @@ -0,0 +1,187 @@ +/** + * Acts as a namespace for movement packet/type related procs + * + * Exists to provide an in code implementation of movement looping + * Replaces things like walk() or walk_to(), among others + * + * Because we're doing things in engine, we have a lot more control over how different operations are performed + * We also get more say in when things happen, so we can subject movements to the whims of the master controller + * Rather then using a fuck ton of cpu just moving mobs or meteors + * + * The goal is to keep the loops themselves reasonably barebone, and implement more advanced behavior and control via the signals + * + * This may be bypassed in cases where snowflakes are nessesary, or where performance is important. S not a hard and fast thing + * + * Every atom can have a movement packet, which contains information and behavior about currently active loops, and queuing info + * Loops control how movement actually happens. So there's a "move in this direction" loop, a "move randomly" loop + * + * You can find the logic for this control in this file + * + * Specifics of how different loops operate can be found in the movement_types.dm file, alongside the [add to loop][/datum/controller/subsystem/move_manager/proc/add_to_loop] helper procs that use them + * +**/ +SUBSYSTEM_DEF(move_manager) + name = "Movement Handler" + flags = SS_NO_INIT|SS_NO_FIRE + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + offline_implications = "Move loops for movables are no longer available. No immediate action is needed." + ss_id = "move_manager" + + +///Adds a movable thing to a movement subsystem. Returns TRUE if it all worked, FALSE if it failed somehow +/datum/controller/subsystem/move_manager/proc/add_to_loop(atom/movable/thing_to_add, datum/controller/subsystem/movement/subsystem = SSmovement, datum/move_loop/loop_type, priority = MOVEMENT_DEFAULT_PRIORITY, flags, datum/extra_info) + var/datum/movement_packet/our_data = thing_to_add.move_packet + if(!our_data) + our_data = new(thing_to_add) + + var/list/arguments = args.Copy(2) //Drop the atom, since the movement packet already knows about it + return our_data.add_loop(arglist(arguments)) + + +///Returns the subsystem's loop if we're processing on it, null otherwise +/datum/controller/subsystem/move_manager/proc/processing_on(atom/movable/packet_owner, datum/controller/subsystem/movement/subsystem) + var/datum/movement_packet/packet = packet_owner.move_packet + if(!packet) + return + var/datum/move_loop/linked_loop = packet.existing_loops[subsystem] + if(!linked_loop) + return + if(linked_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) + return linked_loop + if(linked_loop != packet.running_loop) + return + return linked_loop + + +///A packet of information that describes the current state of a moving object +/datum/movement_packet + ///Our parent atom + var/atom/movable/parent + ///The move loop that's currently running, excluding those that ignore priority. + var/datum/move_loop/running_loop + /** + * Flags passed from the move loop before it calls move() and unset right after. + * Allows for properties of a move loop to be easily checked by mechanics outside of it. + * Having this a bitfield rather than a type var means we don't get screwed over + * if the move loop gets deleted mid-move, FYI. + */ + var/processing_move_loop_flags = NONE + ///Assoc list of subsystems -> loop datum. Only one datum is allowed per subsystem + var/list/existing_loops = list() + + +/datum/movement_packet/New(atom/movable/parent) + src.parent = parent + parent.move_packet = src + + +/datum/movement_packet/Destroy(force) + parent.move_packet = null + parent = null + for(var/datum/controller/subsystem/processor as anything in existing_loops) + var/datum/move_loop/loop = existing_loops[processor] + if(QDELETED(loop)) + continue + qdel(loop) + existing_loops.Cut() + existing_loops = null //Catch anyone modifying this post del + return ..() + + +///Adds a loop to our parent. Returns the created loop if a success, null otherwise +/datum/movement_packet/proc/add_loop(datum/controller/subsystem/movement/subsystem, datum/move_loop/loop_type, priority, flags, datum/extra_info) + var/datum/move_loop/existing_loop = existing_loops[subsystem] + + if(existing_loop && existing_loop.priority > priority) + if(!(existing_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) && !(flags & MOVEMENT_LOOP_IGNORE_PRIORITY)) + return //Give up + + if(existing_loop?.compare_loops(arglist(args.Copy(2)))) + return //it already exists stop trying to make the same moveloop + + var/datum/move_loop/new_loop = new loop_type(src, subsystem, parent, priority, flags, extra_info) //Pass the mob to move and ourselves in via new + var/list/arguments = args.Copy(6) //Just send the args we've not already dealt with + + var/worked_out = new_loop.setup(arglist(arguments)) //Here goes the rest + if(!worked_out) + qdel(new_loop) + return + + existing_loops[subsystem] = new_loop + if(existing_loop) + qdel(existing_loop) //We need to do this here because otherwise the packet would think it was empty, and self destruct + contest_running_loop(new_loop) + return new_loop + + +///Attempts to contest the current running move loop. Returns TRUE if the loop is active, FALSE otherwise +/datum/movement_packet/proc/contest_running_loop(datum/move_loop/contestant) + var/datum/controller/subsystem/movement/contesting_subsystem = contestant.controller + + if(contestant.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) + contesting_subsystem.add_loop(contestant) + return TRUE + if(!running_loop) + running_loop = contestant + contesting_subsystem.add_loop(running_loop) + return TRUE + if(running_loop.priority > contestant.priority) + return FALSE + + var/datum/controller/subsystem/movement/current_subsystem = running_loop.controller + + var/current_running_loop = running_loop + running_loop = contestant + current_subsystem.remove_loop(current_running_loop) + if(running_loop != contestant) // A signal registrant could have messed with things + return FALSE + contesting_subsystem.add_loop(contestant) + return TRUE + + +///Tries to figure out the current favorite loop to run. More complex then just deciding between two different loops, assumes no running loop currently exists +/datum/movement_packet/proc/decide_on_running_loop() + if(running_loop) + return + if(!length(existing_loops)) //Die + qdel(src) + return + var/datum/move_loop/favorite + for(var/datum/controller/subsystem/movement/owner as anything in existing_loops) + var/datum/move_loop/checking = existing_loops[owner] + if(checking.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) + continue + if(favorite && favorite.priority > checking.priority) + continue + favorite = checking + + if(!favorite) //This isn't an error state, since some loops ignore the concept of a running loop + return + + var/datum/controller/subsystem/movement/favorite_subsystem = favorite.controller + + running_loop = favorite + favorite_subsystem.add_loop(running_loop) + + +/datum/movement_packet/proc/remove_loop(datum/controller/subsystem/movement/remove_from, datum/move_loop/loop_to_remove) + if(loop_to_remove == running_loop) + running_loop = null + remove_from.remove_loop(loop_to_remove) + if(loop_to_remove.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) + remove_from.remove_loop(loop_to_remove) + if(QDELETED(src)) + return + if(existing_loops[remove_from] == loop_to_remove) + existing_loops -= remove_from + decide_on_running_loop() + return + + +/datum/movement_packet/proc/remove_subsystem(datum/controller/subsystem/movement/remove) + var/datum/move_loop/our_loop = existing_loops[remove] + if(!our_loop) + return FALSE + qdel(our_loop) + return TRUE + diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm new file mode 100644 index 00000000000..6363d74cf66 --- /dev/null +++ b/code/controllers/subsystem/movement/movement.dm @@ -0,0 +1,145 @@ +SUBSYSTEM_DEF(movement) + name = "Movement Loops" + flags = SS_NO_INIT|SS_BACKGROUND|SS_TICKER + wait = 1 //Fire each tick + offline_implications = "Move loops for movables are no longer available. No immediate action is needed." + cpu_display = SS_CPUDISPLAY_HIGH + ss_id = "move_manager" + /* + A breif aside about the bucketing system here + + The goal is to allow for higher loads of semi long delays while reducing cpu usage + Bucket insertion and management are much less complex then what you might see in SStimer + This is intentional, as we loop our delays much more often then that ss is designed for + We also have much shorter term timers, so we need to worry about redundant buckets much less + */ + ///Assoc list of "target time" -> list(things to process). Used for quick lookup + var/list/buckets = list() + ///Sorted list of list(target time, bucket to process) + var/list/sorted_buckets = list() + ///The time we started our last fire at + var/canonical_time = 0 + ///The visual delay of the subsystem + var/visual_delay = 1 + + +/datum/controller/subsystem/movement/get_stat_details() + var/total_len = 0 + for(var/list/bucket as anything in sorted_buckets) + total_len += length(bucket[MOVEMENT_BUCKET_LIST]) + return "B:[length(sorted_buckets)] E:[total_len]" + + +/datum/controller/subsystem/movement/Recover() + //Get ready this is gonna be horrible + //We need to do this to support subtypes by the by + var/list/typenames = return_typenames(src.type) + var/our_name = typenames[length(typenames)] //Get the last name in the list, IE the subsystem identifier + + var/datum/controller/subsystem/movement/old_version = global.vars["SS[our_name]"] + buckets = old_version.buckets + sorted_buckets = old_version.sorted_buckets + + +/datum/controller/subsystem/movement/fire(resumed) + if(!resumed) + canonical_time = world.time + + for(var/list/bucket_info as anything in sorted_buckets) + var/time = bucket_info[MOVEMENT_BUCKET_TIME] + if(time > canonical_time || MC_TICK_CHECK) + return + pour_bucket(bucket_info) + + +/// Processes a bucket of movement loops (This should only ever be called by fire(), it exists to prevent runtime fuckery) +/datum/controller/subsystem/movement/proc/pour_bucket(list/bucket_info) + var/list/processing = bucket_info[MOVEMENT_BUCKET_LIST] // Cache for lookup speed + while(processing.len) + var/datum/move_loop/loop = processing[processing.len] + processing.len-- + // No longer queued since we just got removed from the loop + loop.queued_time = null + loop.process() //This shouldn't get nulls, if it does, runtime + if(!QDELETED(loop) && loop.status & MOVELOOP_STATUS_QUEUED) //Re-Insert the loop + loop.status &= ~MOVELOOP_STATUS_QUEUED + loop.timer = world.time + loop.delay + queue_loop(loop) + if (MC_TICK_CHECK) + break + + if(length(processing)) + return // Still work to be done + var/bucket_time = bucket_info[MOVEMENT_BUCKET_TIME] + smash_bucket(1, bucket_time) // We assume we're the first bucket in the queue right now + visual_delay = MC_AVERAGE_FAST(visual_delay, max((world.time - canonical_time) / wait, 1)) + + +/// Removes a bucket from our system. You only need to pass in the time, but if you pass in the index of the list you save us some work +/datum/controller/subsystem/movement/proc/smash_bucket(index, bucket_time) + var/sorted_length = length(sorted_buckets) + if(!index) + index = sorted_length + 1 // let's setup the failure condition + for(var/i in 1 to sorted_length) + var/list/bucket_info = sorted_buckets[i] + if(bucket_info[MOVEMENT_BUCKET_TIME] != bucket_time) + continue + index = i + break + //This is technically possible, if our bucket is smashed inside the loop's process + //Let's be nice, the cost of doing it is cheap + if(index > sorted_length || !buckets["[bucket_time]"]) + return + + sorted_buckets.Cut(index, index + 1) //Removes just this list + //Removes the assoc lookup too + buckets -= "[bucket_time]" + + +/datum/controller/subsystem/movement/proc/queue_loop(datum/move_loop/loop) + if(loop.status & MOVELOOP_STATUS_QUEUED) + stack_trace("A move loop attempted to queue while already queued") + return + loop.queued_time = loop.timer + loop.status |= MOVELOOP_STATUS_QUEUED + var/list/our_bucket = buckets["[loop.queued_time]"] + // If there's no bucket for this, lets set them up + if(!our_bucket) + buckets["[loop.queued_time]"] = list() + our_bucket = buckets["[loop.queued_time]"] + // This makes assoc buckets and sorted buckets point to the same place, allowing for quicker inserts + var/list/new_bucket = list(list(loop.queued_time, our_bucket)) + var/list/compare_item = list(loop.queued_time) + BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, compare_item, SORT_FIRST_INDEX, COMPARE_KEY) + + our_bucket += loop + + +/datum/controller/subsystem/movement/proc/dequeue_loop(datum/move_loop/loop) + // Go home, you're not here anyway + if(!(loop.status & MOVELOOP_STATUS_QUEUED)) + return + if(isnull(loop.queued_time)) // This happens if a moveloop is dequeued while handling process() + loop.status &= ~MOVELOOP_STATUS_QUEUED + return + var/list/our_entries = buckets["[loop.queued_time]"] + our_entries -= loop + if(!length(our_entries)) + smash_bucket(bucket_time = loop.queued_time) // We can't pass an index in for context because we don't know our position + loop.queued_time = null + loop.status &= ~MOVELOOP_STATUS_QUEUED + + +/datum/controller/subsystem/movement/proc/add_loop(datum/move_loop/add) + if(add.status & MOVELOOP_STATUS_QUEUED) + CRASH("Loop being added that is already queued.") + add.loop_started() + if(QDELETED(add) || add.status & MOVELOOP_STATUS_QUEUED) + return + queue_loop(add) + + +/datum/controller/subsystem/movement/proc/remove_loop(datum/move_loop/remove) + dequeue_loop(remove) + remove.loop_stopped() + diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm new file mode 100644 index 00000000000..726f32557d8 --- /dev/null +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -0,0 +1,894 @@ +///Template class of the movement datums, handles the timing portion of the loops +/datum/move_loop + ///The movement packet that owns us + var/datum/movement_packet/owner + ///The subsystem we're processing on + var/datum/controller/subsystem/movement/controller + ///An extra reference we pass around + ///It is on occasion useful to have a reference to some datum without storing it on the moving object + ///Mostly comes up in high performance senarios where we care about things being singletons + ///This feels horrible, but constantly making components seems worse + var/datum/extra_info + ///The thing we're moving about + var/atom/movable/moving + ///Defines how different move loops override each other. Higher numbers beat lower numbers + var/priority = MOVEMENT_DEFAULT_PRIORITY + ///Bitfield of different things that affect how a loop operates, and other mechanics around it as well. + var/flags + ///Time till we stop processing in deci-seconds, defaults to forever + var/lifetime = INFINITY + ///Delay between each move in deci-seconds + var/delay = 1 + ///The next time we should process + ///Used primarially as a hint to be reasoned about by our [controller], and as the id of our bucket + var/timer = 0 + ///The time we are CURRENTLY queued for processing + ///Do not modify this directly + var/queued_time = -1 + /// Status bitfield for what state the move loop is currently in + var/status = NONE + + +/datum/move_loop/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) + src.owner = owner + src.controller = controller + src.extra_info = extra_info + if(extra_info) + RegisterSignal(extra_info, COMSIG_PARENT_QDELETING, PROC_REF(info_deleted)) + src.moving = moving + src.priority = priority + src.flags = flags + + +/datum/move_loop/proc/setup(delay = 1, timeout = INFINITY) + if(!ismovable(moving) || !owner) + return FALSE + + src.delay = max(delay, world.tick_lag) //Please... + src.lifetime = timeout + return TRUE + + +///check if this exact moveloop datum already exists (in terms of vars) so we can avoid creating a new one to overwrite the old duplicate +/datum/move_loop/proc/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay = 1, timeout = INFINITY) + SHOULD_CALL_PARENT(TRUE) + if(loop_type == type && priority == src.priority && flags == src.flags && delay == src.delay && timeout == lifetime) + return TRUE + return FALSE + + +///Called when a loop is starting by a movement subsystem +/datum/move_loop/proc/loop_started() + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_MOVELOOP_START) + status |= MOVELOOP_STATUS_RUNNING + //If this is our first time starting to move with this loop + //And we're meant to start instantly + if(!timer && flags & MOVEMENT_LOOP_START_FAST) + timer = world.time + return + timer = world.time + delay + + +///Called when a loop is stopped, doesn't stop the loop itself +/datum/move_loop/proc/loop_stopped() + SHOULD_CALL_PARENT(TRUE) + status &= ~MOVELOOP_STATUS_RUNNING + SEND_SIGNAL(src, COMSIG_MOVELOOP_STOP) + + +/datum/move_loop/proc/info_deleted(datum/source) + SIGNAL_HANDLER + extra_info = null + + +/datum/move_loop/Destroy() + if(owner) + owner.remove_loop(controller, src) + owner = null + moving = null + controller = null + extra_info = null + return ..() + + +///Exists as a helper so outside code can modify delay in a sane way +/datum/move_loop/proc/set_delay(new_delay) + delay = max(new_delay, world.tick_lag) + + +///Pauses the move loop for some passed in period +///This functionally means shifting its timer up, and clearing it from its current bucket +/datum/move_loop/proc/pause_for(time) + if(!controller || !(status & MOVELOOP_STATUS_RUNNING)) //No controller or not running? go away + return + //Dequeue us from our current bucket + controller.dequeue_loop(src) + //Offset our timer + timer = world.time + time + //Now requeue us with our new target start time + controller.queue_loop(src) + + +/datum/move_loop/process() + if(isnull(controller)) + qdel(src) + return + + var/old_delay = delay //The signal can sometimes change delay + + if(SEND_SIGNAL(src, COMSIG_MOVELOOP_PREPROCESS_CHECK) & MOVELOOP_SKIP_STEP) //Chance for the object to react + return + + lifetime -= old_delay //This needs to be based on work over time, not just time passed + + if(lifetime < 0) //Otherwise lag would make things look really weird + qdel(src) + return + + var/visual_delay = controller.visual_delay + var/old_dir = moving.dir + var/old_loc = moving.loc + + owner?.processing_move_loop_flags = flags + var/result = move() //Result is an enum value. Enums defined in __DEFINES/movement.dm + if(moving) + var/direction = get_dir(old_loc, moving.loc) + SEND_SIGNAL(moving, COMSIG_MOVABLE_MOVED_FROM_LOOP, src, old_dir, direction) + owner?.processing_move_loop_flags = NONE + + SEND_SIGNAL(src, COMSIG_MOVELOOP_POSTPROCESS, result, delay * visual_delay) + + if(QDELETED(src) || result != MOVELOOP_SUCCESS) //Can happen + return + + if(flags & MOVEMENT_LOOP_IGNORE_GLIDE) + return + + moving.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, visual_delay)) + + +///Handles the actual move, overriden by children +///Returns FALSE if nothing happen, TRUE otherwise +/datum/move_loop/proc/move() + return MOVELOOP_FAILURE + + +///Pause our loop untill restarted with resume_loop() +/datum/move_loop/proc/pause_loop() + if(!controller || !(status & MOVELOOP_STATUS_RUNNING) || (status & MOVELOOP_STATUS_PAUSED)) //we dead + return + + //Dequeue us from our current bucket + controller.dequeue_loop(src) + status |= MOVELOOP_STATUS_PAUSED + + +///Resume our loop after being paused by pause_loop() +/datum/move_loop/proc/resume_loop() + if(!controller || (status & MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED) != (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED)) + return + + timer = world.time + controller.queue_loop(src) + status &= ~MOVELOOP_STATUS_PAUSED + + +///Removes the atom from some movement subsystem. Defaults to SSmovement +/datum/controller/subsystem/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement) + var/datum/movement_packet/our_info = moving.move_packet + if(!our_info) + return FALSE + return our_info.remove_subsystem(subsystem) + + +/** + * Replacement for walk() + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * direction - The direction we want to move in + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/move, priority, flags, extra_info, delay, timeout, direction) + +///Replacement for walk() +/datum/move_loop/move + var/direction + +/datum/move_loop/move/setup(delay, timeout, dir) + . = ..() + if(!.) + return + direction = dir + +/datum/move_loop/move/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, dir) + if(..() && direction == dir) + return TRUE + return FALSE + +/datum/move_loop/move/move() + var/atom/old_loc = moving.loc + moving.Move(get_step(moving, direction), direction, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + // We cannot rely on the return value of Move(), we care about teleports and it doesn't + // Moving also can be null on occasion, if the move deleted it and therefor us + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Like move(), but we don't care about collision at all + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * direction - The direction we want to move in + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/force_move_dir(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/move/force, priority, flags, extra_info, delay, timeout, direction) + +/datum/move_loop/move/force + +/datum/move_loop/move/force/move() + var/atom/old_loc = moving.loc + moving.forceMove(get_step(moving, direction)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/datum/move_loop/has_target + ///The thing we're moving in relation to, either at or away from + var/atom/target + +/datum/move_loop/has_target/setup(delay, timeout, atom/chasing) + . = ..() + if(!.) + return + if(!isatom(chasing)) + qdel(src) + return FALSE + + target = chasing + + if(!isturf(target)) + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(handle_no_target)) //Don't do this for turfs, because we don't care + +/datum/move_loop/has_target/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing) + if(..() && chasing == target) + return TRUE + return FALSE + +/datum/move_loop/has_target/Destroy() + target = null + return ..() + +/datum/move_loop/has_target/proc/handle_no_target() + SIGNAL_HANDLER + qdel(src) + + +/** + * Used for force-move loops, similar to move_towards_legacy() but not quite the same + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/force_move(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/has_target/force_move, priority, flags, extra_info, delay, timeout, chasing) + +///Used for force-move loops +/datum/move_loop/has_target/force_move + +/datum/move_loop/has_target/force_move/move() + var/atom/old_loc = moving.loc + moving.forceMove(get_step(moving, get_dir(moving, target))) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/* +/** + * Used for following jps defined paths. The proc signature here's a bit long, I'm sorry + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * repath_delay - How often we're allowed to recalculate our path + * max_path_length - The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite) + * miminum_distance - Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example + * access - A list representing what access we have and what doors we can open + * simulated_only - Whether we consider turfs without atmos simulation (AKA do we want to ignore space) + * avoid - If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf + * skip_first - Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break things + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/jps_move(moving, + chasing, + delay, + timeout, + repath_delay, + max_path_length, + minimum_distance, + list/access, + simulated_only, + turf/avoid, + skip_first, + subsystem, + diagonal_handling, + priority, + flags, + datum/extra_info, + initial_path) + return add_to_loop(moving, + subsystem, + /datum/move_loop/has_target/jps, + priority, + flags, + extra_info, + delay, + timeout, + chasing, + repath_delay, + max_path_length, + minimum_distance, + access, + simulated_only, + avoid, + skip_first, + diagonal_handling, + initial_path) + +/datum/move_loop/has_target/jps + ///How often we're allowed to recalculate our path + var/repath_delay + ///Max amount of steps to search + var/max_path_length + ///Minimum distance to the target before path returns + var/minimum_distance + ///A list representing what access we have and what doors we can open. + var/list/access + ///Whether we consider turfs without atmos simulation (AKA do we want to ignore space) + var/simulated_only + ///A perticular turf to avoid + var/turf/avoid + ///Should we skip the first step? This is the tile we're currently on, which breaks some things + var/skip_first + ///Whether we replace diagonal movements with cardinal movements or follow through with them + var/diagonal_handling + ///A list for the path we're currently following + var/list/movement_path + ///Cooldown for repathing, prevents spam + COOLDOWN_DECLARE(repath_cooldown) + ///Bool used to determine if we're already making a path in JPS. this prevents us from re-pathing while we're already busy. + var/is_pathing = FALSE + ///Callbacks to invoke once we make a path + var/list/datum/callback/on_finish_callbacks = list() + +/datum/move_loop/has_target/jps/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) + . = ..() + on_finish_callbacks += CALLBACK(src, PROC_REF(on_finish_pathing)) + +/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, diagonal_handling, list/initial_path) + . = ..() + if(!.) + return + src.repath_delay = repath_delay + src.max_path_length = max_path_length + src.minimum_distance = minimum_distance + src.access = access + src.simulated_only = simulated_only + src.avoid = avoid + src.skip_first = skip_first + src.diagonal_handling = diagonal_handling + movement_path = initial_path?.Copy() + +/datum/move_loop/has_target/jps/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, initial_path) + if(..() && repath_delay == src.repath_delay && max_path_length == src.max_path_length && minimum_distance == src.minimum_distance && access ~= src.access && simulated_only == src.simulated_only && avoid == src.avoid) + return TRUE + return FALSE + +/datum/move_loop/has_target/jps/loop_started() + . = ..() + if(!movement_path) + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + +/datum/move_loop/has_target/jps/loop_stopped() + . = ..() + movement_path = null + +/datum/move_loop/has_target/jps/Destroy() + avoid = null + on_finish_callbacks = null + return ..() + +///Tries to calculate a new path for this moveloop. +/datum/move_loop/has_target/jps/proc/recalculate_path() + if(!COOLDOWN_FINISHED(src, repath_cooldown)) + return + COOLDOWN_START(src, repath_cooldown, repath_delay) + if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, access, simulated_only, avoid, skip_first, diagonal_handling, on_finish = on_finish_callbacks)) + is_pathing = TRUE + SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_REPATH) + +///Called when a path has finished being created +/datum/move_loop/has_target/jps/proc/on_finish_pathing(list/path) + movement_path = path + is_pathing = FALSE + SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, path) + +/datum/move_loop/has_target/jps/move() + if(!length(movement_path)) + if(is_pathing) + return MOVELOOP_NOT_READY + else + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + return MOVELOOP_FAILURE + + var/turf/next_step = movement_path[1] + var/atom/old_loc = moving.loc + moving.Move(next_step, get_dir(moving, next_step), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + . = (old_loc != moving?.loc) ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + // this check if we're on exactly the next tile may be overly brittle for dense objects who may get bumped slightly + // to the side while moving but could maybe still follow their path without needing a whole new path + if(get_turf(moving) == next_step) + if(length(movement_path)) + movement_path.Cut(1,2) + else + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + return MOVELOOP_FAILURE +*/ + + +///Base class of move_to and move_away, deals with the distance and target aspect of things +/datum/move_loop/has_target/dist_bound + var/distance = 0 + +/datum/move_loop/has_target/dist_bound/setup(delay, timeout, atom/chasing, dist = 0) + . = ..() + if(!.) + return + distance = dist + +/datum/move_loop/has_target/dist_bound/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, dist = 0) + if(..() && distance == dist) + return TRUE + return FALSE + +///Returns FALSE if the movement should pause, TRUE otherwise +/datum/move_loop/has_target/dist_bound/proc/check_dist() + return FALSE + +/datum/move_loop/has_target/dist_bound/move() + if(!check_dist()) //If we're too close don't do the move + return MOVELOOP_FAILURE + return MOVELOOP_SUCCESS + + +/** + * Wrapper around walk_to() + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * min_dist - the closest we're allower to get to the target + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_to(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_to, priority, flags, extra_info, delay, timeout, chasing, min_dist) + +///Wrapper around walk_to() +/datum/move_loop/has_target/dist_bound/move_to + +/datum/move_loop/has_target/dist_bound/move_to/check_dist() + return (get_dist(moving, target) > distance) //If you get too close, stop moving closer + +/datum/move_loop/has_target/dist_bound/move_to/move() + . = ..() + if(!.) + return + var/atom/old_loc = moving.loc + var/turf/next = get_step_to(moving, target) + moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Wrapper around walk_away() + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * max_dist - the furthest away from the target we're allowed to get + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_away, priority, flags, extra_info, delay, timeout, chasing, max_dist) + +///Wrapper around walk_away() +/datum/move_loop/has_target/dist_bound/move_away + +/datum/move_loop/has_target/dist_bound/move_away/check_dist() + return (get_dist(moving, target) < distance) //If you get too far out, stop moving away + +/datum/move_loop/has_target/dist_bound/move_away/move() + . = ..() + if(!.) + return + var/atom/old_loc = moving.loc + var/turf/next = get_step_away(moving, target) + moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Helper proc for the move_towards datum + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * home - Should we move towards the object at all times? Or launch towards them, but allow walls and such to take us off track. Defaults to FALSE + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to INFINITY + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_towards(moving, chasing, delay, home, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards, priority, flags, extra_info, delay, timeout, chasing, home) + + +/** + * Helper proc for homing onto something with move_towards + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * home - Should we move towards the object at all times? Or launch towards them, but allow walls and such to take us off track. Defaults to FALSE + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to INFINITY + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info) + return move_towards(moving, chasing, delay, TRUE, timeout, subsystem, priority, flags, extra_info) + +///Used as a alternative to walk_towards +/datum/move_loop/has_target/move_towards + ///The turf we want to move into, used for course correction + var/turf/moving_towards + ///Should we try and stay on the path, or is deviation alright + var/home = FALSE + ///When this gets larger then 1 we move a turf + var/x_ticker = 0 + var/y_ticker = 0 + ///The rate at which we move, between 0 and 1 + var/x_rate = 1 + var/y_rate = 1 + //We store the signs of x and y seperately, because byond will round negative numbers down + //So doing all our operations with absolute values then multiplying them is easier + var/x_sign = 0 + var/y_sign = 0 + +/datum/move_loop/has_target/move_towards/setup(delay, timeout, atom/chasing, home = FALSE) + . = ..() + if(!.) + return FALSE + src.home = home + + if(home) + if(ismovable(target)) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(update_slope)) //If it can move, update your slope when it does + RegisterSignal(moving, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move)) + update_slope() + +/datum/move_loop/has_target/move_towards/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, home = FALSE) + if(..() && home == src.home) + return TRUE + return FALSE + +/datum/move_loop/has_target/move_towards/Destroy() + if(home) + if(ismovable(target)) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + if(moving) + UnregisterSignal(moving, COMSIG_MOVABLE_MOVED) + return ..() + +/datum/move_loop/has_target/move_towards/move() + //Move our tickers forward a step, we're guaranteed at least one step forward because of how the code is written + if(x_rate) //Did you know that rounding by 0 throws a divide by 0 error? + x_ticker = FLOOR(x_ticker + x_rate, x_rate) + if(y_rate) + y_ticker = FLOOR(y_ticker + y_rate, y_rate) + + var/x = moving.x + var/y = moving.y + var/z = moving.z + + moving_towards = locate(x + round(x_ticker) * x_sign, y + round(y_ticker) * y_sign, z) + //The tickers serve as good methods of tracking remainder + if(x_ticker >= 1) + x_ticker = MODULUS(x_ticker, 1) //I swear to god if you somehow go up by one then one in a tick I'm gonna go mad + if(y_ticker >= 1) + y_ticker = MODULUS(x_ticker, 1) + var/atom/old_loc = moving.loc + moving.Move(moving_towards, get_dir(moving, moving_towards), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + + //YOU FOUND THEM! GOOD JOB + if(home && get_turf(moving) == get_turf(target)) + x_rate = 0 + y_rate = 0 + return + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/datum/move_loop/has_target/move_towards/proc/handle_move(source, atom/OldLoc, Dir, Forced = FALSE) + SIGNAL_HANDLER + if(moving.loc != moving_towards && home) //If we didn't go where we should have, update slope to account for the deviation + update_slope() + +/datum/move_loop/has_target/move_towards/handle_no_target() + if(home) + return ..() + target = null + +/** + * Recalculates the slope between our object and the target, sets our rates to it + * + * The math below is reminiscent of something like y = mx + b + * Except we don't need to care about axis, since we do all our movement in steps of 1 + * Because of that all that matters is we only move one tile at a time + * So we take the smaller delta, divide it by the larger one, and get smaller step per large step + * Then we set the large step to 1, and we're done. This way we're guaranteed to never move more then a tile at once + * And we can have nice lines +**/ +/datum/move_loop/has_target/move_towards/proc/update_slope() + SIGNAL_HANDLER + + //You'll notice this is rise over run, except we flip the formula upside down depending on the larger number + //This is so we never move more then one tile at once + var/delta_y = target.y - moving.y + var/delta_x = target.x - moving.x + //It's more convienent to store delta x and y as absolute values + //and modify them right at the end then it is to deal with rounding errors + x_sign = (delta_x > 0) ? 1 : -1 + y_sign = (delta_y > 0) ? 1 : -1 + delta_x = abs(delta_x) + delta_y = abs(delta_y) + + if(delta_x >= delta_y) + if(delta_x == 0) //Just go up/down + x_rate = 0 + y_rate = 1 + return + x_rate = 1 + y_rate = delta_y / delta_x //rise over run, you know the deal + else + if(delta_y == 0) //Just go right/left + x_rate = 1 + y_rate = 0 + return + x_rate = delta_x / delta_y //Keep the larger step size at 1 + y_rate = 1 + + +/** + * Wrapper for walk_towards, not reccomended, as it's movement ends up being a bit stilted + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * chasing - The atom we want to move towards + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards_budget, priority, flags, extra_info, delay, timeout, chasing) + +///The actual implementation of walk_towards() +/datum/move_loop/has_target/move_towards_budget + +/datum/move_loop/has_target/move_towards_budget/move() + var/turf/target_turf = get_step_towards(moving, target) + var/atom/old_loc = moving.loc + moving.Move(target_turf, get_dir(moving, target_turf), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Assigns a target to a move loop that immediately freezes for a set duration of time. + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * halted_turf - The turf we want to freeze on. This should typically be the loc of moving. + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. This should be considered extremely non-optional as it will completely stun out the movement loop forever if unset. + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + */ +/datum/controller/subsystem/move_manager/proc/freeze(moving, halted_turf, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/freeze, priority, flags, extra_info, delay, timeout, halted_turf) + +/// As close as you can get to a "do-nothing" move loop, the pure intention of this is to absolutely resist all and any automated movement until the move loop times out. +/datum/move_loop/freeze + +/datum/move_loop/freeze/move() + return MOVELOOP_SUCCESS // it's successful because it's not moving. we autoclear outselves when `timeout` is reached + + +/** + * Helper proc for the move_rand datum + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * directions - A list of acceptable directions to try and move in. Defaults to GLOB.alldirs + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_rand(moving, directions, delay, timeout, subsystem, priority, flags, datum/extra_info) + if(!directions) + directions = GLOB.alldirs + return add_to_loop(moving, subsystem, /datum/move_loop/move_rand, priority, flags, extra_info, delay, timeout, directions) + +/** + * This isn't actually the same as walk_rand + * Because walk_rand is really more like walk_to_rand + * It appears to pick a spot outside of range, and move towards it, then pick a new spot, etc. + * I can't actually replicate this on our side, because of how bad our pathfinding is, and cause I'm not totally sure I know what it's doing. + * I can just implement a random-walk though +**/ +/datum/move_loop/move_rand + var/list/potential_directions + +/datum/move_loop/move_rand/setup(delay, timeout, list/directions) + . = ..() + if(!.) + return + potential_directions = directions + +/datum/move_loop/move_rand/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, list/directions) + if(..() && (length(potential_directions | directions) == length(potential_directions))) //i guess this could be useful if actually it really has yet to move + return MOVELOOP_SUCCESS + return MOVELOOP_FAILURE + +/datum/move_loop/move_rand/move() + var/list/potential_dirs = potential_directions.Copy() + while(potential_dirs.len) + var/testdir = pick(potential_dirs) + var/turf/moving_towards = get_step(moving, testdir) + var/atom/old_loc = moving.loc + moving.Move(moving_towards, testdir, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + if(old_loc != moving?.loc) //If it worked, we're done + return MOVELOOP_SUCCESS + potential_dirs -= testdir + return MOVELOOP_FAILURE + + +/** + * Wrapper around walk_rand(), doesn't actually result in a random walk, it's more like moving to random places in viewish + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_to_rand(moving, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/move_to_rand, priority, flags, extra_info, delay, timeout) + +///Wrapper around step_rand +/datum/move_loop/move_to_rand + +/datum/move_loop/move_to_rand/move() + var/atom/old_loc = moving.loc + var/turf/next = get_step_rand(moving) + moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/* +/** + * Snowflake disposal movement. Moves a disposal holder along a chain of disposal pipes + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1 + * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity + * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem + * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY + * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm + * +**/ +/datum/controller/subsystem/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info) + return add_to_loop(moving, subsystem, /datum/move_loop/disposal_holder, priority, flags, extra_info, delay, timeout) + +/// Disposal holders need to move through a chain of pipes +/// Rather then through the world. This supports this +/// If this ever changes, get rid of this, add drift component like logic to the holder +/// And move them to move() +/datum/move_loop/disposal_holder + +/datum/move_loop/disposal_holder/setup(delay = 1, timeout = INFINITY) + // This is a horrible pattern. + // Move loops should almost never need to be one offs. Please don't do this if you can help it + if(!istype(moving, /obj/structure/disposalholder)) + stack_trace("You tried to make a [moving.type] object move like a disposals holder, stop that!") + return FALSE + return ..() + +/datum/move_loop/disposal_holder/move() + var/obj/structure/disposalholder/holder = moving + if(!holder.current_pipe) + return FALSE + var/atom/old_loc = moving.loc + holder.current_pipe = holder.current_pipe.transfer(holder) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE +*/ diff --git a/code/controllers/subsystem/movement/spacedrift.dm b/code/controllers/subsystem/movement/spacedrift.dm new file mode 100644 index 00000000000..fb3098c8020 --- /dev/null +++ b/code/controllers/subsystem/movement/spacedrift.dm @@ -0,0 +1,9 @@ +MOVEMENT_SUBSYSTEM_DEF(spacedrift) + name = "Space Drift" + priority = FIRE_PRIORITY_SPACEDRIFT + flags = SS_NO_INIT|SS_TICKER + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + offline_implications = "Mobs will no longer respect a lack of gravity. No immediate action is needed." + cpu_display = SS_CPUDISPLAY_LOW + ss_id = "space_drift" + diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm deleted file mode 100644 index c8e71dce9c2..00000000000 --- a/code/controllers/subsystem/spacedrift.dm +++ /dev/null @@ -1,62 +0,0 @@ -SUBSYSTEM_DEF(spacedrift) - name = "Space Drift" - priority = FIRE_PRIORITY_SPACEDRIFT - wait = 5 - flags = SS_NO_INIT|SS_KEEP_TIMING - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - offline_implications = "Mobs will no longer respect a lack of gravity. No immediate action is needed." - cpu_display = SS_CPUDISPLAY_LOW - ss_id = "space_drift" - var/list/currentrun = list() - var/list/processing = list() - - -/datum/controller/subsystem/spacedrift/get_stat_details() - return "P:[length(processing)]" - - -/datum/controller/subsystem/spacedrift/fire(resumed = 0) - if(!resumed) - src.currentrun = processing.Copy() - - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - - while(currentrun.len) - var/atom/movable/AM = currentrun[currentrun.len] - currentrun.len-- - if(!AM) - processing -= AM - if (MC_TICK_CHECK) - return - continue - - if(AM.inertia_next_move > world.time) - if (MC_TICK_CHECK) - return - continue - - if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(NONE)) - AM.inertia_dir = NONE - - if(!AM.inertia_dir) - AM.inertia_last_loc = null - processing -= AM - if (MC_TICK_CHECK) - return - 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 = NONE - - AM.setDir(old_dir) - AM.inertia_last_loc = AM.loc - if(MC_TICK_CHECK) - return - diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 759c924319b..de814a867c8 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -176,7 +176,7 @@ SUBSYSTEM_DEF(throwing) finalize() return - if(!AM.Move(step, get_dir(AM, step), speed)) // we hit something during our move... + if(!AM.Move(step, get_dir(AM, step), DELAY_TO_GLIDE_SIZE(1 / speed))) // we hit something during our move... if(AM.throwing) // ...but finalize() wasn't called on Bump() because of a higher level definition that doesn't always call parent. finalize() return @@ -218,8 +218,7 @@ SUBSYSTEM_DEF(throwing) if(QDELETED(thrownthing)) return - if(isturf(thrownthing.loc)) - thrownthing.newtonian_move(REVERSE_DIR(init_dir)) + thrownthing.newtonian_move(REVERSE_DIR(init_dir)) qdel(src) diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm index 08930eb5a8c..7e948aa9980 100644 --- a/code/controllers/subsystem/time_track.dm +++ b/code/controllers/subsystem/time_track.dm @@ -31,6 +31,7 @@ SUBSYSTEM_DEF(time_track) time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current) time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast) time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg) + GLOB.glide_size_multiplier = (current_byondtime - last_tick_byond_time) / (current_realtime - last_tick_realtime) else first_run = FALSE last_tick_realtime = current_realtime diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 1f5ba549dbd..e7e93a2ec20 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -138,7 +138,7 @@ SUBSYSTEM_DEF(timer) continue ctime_timer.spent = 0 ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait - BINARY_INSERT_TG(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY) + BINARY_INSERT(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY) else qdel(ctime_timer) @@ -526,7 +526,7 @@ SUBSYSTEM_DEF(timer) else if (timeToRun >= TIMER_MAX(timer_subsystem)) L = timer_subsystem.second_queue if(L) - BINARY_INSERT_TG(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY) + BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY) return // Get a local reference to the bucket list, this is faster than referencing the datum diff --git a/code/datums/action.dm b/code/datums/action.dm index 5f958bcf72f..a8941d73f5d 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -208,7 +208,7 @@ return FALSE if(target && attack_self) var/obj/item/I = target - I.ui_action_click(owner, type, left_click) + I.ui_action_click(owner, src, left_click) return TRUE /datum/action/item_action/ApplyIcon(atom/movable/screen/movable/action_button/current_button) @@ -485,7 +485,7 @@ /datum/action/item_action/jetpack_stabilization/ninja/UpdateButtonIcon() . = ..() var/obj/item/tank/jetpack/J = target - if(!istype(J) || !J.stabilizers) + if(!istype(J) || !J.stabilize) button.icon_state = "[background_icon_state]" else button.icon_state = "[background_icon_state]_active" diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm new file mode 100644 index 00000000000..520d0793aea --- /dev/null +++ b/code/datums/components/drift.dm @@ -0,0 +1,209 @@ +///Component that handles drifting +///Manages a movement loop that actually does the legwork of moving someone +///Alongside dealing with the post movement input blocking required to make things look nice +/datum/component/drift + var/atom/inertia_last_loc + var/old_dir + var/datum/move_loop/move/drifting_loop + ///Should we ignore the next glide rate input we get? + ///This is to some extent a hack around the order of operations + ///Around COMSIG_MOVELOOP_POSTPROCESS. I'm sorry lad + var/ignore_next_glide = FALSE + ///Have we been delayed? IE: active, but not working right this second? + var/delayed = FALSE + var/block_inputs_until + + +/// Accepts three args. The direction to drift in, if the drift is instant or not, and if it's not instant, the delay on the start +/datum/component/drift/Initialize(direction, instant = FALSE, start_delay = 0) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + + var/flags = MOVEMENT_LOOP_OUTSIDE_CONTROL + if(instant) + flags |= MOVEMENT_LOOP_START_FAST + var/atom/movable/movable_parent = parent + drifting_loop = SSmove_manager.move(moving = parent, direction = direction, delay = movable_parent.inertia_move_delay, subsystem = SSspacedrift, priority = MOVEMENT_SPACE_PRIORITY, flags = flags) + + if(!drifting_loop) //Really want to qdel here but can't + return COMPONENT_INCOMPATIBLE + + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_START, PROC_REF(drifting_start)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_STOP, PROC_REF(drifting_stop)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(before_move)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move)) + RegisterSignal(drifting_loop, COMSIG_PARENT_QDELETING, PROC_REF(loop_death)) + RegisterSignal(movable_parent, COMSIG_MOVABLE_NEWTONIAN_MOVE, PROC_REF(newtonian_impulse)) + if(drifting_loop.status & MOVELOOP_STATUS_RUNNING) + drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that + + var/visual_delay = movable_parent.inertia_move_delay + + // Start delay is essentially a more granular version of instant + // Isn't used in the standard case, just for things that have odd wants + if(!instant && start_delay) + drifting_loop.pause_for(start_delay) + visual_delay = start_delay + + apply_initial_visuals(visual_delay) + + +/datum/component/drift/Destroy() + inertia_last_loc = null + if(!QDELETED(drifting_loop)) + qdel(drifting_loop) + drifting_loop = null + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = FALSE + return ..() + + +/datum/component/drift/proc/apply_initial_visuals(visual_delay) + // If something "somewhere" doesn't want us to apply our glidesize delays, don't + if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) & DRIFT_VISUAL_FAILED) + return + + // Ignore the next glide because it's literally just us + ignore_next_glide = TRUE + var/atom/movable/movable_parent = parent + movable_parent.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay)) + if(ismob(parent)) + var/mob/mob_parent = parent + //Ok this is slightly weird, but basically, we need to force the client to glide at our rate + //Make sure moving into a space move looks like a space move essentially + //There is an inbuilt assumption that gliding will be added as a part of a move call, but eh + //It's ok if it's not, it's just important if it is. + mob_parent.client?.visual_delay = MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay) + + +/datum/component/drift/proc/newtonian_impulse(datum/source, inertia_direction) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + inertia_last_loc = movable_parent.loc + if(drifting_loop) + drifting_loop.direction = inertia_direction + if(!inertia_direction) + qdel(src) + return COMPONENT_MOVABLE_NEWTONIAN_BLOCK + + +/datum/component/drift/proc/drifting_start() + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + inertia_last_loc = movable_parent.loc + RegisterSignal(movable_parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move)) + // We will use glide size to intuit how long to delay our loop's next move for + // This way you can't ride two movements at once while drifting, since that'd be dumb as fuck + RegisterSignal(movable_parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(handle_glidesize_update)) + // If you stop pulling something mid drift, I want it to retain that momentum + RegisterSignal(movable_parent, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(stopped_pulling)) + + +/datum/component/drift/proc/drifting_stop() + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = FALSE + ignore_next_glide = FALSE + UnregisterSignal(movable_parent, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, COMSIG_ATOM_NO_LONGER_PULLING)) + + +/datum/component/drift/proc/before_move(datum/source) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = TRUE + old_dir = movable_parent.dir + delayed = FALSE + + +/datum/component/drift/proc/after_move(datum/source, result, visual_delay) + SIGNAL_HANDLER + if(result == MOVELOOP_FAILURE) + qdel(src) + return + + var/atom/movable/movable_parent = parent + movable_parent.setDir(old_dir) + movable_parent.inertia_moving = FALSE + if(movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) + glide_to_halt(visual_delay) + return + + inertia_last_loc = movable_parent.loc + ignore_next_glide = TRUE + + +/datum/component/drift/proc/loop_death(datum/source) + SIGNAL_HANDLER + drifting_loop = null + UnregisterSignal(parent, COMSIG_MOVABLE_NEWTONIAN_MOVE) // We won't block a component from replacing us anymore + + +/datum/component/drift/proc/handle_move(datum/source, old_loc) + SIGNAL_HANDLER + // This can happen, because signals once sent cannot be stopped + if(QDELETED(src)) + return + var/atom/movable/movable_parent = parent + if(!isturf(movable_parent.loc)) + qdel(src) + return + if(movable_parent.inertia_moving) + return + if(!movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) + return + qdel(src) + + +/// We're going to take the passed in glide size +/// and use it to manually delay our loop for that period +/// to allow the other movement to complete +/datum/component/drift/proc/handle_glidesize_update(datum/source, glide_size) + SIGNAL_HANDLER + // If we aren't drifting, or this is us, fuck off + var/atom/movable/movable_parent = parent + if(!drifting_loop || movable_parent.inertia_moving) + return + // If we are drifting, but this set came from the moveloop itself, drop the input + // I'm sorry man + if(ignore_next_glide) + ignore_next_glide = FALSE + return + var/glide_delay = round(world.icon_size / glide_size, 1) * world.tick_lag + drifting_loop.pause_for(glide_delay) + delayed = TRUE + + +/// If we're pulling something and stop, we want it to continue at our rate and such +/datum/component/drift/proc/stopped_pulling(datum/source, atom/movable/was_pulling) + SIGNAL_HANDLER + // This does mean it falls very slightly behind, but otherwise they'll potentially run into us + var/next_move_in = drifting_loop.timer - world.time + world.tick_lag + was_pulling.newtonian_move(drifting_loop.direction, start_delay = next_move_in) + + +/datum/component/drift/proc/glide_to_halt(glide_for) + if(!ismob(parent)) + qdel(src) + return + + var/mob/mob_parent = parent + var/client/our_client = mob_parent.client + // If we're not active, don't do the glide because it'll look dumb as fuck + if(!our_client || delayed) + qdel(src) + return + + block_inputs_until = world.time + glide_for + QDEL_IN(src, glide_for + 1) + qdel(drifting_loop) + RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(allow_final_movement)) + + +/datum/component/drift/proc/allow_final_movement(datum/source) + // Some things want to allow movement out of spacedrift, we should let them + if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) & DRIFT_ALLOW_INPUT) + return + if(world.time < block_inputs_until) + return COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE + diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm new file mode 100644 index 00000000000..604e9027ff8 --- /dev/null +++ b/code/datums/components/jetpack.dm @@ -0,0 +1,167 @@ +// Welcome to the jetpack component +// Apply this to something when you want it to be "like a jetpack" +// So propulsion through space on move, that sort of thing +/datum/component/jetpack + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Checks to ensure if we can move & if we can activate + var/datum/callback/check_on_move + /// If we should stabilize ourselves when not drifting + var/stabilize = FALSE + /// The signal we listen for as an activation + var/activation_signal + /// The signal we listen for as a de-activation + var/deactivation_signal + /// The return flag our parent expects for a failed activation + var/return_flag + /// The effect system for the jet pack trail + var/datum/effect_system/trail_follow/trail + /// Whether we will skip trails effect + var/skip_trails + /// The typepath to instansiate our trail as, when we need it + var/effect_type + + +/** + * Arguments: + * * stabilize - If we should drift when we finish moving, or sit stable in space + * * activation_signal - Signal we activate on + * * deactivation_signal - Signal we deactivate on + * * return_flag - Flag to return if activation fails + * * check_on_move - Callback we call each time we attempt a move, we expect it to return `TRUE` if the move is ok, `FALSE` otherwise. It expects an arg, `TRUE` if fuel should be consumed, `FALSE` othewise + * * effect_type - Type of trail_follow to spawn + */ +/datum/component/jetpack/Initialize(stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type, skip_trails) + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + if(!activation_signal) // Can't activate? go away + return COMPONENT_INCOMPATIBLE + + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.stabilize = stabilize + src.check_on_move = check_on_move + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.return_flag = return_flag + src.effect_type = effect_type + src.skip_trails = skip_trails + + +/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type, skip_trails) + UnregisterSignal(parent, src.activation_signal) + if(src.deactivation_signal) + UnregisterSignal(parent, src.deactivation_signal) + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.stabilize = stabilize + src.check_on_move = check_on_move + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.return_flag = return_flag + src.effect_type = effect_type + src.skip_trails = skip_trails + + if(trail && trail.effect_type != effect_type) + setup_trail(trail.holder) + + +/datum/component/jetpack/Destroy(force) + QDEL_NULL(trail) + check_on_move = null + return ..() + + +/datum/component/jetpack/proc/setup_trail(mob/user) + QDEL_NULL(trail) + + trail = new effect_type + trail.auto_process = FALSE + trail.set_up(user) + trail.start() + + +/datum/component/jetpack/proc/activate(datum/source, mob/user) + SIGNAL_HANDLER + + if(!check_on_move.Invoke(TRUE)) + return return_flag + + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(move_react)) + RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react)) + RegisterSignal(user, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(spacemove_react)) + RegisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT, PROC_REF(block_starting_visuals)) + RegisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(ignore_ending_block)) + + setup_trail(user) + + +/datum/component/jetpack/proc/deactivate(datum/source, mob/user) + SIGNAL_HANDLER + + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE) + UnregisterSignal(user, COMSIG_MOVABLE_SPACEMOVE) + UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) + UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) + + QDEL_NULL(trail) + + +/datum/component/jetpack/proc/move_react(mob/user) + SIGNAL_HANDLER + if(!user || !user.client)//Don't allow jet self using + return + if(!isturf(user.loc))//You cannot use jet in nowhere or from mecha/closet + return + if(!(user.movement_type & FLOATING) || user.buckled)//You cannot use jet in gravity or while buckled. + return + if(user.pulledby)//You cannot use jet if someone pull you + return + if(user.throwing)//You cannot use jet if you thrown + return + if(user.client.input_data.desired_move_dir)//You use jet when press keys. yes. + thrust() + + +/datum/component/jetpack/proc/pre_move_react(mob/user) + SIGNAL_HANDLER + if(!trail) + return FALSE + trail.oldposition = get_turf(user) + + +/datum/component/jetpack/proc/spacemove_react(mob/user, movement_dir, continuous_move) + SIGNAL_HANDLER + if(!continuous_move && movement_dir) + return COMSIG_MOVABLE_STOP_SPACEMOVE + // Check if we have the fuel to stop this. Do NOT cosume any fuel, just check + // This is done because things other then us can use our fuel + if(stabilize && check_on_move.Invoke(FALSE)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + + +/// Returns true if the thrust went well, false otherwise +/datum/component/jetpack/proc/thrust() + if(!check_on_move.Invoke(TRUE)) + return FALSE + if(!skip_trails) + trail.generate_effect() + return TRUE + + +/// Basically, tell the drift component not to do its starting visuals, because they look dumb for us +/datum/component/jetpack/proc/block_starting_visuals(datum/source) + SIGNAL_HANDLER + return DRIFT_VISUAL_FAILED + + +/// If we're on, don't let the drift component block movements at the end since we can speed +/datum/component/jetpack/proc/ignore_ending_block(datum/source) + SIGNAL_HANDLER + return DRIFT_ALLOW_INPUT + diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index 174a0be27a4..0ae8ff1cbc8 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -126,7 +126,7 @@ var/turf/simulated/T = parent var/diff = world.time - last_process var/decrease = 0 - var/t = T.air.temperature + var/t = T.air ? T.air.temperature : T20C switch(t) if(-INFINITY to T0C) add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice! diff --git a/code/datums/spells/rod_form.dm b/code/datums/spells/rod_form.dm index 3864c01bfa6..9fdf4873111 100644 --- a/code/datums/spells/rod_form.dm +++ b/code/datums/spells/rod_form.dm @@ -3,7 +3,7 @@ desc = "Take on the form of an immovable rod, destroying all in your path." clothes_req = TRUE human_req = FALSE - base_cooldown = 60 SECONDS + base_cooldown = 1 MINUTES cooldown_min = 20 SECONDS invocation = "CLANG!" invocation_type = "shout" @@ -27,10 +27,10 @@ for(var/mob/living/M in targets) var/turf/start = get_turf(M) var/obj/effect/immovablerod/wizard/W = new(start, get_ranged_target_turf(M, M.dir, (15 + spell_level * 3)), rod_delay) + W.setDir(M.dir) W.wizard = M W.spell = src W.max_distance += spell_level * 3 //You travel farther when you upgrade the spell - W.start_turf = start M.forceMove(W) ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) M.status_flags |= GODMODE @@ -40,17 +40,17 @@ * Wizard Version of the Immovable Rod */ /obj/effect/immovablerod/wizard + notify = FALSE var/max_distance = 13 var/mob/living/wizard - var/turf/start_turf var/obj/effect/proc_holder/spell/spell - notify = FALSE -/obj/effect/immovablerod/wizard/Move(atom/newloc, direct = NONE, movetime) - if(get_dist(start_turf, get_turf(src)) >= max_distance) +/obj/effect/immovablerod/wizard/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) + . = ..() + max_distance-- + if((!loc || max_distance <= 0) && !QDELETED(src)) qdel(src) - ..() /obj/effect/immovablerod/wizard/Destroy() @@ -60,6 +60,5 @@ wizard.forceMove(get_turf(src)) wizard = null spell = null - start_turf = null return ..() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 1463cacddcd..802a5a2b741 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -9,7 +9,7 @@ /atom layer = TURF_LAYER plane = GAME_PLANE - appearance_flags = TILE_BOUND + appearance_flags = TILE_BOUND|LONG_GLIDE var/level = 2 var/flags = NONE var/flags_2 = NONE @@ -307,9 +307,11 @@ /atom/proc/on_reagent_change() return + /atom/proc/Bumped(atom/movable/moving_atom) SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom) - return + return FALSE + /// Convenience proc to see if a container is open for chemistry handling /atom/proc/is_open_container() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 43915bbabb7..9426860b4c2 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,6 +1,6 @@ /atom/movable layer = OBJ_LAYER - appearance_flags = TILE_BOUND|PIXEL_SCALE + appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE glide_size = 8 // Default, adjusted when mobs move based on their movement delays var/last_move = null var/anchored = FALSE @@ -19,20 +19,20 @@ var/canmove = TRUE var/pull_push_speed_modifier = 1 - ///The last time we pushed off something - ///This is a hack to get around dumb him him me scenarios - var/last_pushoff - /// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour var/generic_canpass = TRUE - var/inertia_dir = NONE - var/atom/inertia_last_loc + /// Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on + var/datum/movement_packet/move_packet + /// Are we moving with inertia? Mostly used as an optimization var/inertia_moving = FALSE - var/inertia_next_move = 0 + /// Delay in deciseconds between inertia based movement var/inertia_move_delay = 5 + ///The last time we pushed off something + ///This is a hack to get around dumb him him me scenarios + var/last_pushoff - ///Used for the calculate_adjacencies proc for icon smoothing. + /// Used for the calculate_adjacencies proc for icon smoothing. var/can_be_unanchored = FALSE /// Whether the atom allows mobs to be buckled to it. Can be ignored in [/atom/movable/proc/buckle_mob()] if force = TRUE @@ -60,6 +60,8 @@ /// NONE:0 not doing a diagonal move. FIRST_DIAG_STEP:1 and SECOND_DIAG_STEP:2 doing the first/second step of the diagonal move. var/moving_diagonally = NONE + ///contains every client mob corresponding to every client eye in this container. lazily updated by SSparallax and is sparse: + ///only the last container of a client eye has this list assuming no movement since SSparallax's last fire var/list/client_mobs_in_contents /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] @@ -78,6 +80,7 @@ ///is the mob currently ascending or descending through z levels? var/currently_z_moving + /atom/movable/attempt_init(loc, ...) var/turf/T = get_turf(src) if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z)) @@ -130,7 +133,7 @@ AddComponent(/datum/component/overlay_lighting, is_directional = TRUE) -/atom/movable/Destroy() +/atom/movable/Destroy(force) unbuckle_all_mobs(force = TRUE) QDEL_NULL(em_block) @@ -140,11 +143,16 @@ for(var/atom/movable/AM in contents) qdel(AM) LAZYCLEARLIST(client_mobs_in_contents) - move_to_null_space() if(pulledby) pulledby.stop_pulling() if(orbiting) stop_orbit() + if(move_packet) + if(!QDELETED(move_packet)) + qdel(move_packet) + move_packet = null + + move_to_null_space() /atom/movable/get_emissive_block() @@ -217,7 +225,10 @@ pulling.pulledby = null var/mob/living/ex_pulled = pulling + var/atom/movable/old_pulling = pulling pulling = null + SEND_SIGNAL(old_pulling, COMSIG_ATOM_NO_LONGER_PULLED, src) + SEND_SIGNAL(src, COMSIG_ATOM_NO_LONGER_PULLING, old_pulling) if(isliving(ex_pulled)) var/mob/living/L = ex_pulled L.update_canmove()// mob gets up if it was lyng down in a chokehold @@ -261,6 +272,17 @@ /atom/movable/proc/setLoc(var/T, var/teleported=0) loc = T + +/atom/movable/proc/set_glide_size(target = 8) + if(HAS_TRAIT(src, TRAIT_NO_GLIDE)) + return + SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target) + glide_size = target + + for(var/mob/buckled_mob as anything in buckled_mobs) + buckled_mob.set_glide_size(target) + + /** * meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts) * if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this @@ -270,23 +292,23 @@ var/atom/old_loc = loc var/direction = get_dir(old_loc, new_loc) loc = new_loc - Moved(old_loc, direction, TRUE) + Moved(old_loc, direction, TRUE, FALSE) -/atom/movable/Move(atom/newloc, direct = NONE, movetime) +/atom/movable/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(!loc || !newloc) return FALSE var/atom/oldloc = loc + //Early override for some cases like diagonal movement + if(glide_size_override && glide_size != glide_size_override) + set_glide_size(glide_size_override) if(loc != newloc) SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, oldloc) - if(movetime > 0) - glide_for(movetime) - - if(!(direct & (direct - 1))) //Cardinal move - . = ..(newloc, direct) // don't pass up movetime + if(!ISDIAGONALDIR(direct)) //Cardinal move + . = ..(newloc, direct) else //Diagonal move, split it into cardinal moves moving_diagonally = FIRST_DIAG_STEP var/first_step_dir @@ -340,8 +362,9 @@ if(!.) setDir(first_step_dir) else if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay newtonian_move(direct) + if(client_mobs_in_contents) + update_parallax_contents() moving_diagonally = NONE return @@ -350,6 +373,11 @@ set_currently_z_moving(FALSE, TRUE) return + //glide_size strangely enough can change mid movement animation and update correctly while the animation is playing + //This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs. + if(glide_size_override) + set_glide_size(glide_size_override) + if(.) Moved(oldloc, direct, FALSE) @@ -357,7 +385,7 @@ move_speed = world.time - l_move_time l_move_time = world.time - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, movetime)) //movement failed due to buckled mob + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s) . = FALSE if(currently_z_moving) @@ -367,13 +395,14 @@ else set_currently_z_moving(FALSE, TRUE) + // Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) +/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) + SHOULD_CALL_PARENT(TRUE) - if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay + if(!inertia_moving && momentum_change) newtonian_move(Dir) - if(length(client_mobs_in_contents)) + if(!moving_diagonally && client_mobs_in_contents) update_parallax_contents() SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) @@ -389,14 +418,6 @@ light.source_atom.update_light() return TRUE -// Change glide size for the duration of one movement -/atom/movable/proc/glide_for(movetime) - if(movetime) - glide_size = world.icon_size/max(DS2TICKS(movetime), 1) -// spawn(movetime) -// glide_size = initial(glide_size) -// else -// glide_size = initial(glide_size) // Previously known as HasEntered() // This is automatically called when something enters your square @@ -407,15 +428,25 @@ /atom/movable/Uncrossed(atom/movable/AM) SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM) -/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) - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - if(throwing) - throwing.finalize(A) - . = TRUE - if(QDELETED(A)) - return - A.Bumped(src) + +/// The "custom_bump" arg is to differentiate our Bump proc in [/turf/Enter()] from byond's. +/// Without it every Bump() call would become a double Bump(). +/// Return `TRUE` if you want to skip bump chain. +/// Always check for null return in children, otherwise you will end with double bump. +/atom/movable/Bump(atom/bumped_atom, custom_bump) + if(!custom_bump) + return null + if(!bumped_atom) + CRASH("Bump was called with no argument.") + . = FALSE + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, bumped_atom) + if(throwing) + throwing.finalize(bumped_atom) + . = TRUE + if(QDELETED(bumped_atom)) + return . + bumped_atom.Bumped(src) + /// Sets the currently_z_moving variable to a new value. Used to allow some zMovement sources to have precedence over others. /atom/movable/proc/set_currently_z_moving(new_z_moving_value, forced = FALSE) @@ -659,15 +690,19 @@ * * Return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting * - * Mobs should return TRUE if they should be able to move of their own volition, see [/client/proc/Move] + * Mobs should return `TRUE` if they should be able to move of their own volition, see [/client/proc/Move] * * Arguments: * * movement_dir - NONE when stopping or any dir when trying to move + * * continuous_move - If this check is coming from something in the context of already drifting */ -/atom/movable/proc/Process_Spacemove(movement_dir = NONE) +/atom/movable/proc/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) if(has_gravity()) return TRUE + if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE) + return TRUE + if(pulledby && pulledby.pulledby != src) return TRUE @@ -684,17 +719,16 @@ /// Only moves the object if it's under no gravity -/atom/movable/proc/newtonian_move(direction) - if(!isturf(loc) || Process_Spacemove(NONE)) - inertia_dir = NONE +/// Accepts the direction to move, if the push should be instant, and an optional parameter to fine tune the start delay +/atom/movable/proc/newtonian_move(direction, instant = FALSE, start_delay = 0) + if(QDELETED(src) || !isturf(loc) || Process_Spacemove(direction, continuous_move = TRUE)) return FALSE - inertia_dir = direction - if(!direction) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_NEWTONIAN_MOVE, direction, start_delay) & COMPONENT_MOVABLE_NEWTONIAN_BLOCK) return TRUE - inertia_last_loc = loc - SSspacedrift.processing[src] = src + AddComponent(/datum/component/drift, direction, instant, start_delay) + return TRUE @@ -804,18 +838,16 @@ if(master) return master.attack_hand(a, b, c) -/atom/movable/proc/handle_buckled_mob_movement(newloc,direct,movetime) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.glide_size = glide_size - if(!buckled_mob.Move(newloc, direct, movetime)) - forceMove(buckled_mob.loc) + +/atom/movable/proc/handle_buckled_mob_movement(newloc, direct, glide_size_override) + for(var/mob/living/buckled_mob as anything in buckled_mobs) + if(!buckled_mob.Move(newloc, direct, glide_size_override)) //If a mob buckled to us can't make the same move as us + Move(buckled_mob.loc, direct) //Move back to its location last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move return FALSE return TRUE + /atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) return FALSE @@ -839,7 +871,7 @@ return TRUE -/atom/movable/proc/get_spacemove_backup(moving_direction) +/atom/movable/proc/get_spacemove_backup(moving_direction, continuous_move) for(var/checked_range in orange(1, get_turf(src))) if(isarea(checked_range)) continue diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm index 68ea63eda06..4a194f55c04 100644 --- a/code/game/gamemodes/blob/overmind.dm +++ b/code/game/gamemodes/blob/overmind.dm @@ -106,14 +106,14 @@ stat(null, "Core Health: [blob_core.obj_integrity]") stat(null, "Power Stored: [blob_points]/[max_blob_points]") -/mob/camera/blob/Move(var/NewLoc, var/Dir = 0) +/mob/camera/blob/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(world.time < last_movement) return last_movement = world.time + 0.5 // cap to 20fps - var/obj/structure/blob/B = locate() in range("3x3", NewLoc) + var/obj/structure/blob/B = locate() in range("3x3", newloc) if(B) - loc = NewLoc + loc = newloc else return 0 diff --git a/code/game/gamemodes/clockwork/clockwork_items.dm b/code/game/gamemodes/clockwork/clockwork_items.dm index d838fd687f3..56b26d04b41 100644 --- a/code/game/gamemodes/clockwork/clockwork_items.dm +++ b/code/game/gamemodes/clockwork/clockwork_items.dm @@ -691,8 +691,8 @@ if(enchant_type) . += "clockwork_robe_overlay_[enchant_type]" -/obj/item/clothing/suit/hooded/clockrobe/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/activate/enchant) +/obj/item/clothing/suit/hooded/clockrobe/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/activate/enchant)) if(!iscarbon(user)) return var/mob/living/carbon/carbon = user diff --git a/code/game/gamemodes/clockwork/clockwork_misc.dm b/code/game/gamemodes/clockwork/clockwork_misc.dm index 9460f2f7f00..c76c66d41ce 100644 --- a/code/game/gamemodes/clockwork/clockwork_misc.dm +++ b/code/game/gamemodes/clockwork/clockwork_misc.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/lavaland/dead_ratvar.dmi' icon_state = "dead_ratvar" flags = ON_BORDER - appearance_flags = 0 + appearance_flags = LONG_GLIDE layer = FLY_LAYER anchored = TRUE density = TRUE diff --git a/code/game/gamemodes/clockwork/cogscarab.dm b/code/game/gamemodes/clockwork/cogscarab.dm index b3490a63842..a3f15934b07 100644 --- a/code/game/gamemodes/clockwork/cogscarab.dm +++ b/code/game/gamemodes/clockwork/cogscarab.dm @@ -216,8 +216,8 @@ SSticker.mode.remove_clocker(mind, FALSE) adjustBruteLoss(health) -/mob/living/silicon/robot/cogscarab/Bump(atom/movable/AM, yes) - if(is_type_in_list(AM, allowed_bumpable_objects)) +/mob/living/silicon/robot/cogscarab/Bump(atom/bumped_atom, custom_bump) + if(custom_bump && is_type_in_list(bumped_atom, allowed_bumpable_objects)) return ..() /mob/living/silicon/robot/cogscarab/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE) diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/game/gamemodes/devil/true_devil/_true_devil.dm index d0c75522530..3bed31b585d 100644 --- a/code/game/gamemodes/devil/true_devil/_true_devil.dm +++ b/code/game/gamemodes/devil/true_devil/_true_devil.dm @@ -134,7 +134,7 @@ // If the devil wants to actually attack, they have the pitchfork. -/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE) +/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index 52347ab0ebb..d3c3eb0fb39 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -25,7 +25,7 @@ var/waitduration = rand(3000,6000) while(waveduration - world.time > 0) sleep(max(65 - text2num("[wave]0") / 2, 40)) - spawn() spawn_meteors(6, GLOB.meteors_normal) + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 6, GLOB.meteors_normal) wave++ sleep(waitduration) sendmeteors() diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 1a1e2dc7414..baace6c32cb 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,84 +1,112 @@ //Meteors probability of spawning during a given wave -GLOBAL_LIST_INIT(meteors_normal, list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ - /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3)) //for normal meteor event - -GLOBAL_LIST_INIT(meteors_threatening, list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ - /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3)) //for threatening meteor event - -GLOBAL_LIST_INIT(meteors_catastrophic, list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ - /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1)) //for catastrophic meteor event +GLOBAL_LIST_INIT(meteors_normal, list( //for normal meteor event + /obj/effect/meteor/dust = 3, + /obj/effect/meteor/medium = 8, + /obj/effect/meteor/big = 3, + /obj/effect/meteor/flaming = 1, + /obj/effect/meteor/irradiated = 3, +)) + +GLOBAL_LIST_INIT(meteors_threatening, list( //for threatening meteor event + /obj/effect/meteor/medium = 4, + /obj/effect/meteor/big = 8, + /obj/effect/meteor/flaming = 3, + /obj/effect/meteor/irradiated = 3, +)) + +GLOBAL_LIST_INIT(meteors_catastrophic, list( //for catastrophic meteor event + /obj/effect/meteor/medium = 5, + /obj/effect/meteor/big = 75, + /obj/effect/meteor/flaming = 10, + /obj/effect/meteor/irradiated = 10, + /obj/effect/meteor/tunguska = 1, +)) GLOBAL_LIST_INIT(meteors_dust, list(/obj/effect/meteor/dust)) //for space dust event GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/gore)) //Meaty Gore -GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops +GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/gore/ops)) //Meaty Ops + +GLOBAL_LIST_INIT(meteors_pigs, list(/obj/effect/meteor/gore/pigops)) // pigOps + +GLOBAL_LIST_INIT(meteors_space_dust, list(/obj/effect/meteor/space_dust/weak)) //for another space dust event /////////////////////////////// //Meteor spawning global procs /////////////////////////////// -/proc/spawn_meteors(var/number = 10, var/list/meteortypes) - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes) +/proc/spawn_meteors(number = 10, list/meteor_types, direction) + for(var/i in 1 to number) + spawn_meteor(meteor_types, direction) -/proc/spawn_meteor(var/list/meteortypes) - var/turf/pickedstart - var/turf/pickedgoal + +/proc/spawn_meteor(list/meteor_types, direction, atom/target) + var/turf/picked_start + var/turf/picked_goal var/max_i = 10//number of tries to spawn meteor. - while(!isspaceturf(pickedstart)) - var/startSide = pick(GLOB.cardinal) - var/level = pick(levels_by_trait(STATION_LEVEL)) - pickedstart = spaceDebrisStartLoc(startSide, level) - pickedgoal = spaceDebrisFinishLoc(startSide, level) + while(!isspaceturf(picked_start)) + var/start_side + if(direction) //If a direction has been specified, we set start_side to it. Otherwise, pick randomly + start_side = direction + else + start_side = pick(GLOB.cardinal) + var/start_Z = pick(levels_by_trait(STATION_LEVEL)) + picked_start = spaceDebrisStartLoc(start_side, start_Z) + if(target) + if(!isturf(target)) + target = get_turf(target) + picked_goal = target + else + picked_goal = spaceDebrisFinishLoc(start_side, start_Z) max_i-- - if(max_i<=0) + if(max_i <= 0) return - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - M.z_original = pick(levels_by_trait(STATION_LEVEL)) - spawn(0) - walk_towards(M, M.dest, 1) - return + var/new_meteor = pickweight(meteor_types) + new new_meteor(picked_start, picked_goal) + + +#define MAP_EDGE_PAD 1 -/proc/spaceDebrisStartLoc(startSide, Z) +/proc/spaceDebrisStartLoc(start_side, Z) var/starty var/startx - switch(startSide) + switch(start_side) if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + starty = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + starty = (TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = (TRANSITIONEDGE + MAP_EDGE_PAD) + . = locate(startx, starty, Z) + /proc/spaceDebrisFinishLoc(startSide, Z) var/endy var/endx switch(startSide) if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + endy = (TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = (TRANSITIONEDGE + MAP_EDGE_PAD) if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + endy = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) + . = locate(endx, endy, Z) + +#undef MAP_EDGE_PAD + /////////////////////// //The meteor effect @@ -91,52 +119,73 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops icon_state = "small" density = TRUE anchored = TRUE - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest pass_flags = PASSTABLE - var/heavy = 0 + + ///The resilience of our meteor + var/hits = 4 + ///Level of ex_act to be called on hit. + var/hitpwr = EXPLODE_HEAVY + //Should we shake people's screens on impact + var/heavy = FALSE + ///Sound to play when we hit something var/meteorsound = 'sound/effects/meteorimpact.ogg' - var/z_original = 1 + ///Our starting z level, prevents infinite meteors + var/z_original - var/meteordrop = /obj/item/stack/ore/iron + //Potential items to spawn when we die. Can be list. + var/list/meteordrop = /obj/item/stack/ore/iron + ///How much stuff to spawn when we die var/dropamt = 2 -/obj/effect/meteor/Initialize(mapload) + ///The thing we're moving towards, usually a turf + var/atom/dest + ///Lifetime in seconds + var/lifetime = 180 SECONDS + /// Chance to shake everyone screen on impact. + var/shake_chance = 50 + + +/obj/effect/meteor/Initialize(mapload, turf/target) . = ..() z_original = z + GLOB.meteor_list += src + SpinAnimation() + chase_target(target) -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - . = ..() //process movement... +/obj/effect/meteor/Destroy() + GLOB.meteor_list -= src + return ..() + - if(.)//.. if did move, ram the turf we get in - var/turf/T = get_turf(loc) - ram_turf(T) +/obj/effect/meteor/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) + . = ..() + if(QDELETED(src)) + return + + if(OldLoc != loc)//If did move, ram the turf we get in + var/turf/ram_turf = get_turf(loc) + ram_turf(ram_turf) - if(prob(10) && !isspaceturf(T))//randomly takes a 'hit' from ramming + if(prob(10) && !isspaceturf(ram_turf))//randomly takes a 'hit' from ramming get_hit() - return . + if(z != z_original || loc == get_turf(dest)) + qdel(src) -/obj/effect/meteor/Destroy() - GLOB.meteor_list -= src - walk(src,0) //this cancels the walk_towards() proc - return ..() -/obj/effect/meteor/New() - ..() - GLOB.meteor_list += src - SpinAnimation() +/obj/effect/meteor/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + return TRUE //Keeps us from drifting for no reason + + +/obj/effect/meteor/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . -/obj/effect/meteor/Bump(atom/A) - if(A) - ram_turf(get_turf(A)) - playsound(src.loc, meteorsound, 40, 1) - get_hit() + ram_turf(get_turf(bumped_atom)) + playsound(loc, meteorsound, 40, TRUE) + get_hit() /obj/effect/meteor/CanAllowThrough(atom/movable/mover, border_dir) @@ -145,15 +194,32 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops return TRUE -/obj/effect/meteor/proc/ram_turf(var/turf/T) +/obj/effect/meteor/proc/chase_target(atom/chasing, delay, home) + if(!isatom(chasing)) + return + var/datum/move_loop/new_loop = SSmove_manager.move_towards(src, chasing, delay, home, lifetime) + if(!new_loop) + return + + RegisterSignal(new_loop, COMSIG_PARENT_QDELETING, PROC_REF(handle_stopping)) + + +///Deals with what happens when we stop moving, IE we die +/obj/effect/meteor/proc/handle_stopping() + SIGNAL_HANDLER + if(!QDELETED(src)) + qdel(src) + + +/obj/effect/meteor/proc/ram_turf(turf/target_turf) //first bust whatever is in the turf - for(var/atom/A in T) - if(A != src) - A.ex_act(hitpwr) + for(var/atom/thing as anything in (target_turf.contents - src)) + thing.ex_act(hitpwr) //then, ram the turf if it still exists - if(T) - T.ex_act(hitpwr) + if(!QDELETED(target_turf)) + target_turf.ex_act(hitpwr) + //process getting 'hit' by colliding with a dense object //or randomly when ramming turfs @@ -161,167 +227,234 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops hits-- if(hits <= 0) make_debris() - meteor_effect(heavy) + meteor_effect() qdel(src) + /obj/effect/meteor/ex_act() return -/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/pickaxe)) + +/obj/effect/meteor/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/pickaxe)) make_debris() qdel(src) return return ..() + /obj/effect/meteor/proc/make_debris() + if(!meteordrop) + return for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/meteor_effect(var/sound=1) - if(sound) - var/sound/meteor_sound = sound(meteorsound) - var/random_frequency = get_rand_frequency() - - for(var/P in GLOB.player_list) - var/mob/M = P - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - if(prob(50)) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) + var/spawn_type = meteordrop + if(islist(meteordrop)) + spawn_type = pick(meteordrop) + var/obj/item/thing_to_spawn = new spawn_type(get_turf(src)) + if(dest) + thing_to_spawn.throw_at(dest, 5, 10) + + +/obj/effect/meteor/proc/meteor_effect() + if(!heavy) + return + + var/sound/meteor_sound = sound(meteorsound) + var/random_frequency = get_rand_frequency() + + for(var/mob/mob as anything in GLOB.player_list) + var/turf/mob_turf = get_turf(mob) + if(!mob_turf || mob_turf.z != z) + continue + var/dist = get_dist(mob.loc, loc) + if(prob(shake_chance)) + shake_camera(mob, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + mob.playsound_local(loc, null, 50, TRUE, random_frequency, 10, S = meteor_sound) + + +/** + * Handles the meteor's interaction with meteor shields. + * + * Returns TRUE if the meteor should be destroyed. Overridable for custom shield interaction. + * Return FALSE if a meteor's interaction with meteor shields should NOT destroy it. + * + * Arguments: + * * defender - The meteor shield that is vaporizing us. + */ +/obj/effect/meteor/proc/shield_defense(obj/machinery/satellite/meteor_shield/defender) + return TRUE + /////////////////////// //Meteor types /////////////////////// -//Dust -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteorsound = 'sound/weapons/tap.ogg' - meteordrop = /obj/item/stack/ore/glass - //Medium-sized /obj/effect/meteor/medium name = "meteor" dropamt = 3 + /obj/effect/meteor/medium/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 1, 2, 3, 0, cause = src) + . = ..() + explosion(loc, 0, 1, 2, 3, adminlog = FALSE, cause = src) + //Large-sized /obj/effect/meteor/big name = "large meteor" icon_state = "large" + heavy = TRUE hits = 6 - heavy = 1 dropamt = 4 + /obj/effect/meteor/big/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0, cause = src) + . = ..() + explosion(loc, 1, 2, 3, 4, adminlog = FALSE, cause = src) + //Flaming meteor /obj/effect/meteor/flaming name = "flaming meteor" icon_state = "flaming" hits = 5 - heavy = 1 + heavy = TRUE meteorsound = 'sound/effects/bamf.ogg' meteordrop = /obj/item/stack/ore/plasma + /obj/effect/meteor/flaming/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5, cause = src) + . = ..() + explosion(loc, 1, 2, 3, 4, adminlog = FALSE, flame_range = 5, cause = src) + //Radiation meteor /obj/effect/meteor/irradiated name = "glowing meteor" icon_state = "glowing" - heavy = 1 + heavy = TRUE meteordrop = /obj/item/stack/ore/uranium /obj/effect/meteor/irradiated/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 0, 4, 3, 0, cause = src) + . = ..() + explosion(loc, 0, 0, 4, 3, adminlog = FALSE, cause = src) new /obj/effect/decal/cleanable/greenglow(get_turf(src)) for(var/mob/living/L in view(5, src)) L.apply_effect(40, IRRADIATE) + //Station buster Tunguska /obj/effect/meteor/tunguska name = "tunguska meteor" icon_state = "flaming" desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." hits = 30 - hitpwr = 1 - heavy = 1 + hitpwr = EXPLODE_DEVASTATE + heavy = TRUE meteorsound = 'sound/effects/bamf.ogg' meteordrop = /obj/item/stack/ore/plasma + /obj/effect/meteor/tunguska/meteor_effect() - ..(heavy) - explosion(src.loc, 5, 10, 15, 20, 0, cause = src) + . = ..() + explosion(loc, 5, 10, 15, 20, adminlog = FALSE, cause = src) -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8, cause = src) + +/obj/effect/meteor/tunguska/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.) || !prob(20)) + return . + explosion(loc, 2, 4, 6, 8, cause = src) //Gore /obj/effect/meteor/gore - name = "Oraganic Debris" + name = "organic debris" icon = 'icons/mob/human.dmi' - icon_state = "body_m_s" + icon_state = "fatbody_s" hits = 1 - hitpwr = 0 + hitpwr = EXPLODE_NONE meteorsound = 'sound/effects/blobattack.ogg' meteordrop = /obj/item/reagent_containers/food/snacks/meat var/meteorgibs = /obj/effect/gibspawner/generic + /obj/effect/meteor/gore/make_debris() - ..() + . = ..() new meteorgibs(get_turf(src)) -/obj/effect/meteor/gore/ram_turf(turf/T) - if(!isspaceturf(T)) - new /obj/effect/decal/cleanable/blood(T) +/obj/effect/meteor/gore/ram_turf(turf/target_turf) + if(!isspaceturf(target_turf)) + new /obj/effect/decal/cleanable/blood(target_turf) + -/obj/effect/meteor/gore/Bump(atom/A) - A.ex_act(hitpwr) +/obj/effect/meteor/gore/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.) || QDELETED(bumped_atom)) + return . + bumped_atom.ex_act(hitpwr) get_hit() + //Meteor Ops -/obj/effect/meteor/goreops - name = "MeteorOps" +/obj/effect/meteor/gore/ops + name = "meteorOps" icon = 'icons/mob/animal.dmi' icon_state = "syndicaterangedpsace" hits = 10 - hitpwr = 1 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic + hitpwr = EXPLODE_DEVASTATE -/obj/effect/meteor/goreops/make_debris() - ..() - new meteorgibs(get_turf(src)) +/obj/effect/meteor/gore/pigops + name = "pigOps" + icon = 'icons/mob/animal.dmi' + icon_state = "pig" + hitpwr = EXPLODE_DEVASTATE + hits = 3 + shake_chance = 20 -/obj/effect/meteor/goreops/ram_turf(turf/T) - if(!isspaceturf(T)) - new /obj/effect/decal/cleanable/blood(T) -/obj/effect/meteor/goreops/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() +//Dust +/obj/effect/meteor/dust + name = "dust" + desc = "Dust in space." + icon_state = "dust" + pass_flags = PASSTABLE|PASSGRILLE + hits = 1 + hitpwr = EXPLODE_LIGHT + meteorsound = 'sound/weapons/tap.ogg' + meteordrop = /obj/item/stack/ore/glass + + +// Space Dust +/obj/effect/meteor/space_dust + name = "space dust" + desc = "Dust in space." + icon_state = "space_dust" + heavy = TRUE + hitpwr = EXPLODE_HEAVY + hits = 2 + meteordrop = null + + +/obj/effect/meteor/space_dust/ex_act(severity) + qdel(src) + + +/obj/effect/meteor/space_dust/weak + hitpwr = EXPLODE_LIGHT + hits = 1 + + +/obj/effect/meteor/space_dust/strong + hitpwr = EXPLODE_DEVASTATE + hits = 6 + + +/obj/effect/meteor/space_dust/super + hitpwr = EXPLODE_DEVASTATE + hits = 40 + diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm index 790b05a17cc..7b76c0c2793 100644 --- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm +++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm @@ -338,7 +338,7 @@ /mob/living/simple_animal/demon/pulse_demon/proc/is_valid_apc(obj/machinery/power/apc/A) return istype(A) && !(A.stat & BROKEN) && !A.shorted -/mob/living/simple_animal/demon/pulse_demon/Move(newloc) +/mob/living/simple_animal/demon/pulse_demon/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/obj/machinery/power/new_power = locate(/obj/machinery/power) in newloc var/obj/structure/cable/new_cable = locate(/obj/structure/cable) in newloc diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm index f32e3abe4b7..bec719818d4 100644 --- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm +++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm @@ -124,7 +124,7 @@ var/mob/living/simple_animal/demon/pulse_demon/demon = user if(demon.bot_movedelay <= world.time && dir) Move(get_step(get_turf(src), dir)) - demon.bot_movedelay = world.time + (BOT_STEP_DELAY * (base_speed - 1)) * ((dir in GLOB.diagonals) ? SQRT_2 : 1) + demon.bot_movedelay = world.time + (BOT_STEP_DELAY * (base_speed - 1)) * ((dir in GLOB.diagonals) ? sqrt(2) : 1) /obj/machinery/recharger/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user) user.forceMove(src) diff --git a/code/game/gamemodes/miniantags/guardian/types/fire.dm b/code/game/gamemodes/miniantags/guardian/types/fire.dm index d060bd60782..86f72a645d0 100644 --- a/code/game/gamemodes/miniantags/guardian/types/fire.dm +++ b/code/game/gamemodes/miniantags/guardian/types/fire.dm @@ -36,29 +36,24 @@ new /obj/effect/temp_visual/guardian/phase/out(get_turf(M)) summoner.AdjustHallucinate(10 SECONDS) -/mob/living/simple_animal/hostile/guardian/fire/Crossed(AM as mob|obj, oldloc) - ..() + +/mob/living/simple_animal/hostile/guardian/fire/Crossed(atom/movable/AM, oldloc) + . = ..() collision_ignite(AM) -/mob/living/simple_animal/hostile/guardian/fire/Bumped(atom/movable/moving_atom) - ..() - collision_ignite(moving_atom) -/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj) - ..() - collision_ignite(AM) +/mob/living/simple_animal/hostile/guardian/fire/MobBump(mob/bumped_mob) + . = ..() + collision_ignite(bumped_mob) + -/mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(AM as mob|obj) +/mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(atom/movable/AM) if(isliving(AM)) var/mob/living/M = AM if(AM != summoner && M.fire_stacks < 7) M.fire_stacks = 7 M.IgniteMob() -/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj) - ..() - collision_ignite(AM) - /obj/effect/proc_holder/spell/aoe/guardian_hallucination name = "Волна галлюцинаций" diff --git a/code/game/gamemodes/shadowling/ascendant_shadowling.dm b/code/game/gamemodes/shadowling/ascendant_shadowling.dm index 552360dff47..45e01eb5e94 100644 --- a/code/game/gamemodes/shadowling/ascendant_shadowling.dm +++ b/code/game/gamemodes/shadowling/ascendant_shadowling.dm @@ -43,7 +43,7 @@ icon_living = "NurnKal" update_icon(UPDATE_OVERLAYS) -/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //copypasta from carp code /mob/living/simple_animal/ascendant_shadowling/ex_act(severity) diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index 934e2ad24f1..a0cb80dd7be 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -388,9 +388,9 @@ GLOBAL_LIST_EMPTY(deathsquad_teles) /obj/structure/deathsquad_tele/Bumped(atom/movable/moving_atom) + . = ..() if(!ztarget || !working) - return ..() - + return . INVOKE_ASYNC(src, PROC_REF(async_bump_effect), moving_atom) diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index 409f107a9bc..4b16a47717e 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -116,12 +116,12 @@ if(!surpress_send) send_status() /obj/machinery/door/airlock/Bumped(atom/movable/moving_atom) - ..(moving_atom) + . = ..() if(ismecha(moving_atom)) var/obj/mecha/mecha = moving_atom if(density && radio_connection && mecha.occupant && (allowed(mecha.occupant) || check_access_list(mecha.operation_req_access))) send_status(1) - return + /obj/machinery/door/airlock/set_frequency(new_frequency) SSradio.remove_object(src, frequency) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 9c2e3cde763..c2dc48f8067 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -94,11 +94,11 @@ QDEL_NULL(spark_system) return ..() -/obj/machinery/door/Bumped(atom/movable/moving_atom) - ..() +/obj/machinery/door/Bumped(atom/movable/moving_atom, skip_effects = FALSE) + . = ..() - if(operating || emagged) - return + if(skip_effects || operating || emagged) + return . if(ismob(moving_atom)) var/mob/B = moving_atom if((isrobot(B)) && B.stat) @@ -130,9 +130,9 @@ cmag_switch(TRUE, mecha.occupant) return do_animate("deny") - return -/obj/machinery/door/Move(new_loc, new_dir) + +/obj/machinery/door/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/turf/T = loc . = ..() move_update_air(T) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index ae7735d96f7..d4b9ef9842c 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -78,13 +78,11 @@ affecting_areas.Cut() return ..() -/obj/machinery/door/firedoor/Bumped(atom/movable/moving_atom) + +/obj/machinery/door/firedoor/Bumped(atom/movable/moving_atom, skip_effects = FALSE) if(panel_open || operating) - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom) - return - if(!density) - return ..() - return 0 + return ..(moving_atom, TRUE) + return ..(moving_atom, density) /obj/machinery/door/firedoor/proc/adjust_light() diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 35d93b4d3fd..56487fd03cc 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -26,12 +26,9 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF hackable = FALSE -/obj/machinery/door/poddoor/Bumped(atom/movable/moving_atom) - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom) - if(density) - return - else - return 0 + +/obj/machinery/door/poddoor/Bumped(atom/movable/moving_atom, skip_effects = TRUE) + . = ..() /obj/machinery/door/poddoor/impassable/preopen icon_state = "open" diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index bc885f22037..1ac89d39d0d 100644 --- a/code/game/machinery/doors/unpowered.dm +++ b/code/game/machinery/doors/unpowered.dm @@ -1,11 +1,8 @@ /obj/machinery/door/unpowered explosion_block = 1 -/obj/machinery/door/unpowered/Bumped(atom/movable/moving_atom) - if(locked) - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom) - return - ..() +/obj/machinery/door/unpowered/Bumped(atom/movable/moving_atom, skip_effects = TRUE) + . = ..() /obj/machinery/door/unpowered/attackby(obj/item/I, mob/user, params) if(locked) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 38cd8a945a4..d9b8990b5c1 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -69,10 +69,10 @@ sleep(20) close() -/obj/machinery/door/window/Bumped(atom/movable/moving_atom) - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom) +/obj/machinery/door/window/Bumped(atom/movable/moving_atom, skip_effects = TRUE) + . = ..() if(operating || !density) - return + return . if(!ismob(moving_atom)) if(ismecha(moving_atom)) var/obj/mecha/mecha = moving_atom diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index a398b59e972..256b97d3170 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -504,7 +504,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ return ..() -/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = NONE) +/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index f466b97cc43..ceb17841630 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -78,7 +78,7 @@ if(bag) . += bag.examine(user) -/obj/machinery/iv_drip/Move(NewLoc, direct) +/obj/machinery/iv_drip/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(!.) // ..() will return 0 if we didn't actually move anywhere. return . diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 4d98f81c249..6fe6387af9c 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -72,15 +72,14 @@ desc = "Now you're here, now you're over there." density = TRUE -/obj/machinery/mass_driver/bumper/Bumped(atom/movable/moving_atom) - ..() +/obj/machinery/mass_driver/bumper/Bumped(atom/movable/moving_atom) + . = ..() set_density(FALSE) step(moving_atom, get_dir(moving_atom, src)) spawn(1) set_density(TRUE) drive() - return ////////////////MASS DRIVER FRAME/////////////////// diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index c36adb4cfff..7c75ebfdfa6 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -99,8 +99,7 @@ qdel(src) /obj/machinery/recharge_station/Bumped(atom/movable/moving_atom) - ..() - + . = ..() if(ismob(moving_atom)) move_inside(moving_atom) diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index eda89339c75..1121d772068 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -88,26 +88,15 @@ icon_state = icon_name + "[is_powered]" + "[(blood ? "bld" : "")]" // add the blood tag at the end -// This is purely for admin possession !FUN!. -/obj/machinery/recycler/Bump(atom/movable/AM) - ..() - if(AM) - Bumped(AM) - /obj/machinery/recycler/Bumped(atom/movable/moving_atom) - ..() - - if(stat & (BROKEN|NOPOWER)) - return - if(!anchored) - return - if(emergency_mode) - return - + . = ..() + if((stat & (BROKEN|NOPOWER)) || !anchored || emergency_mode) + return . var/move_dir = get_dir(loc, moving_atom.loc) if(move_dir == eat_dir) eat(moving_atom) + /obj/machinery/recycler/proc/eat(atom/AM0, sound = 1) var/list/to_eat = list(AM0) if(isitem(AM0)) diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index b23abf4d210..d074e71fc20 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -375,20 +375,20 @@ break return power_station -/obj/machinery/teleport/hub/Bumped(atom/movable/moving_atom) - ..() +/obj/machinery/teleport/hub/Bumped(atom/movable/moving_atom) + . = ..() if(!is_teleport_allowed(z) && !admin_usage) if(ismob(moving_atom)) to_chat(moving_atom, "You can't use this here.") - return + return . if(power_station && power_station.engaged && !panel_open && !blockAI(moving_atom) && !isspacepod(moving_atom)) if(!teleport(moving_atom) && isliving(moving_atom)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub visible_message(span_warning("[src] emits a loud buzz, as its teleport portal flickers and fails!")) playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE) power_station.toggle() // turn off the portal. use_power(5000) - return + /obj/machinery/teleport/hub/attackby(obj/item/I, mob/user, params) if(exchange_parts(user, I)) @@ -497,22 +497,21 @@ return TRUE return FALSE -/obj/machinery/teleport/perma/Bumped(atom/movable/moving_atom) - ..() - if(stat & (BROKEN|NOPOWER)) - return +/obj/machinery/teleport/perma/Bumped(atom/movable/moving_atom) + . = ..() + if((stat & (BROKEN|NOPOWER)) || !target || recalibrating || panel_open || blockAI(moving_atom)) + return . if(!is_teleport_allowed(z)) to_chat(moving_atom, "You can't use this here.") - return + return . + do_teleport(moving_atom, target) + use_power(5000) + if(tele_delay) + recalibrating = TRUE + update_icon() + addtimer(CALLBACK(src, PROC_REF(BumpedCallback)), tele_delay) - if(target && !recalibrating && !panel_open && !blockAI(moving_atom)) - do_teleport(moving_atom, target) - use_power(5000) - if(tele_delay) - recalibrating = TRUE - update_icon() - addtimer(CALLBACK(src, PROC_REF(BumpedCallback)), tele_delay) /obj/machinery/teleport/perma/proc/BumpedCallback() recalibrating = FALSE diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm index 1bacab6a215..aecf632b16c 100644 --- a/code/game/machinery/transformer.dm +++ b/code/game/machinery/transformer.dm @@ -70,11 +70,11 @@ update_icon(UPDATE_ICON_STATE) /obj/machinery/transformer/Bumped(atom/movable/moving_atom) - ..() + . = ..() // They have to be human to be transformed. if(is_on_cooldown || !ishuman(moving_atom)) - return + return . var/mob/living/carbon/human/H = moving_atom var/move_dir = get_dir(loc, H.loc) @@ -126,10 +126,10 @@ desc = "Turns anything placed inside black and white." /obj/machinery/transformer/mime/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(is_on_cooldown) - return + return . // Crossed didn't like people lying down. if(istype(moving_atom)) @@ -137,7 +137,7 @@ do_transform_mime(moving_atom) else to_chat(moving_atom, "Only items can be greyscaled.") - return + /obj/machinery/transformer/proc/do_transform_mime(obj/item/I) if(is_on_cooldown || stat & (BROKEN|NOPOWER)) @@ -188,10 +188,10 @@ icon_state = initial(icon_state) /obj/machinery/transformer/xray/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(is_on_cooldown) - return + return . // Crossed didn't like people lying down. if(ishuman(moving_atom)) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 6744321c2a2..bb48133a3f0 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -169,20 +169,6 @@ /obj/item/projectile/beam/pulse/heavy name = "heavy pulse laser" icon_state = "pulse1_bl" - var/life = 20 - -/obj/item/projectile/beam/pulse/heavy/Bump(atom/A) - A.bullet_act(src, def_zone) - life -= 10 - if(ismob(A)) - var/mob/M = A - if(istype(firer, /mob)) - add_attack_logs(firer, M, "Mecha-shot with [src]") - else - add_attack_logs(null, M, "Mecha-shot with [src]") - if(life <= 0) - qdel(src) - return /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser @@ -239,10 +225,10 @@ if(isobj(H.shoes) && !HAS_TRAIT(H.shoes, TRAIT_NODROP)) var/thingy = H.shoes H.drop_item_ground(H.shoes) - walk_away(thingy,chassis,15,2) + SSmove_manager.move_away(thingy, chassis, 15, 2) spawn(20) if(thingy) - walk(thingy,0) + SSmove_manager.stop_looping(thingy) for(var/obj/mecha/combat/reticence/R in oview(6, chassis)) R.occupant_message("\The [R] has protected you from [chassis]'s HONK at the cost of some power.") R.use_power(R.get_charge() / 4) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index ba08e9f665b..00876139d65 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -334,31 +334,30 @@ ////////////////////////////////// //////// Movement procs //////// ////////////////////////////////// -/obj/mecha/Process_Spacemove(movement_dir = NONE) +/obj/mecha/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) . = ..() if(.) return TRUE - if(thrusters_active && movement_dir && use_power(step_energy_drain)) - return TRUE + //Turns strafe OFF if not enough energy to step (with actuator module only) if(strafe && actuator && !has_charge(actuator.energy_per_step)) toggle_strafe(silent = TRUE) - var/atom/movable/backup = get_spacemove_backup(movement_dir) - if(!backup) - return FALSE - - //get_spacemove_backup() already checks if a returned turf is solid, so we can just go - if(!istype(backup) || !movement_dir || backup.anchored) + var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move) + if(backup) + if(!istype(backup) || !movement_dir || backup.anchored || continuous_move) + return TRUE //get_spacemove_backup() already checks if a returned turf is solid, so we can just go + last_pushoff = world.time + if(backup.newtonian_move(REVERSE_DIR(movement_dir), instant = TRUE)) + backup.last_pushoff = world.time + if(occupant) + to_chat(occupant, span_info("You push off of [backup] to propel yourself.")) return TRUE - last_pushoff = world.time - if(backup.newtonian_move(REVERSE_DIR(movement_dir))) - backup.last_pushoff = world.time - if(occupant) - to_chat(occupant, span_info("You push off of [backup] to propel yourself.")) + if(thrusters_active && movement_dir && use_power(step_energy_drain)) + return TRUE - return TRUE + return FALSE /obj/mecha/relaymove(mob/user, direction) @@ -418,10 +417,6 @@ if(internal_damage & MECHA_INT_CONTROL_LOST) if(strafe) //No strafe while controls are malfunctioning toggle_strafe(silent = TRUE) - if(direction & (direction - 1)) //Trick to check for diagonal direction - glide_for(step_in * 1.41) - else - glide_for(step_in) move_result = mechsteprand() move_type = MECHAMOVE_RAND else if(direction & (UP|DOWN)) @@ -440,28 +435,25 @@ if(direction & (direction - 1)) //Trick to check for diagonal direction if(strafe) if(strafe_diagonal) //Diagonal strafe is overpowered, disabled by default on all mechas - glide_for(step_in * 1.41) step_in_final *= STRAFE_DIAGONAL_FACTOR //Applies speed multiplier if mecha moved diagonally move_result = mechstep(direction, old_direction, step_in_final) move_type = MECHAMOVE_STEP else - glide_for(step_in) strafed_backwards = is_opposite_dir(convert_diagonal_dir(direction)) step_in_final *= strafed_backwards ? STRAFE_BACKWARDS_FACTOR : 1 //Applies speed multiplier if mecha moved backwards move_result = mechstep(convert_diagonal_dir(direction), old_direction, step_in_final) //Any diagonal movement will be converted to cardinal via "convert_diagonal_dir" proc move_type = MECHAMOVE_STEP else - glide_for(step_in * 1.41) move_result = mechstep(direction) move_type = MECHAMOVE_STEP else - glide_for(step_in) strafed_backwards = is_opposite_dir(direction) step_in_final *= strafed_backwards ? STRAFE_BACKWARDS_FACTOR : 1 //Applies speed multiplier if mecha moved backwards move_result = mechstep(direction, old_direction, step_in_final) move_type = MECHAMOVE_STEP if(move_result && move_type) + set_glide_size(DELAY_TO_GLIDE_SIZE(step_in_final)) if(strafe && actuator) //Energy drain mechanics for actuator module use_power(strafed_backwards ? (actuator.energy_per_step * STRAFE_BACKWARDS_FACTOR) : actuator.energy_per_step) aftermove(move_type) @@ -515,10 +507,6 @@ if(can_move < world.time) . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself flick("[initial_icon]-phase", src) - if(direction & (direction - 1)) //moved diagonally - glide_for(step_in * 4.23) - else - glide_for(step_in * 3) forceMove(get_step(src, direction)) use_power(phasing_energy_drain) playsound(src, stepsound, 40, 1) @@ -531,70 +519,72 @@ if(. && stepsound) playsound(src, stepsound, 40, 1) -/obj/mecha/Bump(var/atom/obstacle, bump_allowed) - if(throwing) //high velocity mechas in your face! - var/breakthrough = 0 - if(istype(obstacle, /obj/structure/window)) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/grille/)) - var/obj/structure/grille/G = obstacle - G.obj_break() - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/table)) - var/obj/structure/table/T = obstacle - qdel(T) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/rack)) - new /obj/item/rack_parts(obstacle.loc) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) - obstacle.ex_act(1) - - else if(isliving(obstacle)) - var/mob/living/L = obstacle - var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') - if(L.flags & GODMODE) - return - L.take_overall_damage(5,0) - if(L.buckled) - L.buckled = 0 - L.Weaken(10 SECONDS) - L.apply_effect(STUTTER, 10 SECONDS) - playsound(src, pick(hit_sound), 50, 0, 0) - breakthrough = 1 - else - if(throwing) - throwing.finalize() - crashing = null +/obj/mecha/Bump(atom/bumped_atom, custom_bump) + if(!custom_bump) + return null + + if(!throwing) + . = ..() + if(.) + return . + if(isobj(bumped_atom)) + var/obj/bumped_object = bumped_atom + if(!bumped_object.anchored) + step(bumped_atom, dir) + else if(ismob(bumped_atom)) + step(bumped_atom, dir) + return . + + //high velocity mechas in your face! + var/breakthrough = FALSE + if(istype(bumped_atom, /obj/structure/window)) + qdel(bumped_atom) + breakthrough = TRUE + + else if(istype(bumped_atom, /obj/structure/grille)) + var/obj/structure/grille/grille = bumped_atom + grille.obj_break() + breakthrough = TRUE + + else if(istype(bumped_atom, /obj/structure/table)) + qdel(bumped_atom) + breakthrough = TRUE + + else if(istype(bumped_atom, /obj/structure/rack)) + new /obj/item/rack_parts(bumped_atom.loc) + qdel(bumped_atom) + breakthrough = TRUE + + else if(istype(bumped_atom, /obj/structure/reagent_dispensers/fueltank)) + bumped_atom.ex_act(EXPLODE_DEVASTATE) + + else if(isliving(bumped_atom)) + var/mob/living/bumped_living = bumped_atom + if(bumped_living.flags & GODMODE) + return + var/static/list/mecha_hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') + bumped_living.take_overall_damage(5) + bumped_living.unbuckle_mob(force = TRUE) + bumped_living.Weaken(10 SECONDS) + bumped_living.apply_effect(STUTTER, 10 SECONDS) + playsound(src, pick(mecha_hit_sound), 50, FALSE) + breakthrough = TRUE + else + throwing.finalize() + crashing = null - ..() + . = ..() - if(breakthrough) - if(crashing) - spawn(1) - throw_at(crashing, 50, throw_speed) - else - spawn(1) - crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. - throw_at(crashing, 50, throw_speed) + if(breakthrough) + if(crashing) + spawn(1) + throw_at(crashing, 50, throw_speed) + else + spawn(1) + crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. + throw_at(crashing, 50, throw_speed) - else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) - step(obstacle, dir) /////////////////////////////////// diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index af4bd1ad0f7..6b070361521 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -84,7 +84,7 @@ target.set_buckled(src) buckled_mobs |= target target.throw_alert(ALERT_BUCKLED, /atom/movable/screen/alert/restrained/buckled) - //target.set_glide_size(glide_size) + target.set_glide_size(glide_size) target.Move(loc) target.setDir(dir) @@ -136,7 +136,7 @@ buckled_mob.set_buckled(null) buckled_mob.set_anchored(initial(buckled_mob.anchored)) buckled_mob.clear_alert(ALERT_BUCKLED) - //buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.cached_multiplicative_slowdown)) + buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.cached_multiplicative_slowdown)) buckled_mobs -= buckled_mob if(anchored) REMOVE_TRAIT(buckled_mob, TRAIT_NO_FLOATING_ANIM, BUCKLED_TRAIT) diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 1fe78d5ea00..359b91ff17c 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -122,12 +122,19 @@ . = ..() gravShock(AM) -/obj/effect/anomaly/grav/Bump(atom/A) - gravShock(A) + +/obj/effect/anomaly/grav/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . + gravShock(bumped_atom) + /obj/effect/anomaly/grav/Bumped(atom/movable/moving_atom) + . = ..() gravShock(moving_atom) + /obj/effect/anomaly/grav/proc/gravShock(mob/living/A) if(boing && isliving(A) && !A.stat) if(!knockdown) // no hardstuns with megafauna @@ -161,10 +168,14 @@ . = ..() mobShock(AM) -/obj/effect/anomaly/flux/Bump(atom/A) - mobShock(A) +/obj/effect/anomaly/flux/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . + mobShock(bumped_atom) /obj/effect/anomaly/flux/Bumped(atom/movable/moving_atom) + . = ..() mobShock(moving_atom) /obj/effect/anomaly/flux/proc/mobShock(mob/living/M) @@ -199,6 +210,7 @@ investigate_log("teleported [key_name_log(M)] to [COORD(M)]", INVESTIGATE_TELEPORTATION) /obj/effect/anomaly/bluespace/Bumped(atom/movable/moving_atom) + . = ..() if(isliving(moving_atom)) do_teleport(moving_atom, moving_atom, 8) investigate_log("teleported [key_name_log(moving_atom)] to [COORD(moving_atom)]", INVESTIGATE_TELEPORTATION) diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm index a6a3621d48f..e8c4a2b62df 100644 --- a/code/game/objects/effects/bump_teleporter.dm +++ b/code/game/objects/effects/bump_teleporter.dm @@ -25,20 +25,18 @@ GLOBAL_LIST_EMPTY(bump_teleporters) /obj/effect/bump_teleporter/singularity_pull() return + /obj/effect/bump_teleporter/Bumped(atom/movable/moving_atom) - if (!ismob(moving_atom)) - // user.loc = src.loc // Stop at teleporter location - return - - if (!id_target) - // user.loc = src.loc // Stop at teleporter location, there is nowhere to teleport to. - return - for(var/bt in GLOB.bump_teleporters) - var/obj/effect/bump_teleporter/teleporter = bt + . = ..() + if(!id_target || !ismob(moving_atom)) + return . + + for(var/obj/effect/bump_teleporter/teleporter as anything in GLOB.bump_teleporters) if(teleporter.id == id_target) moving_atom.forceMove(teleporter.loc) process_special_effects(moving_atom) - return + break + ///Special effects for teleporter. Supposed to be overriden. /obj/effect/bump_teleporter/proc/process_special_effects(mob/living/target) diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index 204b341d4d0..6cde8347aaa 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -11,69 +11,78 @@ would spawn and follow the beaker, even if it is carried or thrown. pass_flags = PASSTABLE | PASSGRILLE anchored = TRUE -/obj/effect/particle_effect/New() - ..() - if(SSticker) - GLOB.cameranet.updateVisibility(src) + +/obj/effect/particle_effect/Initialize(mapload) + . = ..() + GLOB.cameranet.updateVisibility(src) + /obj/effect/particle_effect/Destroy() - if(SSticker) - GLOB.cameranet.updateVisibility(src) + GLOB.cameranet.updateVisibility(src) return ..() + +// Prevents effects from getting registered for SSspacedrift +/obj/effect/particle_effect/newtonian_move(direction, instant = FALSE, start_delay = 0) + return TRUE + + /datum/effect_system var/number = 3 - var/cardinals = 0 + var/cardinals = FALSE var/turf/location var/atom/holder var/effect_type var/total_effects = 0 var/autocleanup = FALSE //will delete itself after use + /datum/effect_system/Destroy() holder = null location = null return ..() -/datum/effect_system/proc/set_up(n = 3, c = 0, loca) - if(n > 10) - n = 10 - number = n - cardinals = c - if(isturf(loca)) - location = loca - else - location = get_turf(loca) + +/datum/effect_system/proc/set_up(number = 3, cardinals = FALSE, location) + src.number = min(number, 10) + src.cardinals = cardinals + src.location = get_turf(location) + /datum/effect_system/proc/attach(atom/atom) holder = atom + /datum/effect_system/proc/start() if(QDELETED(src)) return for(var/i in 1 to number) if(total_effects > 20) return - INVOKE_ASYNC(src, PROC_REF(generate_effect)) + generate_effect() + /datum/effect_system/proc/generate_effect() if(holder) location = get_turf(holder) - var/obj/effect/E = new effect_type(location) + var/obj/effect/effect = new effect_type(location) total_effects++ var/direction if(cardinals) direction = pick(GLOB.cardinal) else direction = pick(GLOB.alldirs) - var/steps_amt = pick(1,2,3) - for(var/j in 1 to steps_amt) - sleep(5) - step(E,direction) - if(!QDELETED(src)) - addtimer(CALLBACK(src, PROC_REF(decrement_total_effect)), 20) - -/datum/effect_system/proc/decrement_total_effect() + var/step_amt = pick(1,2,3) + var/step_delay = 5 + + var/datum/move_loop/loop = SSmove_manager.move(effect, direction, step_delay, timeout = step_delay * step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(decrement_total_effect)) + + +/datum/effect_system/proc/decrement_total_effect(datum/source) + SIGNAL_HANDLER total_effects-- - if(autocleanup && total_effects <= 0) - qdel(src) + if(!autocleanup || total_effects > 0) + return + QDEL_IN(src, 2 SECONDS) + diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index 9276465daed..c6d1b3ce437 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -1,51 +1,62 @@ /obj/effect/particle_effect/expl_particles - name = "explosive particles" + name = "fire" icon_state = "explosion_particle" - opacity = 1 + opacity = TRUE anchored = TRUE -/obj/effect/particle_effect/expl_particles/New() + +/obj/effect/particle_effect/expl_particles/Initialize(mapload) ..() - QDEL_IN(src, 15) + return INITIALIZE_HINT_LATELOAD + + +/obj/effect/particle_effect/expl_particles/LateInitialize() + var/step_amt = pick(25;1,50;2,100;3,200;4) + + var/datum/move_loop/loop = SSmove_manager.move(src, pick(GLOB.alldirs), 1, timeout = step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(end_particle)) + + +/obj/effect/particle_effect/expl_particles/proc/end_particle(datum/source) + SIGNAL_HANDLER + if(QDELETED(src)) + return + qdel(src) + /datum/effect_system/expl_particles number = 10 + /datum/effect_system/expl_particles/start() for(var/i in 1 to number) - spawn(0) - var/obj/effect/particle_effect/expl_particles/expl = new /obj/effect/particle_effect/expl_particles(location) - var/direct = pick(GLOB.alldirs) - var/steps_amt = pick(1;25,2;50,3,4;200) - for(var/j in 1 to steps_amt) - sleep(1) - step(expl,direct) + new /obj/effect/particle_effect/expl_particles(location) + /obj/effect/explosion name = "explosive particles" icon = 'icons/effects/96x96.dmi' icon_state = "explosion" - opacity = 1 + opacity = TRUE anchored = TRUE + layer = ABOVE_ALL_MOB_LAYER mouse_opacity = MOUSE_OPACITY_TRANSPARENT pixel_x = -32 pixel_y = -32 -/obj/effect/explosion/New() - ..() - QDEL_IN(src, 10) + +/obj/effect/explosion/Initialize(mapload) + . = ..() + QDEL_IN(src, 1 SECONDS) /datum/effect_system/explosion -/datum/effect_system/explosion/set_up(loca) - if(isturf(loca)) - location = loca - else - location = get_turf(loca) +/datum/effect_system/explosion/set_up(location) + src.location = get_turf(location) /datum/effect_system/explosion/start() new/obj/effect/explosion(location) - var/datum/effect_system/expl_particles/P = new/datum/effect_system/expl_particles() + var/datum/effect_system/expl_particles/P = new P.set_up(10, 0, location) P.start() @@ -58,4 +69,4 @@ /datum/effect_system/explosion/smoke/start() ..() - addtimer(CALLBACK(src, PROC_REF(create_smoke)), 5) + addtimer(CALLBACK(src, PROC_REF(create_smoke)), 0.5 SECONDS) diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index e0aaef0445a..9e0f6c3eb40 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -1,15 +1,141 @@ -/// Ion trails for spacepods and other space-flying things +///////////////////////////////////////////// +//////// Attach a trail to any object, that spawns when it moves (like for the jetpack) +/// just pass in the object to attach it to in set_up +/// Then do start() to start it and stop() to stop it, obviously +/// and don't call start() in a loop that will be repeated otherwise it'll get spammed! +///////////////////////////////////////////// + +/datum/effect_system/trail_follow + var/turf/oldposition + var/active = FALSE + var/allow_overlap = FALSE + var/auto_process = TRUE + var/qdel_in_time = 1 SECONDS + var/fadetype = "ion_fade" + var/fade = TRUE + var/nograv_required = FALSE + + +/datum/effect_system/trail_follow/set_up(atom/atom) + attach(atom) + oldposition = get_turf(atom) + + +/datum/effect_system/trail_follow/Destroy() + oldposition = null + stop() + return ..() + + +/datum/effect_system/trail_follow/proc/stop() + oldposition = null + STOP_PROCESSING(SSfastprocess, src) + active = FALSE + return TRUE + + +/datum/effect_system/trail_follow/start() + oldposition = get_turf(holder) + if(!check_conditions()) + return FALSE + if(auto_process) + START_PROCESSING(SSfastprocess, src) + active = TRUE + return TRUE + + +/datum/effect_system/trail_follow/process() + generate_effect() + + +/datum/effect_system/trail_follow/generate_effect() + if(!check_conditions()) + return stop() + if(oldposition && !(oldposition == get_turf(holder)) && (!oldposition.has_gravity() || !nograv_required)) + var/obj/effect/new_effect = new effect_type(oldposition) + set_dir(new_effect) + if(fade && fadetype) + flick(fadetype, new_effect) + new_effect.icon_state = "" + if(qdel_in_time) + QDEL_IN(new_effect, qdel_in_time) + oldposition = get_turf(holder) + + +/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/trails) + trails.setDir(holder.dir) + + +/datum/effect_system/trail_follow/proc/check_conditions() + if(!get_turf(holder)) + return FALSE + return TRUE + + +/datum/effect_system/trail_follow/ion + effect_type = /obj/effect/particle_effect/ion_trails + nograv_required = TRUE + qdel_in_time = 2 SECONDS + + +/datum/effect_system/trail_follow/ion/grav_allowed + nograv_required = FALSE + + +/datum/effect_system/trail_follow/spacepod + effect_type = /obj/effect/particle_effect/ion_trails + nograv_required = TRUE + qdel_in_time = 2 SECONDS + + +/datum/effect_system/trail_follow/spacepod/set_dir(obj/effect/particle_effect/ion_trails/trails1, obj/effect/particle_effect/ion_trails/trails2) + trails1.setDir(holder.dir) + trails2.setDir(holder.dir) + + +/datum/effect_system/trail_follow/spacepod/generate_effect() + if(!check_conditions()) + return stop() + if(oldposition && !(oldposition == get_turf(holder)) && (!oldposition.has_gravity() || !nograv_required)) + // spacepod loc is always southwest corner of 4x4 space + var/turf/our_turf = holder.loc + var/loc1 + var/loc2 + switch(holder.dir) + if(NORTH) + loc1 = get_step(our_turf, SOUTH) + loc2 = get_step(loc1, EAST) + if(SOUTH) // More difficult, offset to the north! + loc1 = get_step(get_step(our_turf, NORTH), NORTH) + loc2 = get_step(loc1, EAST) + if(EAST) // Just one to the north should suffice + loc1 = get_step(our_turf , WEST) + loc2 = get_step(loc1, NORTH) + if(WEST) // One to the east and north from there + loc1 = get_step(get_step(our_turf, EAST), EAST) + loc2 = get_step(loc1, NORTH) + var/obj/effect/effect1 = new effect_type(loc1) + var/obj/effect/effect2 = new effect_type(loc2) + set_dir(effect1, effect2) + if(fade && fadetype) + flick(fadetype, effect1) + flick(fadetype, effect2) + effect1.icon_state = "" + effect2.icon_state = "" + if(qdel_in_time) + QDEL_IN(effect1, qdel_in_time) + QDEL_IN(effect2, qdel_in_time) + oldposition = get_turf(holder) + + /obj/effect/particle_effect/ion_trails name = "ion trails" icon_state = "ion_trails" - anchored = TRUE - -/obj/effect/particle_effect/ion_trails/Initialize(mapload, targetdir) - . = ..() - dir = targetdir - flick("ion_fade", src) - icon_state = null - QDEL_IN(src, 2 SECONDS) + + +/obj/effect/particle_effect/ion_trails/flight + icon_state = "ion_trails_flight" + //Reagent-based explosion effect /datum/effect_system/reagents_explosion diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm index 272cbebe3b2..1ef5d957809 100644 --- a/code/game/objects/effects/effect_system/effects_water.dm +++ b/code/game/objects/effects/effect_system/effects_water.dm @@ -2,27 +2,70 @@ /obj/effect/particle_effect/water name = "water" icon_state = "extinguish" + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + pass_flags = PASSTABLE|PASSMACHINE|PASSSTRUCTURE|PASSGRILLE|PASSBLOB|PASSVEHICLE var/life = 15 -/obj/effect/particle_effect/water/New() - ..() - QDEL_IN(src, 70) +/obj/effect/particle_effect/water/Initialize(mapload) + . = ..() + QDEL_IN(src, 7 SECONDS) + -/obj/effect/particle_effect/water/Move(turf/newloc) +/obj/effect/particle_effect/water/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(--life < 1) - qdel() - return 0 - if(newloc.density) - return 0 - . = ..() + qdel(src) + return FALSE + return ..() + -/obj/effect/particle_effect/water/Bump(atom/A) +/obj/effect/particle_effect/water/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . if(reagents) - reagents.reaction(A) - if(istype(A,/atom/movable)) - var/atom/movable/AM = A - AM.water_act(life, COLD_WATER_TEMPERATURE, src) - return ..() + reagents.reaction(bumped_atom) + bumped_atom.water_act(life, COLD_WATER_TEMPERATURE, src) + + +///Extinguisher snowflake +/obj/effect/particle_effect/water/extinguisher + + +/obj/effect/particle_effect/water/extinguisher/Move(atom/newloc, direct = NONE, glide_size_override = 0) + . = ..() + if(!reagents) + return + var/turf/source_turf = get_turf(src) + reagents.reaction(source_turf) + for(var/atom/thing as anything in source_turf) + reagents.reaction(source_turf) + + +/// Starts the effect moving at a target with a delay in deciseconds, and a lifetime in moves +/// Returns the created loop +/obj/effect/particle_effect/water/extinguisher/proc/move_at(atom/target, delay, lifetime) + var/datum/move_loop/loop = SSmove_manager.move_towards_legacy(src, target, delay, timeout = delay * lifetime, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_forcemove)) + RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(movement_stopped)) + return loop + + +/obj/effect/particle_effect/water/extinguisher/proc/post_forcemove(datum/move_loop/source, success) + SIGNAL_HANDLER + if(!success) + end_life(source) + + +/obj/effect/particle_effect/water/extinguisher/proc/movement_stopped(datum/move_loop/source) + SIGNAL_HANDLER + if(!QDELETED(src)) + end_life(source) + + +/obj/effect/particle_effect/water/extinguisher/proc/end_life(datum/move_loop/engine) + QDEL_IN(src, engine.delay) //Gotta let it stop drifting + animate(src, alpha = 0, time = engine.delay) + ///////////////////////////////////////////// // GENERIC STEAM SPREAD SYSTEM diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index ebcd597c409..b731d65179f 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -129,11 +129,6 @@ playsound(src.loc, user.dna.species.unarmed.attack_sound, 25, 1, -1) attack_generic(user, max_integrity/3) -/obj/structure/spider/spiderling/Bump(atom/user) - if(istype(user, /obj/structure/table)) - loc = user.loc - else - ..() /obj/structure/spider/spiderling/process() if(travelling_in_vent) @@ -186,8 +181,7 @@ for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) if(!v.welded) entry_vent = v - glide_for(3) - walk_to(src, entry_vent, 1) + SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4)) break if(isturf(loc)) amount_grown += rand(0,2) @@ -227,8 +221,7 @@ available_turfs += S if(!length(available_turfs)) return FALSE - glide_for(3) - walk_to(src, pick(available_turfs)) + SSmove_manager.move_to(src, pick(available_turfs), 1, rand(2, 4)) return TRUE /obj/structure/spider/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index bdc87eb5237..ea7bed32fbf 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -800,7 +800,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g * The default action is attack_self(). * Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob. */ -/obj/item/proc/ui_action_click(mob/user, actiontype, leftclick) +/obj/item/proc/ui_action_click(mob/user, action, leftclick) attack_self(user) diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 602e834f465..440920b1a93 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -16,14 +16,25 @@ attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") dog_fashion = /datum/dog_fashion/back resistance_flags = FIRE_PROOF + /// The max amount of water this extinguisher can hold. var/max_water = 50 - var/last_use = 1.0 - var/safety = 1 + /// Does the welder extinguisher start with water. + var/starting_water = TRUE + /// Cooldown between uses. + var/last_use = 1 + /// Can we refill this at a water tank? var/refilling = FALSE + /// Can we actually fire currently? + var/safety = TRUE + /// something that should be replaced with base_icon_state var/sprite_name = "fire_extinguisher" - var/power = 5 //Maximum distance launched water will travel - var/precision = 0 //By default, turfs picked from a spray are random, set to 1 to make it always have at least one water effect per row - var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled + /// Maximum distance launched water will travel. + var/power = 5 + /// By default, turfs picked from a spray are random, set to TRUE to make it always have at least one water effect per row. + var/precision = FALSE + /// Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled. + var/cooling_power = 2 + /obj/item/extinguisher/mini name = "pocket fire extinguisher" @@ -50,7 +61,7 @@ /obj/item/extinguisher/examine(mob/user) . = ..() - . += "The safety is [safety ? "on" : "off"]." + . += span_info("The safety is [safety ? "on" : "off"].") /obj/item/extinguisher/update_icon_state() @@ -62,8 +73,7 @@ desc = "The safety is [safety ? "on" : "off"]." - -/obj/item/extinguisher/attack_self(mob/user as mob) +/obj/item/extinguisher/attack_self(mob/user) safety = !safety update_appearance(UPDATE_ICON_STATE|UPDATE_DESC) to_chat(user, "The safety is [safety ? "on" : "off"].") @@ -76,27 +86,28 @@ else return ..() + /obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user) if(istype(target, /obj/structure/reagent_dispensers/watertank) && target.Adjacent(user)) var/safety_save = safety - safety = 1 + safety = TRUE if(reagents.total_volume == reagents.maximum_volume) - to_chat(user, "\The [src] is already full!") + to_chat(user, span_notice("[src] is already full!")) safety = safety_save - return 1 - var/obj/structure/reagent_dispensers/watertank/W = target - var/transferred = W.reagents.trans_to(src, max_water) + return TRUE + var/obj/structure/reagent_dispensers/watertank/watertank = target + var/transferred = watertank.reagents.trans_to(src, max_water) if(transferred > 0) - to_chat(user, "\The [src] has been refilled by [transferred] units") - playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) - for(var/datum/reagent/water/R in reagents.reagent_list) - R.cooling_temperature = cooling_power + to_chat(user, span_notice("[src] has been refilled by [transferred] units.")) + playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + for(var/datum/reagent/water/reagent as anything in reagents.reagent_list) + reagent.cooling_temperature = cooling_power else - to_chat(user, "\The [W] is empty!") + to_chat(user, span_notice("[watertank] is empty!")) safety = safety_save - return 1 - else - return 0 + return TRUE + return FALSE + /obj/item/extinguisher/afterattack(atom/target, mob/user , flag) . = ..() @@ -108,93 +119,77 @@ refilling = FALSE return - if(!safety) - if(src.reagents.total_volume < 1) - to_chat(usr, "\The [src] is empty.") - return - - if(world.time < src.last_use + 20) - return - - src.last_use = world.time - - if(reagents.chem_temp > 300 || reagents.chem_temp < 280) - add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K") - playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3) - - var/direction = get_dir(src,target) - - if(usr.buckled && isobj(usr.buckled) && !usr.buckled.anchored ) - spawn(0) - var/obj/structure/chair/C = null - if(istype(usr.buckled, /obj/structure/chair)) - C = usr.buckled - var/obj/B = usr.buckled - var/movementdirection = turn(direction,180) - if(C) C.propelled = 4 - B.glide_for(1) - usr.glide_for(1) - step(B, movementdirection) - sleep(1) - step(B, movementdirection) - if(C) C.propelled = 3 - sleep(1) - step(B, movementdirection) - sleep(1) - step(B, movementdirection) - if(C) C.propelled = 2 - B.glide_for(2) - usr.glide_for(2) - sleep(2) - step(B, movementdirection) - if(C) C.propelled = 1 - sleep(2) - step(B, movementdirection) - if(C) C.propelled = 0 - B.glide_for(3) - usr.glide_for(3) - sleep(3) - step(B, movementdirection) - sleep(3) - step(B, movementdirection) - sleep(3) - step(B, movementdirection) - - else user.newtonian_move(turn(direction, 180)) - - var/turf/T = get_turf(target) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - var/list/the_targets = list(T,T1,T2) - if(precision) - var/turf/T3 = get_step(T1, turn(direction, 90)) - var/turf/T4 = get_step(T2,turn(direction, -90)) - the_targets = list(T,T1,T2,T3,T4) - - for(var/a=0, a<5, a++) - spawn(0) - var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water( get_turf(src) ) - var/turf/my_target = pick(the_targets) - if(precision) - the_targets -= my_target - var/datum/reagents/R = new/datum/reagents(5) - if(!W) return - W.reagents = R - R.my_atom = W - if(!W || !src) return - src.reagents.trans_to(W,1) - for(var/b=0, b<5, b++) - step_towards(W,my_target) - if(!W || !W.reagents) return - W.reagents.reaction(get_turf(W)) - for(var/atom/atm in get_turf(W)) - if(!W) return - W.reagents.reaction(atm) - if(isliving(atm)) //For extinguishing mobs on fire - var/mob/living/M = atm - M.ExtinguishMob() - - if(W.loc == my_target) break - sleep(2) + if(safety) + return + + if(reagents.total_volume < 1) + to_chat(user, span_danger("[src] is empty.")) + return + + if(world.time < last_use + 2 SECONDS) + return + last_use = world.time + + if(reagents.chem_temp > 300 || reagents.chem_temp < 280) + add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K") + playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3) + + var/direction = get_dir(src,target) + + if(user.buckled && isobj(user.buckled) && !user.buckled.anchored) + var/movementdirection = REVERSE_DIR(direction) + addtimer(CALLBACK(src, PROC_REF(move_chair), user.buckled, movementdirection), 0.1 SECONDS) else - return ..() + user.newtonian_move(REVERSE_DIR(direction)) + + //Get all the turfs that can be shot at + var/turf/T = get_turf(target) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + var/list/the_targets = list(T,T1,T2) + if(precision) + var/turf/T3 = get_step(T1, turn(direction, 90)) + var/turf/T4 = get_step(T2,turn(direction, -90)) + the_targets.Add(T3,T4) + + var/list/water_particles = list() + for(var/a in 1 to 5) + var/obj/effect/particle_effect/water/extinguisher/water = new (get_turf(src)) + var/my_target = pick(the_targets) + water_particles[water] = my_target + // If precise, remove turf from targets so it won't be picked more than once + if(precision) + the_targets -= my_target + var/datum/reagents/water_reagents = new(5) + water.reagents = water_reagents + water_reagents.my_atom = water + reagents.trans_to(water, 1) + + //Make em move dat ass, hun + move_particles(water_particles) + + +//Particle movement loop +/obj/item/extinguisher/proc/move_particles(list/particles) + var/delay = 2 + // Second loop: Get all the water particles and make them move to their target + for(var/obj/effect/particle_effect/water/extinguisher/water as anything in particles) + water.move_at(particles[water], delay, power) + + +//Chair movement loop +/obj/item/extinguisher/proc/move_chair(obj/buckled_object, movementdirection) + var/datum/move_loop/loop = SSmove_manager.move(buckled_object, movementdirection, 1, timeout = 9, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + //This means the chair slowing down is dependant on the extinguisher existing, which is weird + //Couldn't figure out a better way though + RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(manage_chair_speed)) + + +/obj/item/extinguisher/proc/manage_chair_speed(datum/move_loop/move/source) + SIGNAL_HANDLER + switch(source.lifetime) + if(4 to 5) + source.delay = 2 + if(1 to 3) + source.delay = 3 + diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index f1b1427cfd1..d4a8d44a53f 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -268,10 +268,11 @@ if(nadeassembly) nadeassembly.hear_message(M, msg) -/obj/item/grenade/chem_grenade/Bump() - ..() - if(nadeassembly) - nadeassembly.process_movement() +/obj/item/grenade/chem_grenade/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.) || !nadeassembly) + return . + nadeassembly.process_movement() /obj/item/grenade/chem_grenade/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // called when a throw stops ..() diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm index 44af94e16d3..fd4195a4093 100644 --- a/code/game/objects/items/weapons/grenades/clusterbuster.dm +++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm @@ -42,7 +42,7 @@ icon_state = "clusterbang_segment_active" payload = payload_type active = 1 - walk_away(src,loc,rand(1,4)) + SSmove_manager.move_away(src, loc, rand(1,4), 1) spawn(rand(15,60)) prime() @@ -64,7 +64,7 @@ var/obj/item/grenade/P = new type(loc) if(istype(P, /obj/item/grenade)) P.active = 1 - walk_away(P,loc,rand(1,4)) + SSmove_manager.move_away(P, loc, rand(1,4), 1) spawn(rand(15,60)) if(!QDELETED(P)) diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 7ab3942f399..e440961a233 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -17,12 +17,6 @@ var/display_timer = TRUE -/obj/item/grenade/Destroy() - ///We need to clear the walk_to on destroy to allow a grenade which uses walk_to or related to properly GC - walk_to(src, 0) - return ..() - - /obj/item/grenade/deconstruct(disassembled = TRUE) if(!disassembled) prime() @@ -104,7 +98,7 @@ return TRUE -/obj/item/grenade/attack_hand() - walk(src, null, null) - ..() +/obj/item/grenade/attack_hand(mob/user) + SSmove_manager.stop_looping(src) + . = ..() diff --git a/code/game/objects/items/weapons/implants/implant_stealth.dm b/code/game/objects/items/weapons/implants/implant_stealth.dm index a87c96dfb8a..d02b1204165 100644 --- a/code/game/objects/items/weapons/implants/implant_stealth.dm +++ b/code/game/objects/items/weapons/implants/implant_stealth.dm @@ -195,14 +195,15 @@ addtimer(CALLBACK(src, PROC_REF(go_invisible)), 1 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE) -/obj/structure/closet/cardboard/agent/Bump(atom/A, yes) +/obj/structure/closet/cardboard/agent/Bump(atom/bumped_atom, custom_bump) . = ..() - if(isliving(A)) - reveal() + if(. || isnull(.) || !isliving(bumped_atom)) + return . + reveal() -/obj/structure/closet/cardboard/agent/Bumped(atom/movable/A) +/obj/structure/closet/cardboard/agent/Bumped(atom/movable/moving_atom) . = ..() - if(isliving(A)) + if(isliving(moving_atom)) reveal() diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 77f9571f448..8f65466256b 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -7,51 +7,103 @@ distribute_pressure = ONE_ATMOSPHERE * O2STANDARD actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) var/gas_type = "oxygen" - var/on = 0 - var/stabilizers = 0 - var/volume_rate = 500 //Needed for borg jetpack transfer + var/on = FALSE + var/stabilize = FALSE + var/skip_trails = FALSE + var/thrust_callback + + +/obj/item/tank/jetpack/Initialize(mapload) + . = ..() + thrust_callback = CALLBACK(src, PROC_REF(allow_thrust), 0.01) + configure_jetpack(stabilize, skip_trails) + + +/obj/item/tank/jetpack/Destroy() + thrust_callback = null + return ..() + + +/** + * Configures/re-configures the jetpack component + * + * Arguments: + * * stabilize - if `TRUE` jetpack owner will not be affected by newtonian movement + * * skip_trails - if `TRUE` skips ion trails visualization + */ +/obj/item/tank/jetpack/proc/configure_jetpack(stabilize, skip_trails) + if(!isnull(stabilize)) + src.stabilize = stabilize + if(!isnull(skip_trails)) + src.skip_trails = skip_trails + AddComponent( + /datum/component/jetpack, \ + src.stabilize, \ + COMSIG_JETPACK_ACTIVATED, \ + COMSIG_JETPACK_DEACTIVATED, \ + JETPACK_ACTIVATION_FAILED, \ + thrust_callback, \ + /datum/effect_system/trail_follow/ion, \ + src.skip_trails \ + ) + /obj/item/tank/jetpack/populate_gas() - if(gas_type) - switch(gas_type) - if("oxygen") - air_contents.oxygen = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) - if("carbon dioxide") - air_contents.carbon_dioxide = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) - -/obj/item/tank/jetpack/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/toggle_jetpack || actiontype == /datum/action/item_action/toggle_jetpack/ninja) + if(!gas_type) + return + switch(gas_type) + if("oxygen") + air_contents.oxygen = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) + if("carbon dioxide") + air_contents.carbon_dioxide = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)) + + +/obj/item/tank/jetpack/item_action_slot_check(slot, mob/user) + if(slot & ITEM_SLOT_BACK) + return TRUE + + +/obj/item/tank/jetpack/equipped(mob/user, slot, initial = FALSE) + . = ..() + if(on && !(slot & ITEM_SLOT_BACK)) + turn_off(user) + + +/obj/item/tank/jetpack/dropped(mob/user, slot, silent = FALSE) + . = ..() + if(on) + turn_off(user) + + +/obj/item/tank/jetpack/ui_action_click(mob/user, action, leftclick) + if(istype(action, /datum/action/item_action/toggle_jetpack)) cycle(user) - else if(actiontype == /datum/action/item_action/jetpack_stabilization || actiontype == /datum/action/item_action/jetpack_stabilization/ninja) - toggle_stabilization(user) + else if(istype(action, /datum/action/item_action/jetpack_stabilization)) + if(on) + configure_jetpack(!stabilize) + to_chat(user, span_notice("You turn the jetpack stabilization [stabilize ? "on" : "off"].")) + for(var/datum/action/existing as anything in actions) + existing.UpdateButtonIcon() else toggle_internals(user) -/obj/item/tank/jetpack/proc/toggle_stabilization(mob/user) - if(on) - stabilizers = !stabilizers - to_chat(user, "You turn [src]'s stabilization [stabilizers ? "on" : "off"].") - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() -/obj/item/tank/jetpack/proc/cycle(mob/user, must_be_on_back = TRUE) +/obj/item/tank/jetpack/proc/cycle(mob/user) if(user.incapacitated()) return - if(must_be_on_back && src != user.back) - to_chat(user, "You need [src] to be on your back!") - return - if(!on) - turn_on(user) - to_chat(user, "You turn the jetpack on.") + if(turn_on(user)) + to_chat(user, span_notice("You turn the jetpack on.")) + else + to_chat(user, span_notice("You fail to turn the jetpack on.")) + return else turn_off(user) - to_chat(user, "You turn the jetpack off.") - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() + to_chat(user, span_notice("You turn the jetpack off.")) + + for(var/datum/action/action as anything in actions) + action.UpdateButtonIcon() /obj/item/tank/jetpack/update_icon_state() @@ -59,39 +111,48 @@ /obj/item/tank/jetpack/proc/turn_on(mob/user) + if(SEND_SIGNAL(src, COMSIG_JETPACK_ACTIVATED, user) & JETPACK_ACTIVATION_FAILED) + return FALSE on = TRUE update_icon(UPDATE_ICON_STATE) + return TRUE + /obj/item/tank/jetpack/proc/turn_off(mob/user) + SEND_SIGNAL(src, COMSIG_JETPACK_DEACTIVATED, user) on = FALSE - stabilizers = FALSE update_icon(UPDATE_ICON_STATE) -/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user, should_leave_trail) - if(!on) - return 0 - if((num < 0.005 || air_contents.total_moles() < num)) + +/// num argument is set on jetpack init, in a CALLBACK +/// use_fuel argument comes from an attached component (used to check if we can start and skips fuel usage) +/obj/item/tank/jetpack/proc/allow_thrust(num, use_fuel = TRUE) + var/mob/user = get_owner() + if(!user) + return FALSE + + if(num < 0.005 || air_contents.total_moles() < num) turn_off(user) - return 0 + return FALSE - var/datum/gas_mixture/removed = air_contents.remove(num) + // We've got the gas, it's chill + if(!use_fuel) + return TRUE + + var/datum/gas_mixture/removed = remove_air(num) if(removed.total_moles() < 0.005) turn_off(user) - return 0 + return FALSE - var/turf/T = get_turf(user) + var/turf/T = get_turf(src) T.assume_air(removed) + return TRUE - if(!user.has_gravity(T) && should_leave_trail) - new /obj/effect/particle_effect/ion_trails(T) - return 1 +/obj/item/tank/jetpack/proc/get_owner() + if(ishuman(loc)) + return loc -/obj/item/tank/jetpack/Moved(OldLoc, Dir, Forced) - var/mob/living/carbon/human/holder = loc - if(on && !(istype(holder) && holder.back == src)) - turn_off() - ..() /obj/item/tank/jetpack/improvised name = "improvised jetpack" @@ -101,13 +162,20 @@ volume = 20 //normal jetpacks have 70 volume gas_type = null //it starts empty -/obj/item/tank/jetpack/improvised/allow_thrust(num, mob/living/user, should_leave_trail) + +/obj/item/tank/jetpack/improvised/allow_thrust(num, use_fuel = TRUE) + var/mob/user = get_owner() + if(!user) + return FALSE + if(rand(0, 250) == 0) - to_chat(user, "You feel your jetpack's engines cut out.") + to_chat(user, span_notice("You feel your jetpack's engines cut out.")) turn_off(user) - return + return FALSE + return ..() + /obj/item/tank/jetpack/void name = "Void Jetpack (Oxygen)" desc = "It works well in a void." @@ -169,60 +237,73 @@ w_class = WEIGHT_CLASS_NORMAL actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) volume = 1 - slot_flags = null + slot_flags = NONE gas_type = null fillable = FALSE var/datum/gas_mixture/temp_air_contents - var/obj/item/tank/internals/tank = null - var/mob/living/carbon/human/cur_user + var/obj/item/tank/internals/tank + var/obj/item/clothing/suit/space/our_suit -/obj/item/tank/jetpack/suit/New() - ..() + +/obj/item/tank/jetpack/suit/Initialize(mapload) + . = ..() STOP_PROCESSING(SSobj, src) temp_air_contents = air_contents + +/obj/item/tank/jetpack/suit/Destroy() + our_suit = null + tank = null + temp_air_contents = null + return ..() + + +/obj/item/tank/jetpack/suit/item_action_slot_check(slot, mob/user) + return TRUE + + +/obj/item/tank/jetpack/suit/get_owner() + if(our_suit && ishuman(our_suit.loc)) + return our_suit.loc + + /obj/item/tank/jetpack/suit/attack_self() return + /obj/item/tank/jetpack/suit/examine(mob/user) . = ..(user, show_contents_info = FALSE) -/obj/item/tank/jetpack/suit/cycle(mob/user) - if(!istype(loc, /obj/item/clothing/suit/space)) - to_chat(user, "[src] must be connected to your suit!") - return - var/mob/living/carbon/human/H = user - if(!istype(H.s_store, /obj/item/tank)) - to_chat(user, "You need a tank in your suit storage!") - return - ..(user, must_be_on_back = FALSE) -/obj/item/tank/jetpack/suit/turn_on(mob/user) - if(!ishuman(loc.loc) || loc.loc != user) - return - var/mob/living/carbon/human/H = user - tank = H.s_store +/obj/item/tank/jetpack/suit/allow_thrust(num, use_fuel = TRUE) + if(!our_suit) + return FALSE + if(!istype(tank, /obj/item/tank)) + return FALSE + return ..() + + +/obj/item/tank/jetpack/suit/turn_on(mob/living/carbon/human/user) + if(!ishuman(user)) + return FALSE + if(!our_suit) + to_chat(user, span_warning("[src] must be connected to your suit!")) + return FALSE + if(!istype(user.s_store, /obj/item/tank)) + to_chat(user, span_warning("You need a tank in your suit storage!")) + return FALSE + tank = user.s_store air_contents = tank.air_contents START_PROCESSING(SSobj, src) - cur_user = user - ..() + return ..() + -/obj/item/tank/jetpack/suit/turn_off(mob/user) +/obj/item/tank/jetpack/suit/turn_off(mob/living/carbon/human/user) tank = null air_contents = temp_air_contents STOP_PROCESSING(SSobj, src) - cur_user = null - ..() + return ..() -/obj/item/tank/jetpack/suit/process() - if(!ishuman(loc.loc)) - turn_off(cur_user) - return - var/mob/living/carbon/human/H = loc.loc - if(!tank || tank != H.s_store) - turn_off(cur_user) - return - ..() /obj/item/tank/jetpack/suit/ninja name = "ninja jetpack upgrade" @@ -231,12 +312,23 @@ icon_state = "ninja_jetpack" actions_types = list(/datum/action/item_action/toggle_jetpack/ninja, /datum/action/item_action/jetpack_stabilization/ninja) -/obj/item/tank/jetpack/suit/ninja/New() + +/obj/item/tank/jetpack/suit/ninja/Initialize(mapload) . = ..() var/datum/action/item_action/jetpack_action for(jetpack_action in actions) jetpack_action.button_icon = 'icons/mob/actions/actions_ninja.dmi' jetpack_action.background_icon_state = "background_green" -/obj/item/tank/jetpack/suit/ninja/allow_thrust(num, mob/living/user, should_leave_trail) - . = ..(num, user, cur_user?.alpha != NINJA_ALPHA_INVISIBILITY && should_leave_trail) + +/obj/item/tank/jetpack/suit/ninja/allow_thrust(num, use_fuel = TRUE) + var/mob/user = get_owner() + if(!user) + return FALSE + if(!skip_trails && user.alpha == NINJA_ALPHA_INVISIBILITY) + configure_jetpack(skip_trails = TRUE) + else if(skip_trails && user.alpha != NINJA_ALPHA_INVISIBILITY) + configure_jetpack(skip_trails = FALSE) + return ..() + + diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 6595d0b319f..ab723214de6 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -18,8 +18,9 @@ var/volume = 70 var/fillable = TRUE -/obj/item/tank/New() - ..() + +/obj/item/tank/Initialize(mapload) + . = ..() air_contents = new /datum/gas_mixture() air_contents.volume = volume //liters @@ -28,7 +29,7 @@ populate_gas() START_PROCESSING(SSobj, src) - return + /obj/item/tank/Destroy() QDEL_NULL(air_contents) diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm index 0c9c8b8c0fb..db4bc1310bd 100644 --- a/code/game/objects/items/weapons/tanks/watertank.dm +++ b/code/game/objects/items/weapons/tanks/watertank.dm @@ -135,7 +135,7 @@ else return 1 -/obj/item/reagent_containers/spray/mister/Move() +/obj/item/reagent_containers/spray/mister/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(loc != tank.loc) loc = tank.loc diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 4fcaa1ef907..be459a21272 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -45,7 +45,7 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/structure/Move(atom/newloc, direct = NONE, movetime) +/obj/structure/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/atom/old = loc if(!..()) return FALSE diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 6ae928ac7c5..5c5afceb0ed 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -181,10 +181,10 @@ /obj/structure/alien/resin/door/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(operating) - return + return . if(isliving(moving_atom)) var/mob/living/living = moving_atom diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index b14aa28d2e4..0b5bc9218f6 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -508,15 +508,15 @@ GLOBAL_LIST_EMPTY(closets) UpdateTransparency(mover, loc) -/obj/structure/closet/bluespace/Move(NewLoc, direct) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls. - var/turf/T = get_turf(NewLoc) +/obj/structure/closet/bluespace/Move(atom/newloc, direct = NONE, glide_size_override = 0) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls. + var/turf/T = get_turf(newloc) if(T.density) return for(var/atom/A in T.contents) if(A.density && istype(A, /obj/machinery/door)) return - UpdateTransparency(src, NewLoc) - forceMove(NewLoc) + UpdateTransparency(src, newloc) + forceMove(newloc) /obj/structure/closet/bluespace/close() . = ..() diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index 6971fbbe60e..17a4c871e19 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -41,7 +41,7 @@ . = Move(next_step, direction, delay) if(!user.client) return - user.client.move_delay += ISDIAGONALDIR(direction) ? delay * SQRT_2 : delay + user.client.move_delay += ISDIAGONALDIR(direction) ? delay * sqrt(2) : delay /obj/structure/closet/cardboard/open() diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 473df42debe..0215d755f1a 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -24,7 +24,7 @@ var/cuttable = TRUE var/hole_size = NO_HOLE var/invulnerable = FALSE - var/shock_cooldown = FALSE + COOLDOWN_DECLARE(shock_cooldown) /obj/structure/fence/Initialize() . = ..() @@ -142,19 +142,14 @@ return . = ..() -/obj/structure/fence/Bumped(atom/movable/moving_atom) - ..() - if(!ismob(moving_atom)) - return - if(shock_cooldown) - return +/obj/structure/fence/Bumped(atom/movable/moving_atom) + . = ..() + if(!COOLDOWN_FINISHED(src, shock_cooldown) || !ismob(moving_atom)) + return . shock(moving_atom, 70) - shock_cooldown = TRUE // We do not want bump shock spam! - addtimer(CALLBACK(src, PROC_REF(shock_cooldown)), 1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + COOLDOWN_START(src, shock_cooldown, 1 SECONDS) // We do not want bump shock spam! -/obj/structure/fence/proc/shock_cooldown() - shock_cooldown = FALSE /obj/structure/fence/attack_animal(mob/user) . = ..() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 58b04b0c954..83a7fefcc5d 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -18,8 +18,7 @@ var/rods_broken = 1 var/grille_type var/broken_type = /obj/structure/grille/broken - var/shockcooldown = 0 - var/my_shockcooldown = 1 SECONDS + COOLDOWN_DECLARE(shock_cooldown) /obj/structure/grille/fence var/width = 2 @@ -101,14 +100,14 @@ take_damage(25) //second time turn into broken . &= ~(FALL_INTERCEPTED | FALL_NO_MESSAGE | FALL_RETAIN_PULL) + /obj/structure/grille/Bumped(atom/movable/moving_atom) - ..() + . = ..() + if(!COOLDOWN_FINISHED(src, shock_cooldown) || !ismob(moving_atom)) + return . + shock(moving_atom, 70) + COOLDOWN_START(src, shock_cooldown, 1 SECONDS) - if(ismob(moving_atom)) - if(!(shockcooldown <= world.time)) - return - shock(moving_atom, 70) - shockcooldown = world.time + my_shockcooldown /obj/structure/grille/attack_animal(mob/user) . = ..() diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm index b1ec7e2f618..2763e9a6f41 100644 --- a/code/game/objects/structures/holosign.dm +++ b/code/game/objects/structures/holosign.dm @@ -143,35 +143,25 @@ name = "Charged Energy Field" desc = "A powerful energy field that blocks movement. Energy arcs off it." max_integrity = 20 - var/shockcd = 0 + COOLDOWN_DECLARE(shock_cooldown) + /obj/structure/holosign/barrier/cyborg/hacked/bullet_act(obj/item/projectile/P) take_damage(P.damage, BRUTE, "melee", 1) //Yeah no this doesn't get projectile resistance. -/obj/structure/holosign/barrier/cyborg/hacked/proc/cooldown() - shockcd = FALSE /obj/structure/holosign/barrier/cyborg/hacked/attack_hand(mob/living/user) . = ..() - if(.) + if(. || !COOLDOWN_FINISHED(src, shock_cooldown) || !isliving(user)) return - if(!shockcd) - if(isliving(user)) - var/mob/living/M = user - M.electrocute_act(15, "Energy Barrier", safety = TRUE) - shockcd = TRUE - addtimer(CALLBACK(src, PROC_REF(cooldown)), 5) - -/obj/structure/holosign/barrier/cyborg/hacked/Bumped(atom/movable/moving_atom) - ..() + user.electrocute_act(15, "Energy Barrier", safety = TRUE) + COOLDOWN_START(src, shock_cooldown, 0.5 SECONDS) - if(shockcd) - return - if(!isliving(moving_atom)) - return +/obj/structure/holosign/barrier/cyborg/hacked/Bumped(mob/living/moving_living) + . = ..() + if(!COOLDOWN_FINISHED(src, shock_cooldown) || !isliving(moving_living)) + return . + moving_living.electrocute_act(15, "Energy Barrier", safety = TRUE) + COOLDOWN_START(src, shock_cooldown, 0.5 SECONDS) - var/mob/living/M = moving_atom - M.electrocute_act(15, "Energy Barrier", safety = TRUE) - shockcd = TRUE - addtimer(CALLBACK(src, PROC_REF(cooldown)), 5) diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index d190746e663..db1767aafed 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -37,7 +37,7 @@ move_update_air(T) /obj/structure/mineral_door/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(!state) return TryToSwitchState(moving_atom) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 04c8dcf9bc4..1d59aecdd8a 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -727,7 +727,7 @@ GLOBAL_LIST_EMPTY(crematoriums) return TRUE -/obj/structure/c_tray/Process_Spacemove(movement_dir = NONE) +/obj/structure/c_tray/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm index b2d91eafcea..04f0fd0d22c 100644 --- a/code/game/objects/structures/railings.dm +++ b/code/game/objects/structures/railings.dm @@ -143,7 +143,7 @@ ..() handle_layer() -/obj/structure/railing/Move(newloc, direct, movetime) +/obj/structure/railing/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() handle_layer() diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm index 6a32ed542ec..1a0b43ff18f 100644 --- a/code/game/objects/structures/stairs.dm +++ b/code/game/objects/structures/stairs.dm @@ -52,7 +52,7 @@ if(!isobserver(leaving) && isTerminator() && direction == dir) leaving.set_currently_z_moving(CURRENTLY_Z_ASCENDING) INVOKE_ASYNC(src, PROC_REF(stair_ascend), leaving) - leaving.Bump(src) + leaving.Bump(src, custom_bump = TRUE) return COMPONENT_ATOM_BLOCK_EXIT /obj/structure/stairs/Cross(atom/movable/AM) diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 7b047fc4128..0ed1d9d22d4 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -222,7 +222,7 @@ /obj/structure/statue/bananium/Bumped(atom/movable/moving_atom) honk() - ..() + . = ..() /obj/structure/statue/bananium/attackby(obj/item/W, mob/user, params) honk() diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 511cef1f25a..9bd0e6583fc 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -28,7 +28,7 @@ B.setDir(dir) qdel(src) -/obj/structure/chair/Move(atom/newloc, direct) +/obj/structure/chair/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() handle_rotation() @@ -261,21 +261,20 @@ buildstackamount = 5 pull_push_speed_modifier = 1 -/obj/structure/chair/office/Bump(atom/A) - ..() - if(!has_buckled_mobs()) - return - if(propelled) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - unbuckle_mob(buckled_mob) - buckled_mob.throw_at(A, 3, propelled) - buckled_mob.Weaken(12 SECONDS) - buckled_mob.Stuttering(12 SECONDS) - buckled_mob.take_organ_damage(10) - playsound(loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - buckled_mob.visible_message(span_danger("[buckled_mob] crashed into [A]!")) +/obj/structure/chair/office/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.) || !has_buckled_mobs() || !propelled) + return . + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + unbuckle_mob(buckled_mob) + buckled_mob.throw_at(bumped_atom, 3, propelled) + buckled_mob.Weaken(12 SECONDS) + buckled_mob.Stuttering(12 SECONDS) + buckled_mob.take_organ_damage(10) + playsound(loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1) + buckled_mob.visible_message(span_danger("[buckled_mob] crashed into [bumped_atom]!")) /obj/structure/chair/office/light icon_state = "officechair_white" diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 226dfcb0c7a..3c558d1661e 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -1,162 +1,128 @@ /obj/structure/chair/wheelchair name = "wheelchair" + desc = "You sit in this. Helps with traumas." + base_icon_state = "wheelchair" icon_state = "wheelchair" item_chair = null movable = TRUE pull_push_speed_modifier = 1 + /// Overlay used to overlap buckled mob. var/mutable_appearance/chair_overlay - var/move_delay = null - -/obj/structure/chair/wheelchair/handle_rotation() - if(chair_overlay) - cut_overlay(chair_overlay) - else - chair_overlay = mutable_appearance(icon, "[icon_state]_overlay", FLY_LAYER) - chair_overlay.dir = src.dir - add_overlay(chair_overlay) - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(dir) + /// If set we cannot go lower than this delay. + var/lowest_move_delay = 0.4 SECONDS + /// Currently applied skin, it contains path, not an instance. + var/obj/item/fluff/rapid_wheelchair_kit/applied_skin + COOLDOWN_DECLARE(wheelchair_move_delay) -/obj/structure/chair/wheelchair/relaymove(mob/user, direction) - if(propelled) - return 0 - if(!Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc)) - return 0 +/obj/structure/chair/wheelchair/Initialize(mapload) + . = ..() + chair_overlay = mutable_appearance(icon, "wheelchair_overlay", ABOVE_MOB_LAYER) + update_icon(UPDATE_OVERLAYS) - if(world.time < move_delay) - return - var/calculated_move_delay - calculated_move_delay += 2 //wheelchairs are not infact sport bikes - - if(has_buckled_mobs()) - var/mob/living/buckled_mob = buckled_mobs[1] - if(buckled_mob.incapacitated()) - return 0 - - var/mob/living/thedriver = user - var/mob_delay = thedriver.cached_multiplicative_slowdown - if(mob_delay > 0) - calculated_move_delay += mob_delay - - if(ishuman(buckled_mob)) - var/mob/living/carbon/human/driver = user - if(!driver.has_left_hand() && !driver.has_right_hand()) - return 0 // No hands to drive your chair? Tough luck! - - for(var/organ_name in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)) - var/obj/item/organ/external/E = driver.get_organ(organ_name) - if(!E) - calculated_move_delay += 4 - else if(E.is_splinted()) - calculated_move_delay += 0.5 - else if(E.has_fracture()) - calculated_move_delay += 1.5 - - if(calculated_move_delay < 4) - calculated_move_delay = 4 //no racecarts - glide_for(calculated_move_delay) - if(direction & (direction - 1)) //moved diagonally - calculated_move_delay *= 1.41 - - move_delay = world.time - move_delay += calculated_move_delay - - if(!buckled_mob.Move(get_step(buckled_mob, direction), direction)) - loc = buckled_mob.loc //we gotta go back - last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move - . = 0 - - else - . = 1 - -/obj/structure/chair/wheelchair/Bump(atom/A) - ..() - - if(!has_buckled_mobs()) +/obj/structure/chair/wheelchair/Destroy() + chair_overlay = null + applied_skin = null + return ..() + + +/obj/structure/chair/wheelchair/proc/on_skin_apply(obj/item/fluff/rapid_wheelchair_kit/kit, mob/user) + if(applied_skin && applied_skin == kit.type) + to_chat(user, span_warning("This [name] is already modified by [kit]!.")) return - var/mob/living/buckled_mob = buckled_mobs[1] - if(istype(A, /obj/machinery/door)) - A.Bumped(buckled_mob) - if(propelled) - var/mob/living/occupant = buckled_mob - unbuckle_mob(occupant) + to_chat(user, span_notice("You modify the appearance of [src].")) + applied_skin = kit.type + chair_overlay = mutable_appearance(icon, kit.new_overlay, ABOVE_MOB_LAYER) + update_appearance() + qdel(kit) - occupant.throw_at(A, 3, propelled) - occupant.Weaken(12 SECONDS) - occupant.Stuttering(12 SECONDS) - playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - if(isliving(A)) - var/mob/living/victim = A - victim.Weaken(12 SECONDS) - victim.Stuttering(12 SECONDS) - victim.take_organ_damage(10) +/obj/structure/chair/wheelchair/update_icon_state() + icon_state = applied_skin ? initial(applied_skin.new_icon_state) : base_icon_state - occupant.visible_message("[occupant] crashed into \the [A]!") -/obj/structure/chair/wheelchair/bike - name = "bicycle" - desc = "Two wheels of FURY!" - //placeholder until i get a bike sprite - icon = 'icons/obj/vehicles/motorcycle.dmi' - icon_state = "motorcycle_4dir" +/obj/structure/chair/wheelchair/update_overlays() + . = ..() + . += chair_overlay -/obj/structure/chair/wheelchair/bike/relaymove(mob/user, direction) - if(propelled) - return 0 - if(!Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc)) //bikes in space. - return 0 +/obj/structure/chair/wheelchair/update_name(updates = ALL) + . = ..() + name = applied_skin ? initial(applied_skin.new_name) : initial(name) - if(world.time < move_delay) - return - var/calculated_move_delay - calculated_move_delay = 0 //bikes are infact sport bikes - - if(has_buckled_mobs()) - var/mob/living/buckled_mob = buckled_mobs[1] - if(buckled_mob.incapacitated()) - unbuckle_mob(buckled_mob) //if the rider is incapacitated, unbuckle them (they can't balance so they fall off) - return 0 - - var/mob/living/thedriver = user - var/mob_delay = thedriver.cached_multiplicative_slowdown - if(mob_delay > 0) - calculated_move_delay += mob_delay - - if(ishuman(buckled_mob)) - var/mob/living/carbon/human/driver = user - var/obj/item/organ/external/l_hand = driver.get_organ(BODY_ZONE_PRECISE_L_HAND) - var/obj/item/organ/external/r_hand = driver.get_organ(BODY_ZONE_PRECISE_R_HAND) - if(!l_hand && !r_hand) - calculated_move_delay += 0.5 //I can ride my bike with no handlebars... (but it's slower) - - for(var/organ_name in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT)) - var/obj/item/organ/external/E = driver.get_organ(organ_name) - if(!E) - return 0 //Bikes need both feet/legs to work. missing even one makes it so you can't ride the bike - else if(E.is_splinted()) - calculated_move_delay += 0.5 - else if(E.has_fracture()) - calculated_move_delay += 1.5 - - move_delay = world.time - move_delay += calculated_move_delay - - if(!buckled_mob.Move(get_step(buckled_mob, direction), direction)) - loc = buckled_mob.loc //we gotta go back - last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move - . = 0 - - else - . = 1 +/obj/structure/chair/wheelchair/update_desc(updates = ALL) + . = ..() + desc = applied_skin ? initial(applied_skin.new_desc) : initial(desc) + + +/obj/structure/chair/wheelchair/handle_layer() + return + + +/obj/structure/chair/wheelchair/relaymove(mob/user, direction) + if(!COOLDOWN_FINISHED(src, wheelchair_move_delay)) + return FALSE + var/turf/next_step = get_step(src, direction) + if(!next_step || propelled || !Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc) || !has_buckled_mobs() || user != buckled_mobs[1]) + COOLDOWN_START(src, wheelchair_move_delay, 0.5 SECONDS) + return FALSE + + var/calculated_move_delay = user.cached_multiplicative_slowdown + + if(ishuman(user)) + var/mob/living/carbon/human/driver = user + if(!driver.num_hands) + COOLDOWN_START(src, wheelchair_move_delay, 0.5 SECONDS) + return FALSE // No hands to drive your chair? Tough luck! + + for(var/organ_name in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)) + var/obj/item/organ/external/bodypart = driver.get_organ(organ_name) + if(!bodypart) + calculated_move_delay += 4 + else if(bodypart.is_splinted()) + calculated_move_delay += 0.5 + else if(bodypart.has_fracture()) + calculated_move_delay += 1.5 + + if(lowest_move_delay && calculated_move_delay < lowest_move_delay) + calculated_move_delay = lowest_move_delay //no racecarts + + . = Move(next_step, direction) + if(ISDIAGONALDIR(direction) && loc == next_step) + calculated_move_delay *= sqrt(2) + + set_glide_size(DELAY_TO_GLIDE_SIZE(calculated_move_delay)) + COOLDOWN_START(src, wheelchair_move_delay, calculated_move_delay) + + +/obj/structure/chair/wheelchair/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.) || !has_buckled_mobs()) + return . + + var/mob/living/buckled_mob = buckled_mobs[1] + if(istype(bumped_atom, /obj/machinery/door)) + bumped_atom.Bumped(buckled_mob) + + if(!propelled) + return . + + var/mob/living/occupant = buckled_mob + unbuckle_mob(occupant) + + occupant.throw_at(bumped_atom, 3, propelled) + + occupant.Weaken(12 SECONDS) + occupant.Stuttering(12 SECONDS) + playsound(src.loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1) + if(isliving(bumped_atom)) + var/mob/living/victim = bumped_atom + victim.Weaken(12 SECONDS) + victim.Stuttering(12 SECONDS) + victim.take_organ_damage(10) + + occupant.visible_message(span_danger("[occupant] crashed into [bumped_atom]!")) + diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 4cb662841a8..a59f2ff9306 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -700,7 +700,7 @@ if(!held.anchored && held.move_resist != INFINITY && is_type_in_typecache(held, typecache_can_hold)) held_items += held.UID() -/obj/structure/table/tray/Move(NewLoc, direct) +/obj/structure/table/tray/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/atom/OldLoc = loc . = ..() @@ -721,7 +721,7 @@ if(OldLoc != held.loc) held_items -= held_uid continue - held.forceMove(NewLoc) + held.forceMove(newloc) /obj/structure/table/tray/can_be_pulled(user, force, show_message) diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm index dd01702738b..b032b376e84 100644 --- a/code/game/objects/structures/transit_tubes/station.dm +++ b/code/game/objects/structures/transit_tubes/station.dm @@ -45,15 +45,19 @@ /obj/structure/transit_tube/station/should_stop_pod(pod, from_dir) return TRUE -/obj/structure/transit_tube/station/Bumped(mob/living/L) - if(!pod_moving && L.dir == boarding_dir && hatch_state == TRANSIT_TUBE_OPEN && isliving(L) && !is_type_in_list(L, disallowed_mobs)) - for(var/obj/structure/transit_tube_pod/pod in loc) - if(length(pod.contents)) - to_chat(L, "The pod is already occupied.") - return - if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions())))) - pod.move_into(L) - return + +/obj/structure/transit_tube/station/Bumped(mob/living/moving_living, skip_effect = FALSE) + . = ..() + if(skip_effect || pod_moving || moving_living.dir != boarding_dir || hatch_state == TRANSIT_TUBE_OPEN || !isliving(moving_living) || is_type_in_list(moving_living, disallowed_mobs)) + return . + for(var/obj/structure/transit_tube_pod/pod in loc) + if(length(pod.contents)) + to_chat(moving_living, "The pod is already occupied.") + break + if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions())))) + pod.move_into(moving_living) + break + /obj/structure/transit_tube/station/attack_hand(mob/user) if(pod_moving) @@ -221,16 +225,18 @@ return TRUE return FALSE -/obj/structure/transit_tube/station/dispenser/Bumped(mob/living/L) - if(!(istype(L) && L.dir == boarding_dir) || L.anchored) - return - if(isliving(L) && !is_type_in_list(L, disallowed_mobs)) - var/obj/structure/transit_tube_pod/dispensed/pod = new(loc) - L.visible_message("[pod] forms around [L].", "[pod] materializes around you.") - playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE) - pod.dir = turn(dir, -90) - pod.move_into(L) - launch_pod() + +/obj/structure/transit_tube/station/dispenser/Bumped(mob/living/moving_living, skip_effect = TRUE) + . = ..() + if(!isliving(moving_living) || moving_living.dir != boarding_dir || moving_living.anchored || is_type_in_list(moving_living, disallowed_mobs)) + return . + var/obj/structure/transit_tube_pod/dispensed/pod = new(loc) + moving_living.visible_message("[pod] forms around [moving_living].", "[pod] materializes around you.") + playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE) + pod.dir = turn(dir, -90) + pod.move_into(moving_living) + launch_pod() + /obj/structure/transit_tube/station/dispenser/pod_stopped(obj/structure/transit_tube_pod/pod) playsound(src, 'sound/machines/ding.ogg', 50, TRUE) diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index bf47793b41f..8060032869a 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -53,7 +53,7 @@ return ..() -/obj/structure/transit_tube_pod/Process_Spacemove(movement_dir = NONE) +/obj/structure/transit_tube_pod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) if(moving) //No drifting while moving in the tubes return TRUE return ..() diff --git a/code/game/objects/structures/tribune.dm b/code/game/objects/structures/tribune.dm index 3e56b27c3b2..a6061b85b57 100644 --- a/code/game/objects/structures/tribune.dm +++ b/code/game/objects/structures/tribune.dm @@ -43,7 +43,7 @@ ..() handle_layer() -/obj/structure/tribune/Move(newloc, direct, movetime) +/obj/structure/tribune/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() handle_layer() diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index 1a07ffbd300..c6be6bc3833 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -54,7 +54,7 @@ desc = "A very large bluespace engine used to propel very large ships." // bound_width = 64 // bound_height = 64 - appearance_flags = 0 + appearance_flags = LONG_GLIDE /obj/structure/shuttle/engine/large/Initialize() ..() @@ -77,7 +77,7 @@ pixel_y = -32 // bound_width = 96 // bound_height = 96 - appearance_flags = 0 + appearance_flags = LONG_GLIDE /obj/structure/shuttle/engine/huge/Initialize() ..() diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 976d5693bcf..2da5980da68 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -145,7 +145,7 @@ /turf/simulated/mineral/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(ishuman(moving_atom)) var/mob/living/carbon/human/H = moving_atom if((istype(H.l_hand,/obj/item/pickaxe)) && (!H.hand)) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index cfa31ca4704..37c95debf00 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -159,9 +159,6 @@ var/turf/T = get_step(L.loc,turn(A.dir, 180)) L.pulling.zMove(null, T, ZMOVE_ALLOW_BUCKLED) - //now we're on the new z_level, proceed the space drifting - spawn(0)//Let a diagonal move finish, if necessary - A.newtonian_move(A.inertia_dir) /turf/space/proc/check_taipan_availability(atom/movable/A as mob|obj, destination_z) var/mob/living/check_mob = A @@ -205,7 +202,7 @@ var/list/y_arr if(src.x <= 1) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + if(istype(A, /obj/effect/meteor)) qdel(A) return @@ -280,7 +277,7 @@ A.loc.Entered(A) else if(src.y >= world.maxy) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + if(istype(A, /obj/effect/meteor)) qdel(A) return var/list/cur_pos = src.get_global_map_pos() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index f03c7009f71..4ebe49fee21 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -191,7 +191,7 @@ if(obstacle == mover || obstacle == oldloc) continue if(!obstacle.CanExit(mover, movement_dir)) - mover.Bump(obstacle, TRUE) + mover.Bump(obstacle, custom_bump = TRUE) return FALSE var/border_dir = get_dir(src, mover) @@ -203,14 +203,14 @@ continue if(border_obstacle.flags & ON_BORDER) if(!border_obstacle.CanPass(mover, border_dir)) - mover.Bump(border_obstacle, TRUE) + mover.Bump(border_obstacle, custom_bump = TRUE) return FALSE else large_dense += border_obstacle //Then, check the turf itself if(!CanPass(mover, border_dir)) - mover.Bump(src, TRUE) + mover.Bump(src, custom_bump = TRUE) return FALSE //Finally, check objects/mobs to block entry that are not on the border @@ -228,7 +228,7 @@ tompost_bump = obstacle top_layer = current_layer if(tompost_bump) - mover.Bump(tompost_bump, TRUE) + mover.Bump(tompost_bump, custom_bump = TRUE) return FALSE return TRUE //Nothing found to block so return success! diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm index a833304faa0..cd7f60a5cc1 100644 --- a/code/modules/antagonists/space_dragon/carp_rift.dm +++ b/code/modules/antagonists/space_dragon/carp_rift.dm @@ -107,7 +107,7 @@ newcarp.faction = dragon.owner.current.faction.Copy() if(SPT_PROB(1.5, seconds_per_tick)) var/rand_dir = pick(GLOB.cardinal) - Move(get_step(src, rand_dir), rand_dir) + SSmove_manager.move_to(src, get_step(src, rand_dir), 1) return // Increase time trackers and check for any updated states. diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index a9a137c8c92..c322de2607f 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -95,7 +95,7 @@ RegisterSignal(small_sprite, COMSIG_ACTION_TRIGGER, PROC_REF(add_dragon_overlay)) -/mob/living/simple_animal/hostile/space_dragon/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/space_dragon/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/modules/antagonists/space_ninja/suit/suit.dm b/code/modules/antagonists/space_ninja/suit/suit.dm index 4e26cec2ce7..ee998057124 100644 --- a/code/modules/antagonists/space_ninja/suit/suit.dm +++ b/code/modules/antagonists/space_ninja/suit/suit.dm @@ -403,18 +403,18 @@ ninja.adjust_bodytemperature(BODYTEMP_NORMAL - ninja.bodytemperature) -/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/ninja, action) +/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/ninja, datum/action/action) if(!isninja(ninja) && !anyone) to_chat(ninja, span_danger("fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAU†HORIZED USÈ DETÈC†††eD\nCoMMÈNCING SUB-R0U†IN3 13...\nTÈRMInATING U-U-USÈR...")) ninja.dust() return FALSE - if(action == /datum/action/item_action/advanced/ninja/SpiderOS) + if(istype(action, /datum/action/item_action/advanced/ninja/SpiderOS)) ui_interact(ninja) return TRUE if(!s_initialized) to_chat(ninja, span_warning("ERROR: suit offline. Please activate suit.")) return FALSE - switch(action) + switch(action.type) if(/datum/action/item_action/advanced/ninja/ninja_autodust) ninja_toggle_autodust() return TRUE diff --git a/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm b/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm index 1bfaba66272..035f05bbd17 100644 --- a/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm +++ b/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm @@ -34,8 +34,8 @@ . = ..() QDEL_NULL(scorpion) -/obj/item/clothing/suit/space/hardsuit/contractor/ui_action_click(user, action) - switch(action) +/obj/item/clothing/suit/space/hardsuit/contractor/ui_action_click(user, datum/action/action) + switch(action.type) if(/datum/action/item_action/toggle_helmet) ToggleHelmet(user) return TRUE diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index 33dbc0517f9..8341808652d 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -344,5 +344,5 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new()) /datum/prize_item/bike name = "Awesome Bike!" desc = "WOAH." - typepath = /obj/structure/chair/wheelchair/bike + typepath = /obj/vehicle/motorcycle cost = 10000 //max stack + 1 tickets. diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index c10d5e3d4eb..595037dd328 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -146,16 +146,11 @@ process_movement(user) -/obj/item/assembly_holder/Bump(atom/A) - ..() - var/triggered - if(ismob(A) || isobj(A)) - var/atom/movable/AM = A - if(AM.throwing?.thrower) - triggered = AM.throwing.thrower - else if(ismob(AM)) - triggered = AM - process_movement(triggered) +/obj/item/assembly_holder/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.) || !ismob(bumped_atom)) + return . + process_movement(bumped_atom) /obj/item/assembly_holder/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // called when a throw stops diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index aa14fb9171f..2c5c8b18d24 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -111,7 +111,7 @@ ..() -/obj/item/assembly/infra/Move(atom/newloc, direct = 0, movetime) +/obj/item/assembly/infra/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/prev_dir = dir . = ..() dir = prev_dir @@ -304,11 +304,14 @@ I.process() -/obj/effect/beam/i_beam/Bump() +/obj/effect/beam/i_beam/Bump(atom/bumped_atom, custom_bump) + if(!custom_bump) + return null qdel(src) /obj/effect/beam/i_beam/Bumped(atom/movable/moving_atom) + . = ..() hit(moving_atom) diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index 0aa93c57feb..da574e9a8f8 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -101,7 +101,7 @@ holder?.update_icon() -/obj/item/assembly/prox_sensor/Move(atom/newloc, direct = NONE, movetime) +/obj/item/assembly/prox_sensor/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() sense() diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index b0590114653..f258814d2e9 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -142,11 +142,8 @@ GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null) //okay, here's the good teleporting stuff /obj/machinery/gateway/centerstation/Bumped(atom/movable/moving_atom) - if(!ready) - return - if(!active) - return - if(!awaygate) + . = ..() + if(!ready || !active || !awaygate) return if(awaygate.calibrated) moving_atom.forceMove(get_step(awaygate.loc, SOUTH)) @@ -254,11 +251,8 @@ GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null) /obj/machinery/gateway/centeraway/Bumped(atom/movable/moving_atom) - if(!ready) - return - if(!active) - return - if(!stationgate || QDELETED(stationgate)) + . = ..() + if(!ready || !active || QDELETED(stationgate)) return if(isliving(moving_atom)) if(exilecheck(moving_atom)) diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm index d484054d701..aca62a5ff11 100644 --- a/code/modules/awaymissions/mission_code/wildwest.dm +++ b/code/modules/awaymissions/mission_code/wildwest.dm @@ -140,28 +140,23 @@ layer = 3 icon = 'icons/mob/blob.dmi' icon_state = "blobpod" - var/triggerproc = "triggerrad1" //name of the proc thats called when the mine is triggered - var/triggered = 0 + var/triggered = FALSE -/obj/effect/meatgrinder/Crossed(AM as mob|obj, oldloc) + +/obj/effect/meatgrinder/Crossed(atom/movable/AM, oldloc) + . = ..() Bumped(AM) -/obj/effect/meatgrinder/Bumped(atom/movable/moving_atom) - if(triggered) - return - - if(ishuman(moving_atom)) - for(var/mob/O in viewers(world.view, src.loc)) - to_chat(O, "[moving_atom] triggered the [bicon(src)] [src]") - triggered = 1 - call(src,triggerproc)(moving_atom) - -/obj/effect/meatgrinder/proc/triggerrad1(mob) - for(var/mob/O in viewers(world.view, src.loc)) - do_sparks(3, 1, src) - explosion(mob, 1, 0, 0, 0) - qdel(src) +/obj/effect/meatgrinder/Bumped(atom/movable/moving_atom) + . = ..() + if(triggered || !ishuman(moving_atom)) + return . + visible_message(span_warning("[moving_atom] triggered the [bicon(src)] [src]!")) + triggered = TRUE + do_sparks(3, 1, src) + explosion(src, 1, 0, 0, 0) + qdel(src) /////For the Wishgranter/////////// diff --git a/code/modules/awaymissions/zvis.dm b/code/modules/awaymissions/zvis.dm index dc88224a7c0..3cbaec07078 100644 --- a/code/modules/awaymissions/zvis.dm +++ b/code/modules/awaymissions/zvis.dm @@ -287,35 +287,38 @@ near_render_block -= T /obj/effect/view_portal/visual/Bumped(atom/movable/thing) - if((isobj(thing) || isliving(thing)) && other && teleport) - if(!near_render_block) - setup_near() - - var/mob/living/M = thing - // make the person glide onto the dest, giving a smooth transition - var/ox = thing.x - x - var/oy = thing.y - y + . = ..() + if(!ismovable(thing) || !other || !teleport) + return . + + if(!near_render_block) + setup_near() + + var/mob/living/M = thing + // make the person glide onto the dest, giving a smooth transition + var/ox = thing.x - x + var/oy = thing.y - y + if(istype(M) && M.client) + ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) + // cover up client-side map loading + M.screen_loc = "CENTER" + M.client.screen += M + for(var/T in tiles) + M.client.screen += tiles[T] + + // wait a tick for the screen to replicate across network + // or this whole exercise of covering the transition is pointless + spawn(1) + thing.forceMove(locate(other.x + ox, other.y + oy, other.z)) + sleep(1) if(istype(M) && M.client) - ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) - // cover up client-side map loading - M.screen_loc = "CENTER" - M.client.screen += M for(var/T in tiles) - M.client.screen += tiles[T] - - // wait a tick for the screen to replicate across network - // or this whole exercise of covering the transition is pointless - spawn(1) - thing.forceMove(locate(other.x + ox, other.y + oy, other.z)) - sleep(1) - if(istype(M) && M.client) - for(var/T in tiles) - M.client.screen -= tiles[T] - M.client.screen -= M - M.screen_loc = initial(M.screen_loc) - thing.forceMove(get_turf(other.loc)) - if(istype(M) && M.client) - REMOVE_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) + M.client.screen -= tiles[T] + M.client.screen -= M + M.screen_loc = initial(M.screen_loc) + thing.forceMove(get_turf(other.loc)) + if(istype(M) && M.client) + REMOVE_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) /obj/effect/view_portal/visual/attack_ghost(mob/user) diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index f7229041da0..e01f9ca14d2 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -17,9 +17,12 @@ //OTHER// ///////// var/datum/preferences/prefs = null + ///Move delay of controlled mob, any keypresses inside this period will persist until the next proper move var/move_delay = 0 - var/current_move_delay = 0 + ///The visual delay to use for the current client.Move(), mostly used for making a client based move look like it came from some other slower source + var/visual_delay = 0 + var/area = null var/time_joined_as_mouse = null //when the client last spawned as a mouse diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index f974659b7e6..7174a96958c 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -806,6 +806,7 @@ BLIND // can't see anything . = ..() if(jetpack && ispath(jetpack)) jetpack = new jetpack(src) + jetpack.our_suit = src /obj/item/clothing/suit/space/screwdriver_act(mob/user, obj/item/I) @@ -821,6 +822,7 @@ BLIND // can't see anything jetpack.turn_off(user) jetpack.forceMove(drop_location()) jetpack = null + jetpack.our_suit = null to_chat(user, span_notice("You successfully remove the jetpack from [src].")) @@ -836,6 +838,7 @@ BLIND // can't see anything if(jetpack) for(var/datum/action/action as anything in jetpack.actions) action.Remove(user) + jetpack.turn_off(user) /obj/item/clothing/suit/space/attackby(obj/item/I, mob/user, params) @@ -852,6 +855,7 @@ BLIND // can't see anything if(user.drop_transfer_item_to_loc(I, src)) jetpack = I + jetpack.our_suit = src to_chat(user, span_notice("You successfully install the jetpack into [src].")) return return ..() diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 822f41f86e6..00b700a1e0d 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -405,12 +405,12 @@ can_toggle = FALSE actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) -/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/halt) +/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/halt)) halt() - else if(actiontype == /datum/action/item_action/adjust) + else if(istype(action, /datum/action/item_action/adjust)) adjustmask(user) - else if(actiontype == /datum/action/item_action/selectphrase) + else if(istype(action, /datum/action/item_action/selectphrase)) var/key = phrase_list[phrase] var/message = phrase_list[key] diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 5c39b7d81b5..32b597cde6b 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -182,26 +182,27 @@ desc = "A weathered Vox thermonocle, doesn't seem to work anymore." icon_state = "thermoncle" + /obj/item/fluff/rapid_wheelchair_kit //Rapidvalj: Hakikarahiti name = "wheelchair conversion kit" desc = "An assorted set of exchangable parts for a wheelchair." icon_state = "modkit" + var/new_icon_state = "vox_wheelchair" + var/new_overlay = "vox_wheelchair_overlay" + var/new_name = "vox wheelchair" + var/new_desc = "A luxurious Vox Wheelchair, weathered from use." + -/obj/item/fluff/rapid_wheelchair_kit/afterattack(atom/target, mob/user, proximity) +/obj/item/fluff/rapid_wheelchair_kit/afterattack(obj/structure/chair/wheelchair/target, mob/user, proximity) if(!proximity || !ishuman(user) || user.incapacitated()) return - if(istype(target, /obj/structure/chair/wheelchair) && !istype(target, /obj/structure/chair/wheelchair/bike)) - to_chat(user, "You modify the appearance of [target].") - var/obj/structure/chair/wheelchair/chair = target - chair.icon_state = "vox_wheelchair" - chair.name = "vox wheelchair" - chair.desc = "A luxurious Vox Wheelchair, weathered from use." - chair.handle_rotation() - qdel(src) + if(istype(target)) + target.on_skin_apply(src, user) return - to_chat(user, "You can't modify [target]!") + to_chat(user, span_warning("You cannot modify [target]!")) + /obj/item/lighter/zippo/fluff/purple // GodOfOreos: Jason Conrad name = "purple engraved zippo" diff --git a/code/modules/detective_work/scanner.dm b/code/modules/detective_work/scanner.dm index 61a7b5ba780..370f0ac7ecf 100644 --- a/code/modules/detective_work/scanner.dm +++ b/code/modules/detective_work/scanner.dm @@ -60,8 +60,8 @@ else to_chat(user, "В записях станции не найдено совпадений.") -/obj/item/detective_scanner/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/print_forensic_report) +/obj/item/detective_scanner/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/print_forensic_report)) print_scanner_report() else clear_scanner() diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 10f7ef9cf72..d39e3b4add4 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -1,102 +1,12 @@ /datum/event/dust var/qnty = 1 + /datum/event/dust/setup() - qnty = rand(1,5) + qnty = rand(1, 5) + /datum/event/dust/start() while(qnty-- > 0) - new /obj/effect/space_dust/weak() - -/obj/effect/space_dust - name = "Space Dust" - desc = "Dust in space." - icon = 'icons/obj/meteor.dmi' - icon_state = "space_dust" - density = TRUE - anchored = TRUE - var/strength = 2 //ex_act severity number - var/life = 2 //how many things we hit before qdel(src) - var/atom/goal = null - var/shake_chance = 50 - -/obj/effect/space_dust/weak - strength = 3 - life = 1 - -/obj/effect/space_dust/strong - strength = 1 - life = 6 - -/obj/effect/space_dust/super - strength = 1 - life = 40 - -/obj/effect/space_dust/New() - . = ..() - var/startx = 0 - var/starty = 0 - var/endy = 0 - var/endx = 0 - var/startside = pick(GLOB.cardinal) - - switch(startside) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - goal = locate(endx, endy, 1) - src.x = startx - src.y = starty - src.z = pick(levels_by_trait(STATION_LEVEL)) - walk_towards(src, goal, 1) - -/obj/effect/space_dust/Bump(atom/A) - if(QDELETED(src)) - return - if(prob(shake_chance)) - for(var/mob/M in range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai)) - shake_camera(M, 3, 1) - playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) - - INVOKE_ASYNC(src, PROC_REF(impact_meteor), A) // ex_act can have some sleeps in it - -/obj/effect/space_dust/proc/impact_meteor(atom/A) - var/turf/where = get_turf(A) - if(ismob(A)) - A.ex_act(strength)//This should work for now I guess - else if(!istype(A, /obj/machinery/power/emitter) && !istype(A, /obj/machinery/field/generator)) //Protect the singularity from getting released every round! - A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 - - life-- - if(life <= 0) - walk(src, 0) - on_shatter(where) - qdel(src) - -/obj/effect/space_dust/proc/on_shatter(turf/where) - return - -/obj/effect/space_dust/Bumped(atom/movable/moving_atom) - Bump(moving_atom) - return + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 1, GLOB.meteors_space_dust) -/obj/effect/space_dust/ex_act(severity) - qdel(src) diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 035b6f5b661..26d50e7223b 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -20,6 +20,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 var/turf/endT = spaceDebrisFinishLoc(startside, level) new /obj/effect/immovablerod/event(startT, endT) + /obj/effect/immovablerod name = "Immovable Rod" desc = "What the fuck is that?" @@ -28,24 +29,40 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 throwforce = 100 density = TRUE anchored = TRUE - var/z_original = 0 - var/destination + movement_type = FLYING + move_force = INFINITY + move_resist = INFINITY + pull_force = INFINITY + /// The turf we're looking to coast to + var/turf/destination_turf + /// Whether we notify ghosts var/notify = TRUE + ///Lower = faster var/move_delay = 1 -/obj/effect/immovablerod/New(atom/start, atom/end, delay) + +/obj/effect/immovablerod/Initialize(mapload, atom/target_atom, move_delay = 1) . = ..() - loc = start - z_original = z - destination = end - move_delay = delay - if(notify) - notify_ghosts("\A [src] is inbound!", - enter_link="(Click to follow)", - source=src, action=NOTIFY_FOLLOW) + GLOB.poi_list |= src - if(end && end.z==z_original) - walk_towards(src, destination, move_delay) + + src.move_delay = move_delay + + if(notify) + notify_ghosts("[src] is inbound!", enter_link="(Click to follow)", source = src, action = NOTIFY_FOLLOW) + + destination_turf = get_turf(target_atom) + if(!destination_turf || destination_turf.z != src.z) + return + + SSmove_manager.move_towards(src, destination_turf, move_delay) + + +/obj/effect/immovablerod/Destroy() + destination_turf = null + GLOB.poi_list.Remove(src) + return ..() + /obj/effect/immovablerod/Topic(href, href_list) if(href_list["follow"]) @@ -53,46 +70,67 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 if(istype(ghost)) ghost.ManualFollow(src) -/obj/effect/immovablerod/Destroy() - GLOB.poi_list.Remove(src) - return ..() /obj/effect/immovablerod/ex_act(severity) - return 0 + return + /obj/effect/immovablerod/singularity_act() return + /obj/effect/immovablerod/singularity_pull() return -/obj/effect/immovablerod/Bump(atom/clong) + +/obj/effect/immovablerod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + return TRUE + + +/obj/effect/immovablerod/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . + if(prob(10)) - playsound(src, 'sound/effects/bang.ogg', 50, 1) + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) audible_message("ЛЯЗГ") - if(clong && prob(25)) - x = clong.x - y = clong.y + var/turf/ref_bumped_turf = get_turf(bumped_atom) + + if(isturf(bumped_atom) || isobj(bumped_atom)) + if(bumped_atom.density) + bumped_atom.ex_act(EXPLODE_HEAVY) + + else if(isliving(bumped_atom)) + var/mob/living/bumped_living = bumped_atom + if(ishuman(bumped_living)) + var/mob/living/carbon/human/bumped_human = bumped_living + bumped_human.visible_message( + span_danger("[bumped_human.name] пронизан незыблемым стержнем!"), + span_userdanger("Стержень пронзает Вас!"), + span_danger("Вы слышите ЛЯЗГ!"), + ) + bumped_human.adjustBruteLoss(160) + if(bumped_living.density || prob(10)) + bumped_living.ex_act(EXPLODE_HEAVY) + + if(ref_bumped_turf != destination_turf && !QDELETED(src)) + forceMove(ref_bumped_turf) + if(prob(25)) + var/new_dir = turn(dir, pick(5; 90, 5; -90, 45, -45)) + destination_turf = get_edge_target_turf(src, new_dir) + SSmove_manager.move_towards(src, destination_turf, move_delay) - if(istype(clong, /turf) || isobj(clong)) - if(clong.density) - clong.ex_act(2) - - else if(istype(clong, /mob)) - if(ishuman(clong)) - var/mob/living/carbon/human/H = clong - H.visible_message("[H.name] пронизан незыблемым стержнем!" , "Стержень пронзает тебя!" , "Вы слышите ЛЯЗГ!") - H.adjustBruteLoss(160) - if(clong.density || prob(10)) - clong.ex_act(2) /obj/effect/immovablerod/event var/tiles_moved = 0 -/obj/effect/immovablerod/event/Move() + +/obj/effect/immovablerod/event/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/atom/oldloc = loc . = ..() tiles_moved++ if(get_dist(oldloc, loc) > 2 && tiles_moved > 10) // We went on a journey, commit sudoku qdel(src) + diff --git a/code/modules/events/meaty_gore.dm b/code/modules/events/meaty_gore.dm index 6672e670057..a1f23466d0d 100644 --- a/code/modules/events/meaty_gore.dm +++ b/code/modules/events/meaty_gore.dm @@ -7,7 +7,7 @@ /datum/event/meteor_wave/gore/tick() if(waves && activeFor >= next_meteor) - spawn() spawn_meteors(rand(5,8), GLOB.meteors_gore) + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, rand(5, 8), GLOB.meteors_gore) next_meteor += rand(15, 30) waves-- endWhen = (waves ? next_meteor + 1 : activeFor + 15) diff --git a/code/modules/events/meaty_ops.dm b/code/modules/events/meaty_ops.dm index 7c1ea501cda..1c242162d85 100644 --- a/code/modules/events/meaty_ops.dm +++ b/code/modules/events/meaty_ops.dm @@ -8,7 +8,7 @@ /datum/event/meteor_wave/goreop/tick() if(waves && activeFor >= next_meteor) - spawn() spawn_meteors(5, GLOB.meteors_ops) + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 5, GLOB.meteors_ops) next_meteor += rand(15, 30) waves-- endWhen = (waves ? next_meteor + 1 : activeFor + 15) diff --git a/code/modules/events/meaty_ores.dm b/code/modules/events/meaty_ores.dm index d8abcb836d7..aedb5c7dc00 100644 --- a/code/modules/events/meaty_ores.dm +++ b/code/modules/events/meaty_ores.dm @@ -1,36 +1,20 @@ +/datum/event/dust/meaty + + +/datum/event/dust/meaty/setup() + qnty = rand(45,125) + + /datum/event/dust/meaty/announce() if(prob(16)) GLOB.event_announcement.Announce("Неизвестные биологические объекты были обнаружены рядом с [station_name()], пожалуйста, будьте наготове.", "ВНИМАНИЕ: НЕОПОЗНАННЫЕ ФОРМЫ ЖИЗНИ.") else GLOB.event_announcement.Announce("На пути станции были обнаружены мясориты.", "ВНИМАНИЕ: МЯСОРИТЫ.", new_sound = 'sound/AI/meteors.ogg') -/datum/event/dust/meaty/setup() - qnty = rand(45,125) /datum/event/dust/meaty/start() while(qnty-- > 0) - new /obj/effect/space_dust/meaty() + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 1, GLOB.meteors_pigs) if(prob(10)) - sleep(rand(10,15)) - -/obj/effect/space_dust/meaty - icon = 'icons/mob/animal.dmi' - icon_state = "pig" + sleep(rand(1 SECONDS, 1.5 SECONDS)) - strength = 1 - life = 3 - shake_chance = 20 - -/obj/effect/space_dust/meaty/impact_meteor(atom/A) - new /obj/effect/decal/cleanable/blood(get_turf(A)) - ..() - -/obj/effect/space_dust/meaty/on_shatter(turf/where) - if(prob(80)) - gibs(where) - if(prob(45)) - new /obj/item/reagent_containers/food/snacks/meat(where) - else if(prob(10)) - explosion(where, 0, pick(0,1), pick(2,3), 0, cause = src) - else - new /mob/living/simple_animal/pig(where) diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index 81efd34fc96..0eed1856f45 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -16,7 +16,7 @@ //meteor showers are lighter and more common, /datum/event/meteor_wave/tick() if(waves && activeFor >= next_meteor) - spawn() spawn_meteors(severity * rand(1,2), get_meteors()) + INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, severity * rand(1, 2), get_meteors()) next_meteor += rand(15, 30) / severity waves-- endWhen = (waves ? next_meteor + 1 : activeFor + 15) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 12319f6936b..2b90c1d034f 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -235,8 +235,7 @@ GLOBAL_LIST_INIT(major_hallutinations, list("fake"=20,"death"=10,"xeno"=10,"sing if(pump) borer = new(pump.loc,target) for(var/i in 0 to 10) - borer.glide_for(3) - walk_to(borer, get_step(borer, get_cardinal_dir(borer, T))) + SSmove_manager.move_to(borer, T, 1, rand(2, 4)) if(borer.Adjacent(T)) to_chat(T, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.") T.Stun(8 SECONDS) diff --git a/code/modules/instruments/objs/structures/drumkit.dm b/code/modules/instruments/objs/structures/drumkit.dm index 77bec99bf63..3ff28c62377 100644 --- a/code/modules/instruments/objs/structures/drumkit.dm +++ b/code/modules/instruments/objs/structures/drumkit.dm @@ -15,7 +15,7 @@ handle_layer() handle_offsets() -/obj/structure/musician/drumkit/Move(NewLoc, Dir = 0, movetime) +/obj/structure/musician/drumkit/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() handle_layer() handle_offsets() diff --git a/code/modules/lighting/lighting_emissive_blocker.dm b/code/modules/lighting/lighting_emissive_blocker.dm index ba6ea24bc57..a57b2cc66d6 100644 --- a/code/modules/lighting/lighting_emissive_blocker.dm +++ b/code/modules/lighting/lighting_emissive_blocker.dm @@ -59,7 +59,7 @@ /atom/movable/emissive_blocker/Uncrossed(atom/movable/AM) return -/atom/movable/emissive_blocker/Bump(atom/A, yes) +/atom/movable/emissive_blocker/Bump(atom/bumped_atom, custom_bump) return /atom/movable/emissive_blocker/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable) diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index f3c485349ea..67994513f79 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) /atom/movable/lighting_object/Uncrossed(atom/movable/AM) return -/atom/movable/lighting_object/Bump(atom/A, yes) +/atom/movable/lighting_object/Bump(atom/bumped_atom, custom_bump) return /atom/movable/lighting_object/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 29871c8f56b..640e5e13776 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -153,7 +153,7 @@ update_icon() playsound(src.loc, 'sound/weapons/crusher_reload.ogg', 135) -/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, actiontype) +/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, action) set_light_on(!light_on) playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) update_icon() diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm index dc846a8a45b..562ca3ba0f0 100644 --- a/code/modules/mining/equipment/mineral_scanner.dm +++ b/code/modules/mining/equipment/mineral_scanner.dm @@ -119,7 +119,7 @@ plane = FULLSCREEN_PLANE layer = FLASH_LAYER icon = 'icons/effects/ore_visuals.dmi' - appearance_flags = 0 //to avoid having TILE_BOUND in the flags, so that the 480x480 icon states let you see it no matter where you are + appearance_flags = LONG_GLIDE //to avoid having TILE_BOUND in the flags, so that the 480x480 icon states let you see it no matter where you are duration = 35 pixel_x = -224 pixel_y = -224 diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index 074e4052aef..e73777071e3 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -71,7 +71,7 @@ return 1 /obj/machinery/anomalous_crystal/Bumped(atom/movable/moving_atom) - ..() + . = ..() if(ismob(moving_atom)) ActivationReaction(moving_atom,"mob_bump") diff --git a/code/modules/mining/lavaland/loot/hierophant_loot.dm b/code/modules/mining/lavaland/loot/hierophant_loot.dm index 650e791d146..5e837c6beb7 100644 --- a/code/modules/mining/lavaland/loot/hierophant_loot.dm +++ b/code/modules/mining/lavaland/loot/hierophant_loot.dm @@ -122,8 +122,8 @@ update_icon(UPDATE_ICON_STATE) -/obj/item/hierophant_club/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/toggle_unfriendly_fire) //toggle friendly fire... +/obj/item/hierophant_club/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/toggle_unfriendly_fire)) //toggle friendly fire... friendly_fire_check = !friendly_fire_check to_chat(user, "You toggle friendly fire [friendly_fire_check ? "off":"on"]!") return diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm index 80776e17e02..d12b4c6b2b1 100644 --- a/code/modules/mining/lavaland/loot/tendril_loot.dm +++ b/code/modules/mining/lavaland/loot/tendril_loot.dm @@ -155,22 +155,26 @@ desc = "A boat used for traversing lava." icon_state = "goliath_boat" icon = 'icons/obj/lavaland/dragonboat.dmi' - held_key_type = /obj/item/oar + layer = ABOVE_MOB_LAYER + key_type = /obj/item/oar + key_in_hands = TRUE resistance_flags = LAVA_PROOF | FIRE_PROOF - /// The last time we told the user that they can't drive on land, so we don't spam them - var/last_message_time = 0 -/obj/vehicle/lavaboat/relaymove(mob/user, direction) - var/turf/next = get_step(src, direction) - var/turf/current = get_turf(src) - if(istype(next, /turf/simulated/floor/plating/lava/smooth) || istype(current, /turf/simulated/floor/plating/lava/smooth)) //We can move from land to lava, or lava to land, but not from land to land - ..() - else - if(last_message_time + 1 SECONDS < world.time) - to_chat(user, "Boats don't go on land!") - last_message_time = world.time +/obj/vehicle/lavaboat/relaymove(mob/user, direction) + if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown)) return FALSE + //We can move from land to lava, or lava to land, but not from land to land + if(!istype(get_step(src, direction), /turf/simulated/floor/plating/lava/smooth) && !istype(get_turf(src), /turf/simulated/floor/plating/lava/smooth)) + to_chat(user, span_warning("You cannot traverse futher!")) + COOLDOWN_START(src, vehicle_move_cooldown, 0.5 SECONDS) + return FALSE + return ..() + + +/obj/vehicle/lavaboat/handle_vehicle_layer() + return + /obj/item/oar name = "oar" @@ -213,11 +217,12 @@ /obj/vehicle/lavaboat/dragon name = "mysterious boat" desc = "This boat moves where you will it, without the need for an oar." - held_key_type = null + key_type = null + key_in_hands = FALSE icon_state = "dragon_boat" generic_pixel_y = 2 generic_pixel_x = 1 - vehicle_move_delay = 1 + vehicle_move_delay = 0.25 SECONDS //Wisp Lantern /obj/item/wisp_lantern diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 9504250c7fe..711c74d82d1 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -239,35 +239,36 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return // Ghosts have no momentum, being massless ectoplasm -/mob/dead/observer/Process_Spacemove(movement_dir = NONE) +/mob/dead/observer/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE -/mob/dead/observer/Move(NewLoc, direct) - if(world.time < last_movement) - return - last_movement = world.time + 0.5 // cap to 20fps - glide_size = 8 - update_parallax_contents() +/mob/dead/observer/Move(atom/newloc, direct = NONE, glide_size_override = 8) setDir(direct) ghostimage.setDir(dir) - var/oldloc = loc + if(glide_size_override) + set_glide_size(glide_size_override) - if(NewLoc) - forceMove(NewLoc) + if(newloc) + abstract_move(newloc) + update_parallax_contents() else - forceMove(get_turf(src)) //Get out of closets and such as a ghost + var/turf/destination = get_turf(src) + if((direct & NORTH) && y < world.maxy) - y++ + destination = get_step(destination, NORTH) + else if((direct & SOUTH) && y > 1) - y-- + destination = get_step(destination, SOUTH) + if((direct & EAST) && x < world.maxx) - x++ + destination = get_step(destination, EAST) + else if((direct & WEST) && x > 1) - x-- + destination = get_step(destination, WEST) - Moved(oldloc, direct) + abstract_move(destination)//Get out of closets and such as a ghost /mob/dead/observer/Stat() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 24bacdc88d0..06f826413f1 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -39,7 +39,7 @@ adjustBruteLoss(10) -/mob/living/carbon/Move(NewLoc, direct) +/mob/living/carbon/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(.) if(nutrition && stat != DEAD && !isvampire(src)) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 1db4d77ac53..9e0287af468 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -4,7 +4,7 @@ voice_name = "unknown" icon = 'icons/mob/human.dmi' icon_state = "body_m_s" - appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE + appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE deathgasp_on_death = TRUE hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD,THOUGHT_HUD,DIAG_STAT_HUD,DIAG_HUD) pressure_resistance = 25 diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 0a05029c233..326d71f8e51 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -16,29 +16,15 @@ return considering -/mob/living/carbon/human/Process_Spacemove(movement_dir = NONE) - . = ..() - if(.) - return . - - var/list/jetpacks = list() - - if(istype(back, /obj/item/tank/jetpack)) - jetpacks += back - - var/obj/item/clothing/suit/space/space_suit = wear_suit - if(istype(space_suit) && space_suit.jetpack) - jetpacks += space_suit.jetpack - - for(var/obj/item/tank/jetpack/jetpack as anything in jetpacks) - if((movement_dir || jetpack.stabilizers) && jetpack.allow_thrust(0.01, src, should_leave_trail = movement_dir)) - return TRUE - - if(dna.species.spec_Process_Spacemove(src, movement_dir)) +/mob/living/carbon/human/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + if(movement_type & FLYING) return TRUE + if(dna.species.spec_Process_Spacemove(src, movement_dir, continuous_move = FALSE)) + return TRUE + return ..() -/mob/living/carbon/human/Move(NewLoc, direct) +/mob/living/carbon/human/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(.) // did we actually move? if(!lying_angle && !buckled && !throwing) @@ -61,7 +47,7 @@ var/obj/item/clothing/shoes/S = shoes - if(S && !lying_angle && loc == NewLoc) + if(S && !lying_angle && loc == newloc) SEND_SIGNAL(S, COMSIG_SHOES_STEP_ACTION) //Bloody footprints diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 16fadc84b16..96cac3bb048 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -1170,7 +1170,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u return user.get_organ_slot(INTERNAL_ORGAN_EYES) -/datum/species/proc/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir) +/datum/species/proc/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir, continuous_move = FALSE) return FALSE diff --git a/code/modules/mob/living/carbon/human/species/moth.dm b/code/modules/mob/living/carbon/human/species/moth.dm index 023433ed833..d370fb7fc7f 100644 --- a/code/modules/mob/living/carbon/human/species/moth.dm +++ b/code/modules/mob/living/carbon/human/species/moth.dm @@ -126,7 +126,7 @@ apply_damage(I.force * FLYSWATTER_DAMAGE_MULTIPLIER, I.damtype, affecting, FALSE, H) //making flyswatters do 10x damage to moff -/datum/species/moth/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir) +/datum/species/moth/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir, continuous_move = FALSE) . = FALSE var/turf/user_turf = get_turf(user) if(!user_turf) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7f13c3e56dc..a299c723d3a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -157,21 +157,23 @@ 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, move_force)) - return +// Generic Bump(). Override MobBump() and ObjBump() instead of this. +/mob/living/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) // byond bump or we are thrown onto something + return . + if(buckled || now_pushing) + return . + if(ismob(bumped_atom)) + if(MobBump(bumped_atom)) + return TRUE + else if(isobj(bumped_atom)) + if(ObjBump(bumped_atom)) + return TRUE + else if(ismovable(bumped_atom)) + if(PushAM(bumped_atom, move_force)) + return TRUE + //Called when we bump into a mob /mob/living/proc/MobBump(mob/M) @@ -294,13 +296,13 @@ if(!client && (mob_size < MOB_SIZE_SMALL)) return now_pushing = TRUE - var/t = get_dir(src, AM) + var/dir_to_target = get_dir(src, AM) var/push_anchored = FALSE if((AM.move_resist * MOVE_FORCE_CRUSH_RATIO) <= force) - if(move_crush(AM, move_force, t)) + if(move_crush(AM, move_force, dir_to_target)) push_anchored = TRUE if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally - if(force_push(AM, move_force, t, push_anchored)) + if(force_push(AM, move_force, dir_to_target, push_anchored)) push_anchored = TRUE if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO))) now_pushing = FALSE @@ -308,22 +310,21 @@ if(istype(AM, /obj/structure/window)) var/obj/structure/window/W = AM if(W.fulltile) - for(var/obj/structure/window/win in get_step(W,t)) + for(var/obj/structure/window/win in get_step(W, dir_to_target)) now_pushing = FALSE return if(pulling == AM) stop_pulling() - if(client) - client.current_move_delay *= AM.get_pull_push_speed_modifier(client.current_move_delay) - glide_for(client.current_move_delay) + //if(client) + // client.move_delay += AM.get_pull_push_speed_modifier(client.move_delay) - AM.glide_size = glide_size var/current_dir if(isliving(AM)) current_dir = AM.dir - if(step(AM, t)) - step(src, t) + if(AM.Move(get_step(AM.loc, dir_to_target), dir_to_target, glide_size)) + AM.add_fingerprint(src) + Move(get_step(loc, dir_to_target), dir_to_target) if(current_dir) AM.setDir(current_dir) now_pushing = FALSE @@ -768,7 +769,7 @@ return -/mob/living/Move(atom/newloc, direct, movetime) +/mob/living/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(buckled && buckled.loc != newloc) //not updating position if(!buckled.anchored) return buckled.Move(newloc, direct) @@ -787,9 +788,9 @@ . = ..() if(.) step_count++ - pull_pulled(old_loc, pulling, movetime) + pull_pulled(old_loc, pulling, glide_size_override) if(!currently_grab_pulled) - pull_grabbed(old_loc, direct, movetime) + pull_grabbed(old_loc, direct, glide_size_override) 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() @@ -812,7 +813,7 @@ return var/pull_dir = get_dir(src, pulling) - pulling.glide_size = glide_size + pulling.set_glide_size(glide_size) if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position // This sucks. // Pulling things up/down & into other z-levels. Conga line lives. diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 3537ba9d33b..4574683803f 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -515,12 +515,9 @@ card.forceMove(card.loc) icon_state = "[chassis]" -/mob/living/silicon/pai/Bump() +/mob/living/silicon/pai/Bump(atom/bumped_atom, custom_bump) return -/mob/living/silicon/pai/Bumped(atom/movable/moving_atom) - return ..() - /mob/living/silicon/pai/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE) return FALSE diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 2ef8571b1f8..1f52fc4759b 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -359,13 +359,10 @@ to_chat(src, "Make sure crew members do not notice you..") -/mob/living/silicon/robot/drone/Bump(atom/movable/AM, yes) - if(is_type_in_list(AM, allowed_bumpable_objects)) +/mob/living/silicon/robot/drone/Bump(atom/bumped_atom, custom_bump) + if(custom_bump && is_type_in_list(bumped_atom, allowed_bumpable_objects)) return ..() -/mob/living/silicon/robot/drone/Bumped(atom/movable/moving_atom) - return ..() - /mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE) if(is_type_in_list(AM, pullable_drone_items)) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 4b23355de46..e750aa77a65 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -111,8 +111,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) var/default_cell_type = /obj/item/stock_parts/cell/high - var/ionpulse = 0 // Jetpack-like effect. - var/ionpulse_on = 0 // Jetpack-like effect. + ///Jetpack-like effect. + var/ionpulse = FALSE + ///Jetpack-like effect. + var/ionpulse_on = FALSE + ///Ionpulse effect. + var/datum/effect_system/trail_follow/ion/ion_trail var/datum/action/innate/research_scanner/scanner = null var/list/module_actions = list() @@ -186,16 +190,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( diag_hud_set_borgcell() scanner = new() scanner.Grant(src) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail)) if(length(module?.borg_skins) <= 1 && (has_transform_animation || module?.has_transform_animation)) transform_animation(icon_state, TRUE) -/mob/living/silicon/robot/proc/create_trail(datum/source, atom/oldloc, _dir, forced) - if(ionpulse_on) - var/turf/T = get_turf(oldloc) - if(!T.has_gravity(T)) - new /obj/effect/particle_effect/ion_trails(T, _dir) /mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) @@ -327,6 +325,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( QDEL_NULL(robot_suit) QDEL_NULL(spark_system) QDEL_NULL(self_diagnosis) + QDEL_NULL(ion_trail) return ..() /mob/living/silicon/robot/proc/pick_module(var/forced_module = null) @@ -655,15 +654,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( alerts.set_content(dat_text) alerts.open() + /mob/living/silicon/robot/proc/ionpulse() if(!ionpulse_on) return FALSE - - if(!cell || cell.charge <= 50) + if(!cell || !cell.use(25)) // 500 steps on a default cell. toggle_ionpulse(silent = TRUE) return FALSE - - cell.charge -= 25 // 500 steps on a default cell. return TRUE @@ -673,12 +670,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( to_chat(src, span_notice("No thrusters are installed!")) return - ionpulse_on = !ionpulse_on + if(!ion_trail) + ion_trail = new + ion_trail.set_up(src) - if(ionpulse_on) - add_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) - else - remove_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) + ionpulse_on = !ionpulse_on if(!silent) to_chat(src, span_notice("You [ionpulse_on ? "" : "de"]activate your ion thrusters.")) @@ -686,6 +682,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(thruster_button) thruster_button.icon_state = "ionpulse[ionpulse_on]" + if(ionpulse_on) + ion_trail.start() + add_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) + else + ion_trail.stop() + remove_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) + /mob/living/silicon/robot/blob_act(obj/structure/blob/B) if(stat != DEAD) @@ -1377,7 +1380,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( drop_hat() qdel(src) -/mob/living/silicon/robot/Move(a, b, flag) +/mob/living/silicon/robot/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/oldLoc = src.loc . = ..() if(.) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index b43b12b2e43..8499452d677 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,7 +1,10 @@ -/mob/living/silicon/robot/Process_Spacemove(movement_dir = NONE) +/mob/living/silicon/robot/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + . = ..() + if(.) + return TRUE if(ionpulse()) return TRUE - return ..() + return FALSE /mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 921b9ff91d4..911033966db 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -637,7 +637,7 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r if(!length(path)) return FALSE - glide_for(BOT_STEP_DELAY) + set_glide_size(DELAY_TO_GLIDE_SIZE(BOT_STEP_DELAY)) if(!step_towards(src, path[1])) tries++ return FALSE diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 5a0ea77acea..42332e76059 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -111,7 +111,7 @@ target = null oldtarget_name = null set_anchored(FALSE) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) last_found = world.time set_weapon() @@ -279,7 +279,7 @@ switch(mode) if(BOT_IDLE) // idle - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) if(!lasercolor) //lasertag bots don't want to arrest anyone look_for_perp() // see if any criminals are in range @@ -289,7 +289,7 @@ if(BOT_HUNT) // hunting for perp // if can't reach perp for long enough, go idle if(frustration >= 8) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) back_to_idle() @@ -309,8 +309,7 @@ else if(!disabled) // not next to perp var/turf/olddist = get_dist(src, target) - glide_for(BOT_STEP_DELAY) - walk_to(src, target,1,4) + SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY) if((get_dist(src, target)) >= (olddist)) frustration++ else @@ -420,7 +419,7 @@ /mob/living/simple_animal/bot/ed209/explode() - walk_to(src,0) + SSmove_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) @@ -563,7 +562,7 @@ if(lasertag_check) icon_state = "[lasercolor]ed2090" disabled = TRUE - walk_to(src, 0) + SSmove_manager.stop_looping(src) target = null addtimer(CALLBACK(src, PROC_REF(unset_disabled)), 10 SECONDS) return TRUE diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index e556f5b2085..6338f4f1730 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -151,7 +151,7 @@ switch(mode) if(BOT_IDLE) // idle icon_state = "[base_icon][on]" - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) look_for_perp() // see if any criminals are in range if(!mode && auto_patrol) // still idle, and set to patrol @@ -160,7 +160,7 @@ icon_state = spin_icon playsound(loc,'sound/effects/spinsabre.ogg',50,1,-1) if(frustration >= frustration_number) // general beepsky doesn't give up so easily, jedi scum - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -174,8 +174,7 @@ return else // not next to perp var/turf/olddist = get_dist(src, target) - glide_for(BOT_STEP_DELAY) - walk_to(src, target,1,3) //he's a fast fucker + SSmove_manager.move_to(src, target, 1, 3) //he's a fast fucker if((get_dist(src, target)) >= (olddist)) frustration++ else @@ -244,7 +243,7 @@ /mob/living/simple_animal/bot/secbot/griefsky/explode() - walk_to(src,0) + SSmove_manager.stop_looping(src) visible_message("[src] lets out a huge cough as it blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/assembly/prox_sensor(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index f260427721f..8157dc5ec6e 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -67,7 +67,7 @@ target = null oldtarget_name = null set_anchored(FALSE) - walk_to(src, 0) + SSmove_manager.stop_looping(src) last_found = world.time spam_flag = FALSE @@ -214,14 +214,14 @@ switch(mode) if(BOT_IDLE) // idle - walk_to(src, 0) + SSmove_manager.stop_looping(src) look_for_perp() if(!mode && auto_patrol) mode = BOT_START_PATROL if(BOT_HUNT) // if can't reach perp for long enough, go idle if(frustration >= 5) //gives up easier than beepsky - walk_to(src, 0) + SSmove_manager.stop_looping(src) playsound(loc, 'sound/misc/sadtrombone.ogg', 25, TRUE, -1) back_to_idle() return @@ -239,8 +239,7 @@ return else // not next to perp var/turf/olddist = get_dist(src, target) - glide_for(BOT_STEP_DELAY) - walk_to(src, target, 1, 4) + SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY) if((get_dist(src, target)) >= (olddist)) frustration++ else @@ -304,7 +303,7 @@ /mob/living/simple_animal/bot/honkbot/explode() //doesn't drop cardboard nor its assembly, since its a very frail material. - walk_to(src, 0) + SSmove_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/bikehorn(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 30e78b2d2af..5fbbe6dc829 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -692,7 +692,7 @@ bot_reset() // otherwise go idle -/mob/living/simple_animal/bot/mulebot/Move(turf/simulated/next) +/mob/living/simple_animal/bot/mulebot/Move(turf/simulated/next, direct = NONE, glide_size_override = 0) . = ..() if(. && istype(next)) @@ -720,19 +720,22 @@ /** * Called when bot bumps into anything. */ -/mob/living/simple_animal/bot/mulebot/Bump(atom/obs) - if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs - var/mob/living/L = obs - if(ismob(L)) - if(isrobot(L)) - visible_message(span_danger("[src] bumps into [L]!")) - else - if(!paicard) - add_attack_logs(src, L, "Knocked down") - visible_message(span_danger("[src] knocks over [L]!")) - L.stop_pulling() - L.Weaken(16 SECONDS) - return ..() +/mob/living/simple_animal/bot/mulebot/Bump(mob/living/bumped_living, custom_bump) + . = ..() + if(isnull(.) || !wires.is_cut(WIRE_MOB_AVOIDANCE) || !isliving(bumped_living)) + return . + + // usually just bumps, but if avoidance disabled knock over mobs + if(isrobot(bumped_living)) + visible_message(span_danger("[src] bumps into [bumped_living]!")) + return . + + if(paicard) + return . + + add_attack_logs(src, bumped_living, "Knocked down") + visible_message(span_danger("[src] knocks over [bumped_living]!")) + bumped_living.Weaken(16 SECONDS) /mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 226d44a3ba3..560e8fc2331 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -140,7 +140,7 @@ target = null oldtarget_name = null set_anchored(FALSE) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) last_found = world.time @@ -361,7 +361,7 @@ switch(mode) if(BOT_IDLE) // idle - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) look_for_perp() // see if any criminals are in range if(!mode && auto_patrol) // still idle, and set to patrol @@ -370,7 +370,7 @@ if(BOT_HUNT) // hunting for perp // if can't reach perp for long enough, go idle if(frustration >= 8) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -386,8 +386,7 @@ else // not next to perp var/turf/olddist = get_dist(src, target) - glide_for(BOT_STEP_DELAY) - walk_to(src, target,1,4) + SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY) if((get_dist(src, target)) >= (olddist)) frustration++ else @@ -493,7 +492,7 @@ /mob/living/simple_animal/bot/secbot/explode() - walk_to(src,0) + SSmove_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) var/obj/item/secbot_assembly/Sa = new /obj/item/secbot_assembly(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/syndicate.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm index 60674386ac4..ee007facf74 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm @@ -94,14 +94,14 @@ saved_turf = current_turf switch(mode) if(BOT_IDLE) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) look_for_perp() if(!mode && auto_patrol) mode = BOT_START_PATROL if(BOT_HUNT) if(frustration >= 8) - walk_to(src,0) + SSmove_manager.stop_looping(src) set_path(null) back_to_idle() if(target) @@ -111,8 +111,7 @@ return shootAt(target) var/turf/olddist = get_dist(src, target) - glide_for(BOT_STEP_DELAY) - walk_to(src, target,1,4) + SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY) if((get_dist(src, target)) >= (olddist)) frustration++ else @@ -183,7 +182,7 @@ if(!QDELETED(src)) if(depotarea) depotarea.list_remove(src, depotarea.guard_list) - walk_to(src,0) + SSmove_manager.stop_looping(src) visible_message(span_userdanger("[src] blows apart!")) do_sparks(3, 1, src) new /obj/effect/decal/cleanable/blood/oil(loc) @@ -220,7 +219,7 @@ return -/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 60c4a939792..3d76fa92c66 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -338,7 +338,7 @@ Bring those who still cling to this world of illusion back to the master so they may know Truth." -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index fdddfb615ad..00169cdbf65 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -164,7 +164,7 @@ if(!stat && !resting && !buckled) turns_since_scan++ if(turns_since_scan > 5) - walk_to(src,0) + SSmove_manager.stop_looping(src) turns_since_scan = 0 if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) movement_target = null @@ -177,9 +177,9 @@ movement_target = snack break if(movement_target) + StopResting() stop_automated_movement = 1 - glide_for(3) - walk_to(src,movement_target,0,3) + SSmove_manager.move_to(src, movement_target, 1, 4) /mob/living/simple_animal/pet/cat/Proc diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 5f6020c0dd1..93105a1f791 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -26,15 +26,6 @@ holder_type = /obj/item/holder/crab mob_size = MOB_SIZE_SMALL -/mob/living/simple_animal/crab/handle_automated_movement() - //CRAB movement - if(!stat) - if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - var/east_vs_west = pick(4, 8) - if(Process_Spacemove(east_vs_west)) - Move(get_step(src, east_vs_west), east_vs_west) /mob/living/simple_animal/crab/royal name = "королевский краб" diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 7a5a90b28a0..2a260ce33d4 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -660,7 +660,7 @@ maxHealth = 60 health = 60 -/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //Void puppies can navigate space. /mob/living/simple_animal/pet/dog/corgi/puppy/slime diff --git a/code/modules/mob/living/simple_animal/friendly/snail.dm b/code/modules/mob/living/simple_animal/friendly/snail.dm index 7d369bfc0e2..aa9f887838f 100644 --- a/code/modules/mob/living/simple_animal/friendly/snail.dm +++ b/code/modules/mob/living/simple_animal/friendly/snail.dm @@ -30,10 +30,10 @@ reagents = new() holder_type = /obj/item/holder/snail -/mob/living/simple_animal/snail/Process_Spacemove(movement_dir = NONE) - return TRUE +/mob/living/simple_animal/snail/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + return TRUE // why??? -/mob/living/simple_animal/snail/Move(atom/newloc, direct, movetime) +/mob/living/simple_animal/snail/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/oldLoc = src.loc . = ..() if(.) diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 1f0023e2fe2..cdf128072bd 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -63,7 +63,7 @@ else icon_state = "[icon_living]floor" -/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //No drifting in space for space bears! /mob/living/simple_animal/hostile/bear/brown diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index f9d5afafcab..cbe696743b8 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -53,7 +53,7 @@ var/static/beehometypecache = typecacheof(/obj/structure/beebox) var/static/hydroponicstypecache = typecacheof(/obj/machinery/hydroponics) -/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/poison/bees/Initialize(mapload) diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index cc59031271d..f533fbf02a5 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -95,7 +95,7 @@ base_dead_overlay.appearance_flags = RESET_COLOR add_overlay(base_dead_overlay) -/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //No drifting in space for space carp! //original comments do not steal /mob/living/simple_animal/hostile/carp/AttackingTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 9d7e146da29..091890e3aa6 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -33,7 +33,7 @@ -/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //copypasta from carp code /mob/living/simple_animal/hostile/deathsquid/ex_act(severity) diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index 002dc17f100..c71a656cdf3 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -29,7 +29,7 @@ faction = list("faithless") gold_core_spawnable = HOSTILE_SPAWN -/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/faithless/AttackingTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm index 1a3b4ef1f78..81fd894210e 100644 --- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm +++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm @@ -124,10 +124,9 @@ /mob/living/simple_animal/hostile/floor_cluwne/Goto(target, delay, minimum_distance) if(!manifested && !is_type_in_typecache(get_area(current_victim.loc), invalid_area_typecache)) - glide_for(delay) - walk_to(src, target, minimum_distance, delay) + SSmove_manager.move_to(src, target, minimum_distance, delay) else - walk_to(src,0) + SSmove_manager.stop_looping(src) /mob/living/simple_animal/hostile/floor_cluwne/FindTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index c5b00f7ce9f..b48be0a138b 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -52,12 +52,12 @@ if(C.can_inject(null, FALSE, inject_target, FALSE)) C.reagents.add_reagent("spidertoxin", venom_per_bite) -/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup() +/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup(moving_direction, continuous_move) . = ..() // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. if(!.) - for(var/obj/structure/spider/S in range(1, get_turf(src))) - return S + for(var/obj/structure/spider/spider_thing in range(1, get_turf(src))) + return spider_thing //nursemaids - these create webs and eggs /mob/living/simple_animal/hostile/poison/giant_spider/nurse @@ -97,7 +97,7 @@ Goto(pick(urange(20, src, 1)), move_to_delay) spawn(50) stop_automated_movement = 0 - walk(src,0) + SSmove_manager.stop_looping(src) return 1 /mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) @@ -202,7 +202,7 @@ busy = SPINNING_COCOON src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") stop_automated_movement = 1 - walk(src,0) + SSmove_manager.stop_looping(src) spawn(50) if(busy == SPINNING_COCOON) if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm index a4988d94684..a4c664e40df 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm @@ -177,7 +177,7 @@ else if(find_phrase(full_message, list("жди", "ожидай", "ждать"))) if(!is_waiting) is_waiting = TRUE - walk(src, 0) + SSmove_manager.stop_looping(src) oogaooga(100) if(is_bipedal) if(LAZYLEN(crates_in_hand)) @@ -501,7 +501,7 @@ */ /mob/living/simple_animal/hostile/gorilla/proc/reset_behavior(play_emote = TRUE, end_of_excitement = FALSE) check_buckled_gorilla() - walk(src, 0) + SSmove_manager.stop_looping(src) toggle_ai(AI_ON) if(play_emote) oogaooga(100, 200) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index d52fe358076..3f70dd84dda 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -8,11 +8,16 @@ var/rapid = 0 //How many shots per volley. var/rapid_fire_delay = 2 //Time between rapid fire shots + ///Are we dodging? var/dodging = TRUE - var/approaching_target = FALSE //We should dodge now - var/in_melee = FALSE //We should sidestep now + ///We should dodge now + var/approaching_target = FALSE + ///We should sidestep now + var/in_melee = FALSE + ///Probability that we dodge var/dodge_prob = 30 - var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee + ///How many sidesteps per npcpool cycle when in melee + var/sidestep_per_cycle = 1 var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile var/projectilesound @@ -56,9 +61,6 @@ var/mob_attack_logs = list() //for hostiles and megafauna - /// Used to disable gliding if mob is too slow, like goliath - var/needs_gliding = TRUE - tts_seed = "Vort_e2" dirslash_enabled = TRUE @@ -87,9 +89,8 @@ /mob/living/simple_animal/hostile/Life(seconds, times_fired) . = ..() - if(!.) - walk(src, 0) - return FALSE + if(!.) // dead + SSmove_manager.stop_looping(src) /mob/living/simple_animal/hostile/handle_automated_action() if(AIStatus == AI_OFF) @@ -107,6 +108,7 @@ toggle_ai(AI_IDLE) // otherwise we go idle return 1 + /mob/living/simple_animal/hostile/handle_automated_movement() . = ..() if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) @@ -116,7 +118,8 @@ for(var/i in 1 to sidestep_per_cycle) addtimer(cb, (i - 1) * sidestep_delay) else //Otherwise randomize it to make the players guessing. - addtimer(cb,rand(1, SSnpcpool.wait)) + addtimer(cb, rand(1, SSnpcpool.wait)) + /mob/living/simple_animal/hostile/proc/sidestep() if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) @@ -125,17 +128,17 @@ var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) - var/chosen_dir = 0 - if(target_dir & (target_dir - 1)) + var/chosen_dir = NONE + if(ISDIAGONALDIR(target_dir)) chosen_dir = pick(diagonal_sidestep_directions) else chosen_dir = pick(cardinal_sidestep_directions) if(chosen_dir) - chosen_dir = turn(target_dir, chosen_dir) - var/step_loc = get_step(src, chosen_dir) - Move(step_loc, chosen_dir, 3) + chosen_dir = turn(target_dir,chosen_dir) + Move(get_step(src, chosen_dir)) face_atom(target) //Looks better if they keep looking at you when dodging + /mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) FindTarget(list(user), 1) @@ -348,13 +351,12 @@ if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown OpenFire(target) if(!Process_Spacemove(NONE)) //Drifting - walk(src,0) - return 1 - if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target + SSmove_manager.stop_looping(src) + return TRUE + if(!isnull(retreat_distance)) //If we have a retreat distance, check if we need to run from our target if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run - if(needs_gliding) - glide_for(move_to_delay) - walk_away(src,target,retreat_distance,move_to_delay) + var/glide_flag = move_to_delay > MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE ? MOVEMENT_LOOP_IGNORE_GLIDE : NONE + SSmove_manager.move_away(src, target, retreat_distance, move_to_delay, flags = glide_flag) else Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them else @@ -382,14 +384,15 @@ LoseTarget() return 0 + /mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) if(target == src.target) approaching_target = TRUE else approaching_target = FALSE - if(needs_gliding) - glide_for(delay) - walk_to(src, target, minimum_distance, delay) + var/glide_flag = move_to_delay > MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE ? MOVEMENT_LOOP_IGNORE_GLIDE : NONE + SSmove_manager.move_to(src, target, minimum_distance, delay, flags = glide_flag) + /mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) . = ..() @@ -428,7 +431,7 @@ target = null approaching_target = FALSE in_melee = FALSE - walk(src, 0) + SSmove_manager.stop_looping(src) LoseAggro() //////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// @@ -504,27 +507,22 @@ return iswallturf(T) || (ismineralturf(T) && !istype(T, /turf/simulated/mineral/ancient/outer)) -/mob/living/simple_animal/hostile/Move(atom/newloc, direct, movetime) - . = dodge(direct, movetime) - if(!.) - . = ..() - - -/mob/living/simple_animal/hostile/proc/dodge(direct, movetime) - . = FALSE - if(client) - return . - if(!dodging || !approaching_target || moving_diagonally) - return . - if(!isturf(loc)) - return . - if(!prob(dodge_prob)) - return . - var/turf/dodge_loc = get_step(loc, pick(turn(direct, 45), turn(direct, -45))) - if(!length(get_path_to(src, dodge_loc, max_distance = 1, simulated_only = FALSE, skip_first = FALSE))) - return . +/mob/living/simple_animal/hostile/Move(atom/newloc, direct = NONE, glide_size_override = 0) + if(dodging && approaching_target && prob(dodge_prob) && !moving_diagonally && isturf(loc) && isturf(newloc)) + return dodge(newloc, dir) + else + return ..() + + +/mob/living/simple_animal/hostile/proc/dodge(moving_to, move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction, 45) + var/ccdir = turn(move_direction, -45) dodging = FALSE - . = Move(dodge_loc, direct, movetime) + . = Move(get_step(loc, pick(cdir, ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to, move_direction) + face_atom(target) dodging = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm index 197f39034ac..9d29c5800d0 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm @@ -273,14 +273,14 @@ Difficulty: Very Hard charging = TRUE revving_charge = TRUE DestroySurroundings() - walk(src, 0) + SSmove_manager.stop_looping(src) setDir(dir) SLEEP_CHECK_DEATH(delay) revving_charge = FALSE var/movespeed = 0.8 - walk_towards(src, T, movespeed) + SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed) - walk(src, 0) // cancel the movement + SSmove_manager.stop_looping(src) charging = FALSE /mob/living/simple_animal/hostile/megafauna/ancient_robot/MeleeAction(patience = TRUE) @@ -288,22 +288,23 @@ Difficulty: Very Hard return return ..() -/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(atom/A) - if(charging) - if(isliving(A)) - var/mob/living/L = A - if(!istype(A, /mob/living/simple_animal/hostile/ancient_robot_leg)) - L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") - forceMove(get_turf(L)) - var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) - L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) - playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE) - shake_camera(L, 4, 3) - shake_camera(src, 2, 3) - if(mode == GRAV || enraged) - var/atom/throw_target = get_edge_target_turf(L, get_dir(src, get_step_away(L, src))) - L.throw_at(throw_target, 3, 2) - ..() + +/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(mob/living/bumped_living, custom_bump) + . = ..() + if(isnull(.) || !charging || istype(bumped_living, /mob/living/simple_animal/hostile/ancient_robot_leg) || !isliving(bumped_living)) + return . + var/turf/living_turf = get_turf(bumped_living) + bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!") + forceMove(living_turf) + var/limb_to_hit = bumped_living.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) + bumped_living.apply_damage(25, BRUTE, limb_to_hit, bumped_living.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) + playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE) + shake_camera(bumped_living, 4, 3) + shake_camera(src, 2, 3) + if(mode == GRAV || enraged) + var/atom/throw_target = get_edge_target_turf(bumped_living, get_dir(src, get_step_away(bumped_living, src))) + bumped_living.throw_at(throw_target, 3, 2) + /mob/living/simple_animal/hostile/megafauna/ancient_robot/proc/body_shield() body_shield_enabled = TRUE @@ -463,7 +464,7 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/ancient_robot/proc/self_destruct() say(pick("OTZKMXOZE LGORAXK, YKRL JKYZXAIZ GIZOBK", "RUYY IKXZGOT, KTMGMKOTM XKIUBKXE JKTOGR", "VUCKX IUXKY 8-12 HXKGINKJ, UBKXRUGJOTM XKSGOTOTM IUXKY", "KXXUX KXXUX KXXUX KXXUX KXX-", "-ROQK ZKGXY OT XGOT- - -ZOSK ZU JOK")) visible_message("[src] begins to overload it's core. It is going to explode!") - walk(src, 0) + SSmove_manager.stop_looping(src) playsound(src,'sound/machines/alarm.ogg',100,0,5) addtimer(CALLBACK(src, PROC_REF(kaboom)), 10 SECONDS) @@ -692,24 +693,23 @@ Difficulty: Very Hard forceMove(core.loc) core.fix_specific_leg(who_am_i) -/mob/living/simple_animal/hostile/ancient_robot_leg/proc/leg_movement(turf/T, movespeed) //byond doesn't like calling walk_towards on the legs directly - walk_towards(src, T, movespeed) - walk_towards(src, T, movespeed) +/mob/living/simple_animal/hostile/ancient_robot_leg/proc/leg_movement(turf/T, movespeed) + SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + + +/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(mob/living/bumped_living, custom_bump) + . = ..() + if(isnull(.) || !core.charging || istype(bumped_living, /mob/living/simple_animal/hostile/megafauna/ancient_robot) || !isliving(bumped_living)) + return . + var/turf/living_turf = get_turf(bumped_living) + bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!") + forceMove(living_turf) + var/limb_to_hit = bumped_living.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) + bumped_living.apply_damage(12.5, BRUTE, limb_to_hit, bumped_living.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) + playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE) + shake_camera(bumped_living, 4, 3) + shake_camera(src, 2, 3) -/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(atom/A) - if(!core.charging) - return - if(isliving(A)) - if(!istype(A, /mob/living/simple_animal/hostile/megafauna/ancient_robot)) - var/mob/living/L = A - L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") - forceMove(get_turf(L)) - var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) - L.apply_damage(12.5, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) - playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE) - shake_camera(L, 4, 3) - shake_camera(src, 2, 3) - ..() /mob/living/simple_animal/hostile/ancient_robot_leg/ex_act(severity, target) switch(severity) @@ -738,6 +738,7 @@ Difficulty: Very Hard ..() /mob/living/simple_animal/hostile/ancient_robot_leg/Moved(atom/OldLoc, Dir, Forced = FALSE) + SHOULD_CALL_PARENT(FALSE) // I'm sorry playsound(src, 'sound/effects/meteorimpact.ogg', 60, TRUE, 2, TRUE) //turned way down from bubblegum levels due to 4 legs /mob/living/simple_animal/hostile/ancient_robot_leg/mob_negates_gravity() @@ -767,11 +768,12 @@ Difficulty: Very Hard /obj/item/projectile/energy/shock_revolver/ancient damage = 5 -/obj/item/projectile/energy/shock_revolver/ancient/Bump(atom/A, yes) // Don't want the projectile hitting the legs - if(!istype(/mob/living/simple_animal/hostile/ancient_robot_leg, A)) - return ..() - var/turf/target_turf = get_turf(A) - loc = target_turf + +/obj/item/projectile/energy/shock_revolver/ancient/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if(istype(mover, /mob/living/simple_animal/hostile/ancient_robot_leg)) + return TRUE + /obj/effect/temp_visual/dragon_swoop/bubblegum/ancient_robot //this is the worst path I have ever made icon_state = "target" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 17cee06ebb8..80aa2553cd8 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -223,7 +223,7 @@ Difficulty: Medium new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir) return ..() -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc) +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid! return FALSE . = ..() 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 1f62b684f52..9e4ccac7ee3 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -240,16 +240,16 @@ Difficulty: Hard charging = TRUE revving_charge = TRUE DestroySurroundings() - walk(src, 0) + SSmove_manager.stop_looping(src) setDir(dir) var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src) animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3) SLEEP_CHECK_DEATH(delay) revving_charge = FALSE var/movespeed = 0.7 - walk_towards(src, T, movespeed) + SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed) - walk(src, 0) // cancel the movement + SSmove_manager.stop_looping(src) // cancel the movement try_bloodattack() charging = FALSE @@ -543,19 +543,24 @@ Difficulty: Hard playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE) return ..() -/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) - if(charging) - if(isturf(A) || isobj(A) && A.density) - A.ex_act(EXPLODE_HEAVY) - if(isliving(A)) - var/mob/living/L = A - L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!") - forceMove(get_turf(L)) - L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE) - playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE) - shake_camera(L, 4, 3) - shake_camera(src, 2, 3) - ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.) || !charging) + return . + if(isturf(bumped_atom) || (isobj(bumped_atom) && bumped_atom.density)) + bumped_atom.ex_act(EXPLODE_HEAVY) + if(!isliving(bumped_atom)) + return . + var/mob/living/bumped_living = bumped_atom + var/turf/living_turf = get_turf(bumped_living) + bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!") + forceMove(living_turf) + bumped_living.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE) + playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE) + shake_camera(bumped_living, 4, 3) + shake_camera(src, 2, 3) + /obj/effect/temp_visual/dragon_swoop/bubblegum duration = 15 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 6ca4a790115..637e764d456 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -429,7 +429,7 @@ Difficulty: Medium if(!swooping) ..() -/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /obj/effect/temp_visual/lava_warning diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index ae38a96663e..3ddd21446a1 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -51,7 +51,7 @@ Difficulty: Medium enraged_loot = /obj/item/disk/fauna_research/legion vision_range = 13 elimination = 1 - appearance_flags = PIXEL_SCALE + appearance_flags = PIXEL_SCALE|LONG_GLIDE mouse_opacity = MOUSE_OPACITY_ICON stat_attack = UNCONSCIOUS // Overriden from /tg/ - otherwise Legion starts chasing its minions @@ -209,7 +209,7 @@ Difficulty: Medium var/armor = M.run_armor_check(limb_to_hit, LASER) M.apply_damage(70 - ((health / maxHealth) * 20), BURN, limb_to_hit, armor) -/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index a460b45e7dd..6dae8efdbeb 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -139,7 +139,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa return -/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc) +/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(!newloc) return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm index cf87927bf94..2953654d9b9 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm @@ -34,7 +34,6 @@ loot = list(/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER}, /obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER}) tts_seed = "Antimage" - needs_gliding = FALSE /obj/item/projectile/temp/basilisk name = "freezing blast" diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm index bd18612953a..c0b348f084c 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm @@ -43,7 +43,6 @@ food_type = list(/obj/item/reagent_containers/food/snacks/meat, /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit, /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_leaf) tame_chance = 0 bonus_tame_chance = 10 - needs_gliding = FALSE /mob/living/simple_animal/hostile/asteroid/goliath/bullet_act(var/obj/item/projectile/P) @@ -135,26 +134,30 @@ return charging = TRUE revving_charge = TRUE - walk(src, 0) + SSmove_manager.stop_looping(src) setDir(dir) var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src) animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3) SLEEP_CHECK_DEATH(delay) revving_charge = FALSE var/movespeed = 0.7 - walk_towards(src, T, movespeed) + SSmove_manager.move_towards(src, T, movespeed) SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed) - walk(src, 0) // cancel the movement + SSmove_manager.stop_looping(src) // cancel the movement charging = FALSE -/mob/living/simple_animal/hostile/asteroid/goliath/beast/Bump(atom/A) - if(isturf(A) && charging) - wall_slam(A) + +/mob/living/simple_animal/hostile/asteroid/goliath/beast/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.) || !charging || !isturf(bumped_atom)) + return . + wall_slam(bumped_atom) + /mob/living/simple_animal/hostile/asteroid/goliath/beast/proc/wall_slam(atom/A) charging = FALSE Stun(100, TRUE, TRUE) - walk(src, 0) // Cancel the movement + SSmove_manager.stop_looping(src) // Cancel the movement if(ismineralturf(A)) var/turf/simulated/mineral/M = A if(M.mineralAmt < 7) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index fce7971b725..7cd7f4df91b 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -32,7 +32,7 @@ pass_flags = PASSTABLE butcher_results = list(/obj/item/organ/internal/regenerative_core = 1) var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood - needs_gliding = FALSE + /mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target) if(world.time >= ranged_cooldown) @@ -88,7 +88,6 @@ pass_flags = PASSTABLE | PASSMOB density = FALSE del_on_death = 1 - needs_gliding = FALSE var/life_time = 10 SECONDS diff --git a/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm b/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm index 6736bc928bc..5842205e900 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm @@ -34,7 +34,7 @@ var/melee_damage_upper_angery1 = 20 var/anger_move_to_delay = 8 var/anger_speed = 4 - needs_gliding = FALSE + /mob/living/simple_animal/hostile/asteroid/marrowweaver/adjustHealth(amount, updating_health = TRUE) if(buttmad == 0) @@ -47,7 +47,6 @@ set_varspeed(anger_speed) poison_type = "venom" poison_per_bite = 6 - needs_gliding = TRUE else if(buttmad == 1) if(health > maxHealth/2) buttmad = 0 @@ -57,7 +56,6 @@ poison_type = initial(poison_type) set_varspeed(initial(speed)) poison_per_bite = initial(poison_per_bite) - needs_gliding = FALSE ..() /mob/living/simple_animal/hostile/asteroid/marrowweaver/AttackingTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index ce1c6025c34..4d6924df255 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -30,18 +30,22 @@ deathmessage = "suddenly breaks apart." del_on_death = 1 var/passive_mode = TRUE // if true, don't target anything. + var/datum/effect_system/trail_follow/ion/ion_trail + /mob/living/simple_animal/hostile/malf_drone/Initialize(mapload) . = ..() - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail)) - update_icons() + ion_trail = new + ion_trail.set_up(src) + ion_trail.start() + + +/mob/living/simple_animal/hostile/malf_drone/Destroy() + QDEL_NULL(ion_trail) + return ..() -/mob/living/simple_animal/hostile/malf_drone/proc/create_trail(datum/source, atom/oldloc, _dir, forced) - var/turf/T = get_turf(oldloc) - if(!T.has_gravity()) - new /obj/effect/particle_effect/ion_trails(T, _dir) -/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/malf_drone/ListTargets() diff --git a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm index cdd4f0faa65..99d371cf5ce 100644 --- a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm +++ b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm @@ -57,7 +57,7 @@ ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) -/mob/living/simple_animal/hostile/spaceWorm/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/spaceWorm/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //space worms can flyyyyyy //Worm Head, Controls the AI for the entire worm "entity" @@ -130,8 +130,11 @@ attemptToEat(target) //Attempt to eat things we bump into, Mobs, Walls, Clowns -/mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/obstacle) - attemptToEat(obstacle) +/mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(isnull(.)) + return . + attemptToEat(bumped_atom) //Attempt to eat things, only the head can eat /mob/living/simple_animal/hostile/spaceWorm/wormHead/proc/attemptToEat(var/atom/noms) diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index 339f55d0d15..f57731bca53 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -64,8 +64,8 @@ if(creator) src.creator = creator -/mob/living/simple_animal/hostile/statue/Move(turf/NewLoc) - if(can_be_seen(NewLoc)) +/mob/living/simple_animal/hostile/statue/Move(atom/newloc, direct = NONE, glide_size_override = 0) + if(can_be_seen(newloc)) if(client) to_chat(src, "You cannot move, there are eyes on you!") return 0 diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index d79affe5d37..efa729cfdcf 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -300,9 +300,9 @@ wander = 0 alert_on_spacing = FALSE -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = NONE) - return TRUE +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + return TRUE /mob/living/simple_animal/hostile/syndicate/melee/space @@ -314,7 +314,7 @@ speed = 1 loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy/syndie) -/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/syndicate/ranged @@ -337,7 +337,7 @@ speed = 1 loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) -/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/syndicate/ranged/space/autogib diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index 30a85d4bb4f..ce25ef42af5 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -253,7 +253,7 @@ visible_message("[src] begins to secrete a sticky substance around [cocoon_target].") playsound(src.loc, 'sound/creatures/terrorspiders/wrap.ogg', 120, 1) stop_automated_movement = 1 - walk(src,0) + SSmove_manager.stop_looping(src) if(do_after(src, 4 SECONDS, cocoon_target.loc)) if(busy == SPINNING_COCOON) if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src,cocoon_target) <= 1) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm index 26114c80803..f89c854de08 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm @@ -56,7 +56,7 @@ visible_message("[src] buries its long fangs deep into the [inject_target] of [target]!") return TRUE -/mob/living/simple_animal/hostile/poison/terror_spider/builder/Move(atom/newloc, dir, step_x, step_y) //moves slow while not in web, but fast while in. does not regenerate if not in web +/mob/living/simple_animal/hostile/poison/terror_spider/builder/Move(atom/newloc, direct = NONE, glide_size_override = 0) //moves slow while not in web, but fast while in. does not regenerate if not in web . = ..() var/obj/structure/spider/terrorweb/W = locate() in get_turf(src) if(W) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm index 7bdab378ddf..2065688439f 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm @@ -31,7 +31,7 @@ tts_seed = "Cassiopeia" var/prob_ai_massweb = 10 -/mob/living/simple_animal/hostile/poison/terror_spider/lurker/Move(atom/newloc, dir, step_x, step_y) +/mob/living/simple_animal/hostile/poison/terror_spider/lurker/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(stat == DEAD) icon_state = icon_dead 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 5f99f1dd409..7047e60aa4d 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 @@ -32,11 +32,6 @@ GLOB.ts_spiderling_list -= src return ..() -/obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O) - if(istype(O, /obj/structure/table)) - forceMove(O.loc) - . = ..() - /obj/structure/spider/spiderling/terror_spiderling/Destroy() for(var/obj/structure/spider/spiderling/terror_spiderling/S in view(7, src)) @@ -151,8 +146,7 @@ new_area.Entered(src) else frustration++ - glide_for(3) - walk_to(src, entry_vent, 1) + SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4)) if(frustration > 2) entry_vent = null else if(prob(33)) @@ -167,8 +161,7 @@ for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) if(!v.welded) entry_vent = v - glide_for(3) - walk_to(src, entry_vent, 1) + SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4)) break 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 8d2923d77a4..e8bb14778f7 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 @@ -388,12 +388,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) return TRUE -/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup() +/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup(moving_direction, continuous_move) . = ..() // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. if(!.) - for(var/obj/structure/spider/S in range(1, get_turf(src))) - return S + for(var/obj/structure/spider/spider_thing in range(1, get_turf(src))) + return spider_thing + /mob/living/simple_animal/hostile/poison/terror_spider/Stat() ..() diff --git a/code/modules/mob/living/simple_animal/hostile/undead.dm b/code/modules/mob/living/simple_animal/hostile/undead.dm index 977036192d3..757c6afc5d8 100644 --- a/code/modules/mob/living/simple_animal/hostile/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/undead.dm @@ -59,7 +59,7 @@ AddElement(/datum/element/simple_flying) -/mob/living/simple_animal/hostile/ghost/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/hostile/ghost/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/ghost/Life(seconds, times_fired) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index f1001edbb48..1aabc969c1e 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -136,7 +136,7 @@ if(held_item) custom_emote(EMOTE_VISIBLE, "lets go of [held_item.name]!") drop_held_item() - walk(src, 0) + SSmove_manager.stop_looping(src) return ..() @@ -409,7 +409,7 @@ //-----WANDERING - This is basically a 'I dont know what to do yet' state else if(parrot_state == PARROT_WANDER) //Stop movement, we'll set it later - walk(src, 0) + SSmove_manager.stop_looping(src) parrot_interest = null //Wander around aimlessly. This will help keep the loops from searches down @@ -449,7 +449,7 @@ //-----STEALING else if(parrot_state == (PARROT_SWOOP|PARROT_STEAL)) - walk(src, 0) + SSmove_manager.stop_looping(src) if(!parrot_interest || held_item || !(parrot_interest in view(src))) parrot_state = PARROT_SWOOP|PARROT_RETURN @@ -476,13 +476,12 @@ parrot_state = PARROT_SWOOP|PARROT_RETURN return - glide_for(parrot_speed) - walk_to(src, path_to_take[2], 0, parrot_speed) + SSmove_manager.move_to(src, path_to_take[2], 0, parrot_speed) return //-----RETURNING TO PERCH else if(parrot_state == (PARROT_SWOOP|PARROT_RETURN)) - walk(src, 0) + SSmove_manager.stop_looping(src) if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. parrot_perch = null @@ -502,20 +501,19 @@ parrot_state = PARROT_WANDER return - glide_for(parrot_speed) - walk_to(src, path_to_take[2], 0, parrot_speed) + SSmove_manager.move_to(src, path_to_take[2], 0, parrot_speed) return //-----FLEEING else if(parrot_state == (PARROT_SWOOP|PARROT_FLEE)) - walk(src, 0) + SSmove_manager.stop_looping(src) if(!parrot_interest || !isliving(parrot_interest) || !Adjacent(parrot_interest)) //Sanity parrot_state = PARROT_WANDER parrot_interest = null return - walk_away(src, parrot_interest, 0, parrot_speed - parrot_been_shot) + SSmove_manager.move_away(src, parrot_interest, 0, parrot_speed - parrot_been_shot) parrot_been_shot-- return @@ -561,12 +559,11 @@ //Otherwise, fly towards the mob! else // No pathfinding here because the parrot is pissed and isn't thinking rationally. - glide_for(parrot_speed) - walk_to(src, parrot_interest, 1, parrot_speed) + SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed) return //-----STATE MISHAP else //This should not happen. If it does lets reset everything and try again - walk(src, 0) + SSmove_manager.stop_looping(src) parrot_interest = null parrot_perch = null drop_held_item() diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 6aedb15f9e6..83246dc8561 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -48,7 +48,7 @@ icon_state = holy ? "shade_angelic" : "shade" -/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 885475dc4aa..0117f88d016 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -222,22 +222,34 @@ WakeUp() ..() + /mob/living/simple_animal/proc/handle_automated_action() set waitfor = FALSE return + /mob/living/simple_animal/proc/handle_automated_movement() set waitfor = FALSE - if(!stop_automated_movement && wander) - if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled - var/anydir = pick(GLOB.cardinal) - if(Process_Spacemove(anydir)) - Move(get_step(src,anydir), anydir, cached_multiplicative_slowdown) - turns_since_move = 0 - return 1 + if(stop_automated_movement || !wander) + return + if(!isturf(loc) && !allow_movement_on_non_turfs) + return + //if(!(mobility_flags & MOBILITY_MOVE)) + // return TRUE + if(resting || buckled || !canmove) + return TRUE + + turns_since_move++ + if(turns_since_move < turns_per_move) + return TRUE + if(stop_automated_movement_when_pulled && pulledby) //Some animals don't move when pulled + return TRUE + var/anydir = pick(GLOB.cardinal) + if(Process_Spacemove(anydir)) + Move(get_step(src, anydir), anydir) + turns_since_move = 0 + return TRUE + /mob/living/simple_animal/proc/handle_automated_speech(override) set waitfor = FALSE @@ -591,7 +603,7 @@ else canmove = TRUE if(!canmove) - walk(src, 0) //stop mid walk + SSmove_manager.stop_looping(src) //stop mid walk update_transform() if(!delay_action_updates) @@ -702,7 +714,7 @@ /mob/living/simple_animal/Login() ..() - walk(src, 0) // if mob is moving under ai control, then stop AI movement + SSmove_manager.stop_looping(src) // if mob is moving under ai control, then stop AI movement /mob/living/simple_animal/say(message, verb = "says", sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE) @@ -752,3 +764,12 @@ /mob/living/simple_animal/proc/pull_constraint(atom/movable/AM, show_message = FALSE) return TRUE + + +/mob/living/simple_animal/update_movespeed() + . = ..() + if(cached_multiplicative_slowdown > END_GLIDE_SPEED) + ADD_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT) + else + REMOVE_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT) + diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index de76e75debf..b3d61d501a3 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -238,7 +238,7 @@ Atkcool = TRUE addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS) -/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = NONE) +/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE /mob/living/simple_animal/slime/Stat() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 02758db4dc5..acc9148f99b 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -1,8 +1,7 @@ /mob density = TRUE layer = MOB_LAYER - glide_size = 1.5 - animate_movement = 2 + animate_movement = SLIDE_STEPS pressure_resistance = 8 throwforce = 10 dont_save = TRUE //to avoid it messing up in buildmode saving diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index de0d4f2020d..e3fb65568e5 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -1,36 +1,55 @@ -/client/verb/toggle_throw_mode() - set hidden = 1 - if(iscarbon(mob)) - var/mob/living/carbon/C = mob - C.toggle_throw_mode() - else - to_chat(usr, "Это существо не может бросать предметы.") - -/client/proc/Move_object(direct) - if(mob && mob.control_object) - if(mob.control_object.density) - step(mob.control_object, direct) - if(!mob.control_object) - return - mob.control_object.setDir(direct) - else - mob.control_object.forceMove(get_step(mob.control_object, direct)) - return - - -/client/Move(n, direct) - if(world.time < move_delay) - return +/** + * Move a client in a direction + * + * Huge proc, has a lot of functionality + * + * Mostly it will despatch to the mob that you are the owner of to actually move + * in the physical realm + * + * Things that stop you moving as a mob: + * * world time being less than your next move_delay + * * not being in a mob, or that mob not having a loc + * * missing the new_loc and direct parameters + * * having TRAIT_NO_TRANSFORM + * * being in remote control of an object (calls Move_object instead) + * * being dead (it ghosts you instead) + * + * Things that stop you moving as a mob living (why even have OO if you're just shoving it all + * in the parent proc with istype checks right?): + * * having incorporeal_move set (calls Process_Incorpmove() instead) + * * being in remote control of a movable, (calls remote_control() instead) + * * being grabbed + * * being buckled (relaymove() is called to the buckled atom instead) + * * having your loc be some other mob (relaymove() is called on that mob instead) + * * Not having MOBILITY_MOVE + * * Failing Process_Spacemove() call + * + * At this point, if the mob is is confused, then a random direction and target turf will be calculated for you to travel to instead + * + * Now the parent call is made (to the byond builtin move), which moves you + * + * Some final move delay calculations (doubling if you moved diagonally successfully) + * + * if mob throwing is set I believe it's unset at this point via a call to finalize + * + * Finally if you're pulling an object and it's dense, you are turned 180 after the move + * (if you ask me, this should be at the top of the move so you don't dance around) // LATER + * + */ +/client/Move(new_loc, direct) + if(world.time < move_delay) //do not move anything ahead of this check please + return FALSE input_data.desired_move_dir_add = NONE input_data.desired_move_dir_sub = NONE var/old_move_delay = move_delay 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(!n || !direct) // why did we never check this before? + if(!direct || !new_loc) + return FALSE + + if(!mob?.loc) return FALSE if(HAS_TRAIT(mob, TRAIT_NO_TRANSFORM)) @@ -40,19 +59,17 @@ return Move_object(direct) if(!isliving(mob)) - return mob.Move(n, direct) + return mob.Move(new_loc, direct) if(mob.stat == DEAD) mob.ghostize() - return 0 + return FALSE - if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, n, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE) + if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, new_loc, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE) return FALSE - var/mob/living/L = mob //Already checked for isliving earlier - if(L.incorporeal_move)//Move though walls - move_delay = world.time + 0.5 // cap to 20fps - L.glide_size = 8 + var/mob/living/living_mob = mob //Already checked for isliving earlier + if(living_mob.incorporeal_move)//Move though walls Process_Incorpmove(direct) return FALSE @@ -61,33 +78,31 @@ if(isAI(mob)) if(istype(mob.loc, /obj/item/aicard)) - var/obj/O = mob.loc - return O.relaymove(mob, direct) // aicards have special relaymove stuff - return AIMove(n, direct, mob) + return mob.loc.relaymove(mob, direct) // aicards have special relaymove stuff + return AIMove(new_loc, direct, mob) if(Process_Grab()) - return + return FALSE if(mob.buckled) //if we're buckled to something, tell it we moved. return mob.buckled.relaymove(mob, direct) if(!mob.canmove) - return + return FALSE if(!mob.lastarea) mob.lastarea = get_area(mob.loc) - 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(ismovable(mob.loc)) //Inside an object, tell it we moved + return mob.loc.relaymove(mob, direct) if(!mob.Process_Spacemove(direct)) - return 0 + return FALSE if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_MOVE, args) & COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE) return FALSE - if(HAS_TRAIT(mob, TRAIT_RESTRAINED) && mob.pulledby) // Why being pulled while cuffed prevents you from moving + if(mob.pulledby && HAS_TRAIT(mob, TRAIT_RESTRAINED)) // Why being pulled while cuffed prevents you from moving var/mob/puller = mob.pulledby if(!puller.incapacitated() && !HAS_TRAIT(puller, TRAIT_HANDS_BLOCKED) && mob.Adjacent(puller)) to_chat(src, span_warning("Вы скованы и не можете пошевелиться!")) @@ -96,44 +111,70 @@ puller.stop_pulling() //We are now going to move - current_move_delay = mob.cached_multiplicative_slowdown + var/add_delay = mob.cached_multiplicative_slowdown - if(!istype(get_turf(mob), /turf/space) && mob.pulling) - var/mob/living/M = mob - var/mob/living/silicon/robot/R = mob - if(!(STRONG in M.mutations) && !isconstruct(M) && !istype(M, /mob/living/simple_animal/hostile/clockwork) && !istype(M, /mob/living/simple_animal/hostile/guardian) && !(istype(R) && (/obj/item/borg/upgrade/vtec in R.upgrades))) //No slowdown for STRONG gene //Blood cult constructs //Clockwork constructs //Borgs with VTEC //Holopigs - current_move_delay *= min(1.4, mob.pulling.get_pull_push_speed_modifier(current_move_delay)) + if(mob.pulling && mob.has_gravity()) + var/mob/living/silicon/robot/robot = mob + if(!(STRONG in living_mob.mutations) && !isconstruct(living_mob) && !istype(living_mob, /mob/living/simple_animal/hostile/clockwork) && !istype(living_mob, /mob/living/simple_animal/hostile/guardian) && !(isrobot(mob) && (/obj/item/borg/upgrade/vtec in robot.upgrades))) //No slowdown for STRONG gene //Blood cult constructs //Clockwork constructs //Borgs with VTEC //Holopigs + add_delay *= min(1.4, mob.pulling.get_pull_push_speed_modifier(add_delay)) + + if(locate(/obj/item/grab, mob)) + add_delay += 7 + var/new_glide_size = DELAY_TO_GLIDE_SIZE(add_delay * ((NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? sqrt(2) : 1)) + mob.set_glide_size(new_glide_size) // set it now in case of pulled objects + + //If the move was recent, count using old_move_delay + //We want fractional behavior and all if(old_move_delay + world.tick_lag > world.time) + //Yes this makes smooth movement stutter if add_delay is too fractional + //Yes this is better then the alternative move_delay = old_move_delay else move_delay = world.time - mob.last_movement = world.time - if(locate(/obj/item/grab, mob)) - current_move_delay += 7 + //Basically an optional override for our glide size + //Sometimes you want to look like you're moving with a delay you don't actually have yet + visual_delay = 0 + var/old_dir = mob.dir - . = mob.SelfMove(n, direct, current_move_delay) - mob.setDir(direct) + . = ..() - if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully - current_move_delay *= 1.41 //Will prevent mob diagonal moves from smoothing accurately, sadly + if(mob.loc == new_loc) + mob.last_movement = world.time + if(ISDIAGONALDIR(direct)) //moved diagonally successfully + add_delay *= sqrt(2) - move_delay += current_move_delay + var/after_glide = 0 + if(visual_delay) + after_glide = visual_delay + else + after_glide = DELAY_TO_GLIDE_SIZE(add_delay) - if(mob.pulledby) - mob.pulledby.stop_pulling() + mob.set_glide_size(after_glide) - if(mob && .) - if(mob.throwing) - mob.throwing.finalize() + move_delay += add_delay - for(var/obj/O in mob) - O.on_mob_move(direct, mob) + if(.) // If mob is null here, we deserve the runtime + mob.throwing?.finalize() + // At this point we've moved the client's attached mob. This is one of the only ways to guess that a move was done + // as a result of player input and not because they were pulled or any other magic. + SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVED, direct, old_dir) + + for(var/obj/object in mob.contents) + object.on_mob_move(direct, mob) + + +/client/proc/Move_object(direct) + if(mob.control_object.density) + 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/proc/SelfMove(turf/n, direct, movetime) - return Move(n, direct, movetime) ///Process_Grab() ///Called by client/Move() @@ -176,9 +217,19 @@ return FALSE -///Process_Incorpmove -///Called by client/Move() -///Allows mobs to run though walls +/** + * Allows mobs to ignore density and phase through objects + * + * Called by client/Move() + * + * The behaviour depends on the incorporeal_move value of the mob + * + * * INCORPOREAL_MOVE_BASIC - forceMoved to the next tile with no stop + * * INCORPOREAL_NINJA - the same but leaves a cool effect path + * * INCORPOREAL_REVENANT - the same but blocked by holy tiles + * + * You'll note this is another mob living level proc living at the client level + */ /client/proc/Process_Incorpmove(direct) var/turf/mobloc = get_turf(mob) if(!isliving(mob)) @@ -186,8 +237,10 @@ var/mob/living/L = mob switch(L.incorporeal_move) if(INCORPOREAL_NORMAL) - L.forceMove(get_step(L, direct)) - L.dir = direct + var/T = get_step(L, direct) + if(T) + L.forceMove(T) + L.setDir(direct) if(INCORPOREAL_NINJA) if(prob(50)) var/locx @@ -215,30 +268,31 @@ return else return - L.glide_size = L.glide_size * 2 - L.forceMove(locate(locx,locy,mobloc.z)) - spawn(0) + var/target = locate(locx,locy,mobloc.z) + if(target) + L.forceMove(target) var/limit = 2//For only two trailing shadows. - for(var/turf/T as anything in get_line(mobloc, L.loc)) + for(var/turf/T in get_line(mobloc, L.loc)) new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir) limit-- - if(limit<=0) + if(limit <= 0) break else new /obj/effect/temp_visual/dir_setting/ninja/shadow(mobloc, L.dir) - L.forceMove(get_step(L, direct)) - L.dir = direct + var/T = get_step(L, direct) + if(T) + L.forceMove(T) + L.setDir(direct) if(INCORPOREAL_REVENANT) //Incorporeal move, but blocked by holy-watered tiles var/turf/simulated/floor/stepTurf = get_step(L, direct) - if(stepTurf.flags & NOJAUNT) - to_chat(L, span_warning("Святые силы блокируют ваш путь.")) - ADD_TRAIT(L, TRAIT_NO_TRANSFORM, INCORPOREAL_TRAIT) - spawn(2) - REMOVE_TRAIT(L, TRAIT_NO_TRANSFORM, INCORPOREAL_TRAIT) - else - L.forceMove(get_step(L, direct)) - L.dir = direct - return 1 + if(stepTurf) + if(stepTurf.flags & NOJAUNT) + move_delay += 0.5 SECONDS + to_chat(L, span_warning("Святые силы блокируют Ваш путь.")) + return + L.forceMove(stepTurf) + L.setDir(direct) + return TRUE /** @@ -250,7 +304,7 @@ * * You can move in space if you have a spacewalk ability */ -/mob/Process_Spacemove(movement_dir = NONE) +/mob/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) . = ..() if(.) return . @@ -258,25 +312,25 @@ if(buckled) return TRUE - var/atom/movable/backup = get_spacemove_backup(movement_dir) + var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move) if(!backup) return FALSE - if(!istype(backup) || !movement_dir || backup.anchored) + if(continuous_move || !istype(backup) || !movement_dir || backup.anchored) return TRUE // last pushoff exists for one reason // to ensure pushing a mob doesn't just lead to it considering us as backup, and failing last_pushoff = world.time - if(backup.newtonian_move(REVERSE_DIR(movement_dir))) //You're pushing off something movable, so it moves + if(backup.newtonian_move(REVERSE_DIR(movement_dir), instant = TRUE)) //You're pushing off something movable, so it moves // We set it down here so future calls to Process_Spacemove by the same pair in the same tick don't lead to fucky backup.last_pushoff = world.time - to_chat(src, span_info("Вы отталкиваетесь от [backup] для продолжения движения.")) + to_chat(src, span_info("Вы отталкиваетесь от [backup.name] для продолжения движения.")) return TRUE -/mob/get_spacemove_backup(moving_direction) +/mob/get_spacemove_backup(moving_direction, continuous_move) for(var/atom/pushover as anything in range(1, get_turf(src))) if(pushover == src) continue @@ -305,7 +359,6 @@ //Sometime this tick, this pushed off something. Doesn't count as a valid pushoff target if(rebound.last_pushoff == world.time) continue - /* if(continuous_move && !pass_allowed) var/datum/move_loop/move/rebound_engine = SSmove_manager.processing_on(rebound, SSspacedrift) // If you're moving toward it and you're both going the same direction, stop @@ -314,7 +367,6 @@ else if(!pass_allowed) if(moving_direction == get_dir(src, pushover)) // Can't push "off" of something that you're walking into continue - */ if(rebound.anchored) return rebound if(pulling == rebound) @@ -497,6 +549,15 @@ mob.toggle_move_intent() +/client/verb/toggle_throw_mode() + set hidden = 1 + if(iscarbon(mob)) + var/mob/living/carbon/C = mob + C.toggle_throw_mode() + else + to_chat(usr, "Это существо не может бросать предметы.") + + /mob/proc/toggle_move_intent() return diff --git a/code/modules/movespeed/_movespeed_modifier.dm b/code/modules/movespeed/_movespeed_modifier.dm index f6df8f3d2b6..b9337be13ae 100644 --- a/code/modules/movespeed/_movespeed_modifier.dm +++ b/code/modules/movespeed/_movespeed_modifier.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) return TRUE remove_movespeed_modifier(existing, update = FALSE) if(length(movespeed_modification)) - BINARY_INSERT_TG(type_or_datum.id, movespeed_modification, /datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE) + BINARY_INSERT(type_or_datum.id, movespeed_modification, /datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE) LAZYSET(movespeed_modification, type_or_datum.id, type_or_datum) if(update) update_movespeed() diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index 66f0b98548c..2db20952c29 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -87,14 +87,15 @@ var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. -/obj/machinery/field/Bumped(atom/movable/mover) +/obj/machinery/field/Bumped(atom/movable/moving_atom) + . = ..() if(hasShocked) - return - if(isliving(mover)) - shock_field(mover) - return - if(ismachinery(mover) || isstructure(mover) || ismecha(mover)) - bump_field(mover) + return . + if(isliving(moving_atom)) + shock_field(moving_atom) + return . + if(ismachinery(moving_atom) || isstructure(moving_atom) || ismecha(moving_atom)) + bump_field(moving_atom) /obj/machinery/field/CanAllowThrough(atom/movable/mover, border_dir) diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 0cc39a63f1f..9d52cf929b5 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -77,13 +77,17 @@ mezzer() -/obj/singularity/narsie/Bump(atom/A)//you dare stand before a god?! - godsmack(A) - return +/obj/singularity/narsie/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE)//you dare stand before a god?! + . = ..() + if(. || isnull(.)) + return . + godsmack(bumped_atom) + -/obj/singularity/narsie/Bumped(atom/movable/moving_atom) +/obj/singularity/narsie/Bumped(atom/movable/moving_atom, effect_applied = TRUE) + . = ..() godsmack(moving_atom) - return + /obj/singularity/narsie/proc/godsmack(atom/A) if(istype(A,/obj/)) diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index d37a4f8f42d..b6c535e035c 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -38,10 +38,12 @@ B.take_damage(energy * 0.6) movement_range = 0 -/obj/effect/accelerated_particle/Bump(obj/singularity/S) - if(!istype(S)) - return ..() - S.energy += energy + +/obj/effect/accelerated_particle/Bump(obj/singularity/bumped_singulo, custom_bump) + . = ..() + if(. || isnull(.) || !istype(bumped_singulo)) + return . + bumped_singulo.energy += energy /obj/effect/accelerated_particle/ex_act(severity) diff --git a/code/modules/power/singularity/ratvar.dm b/code/modules/power/singularity/ratvar.dm index 0b4d442ff73..9ddd98f01f8 100644 --- a/code/modules/power/singularity/ratvar.dm +++ b/code/modules/power/singularity/ratvar.dm @@ -64,13 +64,17 @@ mezzer() -/obj/singularity/ratvar/Bump(atom/A)//you dare stand before a god?! - godsmack(A) - return +/obj/singularity/ratvar/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE)//you dare stand before a god?! + . = ..() + if(. || isnull(.)) + return . + godsmack(bumped_atom) + -/obj/singularity/ratvar/Bumped(atom/movable/moving_atom) +/obj/singularity/ratvar/Bumped(atom/movable/moving_atom, effect_applied = TRUE) + . = ..() godsmack(moving_atom) - return + /obj/singularity/ratvar/proc/godsmack(atom/A) if(istype(A,/obj/)) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index b657aebee69..c887e05391c 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -7,7 +7,7 @@ density = TRUE layer = MASSIVE_OBJ_LAYER light_range = 6 - appearance_flags = 0 + appearance_flags = LONG_GLIDE var/current_size = 1 var/allowed_size = 1 var/contained = 1 //Are we going to move around? @@ -53,7 +53,7 @@ target = null return ..() -/obj/singularity/Move(atom/newloc, direct) +/obj/singularity/Move(atom/newloc, direct = NONE, glide_size_override = 0) if(current_size >= STAGE_FIVE || check_turfs_in(direct)) last_failed_movement = 0//Reset this because we moved return ..() @@ -76,7 +76,7 @@ consume(user) return 1 -/obj/singularity/Process_Spacemove(movement_dir = NONE) //The singularity stops drifting for no man! +/obj/singularity/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) //The singularity stops drifting for no man! return FALSE /obj/singularity/blob_act(obj/structure/blob/B) @@ -103,14 +103,17 @@ return 0 //Will there be an impact? Who knows. Will we see it? No. -/obj/singularity/Bump(atom/A) - consume(A) - return +/obj/singularity/Bump(atom/bumped_atom, custom_bump, effect_applied = FALSE) + . = ..() + if(. || isnull(.) || effect_applied) + return . + consume(bumped_atom) -/obj/singularity/Bumped(atom/movable/moving_atom) - consume(moving_atom) - return +/obj/singularity/Bumped(atom/movable/moving_atom, effect_applied = FALSE) + . = ..() + if(!effect_applied) + consume(moving_atom) /obj/singularity/process() diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 9f9672ab983..2f8070fc660 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -451,9 +451,10 @@ user.apply_effect(150, IRRADIATE) /obj/machinery/power/supermatter_shard/Bumped(atom/movable/moving_atom) + . = ..() if(isnucleation(moving_atom)) nuclear_touch(moving_atom) - return + return . if(isliving(moving_atom)) moving_atom.visible_message("\The [moving_atom] slams into \the [src] inducing a resonance... [moving_atom.p_their(TRUE)] body starts to glow and catch flame before flashing into ash.",\ "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 3654c1c5b0d..ca61c3a1472 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -111,12 +111,18 @@ EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36)) -/obj/singularity/energy_ball/Bump(atom/A) - dust_mobs(A) +/obj/singularity/energy_ball/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE) + . = ..() + if(. || isnull(.)) + return . + dust_mobs(bumped_atom) + -/obj/singularity/energy_ball/Bumped(atom/movable/moving_atom) +/obj/singularity/energy_ball/Bumped(atom/movable/moving_atom, effect_applied = TRUE) + . = ..() dust_mobs(moving_atom) + /obj/singularity/energy_ball/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index c6a0c8dd55d..402bc99119d 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/weapons/projectile.dmi' icon_state = "detective" item_state = "gun" - appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER + appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE flags = CONDUCT slot_flags = ITEM_SLOT_BELT materials = list(MAT_METAL=2000) diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 18ea045c42d..cd0c7327be2 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -53,8 +53,8 @@ update_icon() return 1 -/obj/item/gun/projectile/automatic/ui_action_click(var/owner, var/action_type) - if (ispath(action_type, /datum/action/item_action/toggle_firemode)) +/obj/item/gun/projectile/automatic/ui_action_click(mob/user, action, leftclick) + if(istype(action, /datum/action/item_action/toggle_firemode)) burst_select() return TRUE @@ -149,10 +149,10 @@ icon_state = "wt550[magazine ? "-[CEILING(get_ammo(FALSE)/4, 1)*4]" : ""]" -/obj/item/gun/projectile/automatic/wt550/ui_action_click(owner, action_type) +/obj/item/gun/projectile/automatic/wt550/ui_action_click(mob/user, action, leftclick) if(..()) return TRUE - if(action_type == /datum/action/item_action/toggle_gunlight) + if(istype(action, /datum/action/item_action/toggle_gunlight)) toggle_gunlight() return TRUE @@ -179,10 +179,10 @@ item_state = "SP-91-RC[magazine ? "-[get_ammo(FALSE) ? "20" : "0"]" : ""]" -/obj/item/gun/projectile/automatic/sp91rc/ui_action_click(owner, action_type) +/obj/item/gun/projectile/automatic/sp91rc/ui_action_click(mob/user, action, leftclick) if(..()) return TRUE - if(action_type == /datum/action/item_action/toggle_gunlight) + if(istype(action, /datum/action/item_action/toggle_gunlight)) toggle_gunlight() return TRUE @@ -481,10 +481,10 @@ icon_state = "[initial(icon_state)][magazine ? "" : "-e"][suppressed ? "-suppressed" : ""]" -/obj/item/gun/projectile/automatic/sfg/ui_action_click(owner, action_type) +/obj/item/gun/projectile/automatic/sfg/ui_action_click(mob/user, action, leftclick) if(..()) return TRUE - if(action_type == /datum/action/item_action/toggle_gunlight) + if(istype(action, /datum/action/item_action/toggle_gunlight)) toggle_gunlight() return TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 1137037ca87..299419f3aa6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -240,55 +240,56 @@ return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume -/obj/item/projectile/Bump(atom/A, yes) - if(!yes) //prevents double bumps. - return FALSE +/obj/item/projectile/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.)) + return . - if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL)) + if(check_ricochet(bumped_atom) && check_ricochet_flag(bumped_atom) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL)) ricochets++ - if(A.handle_ricochet(src)) - on_ricochet(A) + if(bumped_atom.handle_ricochet(src)) + on_ricochet(bumped_atom) ignore_source_check = TRUE range = initial(range) return TRUE if(firer && !ignore_source_check) - if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech - loc = A.loc + if(bumped_atom == firer || (bumped_atom == firer.loc && ismecha(bumped_atom))) //cannot shoot yourself or your mech + loc = bumped_atom.loc return FALSE - var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. + var/turf/bumped_turf = get_turf(bumped_atom) + var/distance = get_dist(bumped_turf, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. if(!forced_accuracy) - if(get_dist(A, original) <= 1) + if(get_dist(bumped_atom, original) <= 1) def_zone = ran_zone(def_zone, max(100 - (7 * distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. else def_zone = pick(list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) // If we were aiming at one target but another one got hit, no accuracy is applied - if(isturf(A) && hitsound_wall) + if(isturf(bumped_atom) && hitsound_wall) var/volume = clamp(vol_by_damage() + 20, 0, 100) if(suppressed) volume = 5 playsound(loc, hitsound_wall, volume, 1, -1) - else if(ishuman(A)) - var/mob/living/carbon/human/H = A - var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) + else if(ishuman(bumped_atom)) + var/mob/living/carbon/human/bumped_human = bumped_atom + var/obj/item/organ/external/organ = bumped_human.get_organ(check_zone(def_zone)) if(isnull(organ)) return FALSE - var/turf/target_turf = get_turf(A) - prehit(A) - var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null + prehit(bumped_atom) + var/permutation = bumped_atom.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null if(permutation == -1 || forcedodge)// the bullet passes through a dense object! if(forcedodge > 0) forcedodge -= 1 - loc = target_turf - if(A) - LAZYADD(permutated, A) + loc = bumped_turf + if(bumped_atom) + LAZYADD(permutated, bumped_atom) return FALSE else - if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. + if(bumped_atom && bumped_atom.density && !ismob(bumped_atom) && !(bumped_atom.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. var/list/mobs_list = list() - for(var/mob/living/L in target_turf) - mobs_list += L + for(var/mob/living/mob in bumped_turf) + mobs_list += mob if(mobs_list.len) var/mob/living/picked_mob = pick(mobs_list) prehit(picked_mob) @@ -296,7 +297,7 @@ qdel(src) -/obj/item/projectile/Process_Spacemove(movement_dir = NONE) +/obj/item/projectile/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE //Bullets don't drift in space @@ -348,7 +349,7 @@ step_towards(src, T) if(original && (original.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER || ismob(original))) if(loc == get_turf(original) && !(original in permutated)) - Bump(original, TRUE) + Bump(original, custom_bump = TRUE) if(QDELETED(src)) //deleted on last move return if(!forcemoved) @@ -417,7 +418,7 @@ /obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. ..() if(isliving(AM) && AM.density && !(pass_flags & PASSMOB)) - Bump(AM, TRUE) + Bump(AM, custom_bump = TRUE) /obj/item/projectile/Destroy() diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 7e771e93831..f00078b8c52 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -43,19 +43,19 @@ var/turf/T3 = get_step(src,dir) var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it. if(L && L.stat != DEAD) - Bump(L) //Magic Bullet #teachthecontroversy + Bump(L, custom_bump = TRUE) //Magic Bullet #teachthecontroversy return L = locate(/mob/living) in T2 if(L && L.stat != DEAD) - Bump(L) + Bump(L, custom_bump = TRUE) return L = locate(/mob/living) in T3 if(L && L.stat != DEAD) - Bump(L) + Bump(L, custom_bump = TRUE) return ..() -/obj/item/projectile/magic/fireball/on_hit(var/target) +/obj/item/projectile/magic/fireball/on_hit(atom/target, blocked = 0, hit_zone) . = ..() var/turf/T = get_turf(target) explosion(T, exp_devastate, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire, cause = src) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 9454d62cc90..f9e0b32af8b 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -133,18 +133,17 @@ damage_type = BRUTE nodamage = TRUE flag = "bullet" + hitsound = 'sound/effects/meteorimpact.ogg' + + +/obj/item/projectile/meteor/on_hit(atom/target, blocked, hit_zone) + . = ..() + if(blocked >= 100) + return FALSE + for(var/mob/mob in urange(10, src)) + if(!mob.stat) + shake_camera(mob, 3, 1) -/obj/item/projectile/meteor/Bump(atom/A, yes) - if(yes) - return - if(A == firer) - loc = A.loc - return - playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in urange(10, src)) - if(!M.stat) - shake_camera(M, 3, 1) - qdel(src) /obj/item/projectile/energy/floramut name = "alpha somatoray" @@ -216,13 +215,16 @@ name = "snap-pop" icon = 'icons/obj/toy.dmi' icon_state = "snappop" + nodamage = TRUE + damage = 0 + -/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) +/obj/item/projectile/clown/bullet_act(obj/item/projectile/projectile, def_zone) do_sparks(3, 1, src) new /obj/effect/decal/cleanable/ash(loc) visible_message("The [name] explodes!","You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) + playsound(src, 'sound/effects/snap.ogg', 50, TRUE) + . = ..() /obj/item/projectile/beam/wormhole name = "bluespace beam" diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 785d64d8a40..4eed19fdcfe 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -195,10 +195,12 @@ if(rig) rig.hear_message(M, msg) -/obj/structure/reagent_dispensers/fueltank/Bump() - ..() - if(rig) - rig.process_movement() + +/obj/structure/reagent_dispensers/fueltank/Bump(atom/bumped_atom, custom_bump) + . = ..() + if(. || isnull(.) || !rig) + return . + rig.process_movement() /obj/structure/reagent_dispensers/peppertank diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 5bc413e0e32..8b953983937 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -623,24 +623,26 @@ return /obj/machinery/disposal/deliveryChute/Bumped(atom/movable/moving_atom) //Go straight into the chute - ..() - if(ismecha(moving_atom) || isspacepod(moving_atom)) return - - if(isprojectile(moving_atom) || iseffect(moving_atom)) - return + . = ..() + if(ismecha(moving_atom) || isspacepod(moving_atom) || isprojectile(moving_atom) || iseffect(moving_atom)) + return . switch(dir) if(NORTH) - if(moving_atom.loc.y != src.loc.y+1) return + if(moving_atom.loc.y != src.loc.y+1) + return if(EAST) - if(moving_atom.loc.x != src.loc.x+1) return + if(moving_atom.loc.x != src.loc.x+1) + return if(SOUTH) - if(moving_atom.loc.y != src.loc.y-1) return + if(moving_atom.loc.y != src.loc.y-1) + return if(WEST) - if(moving_atom.loc.x != src.loc.x-1) return + if(moving_atom.loc.x != src.loc.x-1) + return if(isobj(moving_atom) || isliving(moving_atom)) - moving_atom.loc = src + moving_atom.forceMove(src) if(mode != OFF) flush() diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm index 8af8b9e17c8..98fb35b98cc 100644 --- a/code/modules/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -203,8 +203,8 @@ var/obj/effect/sliding_puzzle/source var/icon/puzzle_icon -/obj/structure/puzzle_element/Move(nloc, dir) - if(!isturf(nloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1) +/obj/structure/puzzle_element/Move(atom/newloc, direct = NONE, glide_size_override = 0) + if(!isturf(newloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1) return 0 . = ..() diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm index 57ada89aa7d..b387b68d088 100644 --- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm @@ -5,7 +5,7 @@ icon = 'icons/effects/96x96.dmi' icon_state = "gate_full" flags = ON_BORDER - appearance_flags = 0 + appearance_flags = LONG_GLIDE layer = TABLE_LAYER anchored = TRUE density = TRUE @@ -207,7 +207,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) /obj/effect/temp_visual/necropolis icon = 'icons/effects/96x96.dmi' icon_state = "door_closing" - appearance_flags = 0 + appearance_flags = LONG_GLIDE duration = 6 layer = EDGED_TURF_LAYER pixel_x = -32 @@ -222,7 +222,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) desc = "A massive arch over the necropolis gate, set into a massive tower of stone." icon = 'icons/effects/160x160.dmi' icon_state = "arch_full" - appearance_flags = 0 + appearance_flags = LONG_GLIDE layer = TABLE_LAYER anchored = TRUE pixel_x = -64 diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index ec7c6128e2d..ebcbaf782ed 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -1,11 +1,12 @@ -#define DAMAGE 1 -#define FIRE_OLAY 2 -#define POD_LIGHT 1 -#define WINDOW 2 -#define RIM 3 -#define PAINT 4 -#define NO_GRAVITY_SPEED 1.5 -#define GRAVITY_SPEED 4 +#define DAMAGE 1 +#define FIRE_OLAY 2 +#define POD_LIGHT 1 +#define WINDOW 2 +#define RIM 3 +#define PAINT 4 + +#define NO_GRAVITY_SPEED (0.15 SECONDS) +#define GRAVITY_SPEED (0.4 SECONDS) /obj/item/pod_paint_bucket name = "space pod paintkit" @@ -51,10 +52,11 @@ var/list/pod_paint_effect var/list/colors = new/list(4) var/health = 250 - var/empcounter = 0 //Used for disabling movement when hit by an EMP var/lights = 0 var/lights_power = 6 + var/can_paint = TRUE + var/list/icon_light_color = list("pod_civ" = LIGHT_COLOR_WHITE, \ "pod_mil" = "#BBF093", \ "pod_synd" = LIGHT_COLOR_RED, \ @@ -63,10 +65,11 @@ "pod_industrial" = "#CCCC00") var/unlocked = TRUE - var/move_delay = NO_GRAVITY_SPEED - var/next_move = 0 - var/can_paint = TRUE + COOLDOWN_DECLARE(spacepod_move_cooldown) + COOLDOWN_DECLARE(cooldown_emp) //Used for disabling movement when hit by an EMP + var/datum/effect_system/trail_follow/spacepod/ion_trail + /obj/spacepod/proc/apply_paint(mob/user as mob) var/part_type @@ -94,7 +97,7 @@ /obj/spacepod/get_cell() return battery -/obj/spacepod/New() +/obj/spacepod/Initialize(mapload) . = ..() if(!pod_overlays) pod_overlays = new/list(2) @@ -108,7 +111,6 @@ pod_paint_effect[PAINT] = image(icon,icon_state = "PAINT") bound_width = 64 bound_height = 64 - dir = EAST battery = new battery_type(src) add_cabin() add_airtank() @@ -122,29 +124,10 @@ cargo_hold.max_w_class = 5 //fit almost anything cargo_hold.max_combined_w_class = 0 //you can optimize your stash with larger items START_PROCESSING(SSobj, src) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail)) + ion_trail = new + ion_trail.set_up(src) + ion_trail.start() -/obj/spacepod/proc/create_trail() - var/turf/T = get_turf(src) - var/atom/oldposition - var/atom/oldloc - switch(dir) - if(NORTH) - oldposition = get_step(T, SOUTH) - oldloc = get_step(oldposition, EAST) - if(SOUTH) // More difficult, offset to the north! - oldposition = get_step(get_step(src, NORTH), NORTH) - oldloc = get_step(oldposition, EAST) - if(EAST) // Just one to the north should suffice - oldposition = get_step(T, WEST) - oldloc = get_step(oldposition, NORTH) - if(WEST) // One to the east and north from there - oldposition = get_step(get_step(src, EAST), EAST) - oldloc = get_step(oldposition, NORTH) - - if(!T.has_gravity(T)) - new /obj/effect/particle_effect/ion_trails(oldposition, dir) - new /obj/effect/particle_effect/ion_trails(oldloc, dir) /obj/spacepod/Destroy() if(equipment_system.cargo_system) @@ -154,6 +137,7 @@ QDEL_NULL(battery) QDEL_NULL(cabin_air) QDEL_NULL(internal_tank) + QDEL_NULL(ion_trail) occupant_sanity_check() if(pilot) eject_pilot() @@ -164,11 +148,11 @@ STOP_PROCESSING(SSobj, src) return ..() + /obj/spacepod/process() give_air() regulate_temp() - if(src.empcounter > 0) - src.empcounter-- + /obj/spacepod/proc/update_icons() if(!pod_overlays) @@ -321,8 +305,8 @@ if(battery && battery.charge > 0) battery.use((battery.charge/3)/(severity*2)) deal_damage(80 / severity) - if(empcounter < (40 / severity)) - empcounter = 40 / severity + if(COOLDOWN_TIMELEFT(src, cooldown_emp) < (80 SECONDS / severity)) + COOLDOWN_START(src, cooldown_emp, 80 SECONDS / severity) switch(severity) if(1) @@ -619,8 +603,10 @@ /obj/spacepod/syndi/unlocked unlocked = TRUE -/obj/spacepod/sec/New() - ..() + +/obj/spacepod/sec/Initialize(mapload) + . = ..() + var/obj/item/spacepod_equipment/weaponry/burst_taser/T = new /obj/item/spacepod_equipment/weaponry/taser T.loc = equipment_system equipment_system.weapon_system = T @@ -644,8 +630,8 @@ equipment_system.lock_system.id = 100000 equipment_system.installed_modules += K -/obj/spacepod/random/New() - ..() +/obj/spacepod/random/Initialize(mapload) + . = ..() icon_state = pick("pod_civ", "pod_black", "pod_mil", "pod_synd", "pod_gold", "pod_industrial") switch(icon_state) if("pod_civ") @@ -1050,69 +1036,63 @@ else //just delete the cabin gas, we're in space or some shit qdel(removed) + +// it looks really good with default Process_Spacemove and newtonian movement actually, should make a button to turn it on/off +/obj/spacepod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) + return TRUE // obviously + + /obj/spacepod/relaymove(mob/user, direction) - if(user != src.pilot) - return - handlerelaymove(user, direction) - -/obj/spacepod/proc/handlerelaymove(mob/user, direction) - if(world.time < next_move) - return 0 - var/moveship = 1 - if(has_gravity(loc)) - move_delay = GRAVITY_SPEED - else - move_delay = NO_GRAVITY_SPEED - if(battery && battery.charge >= 1 && health && empcounter == 0) - if(!(direction & (UP|DOWN))) - src.dir = direction - switch(direction) - if(NORTH) - if(inertia_dir == SOUTH) - inertia_dir = NONE - moveship = 0 - if(SOUTH) - if(inertia_dir == NORTH) - inertia_dir = NONE - moveship = 0 - if(EAST) - if(inertia_dir == WEST) - inertia_dir = NONE - moveship = 0 - if(WEST) - if(inertia_dir == EAST) - inertia_dir = NONE - moveship = 0 - if(moveship) - if(direction & (UP|DOWN)) - var/turf/above = GET_TURF_ABOVE(loc) - if((direction & UP) && can_z_move(DOWN, above, z_move_flags = ZMOVE_FALL_FLAGS)) // going up and can fall down is bad. - return - if(!zMove(direction)) - return - pilot.update_z(z) // after we moved - else - Move(get_step(src, direction), direction) - if(equipment_system.cargo_system) - for(var/turf/T in locs) - for(var/obj/item/I in T.contents) - equipment_system.cargo_system.passover(I) + if(!COOLDOWN_FINISHED(src, spacepod_move_cooldown)) + return FALSE - else - if(!battery) - to_chat(user, "No energy cell detected.") - else if(battery.charge < 1) - to_chat(user, "Not enough charge left.") - else if(!health) - to_chat(user, "She's dead, Jim") - else if(empcounter != 0) - to_chat(user, "The pod control interface isn't responding. The console indicates [empcounter] seconds before reboot.") - else - to_chat(user, "Unknown error has occurred, yell at the coders.") - next_move = world.time + move_delay * 10 // Don't make it spam + if(!pilot || user != pilot || !direction) + COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) // Don't make it spam return FALSE - battery.charge = max(0, battery.charge - 1) - next_move = world.time + move_delay + + . = TRUE + + if(!battery) + to_chat(user, span_warning("No energy cell detected.")) + . = FALSE + else if(!battery.use(1)) + to_chat(user, span_warning("Not enough charge left.")) + . = FALSE + else if(health <= 0) + to_chat(user, span_warning("She's dead, Jim.")) + . = FALSE + else if(!COOLDOWN_FINISHED(src, cooldown_emp)) + to_chat(user, span_warning("The pod control interface isn't responding. The console indicates [COOLDOWN_TIMELEFT(src, cooldown_emp)] seconds before reboot.")) + . = FALSE + if(!.) + COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) + return . + + if(direction & (UP|DOWN)) + COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) + var/turf/above = GET_TURF_ABOVE(loc) + if((direction & UP) && can_z_move(DOWN, above, z_move_flags = ZMOVE_FALL_FLAGS)) // going up and can fall down is bad. + return FALSE + . = zMove(direction) + if(.) + pilot.update_z(z) // after we moved + else + var/turf/next_step = get_step(src, direction) + if(!next_step) + COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) + return FALSE + var/calculated_move_delay = has_gravity(loc) ? GRAVITY_SPEED : NO_GRAVITY_SPEED + . = Move(next_step, direction) + if(ISDIAGONALDIR(direction) && loc == next_step) + calculated_move_delay *= sqrt(2) + set_glide_size(DELAY_TO_GLIDE_SIZE(calculated_move_delay)) + COOLDOWN_START(src, spacepod_move_cooldown, calculated_move_delay) + + if(. && equipment_system.cargo_system) + for(var/atom/pod_loc as anything in locs) + for(var/obj/item/item in pod_loc.contents) + equipment_system.cargo_system.passover(item) + //// Damaged spacepod /obj/spacepod/civilian/damaged diff --git a/code/modules/station_goals/bfl.dm b/code/modules/station_goals/bfl.dm index 4ecc626f533..cc5e310a8f6 100644 --- a/code/modules/station_goals/bfl.dm +++ b/code/modules/station_goals/bfl.dm @@ -495,7 +495,7 @@ return ..() -/obj/machinery/bfl_lens/Move(atom/newloc, direction, movetime) +/obj/machinery/bfl_lens/Move(atom/newloc, direct = NONE, glide_size_override = 0) . = ..() if(!.) return diff --git a/code/modules/station_goals/brs/brs_rift_effect.dm b/code/modules/station_goals/brs/brs_rift_effect.dm index 3f1964f4159..784df836635 100644 --- a/code/modules/station_goals/brs/brs_rift_effect.dm +++ b/code/modules/station_goals/brs/brs_rift_effect.dm @@ -3,7 +3,7 @@ desc = "Аномальное образование с неизвестными свойствами." icon = 'icons/obj/engines_and_power/singularity.dmi' icon_state = "singularity_fog" - appearance_flags = 0 + appearance_flags = LONG_GLIDE layer = MASSIVE_OBJ_LAYER invisibility = INVISIBILITY_ANOMALY level = 1 // t-ray scaners show only things with level = 1 diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm index 6ac84b979f7..2aaab022b5f 100644 --- a/code/modules/station_goals/shield.dm +++ b/code/modules/station_goals/shield.dm @@ -204,9 +204,10 @@ GLOBAL_LIST_INIT(meteor_shields, list()) continue if(!emagged && space_los(meteor_to_destroy)) Beam(get_turf(meteor_to_destroy), icon_state = "sat_beam", time = 5, maxdistance = kill_range) - qdel(meteor_to_destroy) + if(meteor_to_destroy.shield_defense(src)) + qdel(meteor_to_destroy) -/obj/machinery/satellite/meteor_shield/Process_Spacemove(movement_dir = NONE) +/obj/machinery/satellite/meteor_shield/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return active /obj/machinery/satellite/meteor_shield/toggle(user) diff --git a/code/modules/surgery/organs/augments_tail.dm b/code/modules/surgery/organs/augments_tail.dm index c3bbe311d88..67a1030a3d8 100644 --- a/code/modules/surgery/organs/augments_tail.dm +++ b/code/modules/surgery/organs/augments_tail.dm @@ -95,7 +95,7 @@ implant_ability.Remove(owner) . = ..() -/obj/item/organ/internal/cyberimp/tail/blade/ui_action_click(mob/user, actiontype, leftclick) +/obj/item/organ/internal/cyberimp/tail/blade/ui_action_click(mob/user, action, leftclick) if(implant_emp_downtime) // 100 sec cooldown after EMP to_chat(owner, span_warning("Ваш имплант всё ещё перегружен после ЭМИ!")) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 10a75cf573d..0a23337a1b9 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -316,10 +316,10 @@ if(isobj(H.shoes)) var/thingy = H.shoes if(H.drop_item_ground(H.shoes)) - walk_away(thingy,H,15,2) + SSmove_manager.move_away(thingy, H, 15, 2) spawn(20) if(thingy) - walk(thingy,0) + SSmove_manager.stop_looping(thingy) /obj/item/organ/internal/honktumor/cursed diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm index 2903d6efcc4..6f80c693f26 100644 --- a/code/modules/vehicle/ambulance.dm +++ b/code/modules/vehicle/ambulance.dm @@ -2,7 +2,7 @@ name = "ambulance" desc = "This is what the paramedic uses to run over people they need to take to medbay." icon_state = "docwagon2" - vehicle_move_delay = 1.5 + vehicle_move_delay = 0.3 SECONDS key_type = /obj/item/key/ambulance var/obj/structure/bed/amb_trolley/bed = null var/datum/action/ambulance_alarm/AA @@ -13,11 +13,20 @@ light_power = 3 light_color = "#F70027" + /obj/vehicle/ambulance/Initialize(mapload) . = ..() AA = new(src) soundloop = new(list(src), FALSE) + +/obj/vehicle/ambulance/Destroy() + QDEL_NULL(soundloop) + QDEL_NULL(AA) + bed = null + return ..() + + /datum/action/ambulance_alarm name = "Toggle Sirens" icon_icon = 'icons/obj/vehicles/vehicles.dmi' @@ -73,37 +82,38 @@ /obj/vehicle/ambulance/handle_vehicle_offsets() - ..() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - switch(buckled_mob.dir) - if(SOUTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 7 - if(WEST) - buckled_mob.pixel_x = 13 - buckled_mob.pixel_y = 7 - if(NORTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - if(EAST) - buckled_mob.pixel_x = -13 - buckled_mob.pixel_y = 7 - -/obj/vehicle/ambulance/Move(newloc, Dir, movetime) + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.setDir(dir) + switch(dir) + if(SOUTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 7 + if(WEST) + buckled_mob.pixel_x = 13 + buckled_mob.pixel_y = 7 + if(NORTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 4 + if(EAST) + buckled_mob.pixel_x = -13 + buckled_mob.pixel_y = 7 + + +/obj/vehicle/ambulance/Move(atom/newloc, direct = NONE, glide_size_override = 0) var/oldloc = loc if(bed && !Adjacent(bed)) bed = null . = ..() - if(bed && get_dist(oldloc, loc) <= 2) - bed.glide_size = glide_size + if(. && bed && get_dist(oldloc, loc) <= 2) bed.Move(oldloc, get_dir(bed, oldloc)) - bed.dir = Dir + bed.set_glide_size(glide_size) + bed.setDir(direct) if(bed.has_buckled_mobs()) - for(var/m in bed.buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(Dir) + for(var/mob/living/buckled_mob as anything in bed.buckled_mobs) + buckled_mob.setDir(direct) + /obj/structure/bed/amb_trolley name = "ambulance train trolley" diff --git a/code/modules/vehicle/atv.dm b/code/modules/vehicle/atv.dm index dde08aff979..06f06cb4e40 100644 --- a/code/modules/vehicle/atv.dm +++ b/code/modules/vehicle/atv.dm @@ -9,72 +9,128 @@ integrity_failure = 70 generic_pixel_x = 0 generic_pixel_y = 4 - vehicle_move_delay = 1 + vehicle_move_delay = 0.25 SECONDS pull_push_speed_modifier = 1 - var/static/mutable_appearance/atvcover + var/mutable_appearance/atvcover + /obj/vehicle/atv/Initialize(mapload) . = ..() - atvcover = mutable_appearance(icon, atvcover, ABOVE_MOB_LAYER) + atvcover = mutable_appearance(icon, "atvcover", ABOVE_MOB_LAYER + 0.1) -/obj/vehicle/atv/post_buckle_mob(mob/living/target) - . = ..() - add_overlay(atvcover) +/obj/vehicle/atv/Destroy() + atvcover = null + return ..() -/obj/vehicle/atv/post_unbuckle_mob(mob/living/target) +/obj/vehicle/atv/update_overlays() . = ..() - cut_overlay(atvcover) + if(!has_buckled_mobs()) + return . + . += atvcover + + +/obj/vehicle/atv/handle_vehicle_icons() + update_icon(UPDATE_OVERLAYS) /obj/vehicle/atv/handle_vehicle_layer() - if(dir == SOUTH) - layer = ABOVE_MOB_LAYER - else - layer = OBJ_LAYER + return + //TURRETS! /obj/vehicle/atv/turret - var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = null + var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = /obj/machinery/porta_turret/syndicate/vehicle_turret -/obj/machinery/porta_turret/syndicate/vehicle_turret - name = "mounted turret" - scan_range = 7 - emp_vulnerable = 1 - density = FALSE /obj/vehicle/atv/turret/Initialize(mapload) . = ..() - turret = new(loc) - //turret.base = src + turret = new turret(loc) + handle_vehicle_offsets() + handle_vehicle_icons() + RegisterSignal(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(on_glide_size_update)) + RegisterSignal(turret, COMSIG_PARENT_QDELETING, PROC_REF(on_turret_deleting)) + + +/obj/vehicle/atv/turret/Destroy() + QDEL_NULL(turret) + return ..() + + +/obj/vehicle/atv/turret/proc/on_glide_size_update(datum/source, new_glide_size) + SIGNAL_HANDLER + turret?.set_glide_size(new_glide_size) + + +/obj/vehicle/atv/turret/proc/on_turret_deleting(datum/source) + SIGNAL_HANDLER + UnregisterSignal(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE) + turret = null + + +/obj/vehicle/atv/turret/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) + . = ..() + if(. && turret) + turret.forceMove(loc) + /obj/vehicle/atv/turret/handle_vehicle_layer() + if(!turret) + return + if(!has_buckled_mobs()) + turret.layer = OBJ_LAYER + 0.01 + return if(dir == SOUTH) - layer = ABOVE_MOB_LAYER + turret.layer = OBJ_LAYER + 0.01 else - layer = OBJ_LAYER + turret.layer = ABOVE_MOB_LAYER + 0.01 + + +/obj/vehicle/atv/turret/update_overlays() + . = list(atvcover) - if(turret) - if(dir == NORTH) - turret.layer = ABOVE_MOB_LAYER - else - turret.layer = OBJ_LAYER /obj/vehicle/atv/turret/handle_vehicle_offsets() - ..() - if(turret) - turret.loc = loc - switch(dir) - if(NORTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - if(EAST) - turret.pixel_x = -12 - turret.pixel_y = 4 - if(SOUTH) - turret.pixel_x = 0 - turret.pixel_y = 4 - if(WEST) - turret.pixel_x = 12 - turret.pixel_y = 4 + . = ..() + if(!turret) + return + + switch(dir) + if(NORTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + if(EAST) + turret.pixel_x = -12 + turret.pixel_y = 4 + if(SOUTH) + turret.pixel_x = 0 + turret.pixel_y = 4 + if(WEST) + turret.pixel_x = 12 + turret.pixel_y = 4 + + +/obj/vehicle/atv/turret/fast + turret = /obj/machinery/porta_turret/syndicate/vehicle_turret/fast + + +/obj/machinery/porta_turret/syndicate/vehicle_turret + name = "mounted turret" + animate_movement = SLIDE_STEPS + scan_range = 7 + emp_vulnerable = TRUE + density = FALSE + layer = OBJ_LAYER + 0.01 + + +/obj/machinery/porta_turret/syndicate/vehicle_turret/fast + projectile = /obj/item/projectile/bullet/weakbullet4/c9mmte + eprojectile = /obj/item/projectile/bullet/weakbullet4/c9mmte + shot_delay = 0.2 SECONDS + + +/obj/machinery/porta_turret/syndicate/vehicle_turret/fast/Initialize(mapload) + . = ..() + makeSpeedProcess() + diff --git a/code/modules/vehicle/janicart.dm b/code/modules/vehicle/janicart.dm index 3e61fc16851..8a1137565b7 100644 --- a/code/modules/vehicle/janicart.dm +++ b/code/modules/vehicle/janicart.dm @@ -11,34 +11,35 @@ QDEL_NULL(trash_bag) return ..() + /obj/vehicle/janicart/handle_vehicle_offsets() - ..() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - switch(buckled_mob.dir) - if(NORTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - if(EAST) - buckled_mob.pixel_x = -12 - buckled_mob.pixel_y = 7 - if(SOUTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 7 - if(WEST) - buckled_mob.pixel_x = 12 - buckled_mob.pixel_y = 7 + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.setDir(dir) + switch(dir) + if(NORTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 4 + if(EAST) + buckled_mob.pixel_x = -12 + buckled_mob.pixel_y = 7 + if(SOUTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 7 + if(WEST) + buckled_mob.pixel_x = 12 + buckled_mob.pixel_y = 7 + -/obj/vehicle/janicart/Move(atom/OldLoc, Dir) +/obj/vehicle/janicart/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) . = ..() - if(floorbuffer) - var/turf/tile = loc - if(isturf(tile)) - tile.clean_blood() - for(var/obj/effect/check in tile) - if(check.is_cleanable()) - qdel(check) + if(. && floorbuffer && isturf(loc)) + loc.clean_blood() + for(var/obj/effect/check in loc) + if(check.is_cleanable()) + qdel(check) + /obj/vehicle/janicart/examine(mob/user) . = ..() @@ -64,7 +65,7 @@ qdel(I) to_chat(user,"You upgrade [src] with [I].") update_icon(UPDATE_OVERLAYS) - else if(trash_bag && (!is_key(I) || is_key(inserted_key))) // don't put a key in the trash when we need it + else if(trash_bag && (initial(key_type.type) != I.type)) // don't put a key in the trash when we need it trash_bag.attackby(I, user) else return ..() diff --git a/code/modules/vehicle/motorcycle.dm b/code/modules/vehicle/motorcycle.dm index f5fe4a986f3..072907a4529 100644 --- a/code/modules/vehicle/motorcycle.dm +++ b/code/modules/vehicle/motorcycle.dm @@ -5,27 +5,32 @@ icon_state = "motorcycle_4dir" generic_pixel_x = 0 generic_pixel_y = 4 - vehicle_move_delay = 1 + vehicle_move_delay = 0.25 SECONDS pull_push_speed_modifier = 1 var/mutable_appearance/bikecover + /obj/vehicle/motorcycle/Initialize(mapload) . = ..() - bikecover = mutable_appearance(icon, "motorcycle_overlay_4d", ABOVE_MOB_LAYER) + bikecover = mutable_appearance(icon, "motorcycle_4dir_overlay", ABOVE_MOB_LAYER) -/obj/vehicle/motorcycle/post_buckle_mob(mob/living/target) - . = ..() - add_overlay(bikecover) +/obj/vehicle/motorcycle/Destroy() + bikecover = null + return ..() -/obj/vehicle/motorcycle/post_unbuckle_mob(mob/living/target) +/obj/vehicle/motorcycle/update_overlays() . = ..() - cut_overlay(bikecover) + if(!has_buckled_mobs()) + return . + . += bikecover + + +/obj/vehicle/motorcycle/handle_vehicle_icons() + update_icon(UPDATE_OVERLAYS) /obj/vehicle/motorcycle/handle_vehicle_layer() - if(dir == SOUTH) - layer = ABOVE_MOB_LAYER - else - layer = OBJ_LAYER + return + diff --git a/code/modules/vehicle/secway.dm b/code/modules/vehicle/secway.dm index d449bab788d..8d9d95a1f17 100644 --- a/code/modules/vehicle/secway.dm +++ b/code/modules/vehicle/secway.dm @@ -8,7 +8,7 @@ integrity_failure = 50 generic_pixel_x = 0 generic_pixel_y = 4 - vehicle_move_delay = 1 + vehicle_move_delay = 0.25 SECONDS pull_push_speed_modifier = 1 diff --git a/code/modules/vehicle/snowmobile.dm b/code/modules/vehicle/snowmobile.dm index 5eecaae6e2b..830e9b29783 100644 --- a/code/modules/vehicle/snowmobile.dm +++ b/code/modules/vehicle/snowmobile.dm @@ -14,11 +14,11 @@ /obj/vehicle/snowmobile/key/Initialize(mapload) . = ..() - inserted_key = new /obj/item/key/snowmobile(null) + inserted_key = new /obj/item/key/snowmobile(src) /obj/vehicle/snowmobile/blue/key/Initialize(mapload) . = ..() - inserted_key = new /obj/item/key/snowmobile(null) + inserted_key = new /obj/item/key/snowmobile(src) /obj/item/key/snowmobile name = "snowmobile key" diff --git a/code/modules/vehicle/speedbike.dm b/code/modules/vehicle/speedbike.dm index 7b4ba3f45e1..057bd6c0e0e 100644 --- a/code/modules/vehicle/speedbike.dm +++ b/code/modules/vehicle/speedbike.dm @@ -2,49 +2,69 @@ name = "Speedbike" icon = 'icons/obj/vehicles/bike.dmi' icon_state = "speedbike_blue" - layer = MOB_LAYER - 0.1 - vehicle_move_delay = 0 + vehicle_move_delay = 0.15 SECONDS pull_push_speed_modifier = 1 var/overlay_state = "cover_blue" - var/mutable_appearance/overlay + var/mutable_appearance/cover_overlay + /obj/vehicle/space/speedbike/Initialize(mapload) . = ..() - overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER) - add_overlay(overlay) + cover_overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER) -/obj/vehicle/space/speedbike/Move(newloc,move_dir) - if(has_buckled_mobs()) - new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc) + +/obj/vehicle/space/speedbike/Destroy() + cover_overlay = null + return ..() + + +/obj/vehicle/space/speedbike/update_overlays() . = ..() + if(!has_buckled_mobs()) + return . + . += cover_overlay + + +/obj/vehicle/space/speedbike/handle_vehicle_icons() + update_icon(UPDATE_OVERLAYS) + + +/obj/vehicle/space/speedbike/Move(atom/newloc, direct = NONE, glide_size_override = 0) + if(has_buckled_mobs()) + new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc, direct) + return ..() + /obj/vehicle/space/speedbike/handle_vehicle_layer() + return + + +/obj/vehicle/space/speedbike/handle_vehicle_offsets() switch(dir) - if(NORTH,SOUTH) + if(NORTH, SOUTH) pixel_x = -16 pixel_y = -16 - if(EAST,WEST) + if(EAST, WEST) pixel_x = -18 pixel_y = 0 + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.setDir(dir) + switch(dir) + if(NORTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = -8 + if(SOUTH) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 4 + if(EAST) + buckled_mob.pixel_x = -10 + buckled_mob.pixel_y = 5 + if(WEST) + buckled_mob.pixel_x = 10 + buckled_mob.pixel_y = 5 -/obj/vehicle/space/speedbike/handle_vehicle_offsets() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(dir) - switch(dir) - if(NORTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = -8 - if(SOUTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - if(EAST) - buckled_mob.pixel_x = -10 - buckled_mob.pixel_y = 5 - if(WEST) - buckled_mob.pixel_x = 10 - buckled_mob.pixel_y = 5 /obj/vehicle/space/speedbike/red icon_state = "speedbike_red" diff --git a/code/modules/vehicle/sportscar.dm b/code/modules/vehicle/sportscar.dm index 7e6305918ee..db063b31d6b 100644 --- a/code/modules/vehicle/sportscar.dm +++ b/code/modules/vehicle/sportscar.dm @@ -3,49 +3,66 @@ desc = "A very luxurious vehicle." icon = 'icons/obj/vehicles/sportscar.dmi' icon_state = "sportscar" - generic_pixel_x = 0 - generic_pixel_y = 4 - vehicle_move_delay = 1 + vehicle_move_delay = 0.25 SECONDS pull_push_speed_modifier = 1 - var/mutable_appearance/carcover -/obj/vehicle/car/Initialize(mapload) - . = ..() - carcover = mutable_appearance(icon, "sportscar_cover", ABOVE_MOB_LAYER) +#define CAR_COVER_NORTH 1 +#define CAR_COVER_SOUTH 2 +#define CAR_COVER_EAST 3 +#define CAR_COVER_WEST 4 -/obj/vehicle/car/post_buckle_mob(mob/living/target) +/obj/vehicle/car/update_overlays() . = ..() - add_overlay(carcover) + if(!has_buckled_mobs()) + return . + var/static/list/car_covers_cache[4] + car_covers_cache[CAR_COVER_NORTH] = mutable_appearance(icon, "sportscar_north", ABOVE_MOB_LAYER) + car_covers_cache[CAR_COVER_SOUTH] = mutable_appearance(icon, "sportscar_south", ABOVE_MOB_LAYER) + car_covers_cache[CAR_COVER_EAST] = mutable_appearance(icon, "sportscar_east", ABOVE_MOB_LAYER) + car_covers_cache[CAR_COVER_WEST] = mutable_appearance(icon, "sportscar_west", ABOVE_MOB_LAYER) -/obj/vehicle/car/post_unbuckle_mob(mob/living/target) - . = ..() - cut_overlay(carcover) + switch(dir) + if(NORTH) + . += car_covers_cache[CAR_COVER_NORTH] + if(SOUTH) + . += car_covers_cache[CAR_COVER_SOUTH] + if(EAST) + . += car_covers_cache[CAR_COVER_EAST] + if(WEST) + . += car_covers_cache[CAR_COVER_WEST] + +#undef CAR_COVER_NORTH +#undef CAR_COVER_SOUTH +#undef CAR_COVER_EAST +#undef CAR_COVER_WEST + + +/obj/vehicle/car/handle_vehicle_icons() + update_icon(UPDATE_OVERLAYS) /obj/vehicle/car/handle_vehicle_offsets() - ..() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - switch(buckled_mob.dir) - if(NORTH) - buckled_mob.pixel_x = 2 - buckled_mob.pixel_y = 20 - if(EAST) - buckled_mob.pixel_x = 20 - buckled_mob.pixel_y = 23 - if(SOUTH) - buckled_mob.pixel_x = 20 - buckled_mob.pixel_y = 27 - if(WEST) - buckled_mob.pixel_x = 34 - buckled_mob.pixel_y = 10 + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.setDir(dir) + switch(dir) + if(NORTH) + buckled_mob.pixel_x = 2 + buckled_mob.pixel_y = 20 + if(EAST) + buckled_mob.pixel_x = 20 + buckled_mob.pixel_y = 23 + if(SOUTH) + buckled_mob.pixel_x = 20 + buckled_mob.pixel_y = 27 + if(WEST) + buckled_mob.pixel_x = 34 + buckled_mob.pixel_y = 10 /obj/vehicle/car/handle_vehicle_layer() - if(dir == SOUTH) - layer = ABOVE_MOB_LAYER - else - layer = OBJ_LAYER + return // we got custom layers, dont worry + diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index eb459a39d97..f9c4366feeb 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -11,23 +11,28 @@ buckle_lying = 0 max_integrity = 300 armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) - var/key_type - var/held_key_type //Similar to above, but the vehicle needs the key in hands as opposed to inserted into the ignition + /// Item required for the vehicle to be inserted into the ignition. + var/obj/item/key_type + /// Whehter our key should be in mob hands, rather than in the vehicle ignintion. + var/key_in_hands = FALSE + /// Currently inerted key. var/obj/item/key/inserted_key - var/key_type_exact = TRUE //can subtypes work - var/last_vehicle_move = 0 //used for move delays - var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower - var/auto_door_open = TRUE - var/needs_gravity = 0 //To allow non-space vehicles to move in no gravity or not, mostly for adminbus - //Pixels - var/generic_pixel_x = 0 //All dirs show this pixel_x for the driver - var/generic_pixel_y = 0 //All dirs shwo this pixel_y for the driver - var/spaceworthy = FALSE + /// To allow non-space vehicles to move in no gravity or not, mostly for adminbus. + var/needs_gravity = FALSE + /// All dirs apply this pixel_x for the driver. + var/generic_pixel_x = 0 + /// All dirs apply this pixel_y for the driver. + var/generic_pixel_y = 0 + /// Delay between movements in deciseconds, lower = faster, higher = slower + var/vehicle_move_delay = 0.35 SECONDS + COOLDOWN_DECLARE(vehicle_move_cooldown) /obj/vehicle/Initialize(mapload) . = ..() handle_vehicle_layer() + handle_vehicle_icons() + /obj/vehicle/Destroy() QDEL_NULL(inserted_key) @@ -37,77 +42,101 @@ /obj/vehicle/examine(mob/user) . = ..() if(key_type) - if(!inserted_key) - . += "Put a key inside it by clicking it with the key." + if(key_in_hands) + . += span_info("[src] requires the [initial(key_type.name)] to be held in hands to start driving.") else - . += "Alt-click [src] to remove the key." + if(inserted_key) + . += span_info("Alt-click [src] to remove the key.") + else + . += span_info("[src] requires the [initial(key_type.name)] to be inserted into ignintion to start driving.") + if(resistance_flags & ON_FIRE) - . += "It's on fire!" + . += span_warning("It's on fire!") var/healthpercent = obj_integrity/max_integrity * 100 switch(healthpercent) if(50 to 99) - . += "It looks slightly damaged." + . += span_notice("It looks slightly damaged.") if(25 to 50) - . += "It appears heavily damaged." + . += span_notice("It appears heavily damaged.") if(0 to 25) - . += "It's falling apart!" + . += span_warning("It's falling apart!") + /obj/vehicle/attackby(obj/item/I, mob/user, params) - if(key_type && !is_key(inserted_key) && is_key(I)) - if(user.drop_transfer_item_to_loc(I, src)) - to_chat(user, "You insert [I] into [src].") - if(inserted_key) //just in case there's an invalid key - inserted_key.forceMove(drop_location()) - inserted_key = I - else - to_chat(user, "[I] seems to be stuck to your hand!") + if(!key_type || I.type != key_type) + return ..() + + if(inserted_key) + to_chat(user, span_warning("[src] already has [inserted_key.name] in the ignition!")) return - return ..() + + if(!user.drop_transfer_item_to_loc(I, src)) + return + + to_chat(user, span_notice("You insert [I] into [src]'s ignintion.")) + inserted_key = I + /obj/vehicle/AltClick(mob/living/user) - if(!istype(user)) + if(!istype(user) || !Adjacent(user)) return if(user.incapacitated() || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) - to_chat(user, "You can't do that right now!") + to_chat(user, span_warning("You can't do that right now!")) return - if(inserted_key && user.Adjacent(user)) - if(!(user in buckled_mobs)) - to_chat(user, "You must be riding [src] to remove [src]'s key!") - return - to_chat(user, "You remove [inserted_key] from [src].") - inserted_key.forceMove_turf() - user.put_in_hands(inserted_key, ignore_anim = FALSE) - inserted_key = null - -/obj/vehicle/proc/is_key(obj/item/I) - return I ? (key_type_exact ? (I.type == key_type) : istype(I, key_type)) : FALSE - -/obj/vehicle/proc/held_keycheck(mob/user) - if(held_key_type) - if(istype(user.l_hand, held_key_type) || istype(user.r_hand, held_key_type)) - return TRUE - else + if(!inserted_key) + to_chat(user, span_warning("[src] has no inserted keys!")) + return + if(!(user in buckled_mobs)) + to_chat(user, span_warning("You must be riding [src] to remove [src]'s key!")) + return + to_chat(user, span_notice("You remove [inserted_key] from [src].")) + inserted_key.forceMove_turf() + user.put_in_hands(inserted_key, ignore_anim = FALSE) + inserted_key = null + + +/obj/vehicle/proc/keycheck(mob/user, provide_feedback = TRUE) + if(!key_type) return TRUE - return FALSE + if(key_in_hands) + if(!(user.l_hand && user.l_hand.type == key_type) && !(user.r_hand && user.r_hand.type == key_type)) + if(provide_feedback) + to_chat(user, span_warning("You'll need the [initial(key_type.name)] in one of your hands to drive [src]!")) + return FALSE + return TRUE + if(!(inserted_key && inserted_key.type == key_type)) + if(provide_feedback) + to_chat(user, span_warning("[src] has no inserted keys!")) + return FALSE + return TRUE + //APPEARANCE /obj/vehicle/proc/handle_vehicle_layer() - if(dir != NORTH) + if(!has_buckled_mobs()) + layer = OBJ_LAYER + return + if(dir == SOUTH) layer = ABOVE_MOB_LAYER else layer = OBJ_LAYER +/// Used to update vehicle icons if needed +/obj/vehicle/proc/handle_vehicle_icons() + return + + //Override this to set your vehicle's various pixel offsets //if they differ between directions, otherwise use the //generic variables /obj/vehicle/proc/handle_vehicle_offsets() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(dir) - buckled_mob.pixel_x = generic_pixel_x - buckled_mob.pixel_y = generic_pixel_y + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.setDir(dir) + buckled_mob.pixel_x = generic_pixel_x + buckled_mob.pixel_y = generic_pixel_y /obj/item/key @@ -120,82 +149,76 @@ //BUCKLE HOOKS /obj/vehicle/post_buckle_mob(mob/living/target) + handle_vehicle_layer() handle_vehicle_offsets() + handle_vehicle_icons() /obj/vehicle/post_unbuckle_mob(mob/living/target) - target.pixel_x = 0 - target.pixel_y = 0 + target.pixel_x = target.base_pixel_x //+ target.body_position_pixel_x_offset + target.pixel_y = target.base_pixel_y //+ target.body_position_pixel_y_offset + handle_vehicle_offsets() + handle_vehicle_layer() + handle_vehicle_icons() /obj/vehicle/bullet_act(obj/item/projectile/Proj) - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.bullet_act(Proj) + if(!has_buckled_mobs()) + return + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.bullet_act(Proj) + //MOVEMENT + /obj/vehicle/relaymove(mob/user, direction) - if(world.time < last_vehicle_move) - return + if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown) || !has_buckled_mobs() || user != buckled_mobs[1]) + return FALSE - if(key_type && !is_key(inserted_key)) - last_vehicle_move = world.time + 5 - to_chat(user, "[src] has no key inserted!") - return + var/turf/next_step = get_step(src, direction) + if(!next_step || !isturf(loc) || !Process_Spacemove(direction) || !keycheck(user)) + COOLDOWN_START(src, vehicle_move_cooldown, 0.5 SECONDS) + return FALSE if(user.incapacitated()) unbuckle_mob(user) - return + return FALSE - if(held_keycheck(user)) - if(!Process_Spacemove(direction) || !isturf(loc)) - return - - last_vehicle_move = get_config_multiplicative_speed_by_path(/mob/living/carbon/human) + vehicle_move_delay - Move(get_step(src, direction), direction, last_vehicle_move) - - if(direction & (direction - 1)) //moved diagonally - last_vehicle_move *= 1.41 - last_vehicle_move += world.time - - if(has_buckled_mobs()) - if(issimulatedturf(loc)) - var/turf/simulated/T = loc - if(T.wet == TURF_WET_LUBE) //Lube! Fall off! - playsound(src, 'sound/misc/slip.ogg', 50, 1, -3) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.Weaken(10 SECONDS) - unbuckle_all_mobs() - step(src, dir) - - handle_vehicle_layer() - handle_vehicle_offsets() - else - to_chat(user, "You'll need the keys in one of your hands to drive [src].") + var/add_delay = vehicle_move_delay + + . = Move(next_step, direction) + if(ISDIAGONALDIR(direction) && loc == next_step) + add_delay *= sqrt(2) + + set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay)) + COOLDOWN_START(src, vehicle_move_cooldown, add_delay) -/obj/vehicle/Move(NewLoc, Dir = 0, movetime) +/obj/vehicle/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE) . = ..() + if(!.) + return . handle_vehicle_layer() handle_vehicle_offsets() + handle_vehicle_icons() + if(has_buckled_mobs() && issimulatedturf(loc)) + var/turf/simulated/check_turf = loc + if(check_turf.wet == TURF_WET_LUBE) + playsound(src, 'sound/misc/slip.ogg', 50, TRUE, -3) + for(var/mob/living/buckled_mob as anything in buckled_mobs) + buckled_mob.Weaken(10 SECONDS) + unbuckle_all_mobs() -/obj/vehicle/Bump(atom/movable/M) - if(!spaceworthy && isspaceturf(get_turf(src))) - return FALSE +/obj/vehicle/Bump(atom/bumped_atom, custom_bump) . = ..() - if(auto_door_open) - if(istype(M, /obj/machinery/door) && has_buckled_mobs()) - for(var/m in buckled_mobs) - M.Bumped(m) - -/obj/vehicle/proc/RunOver(var/mob/living/carbon/human/H) - return //write specifics for different vehicles + if(. || isnull(.) || !has_buckled_mobs() || !istype(bumped_atom, /obj/machinery/door)) + return . + for(var/mob/living/buckled_mob as anything in buckled_mobs) + bumped_atom.Bumped(buckled_mob) -/obj/vehicle/Process_Spacemove(movement_dir = NONE) +/obj/vehicle/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) if(has_gravity()) return TRUE @@ -207,9 +230,11 @@ return FALSE + /obj/vehicle/space pressure_resistance = INFINITY - spaceworthy = TRUE -/obj/vehicle/space/Process_Spacemove(movement_dir = NONE) + +/obj/vehicle/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE + diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 37295319bb3..4cf287bfe54 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/obj/vehicles/4wheeler.dmi b/icons/obj/vehicles/4wheeler.dmi index e1bfd5e18fd..eaa38aefb13 100644 Binary files a/icons/obj/vehicles/4wheeler.dmi and b/icons/obj/vehicles/4wheeler.dmi differ diff --git a/paradise.dme b/paradise.dme index 34579b9d4c3..c3fe228cc47 100644 --- a/paradise.dme +++ b/paradise.dme @@ -131,6 +131,7 @@ #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\mapping.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals_object.dm" #include "code\__DEFINES\traits\_traits.dm" #include "code\__DEFINES\traits\declarations.dm" #include "code\__DEFINES\traits\sources.dm" @@ -282,7 +283,6 @@ #include "code\controllers\subsystem\parallax.dm" #include "code\controllers\subsystem\runechat.dm" #include "code\controllers\subsystem\shuttles.dm" -#include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\speech_controller.dm" #include "code\controllers\subsystem\statistics.dm" #include "code\controllers\subsystem\sun.dm" @@ -295,6 +295,10 @@ #include "code\controllers\subsystem\verb_manager.dm" #include "code\controllers\subsystem\vote.dm" #include "code\controllers\subsystem\weather.dm" +#include "code\controllers\subsystem\movement\move_handler.dm" +#include "code\controllers\subsystem\movement\movement.dm" +#include "code\controllers\subsystem\movement\movement_types.dm" +#include "code\controllers\subsystem\movement\spacedrift.dm" #include "code\controllers\subsystem\non-firing\alarm.dm" #include "code\controllers\subsystem\non-firing\assets.dm" #include "code\controllers\subsystem\non-firing\atoms.dm" @@ -393,6 +397,7 @@ #include "code\datums\components\cross_shock.dm" #include "code\datums\components\decal.dm" #include "code\datums\components\defibrillator.dm" +#include "code\datums\components\drift.dm" #include "code\datums\components\ducttape.dm" #include "code\datums\components\edit_complainer.dm" #include "code\datums\components\examine_override.dm" @@ -400,6 +405,7 @@ #include "code\datums\components\fullauto.dm" #include "code\datums\components\hide_highest_offset.dm" #include "code\datums\components\jackboots.dm" +#include "code\datums\components\jetpack.dm" #include "code\datums\components\label.dm" #include "code\datums\components\material_container.dm" #include "code\datums\components\overlay_lighting.dm"