diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 063905eea04..fe26af36488 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -79,4 +79,12 @@ /// Raised on a mob to check it's psi-sensitivity rating. This is not the same thing as checking if someone is psionic, but psionic people have an innate bonus to the check. #define COMSIG_PSI_CHECK_SENSITIVITY "psi_check_sensitivity" +// directional input signals. +// TODO: Please rework these if you are porting actual keybindings. +/// Raised on a mob when receiving a "Northeast" input key, typically the 'e' key. +#define COMSIG_INPUT_KEY_QUICK_EQUIP "quick-equip_key_pressed" + +/// Raised on a mob when receiving a "Northwest" input key, typically the 'q' key. +#define COMSIG_INPUT_KEY_DROP "drop_key_pressed" + /*******Component Specific Signals*******/ diff --git a/code/modules/heavy_vehicle/components/legs.dm b/code/modules/heavy_vehicle/components/legs.dm index 1b4e2b25718..657abacb33f 100644 --- a/code/modules/heavy_vehicle/components/legs.dm +++ b/code/modules/heavy_vehicle/components/legs.dm @@ -3,8 +3,28 @@ center_of_mass = list("x"=24, "y"=4) icon_state = "loader_legs" power_use = 75 + /// Movement delay added when moving in any direction. var/move_delay = 5 + + /// Movement delay added when turning, cumulative with move_delay. var/turn_delay = 5 + + /// Extra movement delay added when using reverse throttle. + var/reverse_delay = 10 + + /// Extra movement delay added when strafing. + var/strafe_delay_modifier = 1.5 + + /// Whether or not the legs allow strafing at all. + var/can_strafe = TRUE + + /** + * Extra movement delay added based on the ratio of the legs current damage to its maximum damage. + * IE: If your max is 100, and you've taken 50 damage to the legs, this delay should come out to +5. + * At 0 damage, the delay from this is also 0. So it linearly scales with leg damage. + */ + var/damaged_delay = 10 + var/obj/item/robot_parts/robot_component/actuator/motivator var/mech_turn_sound = 'sound/mecha/mechturn.ogg' var/mech_step_sound = 'sound/mecha/mechstep.ogg' diff --git a/code/modules/heavy_vehicle/mech_helpers.dm b/code/modules/heavy_vehicle/mech_helpers.dm index 6356742d8c2..2fe377bc896 100644 --- a/code/modules/heavy_vehicle/mech_helpers.dm +++ b/code/modules/heavy_vehicle/mech_helpers.dm @@ -1,5 +1,4 @@ /mob/living/heavy_vehicle/proc/can_move(var/mob/user) - . = 0 if(world.time < next_mecha_move) return @@ -12,14 +11,12 @@ next_mecha_move = world.time + 3 // Just to stop them from getting spammed with messages. return - if(!legs.motivator || legs.total_damage > 45) + if(!legs.motivator || legs.total_damage != 0 && legs.total_damage >= legs.max_damage) if(user) - to_chat(user, SPAN_WARNING("Your motivators are damaged! You can't move!")) + to_chat(user, SPAN_WARNING("Your motivators are destroyed! You can't move!")) next_mecha_move = world.time + 15 return - next_mecha_move = world.time + (incorporeal_move ? legs.move_delay / 2 : legs.move_delay) - if(maintenance_protocols) if(user) to_chat(user, SPAN_WARNING("Maintenance protocols are in effect.")) @@ -31,6 +28,79 @@ to_chat(user, SPAN_WARNING("The power indicator flashes briefly.")) return + next_mecha_move = world.time + (incorporeal_move ? legs.move_delay / 2 : legs.move_delay) + (legs.damaged_delay * (legs.total_damage / legs.max_damage)) + return TRUE + +/mob/living/heavy_vehicle/proc/can_turn(var/mob/user) + if(world.time < next_mecha_turn) + return + + if(incapacitated() || (user && user.incapacitated()) || lockdown) + return + + if(!legs) + if(user) + to_chat(user, SPAN_WARNING("\The [src] has no means of propulsion!")) + next_mecha_turn = world.time + 3 // Just to stop them from getting spammed with messages. + return + + if(!legs.motivator || legs.total_damage != 0 && legs.total_damage >= legs.max_damage) + if(user) + to_chat(user, SPAN_WARNING("Your motivators are destroyed! You can't turn!")) + next_mecha_turn = world.time + 15 + return + + if(maintenance_protocols) + if(user) + to_chat(user, SPAN_WARNING("Maintenance protocols are in effect.")) + return + + var/obj/item/cell/C = get_cell() + if(!C || !C.check_charge(legs.power_use * CELLRATE)) + if(user) + to_chat(user, SPAN_WARNING("The power indicator flashes briefly.")) + return + + next_mecha_turn = world.time + legs.turn_delay + (legs.damaged_delay * (legs.total_damage / legs.max_damage)) + return TRUE + +/mob/living/heavy_vehicle/proc/can_strafe(var/mob/user) + if (world.time < next_mecha_move) + return + + if(incapacitated() || (user && user.incapacitated()) || lockdown) + return + + if(!legs) + if(user) + to_chat(user, SPAN_WARNING("\The [src] has no means of propulsion!")) + next_mecha_move = world.time + 3 // Just to stop them from getting spammed with messages. + return + + if(!legs.can_strafe) + if(user) + to_chat(user, SPAN_WARNING("Your motivators are not capable of strafing!")) + next_mecha_move = world.time + 15 + return + + if(!legs.motivator || legs.total_damage != 0 && legs.total_damage >= legs.max_damage) + if(user) + to_chat(user, SPAN_WARNING("Your motivators are destroyed! You can't strafe!")) + next_mecha_move = world.time + 15 + return + + if(maintenance_protocols) + if(user) + to_chat(user, SPAN_WARNING("Maintenance protocols are in effect.")) + return + + var/obj/item/cell/C = get_cell() + if(!C || !C.check_charge(legs.power_use * CELLRATE)) + if(user) + to_chat(user, SPAN_WARNING("The power indicator flashes briefly.")) + return + + next_mecha_move = world.time + ((legs.move_delay + (legs.damaged_delay * (legs.total_damage / legs.max_damage))) * legs.strafe_delay_modifier) return TRUE /mob/living/heavy_vehicle/get_standard_pixel_x() diff --git a/code/modules/heavy_vehicle/mech_interaction.dm b/code/modules/heavy_vehicle/mech_interaction.dm index b7f251cae55..8698fb1ad7a 100644 --- a/code/modules/heavy_vehicle/mech_interaction.dm +++ b/code/modules/heavy_vehicle/mech_interaction.dm @@ -228,6 +228,8 @@ user.forceMove(src) LAZYDISTINCTADD(pilots, user) RegisterSignal(user, COMSIG_MOB_FACEDIR, PROC_REF(handle_user_turn)) + RegisterSignal(user, COMSIG_INPUT_KEY_QUICK_EQUIP, PROC_REF(strafe_left)) + RegisterSignal(user, COMSIG_INPUT_KEY_DROP, PROC_REF(strafe_right)) playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) if(user.client) user.client.screen |= hud_elements LAZYDISTINCTADD(user.additional_vision_handlers, src) @@ -260,55 +262,105 @@ set_intent(I_HURT) LAZYREMOVE(pilots, user) UnregisterSignal(user, COMSIG_MOB_FACEDIR) + UnregisterSignal(user, COMSIG_INPUT_KEY_QUICK_EQUIP) + UnregisterSignal(user, COMSIG_INPUT_KEY_DROP) UNSETEMPTY(pilots) /mob/living/heavy_vehicle/proc/handle_user_turn(var/mob/living/user, var/direction) SIGNAL_HANDLER INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, relaymove), user, direction, TRUE) +/mob/living/heavy_vehicle/proc/strafe_left(var/mob/user, var/cancelled) + SIGNAL_HANDLER + // Stop the pilot from attempting to drop the item in their hands, we're replacing it with a strafe input. + *cancelled = TRUE + strafe_move(user, angle2dir(dir2angle(dir) + 90)) + +/mob/living/heavy_vehicle/proc/strafe_right(var/mob/user, var/cancelled) + SIGNAL_HANDLER + // Stop the pilot from attempting to drop the item in their hands, we're replacing it with a strafe input. + *cancelled = TRUE + strafe_move(user, angle2dir(dir2angle(dir) + 270)) + /mob/living/heavy_vehicle/relaymove(mob/living/user, direction, var/turn_only = FALSE) . = ..() - if(!can_move(user)) - return - if(hallucination >= EMP_MOVE_DISRUPT && prob(30)) direction = pick(GLOB.cardinals) - var/do_strafe = !isnull(user.facing_dir) && (legs.turn_delay <= legs.move_delay) - if(!do_strafe && dir != direction) + // Convert keyboard inputs to Battletech-style controls. + switch (direction) + if (NORTH) // "Throttle Forwards" + throttle_move(user, dir, FALSE) + if (SOUTH) // "Throttle Reverse" + throttle_move(user, angle2dir(dir2angle(dir) + 180), TRUE) + if (EAST) // "Turn Right" + rotate_by_angle(user, angle2dir(dir2angle(dir) + 90)) + if (WEST) // "Turn Left" + rotate_by_angle(user, angle2dir(dir2angle(dir) + 270)) + +/mob/living/heavy_vehicle/proc/throttle_move(mob/living/user, direction, reverse) + if (!legs || !can_move(user)) + return + + // Get the tile in the direction. + var/turf/target_loc = get_step(src, direction) + if(!legs.can_move_on(loc, target_loc)) + return + + if (reverse) + next_mecha_move += legs.reverse_delay + + // Then send a move command + if(incorporeal_move) + if(legs.mech_step_sound) + playsound(src.loc,legs.mech_step_sound,40,1) use_cell_power(legs.power_use * CELLRATE) - if(legs && legs.mech_turn_sound) - playsound(src.loc,legs.mech_turn_sound,40,1) - if(world.time + legs.turn_delay > next_mecha_move) - next_mecha_move = world.time + legs.turn_delay - set_dir(direction) - for(var/mob/pilot in pilots) - pilot.set_dir(direction) - if(istype(hardpoints[HARDPOINT_BACK], /obj/item/mecha_equipment/shield)) - var/obj/item/mecha_equipment/shield/S = hardpoints[HARDPOINT_BACK] - if(S.aura) - S.aura.dir = direction - if(S.aura.dir == NORTH) - S.aura.layer = MOB_LAYER - else - S.aura.layer = ABOVE_HUMAN_LAYER - update_icon() + user.client.Process_Incorpmove(direction, src) + else + Move(target_loc, direction, 0, FALSE) - if(!turn_only) - var/turf/target_loc = get_step(src, direction) - if(!legs.can_move_on(loc, target_loc)) - return - if(incorporeal_move) - if(legs && legs.mech_step_sound) - playsound(src.loc,legs.mech_step_sound,40,1) - use_cell_power(legs.power_use * CELLRATE) - user.client.Process_Incorpmove(direction, src) - else - var/new_direction = do_strafe ? user.facing_dir || direction : direction - Move(target_loc, new_direction) +/mob/living/heavy_vehicle/proc/strafe_move(mob/user, direction) + if (!legs || !can_strafe(user)) + return -/mob/living/heavy_vehicle/Move() + // Get the tile in the direction. + var/turf/target_loc = get_step(src, direction) + if(!legs.can_move_on(loc, target_loc)) + return + + // Then send a move command + if(incorporeal_move) + if(legs.mech_step_sound) + playsound(src.loc,legs.mech_step_sound,40,1) + use_cell_power(legs.power_use * CELLRATE) + user.client.Process_Incorpmove(direction, src) + else + Move(target_loc, direction, 0, FALSE) + +/mob/living/heavy_vehicle/proc/rotate_by_angle(mob/living/user, direction) + if (!legs || !can_turn(user)) + return + + use_cell_power(legs.power_use * CELLRATE) + if(legs && legs.mech_turn_sound) + playsound(src.loc,legs.mech_turn_sound,40,1) + + set_dir(direction) + for(var/mob/pilot in pilots) + pilot.set_dir(direction) + if(istype(hardpoints[HARDPOINT_BACK], /obj/item/mecha_equipment/shield)) + var/obj/item/mecha_equipment/shield/S = hardpoints[HARDPOINT_BACK] + if(S.aura) + S.aura.dir = direction + if(S.aura.dir == NORTH) + S.aura.layer = MOB_LAYER + else + S.aura.layer = ABOVE_HUMAN_LAYER + update_icon() + Move(src.loc, direction, 0, TRUE) + +/mob/living/heavy_vehicle/Move(atom/newloc, direct, glide_size_override = 0, update_dir = TRUE) . = ..() if(. && !istype(loc, /turf/space)) if(legs) diff --git a/code/modules/heavy_vehicle/mecha.dm b/code/modules/heavy_vehicle/mecha.dm index f88ec5c4d7e..2fb44bb4330 100644 --- a/code/modules/heavy_vehicle/mecha.dm +++ b/code/modules/heavy_vehicle/mecha.dm @@ -76,7 +76,16 @@ var/use_air = FALSE // Interface stuff. + + /// The next world tick that the mech has to wait for before it can change its Throttle (forward and backward movement). var/next_mecha_move = 0 + + /// The next world tick that the mech has to wait for before it can turn. + var/next_mecha_turn = 0 + + /// The next world tick that the mech has to wait for before it can strafe. + var/next_mecha_strafe = 0 + var/list/hud_elements = list() var/list/hardpoint_hud_elements = list() var/atom/movable/screen/mecha/health/hud_health diff --git a/code/modules/heavy_vehicle/premade/hoverpod.dm b/code/modules/heavy_vehicle/premade/hoverpod.dm index 80c13625529..4c21fe4da72 100644 --- a/code/modules/heavy_vehicle/premade/hoverpod.dm +++ b/code/modules/heavy_vehicle/premade/hoverpod.dm @@ -8,6 +8,8 @@ max_damage = 150 move_delay = 5 turn_delay = 2 + strafe_delay_modifier = 1 // No delay on strafing. + reverse_delay = 10 power_use = 2500 trample_damage = 0 hover = TRUE @@ -20,4 +22,6 @@ max_damage = 75 move_delay = 2 turn_delay = 2 + strafe_delay_modifier = 1 // No delay on strafing. + reverse_delay = 5 power_use = 2000 diff --git a/code/modules/heavy_vehicle/premade/misc.dm b/code/modules/heavy_vehicle/premade/misc.dm index cad77053eb8..30c08fb845c 100644 --- a/code/modules/heavy_vehicle/premade/misc.dm +++ b/code/modules/heavy_vehicle/premade/misc.dm @@ -6,6 +6,8 @@ max_damage = 160 move_delay = 4 turn_delay = 1 + reverse_delay = 2 // Faster than usual reverse for legs + strafe_delay_modifier = 1.2 // Faster than usual strafing power_use = 1250 trample_damage = 10 @@ -17,6 +19,7 @@ max_damage = 250 move_delay = 5 turn_delay = 1 + strafe_delay_modifier = 1.2 // Faster than usual strafing power_use = 3500 trample_damage = 25 @@ -28,7 +31,9 @@ max_damage = 450 move_delay = 2 //Its fast turn_delay = 7 + reverse_delay = 0 // Uniquely the only chassis without a reverse delay. power_use = 3500 color = COLOR_WHITE mech_step_sound = 'sound/mecha/tanktread.ogg' trample_damage = 25 + can_strafe = FALSE diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index ea0dddcfdba..8f97826f736 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -7,6 +7,11 @@ This saves us from having to call add_fingerprint() any time something is put in set name = "quick-equip" set hidden = 1 + var/cancelled = FALSE + SEND_SIGNAL(src, COMSIG_INPUT_KEY_QUICK_EQUIP, &cancelled) + if (cancelled) + return + if(ishuman(src)) var/mob/living/carbon/human/H = src var/obj/item/I = H.get_active_hand() diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 3bbaa730632..a566eb1b1e3 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -52,6 +52,9 @@ diagonal_action(SOUTHWEST) /client/proc/diagonal_action(direction) + if (!mob) + return + switch(client_dir(direction, 1)) if(NORTHEAST) swap_hand() @@ -67,6 +70,11 @@ to_chat(usr, SPAN_WARNING("This mob type cannot throw items.")) return if(NORTHWEST) + var/cancelled = FALSE + SEND_SIGNAL(mob, COMSIG_INPUT_KEY_DROP, &cancelled) + if (cancelled) + return + if(iscarbon(usr)) var/mob/living/carbon/C = usr if(!C.get_active_hand()) diff --git a/html/changelogs/hellfirejag - mech control rework.yml b/html/changelogs/hellfirejag - mech control rework.yml new file mode 100644 index 00000000000..afc225aae85 --- /dev/null +++ b/html/changelogs/hellfirejag - mech control rework.yml @@ -0,0 +1,6 @@ +author: Hellfirejag +delete-after: True +changes: + - rscadd: "Reworked mech controls to be Battletech-style. W and S are Forward and Reverse throttle, while A and D rotate the mech left and right. Mechs have different forward and reverse speeds based on their chassis. When reversing, your faced direction doesn't change." + - rscadd: "Reworked leg damage for mechs. A mech's movement speeds are reduced by up to a certain amount based on their percentage of damage taken. Larger legs can generally take more of a beating before they slow down too much." + - rscadd: "Mechs can now strafe left and right with the Q and E keys respectively."