Skip to content

Commit 836198a

Browse files
committed
add: Лицехват - моб (#6612) [testmerge][59dc99e]
1 parent 2de8e50 commit 836198a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+923
-203
lines changed

code/__DEFINES/dcs/signals.dm

+3
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,9 @@
711711
/// from base of /client/proc/handle_popup_close() : (window_id)
712712
#define COMSIG_POPUP_CLEARED "popup_cleared"
713713

714+
/// from base of /datum/status_effect/Destroy() : (effect_type)
715+
#define COMSIG_MOB_STATUS_EFFECT_ENDED "mob_status_effect_ended"
716+
714717
/// Source: /mob/living/UnarmedAttack (atom/atom, proximity_flag)
715718
#define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack"
716719

code/__DEFINES/gamemode.dm

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone"
6969
#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel"
7070
#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva"
71+
#define SPECIAL_ROLE_FACEHUGGER "Facehugger"
7172
#define SPECIAL_ROLE_TERROR_SPIDER "Terror Spider"
7273
#define SPECIAL_ROLE_TERROR_QUEEN "Terror Queen"
7374
#define SPECIAL_ROLE_TERROR_PRINCE "Terror Prince"

code/__DEFINES/is_helpers.dm

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#define isaliensentinel(A) (istype(A, /mob/living/carbon/alien/humanoid/sentinel))
4040

4141
#define isalienqueen(A) (istype(A, /mob/living/carbon/alien/humanoid/queen))
42+
#define isfacehugger(A) (istype(A, /mob/living/simple_animal/hostile/facehugger))
43+
#define isfacehugger_mask(A) (istype(A, /obj/item/clothing/mask/facehugger) && !istype(A, /obj/item/clothing/mask/facehugger/toy))
4244

4345
// Simple animals
4446
// #define issimple_animal(A) (istype(A, /mob/living/simple_animal)) use isanimal(A) instead
@@ -92,6 +94,8 @@
9294

9395
#define isradio(A) istype(A, /obj/item/radio)
9496

97+
#define isflower(A) istype(A, /obj/item/twohanded/required/kirbyplants)
98+
9599
#define isclothing(A) (istype(A, /obj/item/clothing))
96100

97101
#define is_internal_organ(A) istype(A, /obj/item/organ/internal)
@@ -153,6 +157,7 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
153157

154158
//Structures
155159
#define isstructure(A) (istype(A, /obj/structure))
160+
#define istable(A) (istype(A, /obj/structure/table))
156161

157162
// Misc
158163
#define isclient(A) istype(A, /client)

code/__DEFINES/obj_flags.dm

+4
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,7 @@
7878
/// Flags for the pod_flags var on /obj/structure/closet/supplypod
7979
#define FIRST_SOUNDS (1<<0) // If it shouldn't play sounds the first time it lands, used for reverse mode
8080

81+
82+
#define HUMAN_HOLDER (1<<0)
83+
84+
#define ALIEN_HOLDER (1<<1)

code/__DEFINES/traits/sources.dm

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
/// Traits applied to a silicon mob by their model.
7676
#define ROBOT_TRAIT "robot_trait"
7777

78+
#define FACEHUGER_TRAIT "facehugger_trait"
79+
7880
/// A trait gained from a mob's leap action, like the leaper
7981
#define LEAPING_TRAIT "leaping"
8082

@@ -99,6 +101,8 @@
99101
/// Trait associated to lying down (having a [lying_angle] of a different value than zero).
100102
#define LYING_DOWN_TRAIT "lying-down"
101103

104+
#define THROWED_TRAIT "throwed_trait"
105+
102106
#define NO_GRAVITY_TRAIT "no-gravity"
103107
#define NEGATIVE_GRAVITY_TRAIT "negative-gravity"
104108

code/_globalvars/lists/mobs.dm

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(mob_list) //List of all mobs, including clientless
2121
GLOBAL_LIST_EMPTY(silicon_mob_list) //List of all silicon mobs, including clientless
2222
GLOBAL_LIST_EMPTY(mob_living_list) //all instances of /mob/living and subtypes
2323
GLOBAL_LIST_EMPTY(carbon_list) //all instances of /mob/living/carbon and subtypes, notably does not contain simple animals
24+
GLOBAL_LIST_EMPTY(aliens_list) //all instances of xenomorph mobs
2425
GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and subtypes
2526
GLOBAL_LIST_EMPTY(spirits) //List of all the spirits, including Masks
2627
GLOBAL_LIST_EMPTY(alive_mob_list) //List of all alive mobs, including clientless. Excludes /mob/new_player

code/controllers/subsystem/movement/movement_types.dm

+17-2
Original file line numberDiff line numberDiff line change
@@ -524,10 +524,25 @@
524524
if(!.)
525525
return
526526
var/atom/old_loc = moving.loc
527-
var/turf/next = get_step_to(moving, target)
528-
moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
527+
var/turf/next = get_next_step()
528+
if(isliving(moving))
529+
var/mob/living/moving_mob = moving
530+
if(!(moving_mob.mobility_flags & MOBILITY_MOVE))
531+
return MOVELOOP_FAILURE
532+
moving?.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
529533
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
530534

535+
/datum/move_loop/has_target/dist_bound/move_to/proc/get_next_step()
536+
return get_step_to(moving, target)
537+
538+
/datum/controller/subsystem/move_manager/proc/move_to_pathfind(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
539+
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_to/pathfind, priority, flags, extra_info, delay, timeout, chasing, min_dist)
540+
541+
/datum/move_loop/has_target/dist_bound/move_to/pathfind
542+
543+
/datum/move_loop/has_target/dist_bound/move_to/pathfind/get_next_step()
544+
var/list/points = get_path_to(moving, target, skip_first = TRUE)
545+
return (points.len)? points[1] : null
531546

532547
/**
533548
* Wrapper around walk_away()

code/datums/components/ghost_direct_control.dm

+27-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
var/ban_type
99
/// Check Syndicate ban
1010
var/ban_syndicate
11+
/// Check ghost respawnability
12+
var/respawnable_check = TRUE
13+
/// Check antaghud use
14+
var/check_antaghud = TRUE
1115
/// Any extra checks which need to run before we take over
1216
var/datum/callback/extra_control_checks
1317
/// Callback run after someone successfully takes over the body
@@ -25,6 +29,7 @@
2529
poll_candidates = TRUE,
2630
antag_age_check = TRUE,
2731
check_antaghud = TRUE,
32+
respawnable_check = TRUE,
2833
poll_length = 10 SECONDS,
2934
ban_syndicate = FALSE,
3035
assumed_control_message = null,
@@ -43,6 +48,8 @@
4348
src.extra_control_checks = extra_control_checks
4449
src.after_assumed_control = after_assumed_control
4550
src.question_text = question_text
51+
src.respawnable_check = respawnable_check
52+
src.check_antaghud = check_antaghud
4653

4754
LAZYADD(GLOB.mob_spawners[format_text("[initial(mob_parent.name)]")], mob_parent)
4855

@@ -55,21 +62,29 @@
5562
RegisterSignal(parent, COMSIG_LIVING_EXAMINE, PROC_REF(on_examined))
5663
RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login))
5764
RegisterSignal(parent, COMSIG_IS_GHOST_CONTROLABLE, PROC_REF(on_ghost_controlable_check))
65+
RegisterSignal(parent, COMSIG_MOB_DEATH, PROC_REF(on_death))
5866

5967
/datum/component/ghost_direct_control/UnregisterFromParent()
60-
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_LIVING_EXAMINE, COMSIG_MOB_LOGIN))
68+
UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_LIVING_EXAMINE, COMSIG_MOB_LOGIN, COMSIG_MOB_DEATH))
6169
return ..()
6270

6371
/datum/component/ghost_direct_control/Destroy(force)
6472
extra_control_checks = null
6573
after_assumed_control = null
74+
remove_spawner()
75+
return ..()
6676

67-
var/mob/mob_parent = parent
68-
var/list/spawners = GLOB.mob_spawners[format_text("[initial(mob_parent.name)]")]
69-
LAZYREMOVE(spawners, mob_parent)
77+
/datum/component/ghost_direct_control/proc/on_death(datum/source)
78+
SIGNAL_HANDLER
79+
remove_spawner()
80+
81+
/datum/component/ghost_direct_control/proc/remove_spawner()
82+
var/mob/living/our_mob = parent
83+
var/text = format_text("[initial(our_mob.name)]")
84+
var/list/spawners = GLOB.mob_spawners[text]
85+
LAZYREMOVE(spawners, our_mob)
7086
if(!LAZYLEN(spawners))
71-
GLOB.mob_spawners -= format_text("[initial(mob_parent.name)]")
72-
return ..()
87+
GLOB.mob_spawners -= text
7388

7489
/// Inform ghosts that they can possess this
7590
/datum/component/ghost_direct_control/proc/on_examined(datum/source, mob/user, list/examine_text)
@@ -135,13 +150,18 @@
135150
if(new_body.stat == DEAD)
136151
to_chat(harbinger, span_warning("Это тело умерло, оно бесполезно!"))
137152
return
153+
if(respawnable_check && !(harbinger in GLOB.respawnable_list))
154+
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду.")
155+
return
156+
if(respawnable_check && cannotPossess(harbinger))
157+
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду, активировав антаг худ.")
158+
return
138159
if(new_body.key)
139160
to_chat(harbinger, span_warning("[capitalize(new_body.declent_ru(NOMINATIVE))] уже является разумным!"))
140161
qdel(src)
141162
return
142163
if(extra_control_checks && !extra_control_checks.Invoke(harbinger))
143164
return
144-
145165
add_game_logs("took control of [new_body].", harbinger)
146166
// doesn't transfer mind because that transfers antag datum as well
147167
new_body.key = harbinger.key

code/datums/spawners_menu.dm

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
this["fluff"] = ""
2727
this["uids"] = list()
2828
for(var/spawner_obj in GLOB.mob_spawners[spawner])//each spawner can contain multiple actual spawners, we use only one desc/info
29+
if(isliving(spawner_obj))
30+
var/mob/living/mob = spawner_obj
31+
if(mob.stat == DEAD)
32+
continue
2933
this["uids"] += "\ref[spawner_obj]"
3034
if(!this["desc"]) //haven't set descriptions yet
3135
if(istype(spawner_obj, /obj/effect/mob_spawn))

code/datums/spells/alien_spells/lay_alien_eggs.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "Plant alien eggs"
33
desc = "Allows you to plant alien eggs on your current turf, does not work while in space."
44
base_cooldown = 1 SECONDS
5-
plasma_cost = 75
5+
plasma_cost = 95
66
weed_type = /obj/structure/alien/egg
77
weed_name = "alien egg"
88
action_icon_state = "alien_egg"

code/datums/status_effects/status_effect.dm

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
owner.clear_alert(id)
7575
LAZYREMOVE(owner.status_effects, src)
7676
on_remove()
77+
SEND_SIGNAL(owner, COMSIG_MOB_STATUS_EFFECT_ENDED, type)
7778
owner = null
7879
if(linked_alert)
7980
linked_alert.attached_effect = null

code/game/objects/items.dm

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g
2424
var/slot_flags_2 = NONE
2525
/// This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc.
2626
var/flags_inv = NONE
27+
var/holder_flags = NONE
2728
/// These flags will be added/removed (^=) to/from flags_inv in [/proc/check_obscured_slots()]
2829
/// if check_transparent argument is set to `TRUE`. Used in carbon's update icons shenanigans.
2930
/// Example: you can see someone's mask through their transparent visor, but you cannot reach it

code/game/objects/obj_defense.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
armor_protection = armor.getRating(damage_flag)
3434
if(armor_protection) //Only apply weak-against-armor/hollowpoint effects if there actually IS armor.
3535
armor_protection = clamp(armor_protection - armour_penetration, min(armor_protection, 0), 100)
36-
return round(damage_amount * (100 - armor_protection)*0.01, DAMAGE_PRECISION)
36+
return round(damage_amount * (100 - armor_protection) * 0.01, DAMAGE_PRECISION)
3737

3838

3939
/// Proc for recovering atom_integrity. Returns the amount repaired by

code/game/objects/structures/aliens.dm

+48-19
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,13 @@
169169
if(user.a_intent == INTENT_HARM)
170170
return ..()
171171

172-
try_switch_state(user)
172+
return try_switch_state(user)
173+
174+
/obj/structure/alien/resin/door/attack_animal(mob/living/simple_animal/M)
175+
if(M.a_intent == INTENT_HARM)
176+
return ..()
177+
178+
return try_switch_state(M)
173179

174180

175181
/obj/structure/alien/resin/door/attack_hand(mob/living/user)
@@ -206,18 +212,21 @@
206212

207213
/obj/structure/alien/resin/door/proc/try_switch_state(atom/movable/user)
208214
if(operating)
209-
return
215+
return FALSE
210216

211217
add_fingerprint(user)
212-
213-
if(!isalien(user))
214-
return
218+
if(!isliving(user))
219+
return FALSE
220+
var/mob/living/mob = user
221+
if(!isalien(user) && !("alien" in mob.faction))
222+
return FALSE
215223

216224
var/mob/living/carbon/alien/alien = user
217225
if(alien.incapacitated())
218-
return
226+
return FALSE
219227

220228
switch_state()
229+
return TRUE
221230

222231

223232
/obj/structure/alien/resin/door/proc/switch_state()
@@ -442,6 +451,7 @@
442451
#define GROWN 3
443452
#define MIN_GROWTH_TIME 1200 //time it takes to grow a hugger
444453
#define MAX_GROWTH_TIME 1800
454+
#define PROXIMITY_RADIUS 5
445455

446456
/obj/structure/alien/egg
447457
name = "egg"
@@ -468,11 +478,13 @@
468478
update_icon(UPDATE_ICON_STATE)
469479
switch(status)
470480
if(GROWING)
471-
new /obj/item/clothing/mask/facehugger(src)
481+
var/mob/living/simple_animal/hostile/facehugger/hugger = new(src)
482+
hugger.LoseTarget()
472483
addtimer(CALLBACK(src, PROC_REF(Grow)), rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME))
473484
if(GROWN)
474-
new /obj/item/clothing/mask/facehugger(src)
475-
AddComponent(/datum/component/proximity_monitor)
485+
var/mob/living/simple_animal/hostile/facehugger/hugger = new(src)
486+
hugger.LoseTarget()
487+
AddComponent(/datum/component/proximity_monitor, PROXIMITY_RADIUS)
476488
if(BURST)
477489
obj_integrity = integrity_failure
478490

@@ -512,41 +524,55 @@
512524

513525

514526
/obj/structure/alien/egg/proc/GetFacehugger()
515-
return locate(/obj/item/clothing/mask/facehugger) in contents
527+
return locate(/mob/living/simple_animal/hostile/facehugger) in contents
516528

517529

518530
/obj/structure/alien/egg/proc/Grow()
519531
status = GROWN
520532
update_icon(UPDATE_ICON_STATE)
521-
AddComponent(/datum/component/proximity_monitor)
522-
533+
AddComponent(/datum/component/proximity_monitor, PROXIMITY_RADIUS)
523534

524535
///Need to carry the kill from Burst() to Hatch(), this section handles the alien opening the egg
525-
/obj/structure/alien/egg/proc/Burst(kill = TRUE) //drops and kills the hugger if any is remaining
536+
/obj/structure/alien/egg/proc/Burst(kill = TRUE, atom/movable/trigger) //drops and kills the hugger if any is remaining
526537
if(status == GROWN || status == GROWING)
527538
playsound(get_turf(src), 'sound/creatures/alien/xeno_egg_crack.ogg', 50)
528539
flick("egg_opening", src)
529540
status = BURSTING
530541
qdel(GetComponent(/datum/component/proximity_monitor))
531-
addtimer(CALLBACK(src, PROC_REF(Hatch), kill), 1.5 SECONDS)
542+
addtimer(CALLBACK(src, PROC_REF(Hatch), kill, trigger), 1.5 SECONDS)
532543

533544

534545
///We now check HOW the hugger is hatching, kill carried from Burst() and obj_break()
535-
/obj/structure/alien/egg/proc/Hatch(kill)
546+
/obj/structure/alien/egg/proc/Hatch(kill, atom/movable/trigger)
536547
status = BURST
537548
update_icon(UPDATE_ICON_STATE)
538-
var/obj/item/clothing/mask/facehugger/child = GetFacehugger()
549+
var/mob/living/simple_animal/hostile/facehugger/child = GetFacehugger()
550+
539551
if(!child)
540552
return
553+
541554
child.forceMove(get_turf(src))
555+
child.AddComponent(\
556+
/datum/component/ghost_direct_control,\
557+
ban_type = ROLE_ALIEN,\
558+
poll_candidates = FALSE,\
559+
after_assumed_control = CALLBACK(child, TYPE_PROC_REF(/mob/living/simple_animal/hostile/facehugger, add_datum_if_not_exist)),\
560+
)
542561
if(kill)
543-
child.Die()
562+
child.death()
544563
return
564+
545565
for(var/mob/living/victim in range(1, src))
546566
if(CanHug(victim))
547-
child.Attach(victim)
567+
child.try_hug(victim)
548568
break
549569

570+
if(!CanHug(trigger))
571+
return
572+
573+
child.GiveTarget(trigger)
574+
child.MoveToTarget(list(trigger))
575+
550576

551577
/obj/structure/alien/egg/obj_break(damage_flag)
552578
if(!(obj_flags & NODECONSTRUCT) && status != BURST)
@@ -567,8 +593,10 @@
567593
var/mob/living/carbon/target = AM
568594
if(iscarbon(target) && target.stat == CONSCIOUS && target.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo))
569595
return
596+
if(isalien(target))
597+
return
570598

571-
Burst(kill = FALSE)
599+
Burst(kill = FALSE, trigger = AM)
572600

573601

574602
#undef BURST
@@ -577,6 +605,7 @@
577605
#undef GROWN
578606
#undef MIN_GROWTH_TIME
579607
#undef MAX_GROWTH_TIME
608+
#undef PROXIMITY_RADIUS
580609

581610
#undef ALIEN_RESIN_BURN_MOD
582611
#undef ALIEN_RESIN_BRUTE_MOD

code/game/objects/structures/displaycase.dm

+5
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@
286286
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
287287
req_access = list(ACCESS_RD)
288288

289+
/obj/structure/displaycase/labcage/dump()
290+
var/obj/item/clothing/mask/facehugger/hugger = showpiece
291+
. = ..()
292+
hugger?.check_mob_inside()
293+
289294
/obj/structure/displaycase/stechkin
290295
name = "officer's display case"
291296
desc = "A display case containing a humble stechkin pistol. Never forget your roots."

0 commit comments

Comments
 (0)