mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 04:27:35 +01:00
Merge remote-tracking branch 'upstream/master' into diagonal-movement
This commit is contained in:
@@ -1,4 +1,33 @@
|
||||
/mob/dead/Login()
|
||||
. = ..()
|
||||
var/turf/T = get_turf(src)
|
||||
if (isturf(T))
|
||||
update_z(T.z)
|
||||
|
||||
/mob/dead/Logout()
|
||||
update_z(null)
|
||||
return ..()
|
||||
|
||||
/mob/dead/forceMove(atom/destination)
|
||||
var/turf/old_turf = get_turf(src)
|
||||
var/turf/new_turf = get_turf(destination)
|
||||
if (old_turf?.z != new_turf?.z)
|
||||
onTransitZ(old_turf?.z, new_turf?.z)
|
||||
var/oldloc = loc
|
||||
loc = destination
|
||||
Moved(oldloc, NONE, TRUE)
|
||||
Moved(oldloc, NONE, TRUE)
|
||||
|
||||
/mob/dead/onTransitZ(old_z,new_z)
|
||||
..()
|
||||
update_z(new_z)
|
||||
|
||||
/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister
|
||||
if (registered_z != new_z)
|
||||
if (registered_z)
|
||||
SSmobs.dead_players_by_zlevel[registered_z] -= src
|
||||
if (client)
|
||||
if (new_z)
|
||||
SSmobs.dead_players_by_zlevel[new_z] += src
|
||||
registered_z = new_z
|
||||
else
|
||||
registered_z = null
|
||||
@@ -12,21 +12,21 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
icon_state = "ghost"
|
||||
layer = GHOST_LAYER
|
||||
stat = DEAD
|
||||
density = 0
|
||||
canmove = 0
|
||||
density = FALSE
|
||||
canmove = FALSE
|
||||
alpha = 127
|
||||
move_resist = INFINITY // don't get pushed around
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
var/can_reenter_corpse
|
||||
var/bootime = 0
|
||||
var/bootime = FALSE
|
||||
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
|
||||
//If you died in the game and are a ghsot - this will remain as null.
|
||||
//Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot.
|
||||
universal_speak = 1
|
||||
universal_speak = TRUE
|
||||
var/atom/movable/following = null
|
||||
var/image/ghostimage = null //this mobs ghost image, for deleting and stuff
|
||||
var/ghostvision = 1 //is the ghost able to see things humans can't?
|
||||
var/seedarkness = 1
|
||||
var/ghostvision = TRUE //is the ghost able to see things humans can't?
|
||||
var/seedarkness = TRUE
|
||||
var/data_hud_seen = FALSE //this should one of the defines in __DEFINES/hud.dm
|
||||
var/ghost_orbit = GHOST_ORBIT_CIRCLE
|
||||
|
||||
@@ -95,6 +95,11 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
updateallghostimages()
|
||||
return ..()
|
||||
|
||||
/mob/dead/observer/examine(mob/user)
|
||||
..()
|
||||
if(!invisibility)
|
||||
to_chat(user, "It seems extremely obvious.")
|
||||
|
||||
// This seems stupid, but it's the easiest way to avoid absolutely ridiculous shit from happening
|
||||
// Copying an appearance directly from a mob includes it's verb list, it's invisibility, it's alpha, and it's density
|
||||
// You might recognize these things as "fucking ridiculous to put in an appearance"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
var/global/list/boo_phrases=list(
|
||||
GLOBAL_LIST_INIT(boo_phrases, list(
|
||||
"You feel a chill run down your spine.",
|
||||
"You think you see a figure in your peripheral vision.",
|
||||
"What was that?",
|
||||
@@ -9,16 +7,17 @@ var/global/list/boo_phrases=list(
|
||||
"Something doesn't feel right...",
|
||||
"You feel a presence in the room.",
|
||||
"It feels like someone's standing behind you.",
|
||||
)
|
||||
))
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/boo
|
||||
name = "Boo!"
|
||||
desc = "Fuck with the living."
|
||||
|
||||
ghost = 1
|
||||
ghost = TRUE
|
||||
|
||||
school = "transmutation"
|
||||
charge_max = 600
|
||||
starts_charged = FALSE
|
||||
clothes_req = 0
|
||||
stat_allowed = 1
|
||||
invocation = ""
|
||||
@@ -27,26 +26,4 @@ var/global/list/boo_phrases=list(
|
||||
|
||||
/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)
|
||||
|
||||
// Bug humans
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H && H.client)
|
||||
to_chat(H, "<i>[pick(boo_phrases)]</i>")
|
||||
|
||||
// Flicker unblessed lights in range
|
||||
if(istype(A,/obj/machinery/light))
|
||||
var/obj/machinery/light/L = A
|
||||
if(L)
|
||||
L.flicker()
|
||||
|
||||
// OH GOD BLUE APC (single animation cycle)
|
||||
if(istype(A, /obj/machinery/power/apc))
|
||||
A:spookify()
|
||||
|
||||
if(istype(A, /obj/machinery/status_display))
|
||||
A:spookymode=1
|
||||
|
||||
if(istype(A, /obj/machinery/ai_status_display))
|
||||
A:spookymode=1
|
||||
T.get_spooked()
|
||||
|
||||
@@ -85,13 +85,8 @@ Doesn't work on other aliens/AI.*/
|
||||
return
|
||||
// TURF CHECK
|
||||
else if(istype(O, /turf/simulated))
|
||||
var/turf/T = O
|
||||
// R WALL
|
||||
if(istype(T, /turf/simulated/wall/r_wall))
|
||||
to_chat(src, "<span class='noticealien'>You cannot dissolve this object.</span>")
|
||||
return
|
||||
// R FLOOR
|
||||
if(istype(T, /turf/simulated/floor/engine))
|
||||
var/turf/simulated/T = O
|
||||
if(T.unacidable)
|
||||
to_chat(src, "<span class='noticealien'>You cannot dissolve this object.</span>")
|
||||
return
|
||||
else// Not a type we can acid.
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
return TRUE
|
||||
..()
|
||||
|
||||
/mob/living/carbon/water_act(volume, temperature, source)
|
||||
/mob/living/carbon/water_act(volume, temperature, source, method = TOUCH)
|
||||
. = ..()
|
||||
if(volume > 10) //anything over 10 volume will make the mob wetter.
|
||||
wetlevel = min(wetlevel + 1,5)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
|
||||
if(lying && surgeries.len)
|
||||
|
||||
@@ -1711,8 +1711,8 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
|
||||
|
||||
/mob/living/carbon/human/IsAdvancedToolUser()
|
||||
if(dna.species.has_fine_manipulation)
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/get_permeability_protection()
|
||||
var/list/prot = list("hands"=0, "chest"=0, "groin"=0, "legs"=0, "feet"=0, "arms"=0, "head"=0)
|
||||
@@ -2003,6 +2003,9 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
|
||||
genemutcheck(src, block, null, MUTCHK_FORCED)
|
||||
dna.UpdateSE()
|
||||
|
||||
/mob/living/carbon/human/get_spooked()
|
||||
to_chat(src, "<span class='whisper'>[pick(GLOB.boo_phrases)]</span>")
|
||||
|
||||
/mob/living/carbon/human/extinguish_light()
|
||||
// Parent function handles stuff the human may be holding
|
||||
..()
|
||||
|
||||
@@ -506,9 +506,9 @@ emp_act
|
||||
return 0
|
||||
..()
|
||||
|
||||
/mob/living/carbon/human/water_act(volume, temperature, source)
|
||||
..()
|
||||
dna.species.water_act(src,volume,temperature,source)
|
||||
/mob/living/carbon/human/water_act(volume, temperature, source, method = TOUCH)
|
||||
. = ..()
|
||||
dna.species.water_act(src, volume, temperature, source, method)
|
||||
|
||||
/mob/living/carbon/human/is_eyes_covered(check_glasses = TRUE, check_head = TRUE, check_mask = TRUE)
|
||||
if(check_glasses && glasses && (glasses.flags_cover & GLASSESCOVERSEYES))
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
return has_organ("chest")
|
||||
if(slot_wear_id)
|
||||
// the only relevant check for this is the uniform check
|
||||
return 1
|
||||
return TRUE
|
||||
if(slot_wear_pda)
|
||||
return 1
|
||||
return TRUE
|
||||
if(slot_l_ear)
|
||||
return has_organ("head")
|
||||
if(slot_r_ear)
|
||||
@@ -62,9 +62,9 @@
|
||||
if(slot_s_store)
|
||||
return has_organ("chest")
|
||||
if(slot_in_backpack)
|
||||
return 1
|
||||
return TRUE
|
||||
if(slot_tie)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
// The actual dropping happens at the mob level - checks to prevent drops should
|
||||
// come here
|
||||
@@ -413,56 +413,34 @@
|
||||
|
||||
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = 0)
|
||||
switch(dna.species.handle_can_equip(I, slot, disable_warning, src))
|
||||
if(1) return 1
|
||||
if(2) return 0 //if it returns 2, it wants no normal handling
|
||||
|
||||
if(1) return TRUE
|
||||
if(2) return FALSE //if it returns 2, it wants no normal handling
|
||||
if(!has_organ_for_slot(slot))
|
||||
return FALSE
|
||||
|
||||
if(istype(I, /obj/item/clothing/under) || istype(I, /obj/item/clothing/suit))
|
||||
if(FAT in mutations)
|
||||
//testing("[M] TOO FAT TO WEAR [src]!")
|
||||
if(!(I.flags_size & ONESIZEFITSALL))
|
||||
if(!disable_warning)
|
||||
to_chat(src, "<span class='alert'>You're too fat to wear the [I].</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
switch(slot)
|
||||
if(slot_l_hand)
|
||||
if(l_hand)
|
||||
return 0
|
||||
return !incapacitated()
|
||||
return !l_hand && !incapacitated()
|
||||
if(slot_r_hand)
|
||||
if(r_hand)
|
||||
return 0
|
||||
return !incapacitated()
|
||||
return !r_hand && !incapacitated()
|
||||
if(slot_wear_mask)
|
||||
if(wear_mask)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_MASK))
|
||||
return 0
|
||||
return 1
|
||||
return !wear_mask && (I.slot_flags & SLOT_MASK)
|
||||
if(slot_back)
|
||||
if(back)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_BACK))
|
||||
return 0
|
||||
return 1
|
||||
return !back && (I.slot_flags & SLOT_BACK)
|
||||
if(slot_wear_suit)
|
||||
if(wear_suit)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_OCLOTHING))
|
||||
return 0
|
||||
return 1
|
||||
return !wear_suit && (I.slot_flags & SLOT_OCLOTHING)
|
||||
if(slot_gloves)
|
||||
if(gloves)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_GLOVES))
|
||||
return 0
|
||||
return 1
|
||||
return !gloves && (I.slot_flags & SLOT_GLOVES)
|
||||
if(slot_shoes)
|
||||
if(shoes)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_FEET))
|
||||
return 0
|
||||
return 1
|
||||
return !shoes && (I.slot_flags & SLOT_FEET)
|
||||
if(slot_belt)
|
||||
if(belt)
|
||||
return 0
|
||||
@@ -474,39 +452,15 @@
|
||||
return
|
||||
return 1
|
||||
if(slot_glasses)
|
||||
if(glasses)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_EYES))
|
||||
return 0
|
||||
return 1
|
||||
return !glasses && (I.slot_flags & SLOT_EYES)
|
||||
if(slot_head)
|
||||
if(head)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_HEAD))
|
||||
return 0
|
||||
return 1
|
||||
return !head && (I.slot_flags & SLOT_HEAD)
|
||||
if(slot_l_ear)
|
||||
if(l_ear)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_EARS))
|
||||
return 0
|
||||
if((I.slot_flags & SLOT_TWOEARS) && r_ear )
|
||||
return 0
|
||||
return 1
|
||||
return !l_ear && (I.slot_flags & SLOT_EARS) && !((I.slot_flags & SLOT_TWOEARS) && r_ear)
|
||||
if(slot_r_ear)
|
||||
if(r_ear)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_EARS))
|
||||
return 0
|
||||
if((I.slot_flags & SLOT_TWOEARS) && l_ear)
|
||||
return 0
|
||||
return 1
|
||||
return !r_ear && (I.slot_flags & SLOT_EARS) && !((I.slot_flags & SLOT_TWOEARS) && l_ear)
|
||||
if(slot_w_uniform)
|
||||
if(w_uniform)
|
||||
return 0
|
||||
if(!(I.slot_flags & SLOT_ICLOTHING))
|
||||
return 0
|
||||
return 1
|
||||
return !w_uniform && (I.slot_flags & SLOT_ICLOTHING)
|
||||
if(slot_wear_id)
|
||||
if(wear_id)
|
||||
return 0
|
||||
@@ -575,17 +529,9 @@
|
||||
return 1
|
||||
return 0
|
||||
if(slot_handcuffed)
|
||||
if(handcuffed)
|
||||
return 0
|
||||
if(!istype(I, /obj/item/restraints/handcuffs))
|
||||
return 0
|
||||
return 1
|
||||
return !handcuffed && istype(I, /obj/item/restraints/handcuffs)
|
||||
if(slot_legcuffed)
|
||||
if(legcuffed)
|
||||
return 0
|
||||
if(!istype(I, /obj/item/restraints/legcuffs))
|
||||
return 0
|
||||
return 1
|
||||
return !legcuffed && istype(I, /obj/item/restraints/legcuffs)
|
||||
if(slot_in_backpack)
|
||||
if(back && istype(back, /obj/item/storage/backpack))
|
||||
var/obj/item/storage/backpack/B = back
|
||||
|
||||
@@ -718,7 +718,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u
|
||||
|
||||
H.sync_lighting_plane_alpha()
|
||||
|
||||
/datum/species/proc/water_act(mob/living/carbon/human/M, volume, temperature, source)
|
||||
/datum/species/proc/water_act(mob/living/carbon/human/M, volume, temperature, source, method = TOUCH)
|
||||
if(abs(temperature - M.bodytemperature) > 10) //If our water and mob temperature varies by more than 10K, cool or/ heat them appropriately
|
||||
M.bodytemperature = (temperature + M.bodytemperature) * 0.5 //Approximation for gradual heating or cooling
|
||||
|
||||
|
||||
@@ -36,10 +36,38 @@
|
||||
genemutcheck(H, REMOTETALKBLOCK, null, MUTCHK_FORCED)
|
||||
H.dna.default_blocks.Add(REMOTETALKBLOCK)
|
||||
|
||||
/datum/species/grey/water_act(mob/living/carbon/human/H, volume, temperature, source)
|
||||
..()
|
||||
H.take_organ_damage(5, min(volume, 20))
|
||||
H.emote("scream")
|
||||
/datum/species/grey/water_act(mob/living/carbon/human/H, volume, temperature, source, method = TOUCH)
|
||||
. = ..()
|
||||
|
||||
if(method == TOUCH)
|
||||
if(volume > 25)
|
||||
if(H.wear_mask)
|
||||
to_chat(H, "<span class='danger'>Your [H.wear_mask] protects you from the acid!</span>")
|
||||
return
|
||||
|
||||
if(H.head)
|
||||
to_chat(H, "<span class='danger'>Your [H.wear_mask] protects you from the acid!</span>")
|
||||
return
|
||||
|
||||
if(prob(75))
|
||||
H.take_organ_damage(5, 10)
|
||||
H.emote("scream")
|
||||
var/obj/item/organ/external/affecting = H.get_organ("head")
|
||||
if(affecting)
|
||||
affecting.disfigure()
|
||||
else
|
||||
H.take_organ_damage(5, 10)
|
||||
else
|
||||
H.take_organ_damage(5, 10)
|
||||
else
|
||||
to_chat(H, "<span class='warning'>The water stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!</span>")
|
||||
if(volume >= 10)
|
||||
H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20))
|
||||
H.emote("scream")
|
||||
to_chat(H, "<span class='warning'>The water stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!</span>")
|
||||
if(volume >= 10)
|
||||
H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20))
|
||||
H.emote("scream")
|
||||
|
||||
/datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H)
|
||||
var/translator_pref = H.client.prefs.speciesprefs
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
genemutcheck(H, MONKEYBLOCK, null, MUTCHK_FORCED)
|
||||
|
||||
/datum/species/monkey/handle_can_equip(obj/item/I, slot, disable_warning = 0, mob/living/carbon/human/user)
|
||||
if(!user.has_organ_for_slot(slot))
|
||||
return 2
|
||||
switch(slot)
|
||||
if(slot_l_hand)
|
||||
if(user.l_hand)
|
||||
|
||||
@@ -130,8 +130,8 @@
|
||||
suit=/obj/item/clothing/suit/space/eva/plasmaman/cargo
|
||||
helm=/obj/item/clothing/head/helmet/space/eva/plasmaman/cargo
|
||||
if("Shaft Miner")
|
||||
suit=/obj/item/clothing/suit/space/eva/plasmaman/miner
|
||||
helm=/obj/item/clothing/head/helmet/space/eva/plasmaman/miner
|
||||
suit=/obj/item/clothing/suit/space/eva/plasmaman/explorer
|
||||
helm=/obj/item/clothing/head/helmet/space/eva/plasmaman/explorer
|
||||
if("Botanist")
|
||||
suit=/obj/item/clothing/suit/space/eva/plasmaman/botanist
|
||||
helm=/obj/item/clothing/head/helmet/space/eva/plasmaman/botanist
|
||||
|
||||
@@ -102,7 +102,18 @@
|
||||
to_chat(user, "<span class='warning'>You run out of momentum!</span>")
|
||||
return
|
||||
|
||||
|
||||
|
||||
/datum/species/unathi/handle_death(mob/living/carbon/human/H)
|
||||
H.stop_tail_wagging(1)
|
||||
|
||||
/datum/species/unathi/ashwalker
|
||||
name = "Ash Walker"
|
||||
name_plural = "Ash Walkers"
|
||||
|
||||
blurb = "These reptillian creatures appear to be related to the Unathi, but seem significantly less evolved. \
|
||||
They roam the wastes of Lavaland, worshipping a dead city and capturing unsuspecting miners."
|
||||
|
||||
language = "Sinta'unathi"
|
||||
default_language = "Sinta'unathi"
|
||||
|
||||
slowdown = -0.80
|
||||
species_traits = list(NO_BREATHE, NOGUNS)
|
||||
|
||||
@@ -1223,7 +1223,7 @@ var/global/list/damage_icon_parts = list()
|
||||
|
||||
//Adds a collar overlay above the helmet layer if the suit has one
|
||||
// Suit needs an identically named sprite in icons/mob/collar.dmi
|
||||
// For suits with species_fit and sprite_sheets, an identically named sprite needs to exist in a file like this icons/mob/species/[species_name_here]/collar.dmi.
|
||||
// For suits with sprite_sheets, an identically named sprite needs to exist in a file like this icons/mob/species/[species_name_here]/collar.dmi.
|
||||
/mob/living/carbon/human/proc/update_collar(var/update_icons=1)
|
||||
remove_overlay(COLLAR_LAYER)
|
||||
var/icon/C = new('icons/mob/collar.dmi')
|
||||
|
||||
@@ -216,13 +216,9 @@
|
||||
fire_stacks += L.fire_stacks
|
||||
IgniteMob()
|
||||
|
||||
//Mobs on Fire end
|
||||
|
||||
/mob/living/water_act(volume, temperature)
|
||||
if(volume >= 20) fire_stacks -= 0.5
|
||||
if(volume >= 50) fire_stacks -= 1
|
||||
|
||||
|
||||
/mob/living/water_act(volume, temperature, source, method = TOUCH)
|
||||
. = ..()
|
||||
adjust_fire_stacks(-(volume * 0.2))
|
||||
|
||||
//This is called when the mob is thrown into a dense turf
|
||||
/mob/living/proc/turf_collision(var/turf/T, var/speed)
|
||||
|
||||
@@ -67,3 +67,5 @@
|
||||
var/list/status_effects //a list of all status effects the mob has
|
||||
|
||||
var/deathgasp_on_death = FALSE
|
||||
|
||||
var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
if(ranged_ability)
|
||||
ranged_ability.add_ranged_ability(src, "<span class='notice'>You currently have <b>[ranged_ability]</b> active!</span>")
|
||||
|
||||
//for when action buttons are lost and need to be regained, such as when the mind enters a new mob
|
||||
var/datum/changeling/changeling = usr.mind.changeling
|
||||
if(changeling)
|
||||
for(var/power in changeling.purchasedpowers)
|
||||
var/datum/action/changeling/S = power
|
||||
if(istype(S) && S.needs_button)
|
||||
S.Grant(src)
|
||||
|
||||
//Should update regardless of if we can ventcrawl, since we can end up in pipes in other ways.
|
||||
update_pipe_vision()
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ var/datum/cameranet/cameranet = new()
|
||||
/*
|
||||
/datum/cameranet/proc/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug("Initializing...", src)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat(name, statclick.update("Cameras: [cameranet.cameras.len] | Chunks: [cameranet.chunks.len]"))
|
||||
*/
|
||||
|
||||
@@ -289,7 +289,7 @@ var/list/robot_verbs_default = list(
|
||||
if(islist(force_modules) && force_modules.len)
|
||||
modules = force_modules.Copy()
|
||||
if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis)
|
||||
to_chat(src, "<span class='warning'>Crisis mode active. Combat module available.</span>")
|
||||
to_chat(src, "<span class='warning'>Crisis mode active. The combat module is now available.</span>")
|
||||
modules += "Combat"
|
||||
if(mmi != null && mmi.alien)
|
||||
modules = list("Hunter")
|
||||
@@ -417,6 +417,31 @@ var/list/robot_verbs_default = list(
|
||||
radio.config(module.channels)
|
||||
notify_ai(2)
|
||||
|
||||
/mob/living/silicon/robot/proc/reset_module()
|
||||
notify_ai(2)
|
||||
|
||||
uneq_all()
|
||||
sight_mode = null
|
||||
hands.icon_state = "nomod"
|
||||
icon_state = "robot"
|
||||
module.remove_subsystems_and_actions(src)
|
||||
QDEL_NULL(module)
|
||||
|
||||
camera.network.Remove(list("Engineering", "Medical", "Mining Outpost"))
|
||||
rename_character(real_name, get_default_name("Default"))
|
||||
languages = list()
|
||||
speech_synthesizer_langs = list()
|
||||
|
||||
update_icons()
|
||||
update_headlamp()
|
||||
|
||||
speed = 0 // Remove upgrades.
|
||||
ionpulse = FALSE
|
||||
magpulse = FALSE
|
||||
add_language("Robot Talk", 1)
|
||||
|
||||
status_flags |= CANPUSH
|
||||
|
||||
//for borg hotkeys, here module refers to borg inv slot, not core module
|
||||
/mob/living/silicon/robot/verb/cmd_toggle_module(module as num)
|
||||
set name = "Toggle Module"
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/IsAdvancedToolUser()
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj)
|
||||
|
||||
|
||||
@@ -112,4 +112,4 @@
|
||||
uniform = /obj/item/clothing/under/color/lightpurple
|
||||
suit = /obj/item/clothing/suit/wizrobe
|
||||
shoes = /obj/item/clothing/shoes/sandal
|
||||
head = /obj/item/clothing/head/wizard
|
||||
head = /obj/item/clothing/head/wizard
|
||||
|
||||
@@ -117,13 +117,17 @@
|
||||
|
||||
//MICE!
|
||||
if(eats_mice && isturf(loc) && !incapacitated())
|
||||
for(var/mob/living/simple_animal/mouse/M in view(1,src))
|
||||
for(var/mob/living/simple_animal/mouse/M in view(1, src))
|
||||
if(!M.stat && Adjacent(M))
|
||||
custom_emote(1, "splats \the [M]!")
|
||||
M.splat()
|
||||
movement_target = null
|
||||
stop_automated_movement = 0
|
||||
break
|
||||
for(var/obj/item/toy/cattoy/T in view(1, src))
|
||||
if(T.cooldown < (world.time - 400))
|
||||
custom_emote(1, "bats \the [T] around with its paw!")
|
||||
T.cooldown = world.time
|
||||
make_babies()
|
||||
|
||||
/mob/living/simple_animal/pet/cat/handle_automated_movement()
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
ventcrawler = 2
|
||||
gold_core_spawnable = 2
|
||||
var/squish_chance = 50
|
||||
loot = list(/obj/effect/decal/cleanable/deadcockroach)
|
||||
loot = list(/obj/effect/decal/cleanable/insectguts)
|
||||
del_on_death = 1
|
||||
|
||||
/mob/living/simple_animal/cockroach/can_die()
|
||||
@@ -40,10 +40,3 @@
|
||||
/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
|
||||
|
||||
@@ -25,9 +25,18 @@
|
||||
melee_damage_lower = 1
|
||||
melee_damage_upper = 2
|
||||
stop_automated_movement_when_pulled = 1
|
||||
var/milk_content = 0
|
||||
can_collar = 1
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
var/obj/item/udder/udder = null
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/New()
|
||||
udder = new()
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/Destroy()
|
||||
qdel(udder)
|
||||
udder = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement()
|
||||
..()
|
||||
@@ -51,9 +60,8 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(stat == CONSCIOUS && prob(5))
|
||||
milk_content = min(50, milk_content+rand(5, 10))
|
||||
|
||||
if(stat == CONSCIOUS)
|
||||
udder.generateMilk()
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/Retaliate()
|
||||
..()
|
||||
@@ -64,6 +72,12 @@
|
||||
if(!stat)
|
||||
eat_plants()
|
||||
|
||||
/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/reagent_containers/glass))
|
||||
udder.milkAnimal(O, user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants()
|
||||
var/eaten = FALSE
|
||||
var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc
|
||||
@@ -79,23 +93,6 @@
|
||||
if(eaten && prob(10))
|
||||
say("Nom")
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/attackby(obj/item/I, mob/user, params)
|
||||
if(stat == CONSCIOUS && istype(I, /obj/item/reagent_containers/glass))
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
var/obj/item/reagent_containers/glass/G = I
|
||||
var/transfered = min(milk_content, rand(5,10), (G.volume - G.reagents.total_volume))
|
||||
if(transfered > 0)
|
||||
user.visible_message("<span class='notice'>[user] milks [src] using [G].</span>")
|
||||
G.reagents.add_reagent("milk", transfered)
|
||||
milk_content -= transfered
|
||||
else if(G.reagents.total_volume >= G.volume)
|
||||
to_chat(user, "<span class='warning'>[G] is full.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The udder is dry. Wait a bit longer...</span>")
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget()
|
||||
..()
|
||||
if(isdiona(target))
|
||||
@@ -127,34 +124,31 @@
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
health = 50
|
||||
maxHealth = 50
|
||||
var/milk_content = 0
|
||||
can_collar = 1
|
||||
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
var/obj/item/udder/udder = null
|
||||
|
||||
/mob/living/simple_animal/cow/New()
|
||||
..()
|
||||
/mob/living/simple_animal/cow/Initialize()
|
||||
udder = new()
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/cow/attackby(var/obj/item/O as obj, var/mob/user as mob, params, params)
|
||||
/mob/living/simple_animal/cow/Destroy()
|
||||
qdel(udder)
|
||||
udder = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params)
|
||||
if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass))
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
var/obj/item/reagent_containers/glass/G = O
|
||||
var/transfered = min(milk_content, rand(5,10), (G.volume - G.reagents.total_volume))
|
||||
if(transfered > 0)
|
||||
user.visible_message("<span class='notice'>[user] milks [src] using \the [O].</span>")
|
||||
G.reagents.add_reagent("milk", transfered)
|
||||
milk_content -= transfered
|
||||
else if(G.reagents.total_volume >= G.volume)
|
||||
to_chat(user, "<span class='warning'>\The [O] is full.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The udder is dry. Wait a bit longer...</span>")
|
||||
udder.milkAnimal(O, user)
|
||||
return 1
|
||||
else
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/cow/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(stat == CONSCIOUS && prob(5))
|
||||
milk_content = min(50, milk_content+rand(5, 10))
|
||||
if(stat == CONSCIOUS)
|
||||
udder.generateMilk()
|
||||
|
||||
/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob)
|
||||
if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead)
|
||||
@@ -431,3 +425,26 @@ var/global/chicken_count = 0
|
||||
can_collar = 1
|
||||
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
|
||||
/obj/item/udder
|
||||
name = "udder"
|
||||
|
||||
/obj/item/udder/New()
|
||||
create_reagents(50)
|
||||
reagents.add_reagent("milk", 20)
|
||||
. = ..()
|
||||
|
||||
/obj/item/udder/proc/generateMilk()
|
||||
if(prob(5))
|
||||
reagents.add_reagent("milk", rand(5, 10))
|
||||
|
||||
/obj/item/udder/proc/milkAnimal(obj/O, mob/user)
|
||||
var/obj/item/reagent_containers/glass/G = O
|
||||
if(G.reagents.total_volume >= G.volume)
|
||||
to_chat(user, "<span class='danger'>[O] is full.</span>")
|
||||
return
|
||||
var/transfered = reagents.trans_to(O, rand(5,10))
|
||||
if(transfered)
|
||||
user.visible_message("[user] milks [src] using \the [O].", "<span class='notice'>You milk [src] using \the [O].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='danger'>The udder is dry. Wait a bit longer...</span>")
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
/mob/living/simple_animal/mouse/Life()
|
||||
..()
|
||||
if(prob(0.5))
|
||||
if(prob(0.5) && !ckey)
|
||||
stat = UNCONSCIOUS
|
||||
icon_state = "mouse_[mouse_color]_sleep"
|
||||
wander = 0
|
||||
@@ -220,4 +220,4 @@
|
||||
response_harm = "stamps on"
|
||||
gold_core_spawnable = CHEM_MOB_SPAWN_INVALID
|
||||
can_collar = 0
|
||||
butcher_results = list(/obj/item/stack/sheet/metal = 1)
|
||||
butcher_results = list(/obj/item/stack/sheet/metal = 1)
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
var/list/emote_taunt = list()
|
||||
var/taunt_chance = 0
|
||||
|
||||
var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach
|
||||
var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever
|
||||
|
||||
var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects.
|
||||
|
||||
/mob/living/simple_animal/hostile/New()
|
||||
..()
|
||||
if(!targets_from)
|
||||
@@ -198,13 +203,15 @@
|
||||
return 0
|
||||
return 1
|
||||
if(isobj(the_target))
|
||||
if(is_type_in_typecache(the_target, wanted_objects))
|
||||
if(attack_all_objects || is_type_in_list(the_target, wanted_objects))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target
|
||||
target = new_target
|
||||
LosePatience()
|
||||
if(target != null)
|
||||
GainPatience()
|
||||
Aggro()
|
||||
return 1
|
||||
|
||||
@@ -231,6 +238,7 @@
|
||||
if(target)
|
||||
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
|
||||
AttackingTarget()
|
||||
GainPatience()
|
||||
return 1
|
||||
return 0
|
||||
if(environment_smash)
|
||||
@@ -400,6 +408,17 @@
|
||||
/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets)
|
||||
return !FindTarget(possible_targets, 1)
|
||||
|
||||
|
||||
//These two procs handle losing our target if we've failed to attack them for
|
||||
//more than lose_patience_timeout deciseconds, which probably means we're stuck
|
||||
/mob/living/simple_animal/hostile/proc/GainPatience()
|
||||
if(lose_patience_timeout)
|
||||
LosePatience()
|
||||
lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE)
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LosePatience()
|
||||
deltimer(lose_patience_timer_id)
|
||||
|
||||
/mob/living/simple_animal/hostile/consider_wakeup()
|
||||
..()
|
||||
var/list/tlist
|
||||
@@ -432,4 +451,4 @@
|
||||
if(isturf(M.loc))
|
||||
. += M
|
||||
else if(M.loc.type in hostile_machines)
|
||||
. += M.loc
|
||||
. += M.loc
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
#define MINER_DASH_RANGE 4
|
||||
|
||||
/*
|
||||
|
||||
BLOOD-DRUNK MINER
|
||||
|
||||
Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive.
|
||||
|
||||
The blood-drunk miner's attacks are as follows
|
||||
- If not in KA range, it will rapidly dash at its target
|
||||
- If in KA range, it will fire its kinetic accelerator
|
||||
- If in melee range, will rapidly attack, akin to an actual player
|
||||
- After any of these attacks, may transform its cleaving saw:
|
||||
Untransformed, it attacks very rapidly for smaller amounts of damage
|
||||
Transformed, it attacks at normal speed for higher damage and cleaves enemies hit
|
||||
|
||||
When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator.
|
||||
|
||||
Difficulty: Medium
|
||||
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
|
||||
name = "blood-drunk miner"
|
||||
desc = "A miner destined to wander forever, engaged in an endless hunt."
|
||||
health = 900
|
||||
maxHealth = 900
|
||||
icon_state = "miner"
|
||||
icon_living = "miner"
|
||||
icon = 'icons/mob/alienqueen.dmi'
|
||||
light_color = "#E4C7C5"
|
||||
speak_emote = list("roars")
|
||||
speed = 1
|
||||
move_to_delay = 3
|
||||
projectiletype = /obj/item/projectile/kinetic/miner
|
||||
projectilesound = 'sound/weapons/kenetic_accel.ogg'
|
||||
ranged = 1
|
||||
ranged_cooldown_time = 16
|
||||
pixel_x = -16
|
||||
loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator)
|
||||
wander = FALSE
|
||||
del_on_death = TRUE
|
||||
blood_volume = BLOOD_VOLUME_NORMAL
|
||||
var/obj/item/melee/energy/cleaving_saw/miner/miner_saw
|
||||
var/time_until_next_transform = 0
|
||||
var/dashing = FALSE
|
||||
var/dash_cooldown = 15
|
||||
var/guidance = FALSE
|
||||
deathmessage = "falls to the ground, decaying into glowing particles."
|
||||
death_sound = "bodyfall"
|
||||
|
||||
/obj/item/gps/internal/miner
|
||||
icon_state = null
|
||||
gpstag = "Resonant Signal"
|
||||
desc = "The sweet blood, oh, it sings to me."
|
||||
invisibility = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
|
||||
guidance = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
|
||||
. = ..()
|
||||
if(. && prob(12))
|
||||
INVOKE_ASYNC(src, .proc/dash)
|
||||
|
||||
/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery
|
||||
force = 6
|
||||
force_on = 10
|
||||
|
||||
/obj/item/melee/energy/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user)
|
||||
target.add_stun_absorption("miner", 10, INFINITY)
|
||||
..()
|
||||
target.stun_absorption -= "miner"
|
||||
|
||||
/obj/item/projectile/kinetic/miner
|
||||
damage = 20
|
||||
speed = 0.9
|
||||
icon_state = "ka_tracer"
|
||||
range = MINER_DASH_RANGE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
|
||||
. = ..()
|
||||
miner_saw = new(src)
|
||||
internal_gps = new/obj/item/gps/internal/miner(src)
|
||||
|
||||
// Add a zone selection UI; otherwise the mob can't melee attack properly.
|
||||
zone_sel = new /obj/screen/zone_sel()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||
var/adjustment_amount = amount * 0.1
|
||||
if(world.time + adjustment_amount > next_move)
|
||||
changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death()
|
||||
if(health > 0)
|
||||
return
|
||||
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
|
||||
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target)
|
||||
if(dash())
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
|
||||
if(QDELETED(target))
|
||||
return
|
||||
if(next_move > world.time || !Adjacent(target)) //some cheating
|
||||
INVOKE_ASYNC(src, .proc/quick_attack_loop)
|
||||
return
|
||||
face_atom(target)
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
visible_message("<span class='danger'>[src] butchers [L]!</span>",
|
||||
"<span class='userdanger'>You butcher [L], restoring your health!</span>")
|
||||
if(!is_station_level(z) || client) //NPC monsters won't heal while on station
|
||||
if(guidance)
|
||||
adjustHealth(-L.maxHealth)
|
||||
else
|
||||
adjustHealth(-(L.maxHealth * 0.5))
|
||||
L.gib()
|
||||
return TRUE
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
miner_saw.melee_attack_chain(src, target)
|
||||
if(guidance)
|
||||
adjustHealth(-2)
|
||||
transform_weapon()
|
||||
INVOKE_ASYNC(src, .proc/quick_attack_loop)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
|
||||
if(!used_item && !isturf(A))
|
||||
used_item = miner_saw
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/GiveTarget(new_target)
|
||||
var/targets_the_same = (new_target == target)
|
||||
. = ..()
|
||||
if(. && target && !targets_the_same)
|
||||
wander = TRUE
|
||||
transform_weapon()
|
||||
INVOKE_ASYNC(src, .proc/quick_attack_loop)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
|
||||
Goto(target, move_to_delay, minimum_distance)
|
||||
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
|
||||
INVOKE_ASYNC(src, .proc/dash, target)
|
||||
else
|
||||
shoot_ka()
|
||||
transform_weapon()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
|
||||
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
visible_message("<span class='danger'>[src] fires the proto-kinetic accelerator!</span>")
|
||||
face_atom(target)
|
||||
new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir)
|
||||
Shoot(target)
|
||||
changeNext_move(CLICK_CD_RANGE)
|
||||
|
||||
//I'm still of the belief that this entire proc needs to be wiped from existence.
|
||||
// do not take my touching of it to be endorsement of it. ~mso
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
|
||||
while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep.
|
||||
stoplag(1)
|
||||
sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about
|
||||
if(QDELETED(target))
|
||||
return
|
||||
if(dashing || next_move > world.time || !Adjacent(target))
|
||||
if(dashing && next_move <= world.time)
|
||||
next_move = world.time + 1
|
||||
INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again.
|
||||
return
|
||||
AttackingTarget()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
|
||||
if(world.time < dash_cooldown)
|
||||
return
|
||||
var/list/accessable_turfs = list()
|
||||
var/self_dist_to_target = 0
|
||||
var/turf/own_turf = get_turf(src)
|
||||
if(!QDELETED(dash_target))
|
||||
self_dist_to_target += get_dist(dash_target, own_turf)
|
||||
for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
|
||||
var/turf_dist_to_target = 0
|
||||
if(!QDELETED(dash_target))
|
||||
turf_dist_to_target += get_dist(dash_target, O)
|
||||
if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O))
|
||||
var/valid = TRUE
|
||||
for(var/turf/T in getline(own_turf, O))
|
||||
if(is_blocked_turf(T, TRUE))
|
||||
valid = FALSE
|
||||
continue
|
||||
if(valid)
|
||||
accessable_turfs[O] = turf_dist_to_target
|
||||
var/turf/target_turf
|
||||
if(!QDELETED(dash_target))
|
||||
var/closest_dist = MINER_DASH_RANGE
|
||||
for(var/t in accessable_turfs)
|
||||
if(accessable_turfs[t] < closest_dist)
|
||||
closest_dist = accessable_turfs[t]
|
||||
for(var/t in accessable_turfs)
|
||||
if(accessable_turfs[t] != closest_dist)
|
||||
accessable_turfs -= t
|
||||
if(!LAZYLEN(accessable_turfs))
|
||||
return
|
||||
dash_cooldown = world.time + initial(dash_cooldown)
|
||||
target_turf = pick(accessable_turfs)
|
||||
var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf))
|
||||
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
|
||||
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
|
||||
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
|
||||
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src)
|
||||
animate(D, alpha = 0, time = 5)
|
||||
forceMove(step_back_turf)
|
||||
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
|
||||
dashing = TRUE
|
||||
alpha = 0
|
||||
animate(src, alpha = 255, time = 5)
|
||||
sleep(2)
|
||||
D.forceMove(step_forward_turf)
|
||||
forceMove(target_turf)
|
||||
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
|
||||
sleep(1)
|
||||
dashing = FALSE
|
||||
shoot_ka()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
|
||||
if(time_until_next_transform <= world.time)
|
||||
miner_saw.transform_cooldown = 0
|
||||
miner_saw.transform_weapon(src, TRUE)
|
||||
icon_state = "miner[miner_saw.active ? "_transformed":""]"
|
||||
icon_living = "miner[miner_saw.active ? "_transformed":""]"
|
||||
time_until_next_transform = world.time + rand(50, 100)
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/miner_death
|
||||
icon_state = "miner_death"
|
||||
duration = 15
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir)
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/fade_out)
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out()
|
||||
var/matrix/M = new
|
||||
M.Turn(pick(90, 270))
|
||||
var/final_dir = dir
|
||||
if(dir & (EAST|WEST)) //Facing east or west
|
||||
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
|
||||
|
||||
animate(src, transform = M, pixel_y = -6, dir = final_dir, time = 2, easing = EASE_IN|EASE_OUT)
|
||||
sleep(5)
|
||||
animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = EASE_IN, flags = ANIMATION_PARALLEL)
|
||||
sleep(4)
|
||||
animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
|
||||
|
||||
#undef MINER_DASH_RANGE
|
||||
@@ -1,5 +1,3 @@
|
||||
#define MEDAL_PREFIX "Bubblegum"
|
||||
|
||||
/*
|
||||
|
||||
BUBBLEGUM
|
||||
@@ -46,7 +44,7 @@ Difficulty: Hard
|
||||
del_on_death = 1
|
||||
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
|
||||
var/charging = 0
|
||||
medal_type = MEDAL_PREFIX
|
||||
medal_type = BOSS_MEDAL_BUBBLEGUM
|
||||
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'
|
||||
@@ -389,5 +387,3 @@ Difficulty: Hard
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
#define MEDAL_PREFIX "Colossus"
|
||||
/*
|
||||
|
||||
COLOSSUS
|
||||
@@ -44,7 +43,7 @@ Difficulty: Very Hard
|
||||
ranged = 1
|
||||
pixel_x = -32
|
||||
del_on_death = 1
|
||||
medal_type = MEDAL_PREFIX
|
||||
medal_type = BOSS_MEDAL_COLOSSUS
|
||||
score_type = COLOSSUS_SCORE
|
||||
loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/internal/vocal_cords/colossus)
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)
|
||||
@@ -262,5 +261,3 @@ Difficulty: Very Hard
|
||||
gpstag = "Angelic Signal"
|
||||
desc = "Get in the fucking robot."
|
||||
invisibility = 100
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
|
||||
+3
-6
@@ -1,4 +1,3 @@
|
||||
#define MEDAL_PREFIX "Drake"
|
||||
/*
|
||||
|
||||
ASH DRAKE
|
||||
@@ -48,7 +47,7 @@ Difficulty: Medium
|
||||
butcher_results = list(/obj/item/stack/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
|
||||
medal_type = BOSS_MEDAL_DRAKE
|
||||
score_type = DRAKE_SCORE
|
||||
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
|
||||
death_sound = 'sound/misc/demon_dies.ogg'
|
||||
@@ -131,7 +130,7 @@ Difficulty: Medium
|
||||
playsound(T,'sound/magic/fireball.ogg', 200, 1)
|
||||
new /obj/effect/temp_visual/fireball(T)
|
||||
sleep(12)
|
||||
explosion(T, 0, 0, 1, 0, 0, 0, 1)
|
||||
explosion(T, 0, 0, 1, 0, 0, 0, 1, 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
|
||||
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
|
||||
@@ -274,6 +273,4 @@ Difficulty: Medium
|
||||
loot = list()
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype)
|
||||
return
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
return
|
||||
@@ -1,4 +1,3 @@
|
||||
#define MEDAL_PREFIX "Hierophant"
|
||||
/*
|
||||
|
||||
The Hierophant
|
||||
@@ -66,8 +65,8 @@ Difficulty: Hard
|
||||
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
|
||||
medal_type = BOSS_MEDAL_HIEROPHANT
|
||||
score_type = HIEROPHANT_SCORE
|
||||
del_on_death = TRUE
|
||||
death_sound = 'sound/magic/repulse.ogg'
|
||||
|
||||
@@ -591,24 +590,3 @@ Difficulty: Hard
|
||||
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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#define MEDAL_PREFIX "Legion"
|
||||
/*
|
||||
|
||||
LEGION
|
||||
@@ -31,6 +30,7 @@ Difficulty: Medium
|
||||
armour_penetration = 50
|
||||
melee_damage_lower = 25
|
||||
melee_damage_upper = 25
|
||||
wander = 0
|
||||
speed = 2
|
||||
ranged = 1
|
||||
del_on_death = 1
|
||||
@@ -39,7 +39,7 @@ Difficulty: Medium
|
||||
ranged_cooldown_time = 20
|
||||
var/size = 5
|
||||
var/charging = 0
|
||||
medal_type = MEDAL_PREFIX
|
||||
medal_type = BOSS_MEDAL_LEGION
|
||||
score_type = LEGION_SCORE
|
||||
pixel_y = -90
|
||||
pixel_x = -75
|
||||
@@ -143,5 +143,3 @@ Difficulty: Medium
|
||||
gpstag = "Echoing Signal"
|
||||
desc = "The message repeats."
|
||||
invisibility = 100
|
||||
|
||||
#undef MEDAL_PREFIX
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define MEDAL_PREFIX "Boss"
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna
|
||||
name = "megafauna"
|
||||
desc = "Attack the weak point for massive damage."
|
||||
@@ -33,7 +31,7 @@
|
||||
/obj/structure/barricade,
|
||||
/obj/machinery/field,
|
||||
/obj/machinery/power/emitter)
|
||||
var/medal_type = MEDAL_PREFIX
|
||||
var/medal_type
|
||||
var/score_type = BOSS_SCORE
|
||||
var/elimination = 0
|
||||
var/anger_modifier = 0
|
||||
@@ -42,7 +40,7 @@
|
||||
move_resist = MOVE_FORCE_OVERPOWERING
|
||||
pull_force = MOVE_FORCE_OVERPOWERING
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
layer = MOB_LAYER + 0.5 //Looks weird with them slipping under mineral walls and cameras and shit otherwise
|
||||
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/Destroy()
|
||||
@@ -102,122 +100,16 @@
|
||||
if(3)
|
||||
adjustBruteLoss(50)
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype,scoretype)
|
||||
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill)
|
||||
if(!medal_type || admin_spawned || !SSmedals.hub_enabled) //Don't award medals if the medal type isn't set
|
||||
return FALSE
|
||||
|
||||
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
|
||||
for(var/mob/living/L in view(7,src))
|
||||
if(L.stat || !L.client)
|
||||
continue
|
||||
var/client/C = L.client
|
||||
SSmedals.UnlockMedal("Boss [BOSS_KILL_MEDAL]", C)
|
||||
SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL]", C)
|
||||
SSmedals.SetScore(BOSS_SCORE, C, 1)
|
||||
SSmedals.SetScore(score_type, C, 1)
|
||||
return TRUE
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
|
||||
Swarmer Beacon
|
||||
|
||||
A strange machine appears anywhere a normal lavaland mob can it produces a swarmer at a rate of
|
||||
1/15 seconds, until there are GetTotalAISwarmerCap()/2 swarmers, after this it is up to the swarmers themselves to
|
||||
increase their population (it will repopulate them should they fall under GetTotalAISwarmerCap()/2 again)
|
||||
|
||||
tl;dr A million of the little hellraisers spawn (controlled by AI) and try to eat mining
|
||||
|
||||
Loot: Not much, besides a shit load of artificial bluespace crystals, Oh and mining doesn't get eaten
|
||||
that's a plus I suppose.
|
||||
|
||||
Difficulty: Special
|
||||
|
||||
*/
|
||||
|
||||
GLOBAL_LIST_EMPTY(AISwarmers)
|
||||
GLOBAL_LIST_EMPTY(AISwarmersByType)//AISwarmersByType[.../resource] = list(1st, 2nd, nth), AISwarmersByType[../ranged] = list(1st, 2nd, nth) etc.
|
||||
GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swarmer/ai/resource = 30, /mob/living/simple_animal/hostile/swarmer/ai/ranged_combat = 20, /mob/living/simple_animal/hostile/swarmer/ai/melee_combat = 10))
|
||||
|
||||
|
||||
//returns a type of AI swarmer that is NOT at max cap
|
||||
//type order is shuffled, to prevent bias
|
||||
/proc/GetUncappedAISwarmerType()
|
||||
var/static/list/swarmerTypes = subtypesof(/mob/living/simple_animal/hostile/swarmer/ai)
|
||||
LAZYINITLIST(GLOB.AISwarmersByType)
|
||||
for(var/t in shuffle(swarmerTypes))
|
||||
var/list/amount = GLOB.AISwarmersByType[t]
|
||||
if(!amount || amount.len < GLOB.AISwarmerCapsByType[t])
|
||||
return t
|
||||
|
||||
|
||||
//Total of all subtype caps
|
||||
/proc/GetTotalAISwarmerCap()
|
||||
var/static/list/swarmerTypes = subtypesof(/mob/living/simple_animal/hostile/swarmer/ai)
|
||||
. = 0
|
||||
LAZYINITLIST(GLOB.AISwarmersByType)
|
||||
for(var/t in swarmerTypes)
|
||||
. += GLOB.AISwarmerCapsByType[t]
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon
|
||||
name = "swarmer beacon"
|
||||
desc = "That name is a bit of a mouthful, but stop paying attention to your mouth they're eating everything!"
|
||||
icon = 'icons/mob/swarmer.dmi'
|
||||
icon_state = "swarmer_console"
|
||||
health = 750
|
||||
maxHealth = 750 //""""low-ish"""" HP because it's a passive boss, and the swarm itself is the real foe
|
||||
internal_gps = /obj/item/gps/internal/swarmer_beacon
|
||||
medal_type = BOSS_MEDAL_SWARMERS
|
||||
score_type = SWARMER_BEACON_SCORE
|
||||
faction = list("mining", "boss", "swarmer")
|
||||
weather_immunities = list("lava","ash")
|
||||
stop_automated_movement = TRUE
|
||||
wander = FALSE
|
||||
layer = BELOW_MOB_LAYER
|
||||
AIStatus = AI_OFF
|
||||
var/swarmer_spawn_cooldown = 0
|
||||
var/swarmer_spawn_cooldown_amt = 150 //Deciseconds between the swarmers we spawn
|
||||
var/call_help_cooldown = 0
|
||||
var/call_help_cooldown_amt = 150 //Deciseconds between calling swarmers to help us when attacked
|
||||
var/static/list/swarmer_caps
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Initialize(mapload)
|
||||
. = ..()
|
||||
swarmer_caps = GLOB.AISwarmerCapsByType //for admin-edits
|
||||
for(var/ddir in cardinal)
|
||||
new /obj/structure/swarmer/blockade (get_step(src, ddir))
|
||||
var/mob/living/simple_animal/hostile/swarmer/ai/resource/R = new(loc)
|
||||
step(R, ddir) //Step the swarmers, instead of spawning them there, incase the turf is solid
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Life()
|
||||
. = ..()
|
||||
if(.)
|
||||
var/createtype = GetUncappedAISwarmerType()
|
||||
if(createtype && world.time > swarmer_spawn_cooldown && GLOB.AISwarmers.len < (GetTotalAISwarmerCap()*0.5))
|
||||
swarmer_spawn_cooldown = world.time + swarmer_spawn_cooldown_amt
|
||||
new createtype(loc)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||
. = ..()
|
||||
if(. > 0 && world.time > call_help_cooldown)
|
||||
call_help_cooldown = world.time + call_help_cooldown_amt
|
||||
summon_backup(25) //long range, only called max once per 15 seconds, so it's not deathlag
|
||||
|
||||
|
||||
/obj/item/gps/internal/swarmer_beacon
|
||||
icon_state = null
|
||||
gpstag = "Hungry Signal"
|
||||
desc = "Transmitted over the signal is a strange message repeated in every language you know of, and some you don't too..." //the message is "nom nom nom"
|
||||
invisibility = 100
|
||||
|
||||
//SWARMER AI
|
||||
//AI versions of the swarmer mini-antag
|
||||
//This is an Abstract Base, it re-enables AI, but does not give the swarmer any goals/targets
|
||||
/mob/living/simple_animal/hostile/swarmer/ai
|
||||
wander = 1
|
||||
faction = list("swarmer", "mining")
|
||||
weather_immunities = list("ash") //wouldn't be fun otherwise
|
||||
AIStatus = AI_ON
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/Initialize(mapload)
|
||||
. = ..()
|
||||
ToggleLight() //so you can see them eating you out of house and home/shooting you/stunlocking you for eternity
|
||||
LAZYINITLIST(GLOB.AISwarmersByType[type])
|
||||
GLOB.AISwarmers += src
|
||||
GLOB.AISwarmersByType[type] += src
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/Destroy()
|
||||
GLOB.AISwarmers -= src
|
||||
GLOB.AISwarmersByType[type] -= src
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/SwarmerTypeToCreate()
|
||||
return GetUncappedAISwarmerType()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/handle_automated_action()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(!stop_automated_movement)
|
||||
if(health < maxHealth*0.25)
|
||||
StartAction(100)
|
||||
RepairSelf()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc)
|
||||
if(newloc)
|
||||
if(newloc.z == z) //so these actions are Z-specific
|
||||
if(islava(newloc))
|
||||
var/turf/simulated/floor/plating/lava/L = newloc
|
||||
if(!L.is_safe())
|
||||
StartAction(20)
|
||||
new /obj/structure/lattice/catwalk/swarmer_catwalk(newloc)
|
||||
return FALSE
|
||||
|
||||
if(ischasm(newloc) && !throwing)
|
||||
throw_at(get_edge_target_turf(src, get_dir(src, newloc)), 7 , 3, src, FALSE) //my planet needs me
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/proc/StartAction(deci = 0)
|
||||
stop_automated_movement = TRUE
|
||||
AIStatus = AI_OFF
|
||||
addtimer(CALLBACK(src, .proc/EndAction), deci)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/proc/EndAction()
|
||||
stop_automated_movement = FALSE
|
||||
AIStatus = AI_ON
|
||||
|
||||
|
||||
|
||||
|
||||
//RESOURCE SWARMER:
|
||||
//Similar to the original Player-Swarmers, these dismantle things to obtain the metal inside
|
||||
//They then use this medal to produce more swarmers or traps/barricades
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource
|
||||
search_objects = 1
|
||||
attack_all_objects = TRUE //attempt to nibble everything
|
||||
lose_patience_timeout = 150
|
||||
var/static/list/sharedWanted = typecacheof(list(/turf/simulated/mineral, /turf/simulated/wall)) //eat rocks and walls
|
||||
var/static/list/sharedIgnore = list()
|
||||
|
||||
//This handles viable things to eat/attack
|
||||
//Place specific cases of AI derpiness here
|
||||
//Most can be left to the automatic Gain/LosePatience() system
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/CanAttack(atom/the_target)
|
||||
|
||||
//SPECIFIC CASES:
|
||||
//Smash fulltile windows before grilles
|
||||
if(istype(the_target, /obj/structure/grille))
|
||||
for(var/obj/structure/window/rogueWindow in get_turf(the_target))
|
||||
if(rogueWindow.fulltile) //done this way because the subtypes are weird.
|
||||
the_target = rogueWindow
|
||||
break
|
||||
|
||||
//GENERAL CASES:
|
||||
if(is_type_in_typecache(the_target, sharedIgnore)) //always ignore
|
||||
return FALSE
|
||||
if(is_type_in_typecache(the_target, sharedWanted)) //always eat
|
||||
return TRUE
|
||||
|
||||
return ..() //else, have a nibble, see if it's food
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/OpenFire(atom/A)
|
||||
if(isliving(A)) //don't shoot rocks, sillies.
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/AttackingTarget()
|
||||
if(target.swarmer_act(src))
|
||||
add_type_to_wanted(target.type)
|
||||
return TRUE
|
||||
else
|
||||
add_type_to_ignore(target.type)
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/handle_automated_action()
|
||||
. = ..()
|
||||
if(.)
|
||||
if(!stop_automated_movement)
|
||||
if(GLOB.AISwarmers.len < GetTotalAISwarmerCap() && resources >= 50)
|
||||
StartAction(100) //so they'll actually sit still and use the verbs
|
||||
CreateSwarmer()
|
||||
return
|
||||
|
||||
if(resources > 5)
|
||||
if(prob(5)) //lower odds, as to prioritise reproduction
|
||||
StartAction(10) //not a typo
|
||||
CreateBarricade()
|
||||
return
|
||||
if(prob(5))
|
||||
CreateTrap()
|
||||
return
|
||||
|
||||
|
||||
//So swarmers can learn what is and isn't food
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/proc/add_type_to_wanted(typepath)
|
||||
if(!sharedWanted[typepath])// this and += is faster than |=
|
||||
sharedWanted += typecacheof(typepath)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/resource/proc/add_type_to_ignore(typepath)
|
||||
if(!sharedIgnore[typepath])
|
||||
sharedIgnore += typecacheof(typepath)
|
||||
|
||||
|
||||
//RANGED SWARMER
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/ranged_combat
|
||||
icon_state = "swarmer_ranged"
|
||||
icon_living = "swarmer_ranged"
|
||||
projectiletype = /obj/item/projectile/beam/laser
|
||||
projectilesound = 'sound/weapons/laser.ogg'
|
||||
check_friendly_fire = TRUE //you're supposed to protect the resource swarmers, you poop
|
||||
retreat_distance = 3
|
||||
minimum_distance = 3
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/ranged_combat/Aggro()
|
||||
..()
|
||||
summon_backup(15, TRUE) //Exact matching, so that goliaths don't come to aid the swarmers, that'd be silly
|
||||
|
||||
|
||||
//MELEE SWARMER
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/melee_combat
|
||||
icon_state = "swarmer_melee"
|
||||
icon_living = "swarmer_melee"
|
||||
health = 60
|
||||
maxHealth = 60
|
||||
ranged = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/melee_combat/Aggro()
|
||||
..()
|
||||
summon_backup(15, TRUE)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/ai/melee_combat/AttackingTarget()
|
||||
if(isliving(target))
|
||||
if(prob(35))
|
||||
StartAction(30)
|
||||
DisperseTarget(target)
|
||||
else
|
||||
var/mob/living/L = target
|
||||
L.attack_animal(src)
|
||||
L.electrocute_act(10, src, safety = TRUE) //safety = TRUE means we don't check gloves... Ok?
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
//SWARMER CATWALKS
|
||||
//Used so they can survive lavaland better
|
||||
/obj/structure/lattice/catwalk/swarmer_catwalk
|
||||
name = "swarmer catwalk"
|
||||
desc = "A catwalk-like mesh, produced by swarmers to allow them to navigate hostile terrain."
|
||||
icon = 'icons/obj/smooth_structures/swarmer_catwalk.dmi'
|
||||
icon_state = "swarmer_catwalk"
|
||||
@@ -0,0 +1,140 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk
|
||||
name = "basilisk"
|
||||
desc = "A territorial beast, covered in a thick shell that absorbs energy. Its stare causes victims to freeze from the inside."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Basilisk"
|
||||
icon_living = "Basilisk"
|
||||
icon_aggro = "Basilisk_alert"
|
||||
icon_dead = "Basilisk_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
move_to_delay = 20
|
||||
projectiletype = /obj/item/projectile/temp/basilisk
|
||||
projectilesound = 'sound/weapons/pierce.ogg'
|
||||
ranged = 1
|
||||
ranged_message = "stares"
|
||||
ranged_cooldown_time = 30
|
||||
throw_message = "does nothing against the hard shell of"
|
||||
vision_range = 2
|
||||
speed = 3
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
harm_intent_damage = 5
|
||||
obj_damage = 60
|
||||
melee_damage_lower = 12
|
||||
melee_damage_upper = 12
|
||||
attacktext = "bites into"
|
||||
a_intent = INTENT_HARM
|
||||
speak_emote = list("chitters")
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 2
|
||||
turns_per_move = 5
|
||||
loot = list(/obj/item/stack/ore/diamond{layer = 4.1},
|
||||
/obj/item/stack/ore/diamond{layer = 4.1})
|
||||
|
||||
/obj/item/projectile/temp/basilisk
|
||||
name = "freezing blast"
|
||||
icon_state = "ice_2"
|
||||
damage = 0
|
||||
damage_type = BURN
|
||||
nodamage = 1
|
||||
flag = "energy"
|
||||
temperature = 50
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(var/new_target)
|
||||
if(..()) //we have a target
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.bodytemperature > 261)
|
||||
L.bodytemperature = 261
|
||||
visible_message("<span class='danger'>The [src.name]'s stare chills [L.name] to the bone!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/ex_act(severity)
|
||||
switch(severity)
|
||||
if(1.0)
|
||||
gib()
|
||||
if(2.0)
|
||||
adjustBruteLoss(140)
|
||||
if(3.0)
|
||||
adjustBruteLoss(110)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher
|
||||
name = "watcher"
|
||||
desc = "A levitating, eye-like creature held aloft by winglike formations of sinew. A sharp spine of crystal protrudes from its body."
|
||||
icon = 'icons/mob/lavaland/watcher.dmi'
|
||||
icon_state = "watcher"
|
||||
icon_living = "watcher"
|
||||
icon_aggro = "watcher"
|
||||
icon_dead = "watcher_dead"
|
||||
pixel_x = -10
|
||||
throw_message = "bounces harmlessly off of"
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
attacktext = "impales"
|
||||
a_intent = INTENT_HARM
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
stat_attack = 1
|
||||
flying = TRUE
|
||||
robust_searching = 1
|
||||
loot = list()
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/Initialize()
|
||||
. = ..()
|
||||
if(prob(1))
|
||||
if(prob(75))
|
||||
new /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing(loc)
|
||||
else
|
||||
new /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing(loc)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing
|
||||
name = "magmawing watcher"
|
||||
desc = "When raised very close to lava, some watchers adapt to the extreme heat and use lava as both a weapon and wings."
|
||||
icon_state = "watcher_magmawing"
|
||||
icon_living = "watcher_magmawing"
|
||||
icon_aggro = "watcher_magmawing"
|
||||
icon_dead = "watcher_magmawing_dead"
|
||||
maxHealth = 215 //Compensate for the lack of slowdown on projectiles with a bit of extra health
|
||||
health = 215
|
||||
light_range = 3
|
||||
light_power = 2.5
|
||||
light_color = LIGHT_COLOR_ORANGE
|
||||
projectiletype = /obj/item/projectile/temp/basilisk/magmawing
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing
|
||||
name = "icewing watcher"
|
||||
desc = "Very rarely, some watchers will eke out an existence far from heat sources. In the absence of warmth, they become icy and fragile but fire much stronger freezing blasts."
|
||||
icon_state = "watcher_icewing"
|
||||
icon_living = "watcher_icewing"
|
||||
icon_aggro = "watcher_icewing"
|
||||
icon_dead = "watcher_icewing_dead"
|
||||
maxHealth = 170
|
||||
health = 170
|
||||
projectiletype = /obj/item/projectile/temp/basilisk/icewing
|
||||
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) //No sinew; the wings are too fragile to be usable
|
||||
|
||||
/obj/item/projectile/temp/basilisk/magmawing
|
||||
name = "scorching blast"
|
||||
icon_state = "lava"
|
||||
damage = 5
|
||||
damage_type = BURN
|
||||
nodamage = FALSE
|
||||
temperature = 500 //Heats you up!
|
||||
|
||||
/obj/item/projectile/temp/basilisk/magmawing/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/mob/living/L = target
|
||||
if (istype(L))
|
||||
L.adjust_fire_stacks(0.1)
|
||||
L.IgniteMob()
|
||||
|
||||
/obj/item/projectile/temp/basilisk/icewing
|
||||
damage = 5
|
||||
damage_type = BURN
|
||||
nodamage = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril
|
||||
fromtendril = TRUE
|
||||
@@ -0,0 +1,192 @@
|
||||
/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."
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
icon_state = "Goliath"
|
||||
icon_living = "Goliath"
|
||||
icon_aggro = "Goliath_alert"
|
||||
icon_dead = "Goliath_dead"
|
||||
icon_gib = "syndicate_gib"
|
||||
attack_sound = 'sound/weapons/punch4.ogg'
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
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_time = 120
|
||||
friendly = "wails at"
|
||||
speak_emote = list("bellows")
|
||||
vision_range = 4
|
||||
speed = 3
|
||||
maxHealth = 300
|
||||
health = 300
|
||||
harm_intent_damage = 1 //Only the manliest of men can kill a Goliath with only their fists.
|
||||
obj_damage = 100
|
||||
melee_damage_lower = 25
|
||||
melee_damage_upper = 25
|
||||
attacktext = "pulverizes"
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
throw_message = "does nothing to the rocky hide of the"
|
||||
aggro_vision_range = 9
|
||||
idle_vision_range = 5
|
||||
move_force = MOVE_FORCE_VERY_STRONG
|
||||
move_resist = MOVE_FORCE_VERY_STRONG
|
||||
pull_force = MOVE_FORCE_VERY_STRONG
|
||||
var/pre_attack = 0
|
||||
var/pre_attack_icon = "Goliath_preattack"
|
||||
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/Life()
|
||||
..()
|
||||
handle_preattack()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack()
|
||||
if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack)
|
||||
pre_attack++
|
||||
if(!pre_attack || stat || AIStatus == AI_IDLE)
|
||||
return
|
||||
icon_state = pre_attack_icon
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/revive()
|
||||
move_force = MOVE_FORCE_VERY_STRONG
|
||||
move_resist = MOVE_FORCE_VERY_STRONG
|
||||
pull_force = MOVE_FORCE_VERY_STRONG
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
|
||||
var/tturf = get_turf(target)
|
||||
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/temp_visual/goliath_tentacle/original(tturf, src)
|
||||
ranged_cooldown = world.time + ranged_cooldown_time
|
||||
icon_state = icon_aggro
|
||||
pre_attack = 0
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(damage)
|
||||
ranged_cooldown--
|
||||
handle_preattack()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/Aggro()
|
||||
vision_range = aggro_vision_range
|
||||
handle_preattack()
|
||||
if(icon_state != icon_aggro)
|
||||
icon_state = icon_aggro
|
||||
return
|
||||
|
||||
// Lavaland Goliath
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast
|
||||
name = "goliath"
|
||||
desc = "A hulking, armor-plated beast with long tendrils arching from its back."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "goliath"
|
||||
icon_living = "goliath"
|
||||
icon_aggro = "goliath"
|
||||
icon_dead = "goliath_dead"
|
||||
throw_message = "does nothing to the tough hide of the"
|
||||
pre_attack_icon = "goliath2"
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/goliath = 2, /obj/item/stack/sheet/animalhide/goliath_hide = 1, /obj/item/stack/sheet/bone = 2)
|
||||
loot = list()
|
||||
stat_attack = 1
|
||||
robust_searching = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast/random/Initialize()
|
||||
. = ..()
|
||||
if(prob(1))
|
||||
new /mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient(loc)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient
|
||||
name = "ancient goliath"
|
||||
desc = "Goliaths are biologically immortal, and rare specimens have survived for centuries. This one is clearly ancient, and its tentacles constantly churn the earth around it."
|
||||
icon_state = "Goliath"
|
||||
icon_living = "Goliath"
|
||||
icon_aggro = "Goliath_alert"
|
||||
icon_dead = "Goliath_dead"
|
||||
maxHealth = 400
|
||||
health = 400
|
||||
speed = 4
|
||||
pre_attack_icon = "Goliath_preattack"
|
||||
throw_message = "does nothing to the rocky hide of the"
|
||||
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) //A throwback to the asteroid days
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/goliath = 2, /obj/item/stack/sheet/bone = 2)
|
||||
wander = FALSE
|
||||
var/list/cached_tentacle_turfs
|
||||
var/turf/last_location
|
||||
var/tentacle_recheck_cooldown = 100
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient/Life()
|
||||
. = ..()
|
||||
if(!.) // dead
|
||||
return
|
||||
if(isturf(loc))
|
||||
if(!LAZYLEN(cached_tentacle_turfs) || loc != last_location || tentacle_recheck_cooldown <= world.time)
|
||||
LAZYCLEARLIST(cached_tentacle_turfs)
|
||||
last_location = loc
|
||||
tentacle_recheck_cooldown = world.time + initial(tentacle_recheck_cooldown)
|
||||
for(var/turf/simulated/floor/T in orange(4, loc))
|
||||
LAZYADD(cached_tentacle_turfs, T)
|
||||
for(var/t in cached_tentacle_turfs)
|
||||
if(isfloorturf(t))
|
||||
if(prob(10))
|
||||
new /obj/effect/temp_visual/goliath_tentacle(t, src)
|
||||
else
|
||||
cached_tentacle_turfs -= t
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril
|
||||
fromtendril = TRUE
|
||||
|
||||
//tentacles
|
||||
/obj/effect/temp_visual/goliath_tentacle
|
||||
name = "goliath tentacle"
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "Goliath_tentacle_spawn"
|
||||
layer = BELOW_MOB_LAYER
|
||||
var/mob/living/spawner
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/Initialize(mapload, mob/living/new_spawner)
|
||||
. = ..()
|
||||
for(var/obj/effect/temp_visual/goliath_tentacle/T in loc)
|
||||
if(T != src)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
if(!QDELETED(new_spawner))
|
||||
spawner = new_spawner
|
||||
if(ismineralturf(loc))
|
||||
var/turf/simulated/mineral/M = loc
|
||||
M.gets_drilled()
|
||||
deltimer(timerid)
|
||||
timerid = addtimer(CALLBACK(src, .proc/tripanim), 7, TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner)
|
||||
. = ..()
|
||||
var/list/directions = cardinal.Copy()
|
||||
for(var/i in 1 to 3)
|
||||
var/spawndir = pick_n_take(directions)
|
||||
var/turf/T = get_step(src, spawndir)
|
||||
if(T)
|
||||
new /obj/effect/temp_visual/goliath_tentacle(T, spawner)
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/proc/tripanim()
|
||||
icon_state = "Goliath_tentacle_wiggle"
|
||||
deltimer(timerid)
|
||||
timerid = addtimer(CALLBACK(src, .proc/trip), 3, TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/proc/trip()
|
||||
var/latched = FALSE
|
||||
for(var/mob/living/L in loc)
|
||||
if((!QDELETED(spawner) && spawner.faction_check_mob(L)) || L.stat == DEAD)
|
||||
continue
|
||||
visible_message("<span class='danger'>[src] grabs hold of [L]!</span>")
|
||||
L.Stun(5)
|
||||
L.adjustBruteLoss(rand(10,15))
|
||||
latched = TRUE
|
||||
if(!latched)
|
||||
retract()
|
||||
else
|
||||
deltimer(timerid)
|
||||
timerid = addtimer(CALLBACK(src, .proc/retract), 10, TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/proc/retract()
|
||||
icon_state = "Goliath_tentacle_retract"
|
||||
deltimer(timerid)
|
||||
timerid = QDEL_IN(src, 5)
|
||||
@@ -0,0 +1,114 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch
|
||||
name = "gutlunch"
|
||||
desc = "A scavenger that eats raw meat, often found alongside ash walkers. Produces a thick, nutritious milk."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "gutlunch"
|
||||
icon_living = "gutlunch"
|
||||
icon_dead = "gutlunch"
|
||||
icon_aggro = "gutlunch"
|
||||
speak_emote = list("warbles", "quavers")
|
||||
emote_hear = list("trills.")
|
||||
emote_see = list("sniffs.", "burps.")
|
||||
weather_immunities = list("lava","ash")
|
||||
faction = list("mining", "ashwalker")
|
||||
density = 0
|
||||
speak_chance = 1
|
||||
turns_per_move = 8
|
||||
obj_damage = 0
|
||||
environment_smash = 0
|
||||
move_to_delay = 15
|
||||
response_help = "pets"
|
||||
response_disarm = "gently pushes aside"
|
||||
response_harm = "squishes"
|
||||
friendly = "pinches"
|
||||
a_intent = INTENT_HELP
|
||||
ventcrawler = 2
|
||||
gold_core_spawnable = 2
|
||||
stat_attack = 1
|
||||
gender = NEUTER
|
||||
stop_automated_movement = FALSE
|
||||
stop_automated_movement_when_pulled = TRUE
|
||||
stat_exclusive = TRUE
|
||||
robust_searching = TRUE
|
||||
search_objects = TRUE
|
||||
del_on_death = TRUE
|
||||
loot = list(/obj/effect/decal/cleanable/blood/gibs)
|
||||
deathmessage = "is pulped into bugmash."
|
||||
|
||||
simplespecies = /mob/living/simple_animal/hostile/asteroid/gutlunch
|
||||
childtype = list(/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck = 45, /mob/living/simple_animal/hostile/asteroid/gutlunch/guthen = 55)
|
||||
|
||||
wanted_objects = list(/obj/effect/decal/cleanable/blood/gibs)
|
||||
var/obj/item/udder/gutlunch/udder = null
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/New()
|
||||
udder = new()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/Destroy()
|
||||
qdel(udder)
|
||||
udder = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/regenerate_icons()
|
||||
cut_overlays()
|
||||
if(udder.reagents.total_volume == udder.reagents.maximum_volume)
|
||||
add_overlay("gl_full")
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/attackby(obj/item/O, mob/user, params)
|
||||
if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass))
|
||||
udder.milkAnimal(O, user)
|
||||
regenerate_icons()
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/AttackingTarget()
|
||||
if(is_type_in_list(target, wanted_objects)) //we eats
|
||||
udder.generateMilk()
|
||||
regenerate_icons()
|
||||
visible_message("<span class='notice'>[src] slurps up [target].</span>")
|
||||
qdel(target)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/udder/gutlunch
|
||||
name = "nutrient sac"
|
||||
|
||||
/obj/item/udder/gutlunch/New()
|
||||
reagents = new(50)
|
||||
reagents.my_atom = src
|
||||
|
||||
/obj/item/udder/gutlunch/generateMilk()
|
||||
if(prob(60))
|
||||
reagents.add_reagent("cream", rand(2, 5))
|
||||
if(prob(45))
|
||||
reagents.add_reagent("salglu_solution", rand(2,5))
|
||||
|
||||
|
||||
//Male gutlunch. They're smaller and more colorful!
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck
|
||||
name = "gubbuck"
|
||||
gender = MALE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck/New()
|
||||
..()
|
||||
add_atom_colour(pick("#E39FBB", "#D97D64", "#CF8C4A"), FIXED_COLOUR_PRIORITY)
|
||||
resize = 0.85
|
||||
update_transform()
|
||||
|
||||
//Lady gutlunch. They make the babby.
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen
|
||||
name = "guthen"
|
||||
gender = FEMALE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen/Life()
|
||||
..()
|
||||
if(udder.reagents.total_volume == udder.reagents.maximum_volume) //Only breed when we're full.
|
||||
make_babies()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen/make_babies()
|
||||
. = ..()
|
||||
if(.)
|
||||
udder.reagents.clear_reagents()
|
||||
regenerate_icons()
|
||||
@@ -218,7 +218,7 @@
|
||||
// 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."
|
||||
desc = "You can still see what was once a person under the shifting mass of corruption."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "legion"
|
||||
icon_living = "legion"
|
||||
@@ -236,20 +236,44 @@
|
||||
del_on_death = 1
|
||||
stat_attack = 1
|
||||
robust_searching = 1
|
||||
var/dwarf_mob = FALSE
|
||||
var/mob/living/carbon/human/stored_mob
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/Initialize()
|
||||
. = ..()
|
||||
if(prob(5))
|
||||
new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(loc)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf
|
||||
name = "dwarf legion"
|
||||
desc = "You can still see what was once a midget under the shifting mass of corruption."
|
||||
icon_state = "dwarf_legion"
|
||||
icon_living = "dwarf_legion"
|
||||
icon_aggro = "dwarf_legion"
|
||||
icon_dead = "dwarf_legion"
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
speed = 2 //faster!
|
||||
dwarf_mob = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril
|
||||
fromtendril = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed)
|
||||
if(can_die())
|
||||
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/mob_spawn/human/corpse/charredskeleton(T)
|
||||
// due to `del_on_death`
|
||||
return ..(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 if(fromtendril)
|
||||
new /obj/effect/mob_spawn/human/corpse/charredskeleton(T)
|
||||
else if(dwarf_mob)
|
||||
new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf(T)
|
||||
else
|
||||
new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested(T)
|
||||
..(gibbed)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion
|
||||
@@ -286,8 +310,12 @@
|
||||
|
||||
/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>")
|
||||
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L
|
||||
if((DWARF in H.mutations)) //dwarf legions aren't just fluff!
|
||||
L = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(H.loc)
|
||||
else
|
||||
L = new(H.loc)
|
||||
visible_message("<span class='warning'>[L] staggers to [L.p_their()] feet!</span>")
|
||||
H.death()
|
||||
H.adjustBruteLoss(1000)
|
||||
L.stored_mob = H
|
||||
@@ -337,3 +365,104 @@
|
||||
husk = FALSE
|
||||
mob_species = /datum/species/skeleton
|
||||
mob_color = "#454545"
|
||||
|
||||
//Legion infested mobs
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf/equip(mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
H.dna.SetSEState(SMALLSIZEBLOCK, 1, 1)
|
||||
H.mutations.Add(DWARF)
|
||||
genemutcheck(H, SMALLSIZEBLOCK, null, MUTCHK_FORCED)
|
||||
H.update_mutations()
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/damaged/legioninfested/Initialize()
|
||||
var/type = pickweight(list("Miner" = 66, "Ashwalker" = 10, "Golem" = 10,"Clown" = 10, pick(list("Shadow", "YeOlde","Operative", "Cultist")) = 4))
|
||||
switch(type)
|
||||
if("Miner")
|
||||
mob_species = pickweight(list(/datum/species/human = 72, /datum/species/unathi = 28))
|
||||
uniform = /obj/item/clothing/under/rank/miner/lavaland
|
||||
if (prob(4))
|
||||
belt = /obj/item/storage/belt/mining
|
||||
else if(prob(10))
|
||||
belt = pickweight(list(/obj/item/pickaxe = 8, /obj/item/pickaxe/silver = 2, /obj/item/pickaxe/diamond = 1))
|
||||
else
|
||||
belt = /obj/item/tank/emergency_oxygen/engi
|
||||
if(mob_species != /datum/species/unathi)
|
||||
shoes = /obj/item/clothing/shoes/workboots/mining
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
mask = /obj/item/clothing/mask/gas/explorer
|
||||
if(prob(20))
|
||||
suit = pickweight(list(/obj/item/clothing/suit/hooded/explorer = 18, /obj/item/clothing/suit/hooded/goliath = 2))
|
||||
if(prob(30))
|
||||
r_pocket = pickweight(list(/obj/item/stack/marker_beacon = 20, /obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/autoinjector/survival = 2, /obj/item/borg/upgrade/modkit/damage = 1 ))
|
||||
if(prob(10))
|
||||
l_pocket = pickweight(list(/obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/autoinjector/survival = 2, /obj/item/borg/upgrade/modkit/cooldown = 1 ))
|
||||
if("Ashwalker")
|
||||
mob_species = /datum/species/unathi/ashwalker
|
||||
uniform = /obj/item/clothing/under/gladiator/ash_walker
|
||||
if(prob(95))
|
||||
head = /obj/item/clothing/head/helmet/gladiator
|
||||
else
|
||||
head = /obj/item/clothing/head/skullhelmet
|
||||
suit = /obj/item/clothing/suit/armor/bone
|
||||
if(prob(5))
|
||||
back = pickweight(list(/obj/item/twohanded/spear/bonespear = 3, /obj/item/twohanded/fireaxe/boneaxe = 2))
|
||||
if(prob(10))
|
||||
belt = /obj/item/storage/belt/mining
|
||||
if(prob(30))
|
||||
r_pocket = /obj/item/kitchen/knife/combat/survival/bone
|
||||
if(prob(30))
|
||||
l_pocket = /obj/item/kitchen/knife/combat/survival/bone
|
||||
if("Clown")
|
||||
name = pick(GLOB.clown_names)
|
||||
outfit = /datum/outfit/job/clown
|
||||
belt = null
|
||||
backpack_contents = list()
|
||||
if(prob(70))
|
||||
backpack_contents += pick(list(/obj/item/stamp/clown = 1, /obj/item/reagent_containers/spray/waterflower = 1, /obj/item/reagent_containers/food/snacks/grown/banana = 1, /obj/item/megaphone = 1))
|
||||
if(prob(30))
|
||||
backpack_contents += list(/obj/item/stack/sheet/mineral/bananium = pickweight(list( 1 = 3, 2 = 2, 3 = 1)))
|
||||
if(prob(10))
|
||||
l_pocket = pickweight(list(/obj/item/bikehorn/golden = 3, /obj/item/bikehorn/airhorn= 1 ))
|
||||
if("Golem")
|
||||
mob_species = pick(list(/datum/species/golem/adamantine, /datum/species/golem/plasma, /datum/species/golem/diamond, /datum/species/golem/gold, /datum/species/golem/silver, /datum/species/golem/plasteel, /datum/species/golem/titanium, /datum/species/golem/plastitanium))
|
||||
if(prob(30))
|
||||
glasses = pickweight(list(/obj/item/clothing/glasses/meson = 2, /obj/item/clothing/glasses/hud/health = 2, /obj/item/clothing/glasses/hud/diagnostic =2, /obj/item/clothing/glasses/science = 2, /obj/item/clothing/glasses/welding = 2, /obj/item/clothing/glasses/night = 1))
|
||||
if(prob(10))
|
||||
belt = pick(list(/obj/item/storage/belt/mining, /obj/item/storage/belt/utility/full))
|
||||
if(prob(50))
|
||||
back = /obj/item/bedsheet/rd/royal_cape
|
||||
if(prob(10))
|
||||
l_pocket = pick(list(/obj/item/crowbar/power, /obj/item/wrench/power, /obj/item/weldingtool/experimental))
|
||||
if("YeOlde")
|
||||
mob_gender = FEMALE
|
||||
uniform = /obj/item/clothing/under/maid
|
||||
gloves = /obj/item/clothing/gloves/color/white
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
head = /obj/item/clothing/head/helmet/riot/knight
|
||||
suit = /obj/item/clothing/suit/armor/riot/knight
|
||||
back = /obj/item/shield/riot/buckler
|
||||
belt = /obj/item/nullrod/claymore
|
||||
r_pocket = /obj/item/tank/emergency_oxygen
|
||||
mask = /obj/item/clothing/mask/breath
|
||||
if("Operative")
|
||||
id_job = "Operative"
|
||||
outfit = /datum/outfit/syndicatecommandocorpse
|
||||
if("Shadow")
|
||||
mob_species = /datum/species/shadow
|
||||
uniform = /obj/item/clothing/under/color/black
|
||||
shoes = /obj/item/clothing/shoes/black
|
||||
suit = /obj/item/clothing/suit/storage/labcoat
|
||||
glasses = /obj/item/clothing/glasses/sunglasses/blindfold
|
||||
back = /obj/item/tank/oxygen
|
||||
mask = /obj/item/clothing/mask/breath
|
||||
if("Cultist")
|
||||
uniform = /obj/item/clothing/under/roman
|
||||
suit = /obj/item/clothing/suit/hooded/cultrobes
|
||||
head = /obj/item/clothing/head/culthood
|
||||
suit_store = /obj/item/tome
|
||||
r_pocket = /obj/item/restraints/legcuffs/bola/cult
|
||||
l_pocket = /obj/item/melee/cultblade/dagger
|
||||
glasses = /obj/item/clothing/glasses/hud/health/night
|
||||
backpack_contents = list(/obj/item/reagent_containers/food/drinks/bottle/unholywater = 1, /obj/item/cult_shift = 1, /obj/item/flashlight/flare = 1, /obj/item/stack/sheet/runed_metal = 15)
|
||||
. = ..()
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/
|
||||
vision_range = 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)
|
||||
unsuitable_atmos_damage = 15
|
||||
faction = list("mining")
|
||||
weather_immunities = list("lava","ash")
|
||||
obj_damage = 30
|
||||
environment_smash = 2
|
||||
minbodytemp = 0
|
||||
heat_damage_per_tick = 20
|
||||
response_help = "pokes"
|
||||
response_disarm = "shoves"
|
||||
response_harm = "strikes"
|
||||
status_flags = 0
|
||||
a_intent = INTENT_HARM
|
||||
var/throw_message = "bounces off of"
|
||||
var/icon_aggro = null // for swapping to when we get aggressive
|
||||
var/fromtendril = FALSE
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/Aggro()
|
||||
..()
|
||||
icon_state = icon_aggro
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/LoseAggro()
|
||||
..()
|
||||
if(stat == CONSCIOUS)
|
||||
icon_state = icon_living
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/bullet_act(var/obj/item/projectile/P)//Reduces damage from most projectiles to curb off-screen kills
|
||||
if(!stat)
|
||||
Aggro()
|
||||
if(P.damage < 30 && P.damage_type != BRUTE)
|
||||
P.damage = (P.damage / 3)
|
||||
visible_message("<span class='danger'>The [P] has a reduced effect on [src]!</span>")
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM)//No floor tiling them to death, wiseguy
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/T = AM
|
||||
if(!stat)
|
||||
Aggro()
|
||||
if(T.throwforce <= 20)
|
||||
visible_message("<span class='notice'>The [T.name] [src.throw_message] [src.name]!</span>")
|
||||
return
|
||||
..()
|
||||
@@ -0,0 +1,79 @@
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland
|
||||
name = "necropolis tendril"
|
||||
desc = "A vile tendril of corruption, originating deep underground. Terrible monsters are pouring out of it."
|
||||
icon = 'icons/mob/nest.dmi'
|
||||
icon_state = "tendril"
|
||||
icon_living = "tendril"
|
||||
icon_dead = "tendril"
|
||||
faction = list("mining")
|
||||
weather_immunities = list("lava","ash")
|
||||
luminosity = 1
|
||||
health = 250
|
||||
maxHealth = 250
|
||||
max_mobs = 3
|
||||
spawn_time = 300 //30 seconds default
|
||||
mob_type = /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril
|
||||
spawn_text = "emerges from"
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = INFINITY
|
||||
loot = list(/obj/effect/collapse, /obj/structure/closet/crate/necropolis/tendril)
|
||||
del_on_death = 1
|
||||
var/gps = null
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland/New()
|
||||
..()
|
||||
//for(var/F in RANGE_TURFS(1, src)) TODO: Uncomment
|
||||
//if(ismineralturf(F))
|
||||
//var/turf/simulated/mineral/M = F
|
||||
//M.ChangeTurf(M.turf_type, FALSE, TRUE)
|
||||
gps = new /obj/item/gps/internal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland/Destroy()
|
||||
qdel(gps)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland/death()
|
||||
var/last_tendril = TRUE
|
||||
for(var/mob/living/simple_animal/hostile/spawner/lavaland/other in GLOB.mob_list)
|
||||
if(other != src)
|
||||
last_tendril = FALSE
|
||||
break
|
||||
if(last_tendril && !admin_spawned)
|
||||
if(SSmedals.hub_enabled)
|
||||
for(var/mob/living/L in view(7,src))
|
||||
if(L.stat || !L.client)
|
||||
continue
|
||||
SSmedals.UnlockMedal("[BOSS_MEDAL_TENDRIL] [ALL_KILL_MEDAL]", L.client)
|
||||
SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1)
|
||||
..()
|
||||
|
||||
/obj/effect/collapse
|
||||
name = "collapsing necropolis tendril"
|
||||
desc = "Get clear!"
|
||||
luminosity = 1
|
||||
layer = ABOVE_OPEN_TURF_LAYER
|
||||
icon = 'icons/mob/nest.dmi'
|
||||
icon_state = "tendril"
|
||||
anchored = TRUE
|
||||
|
||||
/obj/effect/collapse/New()
|
||||
..()
|
||||
visible_message("<span class='boldannounce'>The tendril writhes in fury as the earth around it begins to crack and break apart! Get back!</span>")
|
||||
visible_message("<span class='warning'>Something falls free of the tendril!</span>")
|
||||
playsound(get_turf(src),'sound/effects/tendril_destroyed.ogg', 200, 0, 50, 1, 1)
|
||||
spawn(50)
|
||||
for(var/mob/M in range(7,src))
|
||||
shake_camera(M, 15, 1)
|
||||
playsound(get_turf(src),'sound/effects/explosionfar.ogg', 200, 1)
|
||||
visible_message("<span class='boldannounce'>The tendril falls inward, the ground around it widening into a yawning chasm!</span>")
|
||||
for(var/turf/T in range(2,src))
|
||||
if(!T.density)
|
||||
T.TerraformTurf(/turf/simulated/floor/chasm/straight_down/lava_land_surface)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland/goliath
|
||||
mob_type = /mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/lavaland/legion
|
||||
mob_type = /mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril
|
||||
@@ -140,7 +140,7 @@
|
||||
move_resist = MOVE_FORCE_VERY_STRONG
|
||||
pull_force = MOVE_FORCE_VERY_STRONG
|
||||
var/pre_attack = 0
|
||||
loot = list(/obj/item/asteroid/goliath_hide{layer = 4.1})
|
||||
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/goliath/Life()
|
||||
..()
|
||||
@@ -223,46 +223,3 @@
|
||||
else
|
||||
spawn(50)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/asteroid/goliath_hide
|
||||
name = "goliath hide plates"
|
||||
desc = "Pieces of a goliath's rocky hide, these might be able to make your suit a bit more durable to attack from the local fauna."
|
||||
icon = 'icons/obj/items.dmi'
|
||||
icon_state = "goliath_hide"
|
||||
flags = NOBLUDGEON
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
layer = 4
|
||||
|
||||
/obj/item/asteroid/goliath_hide/afterattack(atom/target, mob/user, proximity_flag)
|
||||
if(proximity_flag)
|
||||
if(istype(target, /obj/item/clothing/suit/space/hardsuit/mining) || istype(target, /obj/item/clothing/head/helmet/space/hardsuit/mining) || istype(target, /obj/item/clothing/suit/space/eva/plasmaman/miner) || istype(target, /obj/item/clothing/head/helmet/space/eva/plasmaman/miner))
|
||||
var/obj/item/clothing/C = target
|
||||
var/current_armor = C.armor
|
||||
if(current_armor["melee"] < 60)
|
||||
current_armor["melee"] = min(current_armor["melee"] + 10, 60)
|
||||
to_chat(user, "<span class='info'>You strengthen [target], improving its resistance against melee attacks.</span>")
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user, "<span class='info'>You can't improve [C] any further.</span>")
|
||||
return
|
||||
if(istype(target, /obj/mecha/working/ripley))
|
||||
var/obj/mecha/D = target
|
||||
if(D.icon_state != "ripley-open")
|
||||
to_chat(user, "<span class='info'>You can't add armour onto the mech while someone is inside!</span>")
|
||||
return
|
||||
var/list/damage_absorption = D.damage_absorption
|
||||
if(damage_absorption["brute"] > 0.3)
|
||||
damage_absorption["brute"] = max(damage_absorption["brute"] - 0.1, 0.3)
|
||||
damage_absorption["bullet"] = damage_absorption["bullet"] - 0.05
|
||||
damage_absorption["fire"] = damage_absorption["fire"] - 0.05
|
||||
damage_absorption["laser"] = damage_absorption["laser"] - 0.025
|
||||
to_chat(user, "<span class='info'>You strengthen [target], improving its resistance against melee attacks.</span>")
|
||||
qdel(src)
|
||||
D.overlays += image("icon"="mecha.dmi", "icon_state"="ripley-g-open")
|
||||
D.desc = "Autonomous Power Loader Unit. Its armour is enhanced with some goliath hide plates."
|
||||
if(damage_absorption["brute"] == 0.3)
|
||||
D.overlays += image("icon"="mecha.dmi", "icon_state"="ripley-g-full-open")
|
||||
D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - the pilot must be an experienced monster hunter."
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You can't improve [D] any further!</span>")
|
||||
return
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
|
||||
/mob/living/simple_animal/possessed_object/IsAdvancedToolUser() // So we can shoot guns (Mostly ourselves), among other things.
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/possessed_object/get_access() // If we've possessed an ID card we've got access to lots of fun things!
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/spawner/mining
|
||||
name = "monster den"
|
||||
desc = "A hole dug into the ground, harboring all kinds of monsters found within most caves or mining asteroids."
|
||||
desc = "A hole dug into the ground, harboring all kinds of monsters found within most caves or mining areas."
|
||||
icon_state = "hole"
|
||||
icon_living = "hole"
|
||||
health = 200
|
||||
|
||||
@@ -549,3 +549,37 @@
|
||||
dna.SetSEState(block, 0, 1) //Fix the gene
|
||||
genemutcheck(src, block,null, MUTCHK_FORCED)
|
||||
dna.UpdateSE()
|
||||
|
||||
///////////////////////////////////// STUN ABSORPTION /////////////////////////////////////
|
||||
|
||||
/mob/living/proc/add_stun_absorption(key, duration, priority, message, self_message, examine_message)
|
||||
//adds a stun absorption with a key, a duration in deciseconds, its priority, and the messages it makes when you're stun/examined, if any
|
||||
if(!islist(stun_absorption))
|
||||
stun_absorption = list()
|
||||
if(stun_absorption[key])
|
||||
stun_absorption[key]["end_time"] = world.time + duration
|
||||
stun_absorption[key]["priority"] = priority
|
||||
stun_absorption[key]["stuns_absorbed"] = 0
|
||||
else
|
||||
stun_absorption[key] = list("end_time" = world.time + duration, "priority" = priority, "stuns_absorbed" = 0, \
|
||||
"visible_message" = message, "self_message" = self_message, "examine_message" = examine_message)
|
||||
|
||||
/mob/living/proc/absorb_stun(amount, ignoring_flag_presence)
|
||||
if(!amount || amount <= 0 || stat || ignoring_flag_presence || !islist(stun_absorption))
|
||||
return FALSE
|
||||
var/priority_absorb_key
|
||||
var/highest_priority
|
||||
for(var/i in stun_absorption)
|
||||
if(stun_absorption[i]["end_time"] > world.time && (!priority_absorb_key || stun_absorption[i]["priority"] > highest_priority))
|
||||
priority_absorb_key = stun_absorption[i]
|
||||
highest_priority = priority_absorb_key["priority"]
|
||||
if(priority_absorb_key)
|
||||
if(priority_absorb_key["visible_message"] || priority_absorb_key["self_message"])
|
||||
if(priority_absorb_key["visible_message"] && priority_absorb_key["self_message"])
|
||||
visible_message("<span class='warning'>[src][priority_absorb_key["visible_message"]]</span>", "<span class='boldwarning'>[priority_absorb_key["self_message"]]</span>")
|
||||
else if(priority_absorb_key["visible_message"])
|
||||
visible_message("<span class='warning'>[src][priority_absorb_key["visible_message"]]</span>")
|
||||
else if(priority_absorb_key["self_message"])
|
||||
to_chat(src, "<span class='boldwarning'>[priority_absorb_key["self_message"]]</span>")
|
||||
priority_absorb_key["stuns_absorbed"] += amount
|
||||
return TRUE
|
||||
+25
-9
@@ -967,13 +967,6 @@ var/list/slot_equipment_priority = list( \
|
||||
|
||||
|
||||
if(is_admin(src))
|
||||
if(statpanel("DI")) //not looking at that panel
|
||||
stat("Loc", "([x], [y], [z]) [loc]")
|
||||
stat("CPU", "[world.cpu]")
|
||||
stat("Instances", "[world.contents.len]")
|
||||
|
||||
if(processScheduler)
|
||||
processScheduler.statProcesses()
|
||||
if(statpanel("MC")) //looking at that panel
|
||||
var/turf/T = get_turf(client.eye)
|
||||
stat("Location:", COORD(T))
|
||||
@@ -1077,7 +1070,7 @@ var/list/slot_equipment_priority = list( \
|
||||
|
||||
|
||||
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
/mob/proc/swap_hand()
|
||||
return
|
||||
@@ -1340,6 +1333,29 @@ var/list/slot_equipment_priority = list( \
|
||||
/mob/proc/is_literate()
|
||||
return FALSE
|
||||
|
||||
/mob/proc/faction_check_mob(mob/target, exact_match)
|
||||
if(exact_match) //if we need an exact match, we need to do some bullfuckery.
|
||||
var/list/faction_src = faction.Copy()
|
||||
var/list/faction_target = target.faction.Copy()
|
||||
if(!("\ref[src]" in faction_target)) //if they don't have our ref faction, remove it from our factions list.
|
||||
faction_src -= "\ref[src]" //if we don't do this, we'll never have an exact match.
|
||||
if(!("\ref[target]" in faction_src))
|
||||
faction_target -= "\ref[target]" //same thing here.
|
||||
return faction_check(faction_src, faction_target, TRUE)
|
||||
return faction_check(faction, target.faction, FALSE)
|
||||
|
||||
/proc/faction_check(list/faction_A, list/faction_B, exact_match)
|
||||
var/list/match_list
|
||||
if(exact_match)
|
||||
match_list = faction_A & faction_B //only items in both lists
|
||||
var/length = LAZYLEN(match_list)
|
||||
if(length)
|
||||
return (length == LAZYLEN(faction_A)) //if they're not the same len(gth) or we don't have a len, then this isn't an exact match.
|
||||
else
|
||||
match_list = faction_A & faction_B
|
||||
return LAZYLEN(match_list)
|
||||
return FALSE
|
||||
|
||||
/mob/proc/update_sight()
|
||||
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
|
||||
sync_lighting_plane_alpha()
|
||||
@@ -1348,4 +1364,4 @@ var/list/slot_equipment_priority = list( \
|
||||
if(hud_used)
|
||||
var/obj/screen/plane_master/lighting/L = hud_used.plane_masters["[LIGHTING_PLANE]"]
|
||||
if (L)
|
||||
L.alpha = lighting_alpha
|
||||
L.alpha = lighting_alpha
|
||||
|
||||
@@ -182,7 +182,8 @@
|
||||
return 0
|
||||
|
||||
if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes")
|
||||
if(!client) return 1
|
||||
if(!client)
|
||||
return 1
|
||||
var/mob/dead/observer/observer = new()
|
||||
src << browse(null, "window=playersetup")
|
||||
spawning = 1
|
||||
@@ -220,7 +221,6 @@
|
||||
if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING)
|
||||
to_chat(usr, "<span class='warning'>The round is either not ready, or has already finished...</span>")
|
||||
return
|
||||
|
||||
if(client.prefs.species in GLOB.whitelisted_species)
|
||||
|
||||
if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist)
|
||||
|
||||
Reference in New Issue
Block a user