Merge remote-tracking branch 'upstream/master' into diagonal-movement

This commit is contained in:
Couls
2019-06-02 21:59:58 -04:00
parent cdb867456e
commit 5979166f54
526 changed files with 97898 additions and 5530 deletions
+30 -1
View File
@@ -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
+11 -6
View File
@@ -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"
+5 -28
View File
@@ -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')
+3 -7
View File
@@ -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
+8
View File
@@ -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]"))
*/
+26 -1
View File
@@ -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"
+1 -1
View File
@@ -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
@@ -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
+34
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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)