diff --git a/code/game/objects/alien/facehugger.dm b/code/game/objects/alien/facehugger.dm
index 5c9db1b572d..7c54a78d604 100644
--- a/code/game/objects/alien/facehugger.dm
+++ b/code/game/objects/alien/facehugger.dm
@@ -220,402 +220,3 @@ var/const
return
-/* NOPE.png -- Urist
-
-// Returns the surrounding cardinal turfs with open links
-// Including through doors openable with the ID
-// Includes spacetiles
-/turf/proc/CardinalTurfsWithAccessSpace(var/obj/item/weapon/card/id/ID)
- var/L[] = new()
- for(var/d in cardinal)
- var/turf/simulated/T = get_step(src, d)
- if((istype(T) || istype(T,/turf/space))&& !T.density)
- if(!LinkBlockedWithAccess(src, T, ID))
- L.Add(T)
- return L
-
-/obj/effect/alien/facehugger
- name = "alien"
- desc = "An alien, looks pretty scary!"
- icon_state = "facehugger"
- layer = 5.0
- density = 1
- anchored = 0
-
- var/state = 0
-
- var/list/path = new/list()
-
- var/frustration = 0
- var/mob/living/carbon/target
- var/list/path_target = new/list()
-
- var/turf/trg_idle
- var/list/path_idle = new/list()
-
- var/alive = 1 //1 alive, 0 dead
- var/health = 10
- var/maxhealth = 10
- var/lamarr = 0
- flags = 258.0
-
-
-
-
-
- New()
- ..()
- if(aliens_allowed)
- health = maxhealth
- process()
- else
- del(src)
-
- examine()
- set src in view()
- ..()
- if(!alive)
- usr << text("\red The alien is not moving.")
- else if (health > 5)
- usr << text("\red The alien looks fresh, just out of the egg.")
- else
- usr << text("\red The alien looks injured.")
- if (lamarr)
- usr << text("\red It looks like the proboscis has been removed.")
- return
-
-
- attack_hand(user as mob)
- return
-
- attackby(obj/item/weapon/W as obj, mob/user as mob)
- switch(W.damtype)
- if("fire")
- health -= W.force * 0.75
- if("brute")
- health -= W.force * 0.5
- else
- if (health <= 0)
- death()
- else if (W.force)
- if(ishuman(user) || ismonkey(user))
- target = user
- state = 1
- ..()
-
- bullet_act(var/obj/item/projectile/Proj)
- health -= round(Proj.damage / 2)
- ..()
- healthcheck()
-
- ex_act(severity)
- switch(severity)
- if(1.0)
- death()
- if(2.0)
- health -= 15
- healthcheck()
- return
-
- meteorhit()
- death()
- return
-
- blob_act()
- if(prob(50))
- death()
- return
-
- Bumped(AM as mob|obj)
- if(ismob(AM) && (ishuman(AM) || ismonkey(AM)) )
- target = AM
- set_attack()
- else if(ismob(AM))
- spawn(0)
- var/turf/T = get_turf(src)
- AM:loc = T
-
- Bump(atom/A)
- if(ismob(A) && (ishuman(A) || ismonkey(A)))
- target = A
- set_attack()
- else if(ismob(A))
- loc = A:loc
-
- temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- if(exposed_temperature > 300)
- health -= 5
- healthcheck()
-
-
-
-
- verb/follow()
- set src in view() //set src in get_aliens(view()) - does not work, damn shitty byond :( -- rastaf0
- set name = "Follow Me"
- set category = "Object" //"Alien" does not work perfect - humans get "Alien" tab too, that's annoying
- if(!alive) return
- if(!isalien(usr))
- usr << text("\red The alien ignores you.")
- return
- if(state != 2 || health < maxhealth)
- usr << text("\red The alien is too busy to follow you.")
- return
- usr << text("\green The alien will now try to follow you.")
- trg_idle = usr
- path_idle = new/list()
- return
-
- verb/stop()
- set src in view()
- set name = "Stop Following"
- set category = "Object"
- if(!alive) return
- if(!isalien(usr))
- usr << text("\red The alien ignores you.")
- return
- if(state != 2)
- usr << text("\red The alien is too busy to follow you.")
- return
- usr << text("\green The alien stops following you.")
- set_null()
- return
-
-
-
-
- proc/call_to(var/mob/user)
- if(!alive || !isalien(user) || state != 2) return
- trg_idle = user
- path_idle = new/list()
- return
-
- proc/set_attack()
- state = 1
- if(path_idle.len) path_idle = new/list()
- trg_idle = null
-
- proc/set_idle()
- state = 2
- if (path_target.len) path_target = new/list()
- target = null
- frustration = 0
-
- proc/set_null()
- state = 0
- if (path_target.len) path_target = new/list()
- if (path_idle.len) path_idle = new/list()
- target = null
- trg_idle = null
- frustration = 0
-
- process()
- set background = 1
- var/quick_move = 0
-
- if (!alive)
- return
-
- if (!target)
- if (path_target.len) path_target = new/list()
-
- var/last_health = INFINITY
- var/view
- if (lamarr)
- view = 1
- else
- view = viewrange-2
- for (var/mob/living/carbon/C in range(view,loc))
- if (C.stat == 2 || isalien(C) || C.alien_egg_flag || !can_see(src,C,viewrange) || istype(C, /mob/living/carbon/metroid))
- continue
- if(C:stunned || C:paralysis || C:weakened)
- target = C
-
- break
- if(C:health < last_health)
- last_health = C:health
- target = C
-
- if(target)
- if (!lamarr || prob(10))
- set_attack()
- else if(state != 2)
- set_idle()
- idle()
-
- else if(target)
- var/turf/distance = get_dist(src, target)
- if (!lamarr || prob(10))
- set_attack()
-
- if(can_see(src,target,viewrange))
- if(distance <= 1 && (!lamarr || prob(20)))
- for(var/mob/O in viewers(world.view,src))
- O.show_message("\red [target] has been leapt on by [lamarr ? name : "the alien"]!", 1, "\red You hear someone fall", 2)
- if (!lamarr)
- target:take_overall_damage(5)
- if(prob(70))
- target:paralysis = max(target:paralysis, 5)
- loc = target.loc
-
- if(!target.alien_egg_flag && ( ishuman(target) || ismonkey(target) ) )
- if (!lamarr && target)
- var/mob/trg = target
- death()
- //if(trg.virus)//Viruses are stored in a global database.
- //trg.virus.cure(0)//You need to either cure() or del() them to stop their processing.
- trg.contract_disease(new /datum/disease/alien_embryo(0))//So after that you need to infect the target anew.
- for(var/datum/disease/alien_embryo/A in trg.viruses)
- trg.alien_egg_flag = 1//We finally set their flag to 1.
- return
- else
- sleep(50)
- else
- set_null()
- spawn(cycle_pause) process()
- return
-
- step_towards(src,get_step_towards2(src , target))
- else
- if( !path_target.len )
-
- path_attack(target)
- if(!path_target.len)
- set_null()
- spawn(cycle_pause) process()
- return
- else
- var/turf/next = path_target[1]
-
- if(next in range(1,src))
- path_attack(target)
-
- if(!path_target.len)
- frustration += 5
- else
- next = path_target[1]
- path_target -= next
- step_towards(src,next)
- quick_move = 1
-
- if (get_dist(src, target) >= distance) frustration++
- else frustration--
- if(frustration >= 35 || lamarr) set_null()
-
- if(quick_move)
- spawn(cycle_pause/2)
- process()
- else
- spawn(cycle_pause)
- process()
-
- proc/idle()
- set background = 1
- var/quick_move = 0
-
- if(state != 2 || !alive || target) return
-
- if(locate(/obj/effect/alien/weeds) in loc && health < maxhealth)
- health++
- spawn(cycle_pause) idle()
- return
-
- if(!path_idle.len)
-
- if(isalien(trg_idle))
- if(can_see(src,trg_idle,viewrange))
- step_towards(src,get_step_towards2(src , trg_idle))
- else
- path_idle(trg_idle)
- if(!path_idle.len)
- trg_idle = null
- set_idle()
- spawn(cycle_pause) idle()
- return
- else
- var/obj/effect/alien/weeds/W = null
- if(health < maxhealth)
- var/list/the_weeds = new/list()
-
- find_weeds:
- for(var/obj/effect/alien/weeds/weed in range(viewrange,loc))
- if(!can_see(src,weed,viewrange)) continue
- for(var/atom/A in get_turf(weed))
- if(A.density) continue find_weeds
- the_weeds += weed
- if(the_weeds.len)
- W = pick(the_weeds)
-
- if(W)
- path_idle(W)
- if(!path_idle.len)
- trg_idle = null
- spawn(cycle_pause) idle()
- return
- else
- for(var/mob/living/carbon/alien/humanoid/H in range(1,src))
- spawn(cycle_pause) idle()
- return
- step(src,pick(cardinal))
-
- else
-
- if(can_see(src,trg_idle,viewrange))
- switch(get_dist(src, trg_idle))
- if(1)
- if(istype(trg_idle,/obj/effect/alien/weeds))
- step_towards(src,get_step_towards2(src , trg_idle))
- if(2 to INFINITY)
- step_towards(src,get_step_towards2(src , trg_idle))
- if(path_idle.len) path_idle = new/list()
- /*
- if(viewrange+1 to INFINITY)
- step_towards(src,get_step_towards2(src , trg_idle))
- if(path_idle.len) path_idle = new/list()
- quick_move = 1
- */
- else
- var/turf/next = path_idle[1]
- if(!next in range(1,src))
- path_idle(trg_idle)
-
- if(!path_idle.len)
- spawn(cycle_pause) idle()
- return
- else
- next = path_idle[1]
- path_idle -= next
- step_towards(src,next)
- quick_move = 1
-
- if(quick_move)
- spawn(cycle_pause/2)
- idle()
- else
- spawn(cycle_pause)
- idle()
-
- proc/path_idle(var/atom/trg)
- path_idle = AStar(loc, get_turf(trg), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, null, null)
- path_idle = reverselist(path_idle)
-
- proc/path_attack(var/atom/trg)
- target = trg
- path_target = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, null, null)
- path_target = reverselist(path_target)
-
-
- proc/death()
- if(!alive) return
- alive = 0
- density = 0
- icon_state = "facehugger_l"
- set_null()
- for(var/mob/O in hearers(src, null))
- O.show_message("\red [src] curls up into a ball!", 1)
-
- proc/healthcheck()
- if (health <= 0)
- death()
-
-*/
\ No newline at end of file
diff --git a/code/modules/mob/simple_animal/life.dm b/code/modules/mob/simple_animal/life.dm
index 29da09bb6ed..fea5278ddbc 100644
--- a/code/modules/mob/simple_animal/life.dm
+++ b/code/modules/mob/simple_animal/life.dm
@@ -68,7 +68,7 @@
if(health > 0)
icon_state = icon_living
alive = 1
- stat = 0 //Alive - conscious
+ stat = CONSCIOUS
density = 1
return
@@ -128,35 +128,37 @@
//Speaking
if(speak_chance)
if(prob(speak_chance))
- if(speak && (emote_hear || emote_see) )
- var/length = speak.len
- if(emote_hear)
- length += emote_hear.len
- if(emote_see)
- length += emote_see.len
- var/pick = rand(1,length)
- if(pick <= speak.len)
- say(pick(speak))
- else
- pick -= speak.len
- if(emote_see && pick <= emote_see.len)
- emote(pick(emote_see),1)
+ if(speak && speak.len)
+ if((emote_hear && emote_hear.len) || (emote_see && emote_see.len))
+ var/length = speak.len
+ if(emote_hear && emote_hear.len)
+ length += emote_hear.len
+ if(emote_see && emote_see.len)
+ length += emote_see.len
+ var/randomValue = rand(1,length)
+ if(randomValue <= speak.len)
+ say(pick(speak))
else
- emote(pick(emote_hear),2)
- if(!speak)
- if(!emote_hear && emote_see)
+ randomValue -= speak.len
+ if(emote_see && randomValue <= emote_see.len)
+ emote(pick(emote_see),1)
+ else
+ emote(pick(emote_hear),2)
+ else
+ say(pick(speak))
+ else
+ if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len))
emote(pick(emote_see),1)
- if(emote_hear && !emote_see)
+ if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len))
emote(pick(emote_hear),2)
- if(emote_hear && emote_see)
+ if((emote_hear && emote_hear.len) && (emote_see && emote_see.len))
var/length = emote_hear.len + emote_see.len
var/pick = rand(1,length)
if(pick <= emote_see.len)
emote(pick(emote_see),1)
else
emote(pick(emote_hear),2)
- if(speak && !(emote_see || emote_hear))
- say(pick(speak))
+
//Atmos
var/atmos_suitable = 1
@@ -230,7 +232,7 @@
..()
/mob/living/simple_animal/say_quote(var/text)
- if(speak_emote)
+ if(speak_emote && speak_emote.len)
var/emote = pick(speak_emote)
if(emote)
return "[emote], \"[text]\""
diff --git a/code/modules/mob/simple_animal/worm.dm b/code/modules/mob/simple_animal/worm.dm
new file mode 100644
index 00000000000..18b66178e64
--- /dev/null
+++ b/code/modules/mob/simple_animal/worm.dm
@@ -0,0 +1,199 @@
+/mob/living/simple_animal/space_worm
+ name = "space worm segment"
+ desc = "A part of a space worm."
+ icon = 'critter.dmi'
+ icon_state = "spaceworm"
+ icon_living = "spaceworm"
+ icon_dead = "spacewormdead"
+
+ speak_emote = list("transmits") //not supposed to be used under AI control
+ emote_hear = list("transmits") //I'm just adding it so it doesn't runtime if controlled by player who speaks
+
+ response_help = "touches"
+ response_disarm = "flails at"
+ response_harm = "punches the"
+
+ harm_intent_damage = 2
+
+ maxHealth = 30
+ health = 30
+
+ stop_automated_movement = 1
+ animate_movement = SYNC_STEPS
+
+ minbodytemp = 0
+ maxbodytemp = 350
+ min_oxy = 0
+ max_co2 = 0
+ max_tox = 0
+
+ a_intent = "harm" //so they don't get pushed around
+
+ nopush = 1
+ wall_smash = 1
+
+ speed = -1
+
+ var/mob/living/simple_animal/space_worm/previous //next/previous segments, correspondingly
+ var/mob/living/simple_animal/space_worm/next //head is the nextest segment
+
+ var/stomachProcessProbability = 50
+ var/digestionProbability = 20
+ var/flatPlasmaValue = 5 //flat plasma amount given for non-items
+
+ var/atom/currentlyEating //what the worm is currently eating
+ var/eatingDuration = 0 //how long he's been eating it for
+
+ head
+ name = "space worm head"
+ icon_state = "spacewormhead"
+ icon_living = "spacewormhead"
+ icon_dead = "spacewormdead"
+
+ maxHealth = 20
+ health = 20
+
+ melee_damage_lower = 10
+ melee_damage_upper = 15
+ attacktext = "bites"
+
+ animate_movement = SLIDE_STEPS
+
+ New(var/location, var/segments = 6)
+ ..()
+
+ var/mob/living/simple_animal/space_worm/current = src
+
+ for(var/i = 1 to segments)
+ var/mob/living/simple_animal/space_worm/newSegment = new /mob/living/simple_animal/space_worm(loc)
+ current.Attach(newSegment)
+ current = newSegment
+
+ update_icon()
+ if(stat == CONSCIOUS || stat == UNCONSCIOUS)
+ icon_state = "spacewormhead[previous?1:0]"
+ if(previous)
+ dir = get_dir(previous,src)
+ else
+ icon_state = "spacewormheaddead"
+
+ Life()
+ ..()
+
+ if(next && !(next in view(src,1)))
+ Detach()
+
+ if(stat == DEAD) //dead chunks fall off and die immediately
+ if(previous)
+ previous.Detach()
+ if(next)
+ Detach(1)
+
+ if(prob(stomachProcessProbability))
+ ProcessStomach()
+
+ update_icon()
+
+ return
+
+ Del() //if a chunk a destroyed, make a new worm out of the split halves
+ if(previous)
+ previous.Detach()
+ ..()
+
+ Move()
+ var/attachementNextPosition = loc
+ if(..())
+ if(previous)
+ previous.Move(attachementNextPosition)
+ update_icon()
+
+ Bump(atom/obstacle)
+ if(currentlyEating != obstacle)
+ currentlyEating = obstacle
+ eatingDuration = 0
+
+ if(!AttemptToEat(obstacle))
+ eatingDuration++
+ else
+ currentlyEating = null
+ eatingDuration = 0
+
+ return
+
+ proc/update_icon() //only for the sake of consistency with the other update icon procs
+ if(stat == CONSCIOUS || stat == UNCONSCIOUS)
+ if(previous) //midsection
+ icon_state = "spaceworm[get_dir(src,previous) | get_dir(src,next)]" //see 3 lines below
+ else //tail
+ icon_state = "spacewormtail"
+ dir = get_dir(src,next) //next will always be present since it's not a head and if it's dead, it goes in the other if branch
+ else
+ icon_state = "spacewormdead"
+
+ return
+
+ proc/AttemptToEat(var/atom/target)
+ if(istype(target,/turf/simulated/wall))
+ if((!istype(target,/turf/simulated/wall/r_wall) && eatingDuration >= 100) || eatingDuration >= 200) //need 20 ticks to eat an rwall, 10 for a regular one
+ var/turf/simulated/wall/wall = target
+ wall.ReplaceWithFloor()
+ new /obj/item/stack/sheet/metal(src, flatPlasmaValue)
+ return 1
+ else if(istype(target,/atom/movable))
+ if(istype(target,/mob) || eatingDuration >= 50) //5 ticks to eat stuff like airlocks
+ var/atom/movable/objectOrMob = target
+ contents += objectOrMob
+ return 1
+
+ return 0
+
+ proc/Attach(var/mob/living/simple_animal/space_worm/attachement)
+ if(!attachement)
+ return
+
+ previous = attachement
+ attachement.next = src
+
+ return
+
+ proc/Detach(die = 0)
+ var/mob/living/simple_animal/space_worm/newHead = new /mob/living/simple_animal/space_worm/head(loc,0)
+ var/mob/living/simple_animal/space_worm/newHeadPrevious = previous
+
+ previous = null //so that no extra heads are spawned
+
+ newHead.Attach(newHeadPrevious)
+
+ if(die)
+ newHead.Die()
+
+ del(src)
+
+ proc/ProcessStomach()
+ for(var/atom/movable/stomachContent in contents)
+ if(prob(digestionProbability))
+ if(istype(stomachContent,/obj/item/stack)) //converts to plasma, keeping the stack value
+ if(!istype(stomachContent,/obj/item/stack/sheet/plasma))
+ var/obj/item/stack/oldStack = stomachContent
+ new /obj/item/stack/sheet/plasma(src, oldStack.amount)
+ del(oldStack)
+ continue
+ else if(istype(stomachContent,/obj/item)) //converts to plasma, keeping the w_class
+ var/obj/item/oldItem = stomachContent
+ new /obj/item/stack/sheet/plasma(src, oldItem.w_class)
+ del(oldItem)
+ continue
+ else
+ new /obj/item/stack/sheet/plasma(src, flatPlasmaValue) //just flat amount
+ del(stomachContent)
+ continue
+
+ if(previous)
+ for(var/atom/movable/stomachContent in contents) //transfer it along the digestive tract
+ previous.contents += stomachContent
+ else
+ for(var/atom/movable/stomachContent in contents) //or poop it out
+ loc.contents += stomachContent
+
+ return
\ No newline at end of file