Merge remote-tracking branch 'tgstation/master' into upstream-2025-11-29

# Conflicts:
#	_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm
#	_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	_maps/map_files/tramstation/tramstation.dmm
#	code/_onclick/hud/new_player.dm
#	code/datums/components/squashable.dm
#	code/datums/diseases/advance/symptoms/heal.dm
#	code/datums/diseases/chronic_illness.dm
#	code/datums/status_effects/buffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/stamcrit.dm
#	code/game/machinery/computer/crew.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/wall_mounted.dm
#	code/game/turfs/closed/indestructible.dm
#	code/modules/admin/view_variables/filterrific.dm
#	code/modules/antagonists/heretic/influences.dm
#	code/modules/cargo/orderconsole.dm
#	code/modules/client/preferences.dm
#	code/modules/events/space_vines/vine_mutations.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/carbon/human/death.dm
#	code/modules/mob/living/carbon/human/species_types/jellypeople.dm
#	code/modules/mob/living/damage_procs.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mob_spawn/mob_spawn.dm
#	code/modules/projectiles/ammunition/energy/laser.dm
#	code/modules/projectiles/guns/ballistic/launchers.dm
#	code/modules/projectiles/guns/energy/laser.dm
#	code/modules/reagents/chemistry/machinery/chem_dispenser.dm
#	code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/medicine_reagents.dm
#	code/modules/surgery/healing.dm
#	code/modules/unit_tests/designs.dm
#	icons/mob/inhands/items_lefthand.dmi
#	icons/mob/inhands/items_righthand.dmi
#	tgui/packages/tgui/interfaces/ChemDispenser.tsx
This commit is contained in:
nevimer
2025-11-29 22:49:21 -05:00
1185 changed files with 39068 additions and 32028 deletions
+3
View File
@@ -457,6 +457,9 @@
var/c_type = target.type
qdel(target)
CRASH("Incompatible [c_type] transfer attempt to a [type]!")
if(COMPONENT_NOTRANSFER)
qdel(target)
return
AddComponent(target)
if(!QDELETED(target))
+4 -2
View File
@@ -395,8 +395,8 @@
///Check if an offspring of two fish (or one if self-reproducing) can evolve.
/datum/component/aquarium/proc/check_evolution(atom/movable/source, obj/item/fish/fish, obj/item/fish/mate, datum/fish_evolution/evolution)
SIGNAL_HANDLER
//chances are halved if only one parent has this evolution.
var/real_probability = (mate && (evolution.type in mate.evolution_types)) ? evolution.probability : evolution.probability * 0.5
//chances are halved if it isn't asexual reproduction and one parent doesn't have the evolution.
var/real_probability = (isnull(mate) || (evolution.type in mate.evolution_types)) ? evolution.probability : evolution.probability * 0.5
if(HAS_TRAIT(fish, TRAIT_FISH_MUTAGENIC) || (mate && HAS_TRAIT(mate, TRAIT_FISH_MUTAGENIC)))
real_probability *= 3
if(!prob(real_probability))
@@ -697,6 +697,8 @@
check_fluid_and_temperature(fish)
/datum/component/aquarium/proc/admire(atom/movable/source, mob/living/user)
if(!isliving(user))
return
source.balloon_alert(user, "admiring aquarium...")
if(!do_after(user, 5 SECONDS, target = source))
return
+7 -8
View File
@@ -128,18 +128,18 @@
new /obj/effect/temp_visual/heal(get_turf(candidate), healing_color)
if (iscarbon(candidate) || issilicon(candidate) || isbasicmob(candidate))
candidate.adjustBruteLoss(-brute_heal * seconds_per_tick, updating_health = FALSE)
candidate.adjustFireLoss(-burn_heal * seconds_per_tick, updating_health = FALSE)
candidate.adjust_brute_loss(-brute_heal * seconds_per_tick, updating_health = FALSE)
candidate.adjust_fire_loss(-burn_heal * seconds_per_tick, updating_health = FALSE)
if (iscarbon(candidate))
// Toxin healing is forced for slime people
candidate.adjustToxLoss(-toxin_heal * seconds_per_tick, updating_health = FALSE, forced = TRUE)
candidate.adjust_tox_loss(-toxin_heal * seconds_per_tick, updating_health = FALSE, forced = TRUE)
candidate.adjustOxyLoss(-suffocation_heal * seconds_per_tick, updating_health = FALSE)
candidate.adjustStaminaLoss(-stamina_heal * seconds_per_tick, updating_stamina = FALSE)
candidate.adjust_oxy_loss(-suffocation_heal * seconds_per_tick, updating_health = FALSE)
candidate.adjust_stamina_loss(-stamina_heal * seconds_per_tick, updating_stamina = FALSE)
for (var/organ in organ_healing)
candidate.adjustOrganLoss(organ, -organ_healing[organ] * seconds_per_tick)
candidate.adjust_organ_loss(organ, -organ_healing[organ] * seconds_per_tick)
var/mob/living/carbon/carbidate = candidate
for(var/datum/wound/iter_wound as anything in carbidate.all_wounds)
iter_wound.adjust_blood_flow(-wound_clotting * seconds_per_tick)
@@ -151,8 +151,7 @@
var/mob/living/basic/basic_candidate = candidate
basic_candidate.adjust_health(-simple_heal * seconds_per_tick, updating_health = FALSE)
if (candidate.blood_volume < BLOOD_VOLUME_NORMAL)
candidate.blood_volume += blood_heal * seconds_per_tick
candidate.adjust_blood_volume(blood_heal * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
candidate.updatehealth()
+5 -3
View File
@@ -49,6 +49,8 @@
RegisterSignal(overmind, COMSIG_QDELETING, PROC_REF(overmind_deleted))
RegisterSignal(overmind, COMSIG_BLOB_SELECTED_STRAIN, PROC_REF(strain_properties_changed))
strain_properties_changed(overmind, overmind.blobstrain)
var/mob/living_parent = parent
living_parent.pass_flags |= PASSBLOB
/// Our overmind is gone, uh oh!
/datum/component/blob_minion/proc/overmind_deleted()
@@ -70,7 +72,6 @@
/datum/component/blob_minion/RegisterWithParent()
var/mob/living/living_parent = parent
living_parent.pass_flags |= PASSBLOB
living_parent.faction |= ROLE_BLOB
ADD_TRAIT(parent, TRAIT_BLOB_ALLY, REF(src))
remove_verb(parent, /mob/living/verb/pulled) // No dragging people into the blob
@@ -113,6 +114,7 @@
COMSIG_HOSTILE_PRE_ATTACKINGTARGET,
))
GLOB.blob_telepathy_mobs -= parent
living_parent.pass_flags &= ~PASSBLOB
/// Become blobpilled when we gain a mind
/datum/component/blob_minion/proc/on_mind_init(mob/living/minion, datum/mind/new_mind)
@@ -152,9 +154,9 @@
/datum/component/blob_minion/proc/on_burned(mob/living/minion, exposed_temperature, exposed_volume)
SIGNAL_HANDLER
if(isnull(exposed_temperature))
minion.adjustFireLoss(5)
minion.adjust_fire_loss(5)
return
minion.adjustFireLoss(clamp(0.01 * exposed_temperature, 1, 5))
minion.adjust_fire_loss(clamp(0.01 * exposed_temperature, 1, 5))
/// Someone is attempting to move through us, allow it if it is a blob tile
/datum/component/blob_minion/proc/on_attempted_pass(mob/living/minion, atom/movable/incoming)
+2 -5
View File
@@ -12,15 +12,12 @@
///List of callback timers, used to clear out mobs listening to boss music after `track_duration`.
var/list/music_callbacks = list()
/datum/component/boss_music/Initialize(
boss_track,
track_duration,
)
/datum/component/boss_music/Initialize(boss_track)
. = ..()
if(!ishostile(parent))
return COMPONENT_INCOMPATIBLE
src.boss_track = boss_track
src.track_duration = track_duration
track_duration = SSsounds.get_sound_length(boss_track)
/datum/component/boss_music/Destroy(force)
. = ..()
+3 -1
View File
@@ -42,6 +42,7 @@
RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_stopped))
RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_no_longer_stopped))
target_turf = target
ADD_TRAIT(parent, TRAIT_AI_AVOID_TURF, REF(src))
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_ENTERED, PROC_REF(entered))
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(exited))
RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on))
@@ -53,6 +54,7 @@
parent.AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/chasm])
/datum/component/chasm/UnregisterFromParent()
REMOVE_TRAIT(parent, TRAIT_AI_AVOID_TURF, REF(src))
storage = null
/datum/component/chasm/proc/entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
@@ -161,7 +163,7 @@
if(isliving(dropped_thing))
var/mob/living/fallen = dropped_thing
fallen.Paralyze(100)
fallen.adjustBruteLoss(30)
fallen.adjust_brute_loss(30)
falling_atoms -= falling_ref
return
@@ -110,7 +110,7 @@
/datum/crafting_recipe/xraylaser
name = "X-ray Laser Gun"
result = /obj/item/gun/energy/xray
result = /obj/item/gun/energy/laser/xray
reqs = list(
/obj/item/gun/energy/laser = 1,
/obj/item/weaponcrafting/gunkit/xray = 1,
+6 -4
View File
@@ -300,12 +300,14 @@
return
our_turf = get_turf(cultist) //we may have moved. adjust as needed...
var/can_have_blood = CAN_HAVE_BLOOD(cultist)
cultist.visible_message(
span_warning("[cultist] [cultist.blood_volume ? "cuts open [cultist.p_their()] arm and begins writing in [cultist.p_their()] own blood":"begins sketching out a strange design"]!"),
span_cult("You [cultist.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.")
span_warning("[cultist] [can_have_blood ? "cuts open [cultist.p_their()] arm and begins writing in [cultist.p_their()] own blood":"begins sketching out a strange design"]!"),
span_cult("You [can_have_blood ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.")
)
if(cultist.blood_volume)
if(can_have_blood)
cultist.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(GLOB.arm_zones), wound_bonus = CANT_WOUND) // *cuts arm* *bone explodes* ever have one of those days?
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
@@ -332,7 +334,7 @@
return FALSE
cultist.visible_message(
span_warning("[cultist] creates a strange circle[cultist.blood_volume ? " in [cultist.p_their()] own blood":""]."),
span_warning("[cultist] creates a strange circle[can_have_blood ? " in [cultist.p_their()] own blood":""]."),
span_cult("You finish drawing the arcane markings of the Geometer.")
)
+15 -15
View File
@@ -97,13 +97,14 @@
/// What effect the damage aura has if it has an owner.
/datum/component/damage_aura/proc/owner_effect(mob/living/owner_mob, seconds_per_tick)
var/need_mob_update = FALSE
need_mob_update += owner_mob.adjustStaminaLoss(-20 * seconds_per_tick, updating_stamina = FALSE)
need_mob_update += owner_mob.adjustBruteLoss(-1 * seconds_per_tick, updating_health = FALSE)
need_mob_update += owner_mob.adjustFireLoss(-1 * seconds_per_tick, updating_health = FALSE)
need_mob_update += owner_mob.adjustToxLoss(-1 * seconds_per_tick, updating_health = FALSE, forced = TRUE)
need_mob_update += owner_mob.adjustOxyLoss(-1 * seconds_per_tick, updating_health = FALSE)
if (owner_mob.blood_volume < BLOOD_VOLUME_NORMAL)
owner_mob.blood_volume += 2 * seconds_per_tick
need_mob_update += owner_mob.adjust_stamina_loss(-20 * seconds_per_tick, updating_stamina = FALSE)
need_mob_update += owner_mob.adjust_brute_loss(-1 * seconds_per_tick, updating_health = FALSE)
need_mob_update += owner_mob.adjust_fire_loss(-1 * seconds_per_tick, updating_health = FALSE)
need_mob_update += owner_mob.adjust_tox_loss(-1 * seconds_per_tick, updating_health = FALSE, forced = TRUE)
need_mob_update += owner_mob.adjust_oxy_loss(-1 * seconds_per_tick, updating_health = FALSE)
owner_mob.adjust_blood_volume(2 * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
if(need_mob_update)
owner_mob.updatehealth()
@@ -134,16 +135,16 @@
to_chat(candidate, damage_message)
if (iscarbon(candidate) || issilicon(candidate) || isbasicmob(candidate))
candidate.adjustBruteLoss(brute_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjustFireLoss(burn_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjust_brute_loss(brute_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjust_fire_loss(burn_damage * seconds_per_tick, updating_health = FALSE)
if (iscarbon(candidate))
candidate.adjustToxLoss(toxin_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjustOxyLoss(suffocation_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjustStaminaLoss(stamina_damage * seconds_per_tick, updating_stamina = FALSE)
candidate.adjust_tox_loss(toxin_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjust_oxy_loss(suffocation_damage * seconds_per_tick, updating_health = FALSE)
candidate.adjust_stamina_loss(stamina_damage * seconds_per_tick, updating_stamina = FALSE)
for (var/organ in organ_damage)
candidate.adjustOrganLoss(organ, organ_damage[organ] * seconds_per_tick)
candidate.adjust_organ_loss(organ, organ_damage[organ] * seconds_per_tick)
else if (isanimal(candidate))
var/mob/living/simple_animal/animal_candidate = candidate
animal_candidate.adjustHealth(simple_damage * seconds_per_tick, updating_health = FALSE)
@@ -151,8 +152,7 @@
var/mob/living/basic/basic_candidate = candidate
basic_candidate.adjust_health(simple_damage * seconds_per_tick, updating_health = FALSE)
if (candidate.blood_volume > BLOOD_VOLUME_SURVIVE)
candidate.blood_volume -= blood_damage * seconds_per_tick
candidate.adjust_blood_volume(blood_damage * seconds_per_tick, minimum = BLOOD_VOLUME_SURVIVE)
candidate.updatehealth()
+7 -7
View File
@@ -48,9 +48,9 @@
if(isliving(parent))
var/mob/living/L = parent
tox_loss = L.getToxLoss()
oxy_loss = L.getOxyLoss()
stamina_loss = L.getStaminaLoss()
tox_loss = L.get_tox_loss()
oxy_loss = L.get_oxy_loss()
stamina_loss = L.get_stamina_loss()
brain_loss = L.get_organ_loss(ORGAN_SLOT_BRAIN)
rewind_type = PROC_REF(rewind_living)
@@ -99,10 +99,10 @@
parent.AddComponent(type, 1, rewind_interval, TRUE)
var/mob/living/master = parent
master.setToxLoss(tox_loss)
master.setOxyLoss(oxy_loss)
master.setStaminaLoss(stamina_loss)
master.setOrganLoss(ORGAN_SLOT_BRAIN, brain_loss)
master.set_tox_loss(tox_loss)
master.set_oxy_loss(oxy_loss)
master.set_stamina_loss(stamina_loss)
master.set_organ_loss(ORGAN_SLOT_BRAIN, brain_loss)
rewind()
/datum/component/dejavu/proc/rewind_carbon()
+7 -5
View File
@@ -1,14 +1,16 @@
///Whenever get_ear_protection() is called, this proc adds 'protection_value' to the wearer's natural ear protection.
/datum/component/wearertargeting/earprotection
signals = list(COMSIG_LIVING_GET_EAR_PROTECTION)
mobtype = /mob/living/carbon
proctype = PROC_REF(reducebang)
var/reduce_amount = 1
//positive amount indicating how much protection this gives
var/protection_amount = 1
valid_slots = ITEM_SLOT_EARS | ITEM_SLOT_HEAD
/datum/component/wearertargeting/earprotection/Initialize(reduce_amount = 1)
/datum/component/wearertargeting/earprotection/Initialize(protection_amount = EAR_PROTECTION_NORMAL)
. = ..()
if(reduce_amount)
src.reduce_amount = reduce_amount
src.protection_amount = protection_amount
/datum/component/wearertargeting/earprotection/proc/reducebang(datum/source, list/reflist)
reflist[EAR_PROTECTION_ARG] -= reduce_amount
SIGNAL_HANDLER
reflist[EAR_PROTECTION_ARG] += protection_amount
+3 -5
View File
@@ -106,17 +106,15 @@
if(HAS_TRAIT(echolocator, TRAIT_ECHOLOCATION_EXTRA_RANGE))
real_echo_range += 2
var/list/filtered = list()
var/list/seen = dview(real_echo_range, get_turf(echolocator.client?.eye || echolocator), invis_flags = echolocator.see_invisible)
if(blinding)
for(var/atom/seen_atom as anything in seen)
for(var/atom/seen_atom as anything in dview(real_echo_range, get_turf(echolocator.client?.eye || echolocator), invis_flags = echolocator.see_invisible))
if(!seen_atom.alpha)
continue
if(allowed_paths[seen_atom.type])
filtered += seen_atom
else
var/list/ranged_atoms = range(real_echo_range, get_turf(echolocator.client?.eye || echolocator))
for(var/atom/possible_atom as anything in ranged_atoms)
if(!possible_atom.alpha)
for(var/atom/possible_atom as anything in range(real_echo_range, get_turf(echolocator.client?.eye || echolocator)))
if(!possible_atom.alpha || possible_atom.invisibility > echolocator.see_invisible)
continue
if(allowed_paths[possible_atom.type])
filtered += possible_atom
+2 -2
View File
@@ -57,9 +57,9 @@
var/t_are = target.p_are()
if((visible_info & EMPATH_SEE_COMBAT) && target.combat_mode)
examine_list += "[t_They] seem[p_s()] to be on guard."
if((visible_info & EMPATH_SEE_OXY) && target.getOxyLoss() >= 10)
if((visible_info & EMPATH_SEE_OXY) && target.get_oxy_loss() >= 10)
examine_list += "[t_They] seem[p_s()] winded."
if((visible_info & EMPATH_SEE_TOX) && target.getToxLoss() >= 10)
if((visible_info & EMPATH_SEE_TOX) && target.get_tox_loss() >= 10)
examine_list += "[t_They] seem[p_s()] sickly."
if((visible_info & EMPATH_SEE_SANITY) && target.mob_mood.sanity <= SANITY_DISTURBED)
examine_list += "[t_They] seem[p_s()] distressed."
+2 -1
View File
@@ -111,11 +111,12 @@
/mob/living/simple_animal,
)
for(var/type in mob_subtype_whitelist)
possible_mobtypes += subtypesof(type)
possible_mobtypes += valid_subtypesof(type)
var/list/mob_subtype_blacklist = list(
/mob/living/simple_animal/hostile/asteroid/elite,
/mob/living/simple_animal/hostile/megafauna,
/mob/living/basic/boss,
)
for(var/type in mob_subtype_blacklist)
possible_mobtypes -= subtypesof(type)
+1 -1
View File
@@ -66,7 +66,7 @@
return
if (terror_buildup < TERROR_BUILDUP_HEART_ATTACK || !prob(15))
owner.adjustOxyLoss(8)
owner.adjust_oxy_loss(8)
if (terror_buildup < TERROR_BUILDUP_FEAR)
to_chat(owner, span_warning("Your heart skips a beat."))
else
+3 -1
View File
@@ -287,7 +287,7 @@ Behavior that's still missing from this component that original food items had t
else
examine_list += span_notice("[owner] was bitten multiple times!")
if(GLOB.Debug2)
if(GLOB.debugging_enabled)
examine_list += span_notice("Reagent purities:")
for(var/datum/reagent/reagent as anything in owner.reagents.reagent_list)
examine_list += span_notice("- [reagent.name] [reagent.volume]u: [round(reagent.purity * 100)]% pure")
@@ -739,6 +739,8 @@ Behavior that's still missing from this component that original food items had t
///Ability to feed food to puppers
/datum/component/edible/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(QDELETED(parent))
return
SEND_SIGNAL(parent, COMSIG_FOOD_CROSSED, arrived, bitecount)
///Response to being used to customize something
+6 -6
View File
@@ -165,14 +165,14 @@
/datum/component/healing_touch/proc/has_healable_damage(mob/living/target)
if (!isnull(valid_biotypes) && !(valid_biotypes & target.mob_biotypes))
return FALSE
if (target.getStaminaLoss() > 0 && heal_stamina)
if (target.get_stamina_loss() > 0 && heal_stamina)
return TRUE
if (target.getOxyLoss() > 0 && heal_oxy)
if (target.get_oxy_loss() > 0 && heal_oxy)
return TRUE
if (target.getToxLoss() > 0 && heal_tox)
if (target.get_tox_loss() > 0 && heal_tox)
return TRUE
if (!iscarbon(target))
return (target.getBruteLoss() > 0 && heal_brute) || (target.getFireLoss() > 0 && heal_burn)
return (target.get_brute_loss() > 0 && heal_brute) || (target.get_fire_loss() > 0 && heal_burn)
var/mob/living/carbon/carbon_target = target
for (var/obj/item/bodypart/part in carbon_target.bodyparts)
if (!(part.brute_dam && heal_brute) && !(part.burn_dam && heal_burn))
@@ -207,8 +207,8 @@
required_bodytype = required_bodytype,
updating_health = FALSE,
)
healed += target.adjustOxyLoss(-heal_oxy * mult, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjustToxLoss(-heal_tox * mult, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjust_oxy_loss(-heal_oxy * mult, updating_health = FALSE, required_biotype = valid_biotypes)
healed += target.adjust_tox_loss(-heal_tox * mult, updating_health = FALSE, required_biotype = valid_biotypes)
if (healed <= 0)
return
+6 -6
View File
@@ -111,11 +111,11 @@
///Heart eater give also strong healing from hearts.
/datum/component/heart_eater/proc/healing_heart(mob/living/carbon/human/eater)
for(var/heal_organ in eater.organs)
eater.adjustOrganLoss(heal_organ, -50)
eater.adjust_organ_loss(heal_organ, -50)
for(var/datum/wound/heal_wound in eater.all_wounds)
heal_wound.remove_wound()
eater.adjustBruteLoss(-50)
eater.adjustFireLoss(-50)
eater.adjustToxLoss(-50)
eater.adjustOxyLoss(-50)
eater.adjustStaminaLoss(-50)
eater.adjust_brute_loss(-50)
eater.adjust_fire_loss(-50)
eater.adjust_tox_loss(-50)
eater.adjust_oxy_loss(-50)
eater.adjust_stamina_loss(-50)
@@ -0,0 +1,93 @@
/// Teleports interactors onto a safe turf randomly picked from a list of z-levels.
/datum/component/houlihan_teleport
dupe_mode = COMPONENT_DUPE_HIGHLANDER
/// Text that will appear in the alert prompt.
var/question = "Travel back?"
/// List of z-levels that the user can teleport to. By default, this is station z-levels.
var/list/zlevels
/datum/component/houlihan_teleport/Initialize(question, zlevels)
. = ..()
if(!isstructure(parent))
return COMPONENT_INCOMPATIBLE
if(!isnull(question) && (!istext(question) || !length(question)))
stack_trace("received bad question argument, falling back to default")
question = null
src.question = question || initial(src.question)
if(!isnull(zlevels) && (!islist(zlevels) || !length(zlevels)))
stack_trace("received bad zlevels argument, falling back to default")
zlevels = null
src.zlevels = zlevels || SSmapping.levels_by_trait(ZTRAIT_STATION)
/datum/component/houlihan_teleport/RegisterWithParent()
RegisterSignals(parent, list(COMSIG_ATOM_ATTACK_ROBOT, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_ANIMAL, COMSIG_ATOM_ATTACK_LARVA), PROC_REF(handle_generic_attack))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(handle_attack_hand))
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(handle_attackby))
/datum/component/houlihan_teleport/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_ATTACK_ROBOT, COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_ANIMAL, COMSIG_ATOM_ATTACK_LARVA, //handle_generic_attack
COMSIG_ATOM_ATTACK_HAND, //handle_attack_hand
COMSIG_ATOM_ATTACKBY, //handle_attackby
))
/datum/component/houlihan_teleport/proc/get_me_outta_here(obj/structure/source, mob/living/user)
var/said_yes = (tgui_alert(user, question, source.name, list("Yes", "No")) == "Yes")
if(!said_yes || !source.Adjacent(user))
return
var/turf/destination_turf = zlevels ? find_safe_turf(zlevels) : get_safe_random_station_turf_equal_weight()
if(!destination_turf)
source.balloon_alert(user, "uh oh...")
to_chat(user, span_warning("Nothing happens. You feel like this is a bad sign."))
return
var/turf/user_turf = get_turf(user)
var/atom/movable/dragged = user.pulling
user.forceMove(destination_turf)
user_turf.balloon_alert_to_viewers("(pop)")
if(dragged)
var/turf/dragged_turf = get_turf(dragged)
dragged.forceMove(destination_turf)
user.start_pulling(dragged, force = TRUE)
dragged_turf.balloon_alert_to_viewers("(pop)")
to_chat(list(user, dragged), span_notice("You blink and find yourself in <b>[get_area_name(destination_turf)]</b>."))
user.emote("blink")
astype(dragged, /mob)?.emote("blink") // shhhhh just let it happen
/datum/component/houlihan_teleport/proc/handle_generic_attack(obj/structure/source, mob/living/user, list/modifiers)
SIGNAL_HANDLER
if(user.combat_mode)
return NONE
if(iscyborg(user) && !source.Adjacent(user))
return NONE
INVOKE_ASYNC(src, PROC_REF(get_me_outta_here), source, user)
return COMPONENT_NO_AFTERATTACK
/datum/component/houlihan_teleport/proc/handle_attack_hand(obj/structure/source, mob/user, list/modifiers)
SIGNAL_HANDLER
if(!isliving(user))
return NONE
var/mob/living/living_user = user
if(living_user.combat_mode)
return NONE
INVOKE_ASYNC(src, PROC_REF(get_me_outta_here), source, living_user)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/houlihan_teleport/proc/handle_attackby(obj/structure/source, obj/item/item, mob/living/user, list/modifiers)
SIGNAL_HANDLER
if(user.combat_mode)
return NONE
INVOKE_ASYNC(src, PROC_REF(get_me_outta_here), source, user)
return COMPONENT_NO_AFTERATTACK
+2 -2
View File
@@ -101,7 +101,7 @@
return PROCESS_KILL
var/mob/living/carbon/human/human_parent = parent
if (human_parent.getToxLoss() == 0)
if (human_parent.get_tox_loss() == 0)
qdel(src)
return PROCESS_KILL
@@ -216,7 +216,7 @@
if (isliving(source))
var/mob/living/living_source = source
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated. Contamination traces back to roughly [DisplayTimeText(world.time - beginning_of_irradiation, 5)] ago. Current toxin levels: [living_source.getToxLoss()]."))
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Subject is irradiated. Contamination traces back to roughly [DisplayTimeText(world.time - beginning_of_irradiation, 5)] ago. Current toxin levels: [living_source.get_tox_loss()]."))
else
// In case the green wasn't obvious enough...
to_chat(user, span_bolddanger("[icon2html(geiger_counter, user)] Target is irradiated."))
+1 -1
View File
@@ -31,5 +31,5 @@
var/mob/living/overlord_to_damage = overlord_mob?.resolve()
if(!isnull(overlord_to_damage))
overlord_to_damage.adjustBruteLoss(previous_health_count - source.health) ///damage or heal overlord
overlord_to_damage.adjust_brute_loss(previous_health_count - source.health) ///damage or heal overlord
previous_health_count = source.health
+20 -9
View File
@@ -95,16 +95,26 @@
var/static/list/config_songs
if(isnull(config_songs))
config_songs = list()
var/list/tracks = flist("[global.config.directory]/jukebox_music/sounds/")
var/list/tracks = flist(CONFIG_JUKEBOX_SOUNDS)
for(var/track_file in tracks)
var/datum/track/new_track = new()
new_track.song_path = file("[global.config.directory]/jukebox_music/sounds/[track_file]")
new_track.song_path = file("[CONFIG_JUKEBOX_SOUNDS][track_file]")
var/list/track_data = splittext(track_file, "+")
if(length(track_data) < 3 || !IS_SOUND_FILE(new_track.song_path))
if(!length(track_data) || !IS_SOUND_FILE_SAFE(new_track.song_path))
continue
new_track.song_name = track_data[1]
new_track.song_length = text2num(track_data[2])
new_track.song_beat = text2num(track_data[3])
var/track_name = track_data[JUKEBOX_NAME]
track_name = strip_filepath_extension(track_name, SSsounds.safe_formats)
new_track.song_name = track_name
new_track.song_length = SSsounds.get_sound_length(new_track.song_path)
if(track_data.len >= 3) // Bandaid for legacy tracks to not use the length for the bpm rather then the actual beats.
var/static/logged_to_admins = FALSE
log_game("[new_track.song_path] track data seems to be using the legacy format; we will attempt to make it work.")
if(!logged_to_admins)
message_admins("The jukebox has tracks uploaded in a legacy format. Length is now fetched programmatically, with title and beats being the only required fields.")
logged_to_admins = TRUE
new_track.song_beat_deciseconds = text2num(track_data[3])
else if(track_data.len >= 2)
new_track.song_beat_deciseconds = text2num(track_data[JUKEBOX_BEATS])
config_songs[new_track.song_name] = new_track
if(!length(config_songs))
@@ -128,7 +138,7 @@
UNTYPED_LIST_ADD(songs_data, list( \
"name" = song_name, \
"length" = DisplayTimeText(one_song.song_length), \
"beat" = one_song.song_beat, \
"beat" = one_song.song_beat_deciseconds || "Unknown", \
))
data["active"] = !!active_song_sound
@@ -399,11 +409,12 @@
var/song_length = 0
/// How long is a beat of the song in decisconds
/// Used to determine time between effects when played
var/song_beat = 0
/// Do note this is NOT BPM.
var/song_beat_deciseconds = 0
// Default track supplied for testing and also because it's a banger
/datum/track/default
song_path = 'sound/music/lobby_music/title3.ogg'
song_name = "Tintin on the Moon"
song_length = 3 MINUTES + 52 SECONDS
song_beat = 1 SECONDS
song_beat_deciseconds = 1 SECONDS
+6 -6
View File
@@ -65,13 +65,13 @@
amount *= our_mob.get_damage_mod(type)
switch (type)
if(BRUTE)
host.adjustBruteLoss(amount, forced = TRUE)
host.adjust_brute_loss(amount, forced = TRUE)
if(BURN)
host.adjustFireLoss(amount, forced = TRUE)
host.adjust_fire_loss(amount, forced = TRUE)
if(TOX)
host.adjustToxLoss(amount, forced = TRUE)
host.adjust_tox_loss(amount, forced = TRUE)
if(OXY)
host.adjustOxyLoss(amount, forced = TRUE)
host.adjust_oxy_loss(amount, forced = TRUE)
on_passed_damage?.Invoke(our_mob, host, amount)
return COMPONENT_IGNORE_CHANGE
@@ -80,9 +80,9 @@
/datum/component/life_link/proc/on_limb_damage(mob/living/our_mob, limb, brute, burn)
SIGNAL_HANDLER
if (brute != 0)
host.adjustBruteLoss(brute, updating_health = FALSE)
host.adjust_brute_loss(brute, updating_health = FALSE)
if (burn != 0)
host.adjustFireLoss(burn, updating_health = FALSE)
host.adjust_fire_loss(burn, updating_health = FALSE)
if (brute != 0 || burn != 0)
host.updatehealth()
on_passed_damage?.Invoke(our_mob, host, brute + burn)
+19 -12
View File
@@ -49,22 +49,21 @@
RegisterSignal(parent, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(check_removed_organ))
RegisterSignal(parent, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(check_added_organ))
RegisterSignal(parent, COMSIG_HEART_MANUAL_PULSE, PROC_REF(on_pump))
RegisterSignals(parent, list(COMSIG_LIVING_DEATH, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD)), PROC_REF(pause))
RegisterSignals(parent, list(COMSIG_LIVING_REVIVE, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD)), PROC_REF(restart))
RegisterSignal(parent, COMSIG_LIVING_UPDATE_BLOOD_STATUS, PROC_REF(on_update_blood_status))
pump_action.cooldown_time = pump_delay - (1 SECONDS) //you can pump up to a second early
pump_action.Grant(parent)
var/mob/living/carbon/carbon_parent = parent
var/obj/item/organ/heart/parent_heart = carbon_parent.get_organ_slot(ORGAN_SLOT_HEART)
if(parent_heart && !HAS_TRAIT(carbon_parent, TRAIT_NOBLOOD) && carbon_parent.stat != DEAD)
if(parent_heart && CAN_HAVE_BLOOD(carbon_parent) && carbon_parent.stat != DEAD)
START_PROCESSING(SSdcs, src)
COOLDOWN_START(src, heart_timer, pump_delay)
to_chat(parent, span_userdanger("Your heart no longer beats automatically! You have to pump it manually - otherwise you'll die!"))
/datum/component/manual_heart/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_CARBON_GAIN_ORGAN, COMSIG_CARBON_LOSE_ORGAN, COMSIG_HEART_MANUAL_PULSE, COMSIG_LIVING_REVIVE, COMSIG_LIVING_DEATH, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD)))
UnregisterSignal(parent, list(COMSIG_CARBON_GAIN_ORGAN, COMSIG_CARBON_LOSE_ORGAN, COMSIG_HEART_MANUAL_PULSE, COMSIG_LIVING_REVIVE, COMSIG_LIVING_DEATH, COMSIG_LIVING_UPDATE_BLOOD_STATUS))
to_chat(parent, span_userdanger("You feel your heart start beating normally again!"))
var/mob/living/carbon/carbon_parent = parent
@@ -95,14 +94,14 @@
var/mob/living/carbon/carbon_owner = owner
if(HAS_TRAIT(carbon_owner, TRAIT_NOBLOOD))
if(!CAN_HAVE_BLOOD(carbon_owner))
return
carbon_owner.blood_volume = min(carbon_owner.blood_volume + (blood_loss * 0.5), BLOOD_VOLUME_MAXIMUM)
carbon_owner.adjust_blood_volume(blood_loss * 0.5)
carbon_owner.remove_client_colour(REF(src))
add_colour = TRUE
carbon_owner.adjustBruteLoss(-heal_brute)
carbon_owner.adjustFireLoss(-heal_burn)
carbon_owner.adjustOxyLoss(-heal_oxy)
carbon_owner.adjust_brute_loss(-heal_brute)
carbon_owner.adjust_fire_loss(-heal_burn)
carbon_owner.adjust_oxy_loss(-heal_oxy)
/datum/component/manual_heart/process()
var/mob/living/carbon/carbon_parent = parent
@@ -115,13 +114,21 @@
if(!COOLDOWN_FINISHED(src, heart_timer))
return
carbon_parent.blood_volume = max(carbon_parent.blood_volume - blood_loss, 0)
carbon_parent.adjust_blood_volume(-blood_loss)
to_chat(carbon_parent, span_userdanger("You have to keep pumping your blood!"))
COOLDOWN_START(src, heart_timer, MANUAL_HEART_GRACE_PERIOD) //give two full seconds before losing more blood
if(add_colour)
carbon_parent.add_client_colour(/datum/client_colour/manual_heart_blood, REF(src))
add_colour = FALSE
/datum/component/manual_heart/proc/on_update_blood_status(datum/source, had_blood, has_blood, new_blood_volume, old_blood_volume)
SIGNAL_HANDLER
if (has_blood)
restart()
else
pause()
///If a new heart is added, start processing.
/datum/component/manual_heart/proc/check_added_organ(mob/organ_owner, obj/item/organ/new_organ)
SIGNAL_HANDLER
@@ -151,7 +158,7 @@
/datum/component/manual_heart/proc/check_valid()
var/mob/living/carbon/carbon_parent = parent
var/obj/item/organ/heart/parent_heart = carbon_parent.get_organ_slot(ORGAN_SLOT_HEART)
return !isnull(parent_heart) && !HAS_TRAIT(carbon_parent, TRAIT_NOBLOOD) && carbon_parent.stat != DEAD
return !isnull(parent_heart) && CAN_HAVE_BLOOD(carbon_parent) && carbon_parent.stat != DEAD
///Action to pump your heart. Cooldown will always be set to 1 second less than the pump delay.
/datum/action/cooldown/manual_heart
@@ -169,7 +176,7 @@
///The action button is only available when you're a living carbon with blood and a heart.
/datum/action/cooldown/manual_heart/IsAvailable(feedback = FALSE)
var/mob/living/carbon/heart_haver = owner
if(!istype(heart_haver) || HAS_TRAIT(heart_haver, TRAIT_NOBLOOD) || heart_haver.stat == DEAD)
if(!istype(heart_haver) || !CAN_HAVE_BLOOD(heart_haver) || heart_haver.stat == DEAD)
return FALSE
var/obj/item/organ/heart/heart_havers_heart = heart_haver.get_organ_slot(ORGAN_SLOT_HEART)
if(isnull(heart_havers_heart))
+7 -7
View File
@@ -171,7 +171,7 @@
SIGNAL_HANDLER
if (forced)
return
back?.adjustStaminaLoss(amount, forced = forced)
back?.adjust_stamina_loss(amount, forced = forced)
/// On damage or heal, affect our furthest segment
/datum/component/mob_chain/proc/on_adjust_damage(mob/living/our_mob, type, amount, forced)
@@ -180,13 +180,13 @@
return
switch (type)
if(BRUTE)
back.adjustBruteLoss(amount, forced = forced)
back.adjust_brute_loss(amount, forced = forced)
if(BURN)
back.adjustFireLoss(amount, forced = forced)
back.adjust_fire_loss(amount, forced = forced)
if(TOX)
back.adjustToxLoss(amount, forced = forced)
back.adjust_tox_loss(amount, forced = forced)
if(OXY) // If all segments are suffocating we pile damage backwards until our ass starts dying forwards
back.adjustOxyLoss(amount, forced = forced)
back.adjust_oxy_loss(amount, forced = forced)
return COMPONENT_IGNORE_CHANGE
/// Special handling for if damage is delegated to a mob's limbs instead of its overall damage
@@ -195,9 +195,9 @@
if (isnull(back))
return
if (brute != 0)
back.adjustBruteLoss(brute, updating_health = FALSE)
back.adjust_brute_loss(brute, updating_health = FALSE)
if (burn != 0)
back.adjustFireLoss(burn, updating_health = FALSE)
back.adjust_fire_loss(burn, updating_health = FALSE)
if (brute != 0 || burn != 0)
back.updatehealth()
return COMPONENT_PREVENT_LIMB_DAMAGE
@@ -0,0 +1,36 @@
/datum/component/plumbing/automated_iv
demand_connects = SOUTH
supply_connects = NORTH
///Temporary holder to store all the reagents from the iv drip before transferring it to the ducts
var/datum/reagents/plumbing/holder
/datum/component/plumbing/automated_iv/Initialize(start=TRUE, _ducting_layer, _turn_connects=TRUE, datum/reagents/custom_receiver)
. = ..()
if(!istype(parent, /obj/machinery/iv_drip/plumbing))
return COMPONENT_INCOMPATIBLE
var/obj/machinery/iv_drip/plumbing/drip = parent
holder = new(drip.reagents.maximum_volume, drip.reagents.flags)
holder.my_atom = drip
/datum/component/plumbing/automated_iv/Destroy(force)
QDEL_NULL(holder)
return ..()
/datum/component/plumbing/automated_iv/can_give(amount, reagent)
. = ..()
if(!.)
return
var/obj/machinery/iv_drip/plumbing/drip = parent
return drip.mode == 0
/datum/component/plumbing/automated_iv/send_request(dir)
var/obj/machinery/iv_drip/plumbing/drip = parent
if(drip.mode == 1)
process_request(dir = dir)
/datum/component/plumbing/automated_iv/transfer_to(datum/component/plumbing/target, amount, reagent, datum/ductnet/net, round_robin = TRUE)
reagents.trans_to(holder, reagents.total_volume)
reagents = holder
. = ..()
var/obj/machinery/iv_drip/plumbing/drip = parent
reagents = drip.reagents
+4 -4
View File
@@ -115,13 +115,13 @@
var/need_mob_update = FALSE
if(brute_per_second)
need_mob_update += living_parent.adjustBruteLoss(-1 * heal_mod * brute_per_second * seconds_per_tick, updating_health = FALSE)
need_mob_update += living_parent.adjust_brute_loss(-1 * heal_mod * brute_per_second * seconds_per_tick, updating_health = FALSE)
if(burn_per_second)
need_mob_update += living_parent.adjustFireLoss(-1 * heal_mod * burn_per_second * seconds_per_tick, updating_health = FALSE)
need_mob_update += living_parent.adjust_fire_loss(-1 * heal_mod * burn_per_second * seconds_per_tick, updating_health = FALSE)
if(tox_per_second)
need_mob_update += living_parent.adjustToxLoss(-1 * heal_mod * tox_per_second * seconds_per_tick, updating_health = FALSE)
need_mob_update += living_parent.adjust_tox_loss(-1 * heal_mod * tox_per_second * seconds_per_tick, updating_health = FALSE)
if(oxy_per_second)
need_mob_update += living_parent.adjustOxyLoss(-1 * heal_mod * oxy_per_second * seconds_per_tick, updating_health = FALSE)
need_mob_update += living_parent.adjust_oxy_loss(-1 * heal_mod * oxy_per_second * seconds_per_tick, updating_health = FALSE)
if(heals_wounds && iscarbon(parent))
var/mob/living/carbon/carbon_parent = living_parent
+23 -22
View File
@@ -44,7 +44,7 @@
RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, PROC_REF(on_attack_ghost))
if(!(shell_flags & SHELL_FLAG_CIRCUIT_UNMODIFIABLE))
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(on_multitool_act))
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack_by))
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interaction))
if(!(shell_flags & SHELL_FLAG_CIRCUIT_UNREMOVABLE))
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER), PROC_REF(on_screwdriver_act))
RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, PROC_REF(on_object_deconstruct))
@@ -91,7 +91,7 @@
/datum/component/shell/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_ATTACKBY,
COMSIG_ATOM_ITEM_INTERACTION,
COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER),
COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL),
COMSIG_OBJ_DECONSTRUCT,
@@ -160,55 +160,56 @@
attached_circuit?.set_on(source.anchored)
/**
* Called when an item hits the parent. This is the method to add the circuitboard to the component.
* Called when interacting with the parent using an item. This is the method to add the circuitboard to the component.
*/
/datum/component/shell/proc/on_attack_by(atom/source, obj/item/item, mob/living/attacker)
/datum/component/shell/proc/on_item_interaction(atom/source, mob/living/user, obj/item/item, list/modifiers)
SIGNAL_HANDLER
if(!is_authorized(attacker))
if(!is_authorized(user))
return
if(istype(item, /obj/item/stock_parts/power_store/cell))
source.balloon_alert(attacker, "can't put cell in directly!")
source.balloon_alert(user, "can't put cell in directly!")
return
if(istype(item, /obj/item/inducer))
var/obj/item/inducer/inducer = item
INVOKE_ASYNC(inducer, TYPE_PROC_REF(/obj/item, interact_with_atom), attached_circuit || parent, attacker, list())
return COMPONENT_NO_AFTERATTACK
INVOKE_ASYNC(inducer, TYPE_PROC_REF(/obj/item, interact_with_atom), attached_circuit || parent, user, list())
return ITEM_INTERACT_SUCCESS
if(attached_circuit)
if(attached_circuit.owner_id && item == attached_circuit.owner_id.resolve())
set_locked(!locked)
source.balloon_alert(attacker, "[locked ? "locked" : "unlocked"] [source]")
return COMPONENT_NO_AFTERATTACK
source.balloon_alert(user, "[locked ? "locked" : "unlocked"] [source]")
return ITEM_INTERACT_SUCCESS
if(!attached_circuit.owner_id && isidcard(item))
source.balloon_alert(attacker, "owner id set for [item]")
source.balloon_alert(user, "owner id set for [item]")
attached_circuit.owner_id = WEAKREF(item)
return COMPONENT_NO_AFTERATTACK
return ITEM_INTERACT_SUCCESS
if(istype(item, /obj/item/circuit_component))
attached_circuit.add_component_manually(item, attacker)
return COMPONENT_NO_AFTERATTACK
if(attached_circuit.add_component_manually(item, user))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
if(!istype(item, /obj/item/integrated_circuit))
return
var/obj/item/integrated_circuit/logic_board = item
. = COMPONENT_NO_AFTERATTACK
if(logic_board.shell) // I'll be surprised if this ever happens
return
return ITEM_INTERACT_BLOCKING
if(attached_circuit)
source.balloon_alert(attacker, "there is already a circuitboard inside!")
return
source.balloon_alert(user, "there is already a circuitboard inside!")
return ITEM_INTERACT_BLOCKING
if(logic_board.current_size > capacity)
source.balloon_alert(attacker, "this is too large to fit into [parent]!")
return
source.balloon_alert(user, "this is too large to fit into [parent]!")
return ITEM_INTERACT_BLOCKING
logic_board.inserter_mind = WEAKREF(attacker.mind)
attach_circuit(logic_board, attacker)
logic_board.inserter_mind = WEAKREF(user.mind)
attach_circuit(logic_board, user)
return ITEM_INTERACT_SUCCESS
/// Sets whether the shell is locked or not
/datum/component/shell/proc/set_locked(new_value)
+1 -1
View File
@@ -82,7 +82,7 @@
var/blood_cost = (cooldown_remaining - new_cooldown ) * COOLDOWN_TO_BLOOD_RATIO
spell.StartCooldown(new_cooldown)
source.blood_volume -= blood_cost
source.adjust_blood_volume(-blood_cost)
var/cost_desc
+8 -8
View File
@@ -118,7 +118,7 @@
clicked_atom = get_turf_in_angle(tackle_angle, get_turf(user), min_distance)
user.Knockdown(base_knockdown, ignore_canstun = TRUE)
user.adjustStaminaLoss(stamina_cost)
user.adjust_stamina_loss(stamina_cost)
user.throw_at(clicked_atom, range, speed, user, FALSE)
addtimer(CALLBACK(src, PROC_REF(resetTackle)), base_knockdown, TIMER_STOPPABLE)
return(COMSIG_MOB_CANCEL_CLICKON)
@@ -470,11 +470,11 @@
if(human_sacker.mob_mood.sanity_level == SANITY_LEVEL_INSANE) //I've gone COMPLETELY INSANE
attack_mod += 15
human_sacker.adjustStaminaLoss(100) //AHAHAHAHAHAHAHAHA
human_sacker.adjust_stamina_loss(100) //AHAHAHAHAHAHAHAHA
if(HAS_TRAIT(human_sacker, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)) // tackling with riot specialized armor, like riot armor, is effective but tiring
attack_mod += 2
human_sacker.adjustStaminaLoss(20)
human_sacker.adjust_stamina_loss(20)
var/randomized_tackle_roll = rand(-3, 3) - defense_mod + attack_mod + skill_mod
return randomized_tackle_roll
@@ -626,7 +626,7 @@
embed.ignore_throwspeed_threshold = initial(embed.ignore_throwspeed_threshold)
embed.impact_pain_mult = initial(embed.impact_pain_mult)
windscreen_casualty.atom_destruction()
user.adjustStaminaLoss(10 * speed)
user.adjust_stamina_loss(10 * speed)
user.Paralyze(3 SECONDS)
user.visible_message(span_danger("[user] smacks into [windscreen_casualty] and shatters it, shredding [user.p_them()]self with glass!"), span_userdanger("You smacks into [windscreen_casualty] and shatter it, shredding yourself with glass!"))
@@ -635,8 +635,8 @@
user.Paralyze(1 SECONDS)
user.Knockdown(3 SECONDS)
windscreen_casualty.take_damage(30 * speed)
user.adjustStaminaLoss(10 * speed, updating_stamina=FALSE)
user.adjustBruteLoss(5 * speed)
user.adjust_stamina_loss(10 * speed, updating_stamina=FALSE)
user.adjust_brute_loss(5 * speed)
/datum/component/tackler/proc/delayedSmash(obj/structure/window/windscreen_casualty)
if(windscreen_casualty)
@@ -680,8 +680,8 @@
HOW_big_of_a_miss_did_we_just_make = ", making a ginormous mess!" // an extra exclamation point!! for emphasis!!!
owner.visible_message(span_danger("[owner] trips over [kevved] and slams into it face-first[HOW_big_of_a_miss_did_we_just_make]!"), span_userdanger("You trip over [kevved] and slam into it face-first[HOW_big_of_a_miss_did_we_just_make]!"))
owner.adjustStaminaLoss(15 + messes.len * 2, updating_stamina = FALSE)
owner.adjustBruteLoss(8 + messes.len, updating_health = FALSE)
owner.adjust_stamina_loss(15 + messes.len * 2, updating_stamina = FALSE)
owner.adjust_brute_loss(8 + messes.len, updating_health = FALSE)
owner.Paralyze(0.4 SECONDS * messes.len) // .4 seconds of paralyze for each thing you knock around
owner.Knockdown(2 SECONDS + 0.4 SECONDS * messes.len) // 2 seconds of knockdown after the paralyze
owner.updatehealth()
+3 -3
View File
@@ -276,11 +276,11 @@
var/obj/item/item_parent = parent
switch(item_parent.damtype)
if(STAMINA)
user.adjustStaminaLoss(clumsy_damage)
user.adjust_stamina_loss(clumsy_damage)
if(OXY)
user.adjustOxyLoss(clumsy_damage)
user.adjust_oxy_loss(clumsy_damage)
if(TOX)
user.adjustToxLoss(clumsy_damage)
user.adjust_tox_loss(clumsy_damage)
if(BRUTE)
user.take_bodypart_damage(brute=clumsy_damage)
if(BURN)
+85 -13
View File
@@ -11,10 +11,13 @@
* assembly code at the bottom of this file
*/
/datum/component/trapdoor
can_transfer = TRUE
///assembly tied to this trapdoor
var/obj/item/assembly/trapdoor/assembly
///path of the turf this should change into when the assembly is pulsed. needed for openspace trapdoors knowing what to turn back into
var/trapdoor_turf_path
///stack of baseturfs on top of the openspace under this trapdoor
var/list/trapdoor_baseturfs = list()
/// is this trapdoor "conspicuous" (ie. it gets examine text and overlay added)
var/conspicuous
/// overlay that makes trapdoors more obvious
@@ -32,8 +35,10 @@
var/autoclose = TRUE
/// Delay before trapdoor shuts close
var/autoclose_delay = 5 SECONDS
/// Is this trapdoor on a shuttle
var/on_shuttle = FALSE
/datum/component/trapdoor/Initialize(starts_open, trapdoor_turf_path, assembly, conspicuous = TRUE, list/carried_decals = null, autoclose = TRUE)
/datum/component/trapdoor/Initialize(starts_open, trapdoor_turf_path, trapdoor_baseturfs, assembly, conspicuous = TRUE, list/carried_decals = null, autoclose = TRUE)
if(!isopenturf(parent))
return COMPONENT_INCOMPATIBLE
@@ -46,29 +51,57 @@
if(!trapdoor_overlay)
trapdoor_overlay = mutable_appearance('icons/turf/overlays.dmi', "border_black", ABOVE_NORMAL_TURF_LAYER)
var/turf/parent_turf = parent
if(SSshuttle.get_containing_shuttle(parent_turf))
on_shuttle = TRUE
if(IS_OPEN(parent))
openspace_trapdoor_setup(trapdoor_turf_path, assembly)
openspace_trapdoor_setup(trapdoor_turf_path, trapdoor_baseturfs)
if(autoclose)
addtimer(CALLBACK(src, PROC_REF(try_closing)), autoclose_delay)
else
tile_trapdoor_setup(trapdoor_turf_path, assembly)
tile_trapdoor_setup()
if(starts_open)
try_opening()
/datum/component/trapdoor/PostTransfer(datum/new_parent)
if(!isopenturf(new_parent))
if(isatom(new_parent))
var/atom/new_parent_atom = new_parent
new_parent_atom.visible_message(span_warning("The trapdoor mechanism under [new_parent_atom] is broken!"))
return COMPONENT_NOTRANSFER
if(SSshuttle.get_containing_shuttle(new_parent))
on_shuttle = TRUE
else
on_shuttle = FALSE
/datum/component/trapdoor/proc/is_under_shuttle()
var/turf/parent_turf = parent
var/shuttle_skipover_depth = parent_turf.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle)
return shuttle_skipover_depth && parent_turf.depth_to_find_baseturf(/turf/baseturf_skipover/trapdoor) > shuttle_skipover_depth
///initializing as an opened trapdoor, we need to trust that we were given the data by a closed trapdoor
/datum/component/trapdoor/proc/openspace_trapdoor_setup(trapdoor_turf_path)
/datum/component/trapdoor/proc/openspace_trapdoor_setup(trapdoor_turf_path, trapdoor_baseturfs)
src.trapdoor_turf_path = trapdoor_turf_path
src.trapdoor_baseturfs = trapdoor_baseturfs
///initializing as a closed trapdoor, we need to take data from the tile we're on to give it to the open state to store
/datum/component/trapdoor/proc/tile_trapdoor_setup(trapdoor_turf_path)
/datum/component/trapdoor/proc/tile_trapdoor_setup()
src.trapdoor_turf_path = parent.type
insert_trapdoor_skipover()
if(stored_decals.len)
reapply_all_decals()
if(conspicuous)
var/turf/parent_turf = parent
parent_turf.add_overlay(trapdoor_overlay)
/datum/component/trapdoor/proc/insert_trapdoor_skipover()
var/turf/parent_turf = parent
if(parent_turf.depth_to_find_baseturf(/turf/baseturf_skipover/trapdoor))
return
parent_turf.insert_baseturf((parent_turf.level_to_find_baseturf(/turf/baseturf_skipover/shuttle) || 1) + 1, /turf/baseturf_skipover/trapdoor)
/datum/component/trapdoor/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_TURF_CHANGE, PROC_REF(turf_changed_pre))
@@ -84,6 +117,8 @@
// SKYRAT EDIT END
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(try_unlink))
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_link))
RegisterSignal(parent, COMSIG_SHUTTLE_TURF_SHOULD_MOVE_SPECIAL, PROC_REF(should_move_special))
RegisterSignal(parent, COMSIG_SHUTTLE_TURF_ON_MOVE_SPECIAL, PROC_REF(on_move_special))
/datum/component/trapdoor/UnregisterFromParent()
. = ..()
@@ -94,9 +129,13 @@
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
UnregisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL))
UnregisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION)
UnregisterSignal(parent, COMSIG_SHUTTLE_TURF_SHOULD_MOVE_SPECIAL)
UnregisterSignal(parent, COMSIG_SHUTTLE_TURF_ON_MOVE_SPECIAL)
/datum/component/trapdoor/proc/try_unlink(turf/source, mob/user, obj/item/tool)
SIGNAL_HANDLER
if(is_under_shuttle())
return
if(!assembly)
return
if(IS_OPEN(parent))
@@ -108,6 +147,8 @@
/datum/component/trapdoor/proc/try_link(turf/source, mob/user, obj/item/tool)
SIGNAL_HANDLER
if(is_under_shuttle())
return
if(!istype(tool, /obj/item/trapdoor_remote))
return
var/obj/item/trapdoor_remote/remote = tool
@@ -126,6 +167,8 @@
/datum/component/trapdoor/proc/async_try_link(turf/source, mob/user, obj/item/trapdoor_remote/remote)
if(!do_after(user, 2 SECONDS, target=source))
return
if(QDELETED(src))
return
if(IS_OPEN(parent))
source.balloon_alert(user, "can't link trapdoor when its open")
return
@@ -139,6 +182,8 @@
/datum/component/trapdoor/proc/async_try_unlink(turf/source, mob/user, obj/item/tool)
if(!do_after(user, 5 SECONDS, target=source))
return
if(QDELETED(src))
return
if(IS_OPEN(parent))
source.balloon_alert(user, "can't unlink trapdoor when its open")
return
@@ -161,6 +206,8 @@
* changing turfs does not bring over decals, so we must perform a little bit of element reapplication.
*/
/datum/component/trapdoor/proc/reapply_all_decals()
if(is_under_shuttle())
return
for(var/list/element_data as anything in stored_decals)
apply_decal(element_data[1], element_data[2], element_data[3], element_data[4])
stored_decals = list()
@@ -192,12 +239,12 @@
try_closing()
///signal called by turf changing
/datum/component/trapdoor/proc/turf_changed_pre(datum/source, path, new_baseturfs, flags, post_change_callbacks)
/datum/component/trapdoor/proc/turf_changed_pre(datum/source, path, list/new_baseturfs, flags, post_change_callbacks)
SIGNAL_HANDLER
var/turf/open/dying_trapdoor = parent
if((flags & CHANGETURF_TRAPDOOR_INDUCED) == 0) //not a process of the trapdoor
if(!IS_OPEN(parent) && !ispath(path, /turf/closed) && !ispath(path, /turf/open/openspace)) // allow people to place tiles on plating / change tiles without breaking the trapdoor
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), path, conspicuous, assembly)
if(!IS_OPEN(parent) && !ispath(path, /turf/closed) && islist(new_baseturfs) && new_baseturfs.Find(/turf/baseturf_skipover/trapdoor)) // allow people to place tiles on plating / change tiles without breaking the trapdoor
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), path, null, conspicuous, assembly)
return
// otherwise, break trapdoor
dying_trapdoor.visible_message(span_warning("The trapdoor mechanism in [dying_trapdoor] is broken!"))
@@ -206,7 +253,7 @@
stored_decals.Cut()
assembly = null
return
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), trapdoor_turf_path, conspicuous, assembly)
post_change_callbacks += CALLBACK(src, TYPE_PROC_REF(/datum/component/trapdoor, carry_over_trapdoor), trapdoor_turf_path, trapdoor_baseturfs, conspicuous, assembly)
/**
* ## carry_over_trapdoor
@@ -214,8 +261,8 @@
* applies the trapdoor to the new turf (created by the last trapdoor)
* apparently callbacks with arguments on invoke and the callback itself have the callback args go first. interesting!
*/
/datum/component/trapdoor/proc/carry_over_trapdoor(trapdoor_turf_path, conspicuous, assembly, turf/new_turf)
new_turf.AddComponent(/datum/component/trapdoor, FALSE, trapdoor_turf_path, assembly, conspicuous, stored_decals, autoclose)
/datum/component/trapdoor/proc/carry_over_trapdoor(trapdoor_turf_path, trapdoor_baseturfs, conspicuous, assembly, turf/new_turf)
new_turf.AddComponent(/datum/component/trapdoor, FALSE, trapdoor_turf_path, trapdoor_baseturfs, assembly, conspicuous, stored_decals, autoclose)
/**
* ## on_examine
@@ -224,6 +271,8 @@
*/
/datum/component/trapdoor/proc/on_examine(datum/source, mob/user, list/examine_text)
SIGNAL_HANDLER
if(is_under_shuttle())
return
if(conspicuous)
examine_text += "There seems to be a tiny gap around this tile with some wires that you might be able to pulse with a <b>multitool</b>."
@@ -235,12 +284,19 @@
*/
/datum/component/trapdoor/proc/try_opening()
var/turf/open/trapdoor_turf = parent
var/opening_depth = trapdoor_turf.depth_to_find_baseturf(/turf/baseturf_skipover/trapdoor)
if(!opening_depth)
stack_trace("Could not find baseturf skipover for trapdoor")
return
if(is_under_shuttle())
return
///we want to save this turf's decals as they were right before deletion, so this is the point where we begin listening
if(assembly)
RegisterSignal(parent, COMSIG_TURF_DECAL_DETACHED, PROC_REF(decal_detached))
playsound(trapdoor_turf, 'sound/machines/trapdoor/trapdoor_open.ogg', 50)
trapdoor_baseturfs = trapdoor_turf.get_baseturfs_to_depth(opening_depth)
trapdoor_turf.visible_message(span_warning("[trapdoor_turf] swings open!"))
trapdoor_turf.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
trapdoor_turf.ScrapeAway(opening_depth, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
/**
* ## try_closing
@@ -256,7 +312,23 @@
return
playsound(trapdoor_turf, 'sound/machines/trapdoor/trapdoor_shut.ogg', 50)
trapdoor_turf.visible_message(span_warning("The trapdoor mechanism in [trapdoor_turf] swings shut!"))
trapdoor_turf.ChangeTurf(trapdoor_turf_path, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
var/list/new_baseturfs = list()
new_baseturfs += trapdoor_turf.baseturfs
new_baseturfs += trapdoor_turf.type
if(on_shuttle)
new_baseturfs += /turf/baseturf_skipover/shuttle
new_baseturfs += trapdoor_baseturfs
trapdoor_baseturfs = null
trapdoor_turf.ChangeTurf(trapdoor_turf_path, new_baseturfs, flags = CHANGETURF_INHERIT_AIR | CHANGETURF_TRAPDOOR_INDUCED)
/datum/component/trapdoor/proc/should_move_special(datum/source)
SIGNAL_HANDLER
var/turf/source_turf = source
return IS_OPEN(source_turf) && !is_under_shuttle()
/datum/component/trapdoor/proc/on_move_special(datum/source, turf/new_turf)
SIGNAL_HANDLER
new_turf.TakeComponent(src)
#undef IS_OPEN
+82 -37
View File
@@ -1,50 +1,56 @@
// This element should be applied to wall-mounted machines/structures, so that if the wall it's "hanging" from is broken or deconstructed, the wall-hung structure will deconstruct.
/datum/component/wall_mounted
dupe_mode = COMPONENT_DUPE_ALLOWED
/// The wall our object is currently linked to.
var/turf/closed/wall/hanging_wall_turf
// This element should be applied to wall-mounted machines/structures, so that if the support structure it's "hanging" from is broken or deconstructed, the wall-hung structure will deconstruct.
/datum/component/atom_mounted
/// The closed turf our object is currently linked to.
var/atom/hanging_support_atom
/datum/component/wall_mounted/Initialize(target_wall, on_drop_callback)
/datum/component/atom_mounted/Initialize(target_structure, on_drop_callback)
. = ..()
if(!isobj(parent) || !iswallturf(target_wall))
if(!isobj(parent))
return COMPONENT_INCOMPATIBLE
hanging_wall_turf = target_wall
hanging_support_atom = target_structure
/datum/component/wall_mounted/RegisterWithParent()
/datum/component/atom_mounted/RegisterWithParent()
ADD_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src))
RegisterSignal(hanging_wall_turf, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(hanging_wall_turf, COMSIG_TURF_CHANGE, PROC_REF(on_turf_changing))
RegisterSignal(hanging_support_atom, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
if(isclosedturf(hanging_support_atom))
RegisterSignal(hanging_support_atom, COMSIG_TURF_CHANGE, PROC_REF(on_turf_changing))
else
RegisterSignal(hanging_support_atom, COMSIG_QDELETING, PROC_REF(on_structure_delete))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
/datum/component/wall_mounted/UnregisterFromParent()
/datum/component/atom_mounted/UnregisterFromParent()
REMOVE_TRAIT(parent, TRAIT_WALLMOUNTED, REF(src))
UnregisterSignal(hanging_wall_turf, list(COMSIG_ATOM_EXAMINE, COMSIG_TURF_CHANGE))
UnregisterSignal(hanging_support_atom, list(COMSIG_ATOM_EXAMINE))
if(isclosedturf(hanging_support_atom))
UnregisterSignal(hanging_support_atom, COMSIG_TURF_CHANGE)
else
UnregisterSignal(hanging_support_atom, COMSIG_QDELETING)
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
hanging_wall_turf = null
hanging_support_atom = null
/**
* When the wall is examined, explains that it's supporting the linked object.
*/
/datum/component/wall_mounted/proc/on_examine(datum/source, mob/user, list/examine_list)
/datum/component/atom_mounted/proc/on_examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if (parent in view(user.client?.view || world.view, user))
examine_list += span_notice("\The [hanging_wall_turf] is currently supporting [span_bold("[parent]")]. Deconstruction or excessive damage would cause it to [span_bold("fall to the ground")].")
examine_list += span_notice("\The [hanging_support_atom] is currently supporting [span_bold("[parent]")]. Deconstruction or excessive damage would cause it to [span_bold("fall to the ground")].")
/**
* When the type of turf changes, if it is changing into a floor we should drop our contents
*/
/datum/component/wall_mounted/proc/on_turf_changing(datum/source, path, new_baseturfs, flags, post_change_callbacks)
/// When the type of turf changes, if it is changing into a floor we should drop our contents
/datum/component/atom_mounted/proc/on_turf_changing(datum/source, path, new_baseturfs, flags, post_change_callbacks)
SIGNAL_HANDLER
if(ispath(path, /turf/open))
drop_wallmount()
/datum/component/atom_mounted/proc/on_structure_delete(datum/source, force)
SIGNAL_HANDLER
/**
* If we get dragged from our wall (by a singulo for instance) we should deconstruct
*/
/datum/component/wall_mounted/proc/on_move(datum/source, atom/old_loc, dir, forced, list/old_locs)
drop_wallmount()
/// If we get dragged from our wall (by a singulo for instance) we should deconstruct
/datum/component/atom_mounted/proc/on_move(datum/source, atom/old_loc, dir, forced, list/old_locs)
SIGNAL_HANDLER
// If we're having our lighting messed with we're likely to get dragged about
// That shouldn't lead to a decon
@@ -55,23 +61,62 @@
/**
* Handles the dropping of the linked object. This is done via deconstruction, as that should be the most sane way to handle it for most objects.
* Except for intercoms, which are handled by creating a new wallframe intercom, as they're apparently items.
*/
/datum/component/wall_mounted/proc/drop_wallmount()
*/
/datum/component/atom_mounted/proc/drop_wallmount()
PRIVATE_PROC(TRUE)
var/obj/hanging_parent = parent
hanging_parent.visible_message(message = span_warning("\The [hanging_parent] falls apart!"), vision_distance = 5)
hanging_parent.deconstruct(FALSE)
/**
* Finds an support atom to hang this object on. If you need to mount the object on Late Initialize
* then pass TRUE inside Initialize() but not in LateInitialize().
* The flag is only applied if no support atom could be found during Initialize() as a last resort
*
* Arguments
* * mark_for_late_init - if TRUE will apply the MOUNT_ON_LATE_INITIALIZE which gets cleared on every call
* * late_init - should only be passed as TRUE from inside LateInitialize()
*/
/obj/proc/find_and_hang_on_atom(mark_for_late_init = FALSE, late_init = FALSE)
if(obj_flags & MOUNT_ON_LATE_INITIALIZE)
obj_flags &= ~MOUNT_ON_LATE_INITIALIZE
else if(late_init)
return TRUE
///Checks object direction and then verifies if there's a wall in that direction. Finally, applies a wall_mounted component to the object.
/obj/proc/find_and_hang_on_wall()
if(istype(get_area(src), /area/shuttle))
return FALSE //For now, we're going to keep the component off of shuttles to avoid the turf changing issue. We'll hit that later really;
var/turf/attachable_wall = loc //first attempt to locate a wall in our current turf
if(!iswallturf(attachable_wall))
attachable_wall = get_step(src, dir) //if no then attempt to locate it in our direction
if(!iswallturf(attachable_wall))
return FALSE //Nothing to latch onto, or not the right thing.
AddComponent(/datum/component/wall_mounted, attachable_wall)
return TRUE
var/area/location = get_area(src)
if(!isarea(location) || istype(location, /area/shuttle))
return FALSE
var/msg
if(PERFORM_ALL_TESTS(focus_only/atom_mounted) && !mark_for_late_init)
msg = "[type] Could not find attachable object at [location.type] "
var/list/turf/attachable_turfs = list()
attachable_turfs += get_step(src, dir)
attachable_turfs += get_turf(src)
for(var/turf/target as anything in attachable_turfs)
var/atom/attachable_atom
if(isclosedturf(target))
attachable_atom = target //your usual wallmount
else
var/static/list/attachables = list(
/obj/structure/table,
/obj/structure/window,
/obj/structure/fence,
) //list of structures to mount on
for(var/obj/attachable in target)
if(is_type_in_list(attachable, attachables))
attachable_atom = attachable
break
if(attachable_atom)
AddComponent(/datum/component/atom_mounted, attachable_atom)
return TRUE
if(msg)
msg += "([target.x],[target.y],[target.z]) "
if(msg)
stack_trace(msg)
if(mark_for_late_init)
obj_flags |= MOUNT_ON_LATE_INITIALIZE
return FALSE