Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: Лицехват - моб #6612

Draft
wants to merge 10 commits into
base: master220
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,9 @@
/// from base of /client/proc/handle_popup_close() : (window_id)
#define COMSIG_POPUP_CLEARED "popup_cleared"

/// from base of /datum/status_effect/Destroy() : (effect_type)
#define COMSIG_MOB_STATUS_EFFECT_ENDED "mob_status_effect_ended"

/// Source: /mob/living/UnarmedAttack (atom/atom, proximity_flag)
#define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack"

Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/gamemode.dm
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone"
#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel"
#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva"
#define SPECIAL_ROLE_FACEHUGGER "Facehugger"
#define SPECIAL_ROLE_TERROR_SPIDER "Terror Spider"
#define SPECIAL_ROLE_TERROR_QUEEN "Terror Queen"
#define SPECIAL_ROLE_TERROR_PRINCE "Terror Prince"
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#define isaliensentinel(A) (istype(A, /mob/living/carbon/alien/humanoid/sentinel))

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

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

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

#define isflower(A) istype(A, /obj/item/twohanded/required/kirbyplants)

#define isclothing(A) (istype(A, /obj/item/clothing))

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

//Structures
#define isstructure(A) (istype(A, /obj/structure))
#define istable(A) (istype(A, /obj/structure/table))

// Misc
#define isclient(A) istype(A, /client)
Expand Down
4 changes: 4 additions & 0 deletions code/__DEFINES/obj_flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@
/// Flags for the pod_flags var on /obj/structure/closet/supplypod
#define FIRST_SOUNDS (1<<0) // If it shouldn't play sounds the first time it lands, used for reverse mode


#define HUMAN_HOLDER (1<<0)

#define ALIEN_HOLDER (1<<1)
4 changes: 4 additions & 0 deletions code/__DEFINES/traits/sources.dm
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
/// Traits applied to a silicon mob by their model.
#define ROBOT_TRAIT "robot_trait"

#define FACEHUGER_TRAIT "facehugger_trait"

/// A trait gained from a mob's leap action, like the leaper
#define LEAPING_TRAIT "leaping"

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

#define THROWED_TRAIT "throwed_trait"

#define NO_GRAVITY_TRAIT "no-gravity"
#define NEGATIVE_GRAVITY_TRAIT "negative-gravity"

Expand Down
19 changes: 17 additions & 2 deletions code/controllers/subsystem/movement/movement_types.dm
Original file line number Diff line number Diff line change
Expand Up @@ -524,10 +524,25 @@
if(!.)
return
var/atom/old_loc = moving.loc
var/turf/next = get_step_to(moving, target)
moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
var/turf/next = get_next_step()
if(isliving(moving))
var/mob/living/moving_mob = moving
if(!(moving_mob.mobility_flags & MOBILITY_MOVE))
return MOVELOOP_FAILURE
moving?.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE

/datum/move_loop/has_target/dist_bound/move_to/proc/get_next_step()
return get_step_to(moving, target)

/datum/controller/subsystem/move_manager/proc/move_to_pathfind(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
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)

/datum/move_loop/has_target/dist_bound/move_to/pathfind

/datum/move_loop/has_target/dist_bound/move_to/pathfind/get_next_step()
var/list/points = get_path_to(moving, target, skip_first = TRUE)
return (points.len)? points[1] : null

