diff --git a/code/__DEFINES/dcs/signals/signals_ladder.dm b/code/__DEFINES/dcs/signals/signals_ladder.dm new file mode 100644 index 00000000000..ed398e9012e --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_ladder.dm @@ -0,0 +1,3 @@ +/// Called on a mob attempting to use a ladder to go in either direction. (entrance_ladder, exit_ladder, going_up) +#define COMSIG_LADDER_TRAVEL "ladder-travel" + #define LADDER_TRAVEL_BLOCK (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_vehicle.dm b/code/__DEFINES/dcs/signals/signals_vehicle.dm new file mode 100644 index 00000000000..1717943a81a --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_vehicle.dm @@ -0,0 +1,4 @@ +/// Called on a mob when they start riding a vehicle (obj/vehicle) +#define COMSIG_VEHICLE_RIDDEN "vehicle-ridden" + /// Return this to signal that the mob should be removed from the vehicle + #define EJECT_FROM_VEHICLE (1<<0) diff --git a/code/datums/components/hazard_area.dm b/code/datums/components/hazard_area.dm new file mode 100644 index 00000000000..e29ed529600 --- /dev/null +++ b/code/datums/components/hazard_area.dm @@ -0,0 +1,130 @@ +/** + * This is a relatively simple component that attempts to deter the parent of the component away + * from a specific area or areas. By default it simply applies a penalty where all movement is + * four times slower than usual and any action that would affect your 'next move' has a penalty + * multiplier of 4 attached. + */ +/datum/component/hazard_area + /// The blacklist of areas that the parent will be penalized for entering + var/list/area_blacklist + /// The whitelist of areas that the parent is allowed to be in. If set this overrides the blacklist + var/list/area_whitelist + /// A variable storing the typepath of the last checked area to prevent any further logic running if it has not changed + VAR_PRIVATE/last_parent_area + +/datum/component/hazard_area/Initialize(area_blacklist, area_whitelist) + . = ..() + if(!ismob(parent)) + return COMPONENT_INCOMPATIBLE + if(!islist(area_blacklist) && !islist(area_whitelist)) + stack_trace("[type] - neither area_blacklist nor area_whitelist were provided.") + return COMPONENT_INCOMPATIBLE + src.area_blacklist = area_blacklist + src.area_whitelist = area_whitelist + +/datum/component/hazard_area/RegisterWithParent() + var/mob/parent_mob = parent + parent_mob.become_area_sensitive(type) + RegisterSignal(parent_mob, COMSIG_ENTER_AREA, .proc/handle_parent_area_change) + RegisterSignal(parent_mob, COMSIG_LADDER_TRAVEL, .proc/reject_ladder_movement) + RegisterSignal(parent_mob, COMSIG_VEHICLE_RIDDEN, .proc/reject_vehicle) + +/datum/component/hazard_area/UnregisterFromParent() + var/mob/parent_mob = parent + UnregisterSignal(parent_mob, list(COMSIG_ENTER_AREA, COMSIG_LADDER_TRAVEL, COMSIG_VEHICLE_RIDDEN)) + parent_mob.lose_area_sensitivity(type) + +/** + * This signal handler checks the area the target ladder is in and if hazardous prevents them from using it + */ +/datum/component/hazard_area/proc/reject_ladder_movement(mob/source, obj/entrance_ladder, exit_ladder, going_up) + SIGNAL_HANDLER + + if(check_area_hazardous(get_area(exit_ladder))) + entrance_ladder.balloon_alert(parent, "the path is too dangerous for you!") + return LADDER_TRAVEL_BLOCK + +/** + * A simple signal handler that informs the parent they cannot ride a vehicle and ejects them + */ +/datum/component/hazard_area/proc/reject_vehicle(mob/source, obj/vehicle/vehicle) + SIGNAL_HANDLER + + if(!check_area_hazardous(last_parent_area)) + return + + vehicle.balloon_alert(parent, "you slip and fall off!") + var/mob/living/parent_living = parent + parent_living.Stun(0.5 SECONDS) + return EJECT_FROM_VEHICLE + +/** + * Checks if the area being checked is considered hazardous + * The whitelist is checked first if it exists, otherwise it checks if it is in the blacklist + * + * * checking - This should be the typepath of the area being checked, but there is a conversion handler if you pass in a reference instead + */ +/datum/component/hazard_area/proc/check_area_hazardous(area/checking) + if(!ispath(checking)) + checking = checking.type + if(area_whitelist) + return !(checking in area_whitelist) + return checking in area_blacklist + +/** + * This proc handles the status effect applied to the parent, most noteably applying or removing it as required + */ +/datum/component/hazard_area/proc/update_parent_status_effect() + if(QDELETED(parent)) + return + + var/mob/living/parent_living = parent + var/datum/status_effect/hazard_area/effect = parent_living.has_status_effect(/datum/status_effect/hazard_area) + var/should_have_status_effect = check_area_hazardous(last_parent_area) + + if(should_have_status_effect && !effect) // Should have the status - and doesnt + parent_living.apply_status_effect(/datum/status_effect/hazard_area) + if(parent_living.buckled) + parent_living.buckled.balloon_alert(parent, "you fall off!") + parent_living.buckled.unbuckle_mob(parent_living, force=TRUE) + return + + if(!should_have_status_effect && effect) // Shouldn't have the status - and does + parent_living.remove_status_effect(/datum/status_effect/hazard_area) + +/** + * This signal should be called whenever our parent moves. + */ +/datum/component/hazard_area/proc/handle_parent_area_change(mob/source, area/new_area) + SIGNAL_HANDLER + + if(new_area.type == last_parent_area) + return + last_parent_area = new_area.type + + INVOKE_ASYNC(src, .proc/update_parent_status_effect) + +/// The dedicated status effect for the hazard_area component - use with caution and know what it does! +/datum/status_effect/hazard_area + id = "hazard_area" + examine_text = "SUBJECTPRONOUN appears to be largely immobilized through unknown means." + status_type = STATUS_EFFECT_UNIQUE + alert_type = /atom/movable/screen/alert/status_effect/hazard_area + +/datum/status_effect/hazard_area/nextmove_modifier() + return 4 + +/datum/status_effect/hazard_area/on_apply() + . = ..() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/hazard_area, update=TRUE) + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/status_effect/hazard_area, update=TRUE) + +/datum/status_effect/hazard_area/on_remove() + . = ..() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/hazard_area, update=TRUE) + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/status_effect/hazard_area, update=TRUE) + +/atom/movable/screen/alert/status_effect/hazard_area + name = "Hazardous Area" + desc = "The area you are currently within is incredibly hazardous to you. Check your surroudings and vacate as soon as possible." + icon_state = "hazard_area" diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index ae4b282ef86..e473ac08390 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -71,6 +71,10 @@ qdel(src) /obj/structure/ladder/proc/travel(going_up, mob/user, is_ghost, obj/structure/ladder/ladder) + var/response = SEND_SIGNAL(user, COMSIG_LADDER_TRAVEL, src, ladder, going_up) + if(response & LADDER_TRAVEL_BLOCK) + return + if(!is_ghost) ladder.add_fingerprint(user) if(!do_after(user, travel_time, target = src)) diff --git a/code/modules/actionspeed/modifiers/status_effects.dm b/code/modules/actionspeed/modifiers/status_effects.dm index 65153e08933..a6f812890ef 100644 --- a/code/modules/actionspeed/modifiers/status_effects.dm +++ b/code/modules/actionspeed/modifiers/status_effects.dm @@ -6,3 +6,6 @@ /datum/actionspeed_modifier/nooartrium multiplicative_slowdown = 0.5 + +/datum/actionspeed_modifier/status_effect/hazard_area + multiplicative_slowdown = 4 diff --git a/code/modules/mob_spawn/ghost_roles/golem_roles.dm b/code/modules/mob_spawn/ghost_roles/golem_roles.dm index 8f85972f895..b7fe63bc170 100644 --- a/code/modules/mob_spawn/ghost_roles/golem_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/golem_roles.dm @@ -52,6 +52,10 @@ if (policy) to_chat(new_spawn, policy) to_chat(new_spawn, "Build golem shells in the autolathe, and feed refined mineral sheets to the shells to bring them to life! You are generally a peaceful group unless provoked.") + var/static/list/allowed_areas + if(!allowed_areas) + allowed_areas = typecacheof(list(/area/icemoon, /area/lavaland, /area/ruin)) + new_spawn.AddComponent(/datum/component/hazard_area, area_whitelist=allowed_areas) else new_spawn.mind.enslave_mind_to_creator(owner) log_game("[key_name(new_spawn)] possessed a golem shell enslaved to [key_name(owner)].") diff --git a/code/modules/movespeed/modifiers/status_effects.dm b/code/modules/movespeed/modifiers/status_effects.dm index 40339ed26f1..2d3d69117e1 100644 --- a/code/modules/movespeed/modifiers/status_effects.dm +++ b/code/modules/movespeed/modifiers/status_effects.dm @@ -16,3 +16,6 @@ variable = TRUE blacklisted_movetypes = (FLYING|FLOATING) +/datum/movespeed_modifier/status_effect/hazard_area + multiplicative_slowdown = 4 + diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm index 3246b582dbc..b03229d283e 100644 --- a/code/modules/vehicles/ridden.dm +++ b/code/modules/vehicles/ridden.dm @@ -58,6 +58,11 @@ /obj/vehicle/ridden/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE) if(!force && occupant_amount() >= max_occupants) return FALSE + + var/response = SEND_SIGNAL(M, COMSIG_VEHICLE_RIDDEN, src) + if(response & EJECT_FROM_VEHICLE) + return FALSE + return ..() /obj/vehicle/ridden/zap_act(power, zap_flags) diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index 57e3219a537..de0c95a4087 100755 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/tgstation.dme b/tgstation.dme index e299ac439c1..a94ba129062 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -212,6 +212,7 @@ #include "code\__DEFINES\dcs\signals\signals_heretic.dm" #include "code\__DEFINES\dcs\signals\signals_hydroponic.dm" #include "code\__DEFINES\dcs\signals\signals_janitor.dm" +#include "code\__DEFINES\dcs\signals\signals_ladder.dm" #include "code\__DEFINES\dcs\signals\signals_light_eater.dm" #include "code\__DEFINES\dcs\signals\signals_medical.dm" #include "code\__DEFINES\dcs\signals\signals_mind.dm" @@ -240,6 +241,7 @@ #include "code\__DEFINES\dcs\signals\signals_transform.dm" #include "code\__DEFINES\dcs\signals\signals_turf.dm" #include "code\__DEFINES\dcs\signals\signals_twohand.dm" +#include "code\__DEFINES\dcs\signals\signals_vehicle.dm" #include "code\__DEFINES\dcs\signals\signals_wash.dm" #include "code\__DEFINES\dcs\signals\signals_xeno_control.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" @@ -714,6 +716,7 @@ #include "code\datums\components\gps.dm" #include "code\datums\components\grillable.dm" #include "code\datums\components\gunpoint.dm" +#include "code\datums\components\hazard_area.dm" #include "code\datums\components\heirloom.dm" #include "code\datums\components\holderloving.dm" #include "code\datums\components\igniter.dm"