Skip to content

Commit 12b9811

Browse files
committed
add: Лицехват - моб (#6612) [testmerge][9181dd1]
1 parent 4ed173f commit 12b9811

Some content is hidden

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

46 files changed

+861
-196
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

+13-1
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

@@ -135,13 +142,18 @@
135142
if(new_body.stat == DEAD)
136143
to_chat(harbinger, span_warning("Это тело умерло, оно бесполезно!"))
137144
return
145+
if(respawnable_check && !(harbinger in GLOB.respawnable_list))
146+
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду.")
147+
return
148+
if(respawnable_check && cannotPossess(harbinger))
149+
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду, активировав антаг худ.")
150+
return
138151
if(new_body.key)
139152
to_chat(harbinger, span_warning("[capitalize(new_body.declent_ru(NOMINATIVE))] уже является разумным!"))
140153
qdel(src)
141154
return
142155
if(extra_control_checks && !extra_control_checks.Invoke(harbinger))
143156
return
144-
145157
add_game_logs("took control of [new_body].", harbinger)
146158
// doesn't transfer mind because that transfers antag datum as well
147159
new_body.key = harbinger.key

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

+42-19
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,14 @@
169169
if(user.a_intent == INTENT_HARM)
170170
return ..()
171171

172-
try_switch_state(user)
172+
return try_switch_state(user)
173173

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)
179+
174180

175181
/obj/structure/alien/resin/door/attack_hand(mob/living/user)
176182
if(!isalien(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/player_controlled/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/player_controlled/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,49 @@
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))
542555
if(kill)
543-
child.Die()
556+
child.death()
544557
return
558+
545559
for(var/mob/living/victim in range(1, src))
546560
if(CanHug(victim))
547-
child.Attach(victim)
561+
child.try_hug(victim)
548562
break
549563

564+
if(!CanHug(trigger))
565+
return
566+
567+
child.GiveTarget(trigger)
568+
child.MoveToTarget(list(trigger))
569+
550570

551571
/obj/structure/alien/egg/obj_break(damage_flag)
552572
if(!(obj_flags & NODECONSTRUCT) && status != BURST)
@@ -567,8 +587,10 @@
567587
var/mob/living/carbon/target = AM
568588
if(iscarbon(target) && target.stat == CONSCIOUS && target.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo))
569589
return
590+
if(isalien(target))
591+
return
570592

571-
Burst(kill = FALSE)
593+
Burst(kill = FALSE, trigger = AM)
572594

573595

574596
#undef BURST
@@ -577,6 +599,7 @@
577599
#undef GROWN
578600
#undef MIN_GROWTH_TIME
579601
#undef MAX_GROWTH_TIME
602+
#undef PROXIMITY_RADIUS
580603

581604
#undef ALIEN_RESIN_BURN_MOD
582605
#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."

code/game/objects/structures/fence.dm

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
anchored = TRUE
1919
pass_flags_self = PASSFENCE|LETPASSTHROW
2020

21+
can_astar_pass = CANASTARPASS_ALWAYS_PROC
22+
2123
icon = 'icons/obj/fence.dmi'
2224
icon_state = "straight"
2325

code/game/objects/structures/tables_racks.dm

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
anchored = TRUE
2323
layer = TABLE_LAYER
2424
pass_flags_self = PASSTABLE|LETPASSTHROW
25+
can_astar_pass = CANASTARPASS_ALWAYS_PROC
2526
climbable = TRUE
2627
max_integrity = 100
2728
integrity_failure = 30

code/modules/antagonists/xenomorth/xenomorph.dm

+25
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,28 @@
4040
messages.Add("<center>Помните, что после вашей смерти в гнезде не останется королевы и оно будет обречено на вымирание!</center>")
4141
SEND_SOUND(owner.current, sound('sound/voice/hiss1.ogg'))
4242
return messages
43+
44+
/datum/antagonist/facehugger
45+
name = "Facehugger"
46+
roundend_category = "xenomorph"
47+
job_rank = ROLE_ALIEN
48+
special_role = SPECIAL_ROLE_FACEHUGGER
49+
wiki_page_name = "Xenomorph"
50+
russian_wiki_name = "Ксеноморф"
51+
show_in_roundend = FALSE
52+
show_in_orbit = FALSE
53+
antag_menu_name = "Ксеноморф"
54+
55+
56+
/datum/antagonist/facehugger/on_gain()
57+
if(!isfacehugger(owner.current))
58+
stack_trace("This antag datum cannot be attached to a mob of this type.")
59+
. = ..()
60+
61+
/datum/antagonist/facehugger/greet()
62+
var/list/messages = list()
63+
messages.Add(span_danger("<center>Вы лицехват!</center>"))
64+
messages.Add("<center>Вы одна из первых стадий ксеноморфа. Ваша задача предельно простая: \
65+
найти цель, напрыгнуть ей на лицо, оплодотворить и спрятаться так, чтобы носитель не понял что к чему!</center>")
66+
SEND_SOUND(owner.current, sound('sound/voice/hiss1.ogg'))
67+
return messages

0 commit comments

Comments
 (0)