/**
* Wrapper around walk_away()
Expand Down
14 changes: 13 additions & 1 deletion code/datums/components/ghost_direct_control.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
var/ban_type
/// Check Syndicate ban
var/ban_syndicate
/// Check ghost respawnability
var/respawnable_check = TRUE
/// Check antaghud use
var/check_antaghud = TRUE
/// Any extra checks which need to run before we take over
var/datum/callback/extra_control_checks
/// Callback run after someone successfully takes over the body
Expand All @@ -25,6 +29,7 @@
poll_candidates = TRUE,
antag_age_check = TRUE,
check_antaghud = TRUE,
respawnable_check = TRUE,
poll_length = 10 SECONDS,
ban_syndicate = FALSE,
assumed_control_message = null,
Expand All @@ -43,6 +48,8 @@
src.extra_control_checks = extra_control_checks
src.after_assumed_control = after_assumed_control
src.question_text = question_text
src.respawnable_check = respawnable_check
src.check_antaghud = check_antaghud

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

Expand Down Expand Up @@ -135,13 +142,18 @@
if(new_body.stat == DEAD)
to_chat(harbinger, span_warning("Это тело умерло, оно бесполезно!"))
return
if(respawnable_check && !(harbinger in GLOB.respawnable_list))
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду.")
return
if(respawnable_check && cannotPossess(harbinger))
to_chat(harbinger, "Вы не можете повторно присоединиться к раунду, активировав антаг худ.")
return
if(new_body.key)
to_chat(harbinger, span_warning("[capitalize(new_body.declent_ru(NOMINATIVE))] уже является разумным!"))
qdel(src)
return
if(extra_control_checks && !extra_control_checks.Invoke(harbinger))
return

add_game_logs("took control of [new_body].", harbinger)
// doesn't transfer mind because that transfers antag datum as well
new_body.key = harbinger.key
Expand Down
2 changes: 1 addition & 1 deletion code/datums/spells/alien_spells/lay_alien_eggs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "Plant alien eggs"
desc = "Allows you to plant alien eggs on your current turf, does not work while in space."
base_cooldown = 1 SECONDS
plasma_cost = 75
plasma_cost = 95
weed_type = /obj/structure/alien/egg
weed_name = "alien egg"
action_icon_state = "alien_egg"
Expand Down
1 change: 1 addition & 0 deletions code/datums/status_effects/status_effect.dm
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
owner.clear_alert(id)
LAZYREMOVE(owner.status_effects, src)
on_remove()
SEND_SIGNAL(owner, COMSIG_MOB_STATUS_EFFECT_ENDED, type)
owner = null
if(linked_alert)
linked_alert.attached_effect = null
Expand Down
1 change: 1 addition & 0 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g
var/slot_flags_2 = NONE
/// 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.
var/flags_inv = NONE
var/holder_flags = NONE
/// These flags will be added/removed (^=) to/from flags_inv in [/proc/check_obscured_slots()]
/// if check_transparent argument is set to `TRUE`. Used in carbon's update icons shenanigans.
/// Example: you can see someone's mask through their transparent visor, but you cannot reach it
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/obj_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
armor_protection = armor.getRating(damage_flag)
if(armor_protection) //Only apply weak-against-armor/hollowpoint effects if there actually IS armor.
armor_protection = clamp(armor_protection - armour_penetration, min(armor_protection, 0), 100)
return round(damage_amount * (100 - armor_protection)*0.01, DAMAGE_PRECISION)
return round(damage_amount * (100 - armor_protection) * 0.01, DAMAGE_PRECISION)


/// Proc for recovering atom_integrity. Returns the amount repaired by
Expand Down
61 changes: 42 additions & 19 deletions code/game/objects/structures/aliens.dm
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@
if(user.a_intent == INTENT_HARM)
return ..()

try_switch_state(user)
return try_switch_state(user)

/obj/structure/alien/resin/door/attack_animal(mob/living/simple_animal/M)
if(M.a_intent == INTENT_HARM)
return ..()

return try_switch_state(M)


/obj/structure/alien/resin/door/attack_hand(mob/living/user)
if(!isalien(user))
Expand Down Expand Up @@ -206,18 +212,21 @@

/obj/structure/alien/resin/door/proc/try_switch_state(atom/movable/user)
if(operating)
return
return FALSE

add_fingerprint(user)

if(!isalien(user))
return
if(!isliving(user))
return FALSE
var/mob/living/mob = user
if(!isalien(user) && !("alien" in mob.faction))
return FALSE

var/mob/living/carbon/alien/alien = user
if(alien.incapacitated())
return
return FALSE

switch_state()
return TRUE


/obj/structure/alien/resin/door/proc/switch_state()
Expand Down Expand Up @@ -442,6 +451,7 @@
#define GROWN 3
#define MIN_GROWTH_TIME 1200 //time it takes to grow a hugger
#define MAX_GROWTH_TIME 1800
#define PROXIMITY_RADIUS 5

/obj/structure/alien/egg
name = "egg"
Expand All @@ -468,11 +478,13 @@
update_icon(UPDATE_ICON_STATE)
switch(status)
if(GROWING)
new /obj/item/clothing/mask/facehugger(src)
var/mob/living/simple_animal/hostile/facehugger/player_controlled/hugger = new(src)
hugger.LoseTarget()
addtimer(CALLBACK(src, PROC_REF(Grow)), rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME))
if(GROWN)
new /obj/item/clothing/mask/facehugger(src)
AddComponent(/datum/component/proximity_monitor)
var/mob/living/simple_animal/hostile/facehugger/player_controlled/hugger = new(src)
hugger.LoseTarget()
AddComponent(/datum/component/proximity_monitor, PROXIMITY_RADIUS)
if(BURST)
obj_integrity = integrity_failure

Expand Down Expand Up @@ -512,41 +524,49 @@


/obj/structure/alien/egg/proc/GetFacehugger()
return locate(/obj/item/clothing/mask/facehugger) in contents
return locate(/mob/living/simple_animal/hostile/facehugger) in contents


/obj/structure/alien/egg/proc/Grow()
status = GROWN
update_icon(UPDATE_ICON_STATE)
AddComponent(/datum/component/proximity_monitor)

AddComponent(/datum/component/proximity_monitor, PROXIMITY_RADIUS)

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


///We now check HOW the hugger is hatching, kill carried from Burst() and obj_break()
/obj/structure/alien/egg/proc/Hatch(kill)
/obj/structure/alien/egg/proc/Hatch(kill, atom/movable/trigger)
status = BURST
update_icon(UPDATE_ICON_STATE)
var/obj/item/clothing/mask/facehugger/child = GetFacehugger()
var/mob/living/simple_animal/hostile/facehugger/child = GetFacehugger()

if(!child)
return

child.forceMove(get_turf(src))
if(kill)
child.Die()
child.death()
return

for(var/mob/living/victim in range(1, src))
if(CanHug(victim))
child.Attach(victim)
child.try_hug(victim)
break

if(!CanHug(trigger))
return

child.GiveTarget(trigger)
child.MoveToTarget(list(trigger))


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

Burst(kill = FALSE)
Burst(kill = FALSE, trigger = AM)


#undef BURST
Expand All @@ -577,6 +599,7 @@
#undef GROWN
#undef MIN_GROWTH_TIME
#undef MAX_GROWTH_TIME
#undef PROXIMITY_RADIUS

#undef ALIEN_RESIN_BURN_MOD
#undef ALIEN_RESIN_BRUTE_MOD
Expand Down
5 changes: 5 additions & 0 deletions code/game/objects/structures/displaycase.dm
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
req_access = list(ACCESS_RD)

/obj/structure/displaycase/labcage/dump()
var/obj/item/clothing/mask/facehugger/hugger = showpiece
. = ..()
hugger?.check_mob_inside()

/obj/structure/displaycase/stechkin
name = "officer's display case"
desc = "A display case containing a humble stechkin pistol. Never forget your roots."
Expand Down
2 changes: 2 additions & 0 deletions code/game/objects/structures/fence.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
anchored = TRUE
pass_flags_self = PASSFENCE|LETPASSTHROW

can_astar_pass = CANASTARPASS_ALWAYS_PROC

icon = 'icons/obj/fence.dmi'
icon_state = "straight"

Expand Down
1 change: 1 addition & 0 deletions code/game/objects/structures/tables_racks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
anchored = TRUE
layer = TABLE_LAYER
pass_flags_self = PASSTABLE|LETPASSTHROW
can_astar_pass = CANASTARPASS_ALWAYS_PROC
climbable = TRUE
max_integrity = 100
integrity_failure = 30
Expand Down
25 changes: 25 additions & 0 deletions code/modules/antagonists/xenomorth/xenomorph.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,28 @@
messages.Add("<center>Помните, что после вашей смерти в гнезде не останется королевы и оно будет обречено на вымирание!</center>")
SEND_SOUND(owner.current, sound('sound/voice/hiss1.ogg'))
return messages

/datum/antagonist/facehugger
name = "Facehugger"
roundend_category = "xenomorph"
job_rank = ROLE_ALIEN
special_role = SPECIAL_ROLE_FACEHUGGER
wiki_page_name = "Xenomorph"
russian_wiki_name = "Ксеноморф"
show_in_roundend = FALSE
show_in_orbit = FALSE
antag_menu_name = "Ксеноморф"


/datum/antagonist/facehugger/on_gain()
if(!isfacehugger(owner.current))
stack_trace("This antag datum cannot be attached to a mob of this type.")
. = ..()

/datum/antagonist/facehugger/greet()
var/list/messages = list()
messages.Add(span_danger("<center>Вы лицехват!</center>"))
messages.Add("<center>Вы одна из первых стадий ксеноморфа. Ваша задача предельно простая: \
найти цель, напрыгнуть ей на лицо, оплодотворить и спрятаться так, чтобы носитель не понял что к чему!</center>")
SEND_SOUND(owner.current, sound('sound/voice/hiss1.ogg'))
return messages
2 changes: 1 addition & 1 deletion code/modules/antagonists/xenomorth/xenomorph_objectives.dm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
if(completed)
return .

var/alife_count = xeno_team?.members.len
var/alife_count = xeno_team?.members.len - xeno_team?.facehuggers.len

if(alife_count >= targets_need)
completed = TRUE
Expand Down
Loading