diff --git a/code/game/gamemodes/changeling/powers/headcrab.dm b/code/game/gamemodes/changeling/powers/headslug.dm similarity index 88% rename from code/game/gamemodes/changeling/powers/headcrab.dm rename to code/game/gamemodes/changeling/powers/headslug.dm index b39a2bba0d5..f29ec02db54 100644 --- a/code/game/gamemodes/changeling/powers/headcrab.dm +++ b/code/game/gamemodes/changeling/powers/headslug.dm @@ -1,4 +1,4 @@ -/datum/action/changeling/headcrab +/datum/action/changeling/headslug name = "Last Resort" desc = "We sacrifice our current body in a moment of need, placing us in control of a vessel that can plant our likeness in a new host. Costs 20 chemicals." helptext = "We will be placed in control of a small, fragile creature. We may attack a corpse like this to plant an egg which will slowly mature into a new form for us." @@ -7,12 +7,12 @@ dna_cost = 1 req_human = 1 -/datum/action/changeling/headcrab/try_to_sting(mob/user, mob/target) +/datum/action/changeling/headslug/try_to_sting(mob/user, mob/target) if(alert("Are you sure you wish to do this? This action cannot be undone.",,"Yes","No")=="No") return ..() -/datum/action/changeling/headcrab/sting_action(mob/user) +/datum/action/changeling/headslug/sting_action(mob/user) var/datum/mind/M = user.mind var/list/organs = user.get_organs_zone("head", 1) @@ -33,7 +33,7 @@ S.Weaken(3) var/new_location = user.drop_location() spawn(5) // So it's not killed in explosion - var/mob/living/simple_animal/hostile/headcrab/crab = new(new_location) + var/mob/living/simple_animal/hostile/headslug/crab = new(new_location) for(var/obj/item/organ/internal/I in organs) I.loc = crab crab.origin = M diff --git a/code/modules/events/headcrabs.dm b/code/modules/events/headcrabs.dm new file mode 100644 index 00000000000..3c02212a40f --- /dev/null +++ b/code/modules/events/headcrabs.dm @@ -0,0 +1,146 @@ +#define LOC_ATMOS_CONTROL 0 +#define LOC_FPMAINT 1 +#define LOC_FPMAINT2 2 +#define LOC_FSMAINT 3 +#define LOC_FSMAINT2 4 +#define LOC_ASMAINT 5 +#define LOC_ASMAINT2 6 +#define LOC_APMAINT 7 +#define LOC_MAINTCENTRAL 8 +#define LOC_FORE 9 +#define LOC_STARBOARD 10 +#define LOC_PORT 11 +#define LOC_AFT 12 +#define LOC_STORAGE 13 +#define LOC_DISPOSAL 14 +#define LOC_GENETICS 15 +#define LOC_ELECTRICAL 16 +#define LOC_ABANDONEDBAR 17 +#define LOC_ELECTRICAL_SHOP 18 +#define LOC_GAMBLING_DEN 19 + +#define HEADCRAB_NORMAL 0 +/*#define HEADCRAB_SPECIAL 1 +#define HEADCRAB_CLOWN 2 */ //Planned for the future + +/datum/event/headcrabs + announceWhen = 10 + endWhen = 11 + var/location + var/locstring + var/headcrab + +/datum/event/headcrabs/start() + + location = rand(0,19) + var/list/turf/simulated/floor/turfs = list() + var/spawn_area_type + switch(location) + if(LOC_ATMOS_CONTROL) + spawn_area_type = /area/maintenance/atmos_control + locstring = "Atmospherics Maintenance" + if(LOC_FPMAINT) + spawn_area_type = /area/maintenance/fpmaint + locstring = "EVA Maintenance" + if(LOC_FPMAINT2) + spawn_area_type = /area/maintenance/fpmaint2 + locstring = "Arrivals North Maintenance" + if(LOC_FSMAINT) + spawn_area_type = /area/maintenance/fsmaint + locstring = "Dormitory Maintenance" + if(LOC_FSMAINT2) + spawn_area_type = /area/maintenance/fsmaint2 + locstring = "Bar Maintenance" + if(LOC_ASMAINT) + spawn_area_type = /area/maintenance/asmaint + locstring = "Medbay Maintenance" + if(LOC_ASMAINT2) + spawn_area_type = /area/maintenance/asmaint2 + locstring = "Science Maintenance" + if(LOC_APMAINT) + spawn_area_type = /area/maintenance/apmaint + locstring = "Cargo Maintenance" + if(LOC_MAINTCENTRAL) + spawn_area_type = /area/maintenance/maintcentral + locstring = "Bridge Maintenance" + if(LOC_FORE) + spawn_area_type = /area/maintenance/fore + locstring = "Fore Maintenance" + if(LOC_STARBOARD) + spawn_area_type = /area/maintenance/starboard + locstring = "Starboard Maintenance" + if(LOC_PORT) + spawn_area_type = /area/maintenance/port + locstring = "Locker Room Maintenance" + if(LOC_AFT) + spawn_area_type = /area/maintenance/aft + locstring = "Engineering Maintenance" + if(LOC_STORAGE) + spawn_area_type = /area/maintenance/storage + locstring = "Atmospherics Maintenance" + if(LOC_DISPOSAL) + spawn_area_type = /area/maintenance/disposal + locstring = "Waste Disposal" + if(LOC_GENETICS) + spawn_area_type = /area/maintenance/genetics + locstring = "Genetics Maintenance" + if(LOC_ELECTRICAL) + spawn_area_type = /area/maintenance/electrical + locstring = "Electrical Maintenance" + if(LOC_ABANDONEDBAR) + spawn_area_type = /area/maintenance/abandonedbar + locstring = "Maintenance Bar" + if(LOC_ELECTRICAL_SHOP) + spawn_area_type = /area/maintenance/electrical_shop + locstring ="Electronics Den" + if(LOC_GAMBLING_DEN) + spawn_area_type = /area/maintenance/gambling_den + locstring = "Gambling Den" + for(var/areapath in typesof(spawn_area_type)) + var/area/A = locate(areapath) + for(var/turf/simulated/floor/F in A.contents) + if(turf_clear(F)) + turfs += F + + var/list/spawn_types = list() + var/max_number + headcrab = 0 //rand(0,x) for the future + switch(headcrab) //Switch is for the future + if(HEADCRAB_NORMAL) + spawn_types = list(/mob/living/simple_animal/hostile/headcrab) + max_number = 6 + + var/num = rand(2,max_number) + while(turfs.len > 0 && num > 0) + var/turf/simulated/floor/T = pick(turfs) + turfs.Remove(T) + num-- + var/spawn_type = pick(spawn_types) + new spawn_type(T) + + +/datum/event/headcrabs/announce() + event_announcement.Announce("Bioscans indicate that creatures have been breeding in [locstring]. Clear them out, before this starts to affect productivity", "Lifesign Alert") + +#undef LOC_ATMOS_CONTROL +#undef LOC_FPMAINT +#undef LOC_FPMAINT2 +#undef LOC_FSMAINT +#undef LOC_FSMAINT2 +#undef LOC_ASMAINT +#undef LOC_ASMAINT2 +#undef LOC_APMAINT +#undef LOC_MAINTCENTRAL +#undef LOC_FORE +#undef LOC_STARBOARD +#undef LOC_PORT +#undef LOC_AFT +#undef LOC_STORAGE +#undef LOC_DISPOSAL +#undef LOC_GENETICS +#undef LOC_ELECTRICAL +#undef LOC_ABANDONEDBAR +#undef LOC_ELECTRICAL_SHOP +#undef LOC_GAMBLING_DEN + +#undef HEADCRAB_NORMAL diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index c82a0ee7806..1ef6f06259f 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -1,100 +1,106 @@ -#define EGG_INCUBATION_TIME 120 - /mob/living/simple_animal/hostile/headcrab - name = "headslug" - desc = "Absolutely not de-beaked or harmless. Keep away from corpses." + name = "headcrab" + desc = "A small parasitic creature that would like to connect with your brain stem." + icon = 'icons/mob/headcrab.dmi' icon_state = "headcrab" icon_living = "headcrab" icon_dead = "headcrab_dead" - icon = 'icons/mob/mob.dmi' - health = 50 - maxHealth = 50 + health = 40 + maxHealth = 40 melee_damage_lower = 5 - melee_damage_upper = 5 - attacktext = "chomps" - attack_sound = 'sound/weapons/bite.ogg' - faction = list("creature") - robust_searching = 1 - stat_attack = 2 - obj_damage = 0 - environment_smash = 0 - speak_emote = list("squeaks") - ventcrawler = 2 - var/datum/mind/origin - var/egg_lain = 0 + melee_damage_upper = 10 + ranged = 1 + ranged_message = "leaps" + ranged_cooldown_time = 40 + var/jumpdistance = 4 + var/jumpspeed = 1 + attacktext = "bites" + attack_sound = 'sound/creatures/headcrab_attack.ogg' + speak_emote = list("hisses") + var/is_zombie = 0 + stat_attack = 1 //so they continue to attack when they are on the ground. + var/host_species = "" + var/list/human_overlays = list() -/mob/living/simple_animal/hostile/headcrab/examine(mob/user) +/mob/living/simple_animal/hostile/headcrab/Life(seconds, times_fired) + if(!is_zombie && isturf(src.loc) && stat != DEAD) + for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile + if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD)) + Zombify(H) + break ..() - if(stat == DEAD) - to_chat(desc = "It appears to be dead.") -/mob/living/simple_animal/hostile/headcrab/proc/Infect(mob/living/carbon/victim) - var/obj/item/organ/internal/body_egg/changeling_egg/egg = new(victim) - egg.insert(victim) - if(origin) - egg.origin = origin - else if(mind) // Let's make this a feature - egg.origin = mind - for(var/obj/item/organ/internal/I in src) - I.loc = egg - visible_message("[src] plants something in [victim]'s flesh!", \ - "We inject our egg into [victim]'s body!") - egg_lain = 1 +/mob/living/simple_animal/hostile/headcrab/OpenFire(atom/A) + if(check_friendly_fire) + for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory + for(var/mob/living/L in T) + if(L == src || L == A) + continue + if(faction_check(L) && !attack_same) + return + visible_message("[src] [ranged_message] at [A]!") + throw_at(A, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE) + ranged_cooldown = world.time + ranged_cooldown_time -/mob/living/simple_animal/hostile/headcrab/AttackingTarget() - if(egg_lain) - target.attack_animal(src) - return - if(iscarbon(target) && !issmall(target)) - // Changeling egg can survive in aliens! - var/mob/living/carbon/C = target - if(C.stat == DEAD) - if(C.status_flags & XENO_HOST) - to_chat(src, "A foreign presence repels us from this body. Perhaps we should try to infest another?") - return - Infect(target) - to_chat(src, "With our egg laid, our death approaches rapidly...") - spawn(100) - death() - return - target.attack_animal(src) +/mob/living/simple_animal/hostile/headcrab/proc/Zombify(mob/living/carbon/human/H) + if(!H.check_death_method()) + H.death() + var/obj/item/organ/external/head/head_organ = H.get_organ("head") + is_zombie = TRUE + if(H.wear_suit) + var/obj/item/clothing/suit/armor/A = H.wear_suit + if(A.armor && A.armor["melee"]) + maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! + maxHealth += 50 + health = maxHealth + name = "zombie" + desc = "A corpse animated by the alien being on its head." + melee_damage_lower = 10 + melee_damage_upper = 15 + ranged = 0 + icon = H.icon + speak = list('sound/creatures/zombie_idle1.ogg','sound/creatures/zombie_idle2.ogg','sound/creatures/zombie_idle3.ogg') + speak_chance = 50 + speak_emote = list("groans") + attacktext = "bites" + attack_sound = 'sound/creatures/zombie_attack.ogg' + icon_state = "zombie2_s" + if(head_organ) + head_organ.h_style = null + H.update_hair() + host_species = H.dna.species.name + human_overlays = H.overlays + update_icons() + H.forceMove(src) + visible_message("The corpse of [H.name] suddenly rises!") -/obj/item/organ/internal/body_egg/changeling_egg - name = "changeling egg" - desc = "Twitching and disgusting." - origin_tech = "biotech=7" // You need to be really lucky to obtain it. - var/datum/mind/origin - var/time - -/obj/item/organ/internal/body_egg/changeling_egg/egg_process() - // Changeling eggs grow in dead people - time++ - if(time >= EGG_INCUBATION_TIME) - Pop() - remove(owner) +/mob/living/simple_animal/hostile/headcrab/death() + ..() + if(is_zombie) qdel(src) -/obj/item/organ/internal/body_egg/changeling_egg/proc/Pop() - var/mob/living/carbon/human/monkey/M = new(owner) - owner.stomach_contents += M - for(var/obj/item/organ/internal/I in src) - I.insert(M, 1) +/mob/living/simple_animal/hostile/headcrab/handle_automated_speech() // This way they have different screams when attacking, sometimes. Might be seen as sphagetthi code though. + if(speak_chance) + if(rand(0,200) < speak_chance) + if(speak && speak.len) + playsound(get_turf(src), pick(speak), 200, 1) - if(origin && origin.current && (origin.current.stat == DEAD)) - origin.transfer_to(M) - if(!origin.changeling) - M.make_changeling() - if(origin.changeling.can_absorb_dna(M, owner)) - origin.changeling.absorb_dna(owner, M) +/mob/living/simple_animal/hostile/headcrab/Destroy() + if(contents) + for(var/mob/M in contents) + M.loc = get_turf(src) + return ..() - var/datum/action/changeling/humanform/HF = new - HF.Grant(M) - for(var/power in origin.changeling.purchasedpowers) - var/datum/action/changeling/S = power - if(istype(S) && S.needs_button) - S.Grant(M) - M.key = origin.key - owner.gib() -#undef EGG_INCUBATION_TIME +/mob/living/simple_animal/hostile/headcrab/update_icons() + ..() + if(is_zombie) + overlays.Cut() + overlays = human_overlays + var/image/I = image('icons/mob/headcrab.dmi', icon_state = "headcrabpod") + if(host_species == "Vox") + I = image('icons/mob/headcrab.dmi', icon_state = "headcrabpod_vox") + else if(host_species == "Gray") + I = image('icons/mob/headcrab.dmi', icon_state = "headcrabpod_gray") + overlays += I diff --git a/code/modules/mob/living/simple_animal/hostile/headslug.dm b/code/modules/mob/living/simple_animal/hostile/headslug.dm new file mode 100644 index 00000000000..e2fc98c483f --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/headslug.dm @@ -0,0 +1,100 @@ +#define EGG_INCUBATION_TIME 120 + +/mob/living/simple_animal/hostile/headslug + name = "headslug" + desc = "Absolutely not de-beaked or harmless. Keep away from corpses." + icon_state = "headslug" + icon_living = "headslug" + icon_dead = "headslug_dead" + icon = 'icons/mob/mob.dmi' + health = 50 + maxHealth = 50 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "chomps" + attack_sound = 'sound/weapons/bite.ogg' + faction = list("creature") + robust_searching = 1 + stat_attack = 2 + obj_damage = 0 + environment_smash = 0 + speak_emote = list("squeaks") + ventcrawler = 2 + var/datum/mind/origin + var/egg_lain = 0 + +/mob/living/simple_animal/hostile/headslug/examine(mob/user) + ..() + if(stat == DEAD) + to_chat(desc = "It appears to be dead.") + +/mob/living/simple_animal/hostile/headslug/proc/Infect(mob/living/carbon/victim) + var/obj/item/organ/internal/body_egg/changeling_egg/egg = new(victim) + egg.insert(victim) + if(origin) + egg.origin = origin + else if(mind) // Let's make this a feature + egg.origin = mind + for(var/obj/item/organ/internal/I in src) + I.loc = egg + visible_message("[src] plants something in [victim]'s flesh!", \ + "We inject our egg into [victim]'s body!") + egg_lain = 1 + +/mob/living/simple_animal/hostile/headslug/AttackingTarget() + if(egg_lain) + target.attack_animal(src) + return + if(iscarbon(target) && !issmall(target)) + // Changeling egg can survive in aliens! + var/mob/living/carbon/C = target + if(C.stat == DEAD) + if(C.status_flags & XENO_HOST) + to_chat(src, "A foreign presence repels us from this body. Perhaps we should try to infest another?") + return + Infect(target) + to_chat(src, "With our egg laid, our death approaches rapidly...") + spawn(100) + death() + return + target.attack_animal(src) + +/obj/item/organ/internal/body_egg/changeling_egg + name = "changeling egg" + desc = "Twitching and disgusting." + origin_tech = "biotech=7" // You need to be really lucky to obtain it. + var/datum/mind/origin + var/time + +/obj/item/organ/internal/body_egg/changeling_egg/egg_process() + // Changeling eggs grow in dead people + time++ + if(time >= EGG_INCUBATION_TIME) + Pop() + remove(owner) + qdel(src) + +/obj/item/organ/internal/body_egg/changeling_egg/proc/Pop() + var/mob/living/carbon/human/monkey/M = new(owner) + owner.stomach_contents += M + + for(var/obj/item/organ/internal/I in src) + I.insert(M, 1) + + if(origin && origin.current && (origin.current.stat == DEAD)) + origin.transfer_to(M) + if(!origin.changeling) + M.make_changeling() + if(origin.changeling.can_absorb_dna(M, owner)) + origin.changeling.absorb_dna(owner, M) + + var/datum/action/changeling/humanform/HF = new + HF.Grant(M) + for(var/power in origin.changeling.purchasedpowers) + var/datum/action/changeling/S = power + if(istype(S) && S.needs_button) + S.Grant(M) + M.key = origin.key + owner.gib() + +#undef EGG_INCUBATION_TIME diff --git a/icons/mob/headcrab.dmi b/icons/mob/headcrab.dmi new file mode 100644 index 00000000000..4db08f13e76 Binary files /dev/null and b/icons/mob/headcrab.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 014242b9d90..ed193927004 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/paradise.dme b/paradise.dme index aab6fa63ad7..6b632ad1916 100644 --- a/paradise.dme +++ b/paradise.dme @@ -485,7 +485,7 @@ #include "code\game\gamemodes\changeling\powers\epinephrine.dm" #include "code\game\gamemodes\changeling\powers\fakedeath.dm" #include "code\game\gamemodes\changeling\powers\fleshmend.dm" -#include "code\game\gamemodes\changeling\powers\headcrab.dm" +#include "code\game\gamemodes\changeling\powers\headslug.dm" #include "code\game\gamemodes\changeling\powers\hivemind.dm" #include "code\game\gamemodes\changeling\powers\humanform.dm" #include "code\game\gamemodes\changeling\powers\lesserform.dm" @@ -1444,6 +1444,7 @@ #include "code\modules\events\false_alarm.dm" #include "code\modules\events\floorcluwne.dm" #include "code\modules\events\grid_check.dm" +#include "code\modules\events\headcrabs.dm" #include "code\modules\events\immovable_rod.dm" #include "code\modules\events\infestation.dm" #include "code\modules\events\ion_storm.dm" @@ -1924,6 +1925,7 @@ #include "code\modules\mob\living\simple_animal\hostile\floorcluwne.dm" #include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" #include "code\modules\mob\living\simple_animal\hostile\headcrab.dm" +#include "code\modules\mob\living\simple_animal\hostile\headslug.dm" #include "code\modules\mob\living\simple_animal\hostile\hellhound.dm" #include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" #include "code\modules\mob\living\simple_animal\hostile\hostile.dm" @@ -2442,4 +2444,4 @@ #include "goon\code\datums\browserOutput.dm" #include "interface\interface.dm" #include "interface\skin.dmf" -// END_INCLUDE +// END_INCLUDE \ No newline at end of file diff --git a/sound/creatures/headcrab_attack.ogg b/sound/creatures/headcrab_attack.ogg new file mode 100644 index 00000000000..4f6d114aa75 Binary files /dev/null and b/sound/creatures/headcrab_attack.ogg differ diff --git a/sound/creatures/zombie_attack.ogg b/sound/creatures/zombie_attack.ogg new file mode 100644 index 00000000000..1e9b2a63024 Binary files /dev/null and b/sound/creatures/zombie_attack.ogg differ diff --git a/sound/creatures/zombie_idle1.ogg b/sound/creatures/zombie_idle1.ogg new file mode 100644 index 00000000000..851c88deff4 Binary files /dev/null and b/sound/creatures/zombie_idle1.ogg differ diff --git a/sound/creatures/zombie_idle2.ogg b/sound/creatures/zombie_idle2.ogg new file mode 100644 index 00000000000..04e75da9b22 Binary files /dev/null and b/sound/creatures/zombie_idle2.ogg differ diff --git a/sound/creatures/zombie_idle3.ogg b/sound/creatures/zombie_idle3.ogg new file mode 100644 index 00000000000..b4cd2600757 Binary files /dev/null and b/sound/creatures/zombie_idle3.ogg differ