mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-18 10:33:30 +01:00
conflict resolution
This commit is contained in:
@@ -25,7 +25,7 @@ var/global/list/boo_phrases=list(
|
||||
invocation_type = "none"
|
||||
range = 1 // Or maybe 3?
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/boo/cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/aoe_turf/boo/cast(list/targets, mob/user = usr)
|
||||
for(var/turf/T in targets)
|
||||
for(var/atom/A in T.contents)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
/mob/living/carbon/brain/New()
|
||||
create_reagents(330)
|
||||
add_language("Galactic Common")
|
||||
..()
|
||||
|
||||
/mob/living/carbon/brain/Destroy()
|
||||
|
||||
@@ -914,14 +914,14 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
|
||||
|
||||
/mob/living/carbon/proc/slip(var/description, var/stun, var/weaken, var/tilesSlipped, var/walkSafely, var/slipAny)
|
||||
if(flying || buckled || (walkSafely && m_intent == "walk"))
|
||||
return
|
||||
return 0
|
||||
if((lying) && (!(tilesSlipped)))
|
||||
return
|
||||
return 0
|
||||
if(!(slipAny))
|
||||
if(istype(src, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = src
|
||||
if((isobj(H.shoes) && H.shoes.flags & NOSLIP) || H.species.bodyflags & FEET_NOSLIP)
|
||||
return
|
||||
return 0
|
||||
if(tilesSlipped)
|
||||
for(var/t = 0, t<=tilesSlipped, t++)
|
||||
spawn (t) step(src, src.dir)
|
||||
@@ -995,8 +995,8 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/proc/forceFedAttackLog(var/obj/item/weapon/reagent_containers/food/toEat, mob/user)
|
||||
attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been fed [toEat.name] by [user.name] ([user.ckey]) Reagents: [toEat.reagentlist(toEat)]</font>")
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Fed [toEat.name] to [name] ([ckey]) Reagents: [toEat.reagentlist(toEat)]</font>")
|
||||
create_attack_log("<font color='orange'>Has been fed [toEat.name] by [user.name] ([user.ckey]) Reagents: [toEat.reagentlist(toEat)]</font>")
|
||||
user.create_attack_log("<font color='red'>Fed [toEat.name] to [name] ([ckey]) Reagents: [toEat.reagentlist(toEat)]</font>")
|
||||
log_attack("[user.name] ([user.ckey]) fed [name] ([ckey]) with [toEat.name] Reagents: [toEat.reagentlist(toEat)] (INTENT: [uppertext(user.a_intent)])")
|
||||
if(!iscarbon(user))
|
||||
LAssailant = null
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
for(var/datum/disease/D in user.viruses)
|
||||
if(D.IsSpreadByTouch())
|
||||
ContractDisease(D)
|
||||
return 0
|
||||
return 0
|
||||
@@ -83,12 +83,12 @@
|
||||
message = "<B>[src]</B> pings at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> pings."
|
||||
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("buzz2")
|
||||
message = "<B>[src]</B> emits an irritated buzzing sound."
|
||||
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("buzz", "buzzes")
|
||||
@@ -105,7 +105,7 @@
|
||||
message = "<B>[src]</B> buzzes at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> buzzes."
|
||||
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("beep", "beeps")
|
||||
@@ -122,7 +122,7 @@
|
||||
message = "<B>[src]</B> beeps at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> beeps."
|
||||
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("drone", "drones", "hum", "hums", "rumble", "rumbles")
|
||||
@@ -139,7 +139,7 @@
|
||||
message = "<B>[src]</B> drones at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> rumbles."
|
||||
playsound(src.loc, 'sound/voice/DraskTalk.ogg', 50, 0)
|
||||
playsound(loc, 'sound/voice/DraskTalk.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("squish", "squishes")
|
||||
@@ -156,7 +156,7 @@
|
||||
message = "<B>[src]</B> squishes at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> squishes."
|
||||
playsound(src.loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound.
|
||||
playsound(loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound.
|
||||
m_type = 2
|
||||
|
||||
if("yes")
|
||||
@@ -173,7 +173,7 @@
|
||||
message = "<B>[src]</B> emits an affirmative blip at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> emits an affirmative blip."
|
||||
playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/synth_yes.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("no")
|
||||
@@ -190,7 +190,7 @@
|
||||
message = "<B>[src]</B> emits a negative blip at [param]."
|
||||
else
|
||||
message = "<B>[src]</B> emits a negative blip."
|
||||
playsound(src.loc, 'sound/machines/synth_no.ogg', 50, 0)
|
||||
playsound(loc, 'sound/machines/synth_no.ogg', 50, 0)
|
||||
m_type = 2
|
||||
|
||||
if("wag", "wags")
|
||||
@@ -202,7 +202,7 @@
|
||||
else if(species.bodyflags & TAIL_WAGGING)
|
||||
if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space))
|
||||
message = "<B>[src]</B> starts wagging \his tail."
|
||||
src.start_tail_wagging(1)
|
||||
start_tail_wagging(1)
|
||||
else
|
||||
return
|
||||
else
|
||||
@@ -212,15 +212,25 @@
|
||||
if("swag", "swags")
|
||||
if(species.bodyflags & TAIL_WAGGING || body_accessory)
|
||||
message = "<B>[src]</B> stops wagging \his tail."
|
||||
src.stop_tail_wagging(1)
|
||||
stop_tail_wagging(1)
|
||||
else
|
||||
return
|
||||
m_type = 1
|
||||
|
||||
if("airguitar")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> is strumming the air and headbanging like a safari chimp."
|
||||
m_type = 1
|
||||
|
||||
if("dance")
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> dances around happily."
|
||||
m_type = 1
|
||||
|
||||
if("jump")
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> jumps!"
|
||||
m_type = 1
|
||||
|
||||
if("blink", "blinks")
|
||||
message = "<B>[src]</B> blinks."
|
||||
@@ -231,7 +241,7 @@
|
||||
m_type = 1
|
||||
|
||||
if("bow", "bows")
|
||||
if(!src.buckled)
|
||||
if(!buckled)
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(null, null))
|
||||
@@ -248,7 +258,7 @@
|
||||
m_type = 1
|
||||
|
||||
if("salute", "salutes")
|
||||
if(!src.buckled)
|
||||
if(!buckled)
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(null, null))
|
||||
@@ -288,13 +298,13 @@
|
||||
message = "<B>[src]</B> makes a peculiar noise."
|
||||
m_type = 2
|
||||
if("clap", "claps")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> claps."
|
||||
m_type = 2
|
||||
if(miming)
|
||||
m_type = 1
|
||||
if("flap", "flaps")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> flaps \his wings."
|
||||
m_type = 2
|
||||
if(miming)
|
||||
@@ -302,7 +312,7 @@
|
||||
|
||||
if("flip", "flips")
|
||||
m_type = 1
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(1, null))
|
||||
@@ -340,7 +350,7 @@
|
||||
SpinAnimation(5,1)
|
||||
|
||||
if("aflap", "aflaps")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> flaps \his wings ANGRILY!"
|
||||
m_type = 2
|
||||
if(miming)
|
||||
@@ -376,7 +386,7 @@
|
||||
|
||||
if("faint", "faints")
|
||||
message = "<B>[src]</B> faints."
|
||||
if(src.sleeping)
|
||||
if(sleeping)
|
||||
return //Can't faint while asleep
|
||||
AdjustSleeping(2)
|
||||
m_type = 1
|
||||
@@ -578,11 +588,11 @@
|
||||
message = "<B>[src]</B> takes a drag from a cigarette and blows \"[M]\" out in smoke."
|
||||
m_type = 1
|
||||
else
|
||||
message = "<B>[src]</B> says, \"[M], please. They had a family.\" [src.name] takes a drag from a cigarette and blows their name out in smoke."
|
||||
message = "<B>[src]</B> says, \"[M], please. They had a family.\" [name] takes a drag from a cigarette and blows their name out in smoke."
|
||||
m_type = 2
|
||||
|
||||
if("point", "points")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/atom/M = null
|
||||
if(param)
|
||||
for(var/atom/A as mob|obj|turf in view())
|
||||
@@ -597,7 +607,7 @@
|
||||
m_type = 1
|
||||
|
||||
if("raise", "raises")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
message = "<B>[src]</B> raises a hand."
|
||||
m_type = 1
|
||||
|
||||
@@ -610,12 +620,12 @@
|
||||
m_type = 1
|
||||
|
||||
if("signal", "signals")
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/t1 = round(text2num(param))
|
||||
if(isnum(t1))
|
||||
if(t1 <= 5 && (!src.r_hand || !src.l_hand))
|
||||
if(t1 <= 5 && (!r_hand || !l_hand))
|
||||
message = "<B>[src]</B> raises [t1] finger\s."
|
||||
else if(t1 <= 10 && (!src.r_hand && !src.l_hand))
|
||||
else if(t1 <= 10 && (!r_hand && !l_hand))
|
||||
message = "<B>[src]</B> raises [t1] finger\s."
|
||||
m_type = 1
|
||||
|
||||
@@ -699,7 +709,7 @@
|
||||
|
||||
if("hug", "hugs")
|
||||
m_type = 1
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(1, null))
|
||||
@@ -716,7 +726,7 @@
|
||||
|
||||
if("handshake")
|
||||
m_type = 1
|
||||
if(!src.restrained() && !src.r_hand)
|
||||
if(!restrained() && !r_hand)
|
||||
var/mob/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(1, null))
|
||||
@@ -734,7 +744,7 @@
|
||||
|
||||
if("dap", "daps")
|
||||
m_type = 1
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(1, null))
|
||||
@@ -748,7 +758,7 @@
|
||||
|
||||
if("slap", "slaps")
|
||||
m_type = 1
|
||||
if(!src.restrained())
|
||||
if(!restrained())
|
||||
var/M = null
|
||||
if(param)
|
||||
for(var/mob/A in view(1, null))
|
||||
@@ -757,11 +767,11 @@
|
||||
break
|
||||
if(M)
|
||||
message = "<span class='danger'>[src] slaps [M] across the face. Ouch!</span>"
|
||||
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
else
|
||||
message = "<span class='danger'>[src] slaps \himself!</span>"
|
||||
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
src.adjustFireLoss(4)
|
||||
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
adjustFireLoss(4)
|
||||
|
||||
if("scream", "screams")
|
||||
if(miming)
|
||||
@@ -772,9 +782,9 @@
|
||||
message = "<B>[src]</B> [species.scream_verb]!"
|
||||
m_type = 2
|
||||
if(gender == FEMALE)
|
||||
playsound(src.loc, "[species.female_scream_sound]", 80, 1, 0, pitch = get_age_pitch())
|
||||
playsound(loc, "[species.female_scream_sound]", 80, 1, 0, pitch = get_age_pitch())
|
||||
else
|
||||
playsound(src.loc, "[species.male_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) //default to male screams if no gender is present.
|
||||
playsound(loc, "[species.male_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) //default to male screams if no gender is present.
|
||||
|
||||
else
|
||||
message = "<B>[src]</B> makes a very loud noise."
|
||||
@@ -799,24 +809,24 @@
|
||||
return
|
||||
|
||||
message = "<b>[src]</b> snaps \his fingers."
|
||||
playsound(src.loc, 'sound/effects/fingersnap.ogg', 50, 1, -3)
|
||||
playsound(loc, 'sound/effects/fingersnap.ogg', 50, 1, -3)
|
||||
else
|
||||
message = "<span class='danger'><b>[src]</b> snaps \his fingers right off!</span>"
|
||||
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
|
||||
|
||||
|
||||
// Needed for M_TOXIC_FART
|
||||
if("fart", "farts")
|
||||
if(reagents.has_reagent("simethicone"))
|
||||
return
|
||||
// playsound(src.loc, 'sound/effects/fart.ogg', 50, 1, -3) //Admins still vote no to fun
|
||||
// playsound(loc, 'sound/effects/fart.ogg', 50, 1, -3) //Admins still vote no to fun
|
||||
if(locate(/obj/item/weapon/storage/bible) in get_turf(src))
|
||||
to_chat(viewers(src), "<span class='warning'><b>[src] farts on the Bible!</b></span>")
|
||||
to_chat(viewers(src), "<span class='notice'><b>A mysterious force smites [src]!</b></span>")
|
||||
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
src.gib()
|
||||
gib()
|
||||
else if(TOXIC_FARTS in mutations)
|
||||
message = "<b>[src]</b> unleashes a [pick("horrible","terrible","foul","disgusting","awful")] fart."
|
||||
else if(SUPER_FART in mutations)
|
||||
@@ -841,9 +851,9 @@
|
||||
M.reagents.add_reagent("jenkem", 1)
|
||||
|
||||
if("help")
|
||||
var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-(none)/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s),cry, cries, custom, dap(s)(none)/mob," \
|
||||
+ " deathgasp(s), drool(s), eyebrow,fart(s), faint(s), flap(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-(none)/mob, grin(s), groan(s), grumble(s), handshake-mob, hug(s)-(none)/mob," \
|
||||
+ " glare(s)-(none)/mob, grin(s), johnny, laugh(s), look(s)-(none)/mob, moan(s), mumble(s), nod(s), pale(s), point(s)-atom, quiver(s), raise(s), salute(s)-(none)/mob, scream(s), shake(s)," \
|
||||
var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-(none)/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s),cry, cries, custom, dance, dap(s)(none)/mob," \
|
||||
+ " deathgasp(s), drool(s), eyebrow, fart(s), faint(s), flap(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-(none)/mob, grin(s), groan(s), grumble(s), grin(s)," \
|
||||
+ " handshake-mob, hug(s)-(none)/mob, johnny, jump, laugh(s), look(s)-(none)/mob, moan(s), mumble(s), nod(s), pale(s), point(s)-atom, quiver(s), raise(s), salute(s)-(none)/mob, scream(s), shake(s)," \
|
||||
+ " shiver(s), shrug(s), sigh(s), signal(s)-#1-10,slap(s)-(none)/mob, smile(s),snap(s), sneeze(s), sniff(s), snore(s), stare(s)-(none)/mob, swag(s), tremble(s), twitch(es), twitch(es)_s," \
|
||||
+ " wag(s), wave(s), whimper(s), wink(s), yawn(s)"
|
||||
if(species.name == "Machine")
|
||||
|
||||
@@ -182,8 +182,8 @@
|
||||
apply_effect(2, WEAKEN, run_armor_check(affecting, "melee"))
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has pushed [src]!</span>")
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>Pushed [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been pushed by [M.name] ([M.ckey])</font>")
|
||||
M.create_attack_log("<font color='red'>Pushed [src.name] ([src.ckey])</font>")
|
||||
src.create_attack_log("<font color='orange'>Has been pushed by [M.name] ([M.ckey])</font>")
|
||||
if(!iscarbon(M))
|
||||
LAssailant = null
|
||||
else
|
||||
|
||||
@@ -38,6 +38,7 @@ emp_act
|
||||
|
||||
var/obj/item/organ/external/organ = get_organ(check_zone(def_zone))
|
||||
if(isnull(organ))
|
||||
. = bullet_act(P, "chest") //act on chest instead
|
||||
return
|
||||
|
||||
//Shrapnel
|
||||
@@ -56,6 +57,18 @@ emp_act
|
||||
organ.add_autopsy_data(P.name, P.damage) // Add the bullet's name to the autopsy data
|
||||
|
||||
return (..(P , def_zone))
|
||||
|
||||
/mob/living/carbon/human/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
|
||||
var/obj/item/organ/external/affecting = get_organ(check_zone(def_zone))
|
||||
if(affecting && !affecting.cannot_amputate && affecting.get_damage() >= (affecting.max_damage - P.dismemberment))
|
||||
var/damtype = DROPLIMB_EDGE
|
||||
switch(P.damage_type)
|
||||
if(BRUTE)
|
||||
damtype = DROPLIMB_BLUNT
|
||||
if(BURN)
|
||||
damtype = DROPLIMB_BURN
|
||||
|
||||
affecting.droplimb(FALSE, damtype)
|
||||
|
||||
/mob/living/carbon/human/getarmor(var/def_zone, var/type)
|
||||
var/armorval = 0
|
||||
@@ -187,8 +200,8 @@ emp_act
|
||||
--src.meatleft
|
||||
to_chat(user, "\red You hack off a chunk of meat from [src.name]")
|
||||
if(!src.meatleft)
|
||||
src.attack_log += "\[[time_stamp()]\] Was chopped up into meat by <b>[key_name(user)]</b>"
|
||||
user.attack_log += "\[[time_stamp()]\] Chopped up <b>[key_name(src)]</b> into meat</b>"
|
||||
src.create_attack_log("Was chopped up into meat by <b>[key_name(user)]</b>")
|
||||
user.create_attack_log("Chopped up <b>[key_name(src)]</b> into meat</b>")
|
||||
msg_admin_attack("[key_name_admin(user)] chopped up [key_name_admin(src)] into meat")
|
||||
if(!iscarbon(user))
|
||||
LAssailant = null
|
||||
@@ -436,8 +449,8 @@ emp_act
|
||||
visible_message("<span class='danger'>[src] has been hit by [M.name].</span>", \
|
||||
"<span class='userdanger'>[src] has been hit by [M.name].</span>")
|
||||
|
||||
attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked by \the [M] controlled by [key_name(M.occupant)] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
M.occupant.attack_log += text("\[[time_stamp()]\] <font color='red'>Attacked [src] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
create_attack_log("<font color='orange'>Has been attacked by \the [M] controlled by [key_name(M.occupant)] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
M.occupant.create_attack_log("<font color='red'>Attacked [src] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
msg_admin_attack("[key_name_admin(M.occupant)] attacked [key_name_admin(src)] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])")
|
||||
|
||||
else
|
||||
|
||||
@@ -119,15 +119,15 @@
|
||||
internalBeaker.name = "Grow-U-All Super Spray"
|
||||
|
||||
if(internalBeaker && internalBag)
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/HP
|
||||
var/obj/machinery/hydroponics/HP
|
||||
|
||||
//consider the appropriate target
|
||||
var/list/considered = list()
|
||||
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/tester in view(12,src))
|
||||
for(var/obj/machinery/hydroponics/tester in view(12,src))
|
||||
considered[tester] = 1
|
||||
|
||||
if(!tester.seed)
|
||||
if(!tester.myseed)
|
||||
considered[tester] += 50
|
||||
if(tester.weedlevel > 0)
|
||||
considered[tester] += 5
|
||||
@@ -154,7 +154,7 @@
|
||||
else
|
||||
if(HP.harvest || HP.dead)
|
||||
HP.attack_hand(src)
|
||||
else if(!HP.seed)
|
||||
else if(!HP.myseed)
|
||||
var/obj/item/seeds/SEED = new /obj/item/seeds/random(src)
|
||||
custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!")
|
||||
HP.attackby(SEED, src)
|
||||
@@ -170,8 +170,8 @@
|
||||
internalBeaker.reagents.add_reagent("diethylamine", 10)
|
||||
if(HP.nutrilevel < HP.maxnutri)
|
||||
change = 1
|
||||
if(!internalBeaker.reagents.has_reagent("eznutrient", 15))
|
||||
internalBeaker.reagents.add_reagent("eznutrient", 15)
|
||||
if(!internalBeaker.reagents.has_reagent("eznutriment", 15))
|
||||
internalBeaker.reagents.add_reagent("eznutriment", 15)
|
||||
if(!internalBeaker.reagents.has_reagent("diethylamine", 15))
|
||||
internalBeaker.reagents.add_reagent("diethylamine", 15)
|
||||
if(HP.waterlevel < HP.maxwater)
|
||||
@@ -261,7 +261,7 @@
|
||||
var/pranksNearby = 100
|
||||
for(var/turf/simulated/T in orange(1, C))
|
||||
for(var/obj/item/A in T)
|
||||
if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/bananapeel))
|
||||
if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/grown/bananapeel))
|
||||
pranksNearby--
|
||||
if(T.wet)
|
||||
pranksNearby -= 10
|
||||
@@ -287,7 +287,7 @@
|
||||
if(istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana))
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/B = A
|
||||
B.attack(src, src)
|
||||
if(istype(A,/obj/item/weapon/bananapeel))
|
||||
if(istype(A,/obj/item/weapon/grown/bananapeel))
|
||||
npcDrop(A)
|
||||
hasPranked = 1
|
||||
if(!hasPranked)
|
||||
@@ -403,26 +403,6 @@
|
||||
ingredients += I
|
||||
I.forceMove(null)
|
||||
|
||||
for(var/P in R.fruit)
|
||||
for(var/i = 1 to R.fruit[P])
|
||||
var/obj/item/I = locate(P) in allContents
|
||||
if(I)
|
||||
ingredients += I
|
||||
I.forceMove(null)
|
||||
continue
|
||||
|
||||
I = locate(P) in rangeCheck
|
||||
TARGET = I
|
||||
if(I && !Adjacent(I))
|
||||
tryWalk(get_turf(I))
|
||||
sleep(get_dist(src, I))
|
||||
if(!I || !(I in rangeCheck))
|
||||
refundrecipe(ingredients)
|
||||
return 0
|
||||
custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], picking up [I].")
|
||||
ingredients += I
|
||||
I.forceMove(null)
|
||||
|
||||
// cheaply cook the ingredients into result
|
||||
sleep(R.time)
|
||||
for(var/obj/I in ingredients)
|
||||
@@ -579,8 +559,8 @@
|
||||
var/highest_count = 0
|
||||
var/datum/recipe/winner = null
|
||||
for(var/datum/recipe/R in available_recipes)
|
||||
if(R.check_items(ingredientZone) >= 0 && R.check_fruit(ingredientZone) >= 0)
|
||||
var/count = (R.items ? R.items.len : 0) + (R.fruit ? R.fruit.len : 0)
|
||||
if(R.check_items(ingredientZone) >= 0)
|
||||
var/count = (R.items ? R.items.len : 0)
|
||||
if(count > highest_count)
|
||||
highest_count = count
|
||||
winner = R
|
||||
|
||||
@@ -349,10 +349,10 @@
|
||||
favoured_types = list(/obj/item/weapon/mop, /obj/item/weapon/reagent_containers/glass/bucket, /obj/item/weapon/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable)
|
||||
functions += "dojanitor"
|
||||
if("Clown")
|
||||
favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana, /obj/item/weapon/bananapeel)
|
||||
favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana, /obj/item/weapon/grown/bananapeel)
|
||||
functions += "clowning"
|
||||
if("Botanist")
|
||||
favoured_types = list(/obj/machinery/portable_atmospherics/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon)
|
||||
favoured_types = list(/obj/machinery/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon)
|
||||
functions += "botany"
|
||||
restrictedJob = 1
|
||||
else
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/mob/living/carbon/human/say(var/message)
|
||||
/mob/living/carbon/human/say(var/message, var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE)
|
||||
var/alt_name = ""
|
||||
|
||||
if(name != GetVoice())
|
||||
alt_name = " (as [get_id_name("Unknown")])"
|
||||
|
||||
..(message, alt_name = alt_name) //ohgod we should really be passing a datum here.
|
||||
..(message, alt_name = alt_name, sanitize = sanitize, ignore_speech_problems = ignore_speech_problems, ignore_atmospherics = ignore_atmospherics) //ohgod we should really be passing a datum here.
|
||||
|
||||
/mob/living/carbon/human/proc/forcesay(list/append)
|
||||
if(stat == CONSCIOUS)
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
head_organ.h_style = "Bald"
|
||||
H.update_hair()
|
||||
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>removed antennae [H.name] ([H.ckey])</font>")
|
||||
H.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has had their antennae removed by [M.name] ([M.ckey])</font>")
|
||||
M.create_attack_log("<font color='red'>removed antennae [H.name] ([H.ckey])</font>")
|
||||
H.create_attack_log("<font color='orange'>Has had their antennae removed by [M.name] ([M.ckey])</font>")
|
||||
msg_admin_attack("[key_name(M)] removed [key_name(H)]'s antennae")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
default_language = "Galactic Common"
|
||||
unarmed_type = /datum/unarmed_attack/claws
|
||||
|
||||
ignored_by = list(/mob/living/simple_animal/hostile/faithless)
|
||||
|
||||
blood_color = "#CCCCCC"
|
||||
flesh_color = "#AAAAAA"
|
||||
has_organ = list(
|
||||
@@ -20,6 +22,7 @@
|
||||
oxy_mod = 0
|
||||
|
||||
virus_immune = 1
|
||||
|
||||
dietflags = DIET_OMNI //the mutation process allowed you to now digest all foods regardless of initial race
|
||||
reagent_tag = PROCESS_ORG
|
||||
suicide_messages = list(
|
||||
@@ -28,13 +31,45 @@
|
||||
"is twisting their own neck!",
|
||||
"is staring into the closest light source!")
|
||||
|
||||
var/grant_vision_toggle = 1
|
||||
var/datum/action/innate/shadow/darkvision/vision_toggle
|
||||
|
||||
/datum/action/innate/shadow/darkvision //Darkvision toggle so shadowpeople can actually see where darkness is
|
||||
name = "Toggle Darkvision"
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
background_icon_state = "bg_default"
|
||||
button_icon_state = "blind"
|
||||
|
||||
/datum/action/innate/shadow/darkvision/Activate()
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(!H.vision_type)
|
||||
H.vision_type = new /datum/vision_override/nightvision
|
||||
to_chat(H, "<span class='notice'>You adjust your vision to pierce the darkness.</span>")
|
||||
else
|
||||
H.vision_type = null
|
||||
to_chat(H, "<span class='notice'>You adjust your vision to recognize the shadows.</span>")
|
||||
|
||||
/datum/species/shadow/grant_abilities(var/mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
if(grant_vision_toggle)
|
||||
vision_toggle = new
|
||||
vision_toggle.Grant(H)
|
||||
|
||||
/datum/species/shadow/remove_abilities(var/mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
if(grant_vision_toggle && vision_toggle)
|
||||
H.vision_type = null
|
||||
vision_toggle.Remove(H)
|
||||
|
||||
/datum/species/shadow/handle_life(var/mob/living/carbon/human/H)
|
||||
var/light_amount = 0
|
||||
if(isturf(H.loc))
|
||||
var/turf/T = H.loc
|
||||
light_amount = T.get_lumcount()*10
|
||||
light_amount = T.get_lumcount() * 10
|
||||
|
||||
if(light_amount > 2) //if there's enough light, start dying
|
||||
H.take_overall_damage(1,1)
|
||||
H.throw_alert("lightexposure", /obj/screen/alert/lightexposure)
|
||||
else if(light_amount < 2) //heal in the dark
|
||||
H.heal_overall_damage(1,1)
|
||||
H.heal_overall_damage(1,1)
|
||||
H.clear_alert("lightexposure")
|
||||
@@ -49,6 +49,7 @@
|
||||
var/siemens_coeff = 1 //base electrocution coefficient
|
||||
|
||||
var/invis_sight = SEE_INVISIBLE_LIVING
|
||||
|
||||
var/hazard_high_pressure = HAZARD_HIGH_PRESSURE // Dangerously high pressure.
|
||||
var/warning_high_pressure = WARNING_HIGH_PRESSURE // High pressure warning.
|
||||
var/warning_low_pressure = WARNING_LOW_PRESSURE // Low pressure warning.
|
||||
@@ -84,6 +85,8 @@
|
||||
var/ventcrawler = 0 //Determines if the mob can go through the vents.
|
||||
var/has_fine_manipulation = 1 // Can use small items.
|
||||
|
||||
var/mob/living/list/ignored_by = list() // list of mobs that will ignore this species
|
||||
|
||||
var/list/allowed_consumed_mobs = list() //If a species can consume mobs, put the type of mobs it can consume here.
|
||||
|
||||
var/flags = 0 // Various specific features.
|
||||
@@ -161,7 +164,6 @@
|
||||
"l_foot" = list("path" = /obj/item/organ/external/foot),
|
||||
"r_foot" = list("path" = /obj/item/organ/external/foot/right)
|
||||
)
|
||||
var/cyborg_type = "Cyborg"
|
||||
var/list/proc/species_abilities = list()
|
||||
|
||||
/datum/species/New()
|
||||
|
||||
@@ -806,8 +806,8 @@
|
||||
return ..()
|
||||
|
||||
/datum/species/diona/handle_life(var/mob/living/carbon/human/H)
|
||||
var/rads = H.radiation/25
|
||||
H.radiation = Clamp(H.radiation - rads, 0, 100)
|
||||
var/rads = H.radiation / 25
|
||||
H.apply_effect(-rads,IRRADIATE,0)
|
||||
H.nutrition += rads
|
||||
H.adjustBruteLoss(-(rads))
|
||||
H.adjustOxyLoss(-(rads))
|
||||
@@ -816,18 +816,23 @@
|
||||
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
|
||||
if(isturf(H.loc)) //else, there's considered to be no light
|
||||
var/turf/T = H.loc
|
||||
light_amount = min(T.get_lumcount()*10, 5) //hardcapped so it's not abused by having a ton of flashlights
|
||||
light_amount = min(T.get_lumcount() * 10, 5) //hardcapped so it's not abused by having a ton of flashlights
|
||||
H.nutrition += light_amount
|
||||
H.traumatic_shock -= light_amount
|
||||
|
||||
if(H.nutrition > NUTRITION_LEVEL_WELL_FED)
|
||||
H.nutrition = NUTRITION_LEVEL_WELL_FED
|
||||
|
||||
if(light_amount > 0)
|
||||
H.clear_alert("nolight")
|
||||
else
|
||||
H.throw_alert("nolight", /obj/screen/alert/nolight)
|
||||
|
||||
if((light_amount >= 5) && !H.suiciding) //if there's enough light, heal
|
||||
|
||||
H.adjustBruteLoss(-(light_amount/2))
|
||||
H.adjustFireLoss(-(light_amount/4))
|
||||
H.adjustOxyLoss(-(light_amount))
|
||||
|
||||
if(H.nutrition < 200)
|
||||
H.take_overall_damage(10,0)
|
||||
H.traumatic_shock++
|
||||
|
||||
@@ -264,8 +264,8 @@
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
visible_message("<span class='danger'>[M] [M.attacktext] [src]!</span>", \
|
||||
"<span class='userdanger'>[M] [M.attacktext] [src]!</span>")
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>was attacked by [M.name] ([M.ckey])</font>")
|
||||
M.create_attack_log("<font color='red'>attacked [src.name] ([src.ckey])</font>")
|
||||
src.create_attack_log("<font color='orange'>was attacked by [M.name] ([M.ckey])</font>")
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
attacked += 10
|
||||
adjustBruteLoss(damage)
|
||||
|
||||
@@ -147,69 +147,69 @@
|
||||
action_icon_state = "spell_greytide"
|
||||
var/recruiting = 0
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets,mob/living/user = usr)
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
var/obj/item/organ/external/head/head_organ = target.get_organ("head")
|
||||
if(ticker.mode.greyshirts.len >= 3)
|
||||
to_chat(usr, "<span class='warning'>You have already recruited the maximum number of henchmen.</span>")
|
||||
if(!in_range(usr, target))
|
||||
to_chat(usr, "<span class='warning'>You need to be closer to enthrall [target].</span>")
|
||||
to_chat(user, "<span class='warning'>You have already recruited the maximum number of henchmen.</span>")
|
||||
if(!in_range(user, target))
|
||||
to_chat(user, "<span class='warning'>You need to be closer to enthrall [target].</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!target.ckey)
|
||||
to_chat(usr, "<span class='warning'>The target has no mind.</span>")
|
||||
to_chat(user, "<span class='warning'>The target has no mind.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(usr, "<span class='warning'>The target must be conscious.</span>")
|
||||
to_chat(user, "<span class='warning'>The target must be conscious.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(usr, "<span class='warning'>You can only recruit humans.</span>")
|
||||
to_chat(user, "<span class='warning'>You can only recruit humans.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.mind.assigned_role != "Civilian")
|
||||
to_chat(usr, "<span class='warning'>You can only recruit Civilians.</span>")
|
||||
to_chat(user, "<span class='warning'>You can only recruit Civilians.</span>")
|
||||
if(recruiting)
|
||||
to_chat(usr, "<span class='danger'>You are already recruiting!</span>")
|
||||
to_chat(user, "<span class='danger'>You are already recruiting!</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
recruiting = 1
|
||||
to_chat(usr, "<span class='danger'>This target is valid. You begin the recruiting process.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[usr] focuses in concentration. Your head begins to ache.</span>")
|
||||
to_chat(user, "<span class='danger'>This target is valid. You begin the recruiting process.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[user] focuses in concentration. Your head begins to ache.</span>")
|
||||
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(usr, "<span class='notice'>You begin by introducing yourself and explaining what you're about.</span>")
|
||||
usr.visible_message("<span class='danger'>[usr]'s introduces himself and explains his plans.</span>")
|
||||
to_chat(user, "<span class='notice'>You begin by introducing yourself and explaining what you're about.</span>")
|
||||
user.visible_message("<span class='danger'>[user]'s introduces himself and explains his plans.</span>")
|
||||
if(2)
|
||||
to_chat(usr, "<span class='notice'>You begin the recruitment of [target].</span>")
|
||||
usr.visible_message("<span class='danger'>[usr] leans over towards [target], whispering excitedly as he gives a speech.</span>")
|
||||
to_chat(target, "<span class='danger'>You feel yourself agreeing with [usr], and a surge of loyalty begins building.</span>")
|
||||
to_chat(user, "<span class='notice'>You begin the recruitment of [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] leans over towards [target], whispering excitedly as he gives a speech.</span>")
|
||||
to_chat(target, "<span class='danger'>You feel yourself agreeing with [user], and a surge of loyalty begins building.</span>")
|
||||
target.Weaken(12)
|
||||
sleep(20)
|
||||
if(isloyal(target))
|
||||
to_chat(usr, "<span class='notice'>They are enslaved by Nanotrasen. You feel their interest in your cause wane and disappear.</span>")
|
||||
usr.visible_message("<span class='danger'>[usr] stops talking for a moment, then moves back away from [target].</span>")
|
||||
to_chat(user, "<span class='notice'>They are enslaved by Nanotrasen. You feel their interest in your cause wane and disappear.</span>")
|
||||
user.visible_message("<span class='danger'>[user] stops talking for a moment, then moves back away from [target].</span>")
|
||||
to_chat(target, "<span class='danger'>Your mindshield implant activates, protecting you from conversion.</span>")
|
||||
return
|
||||
if(3)
|
||||
to_chat(usr, "<span class='notice'>You begin filling out the application form with [target].</span>")
|
||||
usr.visible_message("<span class='danger'>[usr] pulls out a pen and paper and begins filling an application form with [target].</span>")
|
||||
to_chat(target, "<span class='danger'>You are being convinced by [usr] to fill out an application form to become a henchman.</span>")//Ow the edge
|
||||
to_chat(user, "<span class='notice'>You begin filling out the application form with [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] pulls out a pen and paper and begins filling an application form with [target].</span>")
|
||||
to_chat(target, "<span class='danger'>You are being convinced by [user] to fill out an application form to become a henchman.</span>")//Ow the edge
|
||||
|
||||
if(!do_mob(usr, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant
|
||||
to_chat(usr, "<span class='danger'>The enrollment process has been interrupted - you have lost the attention of [target].</span>")
|
||||
to_chat(target, "<span class='warning'>You move away and are no longer under the charm of [usr]. The application form is null and void.</span>")
|
||||
if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant
|
||||
to_chat(user, "<span class='danger'>The enrollment process has been interrupted - you have lost the attention of [target].</span>")
|
||||
to_chat(target, "<span class='warning'>You move away and are no longer under the charm of [user]. The application form is null and void.</span>")
|
||||
recruiting = 0
|
||||
return
|
||||
|
||||
recruiting = 0
|
||||
to_chat(usr, "<span class='notice'>You have recruited <b>[target]</b> as your henchman!</span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You have decided to enroll as a henchman for [usr]. You are now part of the feared 'Greyshirts'.</b></span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You must follow the orders of [usr], and help him succeed in his dastardly schemes.</span>")
|
||||
to_chat(target, "<span class='deadsay'>You may not harm other Greyshirt or [usr]. However, you do not need to obey other Greyshirts.</span>")
|
||||
to_chat(user, "<span class='notice'>You have recruited <b>[target]</b> as your henchman!</span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.</b></span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You must follow the orders of [user], and help him succeed in his dastardly schemes.</span>")
|
||||
to_chat(target, "<span class='deadsay'>You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.</span>")
|
||||
ticker.mode.greyshirts += target.mind
|
||||
target.set_species("Human")
|
||||
head_organ.h_style = "Bald"
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
|
||||
/mob/living/Destroy()
|
||||
if(ranged_ability)
|
||||
ranged_ability.remove_ranged_ability(src)
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/mob/living/ghostize(can_reenter_corpse = 1)
|
||||
var/prev_client = client
|
||||
if(..())
|
||||
if(ranged_ability && prev_client)
|
||||
ranged_ability.remove_mousepointer(prev_client)
|
||||
|
||||
/mob/living/proc/OpenCraftingMenu()
|
||||
return
|
||||
|
||||
@@ -49,7 +49,12 @@
|
||||
|
||||
if(!P.nodamage)
|
||||
apply_damage(P.damage, P.damage_type, def_zone, armor)
|
||||
if(P.dismemberment)
|
||||
check_projectile_dismemberment(P, def_zone)
|
||||
return P.on_hit(src, armor, def_zone)
|
||||
|
||||
/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
|
||||
return 0
|
||||
|
||||
/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0)
|
||||
return 0 //only carbon liveforms have this proc
|
||||
@@ -86,8 +91,8 @@
|
||||
if(ismob(I.thrower))
|
||||
var/mob/M = I.thrower
|
||||
if(M)
|
||||
attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been hit with a [I], thrown by [key_name(M)]</font>")
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>Hit [key_name(src)] with a thrown [I]</font>")
|
||||
create_attack_log("<font color='orange'>Has been hit with a [I], thrown by [key_name(M)]</font>")
|
||||
M.create_attack_log("<font color='red'>Hit [key_name(src)] with a thrown [I]</font>")
|
||||
if(!istype(src,/mob/living/simple_animal/mouse))
|
||||
msg_admin_attack("[key_name_admin(src)] was hit by a [I], thrown by [key_name_admin(M)]")
|
||||
|
||||
@@ -135,8 +140,8 @@
|
||||
M.occupant_message("<span class='danger'>You hit [src].</span>")
|
||||
visible_message("<span class='danger'>[src] has been hit by [M.name].</span>", \
|
||||
"<span class='userdanger'>[src] has been hit by [M.name].</span>")
|
||||
attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked by \the [M] controlled by [key_name(M.occupant)] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
M.occupant.attack_log += text("\[[time_stamp()]\] <font color='red'>Attacked [src] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
create_attack_log("<font color='orange'>Has been attacked by \the [M] controlled by [key_name(M.occupant)] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
M.occupant.create_attack_log("<font color='red'>Attacked [src] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])</font>")
|
||||
msg_admin_attack("[key_name_admin(M.occupant)] attacked [key_name_admin(src)] with \the [M] (INTENT: [uppertext(M.occupant.a_intent)])")
|
||||
|
||||
else
|
||||
|
||||
@@ -52,3 +52,5 @@
|
||||
var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them.
|
||||
|
||||
var/gene_stability = DEFAULT_GENE_STABILITY
|
||||
|
||||
var/obj/effect/proc_holder/ranged_ability //Any ranged ability the mob has, as a click override
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
//Vents
|
||||
if(ventcrawler)
|
||||
to_chat(src, "<span class='notice'>You can ventcrawl! Use alt+click on vents to quickly travel about the station.</span>")
|
||||
|
||||
if(ranged_ability)
|
||||
ranged_ability.add_ranged_ability(src, "<span class='notice'>You currently have <b>[ranged_ability]</b> active!</span>")
|
||||
|
||||
//Should update regardless of if we can ventcrawl, since we can end up in pipes in other ways.
|
||||
update_pipe_vision()
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/mob/living/Logout()
|
||||
if(ranged_ability && client)
|
||||
ranged_ability.remove_mousepointer(client)
|
||||
..()
|
||||
if(mind)
|
||||
if(!key) //key and mind have become seperated. I believe this is for when a staff member aghosts.
|
||||
|
||||
@@ -14,6 +14,7 @@ var/list/department_radio_keys = list(
|
||||
":u" = "Supply", "#u" = "Supply", ".u" = "Supply",
|
||||
":z" = "Service", "#z" = "Service", ".z" = "Service",
|
||||
":p" = "AI Private", "#p" = "AI Private", ".p" = "AI Private",
|
||||
":x" = "cords", "#x" = "cords", ".x" = "cords",
|
||||
|
||||
":R" = "right ear", "#R" = "right ear", ".R" = "right ear",
|
||||
":L" = "left ear", "#L" = "left ear", ".L" = "left ear",
|
||||
@@ -30,7 +31,8 @@ var/list/department_radio_keys = list(
|
||||
":Z" = "Service", "#Z" = "Service", ".Z" = "Service",
|
||||
":P" = "AI Private", "#P" = "AI Private", ".P" = "AI Private",
|
||||
":-" = "Special Ops", "#-" = "Special Ops", ".-" = "Special Ops",
|
||||
":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam"
|
||||
":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam",
|
||||
":X" = "cords", "#X" = "cords", ".X" = "cords"
|
||||
)
|
||||
|
||||
|
||||
@@ -106,13 +108,14 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
return returns
|
||||
|
||||
|
||||
/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/alt_name="")
|
||||
/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/alt_name = "", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE)
|
||||
if(client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, "<span class='danger'>You cannot speak in IC (Muted).</span>")
|
||||
return
|
||||
|
||||
message = trim_strip_html_properly(message)
|
||||
if(sanitize)
|
||||
message = trim_strip_html_properly(message)
|
||||
|
||||
if(stat)
|
||||
if(stat == DEAD)
|
||||
@@ -146,6 +149,15 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
if(speaking && (speaking.flags & HIVEMIND))
|
||||
speaking.broadcast(src,trim(message))
|
||||
return 1
|
||||
|
||||
if(message_mode == "cords")
|
||||
if(iscarbon(src))
|
||||
var/mob/living/carbon/C = src
|
||||
var/obj/item/organ/internal/vocal_cords/V = C.get_int_organ(/obj/item/organ/internal/vocal_cords)
|
||||
if(V && V.can_speak_with())
|
||||
C.say(V.handle_speech(message), sanitize = FALSE, ignore_speech_problems = TRUE, ignore_atmospherics = TRUE)
|
||||
V.speak_with(message) //words come before actions
|
||||
return 1
|
||||
|
||||
verb = say_quote(message, speaking)
|
||||
|
||||
@@ -162,7 +174,7 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
|
||||
message = handle_autohiss(message, speaking)
|
||||
|
||||
if(speaking && !(speaking.flags & NO_STUTTER))
|
||||
if(!ignore_speech_problems && (speaking && !(speaking.flags & NO_STUTTER)))
|
||||
var/list/handle_s = handle_speech_problems(message, verb)
|
||||
message = handle_s[1]
|
||||
verb = handle_s[2]
|
||||
@@ -217,12 +229,13 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
//make sure the air can transmit speech - speaker's side
|
||||
var/datum/gas_mixture/environment = T.return_air()
|
||||
var/pressure = environment ? environment.return_pressure() : 0
|
||||
if(pressure < SOUND_MINIMUM_PRESSURE)
|
||||
message_range = 1
|
||||
if(!ignore_atmospherics)
|
||||
if(pressure < SOUND_MINIMUM_PRESSURE)
|
||||
message_range = 1
|
||||
|
||||
if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
|
||||
italics = 1
|
||||
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
|
||||
if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
|
||||
italics = 1
|
||||
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
|
||||
|
||||
var/list/hear = hear(message_range, T)
|
||||
var/list/hearturfs = list()
|
||||
|
||||
@@ -219,8 +219,8 @@
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
for(var/mob/O in viewers(src, null))
|
||||
O.show_message("<span class='danger'>[M]</span> [M.attacktext] [src]!", 1)
|
||||
M.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>was attacked by [M.name] ([M.ckey])</font>")
|
||||
M.create_attack_log("<font color='red'>attacked [src.name] ([src.ckey])</font>")
|
||||
src.create_attack_log("<font color='orange'>was attacked by [M.name] ([M.ckey])</font>")
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
adjustBruteLoss(damage)
|
||||
updatehealth()
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
modules += new /obj/item/weapon/gripper(src)
|
||||
modules += new /obj/item/weapon/matter_decompiler(src)
|
||||
modules += new /obj/item/device/floor_painter(src)
|
||||
|
||||
modules += new /obj/item/areaeditor/blueprints/cyborg(src)
|
||||
emag = new /obj/item/borg/stun(src)
|
||||
|
||||
for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia.
|
||||
|
||||
@@ -788,6 +788,7 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
frustration = 0
|
||||
|
||||
/mob/living/simple_animal/bot/show_inv()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/proc/show_controls(mob/M)
|
||||
users |= M
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
target_types += /obj/effect/decal/cleanable/blood/gibs/robot
|
||||
target_types += /obj/effect/decal/cleanable/crayon
|
||||
target_types += /obj/effect/decal/cleanable/liquid_fuel
|
||||
target_types += /obj/effect/decal/cleanable/molten_item
|
||||
target_types += /obj/effect/decal/cleanable/molten_object
|
||||
target_types += /obj/effect/decal/cleanable/tomato_smudge
|
||||
target_types += /obj/effect/decal/cleanable/egg_smudge
|
||||
target_types += /obj/effect/decal/cleanable/pie_smudge
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/mob/living/simple_animal/cockroach
|
||||
name = "cockroach"
|
||||
desc = "This station is just crawling with bugs."
|
||||
icon_state = "cockroach"
|
||||
icon_dead = "cockroach"
|
||||
health = 1
|
||||
maxHealth = 1
|
||||
turns_per_move = 5
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 270
|
||||
maxbodytemp = INFINITY
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
response_help = "pokes"
|
||||
response_disarm = "shoos"
|
||||
response_harm = "splats"
|
||||
density = 0
|
||||
ventcrawler = 2
|
||||
gold_core_spawnable = 2
|
||||
var/squish_chance = 50
|
||||
loot = list(/obj/effect/decal/cleanable/deadcockroach)
|
||||
del_on_death = 1
|
||||
|
||||
/mob/living/simple_animal/cockroach/death(gibbed)
|
||||
if(ticker.cinematic) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes.
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/cockroach/Crossed(var/atom/movable/AM)
|
||||
if(isliving(AM))
|
||||
var/mob/living/A = AM
|
||||
if(A.mob_size > MOB_SIZE_SMALL)
|
||||
if(prob(squish_chance))
|
||||
A.visible_message("<span class='notice'>\The [A] squashed \the [name].</span>", "<span class='notice'>You squashed \the [name].</span>")
|
||||
death()
|
||||
else
|
||||
visible_message("<span class='notice'>\The [name] avoids getting crushed.</span>")
|
||||
else if(istype(AM, /obj/structure))
|
||||
visible_message("<span class='notice'>As \the [AM] moved over \the [name], it was crushed.</span>")
|
||||
death()
|
||||
|
||||
/mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
|
||||
return
|
||||
|
||||
/obj/effect/decal/cleanable/deadcockroach
|
||||
name = "cockroach guts"
|
||||
desc = "One bug squashed. Four more will rise in its place."
|
||||
icon = 'icons/effects/blood.dmi'
|
||||
icon_state = "xfloor1"
|
||||
random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
|
||||
anchored = 1
|
||||
@@ -58,7 +58,7 @@
|
||||
dat += "<tr><td><B>Back:</B></td><td><A href='?src=[UID()];[inventory_back?"remove_inv":"add_inv"]=back'>[(inventory_back && !(inventory_back.flags&ABSTRACT)) ? inventory_back : "<font color=grey>Empty</font>"]</A></td></tr>"
|
||||
if(can_collar)
|
||||
dat += "<tr><td> </td></tr>"
|
||||
dat += "<tr><td><B>Collar:</B></td><td><A href='?src=[UID()];[collar?"remove_inv":"add_inv"]=collar'>[(collar && !(collar.flags&ABSTRACT)) ? collar : "<font color=grey>Empty</font>"]</A></td></tr>"
|
||||
dat += "<tr><td><B>Collar:</B></td><td><A href='?src=[UID()];item=[slot_collar]'>[(collar && !(collar.flags&ABSTRACT)) ? collar : "<font color=grey>Empty</font>"]</A></td></tr>"
|
||||
|
||||
dat += {"</table>
|
||||
<A href='?src=[user.UID()];mach_close=mob\ref[src]'>Close</A>
|
||||
|
||||
@@ -127,16 +127,16 @@
|
||||
set desc = "Turn your food into nutrients for plants."
|
||||
|
||||
var/list/trays = list()
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1))
|
||||
for(var/obj/machinery/hydroponics/tray in range(1))
|
||||
if(tray.nutrilevel < 10)
|
||||
trays += tray
|
||||
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays
|
||||
var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays
|
||||
|
||||
if(!src || !target || target.nutrilevel == 10) return //Sanity check.
|
||||
|
||||
src.nutrition -= ((10-target.nutrilevel)*5)
|
||||
target.nutrilevel = 10
|
||||
target.adjustNutri(10)
|
||||
src.visible_message("<span class='danger'>[src] secretes a trickle of green liquid from its tail, refilling [target]'s nutrient tray.","\red You secrete a trickle of green liquid from your tail, refilling [target]'s nutrient tray.</span>")
|
||||
|
||||
/mob/living/simple_animal/diona/verb/eat_weeds()
|
||||
@@ -145,16 +145,16 @@
|
||||
set desc = "Clean the weeds out of soil or a hydroponics tray."
|
||||
|
||||
var/list/trays = list()
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1))
|
||||
for(var/obj/machinery/hydroponics/tray in range(1))
|
||||
if(tray.weedlevel > 0)
|
||||
trays += tray
|
||||
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays
|
||||
var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays
|
||||
|
||||
if(!src || !target || target.weedlevel == 0) return //Sanity check.
|
||||
|
||||
src.nutrition += target.weedlevel * 15
|
||||
target.weedlevel = 0
|
||||
target.adjustWeeds(-10)
|
||||
src.visible_message("<span class='danger'>[src] begins rooting through [target], ripping out weeds and eating them noisily.</span>","<span class='danger'>You begin rooting through [target], ripping out weeds and eating them noisily.</span>")
|
||||
|
||||
/mob/living/simple_animal/diona/verb/evolve()
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
for(var/direction in shuffle(list(1,2,4,8,5,6,9,10)))
|
||||
var/step = get_step(src, direction)
|
||||
if(step)
|
||||
if(locate(/obj/effect/plant) in step)
|
||||
if(locate(/obj/effect/spacevine) in step)
|
||||
Move(step, get_dir(src, step))
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_action()
|
||||
@@ -47,11 +47,9 @@
|
||||
LoseTarget()
|
||||
src.visible_message("\blue [src] calms down.")
|
||||
|
||||
if(locate(/obj/effect/plant) in loc)
|
||||
var/obj/effect/plant/SV = locate(/obj/effect/plant) in loc
|
||||
qdel(SV)
|
||||
if(prob(10))
|
||||
say("Nom")
|
||||
var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc
|
||||
if(SV)
|
||||
SV.eat(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/Life()
|
||||
. = ..()
|
||||
@@ -66,11 +64,9 @@
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/Move()
|
||||
..()
|
||||
if(!stat)
|
||||
if(locate(/obj/effect/plant) in loc)
|
||||
var/obj/effect/plant/SV = locate(/obj/effect/plant) in loc
|
||||
qdel(SV)
|
||||
if(prob(10))
|
||||
say("Nom")
|
||||
var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc
|
||||
if(SV)
|
||||
SV.eat(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
|
||||
if(stat == CONSCIOUS && istype(O, /obj/item/weapon/reagent_containers/glass))
|
||||
@@ -162,6 +158,7 @@
|
||||
icon_living = "chick"
|
||||
icon_dead = "chick_dead"
|
||||
icon_gib = "chick_gib"
|
||||
gender = FEMALE
|
||||
speak = list("Cherp.","Cherp?","Chirrup.","Cheep!")
|
||||
speak_emote = list("cheeps")
|
||||
emote_hear = list("cheeps")
|
||||
@@ -205,9 +202,10 @@ var/global/chicken_count = 0
|
||||
/mob/living/simple_animal/chicken
|
||||
name = "\improper chicken"
|
||||
desc = "Hopefully the eggs are good this season."
|
||||
icon_state = "chicken"
|
||||
icon_living = "chicken"
|
||||
icon_dead = "chicken_dead"
|
||||
gender = FEMALE
|
||||
icon_state = "chicken_brown"
|
||||
icon_living = "chicken_brown"
|
||||
icon_dead = "chicken_brown_dead"
|
||||
speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.")
|
||||
speak_emote = list("clucks","croons")
|
||||
emote_hear = list("clucks")
|
||||
@@ -216,62 +214,68 @@ var/global/chicken_count = 0
|
||||
speak_chance = 2
|
||||
turns_per_move = 3
|
||||
butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat = 2)
|
||||
var/egg_type = /obj/item/weapon/reagent_containers/food/snacks/egg
|
||||
var/food_type = /obj/item/weapon/reagent_containers/food/snacks/grown/wheat
|
||||
response_help = "pets the"
|
||||
response_disarm = "gently pushes aside the"
|
||||
response_harm = "kicks the"
|
||||
attacktext = "kicks"
|
||||
health = 10
|
||||
maxHealth = 10
|
||||
health = 15
|
||||
maxHealth = 15
|
||||
ventcrawler = 2
|
||||
var/eggsleft = 0
|
||||
var/chicken_color
|
||||
var/eggsFertile = TRUE
|
||||
var/body_color
|
||||
var/icon_prefix = "chicken"
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_SMALL
|
||||
can_hide = 1
|
||||
can_collar = 1
|
||||
var/list/feedMessages = list("It clucks happily.","It clucks happily.")
|
||||
var/list/layMessage = list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")
|
||||
var/list/validColors = list("brown","black","white")
|
||||
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
|
||||
|
||||
/mob/living/simple_animal/chicken/New()
|
||||
..()
|
||||
if(!chicken_color)
|
||||
chicken_color = pick( list("brown","black","white") )
|
||||
icon_state = "chicken_[chicken_color]"
|
||||
icon_living = "chicken_[chicken_color]"
|
||||
icon_dead = "chicken_[chicken_color]_dead"
|
||||
if(!body_color)
|
||||
body_color = pick(validColors)
|
||||
icon_state = "[icon_prefix]_[body_color]"
|
||||
icon_living = "[icon_prefix]_[body_color]"
|
||||
icon_dead = "[icon_prefix]_[body_color]_dead"
|
||||
pixel_x = rand(-6, 6)
|
||||
pixel_y = rand(0, 10)
|
||||
chicken_count += 1
|
||||
|
||||
/mob/living/simple_animal/chicken/death(gibbed)
|
||||
..()
|
||||
..(gibbed)
|
||||
chicken_count -= 1
|
||||
|
||||
/mob/living/simple_animal/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
|
||||
if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown)) //feedin' dem chickens
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O
|
||||
if(G.seed.kitchen_tag == "wheat")
|
||||
if(!stat && eggsleft < 8)
|
||||
user.visible_message("\blue [user] feeds [O] to [name]! It clucks happily.","\blue You feed [O] to [name]! It clucks happily.")
|
||||
user.drop_item()
|
||||
qdel(O)
|
||||
eggsleft += rand(1, 4)
|
||||
// to_chat(world, eggsleft)
|
||||
else
|
||||
to_chat(user, "\blue [name] doesn't seem hungry!")
|
||||
/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, food_type)) //feedin' dem chickens
|
||||
if(!stat && eggsleft < 8)
|
||||
var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]"
|
||||
user.visible_message(feedmsg)
|
||||
user.drop_item()
|
||||
qdel(O)
|
||||
eggsleft += rand(1, 4)
|
||||
//world << eggsleft
|
||||
else
|
||||
to_chat(user, "\blue [name] doesn't seem interested in [O]!")
|
||||
to_chat(user, "<span class='warning'>[name] doesn't seem hungry!</span>")
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/chicken/Life()
|
||||
. = ..()
|
||||
if(. && prob(3) && eggsleft > 0)
|
||||
visible_message("[src] [pick("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")]")
|
||||
if((. && prob(3) && eggsleft > 0) && egg_type)
|
||||
visible_message("[src] [pick(layMessage)]")
|
||||
eggsleft--
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/egg/E = new(get_turf(src))
|
||||
var/obj/item/E = new egg_type(get_turf(src))
|
||||
E.pixel_x = rand(-6,6)
|
||||
E.pixel_y = rand(-6,6)
|
||||
if(chicken_count < MAX_CHICKENS && prob(10))
|
||||
processing_objects.Add(E)
|
||||
if(eggsFertile)
|
||||
if(chicken_count < MAX_CHICKENS && prob(25))
|
||||
processing_objects.Add(E)
|
||||
|
||||
/obj/item/weapon/reagent_containers/food/snacks/egg/var/amount_grown = 0
|
||||
/obj/item/weapon/reagent_containers/food/snacks/egg/process()
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#define BEE_TRAY_RECENT_VISIT 200 //How long in deciseconds until a tray can be visited by a bee again
|
||||
#define BEE_DEFAULT_COLOUR "#e5e500" //the colour we make the stripes of the bee if our reagent has no colour (or we have no reagent)
|
||||
|
||||
#define BEE_POLLINATE_YIELD_CHANCE 10
|
||||
#define BEE_POLLINATE_YIELD_CHANCE 33
|
||||
#define BEE_POLLINATE_PEST_CHANCE 33
|
||||
#define BEE_POLLINATE_POTENTCY_CHANCE 50
|
||||
#define BEE_POLLINATE_POTENCY_CHANCE 50
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees
|
||||
name = "bee"
|
||||
@@ -15,6 +15,7 @@
|
||||
icon_state = ""
|
||||
icon_living = ""
|
||||
icon = 'icons/mob/bees.dmi'
|
||||
gender = FEMALE
|
||||
speak_emote = list("buzzes")
|
||||
emote_hear = list("buzzes")
|
||||
turns_per_move = 0
|
||||
@@ -142,7 +143,7 @@
|
||||
if(beehome.bees)
|
||||
beehome.bees.Remove(src)
|
||||
beehome = null
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/worker/death(gibbed)
|
||||
if(beehome)
|
||||
@@ -158,10 +159,10 @@
|
||||
to_chat(user, "<span class='warning'>This bee is homeless!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/worker/Found(atom/A)
|
||||
if(istype(A, /obj/machinery/portable_atmospherics/hydroponics))
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/Hydro = A
|
||||
if(Hydro.seed && !Hydro.dead && !Hydro.recent_bee_visit && !Hydro.closed_system)
|
||||
wanted_objects |= /obj/machinery/portable_atmospherics/hydroponics //so we only hunt them while they're alive/seeded/not visisted and uncovered
|
||||
if(istype(A, /obj/machinery/hydroponics))
|
||||
var/obj/machinery/hydroponics/Hydro = A
|
||||
if(Hydro.myseed && !Hydro.dead && !Hydro.recent_bee_visit)
|
||||
wanted_objects |= /obj/machinery/hydroponics //so we only hunt them while they're alive/seeded/not visisted
|
||||
return 1
|
||||
..()
|
||||
|
||||
@@ -175,24 +176,24 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/worker/AttackingTarget()
|
||||
//Pollinate
|
||||
if(istype(target, /obj/machinery/portable_atmospherics/hydroponics))
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/Hydro = target
|
||||
if(istype(target, /obj/machinery/hydroponics))
|
||||
var/obj/machinery/hydroponics/Hydro = target
|
||||
pollinate(Hydro)
|
||||
else if(target == beehome)
|
||||
var/obj/structure/beebox/BB = target
|
||||
forceMove(BB)
|
||||
target = null
|
||||
wanted_objects.Remove(/obj/structure/beebox) //so we don't attack beeboxes when not going home
|
||||
wanted_objects -= /obj/structure/beebox //so we don't attack beeboxes when not going home
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/bees/worker/proc/pollinate(obj/machinery/portable_atmospherics/hydroponics/Hydro)
|
||||
if(!istype(Hydro) || !Hydro.seed || Hydro.dead || Hydro.recent_bee_visit || Hydro.closed_system)
|
||||
/mob/living/simple_animal/hostile/poison/bees/worker/proc/pollinate(obj/machinery/hydroponics/Hydro)
|
||||
if(!istype(Hydro) || !Hydro.myseed || Hydro.dead || Hydro.recent_bee_visit)
|
||||
target = null
|
||||
return
|
||||
|
||||
target = null //so we pick a new hydro tray next FindTarget(), instead of loving the same plant for eternity
|
||||
wanted_objects.Remove(/obj/machinery/portable_atmospherics/hydroponics) //so we only hunt them while they're alive/seeded/not visisted
|
||||
wanted_objects -= /obj/machinery/hydroponics //so we only hunt them while they're alive/seeded/not visisted
|
||||
Hydro.recent_bee_visit = TRUE
|
||||
spawn(BEE_TRAY_RECENT_VISIT)
|
||||
if(Hydro)
|
||||
@@ -200,23 +201,14 @@
|
||||
|
||||
var/growth = health //Health also means how many bees are in the swarm, roughly.
|
||||
//better healthier plants!
|
||||
Hydro.health += round(growth*0.5)
|
||||
Hydro.adjustHealth(growth*0.5)
|
||||
if(prob(BEE_POLLINATE_PEST_CHANCE))
|
||||
Hydro.pestlevel = max(0, --Hydro.pestlevel)
|
||||
if(prob(BEE_POLLINATE_YIELD_CHANCE)) //Yield mod is HELLA powerful, but quite rare
|
||||
if(!isnull(plant_controller.seeds[Hydro.seed.name]))
|
||||
Hydro.seed = Hydro.seed.diverge()
|
||||
else
|
||||
Hydro.seed.update_name_prefixes()
|
||||
var/seed_yield = Hydro.seed.get_trait(TRAIT_YIELD)
|
||||
Hydro.seed.set_trait(TRAIT_YIELD, seed_yield + 1, 10, 0)
|
||||
if(prob(BEE_POLLINATE_POTENTCY_CHANCE))
|
||||
if(!isnull(plant_controller.seeds[Hydro.seed.name]))
|
||||
Hydro.seed = Hydro.seed.diverge()
|
||||
else
|
||||
Hydro.seed.update_name_prefixes()
|
||||
var/seed_potency = Hydro.seed.get_trait(TRAIT_POTENCY)
|
||||
Hydro.seed.set_trait(TRAIT_POTENCY, seed_potency + 1, 200, 0)
|
||||
Hydro.adjustPests(-10)
|
||||
if(prob(BEE_POLLINATE_YIELD_CHANCE))
|
||||
Hydro.myseed.adjust_yield(1)
|
||||
Hydro.yieldmod = 2
|
||||
if(prob(BEE_POLLINATE_POTENCY_CHANCE))
|
||||
Hydro.myseed.adjust_potency(1)
|
||||
|
||||
if(beehome)
|
||||
beehome.bee_resources = min(beehome.bee_resources + growth, 100)
|
||||
@@ -234,7 +226,7 @@
|
||||
idle = max(0, --idle)
|
||||
if(idle <= BEE_IDLE_GOHOME && prob(BEE_PROB_GOHOME))
|
||||
if(!FindTarget())
|
||||
wanted_objects.Add(/obj/structure/beebox) //so we don't attack beeboxes when not going home
|
||||
wanted_objects += /obj/structure/beebox //so we don't attack beeboxes when not going home
|
||||
target = beehome
|
||||
if(!beehome) //add outselves to a beebox (of the same reagent) if we have no home
|
||||
for(var/obj/structure/beebox/BB in view(vision_range, src))
|
||||
@@ -304,6 +296,7 @@
|
||||
|
||||
/obj/item/queen_bee/Destroy()
|
||||
qdel(queen)
|
||||
queen = null
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/mob/living/simple_animal/hostile
|
||||
faction = list("hostile")
|
||||
mouse_opacity = 2 //This makes it easier to hit hostile mobs, you only need to click on their tile, and is set back to 1 when they die
|
||||
stop_automated_movement_when_pulled = 0
|
||||
environment_smash = 1 //Set to 1 to break closets,tables,racks, etc; 2 for walls; 3 for rwalls
|
||||
var/atom/target
|
||||
@@ -13,7 +12,8 @@
|
||||
var/list/friends = list()
|
||||
var/ranged_message = "fires" //Fluff text for ranged mobs
|
||||
var/ranged_cooldown = 0 //What the starting cooldown is on ranged attacks
|
||||
var/ranged_cooldown_cap = 3 //What ranged attacks, after being used are set to, to go back on cooldown, defaults to 3 life() ticks
|
||||
var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is
|
||||
var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash
|
||||
var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting
|
||||
var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat.
|
||||
var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance
|
||||
@@ -30,11 +30,33 @@
|
||||
var/attack_same = 0 //Set us to 1 to allow us to attack our own faction, or 2, to only ever attack our own faction
|
||||
|
||||
var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever)
|
||||
|
||||
//typecache of things this mob will attack in DestroySurroundings() if it has environment_smash
|
||||
var/list/environment_target_typecache = list(
|
||||
/obj/machinery/door/window,
|
||||
/obj/structure/window,
|
||||
/obj/structure/closet,
|
||||
/obj/structure/table,
|
||||
/obj/structure/grille,
|
||||
/obj/structure/girder,
|
||||
/obj/structure/rack,
|
||||
/obj/structure/barricade) //turned into a typecache in New()
|
||||
var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such
|
||||
var/list/emote_taunt = list()
|
||||
var/taunt_chance = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/New()
|
||||
..()
|
||||
if(!targets_from)
|
||||
targets_from = src
|
||||
environment_target_typecache = typecacheof(environment_target_typecache)
|
||||
|
||||
/mob/living/simple_animal/hostile/Destroy()
|
||||
targets_from = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/Life()
|
||||
. = ..()
|
||||
if(ranged)
|
||||
ranged_cooldown--
|
||||
if(!.)
|
||||
walk(src, 0)
|
||||
return 0
|
||||
@@ -58,76 +80,89 @@
|
||||
if(AIShouldSleep(possible_targets)) // we try to acquire a new one
|
||||
AIStatus = AI_IDLE // otherwise we go idle
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
|
||||
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
|
||||
FindTarget(list(user), 1)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P)
|
||||
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client)
|
||||
if(P.firer && get_dist(src, P.firer) <= aggro_vision_range)
|
||||
FindTarget(list(P.firer), 1)
|
||||
Goto(P.starting, move_to_delay, 3)
|
||||
return ..()
|
||||
|
||||
//////////////HOSTILE MOB TARGETTING AND AGGRESSION////////////
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see
|
||||
var/list/L = list()
|
||||
. = list()
|
||||
if(!search_objects)
|
||||
var/list/Mobs = hearers(vision_range, src) - src //Remove self, so we don't suicide
|
||||
L += Mobs
|
||||
for(var/obj/mecha/M in mechas_list)
|
||||
if(get_dist(M, src) <= vision_range && can_see(src, M, vision_range))
|
||||
L += M
|
||||
for(var/obj/spacepod/S in spacepods_list)
|
||||
if(get_dist(S, src) <= vision_range && can_see(src, S, vision_range))
|
||||
L += S
|
||||
var/list/Mobs = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide
|
||||
. += Mobs
|
||||
|
||||
var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod))
|
||||
|
||||
for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines))
|
||||
if(can_see(targets_from, HM, vision_range))
|
||||
. += HM
|
||||
else
|
||||
var/list/Objects = oview(vision_range, src)
|
||||
L += Objects
|
||||
return L
|
||||
var/list/Objects = oview(vision_range, targets_from)
|
||||
. += Objects
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about
|
||||
var/list/Targets = list()
|
||||
. = list()
|
||||
if(!HasTargetsList)
|
||||
possible_targets = ListTargets()
|
||||
for(var/atom/A in possible_targets)
|
||||
for(var/pos_targ in possible_targets)
|
||||
var/atom/A = pos_targ
|
||||
if(Found(A))//Just in case people want to override targetting
|
||||
Targets = list(A)
|
||||
. = list(A)
|
||||
break
|
||||
if(CanAttack(A))//Can we attack it?
|
||||
Targets += A
|
||||
. += A
|
||||
continue
|
||||
var/Target = PickTarget(Targets)
|
||||
var/Target = PickTarget(.)
|
||||
GiveTarget(Target)
|
||||
return Target //We now have a target
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/PossibleThreats()
|
||||
. = list()
|
||||
for(var/pos_targ in ListTargets())
|
||||
var/atom/A = pos_targ
|
||||
if(Found(A))
|
||||
. = list(A)
|
||||
break
|
||||
if(CanAttack(A))
|
||||
. += A
|
||||
continue
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/PickTarget(var/list/Targets)//Step 3, pick amongst the possible, attackable targets
|
||||
if(!Targets.len)//We didnt find nothin!
|
||||
return
|
||||
/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets
|
||||
if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets
|
||||
for(var/atom/A in Targets)
|
||||
var/target_dist = get_dist(src, target)
|
||||
var/possible_target_distance = get_dist(src, A)
|
||||
for(var/pos_targ in Targets)
|
||||
var/atom/A = pos_targ
|
||||
var/target_dist = get_dist(targets_from, target)
|
||||
var/possible_target_distance = get_dist(targets_from, A)
|
||||
if(target_dist < possible_target_distance)
|
||||
Targets -= A
|
||||
var/chosen_target = safepick(Targets)//Pick the remaining targets (if any) at random
|
||||
if(!Targets.len)//We didnt find nothin!
|
||||
return
|
||||
var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random
|
||||
return chosen_target
|
||||
|
||||
/mob/living/simple_animal/hostile/CanAttack(var/atom/the_target)//Can we actually attack a possible target?
|
||||
if(!the_target)
|
||||
return 0
|
||||
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
|
||||
return 0
|
||||
if(search_objects < 2)
|
||||
if(istype(the_target, /obj/mecha))
|
||||
var/obj/mecha/M = the_target
|
||||
if(M.occupant)//Just so we don't attack empty mechs
|
||||
if(CanAttack(M.occupant))
|
||||
return 1
|
||||
if(istype(the_target, /obj/spacepod))
|
||||
var/obj/spacepod/S = the_target
|
||||
if(S.pilot) //Just so we don't attack empty pods
|
||||
if(CanAttack(S.pilot))
|
||||
return 1
|
||||
if(isliving(the_target))
|
||||
var/mob/living/L = the_target
|
||||
var/faction_check = 0
|
||||
for(var/F in faction)
|
||||
if(F in L.faction)
|
||||
faction_check = 1
|
||||
break
|
||||
var/faction_check = faction_check(L)
|
||||
if(robust_searching)
|
||||
if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive == 1)
|
||||
return 0
|
||||
@@ -141,14 +176,42 @@
|
||||
if(faction_check && !attack_same)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
if(ishuman(the_target))
|
||||
var/mob/living/carbon/human/H = the_target
|
||||
if(is_type_in_list(src, H.species.ignored_by))
|
||||
return 0
|
||||
|
||||
if(istype(the_target, /obj/mecha))
|
||||
var/obj/mecha/M = the_target
|
||||
if(M.occupant)//Just so we don't attack empty mechs
|
||||
if(CanAttack(M.occupant))
|
||||
return 1
|
||||
|
||||
if(istype(the_target, /obj/spacepod))
|
||||
var/obj/spacepod/S = the_target
|
||||
if(S.pilot) //Just so we don't attack empty pods
|
||||
if(CanAttack(S.pilot))
|
||||
return 1
|
||||
|
||||
if(istype(the_target, /obj/machinery/porta_turret))
|
||||
var/obj/machinery/porta_turret/P = the_target
|
||||
if(P.faction in faction)
|
||||
return 0
|
||||
if(!P.raised) //Don't attack invincible turrets
|
||||
return 0
|
||||
if(P.stat & BROKEN) //Or turrets that are already broken
|
||||
return 0
|
||||
return 1
|
||||
if(isobj(the_target))
|
||||
if(is_type_in_list(the_target, wanted_objects))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/GiveTarget(var/new_target)//Step 4, give us our selected target
|
||||
/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target
|
||||
target = new_target
|
||||
if(target != null)
|
||||
Aggro()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/MoveToTarget(var/list/possible_targets)//Step 5, handle movement between us and our target
|
||||
@@ -157,38 +220,45 @@
|
||||
LoseTarget()
|
||||
return 0
|
||||
if(target in possible_targets)
|
||||
var/target_distance = get_dist(src,target)
|
||||
if(ranged)//We ranged? Shoot at em
|
||||
if(target_distance >= 2 && ranged_cooldown <= 0)//But make sure they're a tile away at least, and our range attack is off cooldown
|
||||
if(target.z != z)
|
||||
LoseTarget()
|
||||
return 0
|
||||
var/target_distance = get_dist(targets_from,target)
|
||||
if(ranged) //We ranged? Shoot at em
|
||||
if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
|
||||
OpenFire(target)
|
||||
if(retreat_distance != null)//If we have a retreat distance, check if we need to run from our target
|
||||
if(target_distance <= retreat_distance)//If target's closer than our retreat distance, run
|
||||
if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target
|
||||
if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run
|
||||
walk_away(src,target,retreat_distance,move_to_delay)
|
||||
else
|
||||
Goto(target,move_to_delay,minimum_distance)//Otherwise, get to our minimum distance so we chase them
|
||||
Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them
|
||||
else
|
||||
Goto(target,move_to_delay,minimum_distance)
|
||||
if(isturf(loc) && target.Adjacent(src)) //If they're next to us, attack
|
||||
AttackingTarget()
|
||||
return 1
|
||||
if(target)
|
||||
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
|
||||
AttackingTarget()
|
||||
return 1
|
||||
return 0
|
||||
if(environment_smash)
|
||||
if(target.loc != null && get_dist(src, target.loc) <= vision_range)//We can't see our target, but he's in our vision range still
|
||||
if(environment_smash >= 2)//If we're capable of smashing through walls, forget about vision completely after finding our target
|
||||
if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still
|
||||
if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them!
|
||||
OpenFire(target)
|
||||
if(environment_smash >= 2) //If we're capable of smashing through walls, forget about vision completely after finding our target
|
||||
Goto(target,move_to_delay,minimum_distance)
|
||||
FindHidden()
|
||||
return 1
|
||||
else
|
||||
if(FindHidden())
|
||||
return 1
|
||||
LostTarget()
|
||||
LoseTarget()
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Goto(var/target, var/delay, var/minimum_distance)
|
||||
/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance)
|
||||
walk_to(src, target, minimum_distance, delay)
|
||||
|
||||
/mob/living/simple_animal/hostile/adjustHealth(damage)
|
||||
..(damage)
|
||||
if(!stat && search_objects < 3)//Not unconscious, and we don't ignore mobs
|
||||
. = ..()
|
||||
if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs
|
||||
if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight
|
||||
search_objects = 0
|
||||
target = null
|
||||
@@ -203,101 +273,102 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Aggro()
|
||||
vision_range = aggro_vision_range
|
||||
if(target && emote_taunt.len && prob(taunt_chance))
|
||||
emote("me", 1, "[pick(emote_taunt)] at [target].")
|
||||
taunt_chance = max(taunt_chance-7,2)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LoseAggro()
|
||||
stop_automated_movement = 0
|
||||
vision_range = idle_vision_range
|
||||
taunt_chance = initial(taunt_chance)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LoseTarget()
|
||||
target = null
|
||||
walk(src, 0)
|
||||
LoseAggro()
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LostTarget()
|
||||
walk(src, 0)
|
||||
LoseAggro()
|
||||
|
||||
//////////////END HOSTILE MOB TARGETTING AND AGGRESSION////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/death(gibbed)
|
||||
LoseAggro()
|
||||
mouse_opacity = 1
|
||||
..()
|
||||
walk(src, 0)
|
||||
LoseTarget()
|
||||
..(gibbed)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/summon_backup(distance)
|
||||
do_alert_animation(src)
|
||||
playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1)
|
||||
for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from))
|
||||
var/list/L = M.faction&faction
|
||||
if(L.len)
|
||||
if(M.AIStatus == AI_OFF)
|
||||
return
|
||||
else
|
||||
M.Goto(src,M.move_to_delay,M.minimum_distance)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/OpenFire(var/the_target)
|
||||
var/target = the_target
|
||||
/mob/living/simple_animal/hostile/proc/OpenFire(atom/A)
|
||||
if(check_friendly_fire)
|
||||
for(var/turf/T in getline(src, target)) // Not 100% reliable but this is faster than simulating actual trajectory
|
||||
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 == target)
|
||||
if(L == src || L == A)
|
||||
continue
|
||||
if(faction_check(L) && !attack_same)
|
||||
return
|
||||
visible_message("<span class='danger'><b>[src]</b> [ranged_message] at [target]!</span>")
|
||||
visible_message("<span class='danger'><b>[src]</b> [ranged_message] at [A]!</span>")
|
||||
|
||||
if(rapid)
|
||||
spawn(1)
|
||||
Shoot(target, src.loc, src)
|
||||
if(casingtype)
|
||||
new casingtype(get_turf(src))
|
||||
Shoot(A)
|
||||
spawn(4)
|
||||
Shoot(target, src.loc, src)
|
||||
if(casingtype)
|
||||
new casingtype(get_turf(src))
|
||||
Shoot(A)
|
||||
spawn(6)
|
||||
Shoot(target, src.loc, src)
|
||||
if(casingtype)
|
||||
new casingtype(get_turf(src))
|
||||
Shoot(A)
|
||||
else
|
||||
Shoot(target, src.loc, src)
|
||||
if(casingtype)
|
||||
new casingtype
|
||||
ranged_cooldown = ranged_cooldown_cap
|
||||
return
|
||||
Shoot(A)
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Shoot(var/target, var/start, var/user, var/bullet = 0)
|
||||
if(target == start)
|
||||
/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom)
|
||||
if(targeted_atom == targets_from.loc || targeted_atom == targets_from)
|
||||
return
|
||||
|
||||
var/turf/startloc = get_turf(targets_from)
|
||||
if(casingtype)
|
||||
var/obj/item/ammo_casing/casing = new casingtype
|
||||
var/obj/item/ammo_casing/casing = new casingtype(startloc)
|
||||
playsound(src, projectilesound, 100, 1)
|
||||
casing.fire(target, src, zone_override = ran_zone())
|
||||
casing.loc = loc
|
||||
else
|
||||
var/obj/item/projectile/A = new projectiletype(loc)
|
||||
playsound(user, projectilesound, 100, 1)
|
||||
A.current = target
|
||||
A.firer = src
|
||||
A.yo = target:y - start:y
|
||||
A.xo = target:x - start:x
|
||||
if(AIStatus == AI_OFF)//Don't want mindless mobs to have their movement screwed up firing in space
|
||||
newtonian_move(get_dir(target, user))
|
||||
A.original = target
|
||||
A.fire()
|
||||
return A
|
||||
casing.fire(targeted_atom, src, zone_override = ran_zone())
|
||||
else if(projectiletype)
|
||||
var/obj/item/projectile/P = new projectiletype(startloc)
|
||||
playsound(src, projectilesound, 100, 1)
|
||||
P.current = startloc
|
||||
P.starting = startloc
|
||||
P.firer = src
|
||||
P.yo = targeted_atom.y - startloc.y
|
||||
P.xo = targeted_atom.x - startloc.x
|
||||
if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space
|
||||
newtonian_move(get_dir(targeted_atom, targets_from))
|
||||
P.original = targeted_atom
|
||||
P.fire()
|
||||
return P
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/DestroySurroundings()
|
||||
if(environment_smash)
|
||||
EscapeConfinement()
|
||||
for(var/dir in cardinal)
|
||||
var/turf/T = get_step(src, dir)
|
||||
if(istype(T, /turf/simulated/wall) || istype(T, /turf/simulated/mineral))
|
||||
if(T.Adjacent(src))
|
||||
var/turf/T = get_step(targets_from, dir)
|
||||
if(iswallturf(T) || ismineralturf(T))
|
||||
if(T.Adjacent(targets_from))
|
||||
T.attack_animal(src)
|
||||
for(var/atom/A in T)
|
||||
if(!A.Adjacent(src))
|
||||
for(var/a in T)
|
||||
var/atom/A = a
|
||||
if(!A.Adjacent(targets_from))
|
||||
continue
|
||||
if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/rack))
|
||||
if(is_type_in_typecache(A, environment_target_typecache))
|
||||
A.attack_animal(src)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/EscapeConfinement()
|
||||
if(buckled)
|
||||
buckled.attack_animal(src)
|
||||
if(!isturf(src.loc) && src.loc != null)//Did someone put us in something?
|
||||
var/atom/A = src.loc
|
||||
if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something?
|
||||
var/atom/A = get_turf(targets_from)
|
||||
A.attack_animal(src)//Bang on it till we get out
|
||||
return
|
||||
|
||||
@@ -305,12 +376,12 @@
|
||||
if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station))
|
||||
var/atom/A = target.loc
|
||||
Goto(A,move_to_delay,minimum_distance)
|
||||
if(A.Adjacent(src))
|
||||
if(A.Adjacent(targets_from))
|
||||
A.attack_animal(src)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/RangedAttack(var/atom/A, var/params) //Player firing
|
||||
if(ranged && ranged_cooldown <= 0)
|
||||
/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing
|
||||
if(ranged && ranged_cooldown <= world.time)
|
||||
target = A
|
||||
OpenFire(A)
|
||||
..()
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
#define MEDAL_PREFIX "Bubblegum"
|
||||
|
||||
/*
|
||||
|
||||
BUBBLEGUM
|
||||
|
||||
Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence.
|
||||
Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size.
|
||||
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
|
||||
|
||||
It tries to strike at its target through any bloodpools under them; if it fails to do that, it will spray blood and then attempt to warp to a bloodpool near the target.
|
||||
If it fails to warp to a target, it may summon up to 6 slaughterlings from the blood around it.
|
||||
If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood.
|
||||
At half health, it will either charge three times or warp, then charge, instead of doing a single charge.
|
||||
|
||||
When Bubblegum dies, it leaves behind a chest that can contain three things:
|
||||
1. A bottle that, when activated, drives everyone nearby into a frenzy
|
||||
2. A contract that marks for death the chosen target
|
||||
3. A spellblade that can slice off limbs at range
|
||||
|
||||
Difficulty: Hard
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum
|
||||
name = "bubblegum"
|
||||
desc = "In what passes for a heirarchy among slaughter demons, this one is king."
|
||||
health = 2500
|
||||
maxHealth = 2500
|
||||
attacktext = "rends"
|
||||
attack_sound = 'sound/misc/demon_attack1.ogg'
|
||||
icon_state = "bubblegum"
|
||||
icon_living = "bubblegum"
|
||||
icon_dead = ""
|
||||
friendly = "stares down"
|
||||
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
||||
speak_emote = list("gurgles")
|
||||
armour_penetration = 40
|
||||
melee_damage_lower = 40
|
||||
melee_damage_upper = 40
|
||||
speed = 1
|
||||
move_to_delay = 10
|
||||
ranged = 1
|
||||
pixel_x = -32
|
||||
del_on_death = 1
|
||||
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
|
||||
var/charging = 0
|
||||
medal_type = MEDAL_PREFIX
|
||||
score_type = BUBBLEGUM_SCORE
|
||||
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
|
||||
death_sound = 'sound/misc/enter_blood.ogg'
|
||||
|
||||
/obj/item/device/gps/internal/bubblegum
|
||||
icon_state = null
|
||||
gpstag = "Bloody Signal"
|
||||
desc = "You're not quite sure how a signal can be bloody."
|
||||
invisibility = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount)
|
||||
if(amount > 0 && prob(25))
|
||||
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
|
||||
if(prob(40))
|
||||
step(B, pick(cardinal))
|
||||
else
|
||||
B.dir = pick(cardinal)
|
||||
. = ..()
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum
|
||||
name = "thick blood"
|
||||
desc = "Thick, splattered blood."
|
||||
random_icon_states = list("gib3", "gib5", "gib6")
|
||||
amount = 20
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Life()
|
||||
..()
|
||||
move_to_delay = Clamp((health/maxHealth) * 10, 5, 10)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
|
||||
anger_modifier = Clamp(((maxHealth - health)/60),0,20)
|
||||
if(charging)
|
||||
return
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
var/warped = FALSE
|
||||
if(!try_bloodattack())
|
||||
spawn(0)
|
||||
blood_spray()
|
||||
warped = blood_warp()
|
||||
if(warped && prob(100 - anger_modifier))
|
||||
return
|
||||
|
||||
if(prob(90 - anger_modifier) || slaughterlings())
|
||||
if(health > maxHealth * 0.5)
|
||||
spawn(0)
|
||||
charge()
|
||||
else
|
||||
if(prob(70) || warped)
|
||||
spawn(0)
|
||||
triple_charge()
|
||||
else
|
||||
spawn(0)
|
||||
warp_charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/New()
|
||||
..()
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in mob_list)
|
||||
if(B != src)
|
||||
qdel(src) //There can be only one
|
||||
break
|
||||
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
|
||||
AddSpell(bloodspell)
|
||||
if(istype(loc, /obj/effect/dummy/slaughter))
|
||||
bloodspell.phased = 1
|
||||
internal_gps = new/obj/item/device/gps/internal/bubblegum(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon)
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
|
||||
if(!charging)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
|
||||
if(charging)
|
||||
new /obj/effect/overlay/temp/decoy/fading(loc, src)
|
||||
DestroySurroundings()
|
||||
. = ..()
|
||||
if(!stat && .)
|
||||
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
|
||||
if(charging)
|
||||
DestroySurroundings()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/warp_charge()
|
||||
blood_warp()
|
||||
charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
|
||||
charge()
|
||||
charge()
|
||||
charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge()
|
||||
var/turf/T = get_turf(target)
|
||||
if(!T || T == loc)
|
||||
return
|
||||
new /obj/effect/overlay/temp/dragon_swoop(T)
|
||||
charging = 1
|
||||
DestroySurroundings()
|
||||
walk(src, 0)
|
||||
dir = get_dir(src, T)
|
||||
var/obj/effect/overlay/temp/decoy/D = new /obj/effect/overlay/temp/decoy(loc, src)
|
||||
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
|
||||
sleep(5)
|
||||
throw_at(T, get_dist(src, T), 1, src, 0)
|
||||
charging = 0
|
||||
try_bloodattack()
|
||||
if(target)
|
||||
Goto(target, move_to_delay, minimum_distance)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
|
||||
if(charging)
|
||||
if(isturf(A) || isobj(A) && A.density)
|
||||
A.ex_act(2)
|
||||
DestroySurroundings()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/A)
|
||||
if(!charging)
|
||||
return ..()
|
||||
|
||||
else if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] slams into you!</span>")
|
||||
L.apply_damage(40, BRUTE)
|
||||
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
|
||||
shake_camera(L, 4, 3)
|
||||
shake_camera(src, 2, 3)
|
||||
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
|
||||
L.throw_at_fast(throwtarget, 3)
|
||||
|
||||
charging = 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
|
||||
var/list/targets = ListTargets()
|
||||
. = list()
|
||||
for(var/mob/living/L in targets)
|
||||
var/list/bloodpool = get_pools(get_turf(L), 0)
|
||||
if(bloodpool.len && (!faction_check(L) || L.stat == DEAD))
|
||||
. += L
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
|
||||
var/list/targets = get_mobs_on_blood()
|
||||
if(targets.len)
|
||||
spawn(0)
|
||||
bloodattack(targets, prob(50))
|
||||
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodattack(list/targets, handedness)
|
||||
var/mob/living/target_one = pick_n_take(targets)
|
||||
var/turf/target_one_turf = get_turf(target_one)
|
||||
var/mob/living/target_two
|
||||
if(targets.len)
|
||||
target_two = pick_n_take(targets)
|
||||
var/turf/target_two_turf = get_turf(target_two)
|
||||
if(target_two.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_two_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_two_turf, handedness)
|
||||
|
||||
if(target_one)
|
||||
var/list/pools = get_pools(get_turf(target_one), 0)
|
||||
if(pools.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, !handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, !handedness)
|
||||
|
||||
if(!target_two && target_one)
|
||||
var/list/poolstwo = get_pools(get_turf(target_one), 0)
|
||||
if(poolstwo.len)
|
||||
target_one_turf = get_turf(target_one)
|
||||
if(target_one_turf)
|
||||
if(target_one.stat != CONSCIOUS || prob(10))
|
||||
bloodgrab(target_one_turf, handedness)
|
||||
else
|
||||
bloodsmack(target_one_turf, handedness)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodsmack(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/rightsmack(T)
|
||||
else
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/leftsmack(T)
|
||||
sleep(2.5)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check(L))
|
||||
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
|
||||
playsound(T, attack_sound, 100, 1, -1)
|
||||
var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg")
|
||||
L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
|
||||
sleep(3)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
|
||||
if(handedness)
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/rightpaw(T)
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/rightthumb(T)
|
||||
else
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/leftpaw(T)
|
||||
new /obj/effect/overlay/temp/bubblegum_hands/leftthumb(T)
|
||||
sleep(6)
|
||||
for(var/mob/living/L in T)
|
||||
if(!faction_check(L))
|
||||
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
|
||||
playsound(T, 'sound/misc/enter_blood.ogg', 100, 1, -1)
|
||||
var/turf/targetturf = get_step(src, dir)
|
||||
L.forceMove(targetturf)
|
||||
playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, 1, -1)
|
||||
if(L.stat != CONSCIOUS)
|
||||
spawn(2)
|
||||
devour(L)
|
||||
sleep(1)
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands
|
||||
icon = 'icons/effects/bubblegum.dmi'
|
||||
duration = 9
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/rightthumb
|
||||
icon_state = "rightthumbgrab"
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/leftthumb
|
||||
icon_state = "leftthumbgrab"
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/rightpaw
|
||||
icon_state = "rightpawgrab"
|
||||
layer = MOB_LAYER - 0.1
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/leftpaw
|
||||
icon_state = "leftpawgrab"
|
||||
layer = MOB_LAYER - 0.1
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/rightsmack
|
||||
icon_state = "rightsmack"
|
||||
|
||||
/obj/effect/overlay/temp/bubblegum_hands/leftsmack
|
||||
icon_state = "leftsmack"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
|
||||
if(Adjacent(target))
|
||||
return FALSE
|
||||
var/list/can_jaunt = get_pools(get_turf(src), 1)
|
||||
if(!can_jaunt.len)
|
||||
return FALSE
|
||||
|
||||
var/list/pools = get_pools(get_turf(target), 2)
|
||||
var/list/pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
if(!pools.len)
|
||||
return FALSE
|
||||
|
||||
var/obj/effect/overlay/temp/decoy/DA = new /obj/effect/overlay/temp/decoy(loc, src)
|
||||
DA.color = "#FF0000"
|
||||
var/oldtransform = DA.transform
|
||||
DA.transform = matrix()*2
|
||||
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 5)
|
||||
sleep(5)
|
||||
qdel(DA)
|
||||
|
||||
var/obj/effect/decal/cleanable/blood/found_bloodpool
|
||||
pools = get_pools(get_turf(target), 2)
|
||||
pools_to_remove = get_pools(get_turf(target), 1)
|
||||
pools -= pools_to_remove
|
||||
if(pools.len)
|
||||
shuffle(pools)
|
||||
found_bloodpool = pick(pools)
|
||||
if(found_bloodpool)
|
||||
visible_message("<span class='danger'>[src] sinks into the blood...</span>")
|
||||
playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1)
|
||||
forceMove(get_turf(found_bloodpool))
|
||||
playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1)
|
||||
visible_message("<span class='danger'>And springs back out!</span>")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
|
||||
. = list()
|
||||
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
|
||||
if(nearby.can_bloodcrawl_in())
|
||||
. += nearby
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray()
|
||||
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>")
|
||||
var/range = 6 + round(anger_modifier * 0.4)
|
||||
var/turf/previousturf = get_turf(src)
|
||||
var/turf/J = previousturf
|
||||
var/targetdir = get_dir(src, target)
|
||||
if(target.loc == loc)
|
||||
targetdir = dir
|
||||
face_atom(target)
|
||||
new /obj/effect/decal/cleanable/blood/bubblegum(J)
|
||||
for(var/i in 1 to range)
|
||||
J = get_step(previousturf, targetdir)
|
||||
new /obj/effect/overlay/temp/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
|
||||
playsound(previousturf,'sound/effects/splat.ogg', 100, 1, -1)
|
||||
if(!J || !previousturf.CanAtmosPass(J))
|
||||
break
|
||||
new /obj/effect/decal/cleanable/blood/bubblegum(J)
|
||||
previousturf = J
|
||||
sleep(1)
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum
|
||||
amount = 0
|
||||
|
||||
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings()
|
||||
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>")
|
||||
var/max_amount = 6
|
||||
for(var/H in get_pools(get_turf(src), 1))
|
||||
if(!max_amount)
|
||||
break
|
||||
max_amount--
|
||||
var/obj/effect/decal/cleanable/blood/B = H
|
||||
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter(B.loc)
|
||||
return max_amount
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter
|
||||
name = "slaughterling"
|
||||
desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you."
|
||||
density = 0
|
||||
faction = list("mining", "boss")
|
||||
weather_immunities = list("lava","ash")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter/CanPass(atom/movable/mover, turf/target, height = 0)
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -0,0 +1,266 @@
|
||||
|
||||
#define MEDAL_PREFIX "Colossus"
|
||||
/*
|
||||
|
||||
COLOSSUS
|
||||
|
||||
The colossus spawns randomly wherever a lavaland creature is able to spawn. It is powerful, ancient, and extremely deadly.
|
||||
The colossus has a degree of sentience, proving this in speech during its attacks.
|
||||
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
|
||||
|
||||
The colossus' true danger lies in its ranged capabilities. It fires immensely damaging death bolts that penetrate all armor in a variety of ways:
|
||||
1. The colossus fires death bolts in alternating patterns: the cardinal directions and the diagonal directions.
|
||||
2. The colossus fires death bolts in a shotgun-like pattern, instantly downing anything unfortunate enough to be hit by all of them.
|
||||
3. The colossus fires a spiral of death bolts.
|
||||
At 33% health, the colossus gains an additional attack:
|
||||
4. The colossus fires two spirals of death bolts, spinning in opposite directions.
|
||||
|
||||
When a colossus dies, it leaves behind a chunk of glowing crystal known as a black box. Anything placed inside will carry over into future rounds.
|
||||
For instance, you could place a bag of holding into the black box, and then kill another colossus next round and retrieve the bag of holding from inside.
|
||||
|
||||
Difficulty: Very Hard
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus
|
||||
name = "colossus"
|
||||
desc = "A monstrous creature protected by heavy shielding."
|
||||
health = 2500
|
||||
maxHealth = 2500
|
||||
attacktext = "judges"
|
||||
attack_sound = 'sound/magic/ratvar_attack.ogg'
|
||||
icon_state = "eva"
|
||||
icon_living = "eva"
|
||||
icon_dead = "dragon_dead"
|
||||
friendly = "stares down"
|
||||
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
||||
speak_emote = list("roars")
|
||||
armour_penetration = 40
|
||||
melee_damage_lower = 40
|
||||
melee_damage_upper = 40
|
||||
speed = 1
|
||||
move_to_delay = 10
|
||||
ranged = 1
|
||||
pixel_x = -32
|
||||
del_on_death = 1
|
||||
medal_type = MEDAL_PREFIX
|
||||
score_type = COLOSSUS_SCORE
|
||||
loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/internal/vocal_cords/colossus)
|
||||
butcher_results = list(/obj/item/weapon/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)
|
||||
deathmessage = "disintegrates, leaving a glowing core in its wake."
|
||||
death_sound = 'sound/misc/demon_dies.ogg'
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L)
|
||||
visible_message("<span class='colossus'>[src] disintegrates [L]!</span>")
|
||||
L.dust()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/OpenFire()
|
||||
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
|
||||
ranged_cooldown = world.time + 120
|
||||
|
||||
if(enrage(target))
|
||||
if(move_to_delay == initial(move_to_delay))
|
||||
visible_message("<span class='colossus'>\"<b>You can't dodge.</b>\"</span>")
|
||||
ranged_cooldown = world.time + 30
|
||||
telegraph()
|
||||
dir_shots(alldirs)
|
||||
move_to_delay = 3
|
||||
return
|
||||
else
|
||||
move_to_delay = initial(move_to_delay)
|
||||
|
||||
if(prob(20+anger_modifier)) //Major attack
|
||||
telegraph()
|
||||
|
||||
if(health < maxHealth/3)
|
||||
double_spiral()
|
||||
else
|
||||
visible_message("<span class='colossus'>\"<b>Judgement.</b>\"</span>")
|
||||
spawn(0)
|
||||
spiral_shoot(rand(0, 1))
|
||||
|
||||
else if(prob(20))
|
||||
ranged_cooldown = world.time + 30
|
||||
random_shots()
|
||||
else
|
||||
if(prob(70))
|
||||
ranged_cooldown = world.time + 20
|
||||
blast()
|
||||
else
|
||||
ranged_cooldown = world.time + 40
|
||||
spawn(0)
|
||||
alternating_dir_shots()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/New()
|
||||
..()
|
||||
internal_gps = new/obj/item/device/gps/internal/colossus(src)
|
||||
|
||||
/obj/effect/overlay/temp/at_shield
|
||||
name = "anti-toolbox field"
|
||||
desc = "A shimmering forcefield protecting the colossus."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "at_shield2"
|
||||
layer = FLY_LAYER
|
||||
luminosity = 2
|
||||
duration = 8
|
||||
var/target
|
||||
|
||||
/obj/effect/overlay/temp/at_shield/New(new_loc, new_target)
|
||||
..()
|
||||
target = new_target
|
||||
spawn(0)
|
||||
orbit(target, 0, FALSE, 0, 0, FALSE, TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/bullet_act(obj/item/projectile/P)
|
||||
if(!stat)
|
||||
var/obj/effect/overlay/temp/at_shield/AT = new /obj/effect/overlay/temp/at_shield(loc, src)
|
||||
var/random_x = rand(-32, 32)
|
||||
AT.pixel_x += random_x
|
||||
|
||||
var/random_y = rand(0, 72)
|
||||
AT.pixel_y += random_y
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L)
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.martial_art && prob(H.martial_art.deflection_chance))
|
||||
. = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
|
||||
dir_shots(diagonals)
|
||||
sleep(10)
|
||||
dir_shots(cardinal)
|
||||
sleep(10)
|
||||
dir_shots(diagonals)
|
||||
sleep(10)
|
||||
dir_shots(cardinal)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral()
|
||||
visible_message("<span class='colossus'>\"<b>Die.</b>\"</span>")
|
||||
|
||||
sleep(10)
|
||||
spawn(0)
|
||||
spiral_shoot()
|
||||
spawn(0)
|
||||
spiral_shoot(1)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = 0, counter_start = 1)
|
||||
var/counter = counter_start
|
||||
var/turf/marker
|
||||
for(var/i in 1 to 80)
|
||||
switch(counter)
|
||||
if(1)
|
||||
marker = locate(x, y - 2, z)
|
||||
if(2)
|
||||
marker = locate(x - 1, y - 2, z)
|
||||
if(3)
|
||||
marker = locate(x - 2, y - 2, z)
|
||||
if(4)
|
||||
marker = locate(x - 2, y - 1, z)
|
||||
if(5)
|
||||
marker = locate(x - 2, y, z)
|
||||
if(6)
|
||||
marker = locate(x - 2, y + 1, z)
|
||||
if(7)
|
||||
marker = locate(x - 2, y + 2, z)
|
||||
if(8)
|
||||
marker = locate(x - 1, y + 2, z)
|
||||
if(9)
|
||||
marker = locate(x, y + 2, z)
|
||||
if(10)
|
||||
marker = locate(x + 1, y + 2, z)
|
||||
if(11)
|
||||
marker = locate(x + 2, y + 2, z)
|
||||
if(12)
|
||||
marker = locate(x + 2, y + 1, z)
|
||||
if(13)
|
||||
marker = locate(x + 2, y, z)
|
||||
if(14)
|
||||
marker = locate(x + 2, y - 1, z)
|
||||
if(15)
|
||||
marker = locate(x + 2, y - 2, z)
|
||||
if(16)
|
||||
marker = locate(x + 1, y - 2, z)
|
||||
|
||||
if(negative)
|
||||
counter--
|
||||
else
|
||||
counter++
|
||||
if(counter > 16)
|
||||
counter = 1
|
||||
if(counter < 1)
|
||||
counter = 16
|
||||
shoot_projectile(marker)
|
||||
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 20, 1)
|
||||
sleep(1)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker)
|
||||
if(!marker || marker == loc)
|
||||
return
|
||||
var/turf/startloc = get_turf(src)
|
||||
var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc)
|
||||
P.current = startloc
|
||||
P.starting = startloc
|
||||
P.firer = src
|
||||
P.yo = marker.y - startloc.y
|
||||
P.xo = marker.x - startloc.x
|
||||
if(target)
|
||||
P.original = target
|
||||
else
|
||||
P.original = marker
|
||||
P.fire()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots()
|
||||
var/turf/U = get_turf(src)
|
||||
playsound(U, 'sound/magic/invoke_general.ogg', 300, 1, 5)
|
||||
for(var/T in RANGE_TURFS(12, U) - U)
|
||||
if(prob(5))
|
||||
shoot_projectile(T)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast()
|
||||
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2)
|
||||
var/turf/T = get_turf(target)
|
||||
newtonian_move(get_dir(T, targets_from))
|
||||
for(var/turf/turf in range(1, T))
|
||||
shoot_projectile(turf)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs)
|
||||
if(!islist(dirs))
|
||||
dirs = alldirs.Copy()
|
||||
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2)
|
||||
for(var/d in dirs)
|
||||
var/turf/E = get_step(src, d)
|
||||
shoot_projectile(E)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph()
|
||||
for(var/mob/M in range(10,src))
|
||||
if(M.client)
|
||||
flash_color(M.client, rgb(200, 0, 0), 1)
|
||||
shake_camera(M, 4, 3)
|
||||
playsound(get_turf(src),'sound/magic/narsie_attack.ogg', 200, 1)
|
||||
|
||||
/obj/item/projectile/colossus
|
||||
name ="death bolt"
|
||||
icon_state= "chronobolt"
|
||||
damage = 25
|
||||
armour_penetration = 100
|
||||
speed = 2
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
/obj/item/projectile/colossus/on_hit(atom/target, blocked = 0)
|
||||
. = ..()
|
||||
if(isturf(target) || isobj(target))
|
||||
target.ex_act(2)
|
||||
|
||||
/obj/item/device/gps/internal/colossus
|
||||
icon_state = null
|
||||
gpstag = "Angelic Signal"
|
||||
desc = "Get in the fucking robot."
|
||||
invisibility = 100
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -0,0 +1,278 @@
|
||||
#define MEDAL_PREFIX "Drake"
|
||||
/*
|
||||
|
||||
ASH DRAKE
|
||||
|
||||
Ash drakes spawn randomly wherever a lavaland creature is able to spawn. They are the draconic guardians of the Necropolis.
|
||||
|
||||
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
|
||||
|
||||
Whenever possible, the drake will breathe fire in the four cardinal directions, igniting and heavily damaging anything caught in the blast.
|
||||
It also often causes fire to rain from the sky - many nearby turfs will flash red as a fireball crashes into them, dealing damage to anything on the turfs.
|
||||
The drake also utilizes its wings to fly into the sky and crash down onto a specified point. Anything on this point takes tremendous damage.
|
||||
- Sometimes it will chain these swooping attacks over and over, making swiftness a necessity.
|
||||
|
||||
When an ash drake dies, it leaves behind a chest that can contain four things:
|
||||
1. A spectral blade that allows its wielder to call ghosts to it, enhancing its power
|
||||
2. A lava staff that allows its wielder to create lava
|
||||
3. A spellbook and wand of fireballs
|
||||
4. A bottle of dragon's blood with several effects, including turning its imbiber into a drake themselves.
|
||||
|
||||
When butchered, they leave behind diamonds, sinew, bone, and ash drake hide. Ash drake hide can be used to create a hooded cloak that protects its wearer from ash storms.
|
||||
|
||||
Difficulty: Medium
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon
|
||||
name = "ash drake"
|
||||
desc = "Guardians of the necropolis."
|
||||
health = 2500
|
||||
maxHealth = 2500
|
||||
attacktext = "chomps"
|
||||
attack_sound = 'sound/misc/demon_attack1.ogg'
|
||||
icon_state = "dragon"
|
||||
icon_living = "dragon"
|
||||
icon_dead = "dragon_dead"
|
||||
friendly = "stares down"
|
||||
icon = 'icons/mob/lavaland/dragon.dmi'
|
||||
speak_emote = list("roars")
|
||||
armour_penetration = 40
|
||||
melee_damage_lower = 40
|
||||
melee_damage_upper = 40
|
||||
speed = 1
|
||||
move_to_delay = 10
|
||||
ranged = 1
|
||||
pixel_x = -16
|
||||
loot = list(/obj/structure/closet/crate/necropolis/dragon)
|
||||
butcher_results = list(/obj/item/weapon/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)
|
||||
var/swooping = 0
|
||||
var/swoop_cooldown = 0
|
||||
medal_type = MEDAL_PREFIX
|
||||
score_type = DRAKE_SCORE
|
||||
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
|
||||
death_sound = 'sound/misc/demon_dies.ogg'
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/New()
|
||||
..()
|
||||
internal_gps = new/obj/item/device/gps/internal/dragon(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/ex_act(severity, target)
|
||||
if(severity == 3)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount)
|
||||
if(swooping)
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/AttackingTarget()
|
||||
if(!swooping)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/DestroySurroundings()
|
||||
if(!swooping)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/Move()
|
||||
if(!swooping)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/Goto(target, delay, minimum_distance)
|
||||
if(!swooping)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/obj/effect/overlay/temp/fireball
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "fireball"
|
||||
name = "fireball"
|
||||
desc = "Get out of the way!"
|
||||
layer = 6
|
||||
randomdir = 0
|
||||
duration = 12
|
||||
pixel_z = 500
|
||||
|
||||
/obj/effect/overlay/temp/fireball/New(loc)
|
||||
..()
|
||||
animate(src, pixel_z = 0, time = 12)
|
||||
|
||||
/obj/effect/overlay/temp/target
|
||||
icon = 'icons/mob/actions.dmi'
|
||||
icon_state = "sniper_zoom"
|
||||
layer = MOB_LAYER - 0.1
|
||||
luminosity = 2
|
||||
duration = 12
|
||||
|
||||
/obj/effect/overlay/temp/dragon_swoop
|
||||
name = "certain death"
|
||||
desc = "Don't just stand there, move!"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "landing"
|
||||
layer = MOB_LAYER - 0.1
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
color = "#FF0000"
|
||||
duration = 10
|
||||
|
||||
/obj/effect/overlay/temp/target/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/overlay/temp/target/New(loc)
|
||||
..()
|
||||
spawn(0)
|
||||
fall()
|
||||
|
||||
/obj/effect/overlay/temp/target/proc/fall()
|
||||
var/turf/T = get_turf(src)
|
||||
playsound(T,'sound/magic/Fireball.ogg', 200, 1)
|
||||
new /obj/effect/overlay/temp/fireball(T)
|
||||
sleep(12)
|
||||
explosion(T, 0, 0, 1, 0, 0, 0, 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
|
||||
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
if(swooping)
|
||||
fire_rain()
|
||||
return
|
||||
|
||||
if(prob(15 + anger_modifier) && !client)
|
||||
if(health < maxHealth/2)
|
||||
spawn(0)
|
||||
swoop_attack(1)
|
||||
else
|
||||
fire_rain()
|
||||
|
||||
else if(prob(10+anger_modifier) && !client)
|
||||
if(health > maxHealth/2)
|
||||
spawn(0)
|
||||
swoop_attack(0)
|
||||
else
|
||||
spawn(0)
|
||||
triple_swoop(0)
|
||||
else
|
||||
fire_walls()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain()
|
||||
visible_message("<span class='boldwarning'>Fire rains from the sky!</span>")
|
||||
for(var/turf/turf in range(12,get_turf(src)))
|
||||
if(prob(10))
|
||||
new /obj/effect/overlay/temp/target(turf)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_walls()
|
||||
playsound(get_turf(src),'sound/magic/Fireball.ogg', 200, 1)
|
||||
|
||||
for(var/d in cardinal)
|
||||
spawn(0)
|
||||
fire_wall(d)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_wall(dir)
|
||||
var/list/hit_things = list(src)
|
||||
var/turf/E = get_edge_target_turf(src, dir)
|
||||
var/range = 10
|
||||
var/turf/previousturf = get_turf(src)
|
||||
for(var/turf/J in getline(src,E))
|
||||
if(!range || !previousturf.CanAtmosPass(J))
|
||||
break
|
||||
range--
|
||||
new /obj/effect/hotspot(J)
|
||||
J.hotspot_expose(700,50,1)
|
||||
for(var/mob/living/L in J.contents - hit_things)
|
||||
L.adjustFireLoss(20)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
|
||||
hit_things += L
|
||||
previousturf = J
|
||||
sleep(1)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/triple_swoop()
|
||||
swoop_attack()
|
||||
swoop_attack()
|
||||
swoop_attack()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(fire_rain = 0, atom/movable/manual_target)
|
||||
if(stat || swooping)
|
||||
return
|
||||
swoop_cooldown = world.time + 200
|
||||
var/atom/swoop_target
|
||||
if(manual_target)
|
||||
swoop_target = manual_target
|
||||
else
|
||||
swoop_target = target
|
||||
stop_automated_movement = TRUE
|
||||
swooping = 1
|
||||
density = 0
|
||||
icon_state = "swoop"
|
||||
visible_message("<span class='boldwarning'>[src] swoops up high!</span>")
|
||||
if(prob(50))
|
||||
animate(src, pixel_x = 500, pixel_z = 500, time = 10)
|
||||
else
|
||||
animate(src, pixel_x = -500, pixel_z = 500, time = 10)
|
||||
sleep(30)
|
||||
|
||||
var/turf/tturf
|
||||
if(fire_rain)
|
||||
fire_rain()
|
||||
|
||||
icon_state = "dragon"
|
||||
if(swoop_target && !qdeleted(swoop_target) && swoop_target.z == src.z)
|
||||
tturf = get_turf(swoop_target)
|
||||
else
|
||||
tturf = get_turf(src)
|
||||
forceMove(tturf)
|
||||
new /obj/effect/overlay/temp/dragon_swoop(tturf)
|
||||
animate(src, pixel_x = initial(pixel_x), pixel_z = 0, time = 10)
|
||||
sleep(10)
|
||||
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1)
|
||||
for(var/mob/living/L in orange(1, src))
|
||||
if(L.stat)
|
||||
visible_message("<span class='warning'>[src] slams down on [L], crushing them!</span>")
|
||||
L.gib()
|
||||
else
|
||||
L.adjustBruteLoss(75)
|
||||
if(L && !qdeleted(L)) // Some mobs are deleted on death
|
||||
var/throw_dir = get_dir(src, L)
|
||||
if(L.loc == loc)
|
||||
throw_dir = pick(alldirs)
|
||||
var/throwtarget = get_edge_target_turf(src, throw_dir)
|
||||
L.throw_at_fast(throwtarget, 3)
|
||||
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
|
||||
|
||||
for(var/mob/M in range(7, src))
|
||||
shake_camera(M, 15, 1)
|
||||
|
||||
stop_automated_movement = FALSE
|
||||
swooping = 0
|
||||
density = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A)
|
||||
if(!istype(A))
|
||||
return
|
||||
if(swoop_cooldown >= world.time)
|
||||
to_chat(src, "<span class='warning'>You need to wait 20 seconds between swoop attacks!</span>")
|
||||
return
|
||||
swoop_attack(1, A)
|
||||
|
||||
/obj/item/device/gps/internal/dragon
|
||||
icon_state = null
|
||||
gpstag = "Fiery Signal"
|
||||
desc = "Here there be dragons."
|
||||
invisibility = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser
|
||||
name = "lesser ash drake"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
faction = list("neutral")
|
||||
melee_damage_upper = 30
|
||||
melee_damage_lower = 30
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
|
||||
loot = list()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype)
|
||||
return
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -0,0 +1,611 @@
|
||||
#define MEDAL_PREFIX "Hierophant"
|
||||
/*
|
||||
|
||||
The Hierophant
|
||||
|
||||
The Hierophant spawns in its arena, an area designed to make it harder to fight than it would otherwise be.
|
||||
|
||||
The text this boss speaks is ROT4, use ROT22 to decode
|
||||
|
||||
The Hierophant's attacks are as follows, and INTENSIFY at a random chance based on Hierophant's health;
|
||||
- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time.
|
||||
INTENSITY EFFECT: Creates one of the cross blast types under itself instead of under the target.
|
||||
INTENSITY EFFECT: The created Cross Blast fires in all directions if below half health.
|
||||
- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts.
|
||||
INTENSITY EFFECT: Creates a second, slower chaser.
|
||||
- Creates an expanding AoE burst.
|
||||
- INTENSE ATTACKS:
|
||||
If target is at least 2 tiles away; Blinks to the target after a very brief delay, damaging everything near the start and end points.
|
||||
As above, but does so multiple times if below half health.
|
||||
Rapidly creates Cross Blasts under a target.
|
||||
If chasers are off cooldown, creates four high-speed chasers.
|
||||
- IF TARGET WAS STRUCK IN MELEE: Creates a 3x3 square of blasts under the target.
|
||||
|
||||
Cross Blasts and the AoE burst gain additional range as the Hierophant loses health, while Chasers gain additional speed.
|
||||
|
||||
When The Hierophant dies, it leaves behind its staff, which, while much weaker than when wielded by The Hierophant itself, is still quite effective.
|
||||
- The staff can place a teleport rune, allowing the user to teleport themself and their allies to the rune.
|
||||
|
||||
Difficulty: Hard
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant
|
||||
name = "Hierophant"
|
||||
desc = "An ancient, powerful priest-like being wielding a mighty staff."
|
||||
health = 2500
|
||||
maxHealth = 2500
|
||||
attacktext = "clubs"
|
||||
//attack_sound = 'sound/weapons/sonic_jackhammer.ogg'
|
||||
attack_sound = "swing_hit"
|
||||
icon_state = "hierophant"
|
||||
icon_living = "hierophant"
|
||||
friendly = "stares down"
|
||||
icon = 'icons/mob/lavaland/hierophant.dmi'
|
||||
faction = list("boss") //asteroid mobs? get that shit out of my beautiful square house
|
||||
speak_emote = list("preaches")
|
||||
armour_penetration = 50
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
speed = 1
|
||||
move_to_delay = 10
|
||||
ranged = 1
|
||||
pixel_x = -16
|
||||
ranged_cooldown_time = 40
|
||||
aggro_vision_range = 23
|
||||
loot = list(/obj/item/weapon/hierophant_staff)
|
||||
wander = FALSE
|
||||
var/burst_range = 3 //range on burst aoe
|
||||
var/beam_range = 5 //range on cross blast beams
|
||||
var/chaser_speed = 3 //how fast chasers are currently
|
||||
var/chaser_cooldown = 101 //base cooldown/cooldown var between spawning chasers
|
||||
var/major_attack_cooldown = 60 //base cooldown for major attacks
|
||||
var/blinking = FALSE //if we're doing something that requires us to stand still and not attack
|
||||
var/obj/effect/hierophant/spawned_rune //the rune we teleport back to
|
||||
var/timeout_time = 15 //after this many Life() ticks with no target, we return to our rune
|
||||
var/did_reset = TRUE //if we timed out, returned to our rune, and healed some
|
||||
//var/list/kill_phrases = list("Wsyvgi sj irivkc xettih. Vitemvmrk...", "Irivkc wsyvgi jsyrh. Vitemvmrk...", "Jyip jsyrh. Egxmzexmrk vitemv gcgpiw...")
|
||||
//var/list/target_phrases = list("Xevkix psgexih.", "Iriqc jsyrh.", "Eguymvih xevkix.")
|
||||
medal_type = MEDAL_PREFIX
|
||||
score_type = BIRD_SCORE
|
||||
del_on_death = TRUE
|
||||
death_sound = 'sound/magic/Repulse.ogg'
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/New()
|
||||
..()
|
||||
internal_gps = new/obj/item/device/gps/internal/hierophant(src)
|
||||
spawned_rune = new(loc)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/Life()
|
||||
. = ..()
|
||||
if(. && spawned_rune && !client)
|
||||
if(target || loc == spawned_rune.loc)
|
||||
timeout_time = initial(timeout_time)
|
||||
else
|
||||
timeout_time--
|
||||
if(timeout_time <= 0 && !did_reset)
|
||||
did_reset = TRUE
|
||||
//visible_message("<span class='hierophant'>\"Vixyvrmrk xs fewi...\"</span>")
|
||||
blink(spawned_rune)
|
||||
adjustHealth(min((health - maxHealth) * 0.5, -50)) //heal for 50% of our missing health
|
||||
wander = FALSE
|
||||
/*if(health > maxHealth * 0.9)
|
||||
visible_message("<span class='hierophant'>\"Vitemvw gsqtpixi. Stivexmrk ex qebmqyq ijjmgmirgc.\"</span>")
|
||||
else
|
||||
visible_message("<span class='hierophant'>\"Vitemvw gsqtpixi. Stivexmsrep ijjmgmirgc gsqtvsqmwih.\"</span>")*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/death()
|
||||
if(health > 0 || stat == DEAD)
|
||||
return
|
||||
else
|
||||
stat = DEAD
|
||||
blinking = TRUE //we do a fancy animation, release a huge burst(), and leave our staff.
|
||||
animate(src, alpha = 0, color = "660099", time = 20, easing = EASE_OUT)
|
||||
burst_range = 10
|
||||
//visible_message("<span class='hierophant'>\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"</span>")
|
||||
visible_message("<span class='hierophant_warning'>[src] disappears in a massive burst of magic, leaving only its staff.</span>")
|
||||
burst(get_turf(src))
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/Destroy()
|
||||
qdel(spawned_rune)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/devour(mob/living/L)
|
||||
for(var/obj/item/W in L)
|
||||
if(!L.unEquip(W))
|
||||
qdel(W)
|
||||
/*visible_message(
|
||||
"<span class='hierophant'>\"[pick(kill_phrases)]\"</span>\n<span class='hierophant_warning'>[src] annihilates [L]!</span>",
|
||||
"<span class='userdanger'>You annihilate [L], restoring your health!</span>")*/
|
||||
visible_message(
|
||||
"<span class='hierophant'>\"Caw.\"</span>\n<span class='hierophant_warning'>[src] annihilates [L]!</span>",
|
||||
"<span class='userdanger'>You annihilate [L], restoring your health!</span>")
|
||||
adjustHealth(-L.maxHealth*0.5)
|
||||
L.dust()
|
||||
|
||||
/*/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target)
|
||||
var/targets_the_same = (new_target == target)
|
||||
. = ..()
|
||||
if(. && target && !targets_the_same)
|
||||
visible_message("<span class='hierophant'>\"[pick(target_phrases)]\"</span>")*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/adjustHealth(amount)
|
||||
. = ..()
|
||||
if(src && amount > 0 && !blinking)
|
||||
wander = TRUE
|
||||
did_reset = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/AttackingTarget()
|
||||
if(!blinking)
|
||||
if(target && isliving(target))
|
||||
spawn(0)
|
||||
melee_blast(get_turf(target)) //melee attacks on living mobs produce a 3x3 blast
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/DestroySurroundings()
|
||||
if(!blinking)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/Move()
|
||||
if(!blinking)
|
||||
/*if(!stat)
|
||||
playsound(loc, 'sound/mecha/mechmove04.ogg', 150, 1, -4)*/
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/Goto(target, delay, minimum_distance)
|
||||
wander = TRUE
|
||||
if(!blinking)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/calculate_rage() //how angry we are overall
|
||||
did_reset = FALSE //oh hey we're doing SOMETHING, clearly we might need to heal if we recall
|
||||
anger_modifier = Clamp(((maxHealth - health) / 42),0,50)
|
||||
burst_range = initial(burst_range) + round(anger_modifier * 0.08)
|
||||
beam_range = initial(beam_range) + round(anger_modifier * 0.12)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire()
|
||||
calculate_rage()
|
||||
var/target_is_slow = FALSE
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(!blinking && L.stat == DEAD && get_dist(src, L) > 2)
|
||||
blink(L)
|
||||
return
|
||||
if(L.movement_delay() > 1.5)
|
||||
target_is_slow = TRUE
|
||||
chaser_speed = max(1, (3 - anger_modifier * 0.04) + target_is_slow * 0.5)
|
||||
if(blinking)
|
||||
return
|
||||
ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger.
|
||||
|
||||
if(prob(anger_modifier * 0.75)) //major ranged attack
|
||||
var/list/possibilities = list()
|
||||
var/cross_counter = 1 + round(anger_modifier * 0.12)
|
||||
if(cross_counter > 1)
|
||||
possibilities += "cross_blast_spam"
|
||||
if(get_dist(src, target) > 2)
|
||||
possibilities += "blink_spam"
|
||||
if(chaser_cooldown < world.time)
|
||||
if(prob(anger_modifier * 2))
|
||||
possibilities = list("chaser_swarm")
|
||||
else
|
||||
possibilities += "chaser_swarm"
|
||||
if(possibilities.len)
|
||||
ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) //we didn't cancel out of an attack, use the higher cooldown
|
||||
var/blink_counter = 1 + round(anger_modifier * 0.08)
|
||||
switch(pick(possibilities))
|
||||
if("blink_spam") //blink either once or multiple times.
|
||||
if(health < maxHealth * 0.5 && !target_is_slow && blink_counter > 1)
|
||||
//visible_message("<span class='hierophant'>\"Mx ampp rsx iwgeti.\"</span>")
|
||||
animate(src, color = "#660099", time = 6)
|
||||
while(health && target && blink_counter)
|
||||
if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now
|
||||
break
|
||||
blink_counter--
|
||||
blinking = FALSE
|
||||
blink(target)
|
||||
blinking = TRUE
|
||||
sleep(5)
|
||||
animate(src, color = initial(color), time = 8)
|
||||
sleep(8)
|
||||
blinking = FALSE
|
||||
else
|
||||
blink(target)
|
||||
if("cross_blast_spam") //fire a lot of cross blasts at a target.
|
||||
//visible_message("<span class='hierophant'>\"Piezi mx rsalivi xs vyr.\"</span>")
|
||||
blinking = TRUE
|
||||
animate(src, color = "#660099", time = 6)
|
||||
while(health && target && cross_counter)
|
||||
cross_counter--
|
||||
var/delay = 6
|
||||
if(prob(60))
|
||||
spawn(0)
|
||||
cardinal_blasts(target)
|
||||
else
|
||||
spawn(0)
|
||||
diagonal_blasts(target)
|
||||
delay = 5 //this one isn't so mean, so do the next one faster(if there is one)
|
||||
sleep(delay)
|
||||
animate(src, color = initial(color), time = 8)
|
||||
sleep(8)
|
||||
blinking = FALSE
|
||||
if("chaser_swarm") //fire four fucking chasers at a target and their friends.
|
||||
//visible_message("<span class='hierophant'>\"Mx gerrsx lmhi.\"</span>")
|
||||
blinking = TRUE
|
||||
animate(src, color = "#660099", time = 10)
|
||||
var/list/targets = ListTargets()
|
||||
var/list/cardinal_copy = cardinal.Copy()
|
||||
while(health && targets.len && cardinal_copy.len)
|
||||
var/mob/living/pickedtarget = pick(targets)
|
||||
if(targets.len > 4)
|
||||
pickedtarget = pick_n_take(targets)
|
||||
if(pickedtarget.stat == DEAD)
|
||||
pickedtarget = target
|
||||
var/obj/effect/overlay/temp/hierophant/chaser/C = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, pickedtarget, chaser_speed, FALSE)
|
||||
C.moving = 3
|
||||
C.moving_dir = pick_n_take(cardinal_copy)
|
||||
sleep(10)
|
||||
chaser_cooldown = world.time + initial(chaser_cooldown)
|
||||
animate(src, color = initial(color), time = 8)
|
||||
sleep(8)
|
||||
blinking = FALSE
|
||||
return
|
||||
|
||||
if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2)
|
||||
blink(target)
|
||||
|
||||
else if(prob(70 - anger_modifier)) //a cross blast of some type
|
||||
if(prob(anger_modifier)) //at us?
|
||||
if(prob(anger_modifier * 2) && health < maxHealth * 0.5) //we're super angry do it at all dirs
|
||||
spawn(0)
|
||||
alldir_blasts(src)
|
||||
else if(prob(60))
|
||||
spawn(0)
|
||||
cardinal_blasts(src)
|
||||
else
|
||||
spawn(0)
|
||||
diagonal_blasts(src)
|
||||
else //at them?
|
||||
if(prob(anger_modifier * 2) && health < maxHealth * 0.5 && !target_is_slow) //we're super angry do it at all dirs
|
||||
spawn(0)
|
||||
alldir_blasts(target)
|
||||
else if(prob(60))
|
||||
spawn(0)
|
||||
cardinal_blasts(target)
|
||||
else
|
||||
spawn(0)
|
||||
diagonal_blasts(target)
|
||||
else if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some!
|
||||
var/obj/effect/overlay/temp/hierophant/chaser/C = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, target, chaser_speed, FALSE)
|
||||
chaser_cooldown = world.time + initial(chaser_cooldown)
|
||||
if((prob(anger_modifier) || target.Adjacent(src)) && target != src)
|
||||
var/obj/effect/overlay/temp/hierophant/chaser/OC = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, target, max(1.5, 5 - anger_modifier * 0.07), FALSE)
|
||||
OC.moving = 4
|
||||
OC.moving_dir = pick(cardinal - C.moving_dir)
|
||||
else //just release a burst of power
|
||||
spawn(0)
|
||||
burst(get_turf(src))
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/diagonal_blasts(mob/victim) //fire diagonal cross blasts with a delay
|
||||
var/turf/T = get_turf(victim)
|
||||
if(!T)
|
||||
return
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph/diagonal(T, src)
|
||||
playsound(T,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(T,'sound/effects/bin_close.ogg', 200, 1)
|
||||
sleep(2)
|
||||
new /obj/effect/overlay/temp/hierophant/blast(T, src, FALSE)
|
||||
for(var/d in diagonals)
|
||||
spawn(0)
|
||||
blast_wall(T, d)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/cardinal_blasts(mob/victim) //fire cardinal cross blasts with a delay
|
||||
var/turf/T = get_turf(victim)
|
||||
if(!T)
|
||||
return
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph/cardinal(T, src)
|
||||
playsound(T,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(T,'sound/effects/bin_close.ogg', 200, 1)
|
||||
sleep(2)
|
||||
new /obj/effect/overlay/temp/hierophant/blast(T, src, FALSE)
|
||||
for(var/d in cardinal)
|
||||
spawn(0)
|
||||
blast_wall(T, d)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/alldir_blasts(mob/victim) //fire alldir cross blasts with a delay
|
||||
var/turf/T = get_turf(victim)
|
||||
if(!T)
|
||||
return
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph(T, src)
|
||||
playsound(T,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(T,'sound/effects/bin_close.ogg', 200, 1)
|
||||
sleep(2)
|
||||
new /obj/effect/overlay/temp/hierophant/blast(T, src, FALSE)
|
||||
for(var/d in alldirs)
|
||||
spawn(0)
|
||||
blast_wall(T, d)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blast_wall(turf/T, dir) //make a wall of blasts beam_range tiles long
|
||||
var/range = beam_range
|
||||
var/turf/previousturf = T
|
||||
var/turf/J = get_step(previousturf, dir)
|
||||
for(var/i in 1 to range)
|
||||
new /obj/effect/overlay/temp/hierophant/blast(J, src, FALSE)
|
||||
previousturf = J
|
||||
J = get_step(previousturf, dir)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target
|
||||
if(blinking || !victim)
|
||||
return
|
||||
var/turf/T = get_turf(victim)
|
||||
var/turf/source = get_turf(src)
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph(T, src)
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph(source, src)
|
||||
playsound(T,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(T,'sound/magic/Wand_Teleport.ogg', 200, 1)
|
||||
playsound(source,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(source,'sound/machines/AirlockOpen.ogg', 200, 1)
|
||||
blinking = TRUE
|
||||
sleep(2) //short delay before we start...
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph/teleport(T, src)
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph/teleport(source, src)
|
||||
for(var/t in RANGE_TURFS(1, T))
|
||||
var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, src, FALSE)
|
||||
B.damage = 30
|
||||
for(var/t in RANGE_TURFS(1, source))
|
||||
var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, src, FALSE)
|
||||
B.damage = 30
|
||||
animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out
|
||||
sleep(1)
|
||||
visible_message("<span class='hierophant_warning'>[src] fades out!</span>")
|
||||
density = FALSE
|
||||
sleep(2)
|
||||
forceMove(T)
|
||||
sleep(1)
|
||||
animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN
|
||||
sleep(1)
|
||||
density = TRUE
|
||||
visible_message("<span class='hierophant_warning'>[src] fades in!</span>")
|
||||
sleep(1) //at this point the blasts we made detonate
|
||||
blinking = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/melee_blast(mob/victim) //make a 3x3 blast around a target
|
||||
if(!victim)
|
||||
return
|
||||
var/turf/T = get_turf(victim)
|
||||
if(!T)
|
||||
return
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph(T, src)
|
||||
playsound(T,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(T,'sound/effects/bin_close.ogg', 200, 1)
|
||||
sleep(2)
|
||||
for(var/t in RANGE_TURFS(1, T))
|
||||
new /obj/effect/overlay/temp/hierophant/blast(t, src, FALSE)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/burst(turf/original) //release a wave of blasts
|
||||
playsound(original,'sound/magic/blink.ogg', 200, 1)
|
||||
//playsound(original,'sound/machines/AirlockOpen.ogg', 200, 1)
|
||||
var/last_dist = 0
|
||||
for(var/t in spiral_range_turfs(burst_range, original))
|
||||
var/turf/T = t
|
||||
if(!T)
|
||||
continue
|
||||
var/dist = get_dist(original, T)
|
||||
if(dist > last_dist)
|
||||
last_dist = dist
|
||||
sleep(1 + (burst_range - last_dist) * 0.5) //gets faster as it gets further out
|
||||
new /obj/effect/overlay/temp/hierophant/blast(T, src, FALSE)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/hierophant/AltClickOn(atom/A) //player control handler(don't give this to a player holy fuck)
|
||||
if(!istype(A) || get_dist(A, src) <= 2)
|
||||
return
|
||||
blink(A)
|
||||
|
||||
//Hierophant overlays
|
||||
/obj/effect/overlay/temp/hierophant
|
||||
layer = MOB_LAYER - 0.1
|
||||
var/mob/living/caster //who made this, anyway
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/New(loc, new_caster)
|
||||
..()
|
||||
if(new_caster)
|
||||
caster = new_caster
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/chaser //a hierophant's chaser. follows target around, moving and producing a blast every speed deciseconds.
|
||||
duration = 98
|
||||
var/mob/living/target //what it's following
|
||||
var/turf/targetturf //what turf the target is actually on
|
||||
var/moving_dir //what dir it's moving in
|
||||
var/previous_moving_dir //what dir it was moving in before that
|
||||
var/more_previouser_moving_dir //what dir it was moving in before THAT
|
||||
var/moving = 0 //how many steps to move before recalculating
|
||||
var/standard_moving_before_recalc = 4 //how many times we step before recalculating normally
|
||||
var/tiles_per_step = 1 //how many tiles we move each step
|
||||
var/speed = 3 //how many deciseconds between each step
|
||||
var/currently_seeking = FALSE
|
||||
var/friendly_fire_check = FALSE //if blasts produced apply friendly fire
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/chaser/New(loc, new_caster, new_target, new_speed, is_friendly_fire)
|
||||
..()
|
||||
target = new_target
|
||||
friendly_fire_check = is_friendly_fire
|
||||
if(new_speed)
|
||||
speed = new_speed
|
||||
spawn(0)
|
||||
seek_target()
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/chaser/proc/get_target_dir()
|
||||
. = get_cardinal_dir(src, targetturf)
|
||||
if((. != previous_moving_dir && . == more_previouser_moving_dir) || . == 0) //we're alternating, recalculate
|
||||
var/list/cardinal_copy = cardinal.Copy()
|
||||
cardinal_copy -= more_previouser_moving_dir
|
||||
. = pick(cardinal_copy)
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/chaser/proc/seek_target()
|
||||
if(!currently_seeking)
|
||||
currently_seeking = TRUE
|
||||
targetturf = get_turf(target)
|
||||
while(target && src && !qdeleted(src) && currently_seeking && x && y && targetturf) //can this target actually be sook out
|
||||
if(!moving) //we're out of tiles to move, find more and where the target is!
|
||||
more_previouser_moving_dir = previous_moving_dir
|
||||
previous_moving_dir = moving_dir
|
||||
moving_dir = get_target_dir()
|
||||
var/standard_target_dir = get_cardinal_dir(src, targetturf)
|
||||
if((standard_target_dir != previous_moving_dir && standard_target_dir == more_previouser_moving_dir) || standard_target_dir == 0)
|
||||
moving = 1 //we would be repeating, only move a tile before checking
|
||||
else
|
||||
moving = standard_moving_before_recalc
|
||||
if(moving) //move in the dir we're moving in right now
|
||||
var/turf/T = get_turf(src)
|
||||
for(var/i in 1 to tiles_per_step)
|
||||
var/maybe_new_turf = get_step(T, moving_dir)
|
||||
if(maybe_new_turf)
|
||||
T = maybe_new_turf
|
||||
else
|
||||
break
|
||||
forceMove(T)
|
||||
make_blast() //make a blast, too
|
||||
moving--
|
||||
sleep(speed)
|
||||
targetturf = get_turf(target)
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/chaser/proc/make_blast()
|
||||
new /obj/effect/overlay/temp/hierophant/blast(loc, caster, friendly_fire_check)
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/telegraph
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "hierophant_telegraph"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
duration = 3
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/telegraph/diagonal
|
||||
icon_state = "hierophant_telegraph_diagonal"
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/telegraph/cardinal
|
||||
icon_state = "hierophant_telegraph_cardinal"
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/telegraph/teleport
|
||||
icon_state = "hierophant_telegraph_teleport"
|
||||
duration = 9
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/blast
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "hierophant_blast"
|
||||
name = "vortex blast"
|
||||
luminosity = 1
|
||||
desc = "Get out of the way!"
|
||||
duration = 9
|
||||
var/damage = 10 //how much damage do we do?
|
||||
var/list/hit_things = list() //we hit these already, ignore them
|
||||
var/friendly_fire_check = FALSE
|
||||
var/bursting = FALSE //if we're bursting and need to hit anyone crossing us
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/blast/New(loc, new_caster, friendly_fire)
|
||||
..()
|
||||
friendly_fire_check = friendly_fire
|
||||
if(new_caster)
|
||||
hit_things += new_caster
|
||||
if(ismineralturf(loc)) //drill mineral turfs
|
||||
var/turf/simulated/mineral/M = loc
|
||||
M.gets_drilled(caster)
|
||||
spawn(0)
|
||||
blast()
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/blast/proc/blast()
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
playsound(T,'sound/magic/Blind.ogg', 125, 1, -5) //make a sound
|
||||
sleep(6) //wait a little
|
||||
bursting = TRUE
|
||||
do_damage(T) //do damage and mark us as bursting
|
||||
sleep(1.3) //slightly forgiving; the burst animation is 1.5 deciseconds
|
||||
bursting = FALSE //we no longer damage crossers
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/blast/Crossed(atom/movable/AM)
|
||||
..()
|
||||
if(bursting)
|
||||
do_damage(get_turf(src))
|
||||
|
||||
/obj/effect/overlay/temp/hierophant/blast/proc/do_damage(turf/T)
|
||||
for(var/mob/living/L in T.contents - hit_things) //find and damage mobs...
|
||||
hit_things += L
|
||||
if((friendly_fire_check && caster && caster.faction_check(L)) || L.stat == DEAD)
|
||||
continue
|
||||
if(L.client)
|
||||
flash_color(L.client, "#660099", 1)
|
||||
playsound(L,'sound/weapons/sear.ogg', 50, 1, -4)
|
||||
to_chat(L, "<span class='userdanger'>You're struck by a [name]!</span>")
|
||||
var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg")
|
||||
var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!")
|
||||
L.apply_damage(damage, BURN, limb_to_hit, armor)
|
||||
if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid))
|
||||
L.adjustBruteLoss(damage)
|
||||
add_logs(caster, L, "struck with a [name]")
|
||||
for(var/obj/mecha/M in T.contents - hit_things) //and mechs.
|
||||
hit_things += M
|
||||
if(M.occupant)
|
||||
if(friendly_fire_check && caster && caster.faction_check(M.occupant))
|
||||
continue
|
||||
to_chat(M.occupant, "<span class='userdanger'>Your [M.name] is struck by a [name]!</span>")
|
||||
playsound(M,'sound/weapons/sear.ogg', 50, 1, -4)
|
||||
M.take_damage(damage, BURN, 0, 0)
|
||||
|
||||
/obj/effect/hierophant
|
||||
name = "hierophant rune"
|
||||
desc = "A powerful magic mark allowing whomever attunes themself to it to return to it at will."
|
||||
icon = 'icons/obj/rune.dmi'
|
||||
icon_state = "hierophant"
|
||||
layer = 2.5
|
||||
anchored = TRUE
|
||||
color = "#CC00FF"
|
||||
|
||||
/obj/effect/hierophant/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/hierophant_staff))
|
||||
var/obj/item/weapon/hierophant_staff/H = I
|
||||
if(H.rune == src)
|
||||
to_chat(user, "<span class='notice'>You start removing your hierophant rune...</span>")
|
||||
H.timer = world.time + 51
|
||||
if(do_after(user, 50, target = src))
|
||||
playsound(src,'sound/magic/Blind.ogg', 200, 1, -4)
|
||||
new /obj/effect/overlay/temp/hierophant/telegraph/teleport(get_turf(src), user)
|
||||
to_chat(user, "<span class='hierophant_warning'>You touch the rune with the staff, dispelling it!</span>")
|
||||
H.rune = null
|
||||
user.update_action_buttons_icon()
|
||||
qdel(src)
|
||||
else
|
||||
H.timer = world.time
|
||||
else
|
||||
to_chat(user, "<span class='hierophant_warning'>You touch the rune with the staff, but nothing happens.</span>")
|
||||
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/device/gps/internal/hierophant
|
||||
icon_state = null
|
||||
gpstag = "Zealous Signal"
|
||||
desc = "Heed its words."
|
||||
invisibility = 100
|
||||
|
||||
/turf/simulated/indestructible/hierophant
|
||||
icon_state = "hierophant1"
|
||||
oxygen = 14
|
||||
nitrogen = 23
|
||||
temperature = 300
|
||||
desc = "A floor with a square pattern. It's faintly cool to the touch."
|
||||
|
||||
/turf/simulated/indestructible/hierophant/New()
|
||||
..()
|
||||
if(prob(50))
|
||||
icon_state = "hierophant2"
|
||||
|
||||
/turf/simulated/indestructible/riveted/hierophant
|
||||
name = "wall"
|
||||
desc = "A wall made out of smooth, cold stone."
|
||||
icon = 'icons/turf/walls/hierophant_wall.dmi'
|
||||
icon_state = "hierophant"
|
||||
smooth = SMOOTH_TRUE
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -0,0 +1,142 @@
|
||||
#define MEDAL_PREFIX "Legion"
|
||||
/*
|
||||
|
||||
LEGION
|
||||
|
||||
Legion spawns from the necropolis gate in the far north of lavaland. It is the guardian of the Necropolis and emerges from within whenever an intruder tries to enter through its gate.
|
||||
Whenever Legion emerges, everything in lavaland will receive a notice via color, audio, and text. This is because Legion is powerful enough to slaughter the entirety of lavaland with little effort.
|
||||
|
||||
It has two attack modes that it constantly rotates between.
|
||||
|
||||
In ranged mode, it will behave like a normal legion - retreating when possible and firing legion skulls at the target.
|
||||
In charge mode, it will spin and rush its target, attacking with melee whenever possible.
|
||||
|
||||
When Legion dies, it drops a staff of storms, which allows its wielder to call and disperse ash storms at will and functions as a powerful melee weapon.
|
||||
|
||||
Difficulty: Medium
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion
|
||||
name = "Legion"
|
||||
health = 800
|
||||
maxHealth = 800
|
||||
icon_state = "legion"
|
||||
icon_living = "legion"
|
||||
desc = "One of many."
|
||||
icon = 'icons/mob/lavaland/legion.dmi'
|
||||
attacktext = "chomps"
|
||||
attack_sound = 'sound/misc/demon_attack1.ogg'
|
||||
speak_emote = list("echoes")
|
||||
armour_penetration = 50
|
||||
melee_damage_lower = 25
|
||||
melee_damage_upper = 25
|
||||
speed = 2
|
||||
ranged = 1
|
||||
del_on_death = 1
|
||||
retreat_distance = 5
|
||||
minimum_distance = 5
|
||||
ranged_cooldown_time = 20
|
||||
var/size = 5
|
||||
var/charging = 0
|
||||
medal_type = MEDAL_PREFIX
|
||||
score_type = LEGION_SCORE
|
||||
pixel_y = -90
|
||||
pixel_x = -75
|
||||
loot = list(/obj/item/stack/sheet/bone = 3)
|
||||
vision_range = 13
|
||||
elimination = 1
|
||||
idle_vision_range = 13
|
||||
appearance_flags = 0
|
||||
mouse_opacity = 1
|
||||
stat_attack = 1 // Overriden from /tg/ - otherwise Legion starts chasing its minions
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/New()
|
||||
..()
|
||||
internal_gps = new/obj/item/device/gps/internal/legion(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget()
|
||||
..()
|
||||
if(ishuman(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat == UNCONSCIOUS)
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
|
||||
A.infest(L)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target)
|
||||
if(world.time >= ranged_cooldown && !charging)
|
||||
if(prob(75))
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
|
||||
A.GiveTarget(target)
|
||||
A.friends = friends
|
||||
A.faction = faction
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
else
|
||||
visible_message("<span class='warning'><b>[src] charges!</b></span>")
|
||||
SpinAnimation(speed = 20, loops = 5)
|
||||
ranged = 0
|
||||
retreat_distance = 0
|
||||
minimum_distance = 0
|
||||
speed = 0
|
||||
charging = 1
|
||||
spawn(50)
|
||||
reset_charge()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/proc/reset_charge()
|
||||
ranged = 1
|
||||
retreat_distance = 5
|
||||
minimum_distance = 5
|
||||
speed = 2
|
||||
charging = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/death()
|
||||
if(health > 0)
|
||||
return
|
||||
if(size > 1)
|
||||
adjustHealth(-maxHealth) //heal ourself to full in prep for splitting
|
||||
var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc)
|
||||
|
||||
L.maxHealth = maxHealth * 0.6
|
||||
maxHealth = L.maxHealth
|
||||
|
||||
L.health = L.maxHealth
|
||||
health = maxHealth
|
||||
|
||||
size--
|
||||
L.size = size
|
||||
|
||||
L.resize = L.size * 0.2
|
||||
transform = initial(transform)
|
||||
resize = size * 0.2
|
||||
|
||||
L.update_transform()
|
||||
update_transform()
|
||||
|
||||
L.faction = faction.Copy()
|
||||
|
||||
L.GiveTarget(target)
|
||||
|
||||
visible_message("<span class='boldannounce'>[src] splits in twain!</span>")
|
||||
else
|
||||
var/last_legion = TRUE
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/legion/other in mob_list)
|
||||
if(other != src)
|
||||
last_legion = FALSE
|
||||
break
|
||||
if(last_legion)
|
||||
loot = list(/obj/item/weapon/staff/storm)
|
||||
elimination = 0
|
||||
else if(prob(5))
|
||||
loot = list(/obj/structure/closet/crate/necropolis/tendril)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/obj/item/device/gps/internal/legion
|
||||
icon_state = null
|
||||
gpstag = "Echoing Signal"
|
||||
desc = "The message repeats."
|
||||
invisibility = 100
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -0,0 +1,230 @@
|
||||
#define MEDAL_PREFIX "Boss"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna
|
||||
name = "megafauna"
|
||||
desc = "Attack the weak point for massive damage."
|
||||
health = 1000
|
||||
maxHealth = 1000
|
||||
a_intent = "harm"
|
||||
sentience_type = SENTIENCE_BOSS
|
||||
environment_smash = 3
|
||||
luminosity = 3
|
||||
faction = list("mining", "boss")
|
||||
weather_immunities = list("lava","ash")
|
||||
flying = 1
|
||||
robust_searching = 1
|
||||
ranged_ignores_vision = TRUE
|
||||
stat_attack = 2
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = INFINITY
|
||||
aggro_vision_range = 18
|
||||
idle_vision_range = 5
|
||||
environment_target_typecache = list(
|
||||
/obj/machinery/door/window,
|
||||
/obj/structure/window,
|
||||
/obj/structure/closet,
|
||||
/obj/structure/table,
|
||||
/obj/structure/grille,
|
||||
/obj/structure/girder,
|
||||
/obj/structure/rack,
|
||||
/obj/structure/barricade,
|
||||
/obj/machinery/field,
|
||||
/obj/machinery/power/emitter)
|
||||
var/medal_type = MEDAL_PREFIX
|
||||
var/score_type = BOSS_SCORE
|
||||
var/elimination = 0
|
||||
var/anger_modifier = 0
|
||||
var/obj/item/device/gps/internal_gps
|
||||
anchored = TRUE
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
layer = MOB_LAYER + 0.5 //Looks weird with them slipping under mineral walls and cameras and shit otherwise
|
||||
mouse_opacity = 2 // Easier to click on in melee, they're giant targets anyway
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/Destroy()
|
||||
qdel(internal_gps)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/death(gibbed)
|
||||
if(health > 0)
|
||||
return
|
||||
else
|
||||
if(!admin_spawned)
|
||||
feedback_set_details("megafauna_kills","[initial(name)]")
|
||||
if(!elimination) //used so the achievment only occurs for the last legion to die.
|
||||
grant_achievement(medal_type,score_type)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/gib()
|
||||
if(health > 0)
|
||||
return
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dust()
|
||||
if(health > 0)
|
||||
return
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/AttackingTarget()
|
||||
..()
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat != DEAD)
|
||||
if(!client && ranged && ranged_cooldown <= world.time)
|
||||
OpenFire()
|
||||
else
|
||||
devour(L)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/onShuttleMove()
|
||||
var/turf/oldloc = loc
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/turf/newloc = loc
|
||||
message_admins("Megafauna [src] \
|
||||
(<A HREF='?_src_=holder;adminplayerobservefollow=[UID()]'>FLW</A>) \
|
||||
moved via shuttle from ([oldloc.x], [oldloc.y], [oldloc.z]) to \
|
||||
([newloc.x], [newloc.y], [newloc.z])")
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
|
||||
if(!L)
|
||||
return
|
||||
visible_message(
|
||||
"<span class='danger'>[src] devours [L]!</span>",
|
||||
"<span class='userdanger'>You feast on [L], restoring your health!</span>")
|
||||
adjustBruteLoss(-L.maxHealth/2)
|
||||
L.gib()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/ex_act(severity, target)
|
||||
switch(severity)
|
||||
if(1)
|
||||
adjustBruteLoss(250)
|
||||
|
||||
if(2)
|
||||
adjustBruteLoss(100)
|
||||
|
||||
if(3)
|
||||
adjustBruteLoss(50)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype,scoretype)
|
||||
|
||||
if(medal_type == "Boss") //Don't award medals if the medal type isn't set
|
||||
return
|
||||
|
||||
if(admin_spawned)
|
||||
return
|
||||
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
for(var/mob/living/L in view(7,src))
|
||||
if(L.stat)
|
||||
continue
|
||||
if(L.client)
|
||||
var/client/C = L.client
|
||||
var/suffixm = BOSS_KILL_MEDAL
|
||||
UnlockMedal("Boss [suffixm]",C)
|
||||
UnlockMedal("[medaltype] [suffixm]",C)
|
||||
SetScore(BOSS_SCORE,C,1)
|
||||
SetScore(score_type,C,1)
|
||||
|
||||
/proc/UnlockMedal(medal,client/player)
|
||||
|
||||
if(!player || !medal)
|
||||
return
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
spawn()
|
||||
var/result = world.SetMedal(medal, player, global.medal_hub, global.medal_pass)
|
||||
if(isnull(result))
|
||||
global.medals_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.ckey]")
|
||||
message_admins("Error! Failed to contact hub to award [medal] medal to [player.ckey]!")
|
||||
else if(result)
|
||||
to_chat(player.mob, "<span class='greenannounce'><B>Achievement unlocked: [medal]!</B></span>")
|
||||
|
||||
|
||||
/proc/SetScore(score,client/player,increment,force)
|
||||
|
||||
if(!score || !player)
|
||||
return
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
spawn()
|
||||
var/list/oldscore = GetScore(score,player,1)
|
||||
|
||||
if(increment)
|
||||
if(!oldscore[score])
|
||||
oldscore[score] = 1
|
||||
else
|
||||
oldscore[score] = (text2num(oldscore[score]) + 1)
|
||||
else
|
||||
oldscore[score] = force
|
||||
|
||||
var/newscoreparam = list2params(oldscore)
|
||||
|
||||
var/result = world.SetScores(player.ckey, newscoreparam, global.medal_hub, global.medal_pass)
|
||||
|
||||
if(isnull(result))
|
||||
global.medals_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.ckey]")
|
||||
message_admins("Error! Failed to contact hub to set [score] score for [player.ckey]!")
|
||||
|
||||
|
||||
/proc/GetScore(score,client/player,returnlist)
|
||||
|
||||
if(!score || !player)
|
||||
return
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
|
||||
var/scoreget = world.GetScores(player.ckey, score, global.medal_hub, global.medal_pass)
|
||||
if(isnull(scoreget))
|
||||
global.medals_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.ckey]")
|
||||
message_admins("Error! Failed to contact hub to get score: [score] for [player.ckey]!")
|
||||
return
|
||||
|
||||
var/list/scoregetlist = params2list(scoreget)
|
||||
|
||||
if(returnlist)
|
||||
return scoregetlist
|
||||
else
|
||||
return scoregetlist[score]
|
||||
|
||||
|
||||
/proc/CheckMedal(medal,client/player)
|
||||
|
||||
if(!player || !medal)
|
||||
return
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
|
||||
var/result = world.GetMedal(medal, player, global.medal_hub, global.medal_pass)
|
||||
|
||||
if(isnull(result))
|
||||
global.medals_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player:[player.ckey]")
|
||||
message_admins("Error! Failed to contact hub to get [medal] medal for [player.ckey]!")
|
||||
else if(result)
|
||||
to_chat(player.mob, "[medal] is unlocked")
|
||||
|
||||
/proc/LockMedal(medal,client/player)
|
||||
|
||||
if(!player || !medal)
|
||||
return
|
||||
if(global.medal_hub && global.medal_pass && global.medals_enabled)
|
||||
|
||||
var/result = world.ClearMedal(medal, player, global.medal_hub, global.medal_pass)
|
||||
|
||||
if(isnull(result))
|
||||
global.medals_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.ckey]")
|
||||
message_admins("Error! Failed to contact hub to clear [medal] medal for [player.ckey]!")
|
||||
else if(result)
|
||||
message_admins("Medal: [medal] removed for [player.ckey]")
|
||||
else
|
||||
message_admins("Medal: [medal] was not found for [player.ckey]. Unable to clear.")
|
||||
|
||||
|
||||
/proc/ClearScore(client/player)
|
||||
world.SetScores(player.ckey, "", global.medal_hub, global.medal_pass)
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -91,10 +91,6 @@
|
||||
..()
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/mob/living/simple_animal/hostile/mimic/crate/LostTarget()
|
||||
..()
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/mob/living/simple_animal/hostile/mimic/crate/death(gibbed)
|
||||
var/obj/structure/closet/crate/C = new(get_turf(src))
|
||||
// Put loot in crate
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub
|
||||
name = "goldgrub"
|
||||
desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Goldgrub"
|
||||
icon_living = "Goldgrub"
|
||||
icon_aggro = "Goldgrub_alert"
|
||||
icon_dead = "Goldgrub_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
vision_range = 2
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 2
|
||||
move_to_delay = 5
|
||||
friendly = "harmlessly rolls into"
|
||||
maxHealth = 45
|
||||
health = 45
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
attacktext = "barrels into"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
a_intent = I_HELP
|
||||
speak_emote = list("screeches")
|
||||
throw_message = "sinks in slowly, before being pushed out of "
|
||||
status_flags = CANPUSH
|
||||
search_objects = 1
|
||||
wanted_objects = list(/obj/item/weapon/ore/diamond, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/silver,
|
||||
/obj/item/weapon/ore/uranium)
|
||||
|
||||
var/chase_time = 100
|
||||
var/will_burrow = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/New()
|
||||
..()
|
||||
var/i = rand(1,3)
|
||||
while(i)
|
||||
loot += pick(/obj/item/weapon/ore/silver, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/uranium, /obj/item/weapon/ore/diamond)
|
||||
i--
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(new_target)
|
||||
target = new_target
|
||||
if(target != null)
|
||||
if(istype(target, /obj/item/weapon/ore) && loot.len < 10)
|
||||
visible_message("<span class='notice'>The [src.name] looks at [target.name] with hungry eyes.</span>")
|
||||
else if(isliving(target))
|
||||
Aggro()
|
||||
visible_message("<span class='danger'>The [src.name] tries to flee from [target.name]!</span>")
|
||||
retreat_distance = 10
|
||||
minimum_distance = 10
|
||||
if(will_burrow)
|
||||
spawn(chase_time)
|
||||
Burrow()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget()
|
||||
if(istype(target, /obj/item/weapon/ore))
|
||||
EatOre(target)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(var/atom/targeted_ore)
|
||||
for(var/obj/item/weapon/ore/O in targeted_ore.loc)
|
||||
if(loot.len < 10)
|
||||
loot += O.type
|
||||
qdel(O)
|
||||
visible_message("<span class='notice'>The ore was swallowed whole!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time
|
||||
if(!stat)
|
||||
visible_message("<span class='danger'>The [src.name] buries into the ground, vanishing from sight!</span>")
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(obj/item/projectile/P)
|
||||
visible_message("<span class='danger'>The [P.name] was repelled by [src.name]'s girth!</span>")
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(damage)
|
||||
idle_vision_range = 9
|
||||
. = ..()
|
||||
@@ -0,0 +1,319 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord
|
||||
name = "hivelord"
|
||||
desc = "A truly alien creature, it is a mass of unknown organic material, constantly fluctuating. When attacking, pieces of it split off and attack in tandem with the original."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "Hivelord"
|
||||
icon_living = "Hivelord"
|
||||
icon_aggro = "Hivelord_alert"
|
||||
icon_dead = "Hivelord_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
mouse_opacity = 2
|
||||
move_to_delay = 14
|
||||
ranged = 1
|
||||
vision_range = 5
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 5
|
||||
speed = 3
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
attacktext = "lashes out at"
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "falls right through the strange body of the"
|
||||
ranged_cooldown = 0
|
||||
ranged_cooldown_time = 20
|
||||
environment_smash = 0
|
||||
retreat_distance = 3
|
||||
minimum_distance = 3
|
||||
pass_flags = PASSTABLE
|
||||
loot = list(/obj/item/organ/internal/hivelord_core)
|
||||
var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target)
|
||||
if(world.time >= ranged_cooldown)
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A = new brood_type(src.loc)
|
||||
A.admin_spawned = admin_spawned
|
||||
A.GiveTarget(target)
|
||||
A.friends = friends
|
||||
A.faction = faction.Copy()
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget()
|
||||
OpenFire()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/death(gibbed)
|
||||
mouse_opacity = 1
|
||||
..(gibbed)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core
|
||||
name = "hivelord remains"
|
||||
desc = "All that remains of a hivelord, it seems to be what allows it to break pieces of itself off without being hurt... its healing properties will soon become inert if not used quickly."
|
||||
icon_state = "roro core 2"
|
||||
flags = NOBLUDGEON
|
||||
slot = "hivecore"
|
||||
force = 0
|
||||
actions_types = list(/datum/action/item_action/organ_action/use)
|
||||
var/inert = 0
|
||||
var/preserved = 0
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/New()
|
||||
..()
|
||||
addtimer(src, "inert_check", 2400)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/proc/inert_check()
|
||||
if(!owner && !preserved)
|
||||
go_inert()
|
||||
else
|
||||
preserved(implanted = 1)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/proc/preserved(implanted = 0)
|
||||
inert = FALSE
|
||||
preserved = TRUE
|
||||
update_icon()
|
||||
|
||||
if(implanted)
|
||||
feedback_add_details("hivelord_core", "[type]|implanted")
|
||||
else
|
||||
feedback_add_details("hivelord_core", "[type]|stabilizer")
|
||||
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/proc/go_inert()
|
||||
inert = TRUE
|
||||
desc = "The remains of a hivelord that have become useless, having been left alone too long after being harvested."
|
||||
feedback_add_details("hivelord_core", "[src.type]|inert")
|
||||
update_icon()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/ui_action_click()
|
||||
owner.revive()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/on_life()
|
||||
..()
|
||||
if(owner.health < config.health_threshold_crit)
|
||||
ui_action_click()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/afterattack(atom/target, mob/user, proximity_flag)
|
||||
if(proximity_flag && ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(inert)
|
||||
to_chat(user, "<span class='notice'>[src] has become inert, its healing properties are no more.</span>")
|
||||
return
|
||||
else
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
|
||||
return
|
||||
if(H != user)
|
||||
H.visible_message("[user] forces [H] to apply [src]... They quickly regenerate all injuries!")
|
||||
feedback_add_details("hivelord_core","[src.type]|used|other")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
|
||||
feedback_add_details("hivelord_core","[src.type]|used|self")
|
||||
playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
H.revive()
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/prepare_eat()
|
||||
return null
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood
|
||||
name = "hivelord brood"
|
||||
desc = "A fragment of the original Hivelord, rallying behind its original. One isn't much of a threat, but..."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "Hivelordbrood"
|
||||
icon_living = "Hivelordbrood"
|
||||
icon_aggro = "Hivelordbrood"
|
||||
icon_dead = "Hivelordbrood"
|
||||
icon_gib = "syndicate_gib"
|
||||
mouse_opacity = 2
|
||||
move_to_delay = 1
|
||||
friendly = "buzzes near"
|
||||
vision_range = 10
|
||||
speed = 3
|
||||
maxHealth = 1
|
||||
health = 1
|
||||
flying = 1
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 2
|
||||
attacktext = "slashes"
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "falls right through the strange body of the"
|
||||
environment_smash = 0
|
||||
pass_flags = PASSTABLE
|
||||
del_on_death = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New()
|
||||
..()
|
||||
addtimer(src, "death", 100)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood
|
||||
name = "blood brood"
|
||||
desc = "A living string of blood and alien materials."
|
||||
icon_state = "bloodbrood"
|
||||
icon_living = "bloodbrood"
|
||||
icon_aggro = "bloodbrood"
|
||||
attacktext = "pierces"
|
||||
color = "#C80000"
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/death()
|
||||
if(loc) // Splash the turf we are on with blood
|
||||
reagents.reaction(get_turf(src))
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/New()
|
||||
create_reagents(30)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/AttackingTarget()
|
||||
..()
|
||||
if(iscarbon(target))
|
||||
transfer_reagents(target, 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_hand(mob/living/carbon/human/M)
|
||||
if("\ref[M]" in faction)
|
||||
reabsorb_host(M)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if("\ref[M]" in faction)
|
||||
reabsorb_host(M)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/reabsorb_host(mob/living/carbon/C)
|
||||
C.visible_message("<span class='notice'>[src] is reabsorbed by [C]'s body.</span>", \
|
||||
"<span class='notice'>[src] is reabsorbed by your body.</span>")
|
||||
transfer_reagents(C)
|
||||
death()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/transfer_reagents(mob/living/carbon/C, volume = 30)
|
||||
if(!reagents.total_volume)
|
||||
return
|
||||
|
||||
volume = min(volume, reagents.total_volume)
|
||||
|
||||
var/fraction = min(volume/reagents.total_volume, 1)
|
||||
reagents.reaction(C, INGEST, fraction)
|
||||
reagents.trans_to(C, volume)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/link_host(mob/living/carbon/human/H)
|
||||
faction = list("\ref[src]", "\ref[H]") // Hostile to everyone except the host.
|
||||
H.vessel.trans_to(src, 30)
|
||||
color = mix_color_from_reagents(reagents.reagent_list)
|
||||
|
||||
// Legion
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion
|
||||
name = "legion"
|
||||
desc = "You can still see what was once a human under the shifting mass of corruption."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "legion"
|
||||
icon_living = "legion"
|
||||
icon_aggro = "legion"
|
||||
icon_dead = "legion"
|
||||
icon_gib = "syndicate_gib"
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
attacktext = "lashes out at"
|
||||
speak_emote = list("echoes")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "bounces harmlessly off of"
|
||||
loot = list(/obj/item/organ/internal/hivelord_core/legion)
|
||||
brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion
|
||||
del_on_death = 1
|
||||
stat_attack = 1
|
||||
robust_searching = 1
|
||||
var/mob/living/carbon/human/stored_mob
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed)
|
||||
visible_message("<span class='warning'>The skulls on [src] wail in anger as they flee from their dying host!</span>")
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
if(stored_mob)
|
||||
stored_mob.forceMove(get_turf(src))
|
||||
stored_mob = null
|
||||
else
|
||||
new /obj/effect/landmark/corpse/damaged(T)
|
||||
..(gibbed)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion
|
||||
name = "legion"
|
||||
desc = "One of many."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "legion_head"
|
||||
icon_living = "legion_head"
|
||||
icon_aggro = "legion_head"
|
||||
icon_dead = "legion_head"
|
||||
icon_gib = "syndicate_gib"
|
||||
friendly = "buzzes near"
|
||||
vision_range = 10
|
||||
maxHealth = 1
|
||||
health = 5
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 12
|
||||
melee_damage_upper = 12
|
||||
attacktext = "bites"
|
||||
speak_emote = list("echoes")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "is shrugged off by"
|
||||
pass_flags = PASSTABLE
|
||||
del_on_death = 1
|
||||
stat_attack = 1
|
||||
robust_searching = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/Life()
|
||||
if(isturf(loc))
|
||||
for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
|
||||
if(H.stat == UNCONSCIOUS)
|
||||
infest(H)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H)
|
||||
visible_message("<span class='warning'>[name] burrows into the flesh of [H]!</span>")
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L = new(H.loc)
|
||||
visible_message("<span class='warning'>[L] staggers to their feet!</span>")
|
||||
H.death()
|
||||
H.adjustBruteLoss(1000)
|
||||
L.stored_mob = H
|
||||
H.forceMove(L)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/legion
|
||||
name = "legion's soul"
|
||||
desc = "A strange rock that still crackles with power... its \
|
||||
healing properties will soon become inert if not used quickly."
|
||||
icon_state = "legion_soul"
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/legion/New()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/legion/update_icon()
|
||||
icon_state = inert ? "legion_soul_inert" : "legion_soul"
|
||||
overlays.Cut()
|
||||
if(!inert && !preserved)
|
||||
overlays += image(icon, "legion_soul_crackle")
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/legion/go_inert()
|
||||
. = ..()
|
||||
desc = "[src] has become inert, it crackles no more and is useless for \
|
||||
healing injuries."
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/legion/preserved(implanted = 0)
|
||||
..()
|
||||
desc = "[src] has been stabilized. It no longer crackles with power, but it's healing properties are preserved indefinitely."
|
||||
|
||||
/obj/item/weapon/legion_skull
|
||||
name = "legion's head"
|
||||
desc = "The once living, now empty eyes of the former human's skull cut deep into your soul."
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "skull"
|
||||
@@ -58,7 +58,7 @@
|
||||
projectilesound = 'sound/weapons/pierce.ogg'
|
||||
ranged = 1
|
||||
ranged_message = "stares"
|
||||
ranged_cooldown_cap = 20
|
||||
ranged_cooldown_time = 30
|
||||
throw_message = "does nothing against the hard shell of"
|
||||
vision_range = 2
|
||||
speed = 3
|
||||
@@ -71,7 +71,6 @@
|
||||
a_intent = I_HARM
|
||||
speak_emote = list("chitters")
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
ranged_cooldown_cap = 4
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 2
|
||||
turns_per_move = 5
|
||||
@@ -104,232 +103,6 @@
|
||||
if(3.0)
|
||||
adjustBruteLoss(110)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub
|
||||
name = "goldgrub"
|
||||
desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Goldgrub"
|
||||
icon_living = "Goldgrub"
|
||||
icon_aggro = "Goldgrub_alert"
|
||||
icon_dead = "Goldgrub_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
vision_range = 2
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 2
|
||||
move_to_delay = 5
|
||||
friendly = "harmlessly rolls into"
|
||||
maxHealth = 45
|
||||
health = 45
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
attacktext = "barrels into"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
a_intent = I_HELP
|
||||
speak_emote = list("screeches")
|
||||
throw_message = "sinks in slowly, before being pushed out of "
|
||||
status_flags = CANPUSH
|
||||
search_objects = 1
|
||||
wanted_objects = list(/obj/item/weapon/ore/diamond, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/silver,
|
||||
/obj/item/weapon/ore/uranium)
|
||||
|
||||
var/list/ore_types_eaten = list()
|
||||
var/alerted = 0
|
||||
var/ore_eaten = 1
|
||||
var/chase_time = 100
|
||||
var/will_burrow = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/New()
|
||||
..()
|
||||
ore_types_eaten += pick(/obj/item/weapon/ore/silver, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/uranium, /obj/item/weapon/ore/diamond)
|
||||
ore_eaten = rand(1,3)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(var/new_target)
|
||||
target = new_target
|
||||
if(target != null)
|
||||
if(istype(target, /obj/item/weapon/ore))
|
||||
visible_message("<span class='notice'>The [src.name] looks at [target.name] with hungry eyes.</span>")
|
||||
else if(isliving(target))
|
||||
Aggro()
|
||||
visible_message("<span class='danger'>The [src.name] tries to flee from [target.name]!</span>")
|
||||
retreat_distance = 10
|
||||
minimum_distance = 10
|
||||
Burrow()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget()
|
||||
if(istype(target, /obj/item/weapon/ore))
|
||||
EatOre(target)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(var/atom/targeted_ore)
|
||||
for(var/obj/item/weapon/ore/O in targeted_ore.loc)
|
||||
ore_eaten++
|
||||
if(!(O.type in ore_types_eaten))
|
||||
ore_types_eaten += O.type
|
||||
qdel(O)
|
||||
if(ore_eaten > 10)//Limit the scope of the reward you can get, or else things might get silly
|
||||
ore_eaten = 10
|
||||
visible_message("<span class='notice'>The ore was swallowed whole!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time
|
||||
if(!alerted && will_burrow)
|
||||
alerted = 1
|
||||
spawn(chase_time)
|
||||
if(alerted)
|
||||
visible_message("<span class='danger'>The [src.name] buries into the ground, vanishing from sight!</span>")
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Reward()
|
||||
if(!ore_eaten || ore_types_eaten.len == 0)
|
||||
return
|
||||
visible_message("<span class='danger'>[src] spits up the contents of its stomach before dying!</span>")
|
||||
var/counter
|
||||
for(var/R in ore_types_eaten)
|
||||
for(counter=0, counter < ore_eaten, counter++)
|
||||
new R(src.loc)
|
||||
ore_types_eaten.Cut()
|
||||
ore_eaten = 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(var/obj/item/projectile/P)
|
||||
visible_message("<span class='danger'>The [P.name] was repelled by [src.name]'s girth!</span>")
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/death(gibbed)
|
||||
alerted = 0
|
||||
Reward()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(damage)
|
||||
idle_vision_range = 9
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord
|
||||
name = "hivelord"
|
||||
desc = "A truly alien creature, it is a mass of unknown organic material, constantly fluctuating. When attacking, pieces of it split off and attack in tandem with the original."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Hivelord"
|
||||
icon_living = "Hivelord"
|
||||
icon_aggro = "Hivelord_alert"
|
||||
icon_dead = "Hivelord_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
mouse_opacity = 2
|
||||
move_to_delay = 14
|
||||
ranged = 1
|
||||
vision_range = 5
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 5
|
||||
speed = 3
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 0
|
||||
melee_damage_upper = 0
|
||||
attacktext = "lashes out at"
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "falls right through the strange body of the"
|
||||
ranged_cooldown = 0
|
||||
ranged_cooldown_cap = 0
|
||||
environment_smash = 0
|
||||
retreat_distance = 3
|
||||
minimum_distance = 3
|
||||
pass_flags = PASSTABLE
|
||||
loot = list(/obj/item/organ/internal/hivelord_core)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(var/the_target)
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood(src.loc)
|
||||
A.GiveTarget(target)
|
||||
A.friends = friends
|
||||
A.faction = faction
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget()
|
||||
OpenFire()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core
|
||||
name = "hivelord remains"
|
||||
desc = "All that remains of a hivelord, it seems to be what allows it to break pieces of itself off without being hurt... its healing properties will soon become inert if not used quickly. Try not to think about what you're eating."
|
||||
icon = 'icons/obj/food/food.dmi'
|
||||
icon_state = "boiledrorocore"
|
||||
slot = "hivecore"
|
||||
var/inert = 0
|
||||
var/preserved = 0
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/New()
|
||||
spawn(2400)
|
||||
if(!preserved)
|
||||
inert = 1
|
||||
desc = "The remains of a hivelord that have become useless, having been left alone too long after being harvested."
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/on_life()
|
||||
..()
|
||||
if(owner)
|
||||
owner.adjustBruteLoss(-1)
|
||||
owner.adjustFireLoss(-1)
|
||||
owner.adjustOxyLoss(-2)
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
var/datum/reagent/blood/B = locate() in H.vessel.reagent_list //Grab some blood
|
||||
var/blood_volume = round(H.vessel.get_reagent_amount("blood"))
|
||||
if(B && blood_volume < H.max_blood && blood_volume)
|
||||
B.volume += 2 // Fast blood regen
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/attack(mob/living/M as mob, mob/living/user as mob)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(inert)
|
||||
to_chat(user, "<span class='notice'>[src] have become inert, its healing properties are no more.</span>")
|
||||
return
|
||||
else
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
|
||||
return
|
||||
if(H != user)
|
||||
H.visible_message("<span class='notice'>[user] forces [H] to eat [src]... they quickly regenerate all injuries!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You chomp into [src], barely managing to hold it down, but feel amazingly refreshed in mere moments.</span>")
|
||||
playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
H.revive()
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/prepare_eat()
|
||||
return null
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood
|
||||
name = "hivelord brood"
|
||||
desc = "A fragment of the original Hivelord, rallying behind its original. One isn't much of a threat, but..."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Hivelordbrood"
|
||||
icon_living = "Hivelordbrood"
|
||||
icon_aggro = "Hivelordbrood"
|
||||
icon_dead = "Hivelordbrood"
|
||||
icon_gib = "syndicate_gib"
|
||||
mouse_opacity = 2
|
||||
move_to_delay = 1
|
||||
friendly = "buzzes near"
|
||||
vision_range = 10
|
||||
speed = 3
|
||||
maxHealth = 1
|
||||
health = 1
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 2
|
||||
attacktext = "slashes"
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/pierce.ogg'
|
||||
throw_message = "falls right through the strange body of the"
|
||||
environment_smash = 0
|
||||
pass_flags = PASSTABLE
|
||||
del_on_death = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New()
|
||||
..()
|
||||
spawn(100)
|
||||
death()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath
|
||||
name = "goliath"
|
||||
desc = "A massive beast that uses long tentacles to ensare its prey, threatening them is not advised under any conditions."
|
||||
@@ -344,7 +117,7 @@
|
||||
move_to_delay = 40
|
||||
ranged = 1
|
||||
ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight
|
||||
ranged_cooldown_cap = 8
|
||||
ranged_cooldown_time = 120
|
||||
friendly = "wails at"
|
||||
speak_emote = list("bellows")
|
||||
vision_range = 4
|
||||
@@ -368,7 +141,7 @@
|
||||
handle_preattack()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack()
|
||||
if(ranged_cooldown <= 2 && !pre_attack)
|
||||
if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack)
|
||||
pre_attack++
|
||||
if(!pre_attack || stat || AIStatus == AI_IDLE)
|
||||
return
|
||||
@@ -383,7 +156,7 @@
|
||||
if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen
|
||||
visible_message("<span class='warning'>The [src.name] digs its tentacles under [target.name]!</span>")
|
||||
new /obj/effect/goliath_tentacle/original(tturf)
|
||||
ranged_cooldown = ranged_cooldown_cap
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
icon_state = icon_aggro
|
||||
pre_attack = 0
|
||||
return
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
var/counter
|
||||
for(counter=0, counter<=powerlevel, counter++)
|
||||
var/obj/item/weapon/reagent_containers/food/snacks/hugemushroomslice/S = new /obj/item/weapon/reagent_containers/food/snacks/hugemushroomslice(src.loc)
|
||||
S.reagents.add_reagent("mushroomhallucinogen", powerlevel)
|
||||
S.reagents.add_reagent("psilocybin", powerlevel)
|
||||
S.reagents.add_reagent("omnizine", powerlevel)
|
||||
S.reagents.add_reagent("synaptizine", powerlevel)
|
||||
qdel(src)
|
||||
@@ -163,7 +163,7 @@
|
||||
clothes_req = 0
|
||||
range = 14
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets, mob/user = usr)
|
||||
for(var/turf/T in targets)
|
||||
for(var/obj/machinery/light/L in T)
|
||||
L.flicker()
|
||||
@@ -179,9 +179,9 @@
|
||||
clothes_req = 0
|
||||
range = 10
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets, mob/user = usr)
|
||||
for(var/mob/living/L in living_mob_list)
|
||||
if(L == usr)
|
||||
if(L == user)
|
||||
continue
|
||||
var/turf/T = get_turf(L.loc)
|
||||
if(T && T in targets)
|
||||
@@ -200,7 +200,7 @@
|
||||
range = -1
|
||||
include_user = 1
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets, mob/user = usr)
|
||||
|
||||
for(var/mob/living/target in targets)
|
||||
if(target.see_invisible == SEE_INVISIBLE_LIVING)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
|
||||
/obj/structure/alien/resin/flower_bud_enemy //inheriting basic attack/damage stuff from alien structures
|
||||
name = "flower bud"
|
||||
desc = "A large pulsating plant..."
|
||||
icon = 'icons/effects/spacevines.dmi'
|
||||
icon_state = "flower_bud"
|
||||
layer = MOB_LAYER + 0.9
|
||||
opacity = 0
|
||||
canSmoothWith = list()
|
||||
smooth = SMOOTH_FALSE
|
||||
var/growth_time = 1200
|
||||
|
||||
/obj/structure/alien/resin/flower_bud_enemy/New()
|
||||
..()
|
||||
var/list/anchors = list()
|
||||
anchors += locate(x-2,y+2,z)
|
||||
anchors += locate(x+2,y+2,z)
|
||||
anchors += locate(x-2,y-2,z)
|
||||
anchors += locate(x+2,y-2,z)
|
||||
|
||||
for(var/turf/T in anchors)
|
||||
var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
|
||||
B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow)
|
||||
addtimer(src, "bear_fruit", growth_time)
|
||||
|
||||
/obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit()
|
||||
visible_message("<span class='danger'>the plant has borne fruit!</span>")
|
||||
new /mob/living/simple_animal/hostile/venus_human_trap(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/effect/ebeam/vine
|
||||
name = "thick vine"
|
||||
mouse_opacity = 1
|
||||
desc = "A thick vine, painful to the touch."
|
||||
|
||||
|
||||
/obj/effect/ebeam/vine/Crossed(atom/movable/AM)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(!("vines" in L.faction))
|
||||
L.adjustBruteLoss(5)
|
||||
to_chat(L, "<span class='alert'>You cut yourself on the thorny vines.</span>")
|
||||
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/venus_human_trap
|
||||
name = "venus human trap"
|
||||
desc = "Now you know how the fly feels."
|
||||
icon_state = "venus_human_trap"
|
||||
layer = MOB_LAYER + 0.9
|
||||
health = 50
|
||||
maxHealth = 50
|
||||
ranged = 1
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 25
|
||||
melee_damage_upper = 25
|
||||
a_intent = I_HARM
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
unsuitable_atmos_damage = 0
|
||||
faction = list("hostile","vines","plants")
|
||||
var/list/grasping = list()
|
||||
var/max_grasps = 4
|
||||
var/grasp_chance = 20
|
||||
var/grasp_pull_chance = 85
|
||||
var/grasp_range = 4
|
||||
del_on_death = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/venus_human_trap/handle_automated_action()
|
||||
if(..())
|
||||
for(var/mob/living/L in grasping)
|
||||
if(L.stat == DEAD)
|
||||
var/datum/beam/B = grasping[L]
|
||||
if(B)
|
||||
B.End()
|
||||
grasping -= L
|
||||
|
||||
//Can attack+pull multiple times per cycle
|
||||
if(L.Adjacent(src))
|
||||
L.attack_animal(src)
|
||||
else
|
||||
if(prob(grasp_pull_chance))
|
||||
dir = get_dir(src,L) //staaaare
|
||||
step(L,get_dir(L,src)) //reel them in
|
||||
L.Weaken(3) //you can't get away now~
|
||||
|
||||
if(grasping.len < max_grasps)
|
||||
grasping:
|
||||
for(var/mob/living/L in view(grasp_range, src))
|
||||
if(L == src || faction_check(L) || (L in grasping) || L == target)
|
||||
continue
|
||||
for(var/t in getline(src,L))
|
||||
for(var/a in t)
|
||||
var/atom/A = a
|
||||
if(A.density && A != L)
|
||||
continue grasping
|
||||
if(prob(grasp_chance))
|
||||
to_chat(L, "<span class='userdanger'>\The [src] has you entangled!</span>")
|
||||
grasping[L] = Beam(L, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
|
||||
|
||||
break //only take 1 new victim per cycle
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target)
|
||||
var/dist = get_dist(src,the_target)
|
||||
Beam(the_target, "vine", time=dist*2, maxdistance=dist+2, beam_type=/obj/effect/ebeam/vine)
|
||||
the_target.attack_animal(src)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/venus_human_trap/CanAttack(atom/the_target)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(the_target in grasping)
|
||||
return 0
|
||||
@@ -301,6 +301,9 @@
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
attack_threshold_check(damage,M.melee_damage_type)
|
||||
|
||||
/mob/living/simple_animal/proc/attacked_by(obj/item/I, mob/living/user) // Handled in _onclick/click.dm
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj)
|
||||
if(!Proj)
|
||||
return
|
||||
@@ -579,21 +582,7 @@
|
||||
density = initial(density)
|
||||
update_canmove()
|
||||
|
||||
/mob/living/simple_animal/show_inv(mob/user as mob)
|
||||
user.set_machine(src)
|
||||
var/dat = {"<table>
|
||||
<tr><td><B>Left Hand:</B></td><td><A href='?src=[UID()];item=[slot_l_hand]'>[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "<font color=grey>Empty</font>"]</A></td></tr>
|
||||
<tr><td><B>Right Hand:</B></td><td><A href='?src=[UID()];item=[slot_r_hand]'>[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "<font color=grey>Empty</font>"]</A></td></tr>
|
||||
<tr><td> </td></tr>"}
|
||||
if(can_collar)
|
||||
dat += "<tr><td><B>Collar:</B></td><td><A href='?src=[UID()];[collar?"remove_inv":"add_inv"]=collar'>[(collar && !(collar.flags&ABSTRACT)) ? collar : "<font color=grey>Empty</font>"]</A></td></tr>"
|
||||
dat += {"</table>
|
||||
<A href='?src=[user.UID()];mach_close=mob\ref[src]'>Close</A>
|
||||
"}
|
||||
|
||||
var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/mob/living/simple_animal/proc/make_babies() // <3 <3 <3
|
||||
if(gender != FEMALE || stat || !scan_ready || !childtype || !simplespecies)
|
||||
@@ -652,57 +641,74 @@
|
||||
if(changed)
|
||||
animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT)
|
||||
|
||||
/mob/living/simple_animal/Topic(href, href_list)
|
||||
if(!usr.stat && usr.canmove && !usr.restrained() && in_range(src, usr))
|
||||
if(href_list["remove_inv"])
|
||||
if(!Adjacent(usr) || !(ishuman(usr) || isrobot(usr) || isalienadult(usr)))
|
||||
return
|
||||
var/remove_from = href_list["remove_inv"]
|
||||
switch(remove_from)
|
||||
if("collar")
|
||||
if(!can_collar)
|
||||
return
|
||||
if(collar)
|
||||
if(collar.flags & NODROP)
|
||||
to_chat(usr, "<span class='warning'>\The [collar] is stuck too hard to [src] for you to remove!</span>")
|
||||
return
|
||||
collar.dropped(src)
|
||||
collar.forceMove(src.loc)
|
||||
collar = null
|
||||
regenerate_icons()
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>There is nothing to remove from its [remove_from].</span>")
|
||||
return
|
||||
show_inv(usr)
|
||||
else if(href_list["add_inv"])
|
||||
if(!Adjacent(usr) || !(ishuman(usr) || isrobot(usr) || isalienadult(usr)))
|
||||
return
|
||||
/* Inventory */
|
||||
|
||||
var/add_to = href_list["add_inv"]
|
||||
switch(add_to)
|
||||
if("collar")
|
||||
if(!can_collar || collar)
|
||||
return
|
||||
var/obj/item/clothing/accessory/petcollar/C = usr.get_active_hand()
|
||||
if(!istype(C))
|
||||
usr.visible_message("[usr] rubs [src]'s neck.","<span class='notice'>You rub [src]'s neck for a moment.</span>")
|
||||
return
|
||||
usr.drop_item()
|
||||
C.forceMove(src)
|
||||
collar = C
|
||||
collar.equipped(src)
|
||||
regenerate_icons()
|
||||
to_chat(usr, "<span class='notice'>You put \the [C] around \the [src]'s neck.</span>")
|
||||
if(C.tagname)
|
||||
name = C.tagname
|
||||
real_name = C.tagname
|
||||
show_inv(usr)
|
||||
/mob/living/simple_animal/show_inv(mob/user as mob)
|
||||
if(!can_collar)
|
||||
return
|
||||
|
||||
user.set_machine(src)
|
||||
var/dat = "<table><tr><td><B>Collar:</B></td><td><A href='?src=[UID()];item=[slot_collar]'>[(collar && !(collar.flags&ABSTRACT)) ? collar : "<font color=grey>Empty</font>"]</A></td></tr></table>"
|
||||
dat += "<A href='?src=[user.UID()];mach_close=mob\ref[src]'>Close</A>"
|
||||
|
||||
var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/mob/living/simple_animal/get_item_by_slot(slot_id)
|
||||
switch(slot_id)
|
||||
if(slot_collar)
|
||||
return collar
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0)
|
||||
// . = ..() // Do not call parent. We do not want animals using their hand slots.
|
||||
switch(slot)
|
||||
if(slot_collar)
|
||||
if(collar)
|
||||
return 0
|
||||
if(!can_collar)
|
||||
return 0
|
||||
if(!istype(I, /obj/item/clothing/accessory/petcollar))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/equip_to_slot(obj/item/W, slot)
|
||||
if(!istype(W))
|
||||
return 0
|
||||
|
||||
if(!slot)
|
||||
return 0
|
||||
|
||||
W.forceMove(src)
|
||||
W.equipped(src, slot)
|
||||
W.layer = 20
|
||||
W.plane = HUD_PLANE
|
||||
|
||||
switch(slot)
|
||||
if(slot_collar)
|
||||
collar = W
|
||||
if(collar.tagname)
|
||||
name = collar.tagname
|
||||
real_name = collar.tagname
|
||||
regenerate_icons()
|
||||
|
||||
/mob/living/simple_animal/unEquip(obj/item/I, force)
|
||||
. = ..()
|
||||
if(!. || !I)
|
||||
return
|
||||
|
||||
if(I == collar)
|
||||
collar = null
|
||||
regenerate_icons()
|
||||
|
||||
/mob/living/simple_animal/get_access()
|
||||
. = ..()
|
||||
if(collar)
|
||||
. |= collar.GetAccess()
|
||||
|
||||
/* End Inventory */
|
||||
|
||||
/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion
|
||||
return
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
return 0
|
||||
spawn_delay = world.time + spawn_time
|
||||
var/mob/living/simple_animal/L = new mob_type(src.loc)
|
||||
L.admin_spawned = admin_spawned //If we were admin spawned, lets have our children count as that as well.
|
||||
spawned_mobs += L
|
||||
L.nest = src
|
||||
L.faction = src.faction
|
||||
|
||||
+40
-2
@@ -1222,9 +1222,11 @@ mob/proc/yank_out_object()
|
||||
if(mind)
|
||||
return mind.grab_ghost(force = force)
|
||||
|
||||
/mob/proc/notify_ghost_cloning(var/message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", var/sound = 'sound/effects/genetics.ogg', var/atom/source = null)
|
||||
/mob/proc/notify_ghost_cloning(message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", sound = 'sound/effects/genetics.ogg', atom/source = null, flashwindow = TRUE)
|
||||
var/mob/dead/observer/ghost = get_ghost()
|
||||
if(ghost)
|
||||
if(flashwindow)
|
||||
window_flash(ghost.client)
|
||||
ghost.notify_cloning(message, sound, source)
|
||||
return ghost
|
||||
|
||||
@@ -1263,7 +1265,7 @@ mob/proc/yank_out_object()
|
||||
return // Only living mobs can ventcrawl
|
||||
|
||||
//You can buckle on mobs if you're next to them since most are dense
|
||||
/mob/buckle_mob(mob/living/M)
|
||||
/mob/buckle_mob(mob/living/M, force = 0)
|
||||
if(M.buckled)
|
||||
return 0
|
||||
var/turf/T = get_turf(src)
|
||||
@@ -1309,3 +1311,39 @@ mob/proc/yank_out_object()
|
||||
if(F in target.faction)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/proc/create_attack_log(var/text, var/collapse = 1)//forgive me code gods for this shitcode proc
|
||||
//this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar."
|
||||
//That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out.
|
||||
var/new_log = "\[[time_stamp()]] [text]"
|
||||
|
||||
if(length(attack_log) > 0)//if there are other logs already present
|
||||
var/previous_log = attack_log[length(attack_log)]//get the latest log
|
||||
var/last_log_is_range = (copytext(previous_log, 10, 11) == "-") //whether the last log is a time range or not. The "-" will be an indicator that it is.
|
||||
var/x_sign_position = findtext(previous_log, "x")
|
||||
|
||||
if(world.timeofday - last_log > 100)//if more than 10 seconds from last log
|
||||
collapse = 0//don't collapse anyway
|
||||
|
||||
//the following checks if the last log has the same contents as the new one
|
||||
if(last_log_is_range)
|
||||
if(!(copytext(previous_log, x_sign_position + 13) == text))//the 13 is there because of span classes; you won't see those normally in-game
|
||||
collapse = 0
|
||||
else
|
||||
if(!(copytext(previous_log, 12) == text))
|
||||
collapse = 0
|
||||
|
||||
|
||||
if(collapse == 1)
|
||||
var/rep = 0
|
||||
var/old_timestamp = copytext(previous_log, 2, 10)//copy the first time value. This one doesn't move when it's a timespan, so no biggie
|
||||
//An attack log entry can either be a time range with multiple occurences of an action or a single one, with just one time stamp
|
||||
if(last_log_is_range)
|
||||
|
||||
rep = text2num(copytext(previous_log, 44, x_sign_position))//get whatever number is right before the 'x'
|
||||
|
||||
new_log = "\[[old_timestamp]-[time_stamp()]]<font color='purple'><b>[rep?rep+1:2]x</b></font> [text]"
|
||||
attack_log -= attack_log[length(attack_log)]//remove the last log
|
||||
|
||||
attack_log += new_log
|
||||
last_log = world.timeofday
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
var/lastattacker = null
|
||||
var/lastattacked = null
|
||||
var/attack_log = list( )
|
||||
var/last_log = 0
|
||||
var/obj/machinery/machine = null
|
||||
var/other_mobs = null
|
||||
var/memory = ""
|
||||
|
||||
@@ -269,8 +269,8 @@
|
||||
state = GRAB_NECK
|
||||
icon_state = "grabbed+1"
|
||||
assailant.set_dir(get_dir(assailant, affecting))
|
||||
affecting.attack_log += "\[[time_stamp()]\] <font color='orange'>Has had their neck grabbed by [assailant.name] ([assailant.ckey])</font>"
|
||||
assailant.attack_log += "\[[time_stamp()]\] <font color='red'>Grabbed the neck of [affecting.name] ([affecting.ckey])</font>"
|
||||
affecting.create_attack_log("<font color='orange'>Has had their neck grabbed by [assailant.name] ([assailant.ckey])</font>")
|
||||
assailant.create_attack_log("<font color='red'>Grabbed the neck of [affecting.name] ([affecting.ckey])</font>")
|
||||
log_attack("<font color='red'>[assailant.name] ([assailant.ckey]) grabbed the neck of [affecting.name] ([affecting.ckey])</font>")
|
||||
if(!iscarbon(assailant))
|
||||
affecting.LAssailant = null
|
||||
@@ -285,8 +285,8 @@
|
||||
|
||||
state = GRAB_KILL
|
||||
assailant.visible_message("<span class='danger'>[assailant] has tightened \his grip on [affecting]'s neck!</span>")
|
||||
affecting.attack_log += "\[[time_stamp()]\] <font color='orange'>Has been strangled (kill intent) by [assailant.name] ([assailant.ckey])</font>"
|
||||
assailant.attack_log += "\[[time_stamp()]\] <font color='red'>Strangled (kill intent) [affecting.name] ([affecting.ckey])</font>"
|
||||
affecting.create_attack_log("<font color='orange'>Has been strangled (kill intent) by [assailant.name] ([assailant.ckey])</font>")
|
||||
assailant.create_attack_log("<font color='red'>Strangled (kill intent) [affecting.name] ([affecting.ckey])</font>")
|
||||
msg_admin_attack("[key_name(assailant)] strangled (kill intent) [key_name(affecting)]")
|
||||
|
||||
assailant.next_move = world.time + 10
|
||||
@@ -338,8 +338,8 @@
|
||||
damage += hat.force * 3
|
||||
affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee"))
|
||||
playsound(assailant.loc, "swing_hit", 25, 1, -1)
|
||||
assailant.attack_log += text("\[[time_stamp()]\] <font color='red'>Headbutted [affecting.name] ([affecting.ckey])</font>")
|
||||
affecting.attack_log += text("\[[time_stamp()]\] <font color='orange'>Headbutted by [assailant.name] ([assailant.ckey])</font>")
|
||||
assailant.create_attack_log("<font color='red'>Headbutted [affecting.name] ([affecting.ckey])</font>")
|
||||
affecting.create_attack_log("<font color='orange'>Headbutted by [assailant.name] ([assailant.ckey])</font>")
|
||||
msg_admin_attack("[key_name(assailant)] has headbutted [key_name(affecting)]")
|
||||
return
|
||||
|
||||
@@ -357,8 +357,8 @@
|
||||
return
|
||||
assailant.visible_message("<span class='danger'>[assailant] presses \his fingers into [affecting]'s eyes!</span>")
|
||||
to_chat(affecting, "<span class='danger'>You feel immense pain as digits are being pressed into your eyes!</span>")
|
||||
assailant.attack_log += text("\[[time_stamp()]\] <font color='red'>Pressed fingers into the eyes of [affecting.name] ([affecting.ckey])</font>")
|
||||
affecting.attack_log += text("\[[time_stamp()]\] <font color='orange'>Had fingers pressed into their eyes by [assailant.name] ([assailant.ckey])</font>")
|
||||
assailant.create_attack_log("<font color='red'>Pressed fingers into the eyes of [affecting.name] ([affecting.ckey])</font>")
|
||||
affecting.create_attack_log("<font color='orange'>Had fingers pressed into their eyes by [assailant.name] ([assailant.ckey])</font>")
|
||||
msg_admin_attack("[key_name(assailant)] has pressed his fingers into [key_name(affecting)]'s eyes.")
|
||||
var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes)
|
||||
eyes.damage += rand(3,4)
|
||||
@@ -396,8 +396,8 @@
|
||||
|
||||
user.visible_message("<span class='danger'>[user] devours \the [affecting]!</span>")
|
||||
if(affecting.mind)
|
||||
affecting.attack_log += "\[[time_stamp()]\] <font color='orange'>Has been devoured by [attacker.name] ([attacker.ckey])</font>"
|
||||
attacker.attack_log += "\[[time_stamp()]\] <font color='red'>Devoured [affecting.name] ([affecting.ckey])</font>"
|
||||
affecting.create_attack_log("<font color='orange'>Has been devoured by [attacker.name] ([attacker.ckey])</font>")
|
||||
attacker.create_attack_log("<font color='red'>Devoured [affecting.name] ([affecting.ckey])</font>")
|
||||
msg_admin_attack("[key_name(attacker)] devoured [key_name(affecting)]")
|
||||
|
||||
affecting.loc = user
|
||||
|
||||
@@ -453,20 +453,22 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HARM)
|
||||
lname = "<span class='name'>[lname]</span> "
|
||||
to_chat(M, "<span class='deadsay'>[lname][follow][message]</span>")
|
||||
|
||||
/proc/notify_ghosts(var/message, var/ghost_sound = null, var/enter_link = null, var/atom/source = null, var/image/alert_overlay = null, var/attack_not_jump = 0) //Easy notification of ghosts.
|
||||
/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, atom/source = null, image/alert_overlay = null, attack_not_jump = 0, flashwindow = TRUE, var/action = NOTIFY_JUMP) //Easy notification of ghosts.
|
||||
for(var/mob/dead/observer/O in player_list)
|
||||
if(O.client)
|
||||
to_chat(O, "<span class='ghostalert'>[message][(enter_link) ? " [enter_link]" : ""]<span>")
|
||||
if(ghost_sound)
|
||||
O << sound(ghost_sound)
|
||||
if(flashwindow)
|
||||
window_flash(O.client)
|
||||
if(source)
|
||||
var/obj/screen/alert/notify_jump/A = O.throw_alert("\ref[source]_notify_jump", /obj/screen/alert/notify_jump)
|
||||
var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action)
|
||||
if(A)
|
||||
if(O.client.prefs && O.client.prefs.UI_style)
|
||||
A.icon = ui_style2icon(O.client.prefs.UI_style)
|
||||
A.desc = message
|
||||
A.attack_not_jump = attack_not_jump
|
||||
A.jump_target = source
|
||||
A.action = action
|
||||
A.target = source
|
||||
if(!alert_overlay)
|
||||
var/old_layer = source.layer
|
||||
var/old_plane = source.plane
|
||||
|
||||
Reference in New Issue
Block a user