274 lines
8.2 KiB
Plaintext
274 lines
8.2 KiB
Plaintext
|
|
|
|
//TODO: Make these simple_animals
|
|
|
|
#define MIN_IMPREGNATION_TIME 100 //time it takes to impregnate someone
|
|
#define MAX_IMPREGNATION_TIME 150
|
|
|
|
#define MIN_ACTIVE_TIME 200 //time between being dropped and going idle
|
|
#define MAX_ACTIVE_TIME 400
|
|
|
|
/obj/item/clothing/mask/facehugger
|
|
name = "alien"
|
|
desc = "It has some sort of a tube at the end of its tail."
|
|
icon = 'icons/mob/alien.dmi'
|
|
icon_state = "facehugger"
|
|
item_state = "facehugger"
|
|
w_class = WEIGHT_CLASS_TINY //note: can be picked up by aliens unlike most other items of w_class below 4
|
|
clothing_flags = ALLOWINTERNALS
|
|
throw_range = 5
|
|
tint = 3
|
|
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
|
|
layer = MOB_LAYER
|
|
max_integrity = 100
|
|
mutantrace_variation = STYLE_MUZZLE
|
|
|
|
var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case
|
|
|
|
var/sterile = FALSE
|
|
var/real = TRUE //0 for the toy, 1 for real. Sure I could istype, but fuck that.
|
|
var/strength = 5
|
|
|
|
var/attached = 0
|
|
|
|
/obj/item/clothing/mask/facehugger/lamarr
|
|
name = "Lamarr"
|
|
sterile = TRUE
|
|
|
|
/obj/item/clothing/mask/facehugger/dead
|
|
icon_state = "facehugger_dead"
|
|
item_state = "facehugger_inactive"
|
|
sterile = TRUE
|
|
stat = DEAD
|
|
|
|
/obj/item/clothing/mask/facehugger/impregnated
|
|
icon_state = "facehugger_impregnated"
|
|
item_state = "facehugger_impregnated"
|
|
sterile = TRUE
|
|
stat = DEAD
|
|
|
|
/obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
|
..()
|
|
if(obj_integrity < 90)
|
|
Die()
|
|
|
|
/obj/item/clothing/mask/facehugger/attackby(obj/item/O, mob/user, params)
|
|
return O.attack_obj(src, user)
|
|
|
|
/obj/item/clothing/mask/facehugger/attack_alien(mob/user) //can be picked up by aliens
|
|
return attack_hand(user)
|
|
|
|
/obj/item/clothing/mask/facehugger/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
|
if((stat == CONSCIOUS && !sterile) && !isalien(user))
|
|
if(Leap(user))
|
|
return
|
|
. = ..()
|
|
|
|
/obj/item/clothing/mask/facehugger/attack(mob/living/M, mob/user)
|
|
..()
|
|
if(user.transferItemToLoc(src, get_turf(M)))
|
|
Leap(M)
|
|
|
|
/obj/item/clothing/mask/facehugger/examine(mob/user)
|
|
. = ..()
|
|
if(!real)//So that giant red text about probisci doesn't show up.
|
|
return
|
|
switch(stat)
|
|
if(DEAD,UNCONSCIOUS)
|
|
. += "<span class='boldannounce'>[src] is not moving.</span>"
|
|
if(CONSCIOUS)
|
|
. += "<span class='boldannounce'>[src] seems to be active!</span>"
|
|
if (sterile)
|
|
. += "<span class='boldannounce'>It looks like the proboscis has been removed.</span>"
|
|
|
|
|
|
/obj/item/clothing/mask/facehugger/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
|
if(exposed_temperature > 300)
|
|
Die()
|
|
|
|
/obj/item/clothing/mask/facehugger/equipped(mob/M)
|
|
. = ..()
|
|
Attach(M)
|
|
|
|
/obj/item/clothing/mask/facehugger/Crossed(atom/target)
|
|
. = ..()
|
|
HasProximity(target)
|
|
|
|
/obj/item/clothing/mask/facehugger/on_found(mob/finder)
|
|
if(stat == CONSCIOUS)
|
|
return HasProximity(finder)
|
|
return FALSE
|
|
|
|
/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj)
|
|
if(CanHug(AM) && Adjacent(AM))
|
|
return Leap(AM)
|
|
return FALSE
|
|
|
|
/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
|
|
if(!..())
|
|
return
|
|
if(stat == CONSCIOUS)
|
|
icon_state = "[initial(icon_state)]_thrown"
|
|
addtimer(CALLBACK(src, PROC_REF(clear_throw_icon_state)), 15)
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/clear_throw_icon_state()
|
|
if(icon_state == "[initial(icon_state)]_thrown")
|
|
icon_state = "[initial(icon_state)]"
|
|
|
|
/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
..()
|
|
if(stat == CONSCIOUS)
|
|
icon_state = "[initial(icon_state)]"
|
|
Leap(hit_atom)
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/valid_to_attach(mob/living/M)
|
|
// valid targets: carbons except aliens and devils
|
|
// facehugger state early exit checks
|
|
if(stat != CONSCIOUS)
|
|
return FALSE
|
|
if(attached)
|
|
return FALSE
|
|
if(iscarbon(M))
|
|
// disallowed carbons
|
|
if(isalien(M) || isdevil(M))
|
|
return FALSE
|
|
var/mob/living/carbon/target = M
|
|
// gotta have a head to be implanted (no changelings or sentient plants)
|
|
if(!target.get_bodypart(BODY_ZONE_HEAD))
|
|
return FALSE
|
|
|
|
if(target.getorgan(/obj/item/organ/alien/hivenode) || target.getorgan(/obj/item/organ/body_egg/alien_embryo))
|
|
return FALSE
|
|
// carbon, has head, not alien or devil, has no hivenode or embryo: valid
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/Leap(mob/living/M)
|
|
if(!valid_to_attach(M))
|
|
return FALSE
|
|
if(iscarbon(M))
|
|
var/mob/living/carbon/target = M
|
|
if(target.wear_mask && istype(target.wear_mask, /obj/item/clothing/mask/facehugger))
|
|
return FALSE
|
|
// passed initial checks - time to leap!
|
|
M.visible_message("<span class='danger'>[src] leaps at [M]'s face!</span>", \
|
|
"<span class='userdanger'>[src] leaps at [M]'s face!</span>")
|
|
|
|
// probiscis-blocker handling
|
|
if(iscarbon(M))
|
|
var/mob/living/carbon/target = M
|
|
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
if(H.is_mouth_covered(head_only = 1))
|
|
H.visible_message("<span class='danger'>[src] smashes against [H]'s [H.head]!</span>", \
|
|
"<span class='userdanger'>[src] smashes against [H]'s [H.head]!</span>")
|
|
Die()
|
|
return FALSE
|
|
|
|
if(target.wear_mask)
|
|
var/obj/item/clothing/W = target.wear_mask
|
|
if(target.dropItemToGround(W))
|
|
target.visible_message("<span class='danger'>[src] tears [W] off of [target]'s face!</span>", \
|
|
"<span class='userdanger'>[src] tears [W] off of [target]'s face!</span>")
|
|
target.equip_to_slot_if_possible(src, ITEM_SLOT_MASK, 0, 1, 1)
|
|
return TRUE // time for a smoke
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M)
|
|
if(!valid_to_attach(M))
|
|
return
|
|
// early returns and validity checks done: attach.
|
|
attached++
|
|
//ensure we detach once we no longer need to be attached
|
|
addtimer(CALLBACK(src, PROC_REF(detach)), MAX_IMPREGNATION_TIME)
|
|
|
|
|
|
if(!sterile)
|
|
M.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage
|
|
M.Unconscious(MAX_IMPREGNATION_TIME/0.3) //something like 25 ticks = 20 seconds with the default settings
|
|
|
|
GoIdle() //so it doesn't jump the people that tear it off
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(Impregnate), M), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/detach()
|
|
attached = 0
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/Impregnate(mob/living/target)
|
|
if(!target || target.stat == DEAD) //was taken off or something
|
|
return
|
|
|
|
if(iscarbon(target))
|
|
var/mob/living/carbon/C = target
|
|
if(C.wear_mask != src)
|
|
return
|
|
|
|
if(!sterile)
|
|
target.visible_message("<span class='danger'>[src] falls limp after violating [target]'s face!</span>", \
|
|
"<span class='userdanger'>[src] falls limp after violating [target]'s face!</span>")
|
|
|
|
Die()
|
|
icon_state = "[initial(icon_state)]_impregnated"
|
|
|
|
var/obj/item/bodypart/chest/LC = target.get_bodypart(BODY_ZONE_CHEST)
|
|
if((!LC || !LC.is_robotic_limb(FALSE)) && !target.getorgan(/obj/item/organ/body_egg/alien_embryo))
|
|
new /obj/item/organ/body_egg/alien_embryo(target)
|
|
|
|
else
|
|
target.visible_message("<span class='danger'>[src] violates [target]'s face!</span>", \
|
|
"<span class='userdanger'>[src] violates [target]'s face!</span>")
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/GoActive()
|
|
if(stat == DEAD || stat == CONSCIOUS)
|
|
return
|
|
|
|
stat = CONSCIOUS
|
|
icon_state = "[initial(icon_state)]"
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/GoIdle()
|
|
if(stat == DEAD || stat == UNCONSCIOUS)
|
|
return
|
|
|
|
stat = UNCONSCIOUS
|
|
icon_state = "[initial(icon_state)]_inactive"
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(GoActive)), rand(MIN_ACTIVE_TIME, MAX_ACTIVE_TIME))
|
|
|
|
/obj/item/clothing/mask/facehugger/proc/Die()
|
|
if(stat == DEAD)
|
|
return
|
|
|
|
icon_state = "[initial(icon_state)]_dead"
|
|
item_state = "facehugger_inactive"
|
|
stat = DEAD
|
|
|
|
visible_message("<span class='danger'>[src] curls up into a ball!</span>")
|
|
|
|
/proc/CanHug(mob/living/M)
|
|
if(!istype(M))
|
|
return FALSE
|
|
if(M.stat == DEAD)
|
|
return FALSE
|
|
if(M.getorgan(/obj/item/organ/alien/hivenode))
|
|
return FALSE
|
|
if(AmBloodsucker(M))
|
|
return FALSE
|
|
|
|
if(ismonkey(M))
|
|
return TRUE
|
|
|
|
var/mob/living/carbon/C = M
|
|
if(ishuman(C) && !(ITEM_SLOT_MASK in C.dna.species.no_equip))
|
|
var/mob/living/carbon/human/H = C
|
|
if(H.is_mouth_covered(head_only = 1))
|
|
return FALSE
|
|
return TRUE
|
|
return FALSE
|
|
|
|
#undef MIN_ACTIVE_TIME
|
|
#undef MAX_ACTIVE_TIME
|
|
|
|
#undef MIN_IMPREGNATION_TIME
|
|
#undef MAX_IMPREGNATION_TIME
|