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