Merge remote-tracking branch 'tgstation/master' into upstream-feb12-2026

# Conflicts:
#	.github/CODEOWNERS
#	.github/workflows/compile_changelogs.yml
#	.github/workflows/stale.yml
#	SQL/database_changelog.md
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	code/__DEFINES/atom_hud.dm
#	code/__DEFINES/inventory.dm
#	code/__DEFINES/mobs.dm
#	code/__DEFINES/species_clothing_paths.dm
#	code/__DEFINES/subsystems.dm
#	code/__DEFINES/surgery.dm
#	code/__HELPERS/global_lists.dm
#	code/_globalvars/lists/maintenance_loot.dm
#	code/_globalvars/traits/_traits.dm
#	code/controllers/subsystem/minor_mapping.dm
#	code/controllers/subsystem/processing/quirks.dm
#	code/controllers/subsystem/shuttle.dm
#	code/datums/components/palette.dm
#	code/datums/components/surgery_initiator.dm
#	code/datums/diseases/advance/advance.dm
#	code/datums/hud.dm
#	code/datums/mood.dm
#	code/datums/mutations/chameleon.dm
#	code/datums/quirks/negative_quirks/nyctophobia.dm
#	code/datums/status_effects/debuffs/debuffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/slime/slime_leech.dm
#	code/datums/weather/weather.dm
#	code/game/data_huds.dm
#	code/game/objects/items.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/frog_statue.dm
#	code/game/objects/items/rcd/RLD.dm
#	code/game/objects/items/robot/items/hypo.dm
#	code/game/objects/items/stacks/medical.dm
#	code/game/objects/items/stacks/wrap.dm
#	code/game/objects/items/storage/garment.dm
#	code/game/objects/items/tools/medical/defib.dm
#	code/game/objects/items/weaponry.dm
#	code/game/objects/items/weaponry/melee/misc.dm
#	code/game/objects/structures/crates_lockers/closets/secure/security.dm
#	code/game/objects/structures/curtains.dm
#	code/game/objects/structures/dresser.dm
#	code/game/objects/structures/girders.dm
#	code/game/objects/structures/maintenance.dm
#	code/game/objects/structures/mirror.dm
#	code/modules/admin/greyscale_modify_menu.dm
#	code/modules/admin/verbs/light_debug.dm
#	code/modules/antagonists/ashwalker/ashwalker.dm
#	code/modules/antagonists/heretic/knowledge/starting_lore.dm
#	code/modules/antagonists/ninja/ninjaDrainAct.dm
#	code/modules/art/paintings.dm
#	code/modules/client/preferences.dm
#	code/modules/client/verbs/ooc.dm
#	code/modules/clothing/head/wig.dm
#	code/modules/events/disease_outbreak.dm
#	code/modules/holodeck/holo_effect.dm
#	code/modules/jobs/job_types/head_of_security.dm
#	code/modules/jobs/job_types/security_officer.dm
#	code/modules/library/skill_learning/generic_skillchips/point.dm
#	code/modules/mining/lavaland/ash_flora.dm
#	code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/basic/guardian/guardian.dm
#	code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
#	code/modules/mob/living/carbon/carbon.dm
#	code/modules/mob/living/carbon/human/human.dm
#	code/modules/mob/living/carbon/human/human_defines.dm
#	code/modules/mob/living/carbon/life.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob/living/living_defines.dm
#	code/modules/mob/mob.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mod/mod_control.dm
#	code/modules/mod/modules/modules_general.dm
#	code/modules/modular_computers/computers/item/computer_ui.dm
#	code/modules/paperwork/paper.dm
#	code/modules/paperwork/paperbin.dm
#	code/modules/power/lighting/light.dm
#	code/modules/projectiles/guns/energy/kinetic_accelerator.dm
#	code/modules/projectiles/projectile.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/food_reagents.dm
#	code/modules/reagents/chemistry/reagents/other_reagents.dm
#	code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
#	code/modules/research/xenobiology/crossbreeding/_clothing.dm
#	code/modules/research/xenobiology/crossbreeding/prismatic.dm
#	code/modules/surgery/advanced/brainwashing.dm
#	code/modules/surgery/advanced/lobotomy.dm
#	code/modules/surgery/amputation.dm
#	code/modules/surgery/blood_filter.dm
#	code/modules/surgery/bodyparts/_bodyparts.dm
#	code/modules/surgery/brain_surgery.dm
#	code/modules/surgery/cavity_implant.dm
#	code/modules/surgery/coronary_bypass.dm
#	code/modules/surgery/gastrectomy.dm
#	code/modules/surgery/healing.dm
#	code/modules/surgery/limb_augmentation.dm
#	code/modules/surgery/organ_manipulation.dm
#	code/modules/surgery/revival.dm
#	code/modules/surgery/sleeper_protocol.dm
#	code/modules/surgery/surgery_helpers.dm
#	code/modules/surgery/surgery_step.dm
#	code/modules/unit_tests/_unit_tests.dm
#	code/modules/unit_tests/designs.dm
#	code/modules/unit_tests/icon_state_worn.dm
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_cultist.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_headrevolutionary.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_provocateur.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png
#	icons/map_icons/clothing/head/_head.dmi
#	icons/map_icons/clothing/shoes.dmi
#	icons/map_icons/items/_item.dmi
#	icons/mob/huds/hud.dmi
#	icons/mob/inhands/64x64_lefthand.dmi
#	icons/mob/inhands/64x64_righthand.dmi
#	icons/obj/machines/computer.dmi
#	tgui/packages/tgui/interfaces/OperatingComputer.jsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/types.ts
#	tgui/packages/tgui/interfaces/SurgeryInitiator.tsx
#	tools/icon_cutter/check.py
This commit is contained in:
nevimer
2026-02-12 23:50:09 -05:00
2374 changed files with 81136 additions and 52767 deletions
+4 -3
View File
@@ -51,7 +51,7 @@
var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 1, OXY = 1)
///Verbs used for speaking e.g. "Says" or "Chitters". This can be elementized
var/list/speak_emote = list()
var/list/speak_emote
///When someone interacts with the simple animal.
///Help-intent verb in present continuous tense.
@@ -120,8 +120,9 @@
apply_target_randomisation()
make_stamina_slowable()
if(speak_emote)
if(LAZYLEN(speak_emote))
speak_emote = string_list(speak_emote)
damage_coeff = string_assoc_list(damage_coeff)
///We need to wait for SSair to be initialized before we can check atmos/temp requirements.
if(PERFORM_ALL_TESTS(focus_only/atmos_and_temp_requirements) && mapload && !SSair.initialized)
@@ -163,7 +164,7 @@
return
AddElement(/datum/element/attack_zone_randomiser)
/mob/living/basic/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(staminaloss > 0)
adjust_stamina_loss(-stamina_recovery * seconds_per_tick, forced = TRUE)
@@ -53,7 +53,7 @@
visible_message(span_warning("The corpse of [target.name] suddenly rises!"))
var/mob/living/basic/blob_minion/zombie/blombie = change_mob_type(zombie_type, loc, new_name = initial(zombie_type.name))
blombie.pass_flags |= PASSBLOB //No way to pass the blob_borne info through change_mob_type() to Initilize(), so we just circumvent it here.
blombie.faction |= faction //inherit the spore's faction in case it was spawned with a different one (eg gold core)
APPLY_FACTION_AND_ALLIES_FROM(blombie, src) //inherit the spore's faction in case it was spawned with a different one (eg gold core)
blombie.set_name()
if (istype(blombie)) // In case of badmin
blombie.consume_corpse(target)
@@ -117,7 +117,7 @@
/// Is our factory dead?
var/orphaned = FALSE
/mob/living/basic/blob_minion/blobbernaut/minion/Life(seconds_per_tick, times_fired)
/mob/living/basic/blob_minion/blobbernaut/minion/Life(seconds_per_tick)
. = ..()
if (!.)
return FALSE
@@ -0,0 +1,173 @@
/*
BLOOD-DRUNK MINER
Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive in its AI as well as damage.
When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator.
Difficulty: Medium
*/
/mob/living/basic/boss/blood_drunk_miner
name = "blood-drunk miner"
desc = "A miner destined to wander forever, engaged in an endless hunt."
health = 900
maxHealth = 900
icon_state = "miner"
icon_living = "miner"
icon = 'icons/mob/simple/broadMobs.dmi'
health_doll_icon = "miner"
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_SPECIAL|MOB_MINING
light_color = COLOR_LIGHT_GRAYISH_RED
speak_emote = list("roars")
speed = 3
pixel_x = -16
base_pixel_x = -16
basic_mob_flags = DEL_ON_DEATH
default_blood_volume = BLOOD_VOLUME_NORMAL
gps_name = "Resonant Signal"
death_message = "falls to the ground, decaying into glowing particles."
death_sound = SFX_BODYFALL
move_force = MOVE_FORCE_NORMAL //Miner beeing able to just move structures like bolted doors and glass looks kinda strange
ai_controller = /datum/ai_controller/blood_drunk_miner
achievements = list(
/datum/award/achievement/boss/blood_miner_kill,
/datum/award/achievement/boss/boss_killer,
/datum/award/score/blood_miner_score,
/datum/award/score/boss_score,
)
crusher_achievement_type = /datum/award/achievement/boss/blood_miner_crusher
victor_memory_type = /datum/memory/megafauna_slayer
crusher_loot = list(/obj/item/crusher_trophy/miner_eye, /obj/item/knife/hunting/wildhunter)
regular_loot = list(/obj/item/melee/cleaving_saw, /obj/item/gun/energy/recharge/kinetic_accelerator)
/// Their little saw
var/obj/item/melee/cleaving_saw/miner/miner_saw
/// How many hits of our saw we inflict on the target when we melee on them. Get mutated via the transform weapon ability.
var/rapid_melee_hits = 5
/// How long must we wait between ranged attacks
var/ranged_attack_cooldown_duration = 1.6 SECONDS
/mob/living/basic/boss/blood_drunk_miner/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_premove))
AddElement(/datum/element/relay_attackers)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_HEAVY)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(attack_override))
miner_saw = new(src)
RegisterSignal(miner_saw, COMSIG_PREQDELETED, PROC_REF(on_saw_deleted))
RegisterSignal(miner_saw, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_saw_premove))
grant_actions_by_list(get_innate_actions())
ai_controller.set_blackboard_key(BB_BDM_RANGED_ATTACK_COOLDOWN, ranged_attack_cooldown_duration)
RegisterSignals(ai_controller, list(AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/basic_melee_attack), AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/targeted_mob_ability)), PROC_REF(handle_saw_transformation))
RegisterSignal(src, COMSIG_LIVING_DROP_LOOT, PROC_REF(death_effect))
AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET))
/// Block deletion of their saw under normal circumstances. It is fused to their hands as far as we're concerned.
/mob/living/basic/boss/blood_drunk_miner/proc/on_saw_deleted(datum/source, force)
SIGNAL_HANDLER
if(!force)
return TRUE
/mob/living/basic/boss/blood_drunk_miner/Destroy(force)
UnregisterSignal(miner_saw, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_PRE_MOVE)) // unblock deletion, we are dead.
QDEL_NULL(miner_saw)
return ..()
/// Returns a list of innate actions for the blood-drunk miner.
/mob/living/basic/boss/blood_drunk_miner/proc/get_innate_actions()
var/static/list/innate_abilities = list(
/datum/action/cooldown/mob_cooldown/dash = BB_BDM_DASH_ABILITY,
/datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator = BB_BDM_KINETIC_ACCELERATOR_ABILITY,
/datum/action/cooldown/mob_cooldown/dash_attack = BB_BDM_DASH_ATTACK_ABILITY,
/datum/action/cooldown/mob_cooldown/transform_weapon = BB_BDM_TRANSFORM_WEAPON_ABILITY,
)
return innate_abilities
/// Invokes the transform weapon ability when signaled by the AI controller.
/mob/living/basic/boss/blood_drunk_miner/proc/handle_saw_transformation()
SIGNAL_HANDLER
INVOKE_ASYNC(ai_controller.blackboard[BB_BDM_TRANSFORM_WEAPON_ABILITY], TYPE_PROC_REF(/datum/action, Trigger), src, NONE)
/mob/living/basic/boss/blood_drunk_miner/ex_act(severity, target)
var/datum/action/cooldown/mob_cooldown/dash_ability = ai_controller.blackboard[BB_BDM_DASH_ABILITY]
if(dash_ability.Trigger(target = target))
return FALSE
return ..()
/mob/living/basic/boss/blood_drunk_miner/do_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, no_effect)
if(!used_item && !isturf(attacked_atom))
used_item = miner_saw
return ..()
/mob/living/basic/boss/blood_drunk_miner/adjust_health(amount, updating_health = TRUE, forced = FALSE)
var/adjustment_amount = amount * 0.1
if(world.time + adjustment_amount > next_move)
changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly
return ..()
/// Handles spawning a death effect when the blood-drunk miner dies. Tied to COMSIG_LIVING_DROP_LOOT so the timings of spawning the effect should approximately work out with the loot appearing.
/mob/living/basic/boss/blood_drunk_miner/proc/death_effect(datum/source, list/spawn_loot, gibbed)
SIGNAL_HANDLER
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
/// Prevent running into a chasm and other undesirable movements.
/mob/living/basic/boss/blood_drunk_miner/proc/on_premove(datum/source, atom/new_location)
if(isnull(new_location))
return
if(new_location.z != z)
return
var/turf/open/locus = get_turf(new_location)
if(!ischasm(locus) || locus.can_cross_safely(src)) // if it's not a chasm we don't care to check the proc.
return
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
/// Prevent their saw from being moved at all
/mob/living/basic/boss/blood_drunk_miner/proc/on_saw_premove(datum/source, atom/new_location)
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
/// Handles our attack behavior when we're doing melee attacks to override the default basic melee attack behavior when our AI calls upon us to use it.
/// Namely, we just use the miner saw to rapidly hit the target multiple times
/mob/living/basic/boss/blood_drunk_miner/proc/attack_override(mob/living/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
if(!istype(target, /mob/living))
return
var/mob/living/victim = target
if(should_devour(target))
devour(target)
return COMPONENT_HOSTILE_NO_ATTACK
changeNext_move(CLICK_CD_MELEE)
victim.visible_message(
span_danger("[src] slashes at [victim] with [p_their()] cleaving saw!"),
span_userdanger("You are slashed at by [src]'s cleaving saw!"),
)
var/datum/callback/melee_callback = CALLBACK(miner_saw, TYPE_PROC_REF(/obj/item/melee/cleaving_saw/miner, melee_attack_chain), src, victim, modifiers)
var/delay = 0.2 SECONDS
for(var/i in 1 to rapid_melee_hits)
addtimer(melee_callback, (i - 1) * delay)
post_attack_effects(victim, modifiers)
return COMPONENT_HOSTILE_NO_ATTACK
/// Hook for potential additional behaviors after attacking
/mob/living/basic/boss/blood_drunk_miner/proc/post_attack_effects(mob/living/victim, list/modifiers)
return
@@ -0,0 +1,66 @@
/// AI for handling blood-drunk miner behavior
/// General consideration is as follows:
/// - If in PKA range, shoot PKA
/// - If not in PKA range, dash attack on the target
/// - If in melee range, use melee attacks (depending on saw state)
/// - After attacks, transform saw state from open to closed.
/datum/ai_controller/blood_drunk_miner
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/no_gutted_mobs,
BB_TARGET_MINIMUM_STAT = DEAD,
BB_AGGRO_RANGE = 18, // oh fuck oh shit
BB_BDM_RANGED_ATTACK_COOLDOWN = 0,
)
movement_delay = 0.3 SECONDS
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/escape_captivity,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/attack_obstacle_in_path,
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka,
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)
/// Parent type that contains key logic important for subsequent abilities
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk
/// Range where we determine what distance we're at. If higher, we consider ourselves out of PKA range and will dash attack instead. Inclusive when it comes to choosing to shoot PKA.
var/pka_range = 3
/// Check our blackboard to see if we are able to use a ranged ability in the first place
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action)
. = ..()
if(controller.blackboard[BB_BDM_RANGED_ATTACK_COOLDOWN] > world.time)
return FALSE
controller.override_blackboard_key(BB_BDM_RANGED_ATTACK_COOLDOWN, world.time + controller.blackboard[BB_BDM_RANGED_ATTACK_COOLDOWN_DURATION])
/// The BDM will preferentially shoot its PKA within range over other abilities
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka
ability_key = BB_BDM_KINETIC_ACCELERATOR_ABILITY
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/shoot_pka/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action)
. = ..()
// do not shoot the PKA if we are not in the right range
var/mob/living/pawn = controller.pawn
var/mob/living/victim = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(get_dist(pawn, victim) >= pka_range)
return FALSE
return TRUE
/// The BDM will dash attack if not in PKA range
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack
ability_key = BB_BDM_DASH_ATTACK_ABILITY
/datum/ai_planning_subtree/targeted_mob_ability/blood_drunk/dash_attack/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action)
. = ..()
// only dash attack if we are out of PKA range
var/mob/living/pawn = controller.pawn
var/mob/living/victim = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(get_dist(pawn, victim) < pka_range)
return FALSE
return TRUE
/datum/ai_controller/blood_drunk_miner/doom
movement_delay = 0.8 SECONDS
@@ -0,0 +1,35 @@
/// A slightly nerfed saw as the normal one is much too murdery.
/obj/item/melee/cleaving_saw/miner
force = 6
open_force = 10
/obj/item/melee/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user)
target.add_stun_absorption(source = "miner", duration = 1 SECONDS, priority = INFINITY)
return ..()
/obj/projectile/kinetic/miner
damage = 20
speed = 1.1
icon_state = "ka_tracer"
range = 4
/obj/effect/temp_visual/dir_setting/miner_death
icon_state = "miner_death"
duration = 15
/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir)
. = ..()
INVOKE_ASYNC(src, PROC_REF(fade_out))
/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out()
var/matrix/our_matrix = new
our_matrix.Turn(pick(90, 270))
var/final_dir = dir
if(dir & (EAST|WEST)) //Facing east or west
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
animate(src, transform = our_matrix, pixel_y = -6, dir = final_dir, time = 2, easing = QUAD_EASING)
sleep(0.5 SECONDS)
animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = SINE_EASING|EASE_IN, flags = ANIMATION_PARALLEL)
sleep(0.4 SECONDS)
animate(src, alpha = 0, time = 6, easing = SINE_EASING|EASE_OUT, flags = ANIMATION_PARALLEL)
@@ -0,0 +1,35 @@
#define HUNTER_DASH_PROBABILITY 12
/// heals slightly on melee hits
/mob/living/basic/boss/blood_drunk_miner/guidance
/mob/living/basic/boss/blood_drunk_miner/guidance/attack_override(mob/living/source, atom/target, proximity, modifiers)
. = ..()
if(. & COMPONENT_HOSTILE_NO_ATTACK)
adjust_health(-2 * rapid_melee_hits)
/// Better at dash attacking
/mob/living/basic/boss/blood_drunk_miner/hunter
/mob/living/basic/boss/blood_drunk_miner/hunter/post_attack_effects(mob/living/victim, list/modifiers)
if(!prob(HUNTER_DASH_PROBABILITY))
return
var/dash_attack = ai_controller.blackboard[BB_BDM_DASH_ATTACK_ABILITY]
if(!isnull(dash_attack))
INVOKE_ASYNC(dash_attack, TYPE_PROC_REF(/datum/action, Trigger), src, NONE, victim)
/mob/living/basic/boss/blood_drunk_miner/doom
name = "hostile-environment miner"
desc = "A miner destined to hop across dimensions for all eternity, hunting anomalous creatures."
speed = 8
ranged_attack_cooldown_duration = 0.8 SECONDS
ai_controller = /datum/ai_controller/blood_drunk_miner/doom
/mob/living/basic/boss/blood_drunk_miner/doom/Initialize(mapload)
. = ..()
var/datum/action/cooldown/dash_ability = ai_controller.blackboard[BB_BDM_DASH_ABILITY]
if(!isnull(dash_ability))
dash_ability.cooldown_time = 0.8 SECONDS
#undef HUNTER_DASH_PROBABILITY
+54 -23
View File
@@ -20,32 +20,38 @@
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
/// Name for the GPS signal of the megafauna
var/gps_name = null
/// What crusher trophy this mob drops, if any
var/crusher_loot
/// What crusher trophy/trophies this mob drops, if any
/// Should be wrapped in a list for sanity when we pass it to the element.
var/list/crusher_loot = null
/// Loot dropped on death in normal circumstances
var/list/regular_loot = list()
/// What achievements do we give our defeater?
var/list/achievements = null
/// What type of achievement we give for crusher kills, if any.
var/crusher_achievement_type = null
/// What memory to give to victor who have killed us, if any.
var/victor_memory_type = null
/mob/living/basic/boss/Initialize(mapload)
. = ..()
AddElement(/datum/element/wall_tearer, tear_time = 1 SECONDS)
if(gps_name)
AddComponent(/datum/component/gps, gps_name)
if(crusher_loot)
AddElement(\
/datum/element/crusher_loot,\
trophy_type = crusher_loot,\
guaranteed_drop = 0.6,\
drop_immediately = basic_mob_flags & DEL_ON_DEATH,\
)
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
add_traits(list(TRAIT_NO_TELEPORT, TRAIT_MARTIAL_ARTS_IMMUNE, TRAIT_LAVA_IMMUNE,TRAIT_ASHSTORM_IMMUNE, TRAIT_NO_FLOATING_ANIM), MEGAFAUNA_TRAIT)
add_traits(list(TRAIT_NO_TELEPORT, TRAIT_MARTIAL_ARTS_IMMUNE, TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_NO_FLOATING_ANIM), MEGAFAUNA_TRAIT)
AddComponent(/datum/component/seethrough_mob)
AddElement(/datum/element/simple_flying)
AddElement(/datum/element/death_drops, string_list(regular_loot))
handle_crusher_loot()
handle_achievements()
/mob/living/basic/boss/gib()
if(health > 0)
return
return ..()
/mob/living/basic/boss/dust(just_ash, drop_items, force)
/mob/living/basic/boss/dust(just_ash, drop_items, give_moodlet, force)
if(!force && health > 0)
return
return ..()
@@ -55,13 +61,30 @@
return
return ..()
/mob/living/basic/boss/ex_act(severity, target)
switch (severity)
if (EXPLODE_DEVASTATE)
adjust_brute_loss(250)
if (EXPLODE_HEAVY)
adjust_brute_loss(100)
if (EXPLODE_LIGHT)
adjust_brute_loss(50)
return TRUE
/mob/living/basic/boss/early_melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
. = ..()
if(!. || !istype(target))
return
if(target.stat == DEAD || (target.health <= HEALTH_THRESHOLD_DEAD && HAS_TRAIT(target, TRAIT_NODEATH)))
if(should_devour(target))
devour(target)
/// Determines if this mob is worth devouring
/mob/living/basic/boss/proc/should_devour(mob/living/victim)
return victim.stat == DEAD || (victim.health <= HEALTH_THRESHOLD_DEAD && HAS_TRAIT(victim, TRAIT_NODEATH))
/// Devours a target and restores health to the megafauna
/mob/living/basic/boss/proc/devour(mob/living/victim)
if(isnull(victim) || victim.has_status_effect(/datum/status_effect/gutted))
@@ -79,20 +102,28 @@
victim.apply_status_effect(/datum/status_effect/gutted)
return TRUE
/// Small little taunt when we epically troll someone
/mob/living/basic/boss/proc/celebrate_kill(mob/living/poor_sap)
visible_message(
span_danger("[src] disembowels [poor_sap]!"),
span_userdanger("You feast on [poor_sap]'s organs, restoring your health!"))
span_userdanger("You feast on [poor_sap]'s organs, restoring your health!"),
)
/mob/living/basic/boss/ex_act(severity, target)
switch (severity)
if (EXPLODE_DEVASTATE)
adjust_brute_loss(250)
/// Handles adding all relevant achievements when applicable (probably when we are defeated)
/// Achievements being null/no length is handled in the element itself.
/mob/living/basic/boss/proc/handle_achievements()
if(length(achievements) <= 0)
return
AddElement(/datum/element/kill_achievement, string_list(achievements), crusher_achievement_type, victor_memory_type)
if (EXPLODE_HEAVY)
adjust_brute_loss(100)
/// Handles adding crusher loot when applicable (probably when we are defeated)
/mob/living/basic/boss/proc/handle_crusher_loot()
if(isnull(crusher_loot))
return
AddElement(\
/datum/element/crusher_loot,\
trophy_type = string_list(crusher_loot),\
guaranteed_drop = 0.6,\
drop_immediately = basic_mob_flags & DEL_ON_DEATH,\
)
if (EXPLODE_LIGHT)
adjust_brute_loss(50)
return TRUE
@@ -21,8 +21,18 @@
speed = 3.5 //dont make this any faster PLEASE
gps_name = "L-4 Biohazard Beacon"
ai_controller = /datum/ai_controller/basic_controller/thing_boss
crusher_loot = /obj/item/crusher_trophy/flesh_glob
crusher_loot = list(/obj/item/crusher_trophy/flesh_glob)
mouse_opacity = MOUSE_OPACITY_OPAQUE
achievements = list(
/datum/award/achievement/boss/boss_killer,
/datum/award/achievement/boss/thething_kill,
/datum/award/score/boss_score,
/datum/award/score/thething_score,
)
crusher_achievement_type = /datum/award/achievement/boss/thething_crusher
victor_memory_type = /datum/memory/megafauna_slayer
/// Current phase of the boss fight
var/phase = 1
/// Time the Thing will be invulnerable between phases
@@ -41,13 +51,7 @@
/mob/living/basic/boss/thing/Initialize(mapload)
. = ..()
var/static/list/achievements = list(
/datum/award/achievement/boss/boss_killer,
/datum/award/achievement/boss/thething_kill,
/datum/award/score/boss_score,
/datum/award/score/thething_score,
)
AddElement(/datum/element/kill_achievement, achievements, /datum/award/achievement/boss/thething_crusher, /datum/memory/megafauna_slayer)
AddElement(/datum/element/death_drops, /obj/item/keycard/thing_boss, FALSE)
var/static/list/innate_actions = list(
+12 -6
View File
@@ -102,6 +102,10 @@ GLOBAL_LIST_INIT(command_strings, list(
)
///name of the UI we will attempt to open
var/bot_ui = "SimpleBot"
// The faction of the bot before it inherited the pai's faction
var/list/original_faction
// The allies of the bot before it inherited the pai's faction
var/list/original_allies
/// If true we will offer this
COOLDOWN_DECLARE(offer_ghosts_cooldown)
@@ -112,7 +116,7 @@ GLOBAL_LIST_INIT(command_strings, list(
AddElement(/datum/element/ai_retaliate)
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(handle_loop_movement))
RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(after_attacked))
RegisterSignal(src, COMSIG_MOB_TRIED_ACCESS, PROC_REF(attempt_access))
RegisterSignal(src, COMSIG_MOB_RETRIEVE_ACCESS, PROC_REF(retrieve_access))
add_traits(list(TRAIT_NO_GLIDE, TRAIT_SILICON_EMOTES_ALLOWED), INNATE_TRAIT)
LoadComponent(/datum/component/bloodysoles/bot)
GLOB.bots_list += src
@@ -706,7 +710,9 @@ GLOBAL_LIST_INIT(command_strings, list(
paicard.pai.mind.transfer_to(src)
to_chat(src, span_notice("You sense your form change as you are uploaded into [src]."))
name = paicard.pai.name
faction = user.faction.Copy()
original_faction = get_faction()
original_allies = allies
SET_FACTION_AND_ALLIES_FROM(src, user)
log_combat(user, paicard.pai, "uploaded to [initial(src.name)],")
return TRUE
@@ -737,7 +743,8 @@ GLOBAL_LIST_INIT(command_strings, list(
to_chat(paicard.pai, span_notice("You feel your control fade as [paicard] ejects from [initial(name)]."))
paicard = null
name = initial(name)
faction = initial(faction)
set_faction(original_faction)
set_allies(original_allies)
remove_all_languages(source = LANGUAGE_PAI)
get_selected_language()
@@ -773,10 +780,9 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/rust_heretic_act()
adjust_brute_loss(400)
/mob/living/basic/bot/proc/attempt_access(mob/bot, obj/door_attempt)
/mob/living/basic/bot/proc/retrieve_access(mob/bot, list/player_access)
SIGNAL_HANDLER
return (door_attempt.check_access(access_card) ? ACCESS_ALLOWED : ACCESS_DISALLOWED)
player_access += access_card.GetAccess()
/mob/living/basic/bot/proc/generate_speak_list()
return null
@@ -161,9 +161,9 @@
/mob/living/basic/bot/cleanbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if(istype(arrived, /obj/item/reagent_containers/cup/bucket) && isnull(build_bucket))
if(istype(arrived, /obj/item/reagent_containers/cup/bucket))
QDEL_NULL(build_bucket)
build_bucket = arrived
return
if(istype(arrived, /obj/item/mop) && isnull(our_mop))
our_mop = arrived
@@ -208,7 +208,7 @@
/mob/living/basic/bot/cleanbot/explode()
var/atom/drop_loc = drop_location()
build_bucket.forceMove(drop_loc)
build_bucket?.forceMove(drop_loc)
new /obj/item/assembly/prox_sensor(drop_loc)
if(weapon)
weapon.force = initial(weapon.force)
@@ -256,11 +256,6 @@
GLOB.janitor_devices -= src
return ..()
/mob/living/basic/bot/cleanbot/proc/apply_custom_bucket(obj/item/custom_bucket)
if(!isnull(build_bucket))
QDEL_NULL(build_bucket)
custom_bucket.forceMove(src)
/mob/living/basic/bot/cleanbot/proc/on_attack_by(datum/source, obj/item/used_item, mob/living/user)
SIGNAL_HANDLER
if(!istype(used_item, /obj/item/knife) || user.combat_mode)
@@ -29,8 +29,6 @@
/datum/action/cooldown/mob_cooldown/bot/foam/Activate(mob/living/firer, atom/target)
owner.visible_message(span_danger("[owner] whirs and bubbles violently, before releasing a plume of froth!"))
var/datum/effect_system/fluid_spread/foam/foam = new
foam.set_up(foam_range, holder = owner, location = owner.loc)
foam.start()
do_foam(foam_range, owner, owner.loc)
StartCooldown()
return TRUE
@@ -185,7 +185,7 @@
continue
if(!HAS_TRAIT(human_target, TRAIT_CLEANBOT_WHISPERER))
continue
if((living_pawn.faction.Find(REF(human_target))))
if(living_pawn.has_ally(REF(human_target)))
continue
return human_target
return null
+1 -1
View File
@@ -109,7 +109,7 @@
/datum/action/cooldown/mob_cooldown/exenterate/proc/slash_em(atom/caster)
for(var/mob/living/victim in range(ability_range, caster))
if(faction_check(victim.faction, immune_factions) && victim.IsReachableBy(owner))
if(victim.has_faction(immune_factions) && victim.IsReachableBy(owner))
continue
to_chat(caster, span_warning("You slice [victim]!"))
to_chat(victim, span_warning("You are cut by [caster]'s blades!"))
@@ -134,9 +134,7 @@
/mob/living/basic/bot/firebot/atmos_expose(datum/gas_mixture/air, exposed_temperature)
if(!COOLDOWN_FINISHED(src, foam_cooldown))
return
var/datum/effect_system/fluid_spread/foam/firefighting/foam = new
foam.set_up(3, holder = src, location = loc)
foam.start()
do_foam(3, src, loc, foam_type = /datum/effect_system/fluid_spread/foam/firefighting)
COOLDOWN_START(src, foam_cooldown, FOAM_INTERVAL)
/mob/living/basic/bot/firebot/proc/spray_water(atom/attacked_atom, list/modifiers)
@@ -68,9 +68,7 @@
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack))
/mob/living/basic/bot/hygienebot/explode()
var/datum/effect_system/fluid_spread/foam/foam = new
foam.set_up(2, holder = src, location = loc)
foam.start()
do_foam(2, src, loc)
return ..()
/mob/living/basic/bot/hygienebot/generate_speak_list()
@@ -99,6 +99,8 @@
var/skin = "generic"
/// How much healing do we do at a time?
var/heal_amount = 2.5
/// How much healing multiplier do we have from upgrades?
VAR_FINAL/heal_multiplier = 1.0
/// Start healing when they have this much damage in a category
var/heal_threshold = 10
/// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types
@@ -163,6 +165,13 @@
if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb)
CONNECT_TO_RND_SERVER_ROUNDSTART(linked_techweb, src)
if(linked_techweb)
RegisterSignal(linked_techweb, COMSIG_TECHWEB_ADD_DESIGN, PROC_REF(on_techweb_research))
RegisterSignal(linked_techweb, COMSIG_TECHWEB_REMOVE_DESIGN, PROC_REF(on_techweb_unresearch))
for(var/datum/design/medibot_upgrade/design in linked_techweb.get_researched_design_datums())
heal_multiplier += design.additive_multiplier
/mob/living/basic/bot/medbot/on_craft_completion(list/components, datum/crafting_recipe/current_recipe, atom/crafter)
. = ..()
var/obj/item/storage/medkit/medkit = locate() in contents
@@ -214,7 +223,6 @@
data["custom_controls"]["speaker"] = medical_mode_flags & MEDBOT_SPEAK_MODE
data["custom_controls"]["crit_alerts"] = medical_mode_flags & MEDBOT_DECLARE_CRIT
data["custom_controls"]["stationary_mode"] = medical_mode_flags & MEDBOT_STATIONARY_MODE
data["custom_controls"]["sync_tech"] = TRUE
return data
// Actions received from TGUI
@@ -237,21 +245,6 @@
medical_mode_flags ^= MEDBOT_DECLARE_CRIT
if("stationary_mode")
medical_mode_flags ^= MEDBOT_STATIONARY_MODE
if("sync_tech")
if(!linked_techweb)
to_chat(user, span_notice("No research techweb connected."))
return
var/oldheal_amount = heal_amount
var/tech_boosters
for(var/index in linked_techweb.researched_designs)
var/datum/design/surgery/healing/design = SSresearch.techweb_design_by_id(index)
if(!istype(design))
continue
tech_boosters++
if(tech_boosters)
heal_amount = (round(tech_boosters * 0.5, 0.1) * initial(heal_amount)) + initial(heal_amount) //every 2 tend wounds tech gives you an extra 100% healing, adjusting for unique branches (combo is bonus)
if(oldheal_amount < heal_amount)
speak("New knowledge found! Surgical efficacy improved to [round(heal_amount/initial(heal_amount)*100)]%!")
update_appearance()
@@ -347,7 +340,7 @@
if(!do_after(src, delay = 2 SECONDS, target = patient, interaction_key = TEND_DAMAGE_INTERACTION))
update_bot_mode(new_mode = BOT_IDLE)
return
var/modified_heal_amount = heal_amount
var/modified_heal_amount = heal_amount * heal_multiplier
var/done_healing = FALSE
if(damage_type_healer == BRUTE && medkit_type == /obj/item/storage/medkit/brute)
modified_heal_amount *= 1.1
@@ -376,6 +369,24 @@
if(patient.IsReachableBy(src))
melee_attack(patient)
/mob/living/basic/bot/medbot/proc/on_techweb_research(datum/source, datum/design/medibot_upgrade/design)
SIGNAL_HANDLER
if(!istype(design))
return
heal_multiplier += design.additive_multiplier
INVOKE_ASYNC(src, PROC_REF(speak), "New knowledge found! Surgical efficacy improved to [round(heal_multiplier * 100)]%!")
/mob/living/basic/bot/medbot/proc/on_techweb_unresearch(datum/source, datum/design/medibot_upgrade/design)
SIGNAL_HANDLER
if(!istype(design))
return
heal_multiplier -= design.additive_multiplier
INVOKE_ASYNC(src, PROC_REF(speak), "Error! Surgical efficacy decreased to [round(heal_multiplier * 100)]%!")
/datum/id_trim/medibot
assignment = JOB_MEDIBOT
trim_state = "trim_paramedic"
+2 -1
View File
@@ -42,6 +42,7 @@
/mob/living/basic/clown/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NAIVE, INNATE_TRAIT)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SHOE)
AddComponent(/datum/component/ai_retaliate_advanced, CALLBACK(src, PROC_REF(retaliate_callback)))
ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, emotes)
@@ -148,7 +149,7 @@
/obj/effect/gibspawner/human,
/obj/item/clothing/mask/gas/clown_hat,
/obj/item/food/meatclown,
/obj/item/stack/sheet/animalhide/human,
/obj/item/stack/sheet/animalhide/carbon/human,
)
emotes = list(
BB_EMOTE_SAY = list(
@@ -180,7 +180,7 @@
/mob/living/basic/construct/harvester/heretic/grant_abilities()
AddElement(/datum/element/wall_walker, or_trait = TRAIT_RUSTY)
AddElement(/datum/element/leeching_walk)
AddElement(/datum/element/rust_healing)
AddComponent(\
/datum/component/amputating_limbs,\
surgery_time = 1.5 SECONDS,\
@@ -206,7 +206,7 @@
// These aren't friends they're assholes
// Don't let them be near you!
/mob/living/basic/construct/harvester/heretic/Life(seconds_per_tick, times_fired)
/mob/living/basic/construct/harvester/heretic/Life(seconds_per_tick)
. = ..()
if(!.) //dead or deleted
return
@@ -53,7 +53,7 @@
/mob/living/basic/vatbeast/tamed(mob/living/tamer, obj/item/food)
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/vatbeast)
faction = list(FACTION_NEUTRAL)
set_faction(list(FACTION_NEUTRAL))
/mob/living/basic/vatbeast/proc/add_cell_sample()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_VATBEAST, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
@@ -34,6 +34,7 @@
var/grow_as = /mob/living/basic/chicken
/datum/emote/chick
abstract_type = /datum/emote/chick
mob_type_allowed_typecache = /mob/living/basic/chick
mob_type_blacklist_typecache = list()
@@ -39,6 +39,7 @@ GLOBAL_VAR_INIT(chicken_count, 0)
var/fertile = TRUE
/datum/emote/chicken
abstract_type = /datum/emote/chicken
mob_type_allowed_typecache = /mob/living/basic/chicken
mob_type_blacklist_typecache = list()
@@ -37,6 +37,7 @@
var/milked_reagent = /datum/reagent/consumable/milk
/datum/emote/cow
abstract_type = /datum/emote/cow
mob_type_allowed_typecache = /mob/living/basic/cow
mob_type_blacklist_typecache = list()
@@ -37,7 +37,7 @@
/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer, atom/food)
. = ..()
///stop killing my FRIENDS
faction |= tamer.faction
APPLY_FACTION_AND_ALLIES_FROM(src, tamer)
/datum/ai_controller/basic_controller/cow/moonicorn
blackboard = list(
@@ -44,7 +44,7 @@
if(!stat && !user.combat_mode)
to_chat(user, span_nicegreen("[src] whispers you some intense wisdoms and then disappears!"))
user.mind?.adjust_experience(granted_wisdom, granted_experience)
do_smoke(1, holder = src, location = get_turf(src))
do_smoke(1, src, get_turf(src))
qdel(src)
return
return ..()
@@ -1,4 +1,5 @@
/datum/emote/gorilla
abstract_type = /datum/emote/gorilla
mob_type_allowed_typecache = /mob/living/basic/gorilla
mob_type_blacklist_typecache = list()
@@ -29,6 +29,7 @@
ai_controller = /datum/ai_controller/basic_controller/pig
/datum/emote/pig
abstract_type = /datum/emote/pig
mob_type_allowed_typecache = /mob/living/basic/pig
mob_type_blacklist_typecache = list()
@@ -39,6 +40,7 @@
emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
vary = TRUE
sound = SFX_PIG_OINK
/mob/living/basic/pig/Initialize(mapload)
. = ..()
AddElement(/datum/element/pet_bonus, "oink")
@@ -34,6 +34,7 @@
var/list/ponycolors = list("#cc8c5d", "#cc8c5d")
/datum/emote/pony
abstract_type = /datum/emote/pony
mob_type_allowed_typecache = /mob/living/basic/pony
mob_type_blacklist_typecache = list()
@@ -40,10 +40,11 @@
var/icon_prefix = "rabbit"
/datum/emote/rabbit
abstract_type = /datum/emote/rabbit
mob_type_allowed_typecache = /mob/living/basic/rabbit
mob_type_blacklist_typecache = list()
/datum/emote/rabbit
/datum/emote/rabbit/hop
key = "hop"
key_third_person = "hops"
message = "hops around happily!"
@@ -14,6 +14,7 @@
gold_core_spawnable = HOSTILE_SPAWN
basic_mob_flags = DEL_ON_DEATH
status_flags = CANPUSH
mob_biotypes = MOB_MINERAL
response_help_continuous = "rubs"
response_help_simple = "rub"
@@ -15,7 +15,6 @@
mob_biotypes = MOB_SPECIAL
sentience_type = SENTIENCE_HUMANOID
hud_type = /datum/hud/guardian
faction = list()
speed = 0
status_flags = CANPUSH
maxHealth = INFINITY // The spirit itself is invincible and passes damage to its host
@@ -137,7 +136,7 @@
/mob/living/basic/guardian/proc/guardian_recolour()
if (isnull(client))
return
var/chosen_guardian_colour = tgui_color_picker(src, "What would you like your colour to be?", "Choose Your Colour", "#ffffff") // BUBBERSTATION EDIT: TGUI COLOR PICKER
var/chosen_guardian_colour = tgui_color_picker(src, "What would you like your colour to be?", "Choose Your Colour", COLOR_WHITE)
if (isnull(chosen_guardian_colour)) //redo proc until we get a color
to_chat(src, span_warning("Invalid colour, please try again."))
return guardian_recolour()
@@ -200,7 +199,7 @@
/mob/living/basic/guardian/gib()
death(TRUE)
/mob/living/basic/guardian/dust(just_ash, drop_items, force)
/mob/living/basic/guardian/dust(just_ash, drop_items, give_moodlet, force)
death(TRUE)
/// Link up with a summoner mob.
@@ -225,8 +224,9 @@
if (mind)
mind.enslave_mind_to_creator(to_who)
else //mindless guardian, manually give them factions
faction += summoner.faction
summoner.faction += "[REF(src)]"
add_faction(summoner.get_faction())
add_ally(summoner.allies)
summoner.add_ally(src)
remove_all_languages(LANGUAGE_MASTER)
copy_languages(to_who, LANGUAGE_MASTER) // make sure holoparasites speak same language as master
RegisterSignal(to_who, COMSIG_QDELETING, PROC_REF(on_summoner_deletion))
@@ -251,8 +251,8 @@
unleash()
UnregisterSignal(summoner, list(COMSIG_QDELETING, COMSIG_LIVING_ON_WABBAJACKED, COMSIG_LIVING_SHAPESHIFTED, COMSIG_LIVING_UNSHAPESHIFTED))
if (different_person)
summoner.faction -= "[REF(src)]"
faction -= summoner.faction
summoner.remove_ally(src)
remove_faction(summoner.get_faction())
mind?.remove_all_antag_datums()
if (!length(summoner.get_all_linked_holoparasites() - src))
for (var/action_type in control_actions)
@@ -135,6 +135,7 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial())
to_chat(user, guardian_theme.get_fluff_string(summoned_guardian.guardian_type))
to_chat(user, replacetext(success_message, "%GUARDIAN", mob_name))
summoned_guardian.client?.init_verbs()
summoned_guardian.updatehealth() // Set the initial health hud
return summoned_guardian
/// Checks to ensure we're still capable of using the radial selector
@@ -30,9 +30,14 @@
return
. += span_info("It is holding [internal_storage.examine_title(user)] in its internal storage.")
/mob/living/basic/guardian/dextrous/manifest_effects()
. = ..()
REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, GUARDIAN_RECALLED)
/mob/living/basic/guardian/dextrous/recall_effects()
. = ..()
drop_all_held_items()
ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, GUARDIAN_RECALLED)
// Bullshit related to having a fake pocket begins here
@@ -48,7 +48,7 @@
source.set_fire_stacks(0, remove_wet_stacks = FALSE)
/// Maintain our summoner at a stable body temperature
/mob/living/basic/guardian/gaseous/proc/on_summoner_life(mob/living/source, seconds_per_tick, times_fired)
/mob/living/basic/guardian/gaseous/proc/on_summoner_life(mob/living/source, seconds_per_tick)
SIGNAL_HANDLER
source.adjust_bodytemperature(get_temp_change_amount((summoner.get_body_temp_normal() - summoner.bodytemperature), temp_stabilization_rate * seconds_per_tick))
@@ -82,6 +82,8 @@
button_icon_state = "smoke"
cooldown_time = 0 SECONDS // We're here for the interface not the cooldown
click_to_activate = FALSE
/// Particle effect we use to show smoke
VAR_PRIVATE/obj/effect/abstract/particle_holder/mob_smoke
/// Gas being expelled.
var/active_gas = null
/// Associative list of types of gases to moles we create every life tick.
@@ -132,13 +134,10 @@
owner.investigate_log("set their gas type to [picked_gas].", INVESTIGATE_ATMOS)
var/had_gas = !isnull(active_gas)
active_gas = gas_type
if(isnull(owner.particles))
owner.particles = new /particles/smoke/steam()
owner.particles.position = list(-1, 8, 0)
owner.particles.fadein = 5
owner.particles.height = 200
if(isnull(mob_smoke))
mob_smoke = new(owner, /particles/smoke/steam/guardian)
var/datum/gas/chosen_gas = active_gas // Casting it so that we can access gas vars in initial, it's still a typepath
owner.particles.color = initial(chosen_gas.primary_color)
mob_smoke.color = initial(chosen_gas.primary_color)
if (!had_gas)
RegisterSignal(owner, COMSIG_LIVING_LIFE, PROC_REF(on_life))
@@ -148,11 +147,11 @@
if (!isnull(active_gas))
to_chat(src, span_notice("You stop releasing gas."))
active_gas = null
QDEL_NULL(owner.particles)
QDEL_NULL(mob_smoke)
UnregisterSignal(owner, COMSIG_LIVING_LIFE)
/// Release gas every life tick while active
/datum/action/cooldown/mob_cooldown/expel_gas/proc/on_life(datum/source, seconds_per_tick, times_fired)
/datum/action/cooldown/mob_cooldown/expel_gas/proc/on_life(datum/source, seconds_per_tick)
SIGNAL_HANDLER
if (isnull(active_gas))
return // We shouldn't even be registered at this point but just in case
@@ -71,7 +71,9 @@
/// Overlay for our protection shield.
var/mutable_appearance/shield_overlay
/// Damage coefficients when shielded
var/list/shielded_damage = list(BRUTE = 0.05, BURN = 0.05, TOX = 0.05, STAMINA = 0, OXY = 0.05)
var/static/list/shielded_damage = list(BRUTE = 0.05, BURN = 0.05, TOX = 0.05, STAMINA = 0, OXY = 0.05)
// What the damage coefficients were previously
var/list/original_damage_coeff
/datum/status_effect/protector_shield/on_apply()
if (isguardian(owner))
@@ -86,6 +88,7 @@
if (isbasicmob(owner)) // Better hope you are or this status is doing basically nothing useful for you
var/mob/living/basic/basic_owner = owner
original_damage_coeff = basic_owner.damage_coeff
basic_owner.damage_coeff = shielded_damage
to_chat(owner, span_bolddanger("You enter protection mode."))
@@ -101,7 +104,7 @@
if (isbasicmob(owner))
var/mob/living/basic/basic_owner = owner
basic_owner.damage_coeff = initial(basic_owner.damage_coeff)
basic_owner.damage_coeff = original_damage_coeff
to_chat(owner, span_bolddanger("You return to your normal mode."))
UnregisterSignal(owner, list(COMSIG_ATOM_UPDATE_OVERLAYS) + COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES)
@@ -24,6 +24,7 @@
action_text = "",\
complete_text = "",\
required_modifier = RIGHT_CLICK,\
extra_checks = CALLBACK(src, PROC_REF(is_deployed)),\
after_healed = CALLBACK(src, PROC_REF(after_healed)),\
)
@@ -21,6 +21,6 @@
)
grant_actions_by_list(actions_to_add)
/mob/living/basic/heretic_summon/ash_spirit/Life(seconds_per_tick, times_fired)
/mob/living/basic/heretic_summon/ash_spirit/Life(seconds_per_tick)
. = ..()
adjust_brute_loss(-3) // 3 health passively healing
@@ -15,6 +15,7 @@
maxHealth = 65
health = 65
sight = SEE_MOBS|SEE_OBJS|SEE_TURFS
mob_biotypes = MOB_ORGANIC
/// List of innate abilities we have to add.
var/static/list/innate_abilities = list(
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/long = null,
@@ -13,6 +13,7 @@
sight = SEE_TURFS
speed = 1
ai_controller = /datum/ai_controller/basic_controller/rust_walker
mob_biotypes = MOB_ROBOTIC|MOB_MINERAL
/mob/living/basic/heretic_summon/rust_walker/Initialize(mapload)
. = ..()
@@ -28,6 +29,9 @@
. = ..()
update_appearance(UPDATE_ICON_STATE)
/mob/living/basic/heretic_summon/rust_walker/do_rust_heretic_act(atom/target)
target.rust_heretic_act(RUST_RESISTANCE_TITANIUM)
/mob/living/basic/heretic_summon/rust_walker/update_icon_state()
. = ..()
if(stat == DEAD) // We usually delete on death but just in case
@@ -38,7 +42,7 @@
icon_state = "[base_icon_state]_s"
icon_living = icon_state
/mob/living/basic/heretic_summon/rust_walker/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/heretic_summon/rust_walker/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return
@@ -7,6 +7,7 @@
icon_gib = "syndicate_gib"
mouse_opacity = MOUSE_OPACITY_ICON
basic_mob_flags = DEL_ON_DEATH
mob_biotypes = MOB_ORGANIC|MOB_MINERAL|MOB_MINING
speed = 2
maxHealth = 150
health = 150
@@ -83,5 +83,5 @@
. = ..()
if(isnull(.))
return
faction = new_friend.faction.Copy()
SET_FACTION_AND_ALLIES_FROM(src, new_friend)
visible_message(span_notice("[src] lowers [src.p_their()] snout at [new_friend]'s offering and begins to wag [src.p_their()] tail."))
@@ -50,9 +50,11 @@
try_replicate()
/// Full setup for illusion mobs to lessen code duplication in the individual files.
/mob/living/basic/illusion/proc/full_setup(mob/living/original, mob/living/target_mob = null, list/faction = null, life = 5 SECONDS, hp = 100, damage = 0, replicate = 0)
/mob/living/basic/illusion/proc/full_setup(mob/living/original, mob/living/target_mob = null, list/faction_override = null, life = 5 SECONDS, hp = 100, damage = 0, replicate = 0)
mock_as(original, life, hp, damage, replicate)
set_faction(faction)
SET_FACTION_AND_ALLIES_FROM(src, original)
if(faction_override)
set_faction(faction_override)
set_target(target_mob)
/// Gives the illusion a target to focus on in whatever behavior it wants to engage as.
@@ -61,12 +63,6 @@
return
ai_controller.set_blackboard_key(target_key, target_mob)
/// Sets the faction of the illusion
/mob/living/basic/illusion/proc/set_faction(list/new_faction)
if(!new_faction) // can be list with no length or null
return
faction = new_faction.Copy()
/// Does the actual work of setting up the illusion's appearance and some other functionality.
/mob/living/basic/illusion/proc/mock_as(mob/living/original, life, hp, damage, replicate)
if(QDELETED(original))
@@ -83,7 +79,7 @@
melee_damage_upper = damage
multiply_chance = replicate
faction -= FACTION_NEUTRAL
remove_faction(FACTION_NEUTRAL)
transform = initial(transform)
pixel_x = base_pixel_x
pixel_y = base_pixel_y
@@ -104,7 +100,7 @@
new_clone.full_setup(
parent_mob,
target_mob = ai_controller.blackboard[target_key],
faction = faction,
faction_override = faction,
life = 8 SECONDS,
hp = health / 2,
damage = melee_damage_upper,
@@ -62,11 +62,11 @@
taste_description = "french cuisine"
taste_mult = 1.3
/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/carbon/poisoned_mob, seconds_per_tick, times_fired)
/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/carbon/poisoned_mob, seconds_per_tick, metabolization_ratio)
. = ..()
if(volume <= 5)
return
if(poisoned_mob.adjust_tox_loss(2.5 * REM * seconds_per_tick, updating_health = FALSE))
if(poisoned_mob.adjust_tox_loss(1.25 * metabolization_ratio * seconds_per_tick, updating_health = FALSE))
return UPDATE_MOB_HEALTH
// bubble ability structure
@@ -247,4 +247,4 @@
/datum/action/cooldown/spell/conjure/limit_summons/create_suicide_toads/post_summon(atom/summoned_object, atom/cast_on)
. = ..()
var/mob/living/summoned_toad = summoned_object
summoned_toad.faction = owner.faction ///so they dont attack the leaper or the wizard master
SET_FACTION_AND_ALLIES_FROM(summoned_toad, owner) // so they dont attack the leaper or the wizard master
@@ -16,7 +16,7 @@
return ..()
var/mob/living/living_target = target
if(FACTION_JUNGLE in living_target.faction)
if(living_target.has_faction(FACTION_JUNGLE))
return BULLET_ACT_BLOCK
return ..()
@@ -185,7 +185,7 @@
return
tangle_ability.Trigger(target = victim)
/mob/living/basic/venus_human_trap/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/venus_human_trap/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.)
return FALSE
@@ -7,6 +7,7 @@
icon_dead = "basilisk_dead"
speak_emote = list("chimes")
damage_coeff = list(BRUTE = 1, BURN = 0.1, TOX = 1, STAMINA = 0, OXY = 1)
mob_biotypes = parent_type::mob_biotypes | MOB_MINERAL
speed = 20
maxHealth = 200
health = 200
@@ -96,7 +96,7 @@
. += span_info("Someone appears to have attached a saddle to this one.")
// Goliaths can summon tentacles more frequently as they take damage, scary.
/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item)
/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing)
. = ..()
if (. <= 0)
return
@@ -159,7 +159,7 @@
. = ..()
if(isnull(.))
return
faction = new_friend.faction.Copy()
SET_FACTION_AND_ALLIES_FROM(src, new_friend)
/mob/living/basic/mining/goliath/RangedAttack(atom/atom_target, modifiers)
tentacles?.Trigger(target = atom_target)
@@ -209,7 +209,7 @@
/// List of places we might spawn a tentacle, if we're alive
var/list/tentacle_target_turfs
/mob/living/basic/mining/goliath/ancient/immortal/Life(seconds_per_tick, times_fired)
/mob/living/basic/mining/goliath/ancient/immortal/Life(seconds_per_tick)
. = ..()
if (!. || !isturf(loc))
return
@@ -42,7 +42,7 @@
for(var/mob/living/potential_friend in oview(9, living_pawn))
if(!isashwalker(potential_friend))
continue
if((living_pawn.faction.Find(REF(potential_friend))))
if(living_pawn.has_ally(REF(potential_friend)))
continue
living_pawn.befriend(potential_friend)
to_chat(potential_friend, span_nicegreen("[living_pawn] looks at you with endearing eyes!"))
@@ -59,7 +59,7 @@
/// Spawns a worm on the specified turf
/mob/living/basic/mining/hivelord/proc/complete_spawn(turf/spawn_turf)
var/mob/living/brood = new death_spawn_type(spawn_turf)
brood.faction = faction
SET_FACTION_AND_ALLIES_FROM(brood, src)
brood.ai_controller?.set_blackboard_key(ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET])
brood.dir = get_dir(src, spawn_turf)
@@ -79,7 +79,7 @@
/// Actually create a mob
/datum/action/cooldown/mob_cooldown/hivelord_spawn/proc/complete_spawn(turf/spawn_turf, target)
var/mob/living/brood = new spawn_type(spawn_turf)
brood.faction = owner.faction
SET_FACTION_AND_ALLIES_FROM(brood, owner)
brood.ai_controller?.set_blackboard_key(ai_target_key, target)
brood.dir = get_dir(owner, spawn_turf)
@@ -85,7 +85,7 @@
var/spawn_type = get_legion_type(target)
var/mob/living/basic/mining/legion/new_legion = new spawn_type(loc)
new_legion.consume(target)
new_legion.faction = faction.Copy()
new_legion.set_faction(get_faction())
qdel(src)
/// Returns the kind of legion we make out of the target
@@ -99,9 +99,9 @@
/// Sets someone as our creator, mostly so you can't use skulls to heal yourself
/mob/living/basic/mining/legion_brood/proc/assign_creator(mob/living/creator, copy_full_faction = TRUE)
if (copy_full_faction)
faction = creator.faction.Copy()
set_faction(creator.get_faction())
else
faction |= REF(creator)
add_ally(creator)
created_by = WEAKREF(creator)
ai_controller?.set_blackboard_key(BB_LEGION_BROOD_CREATOR, creator)
RegisterSignal(creator, COMSIG_QDELETING, PROC_REF(creator_destroyed))
@@ -86,7 +86,7 @@
skull.melee_attack(target)
return TRUE
/obj/item/organ/legion_tumour/on_life(seconds_per_tick, times_fired)
/obj/item/organ/legion_tumour/on_life(seconds_per_tick)
. = ..()
if (QDELETED(src) || QDELETED(owner))
return
@@ -97,8 +97,8 @@
. = ..()
if(isnull(.))
return
faction |= new_friend.faction
faction -= FACTION_MINING
APPLY_FACTION_AND_ALLIES_FROM(src, new_friend)
remove_faction(FACTION_MINING)
/mob/living/basic/mining/lobstrosity/mind_initialize()
. = ..()
@@ -171,6 +171,7 @@
var/was_tamed = FALSE
/datum/emote/lobstrosity_juvenile
abstract_type = /datum/emote/lobstrosity_juvenile
mob_type_allowed_typecache = /mob/living/basic/mining/lobstrosity/juvenile
mob_type_blacklist_typecache = list()
@@ -58,6 +58,6 @@
/mob/living/basic/mining/proc/check_ashwalker_peace_violation(datum/source, mob/living/carbon/human/possible_ashwalker)
SIGNAL_HANDLER
if(!isashwalker(possible_ashwalker) || !(FACTION_ASHWALKER in faction))
if(!isashwalker(possible_ashwalker) || !has_faction(FACTION_ASHWALKER))
return
faction.Remove(FACTION_ASHWALKER)
remove_faction(FACTION_ASHWALKER)
@@ -31,7 +31,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
///check for faction if not a ash walker, otherwise just attack
/datum/targeting_strategy/basic/mook/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target)
if(FACTION_ASHWALKER in living_mob.faction)
if(living_mob.has_faction(FACTION_ASHWALKER))
return FALSE
return ..()
@@ -176,7 +176,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
if (0.8 to 0.999)
. += span_notice("[p_They()] [p_have()] a few minor bruises and scratches.")
/mob/living/basic/raptor/Life(seconds_per_tick, times_fired)
/mob/living/basic/raptor/Life(seconds_per_tick)
. = ..()
if (growth_stage != RAPTOR_BABY || HAS_TRAIT(src, TRAIT_STASIS) || stat == DEAD)
return
@@ -280,7 +280,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
return partner.raptor_color.guaranteed_crossbreeds[raptor_color.type]
// We've got all the colors in our family tree and aren't rolling a guarantee, bingo
if (length(inherited_stats.parent_colors | partner.inherited_stats.parent_colors | raptor_color.type | partner.raptor_color.type) == length(GLOB.raptor_colors))
if (length(inherited_stats.parent_colors | partner.inherited_stats.parent_colors | raptor_color.type | partner.raptor_color.type) >= (length(GLOB.raptor_colors) - 1))
return /datum/raptor_color/black
var/list/prob_list = list()
@@ -1,5 +1,5 @@
/obj/item/raptor_dex
name = "RaptorDex"
name = "\improper RaptorDex"
desc = "A device used to analyze lavaland raptors!"
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "raptor_dex"
@@ -32,6 +32,8 @@
scan_data["raptor_speed"] = my_raptor.speed + intent_mod?.multiplicative_slowdown
scan_data["raptor_color"] = my_raptor.name
scan_data["raptor_gender"] = my_raptor.gender
scan_data["raptor_growth"] = my_raptor.growth_progress
scan_data["can_grow"] = my_raptor.growth_stage != RAPTOR_ADULT
scan_data["raptor_description"] = my_raptor.raptor_color.description
var/happiness_percentage = my_raptor.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
@@ -276,4 +276,4 @@
if(isnull(required_access))
var/datum/id_trim/access_card = SSid_access.trim_singletons_by_path[/datum/id_trim/job/shaft_miner]
required_access = access_card.access
AddElement(/datum/element/mob_access, required_access)
AddComponent(/datum/component/simple_access, required_access)
@@ -116,7 +116,7 @@
if(isgroundlessturf(my_turf))
return FALSE
var/obj/effect/mine/minebot/my_mine = new(my_turf)
my_mine.ignore_list = owner.faction.Copy()
my_mine.ignore_list = owner.get_faction()
playsound(my_turf, 'sound/items/weapons/armbomb.ogg', 20)
StartCooldown()
return TRUE
@@ -153,9 +153,7 @@
/obj/effect/temp_visual/falling_rocket/proc/create_explosion()
playsound(src, 'sound/items/weapons/minebot_rocket.ogg', 100, FALSE)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(1, holder = src)
smoke.start()
do_smoke(1, src, loc)
for(var/mob/living/living_target in oview(explosion_radius, src))
if(living_target.incorporeal_move)
continue
@@ -171,9 +169,7 @@
/obj/effect/mine/minebot/mineEffect(mob/living/victim)
if(!istype(victim))
return
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = src)
smoke.start()
do_smoke(0, src, loc)
playsound(src, 'sound/effects/explosion/explosion3.ogg', 100)
victim.apply_damage(damage_to_apply)
@@ -183,6 +179,6 @@
if(!isliving(on_who))
return ..()
var/mob/living/stepped_mob = on_who
if(FACTION_NEUTRAL in stepped_mob.faction)
if(stepped_mob.has_faction(FACTION_NEUTRAL))
return FALSE
return ..()
@@ -74,7 +74,7 @@
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_DRONE_DEFEND, /mob/living/basic/node_drone)
return
var/mob/living/living_pawn = controller.pawn
if(!living_pawn.faction.Find(REF(target)))
if(!living_pawn.has_ally(target))
controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_DRONE_DEFEND)
return
if(target.health < (target.maxHealth * 0.75) && controller.blackboard[BB_MINEBOT_REPAIR_DRONE])
@@ -63,6 +63,7 @@
var/datum/callback/post_birth_callback
/datum/emote/cat
abstract_type = /datum/emote/cat
mob_type_allowed_typecache = /mob/living/basic/pet/cat
mob_type_blacklist_typecache = list()
@@ -53,6 +53,7 @@
var/cult_icon_state
/datum/emote/dog
abstract_type = /datum/emote/dog
mob_type_allowed_typecache = /mob/living/basic/pet/dog
mob_type_blacklist_typecache = list()
@@ -35,7 +35,7 @@
update_appearance()
AddElement(/datum/element/strippable, length(strippable_inventory_slots) ? create_strippable_list(strippable_inventory_slots) : GLOB.strippable_corgi_items)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_CORGI, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
RegisterSignal(src, COMSIG_MOB_TRIED_ACCESS, PROC_REF(on_tried_access))
RegisterSignal(src, COMSIG_MOB_RETRIEVE_ACCESS, PROC_REF(retrieve_access))
RegisterSignals(src, list(COMSIG_BASICMOB_LOOK_ALIVE, COMSIG_BASICMOB_LOOK_DEAD), PROC_REF(on_appearance_change))
if(can_breed)
add_breeding_component()
@@ -267,10 +267,10 @@
var/datum/dog_fashion/equipped_back_fashion_item = new inventory_back.dog_fashion(src)
equipped_back_fashion_item.apply(src)
///Handler for COMSIG_MOB_TRIED_ACCESS
/mob/living/basic/pet/dog/corgi/proc/on_tried_access(mob/accessor, obj/locked_thing)
///Handler for COMSIG_MOB_RETRIEVE_ACCESS
/mob/living/basic/pet/dog/corgi/proc/retrieve_access(mob/accessor, list/player_access)
SIGNAL_HANDLER
return locked_thing?.check_access(access_card) ? ACCESS_ALLOWED : ACCESS_DISALLOWED
player_access += access_card.GetAccess()
///Handles updating any existing overlays for the corgi (such as fashion items) when it changes how it appears, as in, dead or alive.
/mob/living/basic/pet/dog/corgi/proc/on_appearance_change()
@@ -117,7 +117,7 @@
name = new_name
real_name = new_name
/mob/living/basic/pet/dog/breaddog/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/pet/dog/breaddog/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return
@@ -39,6 +39,7 @@
)
/datum/emote/fox
abstract_type = /datum/emote/fox
mob_type_allowed_typecache = /mob/living/basic/pet/fox
mob_type_blacklist_typecache = list()
@@ -51,13 +51,13 @@
else
. += span_notice("It looks like it has already made its delivery.")
/mob/living/basic/pet/gondola/gondolapod/setOpened()
/mob/living/basic/pet/gondola/gondolapod/set_opened()
opened = TRUE
layer = initial(layer)
update_appearance()
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, setClosed)), 5 SECONDS)
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, set_closed)), 5 SECONDS)
/mob/living/basic/pet/gondola/gondolapod/setClosed()
/mob/living/basic/pet/gondola/gondolapod/set_closed()
opened = FALSE
layer = LOW_MOB_LAYER
update_appearance()
@@ -57,7 +57,7 @@
return potential_victim
if(!ishuman(potential_victim))
continue
if(living_pawn.faction.Find(REF(potential_victim)))
if(living_pawn.has_ally(potential_victim))
continue //dont steal from friends
if(holding_valuable(controller, potential_victim))
controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE)
@@ -20,6 +20,7 @@
var/obj/carried_egg
/datum/emote/penguin
abstract_type = /datum/emote/penguin
mob_type_allowed_typecache = /mob/living/basic/pet/penguin
mob_type_blacklist_typecache = list()
@@ -51,7 +51,7 @@
/datum/ai_behavior/find_and_set/friendly_cultist/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
var/mob/living/living_pawn = controller.pawn
for(var/mob/living/carbon/possible_cultist in oview(search_range, controller.pawn))
if(IS_CULTIST(possible_cultist) && !(living_pawn.faction.Find(REF(possible_cultist))))
if(IS_CULTIST(possible_cultist) && !(living_pawn.has_ally(possible_cultist)))
return possible_cultist
return null
@@ -38,6 +38,7 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth)
ai_controller = /datum/ai_controller/basic_controller/sloth
/datum/emote/sloth
abstract_type = /datum/emote/sloth
mob_type_allowed_typecache = /mob/living/basic/sloth
mob_type_blacklist_typecache = list()
@@ -7,8 +7,8 @@
name = "\improper Cybersun AI Core"
desc = "An evil looking computer."
icon = 'icons/mob/silicon/ai.dmi'
icon_state = "ai-red"
icon_living = "ai-red"
icon_state = "ai-core"
icon_living = "ai-core"
gender = NEUTER
status_flags = NONE
basic_mob_flags = MOB_ROBOTIC
@@ -45,9 +45,34 @@
BARRAGE_ABILITY_TYPEPATH = BB_CYBERSUN_CORE_BARRAGE,
)
grant_actions_by_list(innate_actions)
update_appearance()
if(mapload && donk_ai_master)
return INITIALIZE_HINT_LATELOAD
/mob/living/basic/cybersun_ai_core/update_overlays()
. = ..()
// Lights
var/lights_state = "lights_active"
var/mutable_appearance/lights_overlay = mutable_appearance(icon, lights_state)
lights_overlay.layer = FLOAT_LAYER
lights_overlay.appearance_flags = RESET_COLOR | KEEP_APART
. += lights_overlay
. += emissive_appearance(icon, lights_state, src)
// Display
var/screen_state = "ai-red"
if(stat == DEAD)
screen_state = "ai-red_dead"
var/mutable_appearance/screen_overlay = mutable_appearance(icon, screen_state)
screen_overlay.layer = FLOAT_LAYER + 0.1
screen_overlay.appearance_flags = RESET_COLOR | KEEP_APART
. += screen_overlay
. += emissive_appearance(icon, screen_state, src)
/mob/living/basic/cybersun_ai_core/LateInitialize()
SSqueuelinks.add_to_queue(src, selfdestruct_queue_id)
SSqueuelinks.pop_link(selfdestruct_queue_id)
@@ -64,11 +89,13 @@
/obj/effect/temp_visual/cybersun_ai_core_death
icon = 'icons/mob/silicon/ai.dmi'
icon_state = "ai-red_dead"
icon_state = "ai-core"
duration = 2 SECONDS
/obj/effect/temp_visual/cybersun_ai_core_death/Initialize(mapload)
. = ..()
add_overlay("ai-red_dead")
playsound(src, 'sound/misc/metal_creak.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
Shake(1, 0, 2 SECONDS)
addtimer(CALLBACK(src, PROC_REF(gib)), duration - 1, TIMER_DELETE_ME)
@@ -59,7 +59,7 @@
if(!QDELETED(bodypart))
qdel(bodypart)
/mob/living/basic/living_limb_flesh/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/living_limb_flesh/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(stat == DEAD)
return
@@ -153,7 +153,7 @@
var/obj/item/bodypart/new_bodypart = new part_type()
forceMove(new_bodypart)
new_bodypart.replace_limb(target, TRUE)
new_bodypart.replace_limb(target)
register_to_limb(new_bodypart)
/mob/living/basic/living_limb_flesh/proc/owner_shocked(datum/source, shock_damage, shock_source, siemens_coeff, flags)
@@ -25,7 +25,7 @@
icon_state = "floor"
icon_living = "floor"
mob_size = MOB_SIZE_HUGE
mob_biotypes = MOB_SPECIAL
mob_biotypes = MOB_SPECIAL|MOB_MINERAL
status_flags = NONE
death_message = ""
unsuitable_atmos_damage = 0
@@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list(
maxHealth = 250
health = 250
gender = NEUTER
mob_biotypes = NONE
mob_biotypes = MOB_MINERAL
pass_flags = PASSFLAPS
melee_damage_lower = 8
melee_damage_upper = 12
@@ -283,7 +283,7 @@ GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list(
copied_ref = null
return ..()
/mob/living/basic/mimic/copy/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/mimic/copy/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return
@@ -50,7 +50,7 @@
/datum/ai_planning_subtree/random_speech/when_has_target/mimic_machine
speech_chance = 7
emote_hear = list()
emote_hear = null
speak = list(
"HUMANS ARE IMPERFECT!",
"YOU SHALL BE ASSIMILATED!",
@@ -3,7 +3,7 @@
desc = "A real bonefied skeleton, doesn't seem like it wants to socialize."
gender = NEUTER
icon = 'icons/mob/simple/simple_human.dmi'
mob_biotypes = MOB_UNDEAD|MOB_HUMANOID
mob_biotypes = MOB_UNDEAD|MOB_HUMANOID|MOB_SKELETAL
speak_emote = list("rattles")
maxHealth = 40
health = 40
@@ -112,7 +112,7 @@
var/list/created_slimes = list(src)
var/list/slime_friends = list()
for(var/faction_member in faction)
for(var/faction_member in get_faction())
var/mob/living/possible_friend = locate(faction_member) in GLOB.mob_living_list
if(QDELETED(possible_friend))
continue
@@ -29,12 +29,12 @@
// Check if the slime can drain the target
/datum/ai_behavior/find_hunt_target/find_slime_food/valid_dinner(mob/living/basic/slime/hunter, mob/living/dinner, radius, datum/ai_controller/controller, seconds_per_tick)
var/static/list/slime_faction
if(isnull(slime_faction)) // This might look silly but by having this list pointer precached we can take advantage of the faster inline macro for this hot proc.
slime_faction = string_list(list(FACTION_SLIME))
if(REF(dinner) in hunter.faction) //Don't eat our friends...
return FALSE
var/static/list/slime_faction = list(FACTION_SLIME)
if(faction_check(slime_faction, dinner.faction)) //Don't try to eat slimy things, no matter how hungry we are. Anyone else can be betrayed.
// Macro here to squeeze as much speed as possible because this code gets exponentially HOT if people are doing a lot of xenobio.
if(FAST_FACTION_CHECK(slime_faction, dinner.get_faction(), hunter.allies, dinner.allies, FALSE)) // Don't try to eat our friends, or slimy things, no matter how hungry we are. Anyone else can be betrayed.
return FALSE
if(!hunter.can_feed_on(dinner, check_adjacent = FALSE)) //Are they tasty to slimes?
@@ -1,4 +1,5 @@
/datum/emote/slime
abstract_type = /datum/emote/slime
mob_type_allowed_typecache = /mob/living/basic/slime
mob_type_blacklist_typecache = list()
@@ -17,7 +17,7 @@
balloon_alert(src, "not hungry!")
return FALSE
if(check_friendship && (REF(meal) in faction))
if(check_friendship && has_faction(REF(meal)))
return FALSE
if(check_adjacent && (!Adjacent(meal) || !isturf(loc)))
+2 -2
View File
@@ -1,5 +1,5 @@
/mob/living/basic/slime/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/slime/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return
@@ -9,7 +9,7 @@
handle_slime_stasis()
/mob/living/basic/slime/handle_environment(datum/gas_mixture/environment, seconds_per_tick, times_fired)
/mob/living/basic/slime/handle_environment(datum/gas_mixture/environment, seconds_per_tick)
..()
if(bodytemperature <= (T0C - 40)) // stun temperature
apply_status_effect(/datum/status_effect/freon, SLIME_COLD)
+5 -4
View File
@@ -157,7 +157,7 @@
/mob/living/basic/slime/random
/mob/living/basic/slime/random/Initialize(mapload, new_colour, new_life_stage)
return ..(mapload, null, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY)
return ..(mapload, SLIME_TYPE_RANDOM, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY)
///Friendly docile subtype
/mob/living/basic/slime/pet
@@ -197,10 +197,11 @@
if(slime_type.transparent)
alpha = SLIME_TRANSPARENCY_ALPHA
icon_living = "[slime_type.colour]-[life_stage]"
icon_dead = !cores ? "[slime_type.colour]-cut" : "[slime_type.colour]-[life_stage]-dead"
if(stat != DEAD)
icon_state = "[slime_type.colour]-[life_stage]"
icon_state = icon_living
if(current_mood && current_mood != SLIME_MOOD_NONE && !stat)
add_overlay("aslime-[current_mood]")
else
@@ -273,8 +274,8 @@
/// Sets the slime's type, name and its icons.
/// If not provided with a type it will instead be random
/mob/living/basic/slime/proc/set_slime_type(new_type = null)
if(isnull(new_type))
/mob/living/basic/slime/proc/set_slime_type(new_type = SLIME_TYPE_RANDOM)
if(new_type == SLIME_TYPE_RANDOM)
new_type = pick(subtypesof(/datum/slime_type))
slime_type = possible_slime_types[new_type]
@@ -37,6 +37,7 @@
ai_controller = /datum/ai_controller/basic_controller/ant
/datum/emote/ant
abstract_type = /datum/emote/ant
mob_type_allowed_typecache = /mob/living/basic/ant
mob_type_blacklist_typecache = list()
@@ -84,6 +84,7 @@
))
/datum/emote/carp
abstract_type = /datum/emote/carp
mob_type_allowed_typecache = /mob/living/basic/carp
mob_type_blacklist_typecache = list()
@@ -33,7 +33,6 @@
unsuitable_cold_damage = 4
unsuitable_heat_damage = 4
combat_mode = TRUE
faction = list() // No allies but yourself
pass_flags = PASSTABLE
unique_name = TRUE
lighting_cutoff_red = 22
@@ -119,4 +119,4 @@
/mob/living/basic/eyeball/tamed(mob/living/tamer, atom/food)
spin(spintime = 2 SECONDS, speed = 1)
//become passive to the humens
faction |= tamer.faction
APPLY_FACTION_AND_ALLIES_FROM(src, tamer)
@@ -67,7 +67,7 @@
/mob/living/basic/lightgeist/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE)
. = ..()
if (. && isliving(target))
faction |= REF(target) // Anyone we heal will treat us as a friend
add_ally(target) // Anyone we heal will treat us as a friend
/mob/living/basic/lightgeist/ghost()
. = ..()
@@ -21,7 +21,6 @@
response_help_simple = "pet"
response_disarm_continuous = "gently pushes"
response_disarm_simple = "gently push"
faction = list()
ai_controller = /datum/ai_controller/basic_controller/meteor_heart
habitable_atmos = null
minimum_survivable_temperature = 0
@@ -60,7 +60,7 @@
return
make_migo_sound()
/mob/living/basic/migo/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/migo/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return
@@ -172,16 +172,16 @@
return
to_chat(affected_mob, span_notice("This food has a funny taste!"))
/datum/reagent/rat_spit/overdose_start(mob/living/affected_mob)
/datum/reagent/rat_spit/overdose_start(mob/living/affected_mob, metabolization_ratio)
. = ..()
var/mob/living/carbon/victim = affected_mob
if (istype(victim) && !(FACTION_RAT in victim.faction))
if (istype(victim) && !victim.has_faction(FACTION_RAT))
to_chat(victim, span_userdanger("With this last sip, you feel your body convulsing horribly from the contents you've ingested. As you contemplate your actions, you sense an awakened kinship with rat-kind and their newly risen leader!"))
victim.faction |= FACTION_RAT
victim.add_faction(FACTION_RAT)
victim.vomit(VOMIT_CATEGORY_DEFAULT)
metabolization_rate = 10 * REAGENTS_METABOLISM
/datum/reagent/rat_spit/on_mob_life(mob/living/carbon/affected_mob)
/datum/reagent/rat_spit/on_mob_life(mob/living/carbon/affected_mob, metabolization_ratio)
. = ..()
if(prob(15))
to_chat(affected_mob, span_notice("You feel queasy!"))
@@ -132,7 +132,7 @@
return TRUE
/// Signal Handler Injection to handle Life() stuff for revenants
/mob/living/basic/revenant/proc/on_life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/revenant/proc/on_life(seconds_per_tick = SSMOBS_DT)
SIGNAL_HANDLER
if(dormant)
@@ -283,7 +283,7 @@
/mob/living/basic/revenant/med_hud_set_status()
return //we use no hud
/mob/living/basic/revenant/dust(just_ash, drop_items, force)
/mob/living/basic/revenant/dust(just_ash, drop_items, give_moodlet, force)
death()
/mob/living/basic/revenant/gib()
@@ -119,9 +119,7 @@
continue
light.visible_message(span_boldwarning("[light] suddenly flares brightly and begins to spark!"))
var/datum/effect_system/spark_spread/light_sparks = new /datum/effect_system/spark_spread()
light_sparks.set_up(4, 0, light)
light_sparks.start()
do_sparks(4, FALSE, light)
new /obj/effect/temp_visual/revenant(get_turf(light))
addtimer(CALLBACK(src, PROC_REF(overload_shock), light, caster), 2 SECONDS)
@@ -37,6 +37,7 @@
ai_controller = /datum/ai_controller/basic_controller/simple/simple_retaliate
/datum/emote/roro
abstract_type = /datum/emote/roro
mob_type_allowed_typecache = /mob/living/basic/roro
mob_type_blacklist_typecache = list()
@@ -46,7 +46,7 @@
removed_from.remove_traits(applied_traits + TRAIT_WING_BUFFET_TIRED, REF(src))
/// Decay our accumulated additional tiredness
/datum/action/cooldown/mob_cooldown/wing_buffet/proc/on_life(mob/living/liver, seconds_per_tick, times_fired)
/datum/action/cooldown/mob_cooldown/wing_buffet/proc/on_life(mob/living/liver, seconds_per_tick)
SIGNAL_HANDLER
if (liver.stat == DEAD)
return // not so life now buddy
@@ -140,7 +140,7 @@
/// Select scale colour with the colour picker
/mob/living/basic/space_dragon/proc/select_colour()
chosen_colour = tgui_color_picker(src, "What colour would you like to be?" , "Colour Selection", COLOR_WHITE) // BUBBERSTATION EDIT: TGUI COLOR PICKER
chosen_colour = tgui_color_picker(src, "What colour would you like to be?" ,"Colour Selection", COLOR_WHITE)
if(!chosen_colour) // Redo proc until we get a color
to_chat(src, span_warning("Not a valid colour, please try again."))
select_colour()
@@ -55,6 +55,7 @@
var/apply_spider_antag = TRUE
/datum/emote/spider
abstract_type = /datum/emote/spider
mob_type_allowed_typecache = /mob/living/basic/spider
mob_type_blacklist_typecache = list()
@@ -152,7 +153,7 @@
var/mob/living/basic/spider/giant/grown = change_mob_type(grow_as, get_turf(src), initial(grow_as.name))
ADD_TRAIT(grown, TRAIT_WAS_EVOLVED, REF(src))
grown.faction = faction.Copy()
SET_FACTION_AND_ALLIES_FROM(grown, src)
grown.directive = directive
grown.set_name()
grown.set_brute_loss(get_brute_loss())
@@ -55,7 +55,7 @@
/datum/action/cooldown/mob_cooldown/lay_eggs/proc/lay_egg()
var/obj/effect/mob_spawn/ghost_role/spider/new_eggs = new egg_type(get_turf(owner))
new_eggs.faction = owner.faction
SET_FACTION_AND_ALLIES_FROM(new_eggs, owner)
var/datum/action/cooldown/mob_cooldown/set_spider_directive/spider_directive = locate() in owner.actions
if (spider_directive)
new_eggs.directive = spider_directive.current_directive
@@ -9,7 +9,7 @@
icon_dead = "human_male"
gender = NEUTER
combat_mode = TRUE
mob_biotypes = MOB_HUMANOID
mob_biotypes = MOB_HUMANOID|MOB_MINERAL
gold_core_spawnable = NO_SPAWN
response_help_continuous = "touches"
@@ -10,7 +10,7 @@
gender = NEUTER
status_flags = CANPUSH
mob_biotypes = MOB_BUG|MOB_ROBOTIC
mob_biotypes = MOB_BUG|MOB_ROBOTIC|MOB_MINERAL
speak_emote = list("vibrates")
+1 -1
View File
@@ -59,7 +59,7 @@
AddElement(/datum/element/death_drops, /obj/item/stack/sheet/mineral/wood)
AddComponent(/datum/component/aggro_emote, emote_list = string_list(list("growls")), emote_chance = 20)
/mob/living/basic/tree/Life(seconds_per_tick = SSMOBS_DT, times_fired)
/mob/living/basic/tree/Life(seconds_per_tick = SSMOBS_DT)
. = ..()
if(!.) //dead or deleted
return

Some files were not shown because too many files have changed in this diff Show More